mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-28 19:07:47 -04:00
launcher: 4:3 window-size picker for launcher + game
The game window was hardcoded to 640x480, so it booted into a tiny window while the launcher was much larger — a jarring shrink on LAUNCH. Add a window-size setting that both windows honor, with a default that matches. - config_loader: settings.toml [video] window_width (height always w*3/4 to keep the PSX 4:3 aspect); load/save, validated 640..3840. - main.cpp: the game window now opens at the chosen size (default 1280x960) instead of 640x480; the launcher opens at the same size (floored at 1280 wide so the dense dashboard stays usable). clamp_window_4x3() fits an oversized choice (e.g. 1920 on a 1080p panel) to the display's usable area. Both present paths already scale a 640x480 logical space to the window, so the larger window just fills with no aspect distortion. - launcher: "Window size (4:3)" toggle in the VIDEO settings cycling 960x720 / 1280x960 / 1600x1200 / 1920x1440; seeded from settings, persisted on launch. Verified: default launcher and game windows both open at 1280x960 (matched, no resize jump); settings.toml window_width=960 boots the game at 960x720 and the PS logo renders correct 4:3 with no distortion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -485,6 +485,10 @@ UserSettings load_user_settings(const fs::path& path) {
|
||||
const auto n = toml::find<int64_t>(v, "supersampling");
|
||||
if (n >= 1 && n <= 4) { s.supersampling = (int)n; s.has_supersampling = true; }
|
||||
});
|
||||
if (v.contains("window_width")) try_get([&]{
|
||||
const auto n = toml::find<int64_t>(v, "window_width");
|
||||
if (n >= 640 && n <= 3840) { s.window_width = (int)n; s.has_window_width = true; }
|
||||
});
|
||||
if (v.contains("antialiasing")) try_get([&]{
|
||||
s.antialiasing = toml::find<bool>(v, "antialiasing"); s.has_antialiasing = true;
|
||||
});
|
||||
@@ -584,6 +588,8 @@ bool save_user_settings(const fs::path& path, const UserSettings& s) {
|
||||
f << "renderer = \"" << (s.renderer ? "opengl" : "software") << "\"\n";
|
||||
if (s.has_supersampling)
|
||||
f << "supersampling = " << s.supersampling << "\n";
|
||||
if (s.has_window_width)
|
||||
f << "window_width = " << s.window_width << "\n";
|
||||
if (s.has_antialiasing)
|
||||
f << "antialiasing = " << (s.antialiasing ? "true" : "false") << "\n";
|
||||
if (s.has_texture_filter)
|
||||
|
||||
@@ -211,6 +211,9 @@ struct UserSettings {
|
||||
// [video]
|
||||
bool has_renderer = false; int renderer = 0; // 0=software,1=opengl
|
||||
bool has_supersampling = false; int supersampling = 1; // 1..4
|
||||
// Window size: width in px; height is always width*3/4 (PSX 4:3). Applies to
|
||||
// both the launcher and the emulator window so they boot at the same size.
|
||||
bool has_window_width = false; int window_width = 1280; // -> 1280x960
|
||||
bool has_antialiasing = false; bool antialiasing = true;
|
||||
bool has_texture_filter = false; int texture_filter = 0; // 0=nearest,1=bilinear
|
||||
bool has_screen_kind = false; int screen_kind = 0; // 0..3 (ScreenKind)
|
||||
|
||||
@@ -146,6 +146,7 @@ button:hover { background-color: #232d3c; }
|
||||
.set-lbl { width: 230dp; flex: 0 0 230dp; color: #adbac7; }
|
||||
.set-ctl { flex: 1 1 auto; display: flex; flex-direction: row; align-items: center; }
|
||||
.toggle { width: 132dp; text-align: center; }
|
||||
.toggle.wide { width: 164dp; }
|
||||
.value { color: #ffffff; font-weight: bold; }
|
||||
.path { flex: 1 1 auto; min-width: 0dp; margin-left: 12dp; color: #7d8590;
|
||||
white-space: nowrap; overflow: hidden; }
|
||||
@@ -300,6 +301,8 @@ button:hover { background-color: #232d3c; }
|
||||
<div data-if="view == 'settings'">
|
||||
<div class="panel">
|
||||
<div class="eyebrow">VIDEO</div>
|
||||
<div class="set-row"><span class="set-lbl">Window size (4:3)</span><span class="set-ctl">
|
||||
<button class="toggle wide" data-event-click="cycle_winsize"><span class="value">{{winsize_label}}</span></button></span></div>
|
||||
<div class="set-row"><span class="set-lbl">Renderer</span><span class="set-ctl">
|
||||
<button class="toggle" data-event-click="cycle_renderer"><span class="value">{{renderer_label}}</span></button></span></div>
|
||||
<div class="set-row"><span class="set-lbl">Supersampling</span><span class="set-ctl">
|
||||
|
||||
@@ -99,6 +99,7 @@ struct LauncherModel {
|
||||
int texture_filter = 0; // 0=nearest, 1=bilinear
|
||||
int crt = 0; // 0=raw,1=crt,2=composite,3=trinitron
|
||||
bool spu_hq = false;
|
||||
int window_width = 1280; // window size (4:3; height = w*3/4)
|
||||
|
||||
Rml::String bios_path;
|
||||
Rml::String disc_path;
|
||||
@@ -107,6 +108,7 @@ struct LauncherModel {
|
||||
Rml::String renderer_label;
|
||||
Rml::String crt_label;
|
||||
Rml::String texfilter_label;
|
||||
Rml::String winsize_label;
|
||||
|
||||
// Disc verification (recomputed whenever disc_path changes).
|
||||
Rml::String disc_file; // file name only, e.g. "tomba.cue"
|
||||
@@ -332,10 +334,29 @@ const char* crt_name(int v) {
|
||||
}
|
||||
}
|
||||
|
||||
// Offered 4:3 window widths (height = w*3/4). The toggle cycles through these.
|
||||
const int kWinWidths[] = { 960, 1280, 1600, 1920 };
|
||||
const int kNumWinWidths = (int)(sizeof(kWinWidths) / sizeof(kWinWidths[0]));
|
||||
|
||||
// Snap an arbitrary width to the nearest offered option index.
|
||||
int winsize_index(int width) {
|
||||
int best = 1, bestd = 1 << 30; // default to 1280
|
||||
for (int i = 0; i < kNumWinWidths; i++) {
|
||||
int d = width > kWinWidths[i] ? width - kWinWidths[i] : kWinWidths[i] - width;
|
||||
if (d < bestd) { bestd = d; best = i; }
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
std::string winsize_label_for(int width) {
|
||||
return std::to_string(width) + " \xC3\x97 " + std::to_string(width * 3 / 4); // "1280 × 960"
|
||||
}
|
||||
|
||||
void refresh_labels(LauncherModel& m) {
|
||||
m.renderer_label = renderer_name(m.renderer);
|
||||
m.crt_label = crt_name(m.crt);
|
||||
m.texfilter_label = texfilter_name(m.texture_filter);
|
||||
m.winsize_label = winsize_label_for(m.window_width);
|
||||
}
|
||||
|
||||
std::string region_long(const std::string& r) {
|
||||
@@ -546,6 +567,7 @@ Result run(SDL_Window* window, void* gl_context,
|
||||
m.texture_filter = io.texture_filter;
|
||||
m.crt = io.screen_kind;
|
||||
m.spu_hq = io.spu_hq;
|
||||
m.window_width = kWinWidths[winsize_index(io.has_window_width ? io.window_width : 1280)];
|
||||
m.bios_path = io.has_bios_path ? io.bios_path.generic_string() : Rml::String();
|
||||
m.disc_path = io.has_disc_path ? io.disc_path.generic_string() : Rml::String();
|
||||
refresh_labels(m);
|
||||
@@ -588,6 +610,7 @@ Result run(SDL_Window* window, void* gl_context,
|
||||
c.Bind("spu_hq", &m.spu_hq);
|
||||
c.Bind("renderer_label", &m.renderer_label);
|
||||
c.Bind("crt_label", &m.crt_label);
|
||||
c.Bind("winsize_label", &m.winsize_label);
|
||||
c.Bind("texfilter_label",&m.texfilter_label);
|
||||
c.Bind("bios_path", &m.bios_path);
|
||||
c.Bind("disc_path", &m.disc_path);
|
||||
@@ -652,6 +675,12 @@ Result run(SDL_Window* window, void* gl_context,
|
||||
m.crt = (m.crt + 1) % 4; refresh_labels(m);
|
||||
handle.DirtyVariable("crt_label");
|
||||
});
|
||||
c.BindEventCallback("cycle_winsize",
|
||||
[&m, handle](Rml::DataModelHandle, Rml::Event&, const Rml::VariantList&) mutable {
|
||||
int i = (winsize_index(m.window_width) + 1) % kNumWinWidths;
|
||||
m.window_width = kWinWidths[i]; refresh_labels(m);
|
||||
handle.DirtyVariable("winsize_label");
|
||||
});
|
||||
c.BindEventCallback("toggle_spu",
|
||||
[&m, handle](Rml::DataModelHandle, Rml::Event&, const Rml::VariantList&) mutable {
|
||||
m.spu_hq = !m.spu_hq;
|
||||
@@ -831,6 +860,7 @@ Result run(SDL_Window* window, void* gl_context,
|
||||
io.texture_filter = m.texture_filter; io.has_texture_filter = true;
|
||||
io.screen_kind = m.crt; io.has_screen_kind = true;
|
||||
io.spu_hq = m.spu_hq; io.has_spu_hq = true;
|
||||
io.window_width = m.window_width; io.has_window_width = true;
|
||||
if (!m.bios_path.empty()) { io.bios_path = fs::path(std::string(m.bios_path)); io.has_bios_path = true; }
|
||||
if (!m.disc_path.empty()) { io.disc_path = fs::path(std::string(m.disc_path)); io.has_disc_path = true; }
|
||||
|
||||
|
||||
+34
-2
@@ -118,7 +118,23 @@ static bool g_video_aa = true; /* linear present filtering */
|
||||
static int g_video_texfilter = 0; /* 0=nearest, 1=bilinear */
|
||||
static int g_video_renderer = 0; /* 0=software, 1=opengl (requested) */
|
||||
static int g_video_screen = 0; /* 0=raw,1=crt,2=composite,3=trinitron */
|
||||
static int g_video_win_w = 1280; /* window width (height = w*3/4, 4:3) */
|
||||
static bool g_audio_spu_hq = false; /* SPU float-shadow (env overrides) */
|
||||
|
||||
/* Clamp a requested 4:3 window width to the primary display's usable area so an
|
||||
* oversized choice (e.g. 1920 on a 1080p panel) still fits on screen. Keeps the
|
||||
* 4:3 aspect: height = width*3/4. */
|
||||
static void clamp_window_4x3(int* w, int* h) {
|
||||
int width = *w;
|
||||
if (width < 640) width = 640;
|
||||
SDL_Rect bounds;
|
||||
if (SDL_GetDisplayUsableBounds(0, &bounds) == 0 && bounds.w > 0 && bounds.h > 0) {
|
||||
if (width > bounds.w) width = bounds.w;
|
||||
if (width * 3 / 4 > bounds.h) width = bounds.h * 4 / 3;
|
||||
}
|
||||
*w = width;
|
||||
*h = width * 3 / 4;
|
||||
}
|
||||
static bool g_gl_active = false; /* GL context live -> GL present path */
|
||||
/* Present straight from the FBO (fast, no readback). Set PSX_GL_FORCE_CPU_PRESENT=1
|
||||
* to force the software readout path instead — a diagnostic/fallback that also
|
||||
@@ -1408,6 +1424,7 @@ int main(int argc, char** argv) {
|
||||
PSXRecompV4::load_user_settings(settings_path);
|
||||
if (us.has_renderer) g_video_renderer = us.renderer;
|
||||
if (us.has_supersampling) g_video_scale = us.supersampling;
|
||||
if (us.has_window_width) g_video_win_w = us.window_width;
|
||||
if (us.has_antialiasing) g_video_aa = us.antialiasing;
|
||||
if (us.has_texture_filter) g_video_texfilter = us.texture_filter;
|
||||
if (us.has_screen_kind) g_video_screen = us.screen_kind;
|
||||
@@ -1460,17 +1477,25 @@ int main(int argc, char** argv) {
|
||||
seed.p2_device = p2_device; seed.has_p2_device = true;
|
||||
seed.p1_analog = p1_analog; seed.has_p1_analog = true;
|
||||
seed.p2_analog = p2_analog; seed.has_p2_analog = true;
|
||||
seed.window_width = g_video_win_w; seed.has_window_width = true;
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
/* Launcher opens at the same 4:3 size the game will, so there's no
|
||||
* jarring resize on LAUNCH. The dense dashboard needs a usable
|
||||
* minimum, so the launcher floor is 1280 wide even if the game
|
||||
* window is set smaller. */
|
||||
int lwin_w = g_video_win_w < 1280 ? 1280 : g_video_win_w;
|
||||
int lwin_h = 0;
|
||||
clamp_window_4x3(&lwin_w, &lwin_h);
|
||||
std::string lwin_title = (game_name.empty() ? std::string("PSX") : game_name)
|
||||
+ " \xE2\x80\x94 Launcher";
|
||||
SDL_Window* lwin = SDL_CreateWindow(
|
||||
lwin_title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
1280, 924, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
||||
lwin_w, lwin_h, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
||||
psx_launcher::Result lr = psx_launcher::Result::Unavailable;
|
||||
if (lwin) {
|
||||
SDL_GLContext lctx = SDL_GL_CreateContext(lwin);
|
||||
@@ -1513,6 +1538,7 @@ int main(int argc, char** argv) {
|
||||
if (seed.has_memcard2_path) memcard2_path = seed.memcard2_path;
|
||||
p1_device = seed.p1_device; p2_device = seed.p2_device;
|
||||
p1_analog = seed.p1_analog; p2_analog = seed.p2_analog;
|
||||
g_video_win_w = seed.window_width;
|
||||
/* Persist the user's choices next to the exe. */
|
||||
PSXRecompV4::save_user_settings(
|
||||
exe_dir_from_argv(argv[0]) / "settings.toml", seed);
|
||||
@@ -1662,10 +1688,16 @@ int main(int argc, char** argv) {
|
||||
|
||||
Uint32 win_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
|
||||
if (g_video_renderer == 1) win_flags |= SDL_WINDOW_OPENGL;
|
||||
/* Open at the user-chosen 4:3 window size (default 1280x960) instead of the
|
||||
* old hardcoded 640x480, so the game doesn't boot into a tiny window. The
|
||||
* present path keeps a 640x480 logical space, so the image scales to fill
|
||||
* the larger window with no aspect distortion. */
|
||||
int game_w = g_video_win_w, game_h = 0;
|
||||
clamp_window_4x3(&game_w, &game_h);
|
||||
sdl_window = SDL_CreateWindow(
|
||||
window_title.c_str(),
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
640, 480,
|
||||
game_w, game_h,
|
||||
win_flags
|
||||
);
|
||||
if (!sdl_window) {
|
||||
|
||||
Reference in New Issue
Block a user