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:
+41
-7
@@ -4,14 +4,48 @@
|
|||||||
|
|
||||||
namespace radiantwave {
|
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)
|
||||||
{
|
{
|
||||||
SDL_RendererLogicalPresentation mode = SDL_LOGICAL_PRESENTATION_DISABLED;
|
if (!w || !h)
|
||||||
if (!SDL_GetRenderLogicalPresentation(renderer, w, h, &mode) ||
|
return;
|
||||||
mode == SDL_LOGICAL_PRESENTATION_DISABLED || *w <= 0 || *h <= 0)
|
*w = kDesignWidth;
|
||||||
{
|
*h = kDesignHeight;
|
||||||
SDL_GetWindowSize(SDL_GetRenderWindow(renderer), w, h);
|
|
||||||
}
|
if (!renderer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SDL_RendererLogicalPresentation mode = SDL_LOGICAL_PRESENTATION_DISABLED;
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace radiantwave
|
} // namespace radiantwave
|
||||||
|
|||||||
+61
-10
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "app_state.h"
|
#include "app_state.h"
|
||||||
#include "home.h"
|
#include "home.h"
|
||||||
|
#include "logical_size.h"
|
||||||
#include "logger/logger.h"
|
#include "logger/logger.h"
|
||||||
#include "provisioning.h"
|
#include "provisioning.h"
|
||||||
|
|
||||||
@@ -32,11 +33,26 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) {
|
|||||||
|
|
||||||
auto *state = new AppState{};
|
auto *state = new AppState{};
|
||||||
|
|
||||||
// SDL_WINDOW_FULLSCREEN fills the desktop's native resolution, so the
|
// Size the fullscreen window from the display's actual mode so the very
|
||||||
// compositor never has to scale the moving scroller (scaling moving text
|
// first buffer commit already matches what the compositor scanouts 1:1.
|
||||||
// causes flicker).
|
// Requesting a hardcoded size left the backbuffer at that size on
|
||||||
state->window =
|
// displays running a different mode, and frames reached the panel
|
||||||
SDL_CreateWindow("RadiantWave", 1920, 1080, SDL_WINDOW_FULLSCREEN);
|
// 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) {
|
if (!state->window) {
|
||||||
SDL_Log("Failed to create window: %s", SDL_GetError());
|
SDL_Log("Failed to create window: %s", SDL_GetError());
|
||||||
delete state;
|
delete state;
|
||||||
@@ -59,16 +75,38 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) {
|
|||||||
std::string(SDL_GetError()) + ")");
|
std::string(SDL_GetError()) + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock the renderer to a 1920x1080 logical coordinate space. All page
|
// All page layout targets the fixed design resolution; LETTERBOX maps it
|
||||||
// layout code already targets this resolution, so everything renders at
|
// onto the real window with aspect preserved (bars on non-16:9 panels).
|
||||||
// the correct size on any display — the GPU scales to native pixels.
|
if (!SDL_SetRenderLogicalPresentation(state->renderer,
|
||||||
if (!SDL_SetRenderLogicalPresentation(state->renderer, 1920, 1080,
|
radiantwave::kDesignWidth,
|
||||||
SDL_LOGICAL_PRESENTATION_STRETCH)) {
|
radiantwave::kDesignHeight,
|
||||||
|
SDL_LOGICAL_PRESENTATION_LETTERBOX)) {
|
||||||
radiantwave::logger::Logger::instance().warning(
|
radiantwave::logger::Logger::instance().warning(
|
||||||
"Graphics", "Could not set logical render size; layout may be "
|
"Graphics", "Could not set logical render size; layout may be "
|
||||||
"wrong on HiDPI displays");
|
"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();
|
SDL_HideCursor();
|
||||||
|
|
||||||
// Initialize configuration, database, and network manager
|
// Initialize configuration, database, and network manager
|
||||||
@@ -157,6 +195,19 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
|
|||||||
state.quitRequested = true;
|
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)) {
|
if (!state.keybinds.handleEvent(*event)) {
|
||||||
state.pages.handleEvent(*event);
|
state.pages.handleEvent(*event);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user