diff --git a/recompiler/src/config_loader.cpp b/recompiler/src/config_loader.cpp index a2436031..2f57085d 100644 --- a/recompiler/src/config_loader.cpp +++ b/recompiler/src/config_loader.cpp @@ -805,6 +805,9 @@ UserSettings load_user_settings(const fs::path& path) { if (v.contains("turbo_loads")) try_get([&]{ s.turbo_loads = toml::find(v, "turbo_loads"); s.has_turbo_loads = true; }); + if (v.contains("fullscreen")) try_get([&]{ + s.fullscreen = toml::find(v, "fullscreen"); s.has_fullscreen = true; + }); if (v.contains("low_latency_input")) try_get([&]{ s.low_latency_input = toml::find(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) diff --git a/recompiler/src/config_loader.h b/recompiler/src/config_loader.h index a5696487..2ad7625b 100644 --- a/recompiler/src/config_loader.h +++ b/recompiler/src/config_loader.h @@ -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 diff --git a/runtime/launcher/assets/launcher.rml b/runtime/launcher/assets/launcher.rml index 1d339a38..387b3df7 100644 --- a/runtime/launcher/assets/launcher.rml +++ b/runtime/launcher/assets/launcher.rml @@ -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; } @@ -343,6 +357,8 @@ button:hover { background-color: #232d3c; }
Turbo loads
+
Fullscreen on launch +
Widescreen EXPERIMENTAL 16:9 native field-of-view
@@ -372,12 +388,25 @@ button:hover { background-color: #232d3c; } + + + diff --git a/runtime/launcher/launcher.cpp b/runtime/launcher/launcher.cpp index f478e2e8..0f810cb0 100644 --- a/runtime/launcher/launcher.cpp +++ b/runtime/launcher/launcher.cpp @@ -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]; diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index 17822894..bb70757e 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -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