mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
launcher: Fullscreen on launch + Skip Launcher on Boot (parity with SMW)
Adds two launcher controls requested for Tomba (issue #6): - Fullscreen on launch: new [video] fullscreen setting, cloned through the full turbo_loads pipeline (UserSettings field + parse/save, launcher model/bind/ toggle/commit, main.cpp seed/read), applied as SDL_WINDOW_FULLSCREEN_DESKTOP at game-window creation. The in-game F11 / Alt+Enter hotkey is unchanged. - Skip Launcher on Boot: a footer switch (bottom-left) that boots straight into the game on subsequent launches, with a (first-in-this-launcher) confirmation modal explaining the --launcher escape hatch. The runtime skip_launcher round- trip + --launcher / PSX_NO_LAUNCHER already existed; this wires the UI to it. Also drops the redundant footer Settings/Back links (the top bar already has them). Mirrors SuperMarioWorldRecomp's feature. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -805,6 +805,9 @@ UserSettings load_user_settings(const fs::path& path) {
|
||||
if (v.contains("turbo_loads")) try_get([&]{
|
||||
s.turbo_loads = toml::find<bool>(v, "turbo_loads"); s.has_turbo_loads = true;
|
||||
});
|
||||
if (v.contains("fullscreen")) try_get([&]{
|
||||
s.fullscreen = toml::find<bool>(v, "fullscreen"); s.has_fullscreen = true;
|
||||
});
|
||||
if (v.contains("low_latency_input")) try_get([&]{
|
||||
s.low_latency_input = toml::find<bool>(v, "low_latency_input");
|
||||
s.has_low_latency_input = true;
|
||||
@@ -950,6 +953,8 @@ bool save_user_settings(const fs::path& path, const UserSettings& s) {
|
||||
f << "auto_skip_fmv = " << (s.auto_skip_fmv ? "true" : "false") << "\n";
|
||||
if (s.has_turbo_loads)
|
||||
f << "turbo_loads = " << (s.turbo_loads ? "true" : "false") << "\n";
|
||||
if (s.has_fullscreen)
|
||||
f << "fullscreen = " << (s.fullscreen ? "true" : "false") << "\n";
|
||||
if (s.has_low_latency_input)
|
||||
f << "low_latency_input = " << (s.low_latency_input ? "true" : "false") << "\n";
|
||||
if (s.has_vsync)
|
||||
|
||||
@@ -441,6 +441,10 @@ struct UserSettings {
|
||||
// (the per-game game.toml value seeds the launcher toggle; user choice in
|
||||
// settings.toml overrides it).
|
||||
bool has_turbo_loads = false; bool turbo_loads = true;
|
||||
// [video] fullscreen: launch the game window in desktop fullscreen (the
|
||||
// launcher's "Fullscreen on launch" toggle; the in-game F11 / Alt+Enter
|
||||
// hotkey still toggles it live). false => windowed (default).
|
||||
bool has_fullscreen = false; bool fullscreen = false;
|
||||
// Low-latency present knobs. low_latency_input re-samples the pad after the
|
||||
// wall-clock pacer (just before present) so the next CPU frame reads fresh
|
||||
// input instead of input ~one frame stale (the dominant input->photon cost
|
||||
|
||||
@@ -176,6 +176,7 @@ button:hover { background-color: #232d3c; }
|
||||
border-top: 1dp #1e2733; padding-top: 16dp; }
|
||||
.foot-link { color: #8b949e; margin-right: 22dp; }
|
||||
.foot-link:hover { color: #e6e9ef; }
|
||||
.skip-lbl { color: #adbac7; font-size: 13dp; margin-left: 9dp; margin-right: 22dp; }
|
||||
.foot-status { display: flex; flex-direction: row; align-items: center; flex: 1 1 auto; }
|
||||
#launch {
|
||||
background-color: #2563eb; border-color: #3b82f6; color: #ffffff; font-weight: bold;
|
||||
@@ -183,6 +184,19 @@ button:hover { background-color: #232d3c; }
|
||||
}
|
||||
#launch:hover { background-color: #3b82f6; }
|
||||
.back-btn { margin-right: 14dp; }
|
||||
|
||||
/* ---- confirmation modal ---- */
|
||||
.modal-backdrop { position: fixed; left: 0dp; top: 0dp; width: 100%; height: 100%;
|
||||
background-color: #000000cc; display: flex; flex-direction: row; align-items: center; z-index: 100; }
|
||||
.modal-box { display: block; width: 460dp; margin: 0dp auto;
|
||||
background-color: #11161f; border: 1dp #2b3543; border-radius: 12dp; padding: 22dp 24dp; }
|
||||
.modal-title { display: block; font-size: 19dp; font-weight: bold; color: #ffffff; margin-bottom: 10dp; }
|
||||
.modal-text { display: block; color: #adbac7; font-size: 14dp; line-height: 20dp; margin-bottom: 20dp; }
|
||||
.modal-btns { display: flex; flex-direction: row; align-items: center; }
|
||||
.modal-spacer { flex: 1 1 auto; }
|
||||
.modal-btns button { margin-left: 10dp; }
|
||||
.modal-confirm { background-color: #2563eb; border-color: #3b82f6; color: #ffffff; font-weight: bold; }
|
||||
.modal-confirm:hover { background-color: #3b82f6; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -343,6 +357,8 @@ button:hover { background-color: #232d3c; }
|
||||
<div class="set-row"><span class="set-lbl">Turbo loads</span><span class="set-ctl">
|
||||
<button class="toggle" data-event-click="toggle_turbo_loads"><span class="value" data-if="turbo_loads">On</span><span class="value" data-if="turbo_loads == false">Off</span></button>
|
||||
</span></div>
|
||||
<div class="set-row"><span class="set-lbl">Fullscreen on launch</span><span class="set-ctl">
|
||||
<button class="toggle" data-event-click="toggle_fullscreen"><span class="value" data-if="fullscreen">On</span><span class="value" data-if="fullscreen == false">Off</span></button></span></div>
|
||||
<div class="set-row" data-if="ws_eligible"><span class="set-lbl">Widescreen <span class="exp-tag">EXPERIMENTAL</span></span><span class="set-ctl">
|
||||
<button class="toggle" data-event-click="toggle_widescreen"><span class="value" data-if="widescreen">On</span><span class="value" data-if="widescreen == false">Off</span></button>
|
||||
<span class="ws-hint" data-if="widescreen">16:9 native field-of-view</span></span></div>
|
||||
@@ -372,12 +388,25 @@ button:hover { background-color: #232d3c; }
|
||||
|
||||
<!-- ================= FOOTER ================= -->
|
||||
<div class="footer">
|
||||
<span class="foot-link" data-if="view == 'dashboard'" data-event-click="show_settings">Settings</span>
|
||||
<span class="foot-link back-btn" data-if="view == 'settings'" data-event-click="show_dashboard">‹ Back</span>
|
||||
<span class="switch" data-class-on="skip_launcher" data-event-click="toggle_skip_launcher"><span class="knob"></span></span>
|
||||
<span class="skip-lbl">Skip Launcher on Boot</span>
|
||||
<span class="foot-status"><span class="dot"></span><span class="status-txt">Recompiler ready</span></span>
|
||||
<button id="launch" data-event-click="launch">LAUNCH GAME</button>
|
||||
</div>
|
||||
|
||||
<!-- ================= SKIP-LAUNCHER CONFIRM MODAL ================= -->
|
||||
<div class="modal-backdrop" data-if="show_skip_modal">
|
||||
<div class="modal-box">
|
||||
<span class="modal-title">Skip the launcher on boot?</span>
|
||||
<span class="modal-text">The launcher will no longer appear — the game boots straight in every time. To get the launcher back, run the game with the --launcher argument, or set skip_launcher = false in settings.toml.</span>
|
||||
<div class="modal-btns">
|
||||
<span class="modal-spacer"></span>
|
||||
<button data-event-click="skip_modal_cancel">Cancel</button>
|
||||
<button class="modal-confirm" data-event-click="skip_modal_confirm">Skip on Boot</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</rml>
|
||||
|
||||
@@ -105,6 +105,12 @@ struct LauncherModel {
|
||||
int window_width = 1280; // window size (height = width*den/num per aspect)
|
||||
bool widescreen = false; // EXPERIMENTAL 16:9 native-wide (aspect_index==1)
|
||||
bool ws_eligible = true; // toggle shown only when renderer==software (native-wide is SW-only)
|
||||
bool fullscreen = false; // launch the game window in desktop fullscreen
|
||||
// Skip-launcher: boot straight into the game on subsequent launches. Turning
|
||||
// it ON shows a confirmation modal (show_skip_modal) so the user is told how
|
||||
// to get the launcher back (run with --launcher). Mirrors SMW's feature.
|
||||
bool skip_launcher = false;
|
||||
bool show_skip_modal = false;
|
||||
|
||||
Rml::String bios_path;
|
||||
Rml::String disc_path;
|
||||
@@ -650,6 +656,8 @@ Result run(SDL_Window* window, void* gl_context,
|
||||
m.crt = io.screen_kind;
|
||||
m.auto_skip_fmv = io.auto_skip_fmv;
|
||||
m.turbo_loads = io.turbo_loads;
|
||||
m.fullscreen = io.fullscreen;
|
||||
m.skip_launcher = io.skip_launcher;
|
||||
m.spu_hq = io.spu_hq;
|
||||
m.aspect_index = io.has_aspect_ratio ? aspect_index_for(io.aspect_num, io.aspect_den) : 0;
|
||||
m.window_width = kWinWidths[winsize_index(io.has_window_width ? io.window_width : 1280)];
|
||||
@@ -702,6 +710,9 @@ Result run(SDL_Window* window, void* gl_context,
|
||||
c.Bind("antialiasing", &m.antialiasing);
|
||||
c.Bind("auto_skip_fmv", &m.auto_skip_fmv);
|
||||
c.Bind("turbo_loads", &m.turbo_loads);
|
||||
c.Bind("fullscreen", &m.fullscreen);
|
||||
c.Bind("skip_launcher", &m.skip_launcher);
|
||||
c.Bind("show_skip_modal",&m.show_skip_modal);
|
||||
c.Bind("spu_hq", &m.spu_hq);
|
||||
c.Bind("renderer_label", &m.renderer_label);
|
||||
c.Bind("crt_label", &m.crt_label);
|
||||
@@ -821,6 +832,27 @@ Result run(SDL_Window* window, void* gl_context,
|
||||
m.turbo_loads = !m.turbo_loads;
|
||||
handle.DirtyVariable("turbo_loads");
|
||||
});
|
||||
c.BindEventCallback("toggle_fullscreen",
|
||||
[&m, handle](Rml::DataModelHandle, Rml::Event&, const Rml::VariantList&) mutable {
|
||||
m.fullscreen = !m.fullscreen;
|
||||
handle.DirtyVariable("fullscreen");
|
||||
});
|
||||
// Skip launcher: turning OFF is immediate; turning ON opens a confirm modal
|
||||
// first, so the user learns the --launcher escape hatch before committing.
|
||||
c.BindEventCallback("toggle_skip_launcher",
|
||||
[&m, handle](Rml::DataModelHandle, Rml::Event&, const Rml::VariantList&) mutable {
|
||||
if (m.skip_launcher) { m.skip_launcher = false; handle.DirtyVariable("skip_launcher"); }
|
||||
else { m.show_skip_modal = true; handle.DirtyVariable("show_skip_modal"); }
|
||||
});
|
||||
c.BindEventCallback("skip_modal_confirm",
|
||||
[&m, handle](Rml::DataModelHandle, Rml::Event&, const Rml::VariantList&) mutable {
|
||||
m.skip_launcher = true; m.show_skip_modal = false;
|
||||
handle.DirtyVariable("skip_launcher"); handle.DirtyVariable("show_skip_modal");
|
||||
});
|
||||
c.BindEventCallback("skip_modal_cancel",
|
||||
[&m, handle](Rml::DataModelHandle, Rml::Event&, const Rml::VariantList&) mutable {
|
||||
m.show_skip_modal = false; handle.DirtyVariable("show_skip_modal");
|
||||
});
|
||||
c.BindEventCallback("browse_bios",
|
||||
[&m, window, handle](Rml::DataModelHandle, Rml::Event&, const Rml::VariantList&) mutable {
|
||||
std::string p = win_pick_file(window, "Select PlayStation BIOS",
|
||||
@@ -1008,6 +1040,8 @@ Result run(SDL_Window* window, void* gl_context,
|
||||
io.screen_kind = m.crt; io.has_screen_kind = true;
|
||||
io.auto_skip_fmv = m.auto_skip_fmv; io.has_auto_skip_fmv = true;
|
||||
io.turbo_loads = m.turbo_loads; io.has_turbo_loads = true;
|
||||
io.fullscreen = m.fullscreen; io.has_fullscreen = true;
|
||||
io.skip_launcher = m.skip_launcher; io.has_skip_launcher = true;
|
||||
io.spu_hq = m.spu_hq; io.has_spu_hq = true;
|
||||
io.aspect_num = kAspects[m.aspect_index][0];
|
||||
io.aspect_den = kAspects[m.aspect_index][1];
|
||||
|
||||
@@ -134,6 +134,7 @@ static int g_video_scale = 1; /* internal-resolution SSAA factor *
|
||||
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_fullscreen = 0; /* launch the game window in desktop 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 bool g_audio_spu_hq = false; /* SPU float-shadow (env overrides) */
|
||||
@@ -2032,6 +2033,7 @@ int main(int argc, char** argv) {
|
||||
if (us.has_screen_kind) g_video_screen = us.screen_kind;
|
||||
if (us.has_auto_skip_fmv) g_auto_skip_fmv = us.auto_skip_fmv ? 1 : 0;
|
||||
if (us.has_turbo_loads) g_turbo_loads_enabled = us.turbo_loads ? 1 : 0;
|
||||
if (us.has_fullscreen) g_fullscreen = us.fullscreen ? 1 : 0;
|
||||
if (us.has_aspect_ratio) {
|
||||
g_video_aspect_num = us.aspect_num;
|
||||
g_video_aspect_den = us.aspect_den;
|
||||
@@ -2129,6 +2131,7 @@ int main(int argc, char** argv) {
|
||||
seed.screen_kind = g_video_screen; seed.has_screen_kind = true;
|
||||
seed.auto_skip_fmv = (g_auto_skip_fmv != 0); seed.has_auto_skip_fmv = true;
|
||||
seed.turbo_loads = (g_turbo_loads_enabled != 0); seed.has_turbo_loads = true;
|
||||
seed.fullscreen = (g_fullscreen != 0); seed.has_fullscreen = true;
|
||||
seed.aspect_num = g_video_aspect_num;
|
||||
seed.aspect_den = g_video_aspect_den; seed.has_aspect_ratio = true;
|
||||
seed.spu_hq = g_audio_spu_hq; seed.has_spu_hq = true;
|
||||
@@ -2198,6 +2201,7 @@ int main(int argc, char** argv) {
|
||||
g_video_screen = seed.screen_kind;
|
||||
g_auto_skip_fmv = seed.auto_skip_fmv ? 1 : 0;
|
||||
g_turbo_loads_enabled = seed.turbo_loads ? 1 : 0;
|
||||
g_fullscreen = seed.fullscreen ? 1 : 0;
|
||||
g_video_aspect_num = seed.aspect_num;
|
||||
g_video_aspect_den = seed.aspect_den;
|
||||
g_audio_spu_hq = seed.spu_hq;
|
||||
@@ -2402,6 +2406,10 @@ 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;
|
||||
if (g_video_renderer == 2) win_flags |= SDL_WINDOW_VULKAN;
|
||||
/* Fullscreen on launch (launcher "Fullscreen on launch" toggle). DESKTOP
|
||||
* fullscreen keeps the desktop resolution and letterboxes the image, matching
|
||||
* the in-game F11 / Alt+Enter hotkey behaviour. */
|
||||
if (g_fullscreen) win_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
/* Open at the user-chosen window size (default 1280 wide) instead of the
|
||||
* old hardcoded 640x480, so the game doesn't boot into a tiny window. The
|
||||
* height follows the configured display aspect (4:3 native, wider for the
|
||||
|
||||
Reference in New Issue
Block a user