From 5844b933382d40223e3110792f60dcad5f64d3a2 Mon Sep 17 00:00:00 2001 From: Tyler Elliott Date: Wed, 28 Jan 2015 14:29:39 -0800 Subject: [PATCH] remove lag from window changes needs tighter integration with recognition engines --- dragonfly/engines/base/engine.py | 4 +++ dragonfly/examples/dfly-loader-wsr.py | 25 ++++++++++++++++- dragonfly/grammar/grammar_base.py | 39 ++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/dragonfly/engines/base/engine.py b/dragonfly/engines/base/engine.py index d2c98ef..1b535dd 100644 --- a/dragonfly/engines/base/engine.py +++ b/dragonfly/engines/base/engine.py @@ -126,6 +126,10 @@ def load_grammar(self, grammar): wrapper = self._load_grammar(grammar) self._grammar_wrappers[wrapper_key] = wrapper + def window_change(self, executable, title, handle): + for wrapper in self._grammar_wrappers.values(): + wrapper.grammar.window_change(executable, title, handle) + def _load_grammar(self, grammar): raise NotImplementedError("Virtual method not implemented for" " engine %s." % self) diff --git a/dragonfly/examples/dfly-loader-wsr.py b/dragonfly/examples/dfly-loader-wsr.py index 976e573..a33be10 100644 --- a/dragonfly/examples/dfly-loader-wsr.py +++ b/dragonfly/examples/dfly-loader-wsr.py @@ -13,12 +13,15 @@ directory it's in and loads any ``*.py`` it finds. """ - +import ctypes +import ctypes.wintypes import time import os.path import logging import pythoncom +import win32con +from dragonfly import Window from dragonfly.engines.backend_sapi5.engine import Sapi5InProcEngine @@ -139,6 +142,26 @@ def main(): directory = CommandModuleDirectory(path, excludes=[__file__]) directory.load() + WinEventProcType = ctypes.WINFUNCTYPE(None, ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.HWND, + ctypes.wintypes.LONG, ctypes.wintypes.LONG, ctypes.wintypes.DWORD, + ctypes.wintypes.DWORD) + + def callback(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime): + window = Window.get_foreground() + if hwnd == window.handle: + engine.window_change(window.executable, window.title, window.handle) + + + def set_hook(win_event_proc, event_type): + return ctypes.windll.user32.SetWinEventHook(event_type, event_type, 0, win_event_proc, 0, 0, win32con.WINEVENT_OUTOFCONTEXT) + + + win_event_proc = WinEventProcType(callback) + ctypes.windll.user32.SetWinEventHook.restype = ctypes.wintypes.HANDLE + + [set_hook(win_event_proc, et) for et in + {win32con.EVENT_SYSTEM_FOREGROUND, win32con.EVENT_OBJECT_NAMECHANGE, }] + engine.speak('beginning loop!') while 1: pythoncom.PumpWaitingMessages() diff --git a/dragonfly/grammar/grammar_base.py b/dragonfly/grammar/grammar_base.py index 41a9076..1bb4c49 100644 --- a/dragonfly/grammar/grammar_base.py +++ b/dragonfly/grammar/grammar_base.py @@ -3,7 +3,7 @@ # (c) Copyright 2007, 2008 by Christo Butcher # Licensed under the LGPL. # -# Dragonfly is free software: you can redistribute it and/or modify it +# Dragonfly is free software: you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. @@ -427,6 +427,43 @@ def process_begin(self, executable, title, handle): self._log_begin.debug("Grammar %s: active rules: %s." % (self._name, [r.name for r in self._rules if r.active])) + def window_change(self, executable, title, handle): + """ + Foreground window changed detected + + This method is called when the foreground window changes or the title of the foreground window changes. + + Arguments: + - *executable* -- the full path to the module whose window is currently in the foreground. + - *title* -- window title of the foreground window. + - *handle* -- window handle to the foreground window. + + """ + self._log_begin.debug("Grammar %s: detected window change." % self._name) + self._log_begin.debug("Grammar %s: executable '%s', title '%s'." % (self._name, executable, title)) + + if not self._enabled: + # Grammar is disabled, so deactivate all active rules. + [r.deactivate() for r in self._rules if r.active] + + elif not self._context or self._context.matches(executable, title, handle): + # Grammar is within context. + if not self._in_context: + self._in_context = True + self.enter_context() + for r in self._rules: + r.activate() + + else: + # Grammar's context doesn't match, deactivate active rules. + if self._in_context: + self._in_context = False + self.exit_context() + [r.deactivate() for r in self._rules if r.active] + + self._log_begin.debug("Grammar %s: active rules: %s." + % (self._name, [r.name for r in self._rules if r.active])) + def enter_context(self): """ Enter context callback.