Files
psxrecomp/runtime/include/psx_menu_host.h
Shokunin 83f1468860 In-game menu host: a provider-neutral contract (psx_menu_host.h)
psxrecomp owns the contract between the runtime and any in-game menu
frontend. A provider (PsxMenuProvider: attach, event, is_open, set_open,
draw, detach) is registered by the title with psx_menu_set_provider();
none is registered by default, so a title's UI and pin are unchanged.
The runtime offers every provider the same host (PsxMenuHost): pause
(P1, refused in netplay), video get/apply on PsxHostVideoSettings (P4),
mod switch/option set (P5, via the new psx_mod_feature_enable /
psx_mod_feature_set_option shared with the launcher ABI provider),
reload bindings, exit (quit, back to the frontend loop in-process, or a
quick relaunch with the original arguments), disc/tray slots filled by
the tray policy (psx_menu_set_disc_ops), and optional helpers on the
shared launcher ABI both frontends already speak (game info with the
mod catalogue and lobby, settings get/apply with the handoff's filter
translations).

Policies live here, the same for every provider: the lobby is pumped
during play and a started match ends the local session and starts in
the frontend loop (pending launch, seats and signals kept, pumping
stopped so the match port stays free), the menu is closed and its pause
dropped first; the provider detaches before its GL context goes away at
session teardown and attaches to the next one. --launcher sets
RECOMP_LAUNCHER_PREGAME for frontends that boot into the game;
PSX_HIDDEN_WINDOW is a hidden-window test hook that keeps the frontend.
Nothing here includes or names a frontend's own headers or types.
Tests: psx_menu_host_test, host_overlay_test (relaunch quoting).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:02:57 -07:00

119 lines
5.5 KiB
C

/* psx_menu_host.h -- the runtime's contract with an in-game menu provider.
*
* psxrecomp owns this header. A frontend (recomp-ui, recomp-launcher, or any
* other) implements PsxMenuProvider; the runtime implements PsxMenuHost. The
* runtime keeps every policy (pause, live settings, mod changes, disc/tray,
* netplay session start/exit) on its side, so all providers get the same
* behaviour. Nothing here is specific to one frontend.
*
* Selection: no provider is registered by default, so a title's existing UI
* (recomp-ui) and its pin are unchanged. A title that wants a provider
* registers it with psx_menu_set_provider() before the game window opens
* (for example from a static initializer in the title's own code).
*
* Threads: attach/event/is_open/set_open run on the main thread; draw runs on
* the thread that owns the GL context (host_overlay.h P2) with the window
* framebuffer bound. PsxMenuHost calls may come from either; the runtime
* applies them on the main thread at the next frame. */
#ifndef PSX_MENU_HOST_H
#define PSX_MENU_HOST_H
#include "host_overlay.h"
#ifdef __cplusplus
extern "C" {
#endif
#define PSX_MENU_ABI_VERSION 1
/* request_exit */
enum {
PSX_MENU_EXIT_QUIT = 0, /* leave the game (netplay: back to the lobby) */
PSX_MENU_EXIT_TO_FRONTEND = 1, /* end the session, re-enter the frontend loop in-process */
PSX_MENU_EXIT_RELAUNCH = 2 /* restart this process with its original arguments */
};
/* video_apply returns PSX_VIDEO_RESTART_* bits (host_overlay.h) plus this one:
* during a netplay match only presentation applies; the rest waits. */
#define PSX_MENU_RESTART_AFTER_MATCH (1u << 31)
/* The shared launcher C ABI (recomp_launcher.h) that recomp-ui and
* recomp-launcher both speak for the pre-game launcher. Optional: a provider
* that uses it gets the same game info, mod catalogue and lobby objects the
* pre-game launcher gets; one that does not ignores these entries. */
struct RecompLauncherCGameInfo;
struct RecompLauncherCSettings;
typedef struct PsxMenuHost {
int abi_version; /* PSX_MENU_ABI_VERSION */
/* Pause the guest and audio (host_overlay.h P1). Returns 0 when refused
* (netplay): the menu then stays open without pausing. */
int (*set_paused)(int paused);
int (*netplay_active)(void);
/* Video settings: current values, and a live apply (P4) that also saves
* settings.toml. Returns the restart bits of the fields that wait. */
void (*video_get)(PsxHostVideoSettings *out);
unsigned (*video_apply)(const PsxHostVideoSettings *next);
/* Mod feature switch (option == NULL, value "true"/"false") or option.
* Saved to mods/state.toml; returns 1 when it applied live (P5), 0 when it
* waits for the next start. Refused (0, unsaved) during netplay. */
int (*mod_set)(const char *package_id, const char *feature_id,
const char *option_id, const char *value);
/* Reload keybinds.ini / input.ini / config.ini after the menu wrote them. */
void (*reload_bindings)(void);
/* PSX_MENU_EXIT_*. Applied on the main thread; the menu is closed and its
* pause dropped before the session ends. */
void (*request_exit)(int how);
/* Disc/tray: the runtime's production tray policy (psx_disc_tray.h),
* shared with its own disc menu and hotkeys; count is 0 without a
* multi-disc roster or a policy. insert queues the lid cycle for the main
* thread: 0 = queued, -1 = no such disc, 3 = refused (netplay). */
int (*disc_count)(void);
int (*disc_mounted)(void); /* 1-based, 0 = none */
const char *(*disc_path)(int disc_1based);
int (*disc_insert)(int disc_1based);
/* Shared launcher ABI helpers (see above); NULL in builds without it. */
void (*launcher_game_info)(struct RecompLauncherCGameInfo *out);
void (*launcher_settings_get)(struct RecompLauncherCSettings *out);
unsigned (*launcher_settings_apply)(const struct RecompLauncherCSettings *next);
const char *assets_dir; /* the title's assets folder */
} PsxMenuHost;
typedef struct PsxMenuProvider {
int abi_version; /* PSX_MENU_ABI_VERSION */
const char *name;
void *ctx;
/* Once per GL context, with it current. Returns 0 to decline. */
int (*attach)(void *ctx, void *sdl_window, void *gl_context, const PsxMenuHost *host);
/* Every SDL event first; 1 = consumed (the game does not see it). The
* provider opens itself (its own key/pad binding) from here. */
int (*event)(void *ctx, const void *sdl_event);
int (*is_open)(void *ctx);
void (*set_open)(void *ctx, int open);
/* Draw over the presented frame (P2). */
void (*draw)(void *ctx, int framebuffer_w, int framebuffer_h);
/* Before its GL context goes away (session end, rematch), with it current;
* attach follows for the next context. */
void (*detach)(void *ctx);
} PsxMenuProvider;
/* Register (or clear, NULL) the in-game menu provider. Main thread, before
* the game window opens; a provider with another ABI version is refused. */
int psx_menu_set_provider(const PsxMenuProvider *provider);
const PsxMenuProvider *psx_menu_provider(void);
/* The disc/tray policy publishes itself here (PsxMenuHost disc_* entries). */
typedef struct PsxMenuDiscOps {
int (*count)(void);
int (*mounted)(void);
const char *(*path)(int disc_1based);
int (*insert)(int disc_1based);
} PsxMenuDiscOps;
void psx_menu_set_disc_ops(const PsxMenuDiscOps *ops);
const PsxMenuDiscOps *psx_menu_disc_ops(void);
#ifdef __cplusplus
}
#endif
#endif