mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
Menu host follow-ups: restart bits for touched groups, local return, default provider
- Restart bits: a group waits only when the edit touches it (next differs from the live value) and the result differs from boot. Dynamic resolution moves the live internal resolution off boot, so an in-game texture filter change relaunched the game. test_menu_video_snapshot covers both cases. - Return to the frontend from a local game opens the launcher as at boot; only a lobby match start (the runtime's pending-launch exit, or the provider's lobby view asking once its match started) keeps the launch and resumes the room. - Default provider: with no title choice, the UI library's provider is used when it ships one (recomp_ui_psx_menu.h), so recomp-ui stays the default; PsxMenuHost.default_selection tells it to keep the title's default presentation. - Debug: menu_host reports provider, open, pause, capture and snapshot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -493,9 +493,9 @@ between Play and TCP availability.
|
||||
|
||||
## Complete command index (generated)
|
||||
|
||||
**375 commands registered** — 362 on the native server (`runtime/src/debug_server.c`), 61 on the Beetle server (`runtime/src/beetle_debug_server.c`).
|
||||
**376 commands registered** — 363 on the native server (`runtime/src/debug_server.c`), 61 on the Beetle server (`runtime/src/beetle_debug_server.c`).
|
||||
|
||||
84 of 375 have prose above; **291 are index-only**. An index-only command still works — it just has no description here yet. Send it `{"cmd":"<name>"}` and read the reply, or find its `handle_*` function in the server source.
|
||||
84 of 376 have prose above; **292 are index-only**. An index-only command still works — it just has no description here yet. Send it `{"cmd":"<name>"}` and read the reply, or find its `handle_*` function in the server source.
|
||||
|
||||
Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this block has drifted from the code.
|
||||
|
||||
@@ -687,6 +687,7 @@ Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this bloc
|
||||
| `mdec_trace` | ✓ | | |
|
||||
| `mdec_trace_clear` | ✓ | | |
|
||||
| `mem_words` | ✓ | | |
|
||||
| `menu_host` | ✓ | | |
|
||||
| `mmio_clear` | ✓ | | ✓ |
|
||||
| `mmio_dump` | ✓ | | ✓ |
|
||||
| `mmx6_freshfix` | ✓ | | |
|
||||
|
||||
@@ -77,6 +77,11 @@ typedef struct PsxMenuHost {
|
||||
void (*launcher_settings_get)(struct RecompLauncherCSettings *out);
|
||||
unsigned (*launcher_settings_apply)(const struct RecompLauncherCSettings *next);
|
||||
const char *assets_dir; /* the title's assets folder */
|
||||
/* 1 when the runtime chose this provider because the title chose none
|
||||
* (the UI library's default provider, recomp_ui_psx_menu.h). Such a
|
||||
* provider keeps the title's default presentation: it may decline to
|
||||
* attach when it would only add a menu the title did not have. */
|
||||
int default_selection;
|
||||
} PsxMenuHost;
|
||||
|
||||
typedef struct PsxMenuProvider {
|
||||
|
||||
@@ -9267,6 +9267,17 @@ static void handle_host_pause(int id, const char *json)
|
||||
psx_host_pause_depth());
|
||||
}
|
||||
|
||||
/* menu_host: in-game menu host state (psx_menu_host.h): provider, attached,
|
||||
* open, pause, ui capture, and the video/netplay snapshot. */
|
||||
extern int psx_debug_menu_host_json(char *out, size_t cap);
|
||||
static void handle_menu_host(int id, const char *json)
|
||||
{
|
||||
char body[768];
|
||||
(void)json;
|
||||
psx_debug_menu_host_json(body, sizeof body);
|
||||
send_fmt("{\"id\":%d,\"ok\":true,%s}", id, body);
|
||||
}
|
||||
|
||||
/* host_key name=<SDL scancode name> down=<1|0> (both when omitted). */
|
||||
extern int psx_debug_push_host_key(const char *name, int down);
|
||||
static void handle_host_key(int id, const char *json)
|
||||
@@ -15952,6 +15963,7 @@ static const CmdEntry s_commands[] = {
|
||||
{ "debug_key", handle_debug_key },
|
||||
{ "host_pause", handle_host_pause },
|
||||
{ "host_key", handle_host_key },
|
||||
{ "menu_host", handle_menu_host },
|
||||
{ "host_mouse", handle_host_mouse },
|
||||
{ "scanline", handle_scanline },
|
||||
{ "ws_backdrop_ring", handle_ws_backdrop_ring },
|
||||
|
||||
+59
-4
@@ -3477,6 +3477,9 @@ static void shutdown_runtime(void);
|
||||
* session ends but the pending launch (and its seats/signals) must survive
|
||||
* into the launcher loop that starts it. */
|
||||
static int g_ovl_launch_to_start = 0;
|
||||
/* The in-game menu asked to go back to the frontend from a local game (not a
|
||||
* match, no pending launch): the launcher opens normally, not on the lobby. */
|
||||
static int g_ovl_frontend_from_local = 0;
|
||||
static void recomp_overlay_detach(void); /* in-game menu host, below */
|
||||
|
||||
/* Set when this process started netplay from the lobby room — soft-exit returns
|
||||
@@ -16150,7 +16153,13 @@ void mh_video_get(PsxHostVideoSettings* out) {
|
||||
unsigned mh_restart_bits(const PsxHostVideoSettings& next) {
|
||||
std::lock_guard<std::mutex> lk(g_ovl_mu);
|
||||
const PsxHostVideoSettings now = g_ovl_video_current;
|
||||
unsigned out = psx_host_video_restart_bits(&g_ovl_video_boot, &next);
|
||||
/* A restart group waits only when this edit touches it (next differs from
|
||||
* the live value) and the result differs from what the session booted
|
||||
* with. The live internal resolution moves under dynamic resolution, so
|
||||
* comparing with boot alone flagged edits that never touched it (an
|
||||
* in-game texture filter change relaunched the game). */
|
||||
unsigned out = psx_host_video_restart_bits(&g_ovl_video_boot, &next) &
|
||||
psx_host_video_restart_bits(&now, &next);
|
||||
/* In a netplay match only presentation applies live (psx_video_apply_live);
|
||||
* the rest waits for the next session. */
|
||||
if (g_ovl_netplay &&
|
||||
@@ -16309,6 +16318,7 @@ PsxMenuHost g_menu_host = {
|
||||
nullptr, nullptr, nullptr,
|
||||
#endif
|
||||
nullptr,
|
||||
0,
|
||||
};
|
||||
|
||||
int ovl_sink(const void* ev, void*) { return g_ovl && g_ovl->event ? g_ovl->event(g_ovl->ctx, ev) : 0; }
|
||||
@@ -16316,9 +16326,25 @@ void ovl_draw(int w, int h, void*) { if (g_ovl && g_ovl->draw) g_ovl->draw(g_ovl
|
||||
} // namespace
|
||||
|
||||
/* Main thread, once per GL context (windowed OpenGL only). */
|
||||
#if defined(__has_include)
|
||||
#if __has_include("recomp_ui_psx_menu.h")
|
||||
#include "recomp_ui_psx_menu.h" /* the maintained UI's provider: the default */
|
||||
#define PSX_MENU_HAS_DEFAULT_PROVIDER 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void recomp_overlay_setup(const char* argv0, const std::string& title) {
|
||||
const char* e = std::getenv("PSX_OVERLAY");
|
||||
const PsxMenuProvider* p = psx_menu_provider();
|
||||
g_menu_host.default_selection = 0;
|
||||
#if defined(PSX_MENU_HAS_DEFAULT_PROVIDER)
|
||||
/* No title choice: the UI the title is built with (recomp-ui) provides the
|
||||
* menu, and decides from default_selection what to offer by default. */
|
||||
if (!p) {
|
||||
p = recomp_ui_psx_menu_provider();
|
||||
g_menu_host.default_selection = 1;
|
||||
}
|
||||
#endif
|
||||
if (!p || g_ovl_on || !g_gl_active || g_headless || (e && *e == '0')) return;
|
||||
const std::filesystem::path dir = exe_dir_from_argv(argv0);
|
||||
g_ovl_name = title;
|
||||
@@ -16336,6 +16362,23 @@ static void recomp_overlay_setup(const char* argv0, const std::string& title) {
|
||||
std::fprintf(stdout, "psxrecomp: in-game menu ready (%s)\n", p->name ? p->name : "provider");
|
||||
}
|
||||
|
||||
/* Debug server: menu host state (provider, open, pause, capture, snapshot). */
|
||||
extern "C" int psx_debug_menu_host_json(char* out, size_t cap) {
|
||||
PsxHostVideoSettings v;
|
||||
mh_video_get(&v);
|
||||
const PsxMenuProvider* p = g_ovl ? g_ovl : psx_menu_provider();
|
||||
return std::snprintf(out, cap,
|
||||
"\"provider\":\"%s\",\"attached\":%d,\"default_selection\":%d,\"open\":%d,"
|
||||
"\"paused\":%d,\"pause_depth\":%d,\"ui_capture\":%d,\"netplay\":%d,"
|
||||
"\"texture_filter\":%d,\"fmv_filter\":%d,\"present_linear\":%d,\"fullscreen\":%d,"
|
||||
"\"render_thread\":%d,\"frame_generation\":%d,\"disc_count\":%d,\"disc_mounted\":%d",
|
||||
p && p->name ? p->name : "", g_ovl_on ? 1 : 0, g_menu_host.default_selection,
|
||||
ovl_open() ? 1 : 0, g_ovl_pause_held ? 1 : 0, psx_host_pause_depth(),
|
||||
psx_host_ui_capture_active(), mh_netplay_active(), v.texture_filter, v.fmv_filter,
|
||||
v.present_linear, v.fullscreen, v.render_thread, v.frame_generation, mh_disc_count(),
|
||||
mh_disc_mounted());
|
||||
}
|
||||
|
||||
/* Before the GL context goes away (session teardown), with it current: the
|
||||
* draw hook is removed first (it waits for an in-flight draw), then the
|
||||
* provider frees its GL objects. setup attaches again on the next context. */
|
||||
@@ -16436,15 +16479,26 @@ static void host_pause_service(void) {
|
||||
g_ovl_pause_wanted = 0;
|
||||
if (g_ovl_pause_held) { psx_host_pause_pop(); g_ovl_pause_held = false; }
|
||||
if (g_ovl->set_open) g_ovl->set_open(g_ovl->ctx, 0);
|
||||
/* A lobby match start (the runtime's pending-launch exit, or the
|
||||
* provider's own lobby view asking once its match started) keeps
|
||||
* the launch and resumes the room; any other request from a local
|
||||
* game is a plain return: the launcher opens as at boot. */
|
||||
bool for_launch = false;
|
||||
#if defined(RECOMP_LAUNCHER) && defined(PSX_HAS_RECOMP_NET) && defined(PSX_HAS_LOBBY_CLIENT)
|
||||
for_launch = g_lnch_netplay_available && g_lnch_netplay_callbacks.launch_pending &&
|
||||
g_lnch_netplay_callbacks.launch_pending(g_lnch_netplay_callbacks.ctx);
|
||||
#endif
|
||||
if (psx_netplay_active()) {
|
||||
netplay_soft_exit("menu_to_frontend");
|
||||
} else {
|
||||
/* End this session and re-enter the frontend loop in-process
|
||||
* (the netplay soft-return path); it then starts whatever the
|
||||
* menu asked for, e.g. the lobby's match. */
|
||||
std::fprintf(stdout, "psxrecomp: in-game menu -> frontend loop\n");
|
||||
std::fprintf(stdout, "psxrecomp: in-game menu -> frontend loop%s\n",
|
||||
for_launch ? " (lobby match)" : "");
|
||||
g_netplay_from_lobby = 1;
|
||||
g_ovl_launch_to_start = 1;
|
||||
g_ovl_launch_to_start = for_launch ? 1 : 0;
|
||||
g_ovl_frontend_from_local = for_launch ? 0 : 1;
|
||||
psx_request_return_to_lobby();
|
||||
}
|
||||
return;
|
||||
@@ -20729,7 +20783,8 @@ soft_return_lobby:
|
||||
* happens; an automatch room is the server's and cannot. Leave it
|
||||
* here, and reopen the launcher on the browser instead. */
|
||||
const int rui_resume_room =
|
||||
ae_np_leave_automatch_room_after_match() ? 0 : 1;
|
||||
(ae_np_leave_automatch_room_after_match() || g_ovl_frontend_from_local) ? 0 : 1;
|
||||
g_ovl_frontend_from_local = 0;
|
||||
|
||||
ae_rui_set_sidecar_paths(argv[0]);
|
||||
g_lnch_expected_serial = game_id;
|
||||
|
||||
@@ -52,7 +52,7 @@ int main() {
|
||||
assert(got.fullscreen==got.texture_filter && got.texture_filter==got.renderer &&
|
||||
got.renderer==got.frame_generation && got.frame_generation==got.supersampling &&
|
||||
got.supersampling==got.render_thread && got.render_thread==got.present_thread);
|
||||
const auto next=values(2);
|
||||
const auto next=values(9); /* differs from every live and boot value */
|
||||
const unsigned restart=mh_video_apply(&next);
|
||||
assert(restart & PSX_VIDEO_RESTART_RENDERER);
|
||||
++reads;
|
||||
@@ -61,6 +61,15 @@ int main() {
|
||||
for (int i=0;i<50000;++i) { live=values(1+i%8); netplay=i%2; mh_refresh_snapshot(); }
|
||||
draw.join();
|
||||
assert(reads==50000 && wrong_thread_reads==0 && g_ovl_video_pending);
|
||||
/* Restart bits only for groups the edit touches: when the live value has
|
||||
* moved off boot (dynamic resolution moves the internal resolution), an
|
||||
* edit that leaves that group at its live value asks for no restart; one
|
||||
* that changes it does. */
|
||||
live=values(1); live.renderer=3; g_video_boot=values(1); netplay=0; mh_refresh_snapshot();
|
||||
PsxHostVideoSettings same=live; same.texture_filter=5;
|
||||
assert(mh_restart_bits(same)==0);
|
||||
PsxHostVideoSettings changed=live; changed.renderer=4;
|
||||
assert(mh_restart_bits(changed) & PSX_VIDEO_RESTART_RENDERER);
|
||||
puts("menu video snapshot: coherent draw-thread reads and main-thread ownership PASS");
|
||||
}
|
||||
'''
|
||||
|
||||
Reference in New Issue
Block a user