import ctypes from ctypes import wintypes import logging # Win32 Types & Constants # Use standard wintypes to ensure architecture safety (32-bit vs 64-bit) WPARAM = wintypes.WPARAM LPARAM = wintypes.LPARAM HWND = wintypes.HWND UINT = wintypes.UINT # LRESULT is technically LONG_PTR (signed) # In 64-bit, c_longlong. In 32-bit, c_long. if ctypes.sizeof(ctypes.c_void_p) == 8: LRESULT = ctypes.c_longlong else: LRESULT = ctypes.c_long # Callback Type WNDPROC = ctypes.WINFUNCTYPE(LRESULT, HWND, UINT, WPARAM, LPARAM) GWLP_WNDPROC = -4 WM_NCHITTEST = 0x0084 HTTRANSPARENT = -1 HTCLIENT = 1 HTCAPTION = 2 user32 = ctypes.windll.user32 # SetWindowLongPtr Check if hasattr(user32, "SetWindowLongPtrW"): SetWindowLongPtr = user32.SetWindowLongPtrW SetWindowLongPtr.argtypes = [HWND, ctypes.c_int, ctypes.c_void_p] SetWindowLongPtr.restype = ctypes.c_void_p else: SetWindowLongPtr = user32.SetWindowLongW SetWindowLongPtr.argtypes = [HWND, ctypes.c_int, ctypes.c_long] SetWindowLongPtr.restype = ctypes.c_long # Define ArgTypes to prevent truncation/stack corruption user32.CallWindowProcW.argtypes = [ctypes.c_void_p, HWND, UINT, WPARAM, LPARAM] user32.CallWindowProcW.restype = LRESULT user32.ScreenToClient.argtypes = [HWND, ctypes.POINTER(wintypes.POINT)] user32.ScreenToClient.restype = ctypes.c_bool # DPI API try: GetDpiForWindow = user32.GetDpiForWindow GetDpiForWindow.argtypes = [HWND] GetDpiForWindow.restype = UINT except AttributeError: GetDpiForWindow = None def LOWORD(l): return l & 0xffff def HIWORD(l): return (l >> 16) & 0xffff class WindowHook: def __init__(self, hwnd, width, height, initial_scale=1.0): self.hwnd = hwnd self.old_wnd_proc = None self.new_wnd_proc = WNDPROC(self.wnd_proc_callback) # TIGHT FIT: [20, 20, 400, 120] # (Window 420x140, Pill 380x100) self.logical_rect = [20, 20, 20+380, 20+100] self.current_scale = initial_scale self.enabled = True # New flag def set_enabled(self, enabled): self.enabled = enabled def install(self): proc_address = ctypes.cast(self.new_wnd_proc, ctypes.c_void_p) self.old_wnd_proc = SetWindowLongPtr(self.hwnd, GWLP_WNDPROC, proc_address) def wnd_proc_callback(self, hwnd, msg, wParam, lParam): try: if msg == WM_NCHITTEST: # If disabled (invisible/inactive), let clicks pass through (HTTRANSPARENT) if not self.enabled: return HTTRANSPARENT res = self.on_nchittest(lParam) if res != 0: return res return user32.CallWindowProcW(self.old_wnd_proc, hwnd, msg, wParam, lParam) except Exception: # Swallow exceptions in callback to prevent recursive crash loops return 0 def on_nchittest(self, lParam): if GetDpiForWindow: dpi = GetDpiForWindow(self.hwnd) if dpi > 0: self.current_scale = dpi / 96.0 # Physics s = self.current_scale phys_rect = [ self.logical_rect[0] * s, self.logical_rect[1] * s, self.logical_rect[2] * s, self.logical_rect[3] * s ] x_screen = LOWORD(lParam) if x_screen > 32767: x_screen -= 65536 y_screen = HIWORD(lParam) if y_screen > 32767: y_screen -= 65536 pt = wintypes.POINT(x_screen, y_screen) user32.ScreenToClient(self.hwnd, ctypes.byref(pt)) lx = pt.x ly = pt.y inside = (lx >= phys_rect[0] and lx <= phys_rect[2] and ly >= phys_rect[1] and ly <= phys_rect[3]) if inside: return HTCAPTION else: return HTTRANSPARENT