keybinds: dual bindings per control (primary + alt, comma ini syntax)

This commit is contained in:
Alexandros Mandravillis
2026-08-06 12:28:58 +03:00
committed by Matthew Stanley
parent e4658ee680
commit 63db8f430d
2 changed files with 69 additions and 12 deletions
+7 -1
View File
@@ -93,7 +93,13 @@ const char *psx_keybinds_button_name(int button); /* "up".."rs_right"
const char *psx_keybinds_button_label(int button); /* pretty, e.g. "Cross" */
SDL_Scancode psx_keybinds_get_button(int player, int button);
void psx_keybinds_set_button(int player, int button, SDL_Scancode sc);
/* Reset one player's bindings to the built-in defaults (same map for every slot). */
/* Alternate (secondary) binding per control: both the primary and the alt
* assert the input (e.g. cross = X, Mouse1). Stored in keybinds.ini as a
* comma-separated second name; SDL_SCANCODE_UNKNOWN = no alt. */
SDL_Scancode psx_keybinds_get_button_alt(int player, int button);
void psx_keybinds_set_button_alt(int player, int button, SDL_Scancode sc);
/* Reset one player's bindings to the shared built-in primary defaults and
* clear that player's alternate bindings. */
void psx_keybinds_reset_player(int player);
/* Persist the current bindings to keybinds.ini (path resolved by
* psx_keybinds_init; call that first). */
+62 -11
View File
@@ -82,6 +82,10 @@
static PsxKeyBinds s_binds = PSXKB_DEFAULTS;
static const PsxKeyBinds s_default_binds = PSXKB_DEFAULTS;
/* Alternate bindings: zero-initialised = SDL_SCANCODE_UNKNOWN = no alt bound.
* Primary OR alt asserts the input; both persist to keybinds.ini as
* "primary, alt" on one line. */
static PsxKeyBinds s_alt_binds;
typedef struct {
const char *name; /* ini key */
@@ -212,11 +216,18 @@ static void derive_ini_path(const char *exe_path) {
snprintf(s_ini_path, sizeof(s_ini_path), "%skeybinds.ini", dir);
}
static void write_player_section(FILE *f, const char *section, const PsxPlayerBinds *pb) {
static void write_player_section(FILE *f, const char *section,
const PsxPlayerBinds *pb,
const PsxPlayerBinds *alt) {
fprintf(f, "[%s]\n", section);
for (int i = 0; i < PSXKB_N; i++) {
SDL_Scancode sc = *(const SDL_Scancode *)((const char *)pb + s_buttons[i].offset);
fprintf(f, "%-9s = %s\n", s_buttons[i].name, scancode_to_name(sc));
SDL_Scancode sc = *(const SDL_Scancode *)((const char *)pb + s_buttons[i].offset);
SDL_Scancode al = *(const SDL_Scancode *)((const char *)alt + s_buttons[i].offset);
if (al != SDL_SCANCODE_UNKNOWN)
fprintf(f, "%-9s = %s, %s\n", s_buttons[i].name,
scancode_to_name(sc), scancode_to_name(al));
else
fprintf(f, "%-9s = %s\n", s_buttons[i].name, scancode_to_name(sc));
}
fprintf(f, "\n");
}
@@ -232,6 +243,8 @@ static void write_ini(const char *path) {
"# Backspace, Escape, Backslash. Use \"None\" to leave an input unbound.\n"
"# Mouse buttons also bind: Mouse1 (left), Mouse2 (middle), Mouse3 (right),\n"
"# Mouse4/Mouse5 (side); aliases LMB, MMB, RMB.\n"
"# Each input accepts an optional SECOND binding after a comma - both\n"
"# assert the input: cross = X, Mouse1\n"
"#\n"
"# Buttons: up/down/left/right, cross/circle/square/triangle, l1/r1/l2/r2,\n"
"# l3/r3 (stick clicks), start/select. ls_* / rs_* are the left/right\n"
@@ -243,7 +256,8 @@ static void write_ini(const char *path) {
for (int p = 0; p < PSXKB_MAX_PLAYERS; ++p) {
char section[16];
snprintf(section, sizeof(section), "player%d", p + 1);
write_player_section(f, section, &s_binds.player[p]);
write_player_section(f, section, &s_binds.player[p],
&s_alt_binds.player[p]);
}
fclose(f);
printf("[Keybinds] Wrote %s\n", path);
@@ -269,7 +283,7 @@ static void promote_empty_players(void) {
static void load_ini(const char *path) {
FILE *f = fopen(path, "r");
if (!f) return;
PsxPlayerBinds *current = NULL;
PsxPlayerBinds *current = NULL, *current_alt = NULL;
char line[256];
while (fgets(line, sizeof(line), f)) {
trim(line);
@@ -278,11 +292,13 @@ static void load_ini(const char *path) {
char *end = strchr(line, ']');
if (end) *end = '\0';
const char *section = line + 1;
current = NULL;
current = NULL; current_alt = NULL;
if (!strncmp(section, "player", 6)) {
int n = atoi(section + 6);
if (n >= 1 && n <= PSXKB_MAX_PLAYERS)
if (n >= 1 && n <= PSXKB_MAX_PLAYERS) {
current = &s_binds.player[n - 1];
current_alt = &s_alt_binds.player[n - 1];
}
}
continue;
}
@@ -293,9 +309,16 @@ static void load_ini(const char *path) {
trim(key); trim(val);
for (char *c = key; *c; c++) *c = (char)tolower((unsigned char)*c);
if (!current) continue;
/* Optional alternate binding after a comma: "cross = X, Mouse1". */
char *comma = strchr(val, ',');
char *alt_val = NULL;
if (comma) { *comma = '\0'; alt_val = comma + 1; trim(val); trim(alt_val); }
for (int i = 0; i < PSXKB_N; i++) {
if (!strcmp(key, s_buttons[i].name)) {
*(SDL_Scancode *)((char *)current + s_buttons[i].offset) = name_to_scancode(val);
*(SDL_Scancode *)((char *)current + s_buttons[i].offset) =
name_to_scancode(val);
*(SDL_Scancode *)((char *)current_alt + s_buttons[i].offset) =
alt_val ? name_to_scancode(alt_val) : SDL_SCANCODE_UNKNOWN;
break;
}
}
@@ -325,11 +348,10 @@ static PsxPlayerBinds *player_binds(int player) {
return &s_binds.player[player - 1];
}
/* Is the scancode at button-def index i currently held for this player?
/* Is this single scancode (keyboard or mouse pseudo-code) currently held?
* Mouse pseudo-scancodes MUST be checked before indexing keys[] — they sit
* beyond the keyboard state array (SDL_NUM_SCANCODES entries). */
static int held(const uint8_t *keys, const PsxPlayerBinds *pb, int i) {
SDL_Scancode sc = *(const SDL_Scancode *)((const char *)pb + s_buttons[i].offset);
static int held_sc(const uint8_t *keys, SDL_Scancode sc) {
if (PSXKB_IS_MOUSE_SC(sc)) {
Uint32 m = SDL_GetMouseState(NULL, NULL);
return (m & SDL_BUTTON((int)sc - PSXKB_MOUSE_SC_BASE)) != 0;
@@ -337,6 +359,18 @@ static int held(const uint8_t *keys, const PsxPlayerBinds *pb, int i) {
return sc != SDL_SCANCODE_UNKNOWN && (int)sc < SDL_NUM_SCANCODES && keys[sc];
}
/* Is the input at button-def index i held for this player, via either its
* primary or its alternate binding? The alternate table uses the same player
* slot as the primary table. */
static int held(const uint8_t *keys, const PsxPlayerBinds *pb, int i) {
ptrdiff_t slot = pb - &s_binds.player[0];
if (slot < 0 || slot >= PSXKB_MAX_PLAYERS) slot = 0;
const PsxPlayerBinds *alt = &s_alt_binds.player[slot];
SDL_Scancode p = *(const SDL_Scancode *)((const char *)pb + s_buttons[i].offset);
SDL_Scancode a = *(const SDL_Scancode *)((const char *)alt + s_buttons[i].offset);
return held_sc(keys, p) || held_sc(keys, a);
}
uint16_t psx_keybinds_pad_word(const uint8_t *keys, int player) {
if (!keys || player < 1 || player > PSXKB_MAX_PLAYERS) return 0xFFFF;
const PsxPlayerBinds *pb = player_binds_c(player);
@@ -393,9 +427,26 @@ void psx_keybinds_set_button(int player, int button, SDL_Scancode sc) {
*(SDL_Scancode *)((char *)player_binds(player) + s_buttons[button].offset) = sc;
}
static PsxPlayerBinds *player_alt_binds(int player) {
if (player < 1 || player > PSXKB_MAX_PLAYERS)
return &s_alt_binds.player[0];
return &s_alt_binds.player[player - 1];
}
SDL_Scancode psx_keybinds_get_button_alt(int player, int button) {
if (button < 0 || button >= PSXKB_N) return SDL_SCANCODE_UNKNOWN;
return *(SDL_Scancode *)((char *)player_alt_binds(player) + s_buttons[button].offset);
}
void psx_keybinds_set_button_alt(int player, int button, SDL_Scancode sc) {
if (button < 0 || button >= PSXKB_N) return;
*(SDL_Scancode *)((char *)player_alt_binds(player) + s_buttons[button].offset) = sc;
}
void psx_keybinds_reset_player(int player) {
if (player < 1 || player > PSXKB_MAX_PLAYERS) return;
*player_binds(player) = s_default_binds.player[0];
memset(player_alt_binds(player), 0, sizeof(PsxPlayerBinds)); /* alts: unbound */
}
void psx_keybinds_save(void) {