From 8b92624d4a56196463373c47ac4397abae69d166 Mon Sep 17 00:00:00 2001 From: Klesh Wong Date: Sat, 6 Apr 2024 23:38:43 +0800 Subject: [PATCH] fix: dpi unaware window showing across multiple monitors --- src/jigsawwm/w32/process.py | 14 ++++++++++++++ src/jigsawwm/w32/window.py | 6 ++++++ src/jigsawwm/wm/state.py | 8 +++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/jigsawwm/w32/process.py b/src/jigsawwm/w32/process.py index 9fb75f8..7b74686 100644 --- a/src/jigsawwm/w32/process.py +++ b/src/jigsawwm/w32/process.py @@ -2,10 +2,12 @@ from ctypes import * from ctypes.wintypes import * from typing import List +from enum import IntEnum kernel32 = WinDLL("kernel32", use_last_error=True) advapi32 = WinDLL("advapi32", use_last_error=True) psapi = WinDLL("psapi", use_last_error=True) +shcore = WinDLL("shcore", use_last_error=True) TOKEN_QUERY = DWORD(8) @@ -125,6 +127,18 @@ def get_session_id(): kernel32.ProcessIdToSessionId(kernel32.GetCurrentProcessId(), byref(session_id)) return kernel32.WTSGetActiveConsoleSessionId() +class ProcessDpiAwareness(IntEnum): + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 + +def get_process_dpi_awareness(pid: int) -> ProcessDpiAwareness: + """Retrieves the DPI awareness of the process""" + hprc = open_process_for_limited_query(pid) + awareness = c_int() + if shcore.GetProcessDpiAwareness(hprc, pointer(awareness)): + raise WinError(get_last_error()) + return ProcessDpiAwareness(awareness.value) if __name__ == "__main__": # import sys diff --git a/src/jigsawwm/w32/window.py b/src/jigsawwm/w32/window.py index 207fa78..058b921 100644 --- a/src/jigsawwm/w32/window.py +++ b/src/jigsawwm/w32/window.py @@ -351,6 +351,11 @@ def is_evelated(self): """Check if window is elevated (Administrator)""" return process.is_elevated(self.pid) + @property + def dpi_awareness(self): + """Check if window is api aware""" + return process.get_process_dpi_awareness(self.pid) + @property def is_cloaked(self) -> bool: """Check if window is cloaked (DWM) @@ -547,6 +552,7 @@ def inspect_window(hwnd: HWND, file=sys.stdout): print("is_app_window:", is_app_window(hwnd), file=file) print("is_manageable:", is_manageable_window(hwnd), file=file) print("is_evelated :", window.is_evelated, file=file) + print("dpi_awareness:", window.dpi_awareness.name, file=file) def inspect_active_window(): diff --git a/src/jigsawwm/wm/state.py b/src/jigsawwm/wm/state.py index 55b9061..09d0477 100644 --- a/src/jigsawwm/wm/state.py +++ b/src/jigsawwm/wm/state.py @@ -5,6 +5,7 @@ from jigsawwm.tiler.tilers import * from jigsawwm.w32.monitor import Monitor, get_monitor_from_window from jigsawwm.w32.window import RECT, Window, get_active_window +from jigsawwm.w32.process import ProcessDpiAwareness from .theme import Theme @@ -146,6 +147,12 @@ def arrange(self, theme: Optional[Theme] = None): rect = RECT(left, top, right, bottom) logger.debug("arrange %s %s", window, rect) window.set_rect(rect) + i += 1 + if window.dpi_awareness == ProcessDpiAwareness.PROCESS_DPI_UNAWARE: + # seems like the `get_extended_frame_bounds` would return physical size + # for DPI unware window, skip them for now + # TODO: convert physical size to logical size for DPI unware window + continue # compensation r = window.get_rect() b = window.get_extended_frame_bounds() @@ -156,7 +163,6 @@ def arrange(self, theme: Optional[Theme] = None): round(bottom + r.bottom - b.bottom), ) window.set_rect(RECT(*compensated_rect)) - i += 1 def restrict(self, theme: Optional[Theme] = None): """Restrict all managed windows to their specified rect"""