Skip to content

fix(windows): make portal geometry per-monitor DPI aware - #1

Open
s-qin wants to merge 1 commit into
Divisionless:mainfrom
s-qin:fix/windows-mixed-dpi-portal
Open

fix(windows): make portal geometry per-monitor DPI aware#1
s-qin wants to merge 1 commit into
Divisionless:mainfrom
s-qin:fix/windows-mixed-dpi-portal

Conversation

@s-qin

@s-qin s-qin commented Sep 7, 2026

Copy link
Copy Markdown

fix(windows): make portal geometry per-monitor DPI aware

Problem

OpenSpan mixed logical and physical coordinates on mixed-DPI Windows displays.

Reproduction

Real physical desktop: left monitor 1600x2560, PRIMARY 1920x1080, right monitor 2560x1600.

OpenSpan before the fix enumerated: 800x1280 / 1536x864 (PRIMARY) / 1280x800 — DPI-virtualized logical values.

Observed effect: the portal captured the mouse before the physical screen edge — premature capture while the cursor was still above the true bottom edge of the PRIMARY monitor.

Root cause

No Windows OpenSpan process ever declared DPI awareness. The GUI / setup / portal processes ran as DPI-unaware (or system-aware), so monitor rectangles (GetMonitorInfoW in win/openspan_setup.py), the portal edge geometry (written into openspan_config.json by the GUI) and low-level-hook cursor coordinates ended up in DPI-virtualized coordinate spaces (e.g. primary reported 1536x864 instead of 1920x1080). Portal edges were therefore crossed early.

Fix

  • New win/dpi_aware.py: declares per-monitor DPI awareness V2 first (SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)), with fallbacks (shcore SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE), then user32 SetProcessDPIAware), and reports the awareness actually in force via GetProcessDpiAwareness.
  • Called from every Windows process before any Tk window / monitor / hook API: openspan_setup.py (GUI + setup role), openspan_portal.py, openspan_launcher.py.
  • Result: monitor rectangles, cursor / low-level hook coordinates and portal edge geometry all live in one physical-pixel coordinate space. Nothing hardcoded — any monitor layout stays dynamic, mixed-DPI included.

Validation

Manually tested on real hardware (mixed-DPI, three displays):

  • GUI enumerates 1600x2560 / 1920x1080 (PRIMARY) / 2560x1600
  • no premature capture in non-portal areas
  • portal entry from the PRIMARY bottom edge into the iPad
  • crossing back from the iPad edge returns control to Windows
  • Esc x3 panic bail still works

No CI pipeline configured in this repository (no .github/workflows); static checks passed (python -m py_compile, git diff --check).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant