mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Fix PSX launcher hotkeys and audio persistence
This commit is contained in:
@@ -2103,6 +2103,10 @@ GameOptions load_game_options(const fs::path& path) {
|
||||
|
||||
// ---- UserSettings (settings.toml) — launcher-written override layer ----
|
||||
|
||||
static bool valid_user_audio_freq(int n) {
|
||||
return n == 32040 || n == 32000 || n == 44100 || n == 48000;
|
||||
}
|
||||
|
||||
UserSettings load_user_settings(const fs::path& path) {
|
||||
UserSettings s;
|
||||
std::error_code ec;
|
||||
@@ -2245,6 +2249,13 @@ UserSettings load_user_settings(const fs::path& path) {
|
||||
}
|
||||
if (doc.contains("audio")) {
|
||||
const toml::value& a = toml::find(doc, "audio");
|
||||
if (a.contains("frequency")) try_get([&]{
|
||||
const auto n = toml::find<int64_t>(a, "frequency");
|
||||
if (valid_user_audio_freq((int)n)) {
|
||||
s.audio_freq = (int)n;
|
||||
s.has_audio_freq = true;
|
||||
}
|
||||
});
|
||||
if (a.contains("spu_hq")) try_get([&]{
|
||||
s.spu_hq = toml::find<bool>(a, "spu_hq"); s.has_spu_hq = true;
|
||||
});
|
||||
@@ -2487,6 +2498,8 @@ bool save_user_settings(const fs::path& path, const UserSettings& s) {
|
||||
if (s.has_rewind_interval)
|
||||
f << "rewind_interval = " << s.rewind_interval << "\n";
|
||||
f << "\n[audio]\n";
|
||||
if (s.has_audio_freq)
|
||||
f << "frequency = " << s.audio_freq << "\n";
|
||||
if (s.has_spu_hq)
|
||||
f << "spu_hq = " << (s.spu_hq ? "true" : "false") << "\n";
|
||||
if (s.has_hotkey_pad_rewind || s.has_hotkey_pad_save_state_menu) {
|
||||
|
||||
@@ -1115,6 +1115,7 @@ struct UserSettings {
|
||||
bool has_hotkey_pad_save_state_menu = false; int hotkey_pad_save_state_menu = 2040; /* select+r1 */
|
||||
// [audio]
|
||||
bool has_spu_hq = false; bool spu_hq = false;
|
||||
bool has_audio_freq = false; int audio_freq = 44100;
|
||||
// [bios] / [disc] / [memcard]
|
||||
bool has_bios_path = false; std::filesystem::path bios_path;
|
||||
bool has_disc_path = false; std::filesystem::path disc_path;
|
||||
|
||||
@@ -122,13 +122,17 @@ static void test_user_settings_read() {
|
||||
fs::path p = write_temp("psxrecomp_pgxp_settings.toml",
|
||||
"[video]\n"
|
||||
"geometry_correction = true\n"
|
||||
"perspective_texturing = false\n");
|
||||
"perspective_texturing = false\n"
|
||||
"[audio]\n"
|
||||
"frequency = 48000\n");
|
||||
auto us = PSXRecompV4::load_user_settings(p);
|
||||
check(!us.parse_error, "settings.toml parses");
|
||||
check(us.has_geometry_correction && us.geometry_correction,
|
||||
"settings.toml geometry_correction = true read");
|
||||
check(us.has_perspective_texturing && !us.perspective_texturing,
|
||||
"settings.toml perspective_texturing = false read (explicit off)");
|
||||
check(us.has_audio_freq && us.audio_freq == 48000,
|
||||
"settings.toml audio frequency read");
|
||||
fs::remove(p);
|
||||
}
|
||||
|
||||
@@ -150,6 +154,7 @@ static void test_user_settings_round_trip() {
|
||||
PSXRecompV4::UserSettings out;
|
||||
out.geometry_correction = true; out.has_geometry_correction = true;
|
||||
out.perspective_texturing = true; out.has_perspective_texturing = true;
|
||||
out.audio_freq = 48000; out.has_audio_freq = true;
|
||||
|
||||
fs::path p = fs::temp_directory_path() / "psxrecomp_pgxp_roundtrip.toml";
|
||||
check(PSXRecompV4::save_user_settings(p, out), "save_user_settings writes");
|
||||
@@ -160,6 +165,8 @@ static void test_user_settings_round_trip() {
|
||||
"geometry_correction survives a save/load round trip");
|
||||
check(back.has_perspective_texturing && back.perspective_texturing,
|
||||
"perspective_texturing survives a save/load round trip");
|
||||
check(back.has_audio_freq && back.audio_freq == 48000,
|
||||
"audio frequency survives a save/load round trip");
|
||||
fs::remove(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,17 @@ if(BUILD_TESTING)
|
||||
PSX_OVERLAY_DLL_BUILD=1)
|
||||
add_test(NAME psx_icache_fastpath_test COMMAND psx_icache_fastpath_test)
|
||||
|
||||
add_executable(host_keymap_test
|
||||
tests/test_host_keymap.c
|
||||
src/host_keymap.c)
|
||||
target_include_directories(host_keymap_test PRIVATE
|
||||
include ${PSX_SDL_INCLUDE_DIRS})
|
||||
target_compile_definitions(host_keymap_test PRIVATE
|
||||
SDL_MAIN_HANDLED
|
||||
$<$<BOOL:${PSX_SDL3}>:PSX_SDL3=1>)
|
||||
target_link_libraries(host_keymap_test PRIVATE ${PSX_SDL_LIBRARIES})
|
||||
add_test(NAME host_keymap_test COMMAND host_keymap_test)
|
||||
|
||||
# Registered since before this change, but it did NOT LINK on origin/master:
|
||||
# sio.c had grown psx_read_word / psx_get_cycle_count / i_mask /
|
||||
# g_debug_last_store_pc references that this test's stub block never gained,
|
||||
|
||||
@@ -10,13 +10,15 @@ extern "C" {
|
||||
|
||||
/*
|
||||
* Host hotkeys from recomp-ui config.ini [KeyMap] (SDL key names + Ctrl+/Alt+/
|
||||
* Shift+ prefixes). VolumeUp / VolumeDown are honored by the runtime; missing
|
||||
* lines keep the historic keypad +/- defaults.
|
||||
* Shift+ prefixes). Missing lines keep the historic runtime defaults.
|
||||
*/
|
||||
|
||||
typedef enum HostKeymapAction {
|
||||
HOST_KEYMAP_VOLUME_UP = 0,
|
||||
HOST_KEYMAP_FULLSCREEN = 0,
|
||||
HOST_KEYMAP_TURBO,
|
||||
HOST_KEYMAP_VOLUME_UP,
|
||||
HOST_KEYMAP_VOLUME_DOWN,
|
||||
HOST_KEYMAP_DISPLAY_PERF,
|
||||
HOST_KEYMAP_REWIND, /* default F8 */
|
||||
HOST_KEYMAP_SAVE_STATE_MENU, /* default F7 */
|
||||
HOST_KEYMAP_ACTION_COUNT
|
||||
@@ -28,6 +30,9 @@ void host_keymap_load(const char *config_ini_path);
|
||||
/* 1 if (keycode, mod) matches a binding for `action`. */
|
||||
int host_keymap_match(HostKeymapAction action, int keycode, int mod);
|
||||
|
||||
/* 1 if any configured bind for `action` is currently held. */
|
||||
int host_keymap_down(HostKeymapAction action, const uint8_t *keys, int mod);
|
||||
|
||||
/* Format the primary bind for `action` into out (e.g. "F8", "Ctrl+R",
|
||||
* "GRAVE"). Punctuation key names are tokenized for the rewind overlay font.
|
||||
* Returns out, or "F8" / "" on empty action. Always NUL-terminates. */
|
||||
|
||||
@@ -19,6 +19,9 @@ extern "C" {
|
||||
/* Top-left text toast. duration_ms <= 0 uses 2000. */
|
||||
void host_osd_push(const char *msg, int duration_ms);
|
||||
|
||||
/* Persistent top-left status text. Passing NULL/empty clears it. */
|
||||
void host_osd_set_status(const char *msg);
|
||||
|
||||
/* Right-side vertical volume bar (percent clamped 0..100). */
|
||||
void host_osd_show_volume(int percent, int duration_ms);
|
||||
|
||||
|
||||
+28
-10
@@ -441,18 +441,34 @@ endif()
|
||||
|
||||
# Local rewind: full rbengine when netplay is on; otherwise compile snap_ring.c
|
||||
# only (no recomp-net / sched / hash_confirm). Never both — duplicate symbols.
|
||||
# Defaults ON because local rewind is useful for every PSX title. If a title
|
||||
# deliberately opts out with -DPSX_REWIND=OFF, recomp-ui hides the Rewind
|
||||
# controls instead of exposing dead hotkeys.
|
||||
option(PSX_REWIND "Build and expose local rewind support" ON)
|
||||
set(PSXRECOMP_HAS_RBENGINE_SNAP FALSE)
|
||||
set(PSXRECOMP_RBENGINE_SNAP_INCLUDE "")
|
||||
if(PSXRECOMP_HAS_RBENGINE)
|
||||
set(PSXRECOMP_HAS_RBENGINE_SNAP TRUE)
|
||||
elseif(RECOMP_RBENGINE_ROOT
|
||||
AND EXISTS "${RECOMP_RBENGINE_ROOT}/src/snap/rbe_snap_ring.c"
|
||||
AND EXISTS "${RECOMP_RBENGINE_ROOT}/include/retcomm_rbengine/snap_ring.h")
|
||||
list(APPEND PSXRECOMP_RUNTIME_SOURCES
|
||||
${RECOMP_RBENGINE_ROOT}/src/snap/rbe_snap_ring.c)
|
||||
set(PSXRECOMP_HAS_RBENGINE_SNAP TRUE)
|
||||
set(PSXRECOMP_RBENGINE_SNAP_INCLUDE "${RECOMP_RBENGINE_ROOT}/include")
|
||||
message(STATUS "psxrecomp: rewind snap_ring (${RECOMP_RBENGINE_ROOT})")
|
||||
if(PSX_REWIND)
|
||||
if(PSXRECOMP_HAS_RBENGINE)
|
||||
set(PSXRECOMP_HAS_RBENGINE_SNAP TRUE)
|
||||
elseif(RECOMP_RBENGINE_ROOT
|
||||
AND EXISTS "${RECOMP_RBENGINE_ROOT}/src/snap/rbe_snap_ring.c"
|
||||
AND EXISTS "${RECOMP_RBENGINE_ROOT}/include/retcomm_rbengine/snap_ring.h")
|
||||
list(APPEND PSXRECOMP_RUNTIME_SOURCES
|
||||
${RECOMP_RBENGINE_ROOT}/src/snap/rbe_snap_ring.c)
|
||||
set(PSXRECOMP_HAS_RBENGINE_SNAP TRUE)
|
||||
set(PSXRECOMP_RBENGINE_SNAP_INCLUDE "${RECOMP_RBENGINE_ROOT}/include")
|
||||
message(STATUS "psxrecomp: rewind snap_ring (${RECOMP_RBENGINE_ROOT})")
|
||||
endif()
|
||||
if(NOT PSXRECOMP_HAS_RBENGINE_SNAP)
|
||||
message(FATAL_ERROR
|
||||
"psxrecomp: PSX_REWIND=ON exposes the Rewind launcher controls "
|
||||
"but no retcomm-rbengine snap-ring backend was found.\n"
|
||||
" git submodule update --init lib/retcomm-rbengine\n"
|
||||
" or -DRECOMP_RBENGINE_ROOT=/path/to/retcomm-rbengine\n"
|
||||
" or configure with -DPSX_REWIND=OFF to hide Rewind.")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "psxrecomp: local rewind disabled (PSX_REWIND=OFF)")
|
||||
endif()
|
||||
|
||||
# Lobby WebSocket client helpers are vendored under runtime/src/lobby_ws/
|
||||
@@ -1358,6 +1374,8 @@ function(psxrecomp_add_runtime_target target)
|
||||
list(APPEND _psx_recomp_ui_args BRAND "${PSXRT_LAUNCHER_BRAND}")
|
||||
endif()
|
||||
recomp_target_launcher_ui(${target} ${_psx_recomp_ui_args})
|
||||
target_compile_definitions(${target} PRIVATE
|
||||
RECOMP_UI_PSX_HAS_REWIND=$<BOOL:${PSXRECOMP_HAS_RBENGINE_SNAP}>)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR MINGW)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
typedef struct HostKeyBind {
|
||||
int keycode;
|
||||
int scancode;
|
||||
int mods; /* KMOD_CTRL | KMOD_ALT | KMOD_SHIFT subset */
|
||||
} HostKeyBind;
|
||||
|
||||
@@ -65,32 +66,44 @@ static void clear_all(void) {
|
||||
memset(s_actions, 0, sizeof(s_actions));
|
||||
}
|
||||
|
||||
static void add_bind(HostKeymapAction action, int keycode, int mods) {
|
||||
static void add_bind(HostKeymapAction action, int keycode, int scancode, int mods) {
|
||||
HostKeyAction *a;
|
||||
if (action < 0 || action >= HOST_KEYMAP_ACTION_COUNT) return;
|
||||
if (keycode == 0 || keycode == SDLK_UNKNOWN) return;
|
||||
a = &s_actions[action];
|
||||
if (a->count >= HOST_KEYMAP_MAX_BINDS) return;
|
||||
a->binds[a->count].keycode = keycode;
|
||||
a->binds[a->count].scancode = scancode;
|
||||
a->binds[a->count].mods = mods;
|
||||
a->count++;
|
||||
}
|
||||
|
||||
static void apply_defaults(void) {
|
||||
if (s_actions[HOST_KEYMAP_FULLSCREEN].count == 0) {
|
||||
add_bind(HOST_KEYMAP_FULLSCREEN, (int)SDLK_RETURN, (int)SDL_SCANCODE_RETURN, KMOD_ALT);
|
||||
add_bind(HOST_KEYMAP_FULLSCREEN, (int)SDLK_f, (int)SDL_SCANCODE_F, KMOD_CTRL);
|
||||
}
|
||||
if (s_actions[HOST_KEYMAP_TURBO].count == 0)
|
||||
add_bind(HOST_KEYMAP_TURBO, (int)SDLK_TAB, (int)SDL_SCANCODE_TAB, 0);
|
||||
if (s_actions[HOST_KEYMAP_VOLUME_UP].count == 0)
|
||||
add_bind(HOST_KEYMAP_VOLUME_UP, (int)SDLK_KP_PLUS, 0);
|
||||
add_bind(HOST_KEYMAP_VOLUME_UP, (int)SDLK_KP_PLUS, (int)SDL_SCANCODE_KP_PLUS, 0);
|
||||
if (s_actions[HOST_KEYMAP_VOLUME_DOWN].count == 0)
|
||||
add_bind(HOST_KEYMAP_VOLUME_DOWN, (int)SDLK_KP_MINUS, 0);
|
||||
add_bind(HOST_KEYMAP_VOLUME_DOWN, (int)SDLK_KP_MINUS, (int)SDL_SCANCODE_KP_MINUS, 0);
|
||||
if (s_actions[HOST_KEYMAP_DISPLAY_PERF].count == 0)
|
||||
add_bind(HOST_KEYMAP_DISPLAY_PERF, (int)SDLK_f, (int)SDL_SCANCODE_F, 0);
|
||||
#if defined(PSX_HAS_RBENGINE_SNAP)
|
||||
if (s_actions[HOST_KEYMAP_REWIND].count == 0)
|
||||
add_bind(HOST_KEYMAP_REWIND, (int)SDLK_F8, 0);
|
||||
add_bind(HOST_KEYMAP_REWIND, (int)SDLK_F8, (int)SDL_SCANCODE_F8, 0);
|
||||
#endif
|
||||
if (s_actions[HOST_KEYMAP_SAVE_STATE_MENU].count == 0)
|
||||
add_bind(HOST_KEYMAP_SAVE_STATE_MENU, (int)SDLK_F7, 0);
|
||||
add_bind(HOST_KEYMAP_SAVE_STATE_MENU, (int)SDLK_F7, (int)SDL_SCANCODE_F7, 0);
|
||||
}
|
||||
|
||||
/* Parse one "Ctrl+Alt+PageUp" token into key+mods. */
|
||||
static void parse_one_token(HostKeymapAction action, char *tok) {
|
||||
int mods = 0;
|
||||
SDL_Keycode key;
|
||||
SDL_Scancode sc;
|
||||
trim_inplace(tok);
|
||||
if (!tok[0] || ieq(tok, "(unbound)") || ieq(tok, "None")) return;
|
||||
for (;;) {
|
||||
@@ -114,7 +127,8 @@ static void parse_one_token(HostKeymapAction action, char *tok) {
|
||||
fprintf(stderr, "host_keymap: unknown key '%s'\n", tok);
|
||||
return;
|
||||
}
|
||||
add_bind(action, (int)key, mods);
|
||||
sc = SDL_GetScancodeFromName(tok);
|
||||
add_bind(action, (int)key, (int)sc, mods);
|
||||
}
|
||||
|
||||
static void parse_value(HostKeymapAction action, const char *value) {
|
||||
@@ -133,8 +147,11 @@ static void parse_value(HostKeymapAction action, const char *value) {
|
||||
}
|
||||
|
||||
static HostKeymapAction action_for_key(const char *name) {
|
||||
if (ieq(name, "Fullscreen")) return HOST_KEYMAP_FULLSCREEN;
|
||||
if (ieq(name, "Turbo")) return HOST_KEYMAP_TURBO;
|
||||
if (ieq(name, "VolumeUp")) return HOST_KEYMAP_VOLUME_UP;
|
||||
if (ieq(name, "VolumeDown")) return HOST_KEYMAP_VOLUME_DOWN;
|
||||
if (ieq(name, "DisplayPerf")) return HOST_KEYMAP_DISPLAY_PERF;
|
||||
if (ieq(name, "Rewind")) return HOST_KEYMAP_REWIND;
|
||||
if (ieq(name, "SaveStateMenu")) return HOST_KEYMAP_SAVE_STATE_MENU;
|
||||
return HOST_KEYMAP_ACTION_COUNT;
|
||||
@@ -201,6 +218,21 @@ int host_keymap_match(HostKeymapAction action, int keycode, int mod) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int host_keymap_down(HostKeymapAction action, const uint8_t *keys, int mod) {
|
||||
const HostKeyAction *a;
|
||||
const int relevant = (int)(KMOD_CTRL | KMOD_ALT | KMOD_SHIFT);
|
||||
int i;
|
||||
if (!keys || action < 0 || action >= HOST_KEYMAP_ACTION_COUNT) return 0;
|
||||
a = &s_actions[action];
|
||||
for (i = 0; i < a->count; i++) {
|
||||
const int sc = a->binds[i].scancode;
|
||||
if (sc <= 0 || sc >= SDL_NUM_SCANCODES) continue;
|
||||
if (!keys[sc]) continue;
|
||||
if ((mod & relevant) == a->binds[i].mods) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Rewind overlay FONT8 only draws ASCII 32..90 (space..Z); lowercase is
|
||||
* uppercased by the drawer. Map punctuation / odd SDL single-glyph names to
|
||||
* short tokens so binds like backtick don't render as "?". */
|
||||
@@ -257,9 +289,12 @@ const char *host_keymap_label(HostKeymapAction action, char *out, size_t cap) {
|
||||
if (action < 0 || action >= HOST_KEYMAP_ACTION_COUNT) return out;
|
||||
a = &s_actions[action];
|
||||
if (a->count <= 0) {
|
||||
#if defined(PSX_HAS_RBENGINE_SNAP)
|
||||
if (action == HOST_KEYMAP_REWIND)
|
||||
snprintf(out, cap, "F8");
|
||||
else if (action == HOST_KEYMAP_SAVE_STATE_MENU)
|
||||
else
|
||||
#endif
|
||||
if (action == HOST_KEYMAP_SAVE_STATE_MENU)
|
||||
snprintf(out, cap, "F7");
|
||||
return out;
|
||||
}
|
||||
|
||||
+38
-7
@@ -130,6 +130,10 @@ static Uint32 s_expire_ms;
|
||||
static int s_active;
|
||||
static int s_img_dirty = 1;
|
||||
|
||||
static char s_status_msg[OSD_MAX_CHARS];
|
||||
static int s_status_active;
|
||||
static int s_status_dirty = 1;
|
||||
|
||||
static int s_volume = 100; /* host master 0..100 */
|
||||
static int s_vol_pct = 100; /* last shown bar fill */
|
||||
static Uint32 s_vol_expire_ms;
|
||||
@@ -197,8 +201,8 @@ static int vol_visible(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void rasterize_text(void) {
|
||||
int n = (int)strlen(s_msg);
|
||||
static void rasterize_text(const char *msg) {
|
||||
int n = (int)strlen(msg);
|
||||
if (n > OSD_MAX_CHARS) n = OSD_MAX_CHARS;
|
||||
s_img_w = (OSD_PAD_X * 2 + n * OSD_GLYPH_W) * OSD_SCALE;
|
||||
s_img_h = (OSD_PAD_Y * 2 + OSD_GLYPH_H) * OSD_SCALE;
|
||||
@@ -211,7 +215,7 @@ static void rasterize_text(void) {
|
||||
s_img[i] = 0xFF202020u;
|
||||
|
||||
for (int ci = 0; ci < n; ci++) {
|
||||
unsigned char ch = (unsigned char)s_msg[ci];
|
||||
unsigned char ch = (unsigned char)msg[ci];
|
||||
if (ch < 32 || ch > 126) ch = '?';
|
||||
const uint8_t *g = FONT8X8[ch - 32];
|
||||
for (int row = 0; row < OSD_GLYPH_H; row++) {
|
||||
@@ -338,6 +342,25 @@ void host_osd_push(const char *msg, int duration_ms) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void host_osd_set_status(const char *msg) {
|
||||
#if !HOST_OSD_VISUAL
|
||||
(void)msg;
|
||||
return;
|
||||
#else
|
||||
if (!msg || !msg[0]) {
|
||||
if (s_status_active) s_needs_clear = 1;
|
||||
s_status_msg[0] = '\0';
|
||||
s_status_active = 0;
|
||||
s_status_dirty = 1;
|
||||
return;
|
||||
}
|
||||
snprintf(s_status_msg, sizeof(s_status_msg), "%s", msg);
|
||||
s_status_active = 1;
|
||||
s_status_dirty = 1;
|
||||
s_needs_clear = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void host_osd_show_volume(int percent, int duration_ms) {
|
||||
#if !HOST_OSD_VISUAL
|
||||
(void)percent;
|
||||
@@ -375,7 +398,7 @@ int host_osd_needs_present(void) {
|
||||
#if !HOST_OSD_VISUAL
|
||||
return 0;
|
||||
#else
|
||||
if (msg_visible() || vol_visible()) return 1;
|
||||
if (msg_visible() || s_status_active || vol_visible()) return 1;
|
||||
return s_needs_clear;
|
||||
#endif
|
||||
}
|
||||
@@ -387,13 +410,21 @@ int host_osd_image(const uint32_t **pixels, int *w, int *h) {
|
||||
if (h) *h = 0;
|
||||
return 0;
|
||||
#else
|
||||
if (!msg_visible()) {
|
||||
const int show_msg = msg_visible();
|
||||
if (!show_msg && !s_status_active) {
|
||||
if (pixels) *pixels = NULL;
|
||||
if (w) *w = 0;
|
||||
if (h) *h = 0;
|
||||
return 0;
|
||||
}
|
||||
if (s_img_dirty) rasterize_text();
|
||||
if (show_msg) {
|
||||
if (s_img_dirty) rasterize_text(s_msg);
|
||||
} else {
|
||||
if (s_status_dirty || s_img_dirty) {
|
||||
rasterize_text(s_status_msg);
|
||||
s_status_dirty = 0;
|
||||
}
|
||||
}
|
||||
if (pixels) *pixels = s_img;
|
||||
if (w) *w = s_img_w;
|
||||
if (h) *h = s_img_h;
|
||||
@@ -426,7 +457,7 @@ void host_osd_present_done(void) {
|
||||
#if !HOST_OSD_VISUAL
|
||||
return;
|
||||
#else
|
||||
if (!s_active && !s_vol_active) s_needs_clear = 0;
|
||||
if (!s_active && !s_status_active && !s_vol_active) s_needs_clear = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+49
-8
@@ -465,6 +465,7 @@ static int post_load_probe_env_on(void) {
|
||||
static Uint64 s_fps_last_time = 0;
|
||||
static uint64_t s_fps_last_frame = 0;
|
||||
static std::string s_fps_base_title;
|
||||
static int s_fps_telemetry_enabled = -1; /* -1 = unread env */
|
||||
static FramePacer s_frame_pacer = { 0 };
|
||||
static int s_turbo_present_skip = 0;
|
||||
static int s_fmv_skip_present_skip = 0;
|
||||
@@ -517,6 +518,27 @@ static void present_session_reset(void) {
|
||||
smooth_60_reset();
|
||||
}
|
||||
|
||||
static int fps_telemetry_enabled(void) {
|
||||
if (s_fps_telemetry_enabled < 0) {
|
||||
const char *e = std::getenv("PSX_FPS_TELEMETRY");
|
||||
s_fps_telemetry_enabled = (e && e[0] && e[0] != '0') ? 1 : 0;
|
||||
}
|
||||
return s_fps_telemetry_enabled;
|
||||
}
|
||||
|
||||
static void fps_telemetry_toggle(void) {
|
||||
const int enabled = fps_telemetry_enabled() ? 0 : 1;
|
||||
s_fps_telemetry_enabled = enabled;
|
||||
s_fps_last_time = 0;
|
||||
s_fps_last_frame = 0;
|
||||
if (!enabled && sdl_window && !s_fps_base_title.empty())
|
||||
SDL_SetWindowTitle(sdl_window, s_fps_base_title.c_str());
|
||||
if (!enabled)
|
||||
host_osd_set_status(NULL);
|
||||
s_fps_base_title.clear();
|
||||
host_osd_push(enabled ? "FPS readout on" : "FPS readout off", 1500);
|
||||
}
|
||||
|
||||
static void post_load_probe_stall_pc_note(uint32_t pc) {
|
||||
if (!pc) return;
|
||||
for (int i = 0; i < s_stall_pc_used; i++) {
|
||||
@@ -1065,6 +1087,7 @@ static int g_fullscreen = 0; /* tri-state: 0 windowed, 1 borderle
|
||||
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) */
|
||||
static int g_audio_freq = 44100; /* host device request */
|
||||
static int g_auto_skip_fmv = 0; /* skip FMVs the instant they're detected */
|
||||
static int g_rewind_depth = 50; /* local rewind snap count (50/100/150/200) */
|
||||
static int g_rewind_interval = 15; /* frames between snaps (1/4/8/12/15) */
|
||||
@@ -5635,14 +5658,14 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
|
||||
* than presents so turbo and skipped-frame modes still report game speed.
|
||||
* Skip during netplay post-load barrier — admit is stalled and the window
|
||||
* is not updating, so a climbing FPS line is misleading. */
|
||||
if (!psx_netplay_in_load_barrier()) {
|
||||
if (fps_telemetry_enabled() && !psx_netplay_in_load_barrier()) {
|
||||
extern uint64_t s_frame_count;
|
||||
const Uint64 now = SDL_GetPerformanceCounter();
|
||||
const Uint64 frequency = SDL_GetPerformanceFrequency();
|
||||
if (!s_fps_last_time) {
|
||||
s_fps_last_time = now;
|
||||
s_fps_last_frame = s_frame_count;
|
||||
if (sdl_window) {
|
||||
if (sdl_window && s_fps_base_title.empty()) {
|
||||
const char *title = SDL_GetWindowTitle(sdl_window);
|
||||
if (title) s_fps_base_title = title;
|
||||
}
|
||||
@@ -5656,6 +5679,11 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
|
||||
s_fps_base_title.c_str(), fps, speed);
|
||||
SDL_SetWindowTitle(sdl_window, title);
|
||||
}
|
||||
if (!g_headless) {
|
||||
char osd[64];
|
||||
snprintf(osd, sizeof(osd), "FPS %.0f %.2fx", fps, speed);
|
||||
host_osd_set_status(osd);
|
||||
}
|
||||
if (netplay_timing_on() && s_np_timing_frames > 0) {
|
||||
const double invf = 1000.0 / (double)frequency;
|
||||
const double admit_ms =
|
||||
@@ -5773,6 +5801,11 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
|
||||
debug_force_cd_reinsert();
|
||||
host_osd_push("CD reinsert", 1500);
|
||||
}
|
||||
else if (!key_repeat &&
|
||||
host_keymap_match(HOST_KEYMAP_DISPLAY_PERF, (int)key,
|
||||
(int)mod)) {
|
||||
fps_telemetry_toggle();
|
||||
}
|
||||
/* Host volume: config.ini [KeyMap] VolumeUp/VolumeDown
|
||||
* (defaults: keypad +/-). 5% steps; shows right-side bar. */
|
||||
else if (host_keymap_match(HOST_KEYMAP_VOLUME_UP, (int)key,
|
||||
@@ -5790,8 +5823,9 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
|
||||
* set in both SDL_WINDOW_FULLSCREEN and
|
||||
* SDL_WINDOW_FULLSCREEN_DESKTOP, so testing just that bit
|
||||
* detects "currently fullscreen, either mode". */
|
||||
else if ((key == SDLK_RETURN && (mod & KMOD_ALT)) ||
|
||||
(key == SDLK_f && (mod & (KMOD_GUI | KMOD_CTRL)))) {
|
||||
else if (!key_repeat &&
|
||||
host_keymap_match(HOST_KEYMAP_FULLSCREEN, (int)key,
|
||||
(int)mod)) {
|
||||
Uint32 is_fs = SDL_GetWindowFlags(sdl_window) &
|
||||
SDL_WINDOW_FULLSCREEN;
|
||||
if (is_fs) {
|
||||
@@ -6004,7 +6038,7 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
|
||||
const Uint8* keys = SDL_GetKeyboardState(NULL);
|
||||
static int turbo_skip = 0;
|
||||
const int TURBO_PRESENT_EVERY = 30;
|
||||
if (keys[SDL_SCANCODE_TAB]) {
|
||||
if (host_keymap_down(HOST_KEYMAP_TURBO, keys, (int)SDL_GetModState())) {
|
||||
turbo_skip = (turbo_skip + 1) % TURBO_PRESENT_EVERY;
|
||||
if (turbo_skip != 0) {
|
||||
ep.skip_pace = 1;
|
||||
@@ -10657,6 +10691,7 @@ int main(int argc, char** argv) {
|
||||
g_video_aspect_num = us.aspect_num;
|
||||
g_video_aspect_den = us.aspect_den;
|
||||
}
|
||||
if (us.has_audio_freq) g_audio_freq = us.audio_freq;
|
||||
if (us.has_spu_hq) g_audio_spu_hq = us.spu_hq;
|
||||
if (us.has_rewind_depth) g_rewind_depth = us.rewind_depth;
|
||||
if (us.has_rewind_interval) g_rewind_interval = us.rewind_interval;
|
||||
@@ -11073,6 +11108,7 @@ int main(int argc, char** argv) {
|
||||
seed.has_frame_interpolation_fps = true;
|
||||
seed.aspect_num = g_video_aspect_num;
|
||||
seed.aspect_den = g_video_aspect_den; seed.has_aspect_ratio = true;
|
||||
seed.audio_freq = g_audio_freq; seed.has_audio_freq = true;
|
||||
seed.spu_hq = g_audio_spu_hq; seed.has_spu_hq = true;
|
||||
seed.rewind_depth = g_rewind_depth; seed.has_rewind_depth = true;
|
||||
seed.rewind_interval = g_rewind_interval; seed.has_rewind_interval = true;
|
||||
@@ -11178,7 +11214,7 @@ int main(int argc, char** argv) {
|
||||
ls.widescreen = (seed.aspect_num == 16 && seed.aspect_den == 9) ? 1 : 0;
|
||||
ls.widescreen_hud = ls.widescreen;
|
||||
ls.enable_audio = 1;
|
||||
ls.audio_freq = 44100;
|
||||
ls.audio_freq = seed.audio_freq;
|
||||
ls.volume = host_volume_get();
|
||||
{
|
||||
const int n = std::min(PSX_MAX_PLAYERS, RECOMP_LAUNCHER_MAX_PLAYERS);
|
||||
@@ -11466,6 +11502,7 @@ int main(int argc, char** argv) {
|
||||
seed.screen_kind = ls.screen_kind; seed.has_screen_kind = true;
|
||||
seed.frame_interpolation = ls.frame_interp != 0; seed.has_frame_interpolation = true;
|
||||
seed.frame_interpolation_fps = ls.frame_interp_fps; seed.has_frame_interpolation_fps = true;
|
||||
seed.audio_freq = ls.audio_freq; seed.has_audio_freq = true;
|
||||
seed.spu_hq = ls.spu_hq != 0; seed.has_spu_hq = true;
|
||||
seed.rewind_depth = ls.rewind_depth > 0 ? ls.rewind_depth : 50;
|
||||
seed.has_rewind_depth = true;
|
||||
@@ -11677,6 +11714,7 @@ int main(int argc, char** argv) {
|
||||
g_frame_interpolation_fps = seed.frame_interpolation_fps;
|
||||
g_video_aspect_num = seed.aspect_num;
|
||||
g_video_aspect_den = seed.aspect_den;
|
||||
g_audio_freq = seed.audio_freq;
|
||||
g_audio_spu_hq = seed.spu_hq;
|
||||
g_rewind_depth = seed.has_rewind_depth && seed.rewind_depth > 0
|
||||
? seed.rewind_depth : 50;
|
||||
@@ -12250,7 +12288,7 @@ session_reboot:
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0) {
|
||||
PsxSdlAudioSpec want = {};
|
||||
PsxSdlAudioSpec have = {};
|
||||
want.freq = 44100;
|
||||
want.freq = g_audio_freq;
|
||||
want.format = AUDIO_S16SYS;
|
||||
want.channels = 2;
|
||||
want.samples = 1024;
|
||||
@@ -13008,7 +13046,7 @@ soft_return_lobby:
|
||||
(g_video_aspect_num == 16 && g_video_aspect_den == 9) ? 1 : 0;
|
||||
ls.widescreen_hud = ls.widescreen;
|
||||
ls.enable_audio = 1;
|
||||
ls.audio_freq = 44100;
|
||||
ls.audio_freq = g_audio_freq;
|
||||
ls.volume = host_volume_get();
|
||||
ls.window_width = g_video_win_w;
|
||||
ls.renderer = g_video_renderer;
|
||||
@@ -13279,6 +13317,8 @@ soft_return_lobby:
|
||||
us.has_frame_interpolation = true;
|
||||
us.frame_interpolation_fps = ls.frame_interp_fps;
|
||||
us.has_frame_interpolation_fps = true;
|
||||
us.audio_freq = ls.audio_freq;
|
||||
us.has_audio_freq = true;
|
||||
us.spu_hq = ls.spu_hq != 0;
|
||||
us.has_spu_hq = true;
|
||||
us.rewind_depth = ls.rewind_depth > 0 ? ls.rewind_depth : 50;
|
||||
@@ -13337,6 +13377,7 @@ soft_return_lobby:
|
||||
g_fullscreen = ls.fullscreen != 0;
|
||||
g_frame_interpolation = ls.frame_interp ? 1 : 0;
|
||||
g_frame_interpolation_fps = ls.frame_interp_fps;
|
||||
g_audio_freq = ls.audio_freq;
|
||||
g_audio_spu_hq = ls.spu_hq != 0;
|
||||
if (ls.rewind_depth > 0) {
|
||||
g_rewind_depth = ls.rewind_depth;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "host_keymap.h"
|
||||
#include "psx_sdl.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
static void check(int cond, const char *what) {
|
||||
if (!cond) {
|
||||
fprintf(stderr, "FAIL: %s\n", what);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static int mod_ctrl(void) { return (int)KMOD_CTRL; }
|
||||
static int mod_alt(void) { return (int)KMOD_ALT; }
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uint8_t keys[SDL_NUM_SCANCODES];
|
||||
const char *cfg = "host_keymap_test.ini";
|
||||
FILE *f;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
memset(keys, 0, sizeof(keys));
|
||||
host_keymap_load(NULL);
|
||||
check(host_keymap_match(HOST_KEYMAP_FULLSCREEN, (int)SDLK_RETURN, mod_alt()),
|
||||
"default fullscreen includes Alt+Return");
|
||||
check(host_keymap_match(HOST_KEYMAP_FULLSCREEN, (int)SDLK_f, mod_ctrl()),
|
||||
"default fullscreen includes Ctrl+F");
|
||||
keys[SDL_SCANCODE_TAB] = 1;
|
||||
check(host_keymap_down(HOST_KEYMAP_TURBO, keys, 0),
|
||||
"default turbo is held by Tab");
|
||||
keys[SDL_SCANCODE_TAB] = 0;
|
||||
check(host_keymap_match(HOST_KEYMAP_VOLUME_UP, (int)SDLK_KP_PLUS, 0),
|
||||
"default volume up is keypad plus");
|
||||
check(host_keymap_match(HOST_KEYMAP_VOLUME_DOWN, (int)SDLK_KP_MINUS, 0),
|
||||
"default volume down is keypad minus");
|
||||
check(host_keymap_match(HOST_KEYMAP_DISPLAY_PERF, (int)SDLK_f, 0),
|
||||
"default display perf is F");
|
||||
|
||||
f = fopen(cfg, "wb");
|
||||
check(f != NULL, "create temporary config.ini");
|
||||
if (!f) return 1;
|
||||
fputs("[KeyMap]\n"
|
||||
"Fullscreen = F11\n"
|
||||
"Turbo = Q\n"
|
||||
"VolumeUp = Up\n"
|
||||
"VolumeDown = Down\n"
|
||||
"DisplayPerf = F10\n",
|
||||
f);
|
||||
fclose(f);
|
||||
|
||||
host_keymap_load(cfg);
|
||||
check(!host_keymap_match(HOST_KEYMAP_FULLSCREEN, (int)SDLK_f, mod_ctrl()),
|
||||
"fullscreen rebind disables Ctrl+F fallback");
|
||||
check(host_keymap_match(HOST_KEYMAP_FULLSCREEN, (int)SDLK_F11, 0),
|
||||
"fullscreen rebind uses F11");
|
||||
memset(keys, 0, sizeof(keys));
|
||||
keys[SDL_SCANCODE_TAB] = 1;
|
||||
check(!host_keymap_down(HOST_KEYMAP_TURBO, keys, 0),
|
||||
"turbo rebind disables Tab fallback");
|
||||
keys[SDL_SCANCODE_TAB] = 0;
|
||||
keys[SDL_SCANCODE_Q] = 1;
|
||||
check(host_keymap_down(HOST_KEYMAP_TURBO, keys, 0),
|
||||
"turbo rebind uses Q");
|
||||
check(host_keymap_match(HOST_KEYMAP_VOLUME_UP, (int)SDLK_UP, 0),
|
||||
"volume up rebind uses Up");
|
||||
check(host_keymap_match(HOST_KEYMAP_VOLUME_DOWN, (int)SDLK_DOWN, 0),
|
||||
"volume down rebind uses Down");
|
||||
check(!host_keymap_match(HOST_KEYMAP_DISPLAY_PERF, (int)SDLK_f, 0),
|
||||
"display perf rebind disables F fallback");
|
||||
check(host_keymap_match(HOST_KEYMAP_DISPLAY_PERF, (int)SDLK_F10, 0),
|
||||
"display perf rebind uses F10");
|
||||
|
||||
remove(cfg);
|
||||
if (failures) {
|
||||
fprintf(stderr, "host_keymap_test: %d failure(s)\n", failures);
|
||||
return 1;
|
||||
}
|
||||
printf("PASS: host keymap defaults and rebinds\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user