Skip to content

fix(windows): scale and frame the Actions Ring correctly - #1186

Open
zrides wants to merge 2 commits into
AprilNEA:masterfrom
zrides:fix/windows-action-ring-dpi
Open

fix(windows): scale and frame the Actions Ring correctly#1186
zrides wants to merge 2 commits into
AprilNEA:masterfrom
zrides:fix/windows-action-ring-dpi

Conversation

@zrides

@zrides zrides commented Aug 31, 2026

Copy link
Copy Markdown

Summary

On Windows the Actions Ring opened at the centre of the primary display instead
of at the cursor whenever display scaling was above 100% (#1027).

The overlay had no native display lookup on Windows, so ring_window_options
fell through to the GPUI display list and compared the hook's physical
cursor position against logical display bounds. Above 100% scaling the
physical point matches no display, the lookup falls back to the primary display,
and the ring is placed at that display's centre — the "opens at a fixed
position" symptom in the issue.

The fix resolves the cursor's monitor with MonitorFromPoint and converts the
cursor and the monitor bounds with that monitor's own effective DPI. That is
exactly the conversion GPUI's Windows backend applies when it builds its display
bounds (logical_point(rcMonitor.left, top, scale) with
scale = GetDpiForMonitor(MDT_EFFECTIVE_DPI) / 96), so the overlay and GPUI now
describe one coordinate space instead of two.

A second commit removes the DWM frame that Windows drew around the transparent
ring window.

Changes

crates/openlogi-overlay

  • platform.rs becomes a per-OS module tree (macos.rs, windows.rs,
    other.rs) behind a facade owning the shared CursorPlacement type. A second
    platform turns the interleaved cfg arms into the shape the workspace rules
    already reject.
  • New Windows placement path: MonitorFromPoint + GetMonitorInfoW +
    GetDpiForMonitor(MDT_EFFECTIVE_DPI). It returns None — and so falls back
    to the primary display — for a point no monitor covers, a failed query, or
    anisotropic DPI, rather than guessing.
  • scaled_cursor_placement is kept free of Win32, so the 100/125/150% and
    negative-origin monitor cases are unit-tested on every host.
  • configure_windows() becomes configure_window(&Window), applying DWM
    NCRENDERING_POLICY, BORDER_COLOR and WINDOW_CORNER_PREFERENCE to the
    GPUI HWND so the transparent ring no longer sits inside a shadowed, rounded
    rectangle. Each attribute is set independently and a rejection is logged, not
    propagated: they are presentation hints, and a Windows build that does not
    recognise one should still get a working ring.
  • macOS keeps its display-relative CoreGraphics lookup unchanged.
  • New Windows-only dependencies: raw-window-handle, and windows-sys with the
    Win32_Foundation, Win32_Graphics_Dwm, Win32_Graphics_Gdi and
    Win32_UI_HiDpi features.

One macOS behaviour change

When the native display lookup fails, macOS no longer falls back to matching the
raw global cursor point against GPUI's display list. That point is not in GPUI's
coordinate space, so any placement derived from it was accidental; the ring now
centres on the primary display, matching the new Windows path. Flagging it
because it is a macOS change inside a Windows fix.

Testing

Full local gate on Windows 11 (x86_64-pc-windows-msvc), RUSTFLAGS=-D warnings:

  • cargo fmt --all -- --check — green
  • cargo clippy --workspace --all-targets -- -D warnings — green
  • cargo test --workspace — one pre-existing failure, unrelated to this branch
  • RUSTDOCFLAGS=-D warnings cargo doc --workspace --no-deps --document-private-items --exclude openlogi-ui --exclude openlogi-desktop --exclude openlogi-overlay --exclude openlogi-agent — pre-existing failure, unrelated to this branch

cargo test -p openlogi-overlay covers the new conversion directly: identity at
100%, 125% and 150% scaling, negative monitor origins preserved, and an invalid
scale factor falling through to the primary-display path.

The two failures above reproduce on master from a Windows checkout and are
untouched by this branch. Neither is visible in CI:

  • xtask's ci_yml_runs_what_this_runner_runs joins ci.yml line
    continuations by stripping a backslash followed by a bare LF, so a checkout
    with CRLF endings (the Git for Windows default, and there is no
    .gitattributes here) fails to match every multi-line run:.
  • openlogi-permissions links to PermissionStatus::Unknown in its crate-level
    docs, but that type is gated to macOS and Linux, so the link resolves nowhere
    under a Windows rustdoc run. The CI rustdoc job runs on Linux.

Both were verified green with local one-line fixes applied, confirming they are
the only two failures and that this branch adds none.

Not run locally: the macOS and Linux CI lanes, MSRV, cargo-deny, typos, shell
lint, and wasm — this host cannot reproduce them. cargo xtask ci cannot
complete on Windows either: the publish closure job shells out to
cargo xtask release check-publish, which cannot relink the xtask binary under
target/debug while that binary is the running process.

Hardware verification

Verified on Windows 11 at 125% display scaling: the Actions Ring opens centred
on the cursor, and the shadow, border and rounded corner previously drawn around
the transparent ring window are gone.

Not exercised: a multi-monitor setup where the ring's target display has a
different scale factor from the display a new window default-places on. GPUI
converts the requested window bounds with GetDpiForWindow sampled while the
window is still at CW_USEDEFAULT rather than with the target display's scale
factor, so that mixed-DPI case is worth a look from anyone who has the setup.

Fixes #1027

Zachary Rideout added 2 commits August 31, 2026 02:34
Windows had no native display lookup, so `ring_window_options` fell through to
the GPUI display list and compared the hook's physical cursor position against
logical display bounds. Above 100% scaling the physical point matches no
display, the lookup falls back to the primary display, and the ring opens at
that display's centre instead of at the cursor.

Resolve the cursor's monitor with `MonitorFromPoint` and convert the cursor and
the monitor bounds using that monitor's own effective DPI, which is exactly the
conversion GPUI's Windows backend applies when it builds its display bounds, so
the two describe the same coordinate space. The conversion is kept free of
Win32 so the scaling and negative-origin cases are covered on every host.

`platform.rs` becomes a per-OS module tree: a second platform turns the
interleaved cfg arms into the shape the workspace rules already reject. macOS
keeps its display-relative CoreGraphics lookup unchanged.

One macOS behaviour does change. When the native lookup fails, macOS no longer
reuses the raw global cursor point against GPUI's display list: that point is
not in GPUI's coordinate space, so placing a window from it was only ever
accidental. The ring now centres on the primary display instead.

Refs AprilNEA#1027
The ring window is transparent apart from the ring itself, but DWM still drew
its default frame around it: a drop shadow, a border colour and rounded
corners, so the ring rendered inside a visible rectangle.

Disable non-client rendering, clear the border colour and opt out of corner
rounding on the GPUI HWND. Each attribute is set independently and a rejection
is logged rather than propagated: these are presentation hints, and a Windows
build that does not recognise one should still get a working ring.
@zrides
zrides requested a review from AprilNEA as a code owner August 31, 2026 07:32
@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds native Windows monitor lookup and effective-DPI conversion so the Actions Ring uses the same logical coordinate space as GPUI, while also removing native framing from the transparent window.

  • Splits overlay platform behavior into macOS, Windows, and fallback modules.
  • Resolves the Windows cursor monitor and converts its physical geometry using effective DPI.
  • Applies independent DWM attributes to disable non-client rendering, borders, and rounded corners.
  • Preserves Linux fallback placement and changes failed native lookup on Windows and macOS to center on the primary display.
  • Adds host-independent tests for scaling, negative monitor origins, and invalid geometry.

Confidence Score: 5/5

The PR appears safe to merge based on the reviewed changes, with the explicitly acknowledged mixed-DPI window-creation behavior remaining an unverified hardware scenario rather than an established defect.

The Windows coordinate conversion matches the intended cursor-monitor geometry model, platform fallbacks remain defined, and no concrete changed-code failure was established from the available repository evidence.

Important Files Changed

Filename Overview
crates/openlogi-overlay/src/platform/windows.rs Adds Windows monitor resolution, effective-DPI conversion, HWND extraction, and best-effort DWM frame configuration; no publishable defect was established.
crates/openlogi-overlay/src/platform.rs Introduces the shared placement model, platform dispatch, geometry validation, and portable conversion tests.
crates/openlogi-overlay/src/ring.rs Uses native placement when available and safely falls back according to each platform’s coordinate-space guarantees.
crates/openlogi-overlay/src/main.rs Applies native window policy directly to each newly created GPUI window.
crates/openlogi-overlay/src/platform/macos.rs Preserves native display-relative placement and existing application, window, and click-away policies in the new module structure.
crates/openlogi-overlay/Cargo.toml Adds narrowly gated Windows dependencies and required Win32 feature sets.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Native cursor position] --> B{Platform lookup succeeds?}
  B -->|Windows| C[Resolve HMONITOR and effective DPI]
  B -->|macOS| D[Resolve CoreGraphics display]
  B -->|Linux fallback| E[Match GPUI display bounds]
  C --> F[Convert physical global geometry to logical geometry]
  D --> G[Convert cursor to display-relative geometry]
  F --> H[Clamp ring within cursor display]
  G --> H
  E --> H
  B -->|Windows or macOS failure| I[Center on primary display]
  I --> H
  H --> J[Create transparent Actions Ring window]
  J --> K[Apply native window-frame policy]
Loading

Reviews (1): Last reviewed commit: "fix(windows): suppress action ring windo..." | Re-trigger Greptile

@davidbudnick davidbudnick added type: bug Something is broken or behaves incorrectly area: gui Graphical user interface platform: windows Windows-specific issue labels Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: gui Graphical user interface platform: windows Windows-specific issue type: bug Something is broken or behaves incorrectly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Windows + 150% DPI scaling: Actions Ring does not follow cursor (opens at fixed position)

2 participants