Field report (0.24.0-1, CachyOS / KDE Plasma 6.7.4 / Wayland, 2560×1600@165 laptop panel at 150 % scaling): "Native resolution" negotiated 1706×1066 instead of the panel's real mode, and the stream looked blurry. At 100 % scaling it was sharp, but the laptop UI was unusably small.
Two independent defects, and they stack — which is why the reporter's own workaround of forcing 2560×1600 by hand still left the image soft until they also set SDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY=1.
1. Native resolved to logical points
crates/pf-presenter/src/run.rs is the only place the client ever asks what "Native" means, and it read the mode raw:
Display::get_mode() is SDL_GetDesktopDisplayMode, and an SDL3 display mode is in screen coordinates, not pixels — the ratio to real pixels is a separate field, pixel_density, which this never read. SDL's Wayland backend fills the desktop mode with the compositor's logical size on purpose (SDL_waylandvideo.c:1023) and stashes the scale there, unlessSDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY=1 flips it to report native pixels. That is exactly why the reporter's env var changed the picture.
The chain to the logged number: KWin advertises the output as 1707×1067 points (matching what KDE's own panel shows) → SDL hands that back → clients/session/src/main.rs:295 substitutes it for the stored width: 0, height: 0 → render_scale::apply even-floors both odd axes (the host's validate_dimensions rejects odd) → 1706×1066.
Multiplying by pixel_density recovers 2560×1600 exactly, because SDL derives the density as the output's exact pixels-per-point ratio rather than the nominal 1.5.
This is inert off Wayland. SDL never sets a density on the X11 or Windows backends, and SDL_video.c:821 normalizes the unset 0.0 up to 1.0. WIN_GetWindowSizeInPixels returns the real client rect in device pixels. The bug needed a compositor doing fractional scaling. (The Apple client was already correct — it resolves Native from UIScreen.nativeBounds / backingScaleFactor.)
2. The window was never high-DPI, so presentation blurred anyway
The SDL window was built as b.resizable().vulkan() with no HIGH_PIXEL_DENSITY. Without that flag SDL leaves the Wayland surface at buffer scale 1 (SDL_waylandwindow.c:58), so SDL_GetWindowSizeInPixels returns the logical size and the Vulkan swapchain gets built at 1707×1067 — which KWin then upscales to the glass. Even a correct 2560×1600 stream was resampled down and back up. The same flaw silently shrank Match window, which asks the host for size_in_pixels().
The surrounding code was already written for pixels ≠ points: the swapchain (vk/reconfig.rs:63), match-window, and pointer mapping all use size_in_pixels(), while window-size persistence uses logical size(). The flag just makes those two stop being the same number. display_scale() starts reporting 1.5 into a swapchain that is 1.5× larger, so the OSD keeps the on-screen size it already had.
Also
Only an Err from SDL reached the 1920×1080 fallback, so a display reporting a 0×0 mode sent a 0×0 request. Now a zero-sized mode falls back too.
Verification
On home-worker-5 (CachyOS — the reporter's own distro, real SDL 3.4.14):
cargo clippy --workspace --all-targets --locked -- -D warnings — clean (full CI parity, not just the touched crate)
cargo test -p pf-presenter — 18/18 pass, three of them new
cargo fmt --all --check — clean
The new tests pin the field-reported numbers directly: native_mode(1707, 1067, 2560.0/1707.0, 165.0) must be 2560×1600@165, render_scale::apply on it must stay 2560×1600, and the pre-fix apply(1707, 1067, …) == (1706, 1066) is kept alongside so the regression stays legible. Two more cover density 1.0 / 2.0 staying inert-or-exact and a nonsense density (0.0, negative, NaN, ∞) falling back to 1× rather than zeroing the mode.
Not verified on the reporter's glass — worth a confirmation run at 150 % before this is called closed.
Field report (0.24.0-1, CachyOS / KDE Plasma 6.7.4 / Wayland, 2560×1600@165 laptop panel at **150 % scaling**): "Native resolution" negotiated **1706×1066** instead of the panel's real mode, and the stream looked blurry. At 100 % scaling it was sharp, but the laptop UI was unusably small.
Two independent defects, and they stack — which is why the reporter's own workaround of forcing 2560×1600 by hand *still* left the image soft until they also set `SDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY=1`.
## 1. Native resolved to logical points
`crates/pf-presenter/src/run.rs` is the only place the client ever asks what "Native" means, and it read the mode raw:
```rust
.map(|m| Mode { width: m.w.max(0) as u32, height: m.h.max(0) as u32, … })
```
`Display::get_mode()` is `SDL_GetDesktopDisplayMode`, and an SDL3 display mode is in **screen coordinates**, not pixels — the ratio to real pixels is a separate field, `pixel_density`, which this never read. SDL's Wayland backend fills the desktop mode with the compositor's logical size on purpose (`SDL_waylandvideo.c:1023`) and stashes the scale there, *unless* `SDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY=1` flips it to report native pixels. That is exactly why the reporter's env var changed the picture.
The chain to the logged number: KWin advertises the output as 1707×1067 points (matching what KDE's own panel shows) → SDL hands that back → `clients/session/src/main.rs:295` substitutes it for the stored `width: 0, height: 0` → `render_scale::apply` even-floors both odd axes (the host's `validate_dimensions` rejects odd) → **1706×1066**.
Multiplying by `pixel_density` recovers 2560×1600 *exactly*, because SDL derives the density as the output's exact pixels-per-point ratio rather than the nominal 1.5.
**This is inert off Wayland.** SDL never sets a density on the X11 or Windows backends, and `SDL_video.c:821` normalizes the unset `0.0` up to `1.0`. `WIN_GetWindowSizeInPixels` returns the real client rect in device pixels. The bug needed a compositor doing *fractional* scaling. (The Apple client was already correct — it resolves Native from `UIScreen.nativeBounds` / `backingScaleFactor`.)
## 2. The window was never high-DPI, so presentation blurred anyway
The SDL window was built as `b.resizable().vulkan()` with no `HIGH_PIXEL_DENSITY`. Without that flag SDL leaves the Wayland surface at buffer scale 1 (`SDL_waylandwindow.c:58`), so `SDL_GetWindowSizeInPixels` returns the *logical* size and the Vulkan swapchain gets built at 1707×1067 — which KWin then upscales to the glass. Even a correct 2560×1600 stream was resampled down and back up. The same flaw silently shrank **Match window**, which asks the host for `size_in_pixels()`.
The surrounding code was already written for pixels ≠ points: the swapchain (`vk/reconfig.rs:63`), match-window, and pointer mapping all use `size_in_pixels()`, while window-size persistence uses logical `size()`. The flag just makes those two stop being the same number. `display_scale()` starts reporting 1.5 into a swapchain that is 1.5× larger, so the OSD keeps the on-screen size it already had.
## Also
Only an `Err` from SDL reached the 1920×1080 fallback, so a display reporting a 0×0 mode sent a 0×0 request. Now a zero-sized mode falls back too.
## Verification
On **home-worker-5** (CachyOS — the reporter's own distro, real SDL 3.4.14):
- `cargo clippy --workspace --all-targets --locked -- -D warnings` — clean (full CI parity, not just the touched crate)
- `cargo test -p pf-presenter` — 18/18 pass, three of them new
- `cargo fmt --all --check` — clean
The new tests pin the field-reported numbers directly: `native_mode(1707, 1067, 2560.0/1707.0, 165.0)` must be 2560×1600@165, `render_scale::apply` on it must stay 2560×1600, and the pre-fix `apply(1707, 1067, …) == (1706, 1066)` is kept alongside so the regression stays legible. Two more cover density 1.0 / 2.0 staying inert-or-exact and a nonsense density (`0.0`, negative, NaN, ∞) falling back to 1× rather than zeroing the mode.
Not verified on the reporter's glass — worth a confirmation run at 150 % before this is called closed.
A CachyOS / KDE Plasma 6.7.4 Wayland client with its 2560x1600@165 laptop panel at
150 % scaling negotiated 1706x1066 for "Native resolution" and streamed a visibly
blurry image. Two independent defects, and they stack — which is why forcing the mode
to 2560x1600 by hand did not fully fix it either.
1. `SDL_GetDesktopDisplayMode` reports a mode in SCREEN COORDINATES and hands the
pixels-per-point ratio back separately as `pixel_density`. We read `m.w`/`m.h` raw.
KDE advertises that panel as 1707x1067 points with a density of ~1.4997,
`render_scale::apply` even-floors both odd axes, and 1706x1066 goes on the wire —
exactly the mode in the reporter's handshake log. Multiplying by the density
recovers 2560x1600 to the pixel, because SDL derives it as the output's exact
pixels/points ratio. On X11 and Windows SDL never sets a density and `SDL_video.c`
normalizes the unset 0.0 to 1.0, so this is inert there: the bug needed a
compositor doing FRACTIONAL scaling.
2. The SDL window was created without `HIGH_PIXEL_DENSITY`, so the Wayland surface
stayed at buffer scale 1 — the Vulkan swapchain was built at 1707x1067 and KWin
upscaled it to the glass. Even a correct 2560x1600 stream was resampled down and
then back up. The same flaw silently shrank "Match window", which asks the host for
`size_in_pixels()`. The reporter's `SDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY=1` workaround
is this same fix applied from outside SDL, which is why it helped.
The surrounding code was already written for pixels != points — the swapchain,
match-window and pointer mapping all read `size_in_pixels()` while window-size
persistence reads logical `size()` — so the flag only makes those two stop being the
same number. `display_scale()` starts reporting 1.5 into a swapchain that is 1.5x
larger, leaving the OSD the size it already was.
Also closes a smaller hole on the way past: only an `Err` from SDL reached the
1920x1080 fallback, so a display that reported a 0x0 mode sent a 0x0 request.
Verified on home-worker-5 (CachyOS — the reporter's distro, real SDL 3.4.14):
`cargo clippy --all-targets -p pf-presenter -- -D warnings` clean and 18/18
pf-presenter tests pass, three of them new and pinned to the field-reported numbers.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Field report (0.24.0-1, CachyOS / KDE Plasma 6.7.4 / Wayland, 2560×1600@165 laptop panel at 150 % scaling): "Native resolution" negotiated 1706×1066 instead of the panel's real mode, and the stream looked blurry. At 100 % scaling it was sharp, but the laptop UI was unusably small.
Two independent defects, and they stack — which is why the reporter's own workaround of forcing 2560×1600 by hand still left the image soft until they also set
SDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY=1.1. Native resolved to logical points
crates/pf-presenter/src/run.rsis the only place the client ever asks what "Native" means, and it read the mode raw:Display::get_mode()isSDL_GetDesktopDisplayMode, and an SDL3 display mode is in screen coordinates, not pixels — the ratio to real pixels is a separate field,pixel_density, which this never read. SDL's Wayland backend fills the desktop mode with the compositor's logical size on purpose (SDL_waylandvideo.c:1023) and stashes the scale there, unlessSDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY=1flips it to report native pixels. That is exactly why the reporter's env var changed the picture.The chain to the logged number: KWin advertises the output as 1707×1067 points (matching what KDE's own panel shows) → SDL hands that back →
clients/session/src/main.rs:295substitutes it for the storedwidth: 0, height: 0→render_scale::applyeven-floors both odd axes (the host'svalidate_dimensionsrejects odd) → 1706×1066.Multiplying by
pixel_densityrecovers 2560×1600 exactly, because SDL derives the density as the output's exact pixels-per-point ratio rather than the nominal 1.5.This is inert off Wayland. SDL never sets a density on the X11 or Windows backends, and
SDL_video.c:821normalizes the unset0.0up to1.0.WIN_GetWindowSizeInPixelsreturns the real client rect in device pixels. The bug needed a compositor doing fractional scaling. (The Apple client was already correct — it resolves Native fromUIScreen.nativeBounds/backingScaleFactor.)2. The window was never high-DPI, so presentation blurred anyway
The SDL window was built as
b.resizable().vulkan()with noHIGH_PIXEL_DENSITY. Without that flag SDL leaves the Wayland surface at buffer scale 1 (SDL_waylandwindow.c:58), soSDL_GetWindowSizeInPixelsreturns the logical size and the Vulkan swapchain gets built at 1707×1067 — which KWin then upscales to the glass. Even a correct 2560×1600 stream was resampled down and back up. The same flaw silently shrank Match window, which asks the host forsize_in_pixels().The surrounding code was already written for pixels ≠ points: the swapchain (
vk/reconfig.rs:63), match-window, and pointer mapping all usesize_in_pixels(), while window-size persistence uses logicalsize(). The flag just makes those two stop being the same number.display_scale()starts reporting 1.5 into a swapchain that is 1.5× larger, so the OSD keeps the on-screen size it already had.Also
Only an
Errfrom SDL reached the 1920×1080 fallback, so a display reporting a 0×0 mode sent a 0×0 request. Now a zero-sized mode falls back too.Verification
On home-worker-5 (CachyOS — the reporter's own distro, real SDL 3.4.14):
cargo clippy --workspace --all-targets --locked -- -D warnings— clean (full CI parity, not just the touched crate)cargo test -p pf-presenter— 18/18 pass, three of them newcargo fmt --all --check— cleanThe new tests pin the field-reported numbers directly:
native_mode(1707, 1067, 2560.0/1707.0, 165.0)must be 2560×1600@165,render_scale::applyon it must stay 2560×1600, and the pre-fixapply(1707, 1067, …) == (1706, 1066)is kept alongside so the regression stays legible. Two more cover density 1.0 / 2.0 staying inert-or-exact and a nonsense density (0.0, negative, NaN, ∞) falling back to 1× rather than zeroing the mode.Not verified on the reporter's glass — worth a confirmation run at 150 % before this is called closed.