Skip to content

fix(wm): correct window sizing across monitors with differing DPI - #1405

Open
szsolt wants to merge 1 commit into
glzr-io:mainfrom
szsolt:fix/dpi-window-scaling
Open

fix(wm): correct window sizing across monitors with differing DPI#1405
szsolt wants to merge 1 commit into
glzr-io:mainfrom
szsolt:fix/dpi-window-scaling

Conversation

@szsolt

@szsolt szsolt commented Jul 9, 2026

Copy link
Copy Markdown

Problem

On a setup with monitors of differing DPI (e.g. 150% and 100% scaling), tiling windows get the wrong size when opened on, moved into, or moved out of a monitor — visibly overflowing their tile and leaking onto the neighbouring monitor. Very reproducible: opening a new window on a 150% workspace leaks it ~10% into the adjacent 100% workspace.

Root cause

A per-monitor-DPI-aware window that Windows places on a monitor with a different DPI receives WM_DPICHANGED and resizes itself to old_size × new_dpi/old_dpi (its OS-suggested rect). That self-resize arrives after GlazeWM's SetWindowPos, overriding the tile frame we set. GlazeWM never reconciled this:

  • On reposition, the single SetWindowPos ran while the window's DPI-awareness context still reflected the old monitor, so it was sized under the wrong scale.
  • For tiling windows, the location-change event handler ignored the uncommanded self-resize entirely (_ => {}), so nothing snapped the window back.

Fix

  • Reposition (platform_sync.rs): detect a window/monitor DPI mismatch — via a new GetDpiForWindow-backed dpi() platform method — and issue a corrective second SetWindowPos once Windows has updated the window's DPI.
  • Tiling self-resize (handle_window_moved_or_resized.rs): when a DPI adjustment is pending and the frame's size has diverged from the computed tile rect, reassert the size via a redraw and clear the flag. Gating on size divergence (not position) prevents position-only echoes emitted during cross-workspace re-tiling from consuming the flag prematurely; clearing on the real divergence means it fires at most once per DPI transition and can't feed a redraw loop.

Tests

Extracted the size-divergence predicate into a pure size_diverged helper and added unit tests mirroring the existing is_in_corner tests. The native DPI paths (GetDpiForWindow / SetWindowPos) are inherently Windows-only and not unit-testable off-platform.

Verification

Manually verified on a dual-monitor setup (150% + 100% scaling): windows opened on, and moved between, the workspaces are now sized correctly with no leak across the monitor boundary.

When a per-monitor-DPI-aware window is moved onto a monitor with a
different DPI, Windows sends it WM_DPICHANGED and the app resizes itself
to its old size scaled by the DPI ratio. That self-resize arrives after
GlazeWM's SetWindowPos, overriding the tile frame and overflowing onto
the neighbouring monitor.

Two paths caused the wrong size:

- Repositioning a window onto a different-DPI monitor sized it under the
  stale scale. Detect the window/monitor DPI mismatch (via a new
  GetDpiForWindow-backed `dpi()` platform method) and issue a corrective
  second SetWindowPos once Windows has updated the window's DPI.

- A tiling window's DPICHANGED self-resize was ignored by the
  location-change handler. Reassert the computed tile size via a redraw,
  gated on an actual size divergence so position-only echoes from
  re-tiling don't consume the pending-DPI flag prematurely.
@github-project-automation github-project-automation Bot moved this to 📬 Needs triage in glazewm Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 📬 Needs triage

Development

Successfully merging this pull request may close these issues.

1 participant