Merge remote-tracking branch 'origin/master' into split/pr169-presentation-bezel

# Conflicts:
#	runtime/runtime.cmake
This commit is contained in:
Matthew Stanley
2026-08-24 15:16:40 -07:00
28 changed files with 3677 additions and 138 deletions
+66 -3
View File
@@ -1119,7 +1119,8 @@ static std::string g_bezel_path; /* mod-owned OpenGL margin artwork */
static int g_fullscreen = 0; /* tri-state: 0 windowed, 1 borderless (desktop)
* fullscreen, 2 exclusive fullscreen */
static int g_video_screen = 0; /* 0=raw,1=crt,2=composite,3=trinitron */
static int g_video_win_w = 1280; /* window width (height follows aspect) */
static int g_video_win_w = 0; /* 0 = fit the display; see clamp_window_aspect */
static bool g_video_win_w_explicit = false; /* user chose a width */
static bool g_audio_spu_hq = false; /* SPU float-shadow (env overrides) */
static int g_audio_freq = 44100; /* host device request */
static int g_auto_skip_fmv = 0; /* skip FMVs the instant they're detected */
@@ -1474,9 +1475,18 @@ static int g_logical_w = 640;
* the given aspect: height = width*den/num. */
static void clamp_window_aspect(int* w, int* h, int num, int den) {
int width = *w;
if (width < 640) width = 640;
SDL_Rect bounds;
if (SDL_GetDisplayUsableBounds(0, &bounds) == 0 && bounds.w > 0 && bounds.h > 0) {
const int have_bounds =
(SDL_GetDisplayUsableBounds(0, &bounds) == 0 && bounds.w > 0 && bounds.h > 0);
/* 0 = "fit the display". The old default was a hardcoded 1280, which on a
* 4K or 8K panel opens a small window in the corner and, worse, makes the
* image far smaller than the internal render resolution the user chose --
* supersampling 16 rendering into a 1280-wide window throws almost all of
* it away. Fitting the usable bounds keeps the window proportional to the
* display it is actually on. An explicit width still wins. */
if (width <= 0) width = have_bounds ? bounds.w : 1280;
if (width < 640) width = 640;
if (have_bounds) {
if (width > bounds.w) width = bounds.w;
if (width * den / num > bounds.h) width = bounds.h * num / den;
}
@@ -11026,6 +11036,7 @@ int main(int argc, char** argv) {
#endif
if (us.has_supersampling) g_video_scale = us.supersampling;
if (us.has_window_width) g_video_win_w = us.window_width;
if (us.has_window_width && us.window_width > 0) g_video_win_w_explicit = true;
if (us.has_antialiasing) g_video_aa = us.antialiasing;
if (us.has_texture_filter) g_video_texfilter = us.texture_filter;
if (us.has_geometry_correction)
@@ -11439,6 +11450,24 @@ int main(int argc, char** argv) {
(!std::getenv("PSX_NO_LAUNCHER") && !force_no_launcher && !skip_launcher_setting);
if (want_launcher) {
launcher_boot_timing_mark("host:before_sdl_init");
/* Per-monitor DPI awareness, BEFORE any SDL_Init.
*
* Without it Windows virtualises everything this process sees: on a
* 7680x4320 panel at 400% scaling SDL_GetDisplayUsableBounds reports
* 1920x1032, so the window is clamped to roughly 1376 LOGICAL pixels and
* opens as a small box, while the desktop compositor then upscales it.
* The internal render resolution is unaffected -- which is the trap: the
* game renders at supersampling 16 and the result is thrown away scaling
* a 1376-wide window up to an 8K display.
*
* permonitorv2 makes SDL report physical pixels, so the window sizes
* against the real panel and the drawable matches it 1:1. */
#ifdef SDL_HINT_WINDOWS_DPI_AWARENESS
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2");
#endif
#ifdef SDL_HINT_WINDOWS_DPI_SCALING
SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "0");
#endif
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) == 0) {
launcher_boot_timing_mark("host:after_sdl_init");
recomp_launcher_set_preserve_sdl(1);
@@ -12653,6 +12682,24 @@ session_reboot:
* default). Enable the HIDAPI Xbox driver so HIDAPI handles Xbox pads too. */
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "1");
if (!SDL_WasInit(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER)) {
/* Per-monitor DPI awareness, BEFORE any SDL_Init.
*
* Without it Windows virtualises everything this process sees: on a
* 7680x4320 panel at 400% scaling SDL_GetDisplayUsableBounds reports
* 1920x1032, so the window is clamped to roughly 1376 LOGICAL pixels and
* opens as a small box, while the desktop compositor then upscales it.
* The internal render resolution is unaffected -- which is the trap: the
* game renders at supersampling 16 and the result is thrown away scaling
* a 1376-wide window up to an 8K display.
*
* permonitorv2 makes SDL report physical pixels, so the window sizes
* against the real panel and the drawable matches it 1:1. */
#ifdef SDL_HINT_WINDOWS_DPI_AWARENESS
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitorv2");
#endif
#ifdef SDL_HINT_WINDOWS_DPI_SCALING
SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "0");
#endif
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) != 0) {
std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
return 1;
@@ -12737,6 +12784,22 @@ session_reboot:
}
psx_apply_window_icon(sdl_window, argv[0]);
/* Maximise instead of computing the frame size ourselves.
*
* clamp_window_aspect fits the CLIENT area to the usable bounds, but a
* window is client plus title bar and borders, so fitting the client to a
* full-height display produced a window taller than the screen that hung
* off the top. Deriving the decoration size first does not work either:
* SDL_GetWindowBordersSize reports nothing useful before the window is
* shown, so the correction silently did not apply.
*
* The window manager already solves this exactly. Maximise and let it fit
* the work area, decorations and taskbar included. Only when the request
* was "fit the display" (window_width unset) -- an explicit width is a
* deliberate choice and is left alone. */
if (!g_fullscreen && !g_video_win_w_explicit)
SDL_MaximizeWindow(sdl_window);
/* Host refresh: if the panel is within ~2% of 60 Hz, record it so driver
* vsync can own cadence (pacer skipped). Non-~60 Hz and unknown refresh
* (common on Wayland) keep PSX 59.94 Hz pacing and force swap interval 0