Dynamically size the fullscreen window; present design space via LETTERBOX
build-dev / nix-build (push) Successful in 42s
build-dev / nix-build (push) Successful in 42s
SDL_CreateWindow hardcoded a 1920x1080 backbuffer while SDL_WINDOW_FULLSCREEN
let compositors run a different output mode; the mismatched buffer reached
the panel unscaled — quarter-size in the top-left on 4K sets, cropped ("4x
too large") on smaller non-1080p panels under cage/Wayland. Logical
presentation never got to map the fixed design space because its target
(the backbuffer) was pinned to stale dimensions.
Size the fullscreen window from SDL_GetCurrentDisplayMode so the first
commit already matches scanout 1:1, switch logical presentation to LETTERBOX
(aspect-safe bars on non-16:9 panels), and route both through new
kDesignWidth/kDesignHeight constants. Harden getLayoutSize() to always write
its out params and null-check the renderer/render-window before use.
Log requested/window/pixel-buffer/render-output sizes at startup and on
every resize event so field units self-diagnose this bug class into
~/.local/radiantwave/log.txt. SDL_WINDOW_HIGH_PIXEL_DENSITY stays off on
purpose: kiosk panels are not HiDPI, and keeping the pixel buffer equal to
the display mode keeps compositor scanout unscaled.
This commit is contained in:
+39
-5
@@ -4,13 +4,47 @@
|
||||
|
||||
namespace radiantwave {
|
||||
|
||||
inline void getLayoutSize(SDL_Renderer* renderer, int* w, int* h)
|
||||
// Design resolution every page lays out against. The renderer's logical
|
||||
// presentation (LETTERBOX) scales this fixed space onto whatever
|
||||
// window/backbuffer the display actually provides.
|
||||
inline constexpr int kDesignWidth = 1920;
|
||||
inline constexpr int kDesignHeight = 1080;
|
||||
|
||||
// Returns the size pages should lay out against: the renderer's logical size
|
||||
// when presentation is active, otherwise the real window size. The output
|
||||
// params are always written — design constants are the last-resort fallback.
|
||||
inline void getLayoutSize(SDL_Renderer *renderer, int *w, int *h)
|
||||
{
|
||||
if (!w || !h)
|
||||
return;
|
||||
*w = kDesignWidth;
|
||||
*h = kDesignHeight;
|
||||
|
||||
if (!renderer)
|
||||
return;
|
||||
|
||||
SDL_RendererLogicalPresentation mode = SDL_LOGICAL_PRESENTATION_DISABLED;
|
||||
if (!SDL_GetRenderLogicalPresentation(renderer, w, h, &mode) ||
|
||||
mode == SDL_LOGICAL_PRESENTATION_DISABLED || *w <= 0 || *h <= 0)
|
||||
{
|
||||
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), w, h);
|
||||
int logicalW = 0;
|
||||
int logicalH = 0;
|
||||
if (SDL_GetRenderLogicalPresentation(renderer, &logicalW, &logicalH,
|
||||
&mode) &&
|
||||
mode != SDL_LOGICAL_PRESENTATION_DISABLED && logicalW > 0 &&
|
||||
logicalH > 0) {
|
||||
*w = logicalW;
|
||||
*h = logicalH;
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Window *window = SDL_GetRenderWindow(renderer);
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
int windowW = 0;
|
||||
int windowH = 0;
|
||||
SDL_GetWindowSize(window, &windowW, &windowH);
|
||||
if (windowW > 0 && windowH > 0) {
|
||||
*w = windowW;
|
||||
*h = windowH;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+61
-10
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "app_state.h"
|
||||
#include "home.h"
|
||||
#include "logical_size.h"
|
||||
#include "logger/logger.h"
|
||||
#include "provisioning.h"
|
||||
|
||||
@@ -32,11 +33,26 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) {
|
||||
|
||||
auto *state = new AppState{};
|
||||
|
||||
// SDL_WINDOW_FULLSCREEN fills the desktop's native resolution, so the
|
||||
// compositor never has to scale the moving scroller (scaling moving text
|
||||
// causes flicker).
|
||||
state->window =
|
||||
SDL_CreateWindow("RadiantWave", 1920, 1080, SDL_WINDOW_FULLSCREEN);
|
||||
// Size the fullscreen window from the display's actual mode so the very
|
||||
// first buffer commit already matches what the compositor scanouts 1:1.
|
||||
// Requesting a hardcoded size left the backbuffer at that size on
|
||||
// displays running a different mode, and frames reached the panel
|
||||
// unscaled (quarter-size on 4K, cropped on smaller panels).
|
||||
const SDL_DisplayMode *displayMode =
|
||||
SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay());
|
||||
int windowWidth =
|
||||
(displayMode && displayMode->w > 0) ? displayMode->w
|
||||
: radiantwave::kDesignWidth;
|
||||
int windowHeight =
|
||||
(displayMode && displayMode->h > 0) ? displayMode->h
|
||||
: radiantwave::kDesignHeight;
|
||||
|
||||
// Borderless desktop fullscreen: the compositor never has to scale the
|
||||
// moving scroller (scaling moving text causes flicker). Deliberately no
|
||||
// SDL_WINDOW_HIGH_PIXEL_DENSITY — kiosk panels are not HiDPI, so keeping
|
||||
// the pixel buffer equal to the display mode keeps scanout 1:1.
|
||||
state->window = SDL_CreateWindow("RadiantWave", windowWidth, windowHeight,
|
||||
SDL_WINDOW_FULLSCREEN);
|
||||
if (!state->window) {
|
||||
SDL_Log("Failed to create window: %s", SDL_GetError());
|
||||
delete state;
|
||||
@@ -59,16 +75,38 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) {
|
||||
std::string(SDL_GetError()) + ")");
|
||||
}
|
||||
|
||||
// Lock the renderer to a 1920x1080 logical coordinate space. All page
|
||||
// layout code already targets this resolution, so everything renders at
|
||||
// the correct size on any display — the GPU scales to native pixels.
|
||||
if (!SDL_SetRenderLogicalPresentation(state->renderer, 1920, 1080,
|
||||
SDL_LOGICAL_PRESENTATION_STRETCH)) {
|
||||
// All page layout targets the fixed design resolution; LETTERBOX maps it
|
||||
// onto the real window with aspect preserved (bars on non-16:9 panels).
|
||||
if (!SDL_SetRenderLogicalPresentation(state->renderer,
|
||||
radiantwave::kDesignWidth,
|
||||
radiantwave::kDesignHeight,
|
||||
SDL_LOGICAL_PRESENTATION_LETTERBOX)) {
|
||||
radiantwave::logger::Logger::instance().warning(
|
||||
"Graphics", "Could not set logical render size; layout may be "
|
||||
"wrong on HiDPI displays");
|
||||
}
|
||||
|
||||
{
|
||||
// Record how the requested mode, window, pixel buffer, and render
|
||||
// output actually compare — this is the evidence trail for
|
||||
// display-scaling problems on field units.
|
||||
int windowW = 0, windowH = 0;
|
||||
SDL_GetWindowSize(state->window, &windowW, &windowH);
|
||||
int pixelW = 0, pixelH = 0;
|
||||
SDL_GetWindowSizeInPixels(state->window, &pixelW, &pixelH);
|
||||
int outputW = 0, outputH = 0;
|
||||
SDL_GetRenderOutputSize(state->renderer, &outputW, &outputH);
|
||||
radiantwave::logger::Logger::instance().info(
|
||||
"Graphics",
|
||||
"Requested " + std::to_string(windowWidth) + "x" +
|
||||
std::to_string(windowHeight) + ", window " +
|
||||
std::to_string(windowW) + "x" + std::to_string(windowH) +
|
||||
", pixels " + std::to_string(pixelW) + "x" +
|
||||
std::to_string(pixelH) + ", render output " +
|
||||
std::to_string(outputW) + "x" + std::to_string(outputH) +
|
||||
", presentation LETTERBOX");
|
||||
}
|
||||
|
||||
SDL_HideCursor();
|
||||
|
||||
// Initialize configuration, database, and network manager
|
||||
@@ -157,6 +195,19 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
|
||||
state.quitRequested = true;
|
||||
}
|
||||
|
||||
if (event->type == SDL_EVENT_WINDOW_RESIZED) {
|
||||
radiantwave::logger::Logger::instance().info(
|
||||
"Graphics", "Window resized to " +
|
||||
std::to_string(event->window.data1) + "x" +
|
||||
std::to_string(event->window.data2));
|
||||
// Logical presentation persists across resizes; re-apply defensively
|
||||
// in case backend state was reset underneath us.
|
||||
SDL_SetRenderLogicalPresentation(state.renderer,
|
||||
radiantwave::kDesignWidth,
|
||||
radiantwave::kDesignHeight,
|
||||
SDL_LOGICAL_PRESENTATION_LETTERBOX);
|
||||
}
|
||||
|
||||
if (!state.keybinds.handleEvent(*event)) {
|
||||
state.pages.handleEvent(*event);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user