Skip to content

Fix Windows HiDPI scaling: declare PerMonitorV2 before mpv, map input to logical coords - #627

Open
vuksv wants to merge 1 commit into
andrewrabert:mainfrom
vuksv:fix/windows-hidpi-scaling
Open

Fix Windows HiDPI scaling: declare PerMonitorV2 before mpv, map input to logical coords#627
vuksv wants to merge 1 commit into
andrewrabert:mainfrom
vuksv:fix/windows-hidpi-scaling

Conversation

@vuksv

@vuksv vuksv commented Jul 21, 2026

Copy link
Copy Markdown

Fix Windows HiDPI scaling: declare PerMonitorV2 before mpv, map input to logical coords

Problem

On Windows with a scaled display (4K TVs/monitors at 125–300%), the Jellyfin
UI renders at the wrong scale — everything laid out tiny/half-size and packed
into part of the window — and mouse hit-testing is misaligned so buttons appear
not to work while scaled. At 100% everything is fine. Fixes #625.

Root cause

The process never declares DPI awareness. The exe ships no application
manifest
and nothing calls SetProcessDpiAwareness* at startup, so the
process launches DPI-unaware. The startup order is then:

  1. mpv creates its window while the process is still DPI-unaware, so
    GetDpiForWindow returns 96 and mpv reports display-hidpi-scale=1.
  2. CefInitialize runs afterwards and Chromium flips the whole process to
    PerMonitorV2 — too late. The window now really reports its true DPI, but
    mpv has already latched scale 1.

Because scale is authoritatively sourced from mpv's display-hidpi-scale, the
CEF overlay is sized for a physical-pixel logical viewport (e.g. 3840 instead
of 1920 at 200%), so the web UI is laid out at the wrong scale. Mouse
coordinates are separately affected (see fix #2).

Fix

Two small changes, both in src/windows:

  1. Declare Per-Monitor-V2 DPI awareness before mpv starts. win_early_init()
    runs before the mpv proxy, so a single
    SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
    there makes mpv detect the real monitor DPI and report the correct
    display-hidpi-scale. Using the API (not a manifest) keeps the change to one
    file and avoids adding a <supportedOS>/compatibility block; it requires
    Windows 10 1703+, which CEF already mandates, and silently no-ops on anything
    older (falling back to today's behaviour).

  2. Convert mouse input from physical to logical coordinates. CEF's
    off-screen renderer expects mouse events in logical/DIP space (matching its
    view rect), but the Win32 input child window reports physical pixels. This
    only worked before because the broken scale of 1.0 made physical == logical;
    once fix Program crashes due to failing to create Vulkan device #1 makes the scale correct, un-converted clicks would land at
    scale× the intended point. A small phys_to_logical() helper divides the
    incoming coordinates by the current display scale at every mouse dispatch
    site (move, buttons, both wheels).

Relation to #593

Same root cause as #593. This is a much smaller change: it does not need the
CEF frame cropping, idle-resize propagation, upward rounding, cross-thread DPI
juggling, or the layout=tv migration from that PR. Establishing awareness
process-wide at startup makes the per-thread DPI-context work unnecessary, and
common scales (125/150/200/300%) divide the 3840-wide panel evenly so no
rounding compensation is required. No manifest is added, so there is no
pre-Windows-10 <supportedOS> entry to remove.

Testing

  • Built and run on a 3840×2160 display.
  • Verified display-hidpi-scale now matches the OS scale (observed at 200% and
    300%); UI renders at correct size and fills the maximized window.
  • Display scale can be changed live without relaunching — the app rescales
    correctly (PerMonitorV2 WM_DPICHANGED handled via mpv).
  • cargo fmt --check and cargo clippy -D warnings pass.

Screenshots

repro_aware fixed_300

… to logical coords

The process shipped no manifest and set no DPI awareness, so it launched
DPI-unaware. mpv created its window and detected DPI 96 (display-hidpi-scale=1)
before CefInitialize later flipped the process to PerMonitorV2 — too late, so
the CEF UI was laid out at the wrong scale and mouse hit-testing was misaligned
on scaled displays.

Declare Per-Monitor-V2 awareness in win_early_init() (before mpv starts) so mpv
detects the real monitor DPI, and convert Win32 mouse coordinates from physical
to logical before dispatching to CEF's off-screen renderer.

Fixes andrewrabert#625.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Uncle-Tio

Copy link
Copy Markdown

Tested this PR on Windows 11, 3440×1440 display at 125% scaling, fixes the maximize bug for me.

The startup log shows display-hidpi-scale=1.25, and the CEF layer syncs to logical=2752x1080 physical=3440x1351, the window fills the screen on launch and the web UI is laid out at the correct scale.

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

Labels

None yet

Projects

None yet

2 participants