1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
# Required for the window title name
from ctypes import wintypes, windll, create_unicode_buffer, WINFUNCTYPE
from typing import 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, mapping: Dict[str, str], queue):
self.WinEventProc = WinEventProcType(self.callback)
self.mapping = mapping
self.queue = queue
def getForegroundWindowTitle(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, 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 == "default":
continue
if pattern in title:
self.queue.put ( (code, None) )
return
default = self.mapping.get("default", None)
if default is not None:
self.queue.put ( (default, None) )
def start(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) -> None:
for hook in self.hookIDs:
windll.user32.UnhookWinEvent(hook)
|