Block all keyboard (except ESC) and all mouse input via WH_MOUSE_LL; tagged idle nudge passes through own hook; hint updated
This commit is contained in:
1 parent
d16a2db9ae
commit
d3286ef3b8
1 file changed
+40
-7
+40
-7
@@ -245,6 +245,8 @@ if sys.platform == "win32":
|
|||||||
WM_KEYDOWN_LL = 0x0100
|
WM_KEYDOWN_LL = 0x0100
|
||||||
VK_ESCAPE = 0x1B
|
VK_ESCAPE = 0x1B
|
||||||
WH_KEYBOARD_LL = 13
|
WH_KEYBOARD_LL = 13
|
||||||
|
WH_MOUSE_LL = 14
|
||||||
|
MOUSE_NUDGE_TAG = 0x42424242
|
||||||
DT_CENTER = 0x00000001
|
DT_CENTER = 0x00000001
|
||||||
DT_VCENTER = 0x00000004
|
DT_VCENTER = 0x00000004
|
||||||
DT_SINGLELINE = 0x00000020
|
DT_SINGLELINE = 0x00000020
|
||||||
@@ -302,6 +304,15 @@ if sys.platform == "win32":
|
|||||||
("dwExtraInfo", ULONG_PTR),
|
("dwExtraInfo", ULONG_PTR),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
class MSLLHOOKSTRUCT(ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("pt", wt.POINT),
|
||||||
|
("mouseData", wt.DWORD),
|
||||||
|
("flags", wt.DWORD),
|
||||||
|
("time", wt.DWORD),
|
||||||
|
("dwExtraInfo", ULONG_PTR),
|
||||||
|
]
|
||||||
|
|
||||||
# --- prototypes (64-bit safe) ---
|
# --- prototypes (64-bit safe) ---
|
||||||
user32.EnumDisplayMonitors.argtypes = [wt.HDC, ctypes.POINTER(wt.RECT), MONITORENUMPROC, wt.LPARAM]
|
user32.EnumDisplayMonitors.argtypes = [wt.HDC, ctypes.POINTER(wt.RECT), MONITORENUMPROC, wt.LPARAM]
|
||||||
user32.EnumDisplayMonitors.restype = wt.BOOL
|
user32.EnumDisplayMonitors.restype = wt.BOOL
|
||||||
@@ -401,7 +412,7 @@ if sys.platform == "win32":
|
|||||||
_hwnds = []
|
_hwnds = []
|
||||||
_hint_active = True
|
_hint_active = True
|
||||||
_hook_id = None
|
_hook_id = None
|
||||||
_hint_text = "ESC - exit"
|
_hint_text = "ESC - exit | mouse/keyboard locked"
|
||||||
|
|
||||||
def enum_monitor_rects():
|
def enum_monitor_rects():
|
||||||
rects = []
|
rects = []
|
||||||
@@ -456,15 +467,28 @@ if sys.platform == "win32":
|
|||||||
return user32.DefWindowProcW(hwnd, msg, wparam, lparam)
|
return user32.DefWindowProcW(hwnd, msg, wparam, lparam)
|
||||||
|
|
||||||
def _low_level_keyboard_proc(nCode, wParam, lParam):
|
def _low_level_keyboard_proc(nCode, wParam, lParam):
|
||||||
if nCode >= 0 and wParam == WM_KEYDOWN_LL:
|
if nCode >= 0:
|
||||||
kb = ctypes.cast(lParam, ctypes.POINTER(KBDLLHOOKSTRUCT)).contents
|
kb = ctypes.cast(lParam, ctypes.POINTER(KBDLLHOOKSTRUCT)).contents
|
||||||
if kb.vkCode == VK_ESCAPE:
|
if kb.vkCode == VK_ESCAPE and wParam == WM_KEYDOWN_LL:
|
||||||
LOG.info("ESC pressed -> exit")
|
LOG.info("ESC pressed -> exit")
|
||||||
user32.PostQuitMessage(0)
|
user32.PostQuitMessage(0)
|
||||||
|
return 1 # block ALL keyboard input while the overlay is up
|
||||||
return user32.CallNextHookEx(_hook_id, nCode, wParam, lParam)
|
return user32.CallNextHookEx(_hook_id, nCode, wParam, lParam)
|
||||||
|
|
||||||
|
_mouse_hook_id = None
|
||||||
|
|
||||||
|
def _low_level_mouse_proc(nCode, wParam, lParam):
|
||||||
|
if nCode >= 0:
|
||||||
|
ms = ctypes.cast(lParam, ctypes.POINTER(MSLLHOOKSTRUCT)).contents
|
||||||
|
# allow only our own idle-keepalive nudge (tagged dwExtraInfo);
|
||||||
|
# block every real mouse event (moves, clicks, wheel)
|
||||||
|
if int(ms.dwExtraInfo) == MOUSE_NUDGE_TAG:
|
||||||
|
return user32.CallNextHookEx(_mouse_hook_id, nCode, wParam, lParam)
|
||||||
|
return 1
|
||||||
|
return user32.CallNextHookEx(_mouse_hook_id, nCode, wParam, lParam)
|
||||||
|
|
||||||
def run_overlay():
|
def run_overlay():
|
||||||
global _hwnds, _hook_id
|
global _hwnds, _hook_id, _mouse_hook_id
|
||||||
# Per-monitor V2 DPI awareness: device-coord rects map 1:1 to each
|
# Per-monitor V2 DPI awareness: device-coord rects map 1:1 to each
|
||||||
# monitor regardless of mixed DPI / where the primary sits.
|
# monitor regardless of mixed DPI / where the primary sits.
|
||||||
if shcore is not None:
|
if shcore is not None:
|
||||||
@@ -522,11 +546,16 @@ if sys.platform == "win32":
|
|||||||
for _ in range(12):
|
for _ in range(12):
|
||||||
if user32.ShowCursor(False) < 0:
|
if user32.ShowCursor(False) < 0:
|
||||||
break
|
break
|
||||||
# global ESC hook
|
# global ESC hook (also blocks all other keyboard input)
|
||||||
kb_proc = HOOKPROC(_low_level_keyboard_proc)
|
kb_proc = HOOKPROC(_low_level_keyboard_proc)
|
||||||
_hook_id = user32.SetWindowsHookExW(WH_KEYBOARD_LL, kb_proc, hinst, 0)
|
_hook_id = user32.SetWindowsHookExW(WH_KEYBOARD_LL, kb_proc, hinst, 0)
|
||||||
if not _hook_id:
|
if not _hook_id:
|
||||||
LOG.warning("SetWindowsHookExW failed: %d", kernel32.GetLastError())
|
LOG.warning("SetWindowsHookExW failed: %d", kernel32.GetLastError())
|
||||||
|
# global mouse hook: block all real mouse input
|
||||||
|
mouse_proc = HOOKPROC(_low_level_mouse_proc)
|
||||||
|
_mouse_hook_id = user32.SetWindowsHookExW(WH_MOUSE_LL, mouse_proc, hinst, 0)
|
||||||
|
if not _mouse_hook_id:
|
||||||
|
LOG.warning("mouse hook failed: %d", kernel32.GetLastError())
|
||||||
|
|
||||||
# keep display awake while running (prevents monitor standby, which would
|
# keep display awake while running (prevents monitor standby, which would
|
||||||
# make ESC's first press get swallowed by the wake and a ~30s DDC hang)
|
# make ESC's first press get swallowed by the wake and a ~30s DDC hang)
|
||||||
@@ -542,8 +571,9 @@ if sys.platform == "win32":
|
|||||||
def _activity():
|
def _activity():
|
||||||
while not _activity_stop.wait(30):
|
while not _activity_stop.wait(30):
|
||||||
try:
|
try:
|
||||||
user32.mouse_event(0x0001, 1, 0, 0, None)
|
tag = ctypes.c_void_p(MOUSE_NUDGE_TAG)
|
||||||
user32.mouse_event(0x0001, 0xFFFFFFFF, 0, 0, None)
|
user32.mouse_event(0x0001, 1, 0, 0, tag)
|
||||||
|
user32.mouse_event(0x0001, 0xFFFFFFFF, 0, 0, tag)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -557,6 +587,9 @@ if sys.platform == "win32":
|
|||||||
|
|
||||||
if _hook_id:
|
if _hook_id:
|
||||||
user32.UnhookWindowsHookEx(_hook_id)
|
user32.UnhookWindowsHookEx(_hook_id)
|
||||||
|
if _mouse_hook_id:
|
||||||
|
user32.UnhookWindowsHookEx(_mouse_hook_id)
|
||||||
|
_mouse_hook_id = None
|
||||||
_activity_stop.set()
|
_activity_stop.set()
|
||||||
at.join(timeout=1)
|
at.join(timeout=1)
|
||||||
kernel32.SetThreadExecutionState(ES_CONTINUOUS) # allow display sleep again
|
kernel32.SetThreadExecutionState(ES_CONTINUOUS) # allow display sleep again
|
||||||
|
|||||||
Reference in new issue
Block a user