Skip to content

Experiment: explicit user-controlled pixel scale (no HiDPI automagic) - #579

Draft
pusewicz wants to merge 7 commits into
RandyGaul:masterfrom
pusewicz:hidpi-explicit-scale
Draft

Experiment: explicit user-controlled pixel scale (no HiDPI automagic)#579
pusewicz wants to merge 7 commits into
RandyGaul:masterfrom
pusewicz:hidpi-explicit-scale

Conversation

@pusewicz

@pusewicz pusewicz commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Working demo of @bullno1's proposal from the #575 review thread (including the follow-up naming refinements): remove all event-driven HiDPI behavior and make pixel scale a plain user-controlled value. Draft on purpose — it exists to give that design discussion something concrete to poke at.

  • Startup is the only automatic step: canvas at window_size * display_scale, default 2d projection from the logical window size, both set once. Resizes and scale changes only raise flags (cf_app_was_resized / cf_app_display_scale_was_changed).
  • Terminology follows the SDL functions underneath, per the thread. cf_app_get_display_scale (+_was_changed) is SDL_GetWindowDisplayScale — what the OS wants, consistent across platforms; the dpi names are gone. cf_app_set_pixel_scale is how fonts/shapes scale — arbitrary, or following the reported display scale; it does not touch the canvas (cf_app_set_canvas_size is separate). cf_app_update_display(scale) is the all-in-one helper bringing canvas, projection, and pixel scale together, usable on both resize and scale change — the w/h params are omitted since the window size is always current by the time user code reacts.
  • Window size and mouse coordinates are normalized to logical points at the SDL boundary via SDL_GetDisplayContentScale (window creation, cf_app_set_size, resize events, mouse events; touch derives from window size). This is a no-op on macOS (content scale 1.0) and needs verification on Windows/X11 at >100% scaling — I can only test on a Mac.
  • cf_draw_projection becomes truly sticky and cf_app_set_canvas_size persistent. The half-size regression Fix HiDPI half-size regression: default 2d projection is logical points #575 fixes dies structurally — no code path can rebuild the projection in pixel units — and all of Fix HiDPI half-size regression: default 2d projection is logical points #575's deferral/guard machinery becomes unnecessary. 351/351 tests on a 2x display (master: 319/339, the 20 failures being that regression).
  • The hidpi sample carries the recipe (N follows the display scale, 1/2/4 force scales — forcing a value is also how you test HiDPI on a normal monitor); the hidpi topic doc is rewritten; test_hidpi covers the new contracts.

Cost, stated plainly: breaking change. 44 of 76 samples use resizable windows and each needs the two-line recipe (deliberately not yet updated); canvas_modes.c needs a rewrite. NO_HIGH_DPI_BIT is kept: it is the only handle on the creation-time 1x swapchain (memory + final-blit bandwidth), which cf_app_update_display(1) cannot replicate.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LHq3Cx7xcjAQhji9m1ADJn

Comment thread docs/topics/hidpi.md Outdated
Comment thread samples/hidpi.c
Comment thread docs/topics/hidpi.md Outdated
@pusewicz
pusewicz force-pushed the hidpi-explicit-scale branch from b90e4ae to d62ec37 Compare August 17, 2026 21:59
pusewicz added a commit to pusewicz/cute_framework that referenced this pull request Aug 17, 2026
cf_app_get_dpi_scale / cf_app_dpi_scale_was_changed read like the OS's
vague "suggested UI content scale" concept. The value is actually
SDL_GetWindowDisplayScale -- the OS's points-to-pixels conversion for
the window's display -- so name it that. Hard rename, no compat alias.

Also drops CF_App::dpi_scale_prev, which was written once at init and
never read.

First slice of the RandyGaul#579 split.
pusewicz and others added 2 commits August 21, 2026 09:23
…ion)

Experiment, not a proposal: remove all event-driven HiDPI automagic in
favor of user-controlled state, to see what the model looks like.

- cf_app_set_pixel_scale: pixel scale is now a plain user value (AA +
  glyph density); the canvas is resized explicitly via set_canvas_size.
- cf_app_pixel_scale_was_changed + cf_app_get_natural_pixel_scale; a
  density change only raises dpi_scale_was_changed, never applies.
- Startup is the single automatic step: canvas at natural density,
  default 2d projection from the logical window size -- both set once.
- cf_draw_projection is now truly sticky: window resizes, canvas
  recreation, and MSAA changes never touch the projection.
- hidpi sample carries the copy-paste resize/density recipe and forced
  1x/2x/4x switching; test_hidpi reworked for the new contracts.

350/350 tests pass on a 2x display (master baseline: 319/339 -- the 20
Retina failures were the half-size regression, which dies structurally
here since nothing ever rebuilds the projection in pixel units).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHq3Cx7xcjAQhji9m1ADJn
bullno1's follow-up suggestion on the PR RandyGaul#575 thread: package the
recurring three-step reaction (set pixel scale, resize canvas to
window * scale, rebuild the logical-points projection) into a single
public helper. The window size is read internally rather than passed
in -- it is always current by the time user code reacts to an event,
and passing it would only invite stale values.

The sample, the shared test-app sweep, and the hidpi tests all shrink
to one call each, which was the tell that the helper deserved to be
public API.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHq3Cx7xcjAQhji9m1ADJn
@pusewicz
pusewicz force-pushed the hidpi-explicit-scale branch from d62ec37 to 1654603 Compare August 21, 2026 13:35
pusewicz and others added 5 commits August 21, 2026 15:38
Terminology follows the SDL functions underneath, per the PR thread:

- cf_app_get_display_scale / cf_app_display_scale_was_changed (backed
  by SDL_GetWindowDisplayScale): what the OS wants, point-to-pixel,
  consistent across platforms. Replaces both the dpi_scale pair (term
  dropped) and cf_app_get_natural_pixel_scale (SDL_GetWindowPixelDensity
  is 1.0 on Windows/X11 where the scale lives in content-scale, so
  display scale is the right value to follow everywhere).
- cf_app_update_display(scale): renamed from cf_app_apply_pixel_scale.
- pixel_scale keeps its name: how fonts/shapes are scaled, arbitrary or
  following the reported display scale.
- Initial pixel_scale now comes from display scale (identical on Mac,
  correct on Windows where density alone under-reports).

Window size and mouse coordinates are now normalized to logical points
at the SDL boundary via SDL_GetDisplayContentScale (window create,
set_size, resize events, mouse events; touch already derives from
app->w/h). A no-op on macOS where content scale is 1.0 -- the
normalization needs verification on Windows and X11 at >100% scaling.

351/351 tests, docsparser clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHq3Cx7xcjAQhji9m1ADJn
cf_destroy_draw freed s_draw but never nulled it (the make-error path
did), so cf_app_set_pixel_scale on a NO_GFX app created after a gfx
app's destruction passed the hook's s_draw guard with a dangling
pointer -- an instant access violation on Windows, silently-readable
freed memory on POSIX.

The synthetic WINDOW_RESIZED event in test_hidpi pushed 500 raw, but
SDL resize events carry raw window coordinates which CF now divides by
the display content scale -- not 1.0 on the X11 CI runners. Push
points * content_scale like a real event, and compare against the
handler's exact round-trip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHq3Cx7xcjAQhji9m1ADJn
…ic event

On X11 the ConfigureNotify confirming test_make_app's SDL_SetWindowSize
can arrive late and land in the same pump as the synthetic resize event,
stomping app->w after it. Drain it first -- the same settling dance the
one-shot test used on the previous branch, lost in the rewrite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHq3Cx7xcjAQhji9m1ADJn
Shows raw screen coords next to cf_screen_to_world's translation, so
the HiDPI mouse mapping can be eyeballed against known shape positions
at any forced pixel scale.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The pixel scale only ever changes when user code calls
cf_app_set_pixel_scale, so this flag just echoed the caller's own
action back one frame later -- no OS event ever set it. The real
event remains cf_app_display_scale_was_changed, and anyone wanting
change detection can compare cf_app_get_pixel_scale against a cached
value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S4anUDPNkAio27kQbWAqrn
@pusewicz
pusewicz force-pushed the hidpi-explicit-scale branch from bf973ba to 49a6b0c Compare August 21, 2026 13:38
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.

2 participants