From 02d676bda89c2fb8469ea81f7429c19c1e29df7c Mon Sep 17 00:00:00 2001 From: Sébastien Dailly Date: Sat, 15 Jul 2023 14:44:41 +0200 Subject: Initial commit --- win32.py | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100755 win32.py (limited to 'win32.py') 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) -- cgit v1.2.3