aboutsummaryrefslogtreecommitdiff
path: root/win32.py
diff options
context:
space:
mode:
authorSébastien Dailly <sebastien@dailly.me>2023-07-15 14:44:41 +0200
committerSébastien Dailly <sebastien@dailly.me>2023-07-15 14:59:43 +0200
commit02d676bda89c2fb8469ea81f7429c19c1e29df7c (patch)
tree11dae86a561a4800661e6c174b47611a704f857e /win32.py
Initial commit
Diffstat (limited to 'win32.py')
-rwxr-xr-xwin32.py104
1 files changed, 104 insertions, 0 deletions
diff --git a/win32.py b/win32.py
new file mode 100755
index 0000000..46d1efe
--- /dev/null
+++ b/win32.py
@@ -0,0 +1,104 @@
+# Required for the window title name
+from ctypes import wintypes, windll, create_unicode_buffer, WINFUNCTYPE
+from typing import Self, Optional, Dict
+
+from zope import interface
+from interfaces import desktopEvent
+
+# This code tries to hook the Focus changes events with the callback method.
+
+# The types of events we want to listen for, and the names we'll use for
+# them in the log output. Pick from
+# http://msdn.microsoft.com/en-us/library/windows/desktop/dd318066(v=vs.85).aspx
+EVENT_OBJECT_FOCUS = 0x8005
+EVENT_OBJECT_NAMECHANGE = 0x800C
+WINEVENT_OUTOFCONTEXT = 0x0000
+
+eventTypes = {
+ #win32con.EVENT_SYSTEM_FOREGROUND: "Foreground",
+ EVENT_OBJECT_FOCUS: "Focus",
+ EVENT_OBJECT_NAMECHANGE: "NameChange",
+ #win32con.EVENT_OBJECT_SHOW: "Show",
+ #win32con.EVENT_SYSTEM_DIALOGSTART: "Dialog",
+ #win32con.EVENT_SYSTEM_CAPTURESTART: "Capture",
+ #win32con.EVENT_SYSTEM_MINIMIZEEND: "UnMinimize"
+}
+
+WinEventProcType = WINFUNCTYPE(
+ None,
+ wintypes.HANDLE,
+ wintypes.DWORD,
+ wintypes.HWND,
+ wintypes.LONG,
+ wintypes.LONG,
+ wintypes.DWORD,
+ wintypes.DWORD
+)
+
+def setHook(WinEventProc, eventType):
+ """ Register the hook foo being notified when the window change
+ """
+ return windll.user32.SetWinEventHook(
+ eventType,
+ eventType,
+ 0,
+ WinEventProc,
+ 0,
+ 0,
+ WINEVENT_OUTOFCONTEXT
+ )
+
+@interface.implementer(desktopEvent.IDesktop)
+class Listener(object):
+
+ def __init__(self: Self, mapping: Dict[str, str], queue):
+ self.WinEventProc = WinEventProcType(self.callback)
+ self.mapping = mapping
+ self.queue = queue
+
+ def getForegroundWindowTitle(self: Self) -> Optional[str]:
+ """ Get the window title name.
+ Example found from https://stackoverflow.com/a/58355052
+ See the function in the winuser librarry :
+ https://learn.microsoft.com/en-us/windows/win32/api/winuser/
+ """
+ hWnd = windll.user32.GetForegroundWindow()
+ length = windll.user32.GetWindowTextLengthW(hWnd)
+ buf = create_unicode_buffer(length + 1)
+ windll.user32.GetWindowTextW(hWnd, buf, length + 1)
+
+ # 1-liner alternative: return buf.value if buf.value else None
+ if buf.value:
+ return buf.value
+ else:
+ return None
+
+ def callback(self: Self, hWinEventHook, event, hwnd, idObject, idChild, dwEventThread,
+ dwmsEventTime) -> None:
+
+ if hwnd != windll.user32.GetForegroundWindow():
+ # Only check the active window, the events received from other
+ # windows are ignored.
+ return
+
+ length = windll.user32.GetWindowTextLengthW(hwnd)
+ title = create_unicode_buffer(length + 1)
+ windll.user32.GetWindowTextW(hwnd, title, length + 1)
+ if title.value is None:
+ return
+ title = str(title.value).lower()
+ for pattern, code in self.mapping.items():
+ if pattern in title:
+ self.queue.put ( (code, None) )
+ return
+ self.queue.put ( ("Windows", None) )
+
+ def start(self: Self) -> None:
+ self.hookIDs = [setHook(self.WinEventProc, et) for et in eventTypes.keys()]
+ if not any(self.hookIDs):
+ print('SetWinEventHook failed')
+ sys.exit(1)
+
+ def stop(self: Self) -> None:
+ for hook in self.hookIDs:
+ windll.user32.UnhookWinEvent(hook)