Skip to content

Commit da4aac4

Browse files
committed
Gamepad buttons can now be remapped
1 parent 27cc763 commit da4aac4

File tree

6 files changed

+203
-43
lines changed

6 files changed

+203
-43
lines changed

config.c

Lines changed: 124 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Config g_config;
2222
#define C(x) REMAP_SDL_KEYCODE(x) | kKeyMod_Ctrl
2323
#define N 0
2424
static const uint16 kDefaultKbdControls[kKeys_Total] = {
25+
0,
2526
// Controls
2627
_(SDLK_UP), _(SDLK_DOWN), _(SDLK_LEFT), _(SDLK_RIGHT), _(SDLK_RSHIFT), _(SDLK_RETURN), _(SDLK_x), _(SDLK_z), _(SDLK_s), _(SDLK_a), _(SDLK_c), _(SDLK_v),
2728
// LoadState
@@ -53,6 +54,7 @@ typedef struct KeyNameId {
5354
#define M(n) {#n, kKeys_##n, kKeys_##n##_Last - kKeys_##n + 1}
5455
#define S(n) {#n, kKeys_##n, 1}
5556
static const KeyNameId kKeyNameId[] = {
57+
{"Null", kKeys_Null, 65535},
5658
M(Controls), M(Load), M(Save), M(Replay), M(LoadRef), M(ReplayRef),
5759
S(CheatLife), S(CheatKeys), S(CheatEquipment), S(CheatWalkThroughWalls),
5860
S(ClearKeyLog), S(StopReplay), S(Fullscreen), S(Reset),
@@ -69,7 +71,7 @@ static KeyMapHashEnt *keymap_hash;
6971
static int keymap_hash_size;
7072
static bool has_keynameid[countof(kKeyNameId)];
7173

72-
bool KeyMapHash_Add(uint16 key, uint16 cmd) {
74+
static bool KeyMapHash_Add(uint16 key, uint16 cmd) {
7375
if ((keymap_hash_size & 0xff) == 0) {
7476
if (keymap_hash_size > 10000)
7577
Die("Too many keys");
@@ -101,12 +103,12 @@ static int KeyMapHash_Find(uint16 key) {
101103
return ent->cmd;
102104
i = ent->next;
103105
}
104-
return -1;
106+
return 0;
105107
}
106108

107109
int FindCmdForSdlKey(SDL_Keycode code, SDL_Keymod mod) {
108110
if (code & ~(SDLK_SCANCODE_MASK | 0x1ff))
109-
return -1;
111+
return 0;
110112
int key = mod & KMOD_ALT ? kKeyMod_Alt : 0;
111113
key |= mod & KMOD_CTRL ? kKeyMod_Ctrl : 0;
112114
key |= mod & KMOD_SHIFT ? kKeyMod_Shift : 0;
@@ -117,7 +119,7 @@ int FindCmdForSdlKey(SDL_Keycode code, SDL_Keymod mod) {
117119
static void ParseKeyArray(char *value, int cmd, int size) {
118120
char *s;
119121
int i = 0;
120-
for (; i < size && (s = NextDelim(&value, ',')) != NULL; i++, cmd++) {
122+
for (; i < size && (s = NextDelim(&value, ',')) != NULL; i++, cmd += (cmd != 0)) {
121123
if (*s == 0)
122124
continue;
123125
int key_with_mod = 0;
@@ -142,14 +144,120 @@ static void ParseKeyArray(char *value, int cmd, int size) {
142144
}
143145
}
144146

147+
typedef struct GamepadMapEnt {
148+
uint32 modifiers;
149+
uint16 cmd, next;
150+
} GamepadMapEnt;
151+
152+
static uint16 joymap_first[kGamepadBtn_Count];
153+
static GamepadMapEnt *joymap_ents;
154+
static int joymap_size;
155+
static bool has_joypad_controls;
156+
157+
static int CountBits32(uint32 n) {
158+
int count = 0;
159+
for (; n != 0; count++)
160+
n &= (n - 1);
161+
return count;
162+
}
163+
164+
static void GamepadMap_Add(int button, uint32 modifiers, uint16 cmd) {
165+
if ((joymap_size & 0xff) == 0) {
166+
if (joymap_size > 1000)
167+
Die("Too many joypad keys");
168+
joymap_ents = realloc(joymap_ents, sizeof(GamepadMapEnt) * (joymap_size + 64));
169+
if (!joymap_ents) Die("realloc failure");
170+
}
171+
uint16 *p = &joymap_first[button];
172+
// Insert it as early as possible but before after any entry with more modifiers.
173+
int cb = CountBits32(modifiers);
174+
while (*p && cb < CountBits32(joymap_ents[*p - 1].modifiers))
175+
p = &joymap_ents[*p - 1].next;
176+
int i = joymap_size++;
177+
GamepadMapEnt *ent = &joymap_ents[i];
178+
ent->modifiers = modifiers;
179+
ent->cmd = cmd;
180+
ent->next = *p;
181+
*p = i + 1;
182+
}
183+
184+
int FindCmdForGamepadButton(int button, uint32 modifiers) {
185+
GamepadMapEnt *ent;
186+
for(int e = joymap_first[button]; e != 0; e = ent->next) {
187+
ent = &joymap_ents[e - 1];
188+
if ((modifiers & ent->modifiers) == ent->modifiers)
189+
return ent->cmd;
190+
}
191+
return 0;
192+
}
193+
194+
static int ParseGamepadButtonName(const char **value) {
195+
const char *s = *value;
196+
// Longest substring first
197+
static const char *const kGamepadKeyNames[] = {
198+
"Back", "Guide", "Start", "L3", "R3",
199+
"L1", "R1", "DpadUp", "DpadDown", "DpadLeft", "DpadRight", "L2", "R2",
200+
"Lb", "Rb", "A", "B", "X", "Y"
201+
};
202+
static const uint8 kGamepadKeyIds[] = {
203+
kGamepadBtn_Back, kGamepadBtn_Guide, kGamepadBtn_Start, kGamepadBtn_L3, kGamepadBtn_R3,
204+
kGamepadBtn_L1, kGamepadBtn_R1, kGamepadBtn_DpadUp, kGamepadBtn_DpadDown, kGamepadBtn_DpadLeft, kGamepadBtn_DpadRight, kGamepadBtn_L2, kGamepadBtn_R2,
205+
kGamepadBtn_L1, kGamepadBtn_R1, kGamepadBtn_A, kGamepadBtn_B, kGamepadBtn_X, kGamepadBtn_Y,
206+
};
207+
for (size_t i = 0; i != countof(kGamepadKeyNames); i++) {
208+
const char *r = StringStartsWithNoCase(s, kGamepadKeyNames[i]);
209+
if (r) {
210+
*value = r;
211+
return kGamepadKeyIds[i];
212+
}
213+
}
214+
return kGamepadBtn_Invalid;
215+
}
216+
217+
static const uint8 kDefaultGamepadCmds[] = {
218+
kGamepadBtn_DpadUp, kGamepadBtn_DpadDown, kGamepadBtn_DpadLeft, kGamepadBtn_DpadRight, kGamepadBtn_Back, kGamepadBtn_Start,
219+
kGamepadBtn_B, kGamepadBtn_A, kGamepadBtn_Y, kGamepadBtn_X, kGamepadBtn_L1, kGamepadBtn_R1,
220+
};
221+
222+
static void ParseGamepadArray(char *value, int cmd, int size) {
223+
char *s;
224+
int i = 0;
225+
for (; i < size && (s = NextDelim(&value, ',')) != NULL; i++, cmd += (cmd != 0)) {
226+
if (*s == 0)
227+
continue;
228+
uint32 modifiers = 0;
229+
const char *ss = s;
230+
for (;;) {
231+
int button = ParseGamepadButtonName(&ss);
232+
if (button == kGamepadBtn_Invalid) BAD: {
233+
fprintf(stderr, "Unknown gamepad button: '%s'\n", s);
234+
break;
235+
}
236+
while (*ss == ' ' || *ss == '\t') ss++;
237+
if (*ss == '+') {
238+
ss++;
239+
modifiers |= 1 << button;
240+
} else if (*ss == 0) {
241+
GamepadMap_Add(button, modifiers, cmd);
242+
break;
243+
} else
244+
goto BAD;
245+
}
246+
}
247+
}
248+
145249
static void RegisterDefaultKeys() {
146-
for (int i = 0; i < countof(kKeyNameId); i++) {
250+
for (int i = 1; i < countof(kKeyNameId); i++) {
147251
if (!has_keynameid[i]) {
148252
int size = kKeyNameId[i].size, k = kKeyNameId[i].id;
149253
for (int j = 0; j < size; j++, k++)
150254
KeyMapHash_Add(kDefaultKbdControls[k], k);
151255
}
152256
}
257+
if (!has_joypad_controls) {
258+
for (int i = 0; i < countof(kDefaultGamepadCmds); i++)
259+
GamepadMap_Add(kDefaultGamepadCmds[i], 0, kKeys_Controls + i);
260+
}
153261
}
154262

155263
static int GetIniSection(const char *s) {
@@ -163,6 +271,8 @@ static int GetIniSection(const char *s) {
163271
return 3;
164272
if (StringEqualsNoCase(s, "[Features]"))
165273
return 4;
274+
if (StringEqualsNoCase(s, "[GamepadMap]"))
275+
return 5;
166276
return -1;
167277
}
168278

@@ -205,6 +315,15 @@ static bool HandleIniConfig(int section, const char *key, char *value) {
205315
return true;
206316
}
207317
}
318+
} else if (section == 5) {
319+
for (int i = 0; i < countof(kKeyNameId); i++) {
320+
if (StringEqualsNoCase(key, kKeyNameId[i].name)) {
321+
if (i == 1)
322+
has_joypad_controls = true;
323+
ParseGamepadArray(value, kKeyNameId[i].id, kKeyNameId[i].size);
324+
return true;
325+
}
326+
}
208327
} else if (section == 1) {
209328
if (StringEqualsNoCase(key, "WindowSize")) {
210329
char *s;

config.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
#include <SDL_keycode.h>
44

55
enum {
6-
kKeys_Controls = 0,
6+
kKeys_Null,
7+
kKeys_Controls,
78
kKeys_Controls_Last = kKeys_Controls + 11,
89
kKeys_Load,
910
kKeys_Load_Last = kKeys_Load + 19,
@@ -78,9 +79,30 @@ enum {
7879
kMsuEnabled_MsuDeluxe = 2,
7980
kMsuEnabled_Opuz = 4,
8081
};
81-
82+
enum {
83+
kGamepadBtn_Invalid = -1,
84+
kGamepadBtn_A,
85+
kGamepadBtn_B,
86+
kGamepadBtn_X,
87+
kGamepadBtn_Y,
88+
kGamepadBtn_Back,
89+
kGamepadBtn_Guide,
90+
kGamepadBtn_Start,
91+
kGamepadBtn_L3,
92+
kGamepadBtn_R3,
93+
kGamepadBtn_L1,
94+
kGamepadBtn_R1,
95+
kGamepadBtn_DpadUp,
96+
kGamepadBtn_DpadDown,
97+
kGamepadBtn_DpadLeft,
98+
kGamepadBtn_DpadRight,
99+
kGamepadBtn_L2,
100+
kGamepadBtn_R2,
101+
kGamepadBtn_Count,
102+
};
82103

83104
extern Config g_config;
84105

85106
void ParseConfigFile(const char *filename);
86107
int FindCmdForSdlKey(SDL_Keycode code, SDL_Keymod mod);
108+
int FindCmdForGamepadButton(int button, uint32 modifiers);

main.c

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ static void LoadLinkGraphics();
3737
static void RenderNumber(uint8 *dst, size_t pitch, int n, bool big);
3838
static void HandleInput(int keyCode, int modCode, bool pressed);
3939
static void HandleCommand(uint32 j, bool pressed);
40+
static int RemapSdlButton(int button);
4041
static void HandleGamepadInput(int button, bool pressed);
4142
static void HandleGamepadAxisInput(int gamepad_id, int axis, int value);
4243
static void OpenOneGamepad(int i);
@@ -68,6 +69,8 @@ static int g_ppu_render_flags = 0;
6869
static int g_snes_width, g_snes_height;
6970
static int g_sdl_audio_mixer_volume = SDL_MIX_MAXVOLUME;
7071
static struct RendererFuncs g_renderer_funcs;
72+
static uint32 g_gamepad_modifiers;
73+
static uint16 g_gamepad_last_cmd[kGamepadBtn_Count];
7174

7275
void NORETURN Die(const char *error) {
7376
#if defined(NDEBUG) && defined(_WIN32)
@@ -77,16 +80,6 @@ void NORETURN Die(const char *error) {
7780
exit(1);
7881
}
7982

80-
void SetButtonState(int button, bool pressed) {
81-
// set key in constroller
82-
if (pressed) {
83-
g_input1_state |= 1 << button;
84-
} else {
85-
g_input1_state &= ~(1 << button);
86-
}
87-
}
88-
89-
9083
void ChangeWindowScale(int scale_step) {
9184
if ((SDL_GetWindowFlags(g_window) & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED)) != 0)
9285
return;
@@ -419,11 +412,12 @@ int main(int argc, char** argv) {
419412
HandleGamepadAxisInput(event.caxis.which, event.caxis.axis, event.caxis.value);
420413
break;
421414
case SDL_CONTROLLERBUTTONDOWN:
422-
HandleGamepadInput(event.cbutton.button, event.cbutton.state == SDL_PRESSED);
423-
break;
424-
case SDL_CONTROLLERBUTTONUP:
425-
HandleGamepadInput(event.cbutton.button, event.cbutton.state == SDL_PRESSED);
415+
case SDL_CONTROLLERBUTTONUP: {
416+
int b = RemapSdlButton(event.cbutton.button);
417+
if (b >= 0)
418+
HandleGamepadInput(b, event.type == SDL_CONTROLLERBUTTONDOWN);
426419
break;
420+
}
427421
case SDL_MOUSEWHEEL:
428422
if (SDL_GetModState() & KMOD_CTRL && event.wheel.y != 0)
429423
ChangeWindowScale(event.wheel.y > 0 ? 1 : -1);
@@ -572,8 +566,11 @@ static void HandleCommand_Locked(uint32 j, bool pressed);
572566

573567
static void HandleCommand(uint32 j, bool pressed) {
574568
if (j <= kKeys_Controls_Last) {
575-
static const uint8 kKbdRemap[12] = { 4, 5, 6, 7, 2, 3, 8, 0, 9, 1, 10, 11 };
576-
SetButtonState(kKbdRemap[j], pressed);
569+
static const uint8 kKbdRemap[] = { 0, 4, 5, 6, 7, 2, 3, 8, 0, 9, 1, 10, 11 };
570+
if (pressed)
571+
g_input1_state |= 1 << kKbdRemap[j];
572+
else
573+
g_input1_state &= ~(1 << kKbdRemap[j]);
577574
return;
578575
}
579576

@@ -656,7 +653,7 @@ static void HandleCommand_Locked(uint32 j, bool pressed) {
656653

657654
static void HandleInput(int keyCode, int keyMod, bool pressed) {
658655
int j = FindCmdForSdlKey(keyCode, keyMod);
659-
if (j >= 0)
656+
if (j != 0)
660657
HandleCommand(j, pressed);
661658
}
662659

@@ -668,23 +665,37 @@ static void OpenOneGamepad(int i) {
668665
}
669666
}
670667

671-
static void HandleGamepadInput(int button, bool pressed) {
668+
static int RemapSdlButton(int button) {
672669
switch (button) {
673-
case SDL_CONTROLLER_BUTTON_A: SetButtonState(0, pressed); break;
674-
case SDL_CONTROLLER_BUTTON_X: SetButtonState(1, pressed); break;
675-
case SDL_CONTROLLER_BUTTON_BACK: SetButtonState(2, pressed); break;
676-
case SDL_CONTROLLER_BUTTON_START: SetButtonState(3, pressed); break;
677-
case SDL_CONTROLLER_BUTTON_DPAD_UP: SetButtonState(4, pressed); break;
678-
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: SetButtonState(5, pressed); break;
679-
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: SetButtonState(6, pressed); break;
680-
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: SetButtonState(7, pressed); break;
681-
case SDL_CONTROLLER_BUTTON_B: SetButtonState(8, pressed); break;
682-
case SDL_CONTROLLER_BUTTON_Y: SetButtonState(9, pressed); break;
683-
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: SetButtonState(10, pressed); break;
684-
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: SetButtonState(11, pressed); break;
670+
case SDL_CONTROLLER_BUTTON_A: return kGamepadBtn_A;
671+
case SDL_CONTROLLER_BUTTON_B: return kGamepadBtn_B;
672+
case SDL_CONTROLLER_BUTTON_X: return kGamepadBtn_X;
673+
case SDL_CONTROLLER_BUTTON_Y: return kGamepadBtn_Y;
674+
case SDL_CONTROLLER_BUTTON_BACK: return kGamepadBtn_Back;
675+
case SDL_CONTROLLER_BUTTON_GUIDE: return kGamepadBtn_Guide;
676+
case SDL_CONTROLLER_BUTTON_START: return kGamepadBtn_Start;
677+
case SDL_CONTROLLER_BUTTON_LEFTSTICK: return kGamepadBtn_L3;
678+
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return kGamepadBtn_R3;
679+
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return kGamepadBtn_L1;
680+
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return kGamepadBtn_R1;
681+
case SDL_CONTROLLER_BUTTON_DPAD_UP: return kGamepadBtn_DpadUp;
682+
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return kGamepadBtn_DpadDown;
683+
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return kGamepadBtn_DpadLeft;
684+
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return kGamepadBtn_DpadRight;
685+
default: return -1;
685686
}
686687
}
687688

689+
static void HandleGamepadInput(int button, bool pressed) {
690+
if (!!(g_gamepad_modifiers & (1 << button)) == pressed)
691+
return;
692+
g_gamepad_modifiers ^= 1 << button;
693+
if (pressed)
694+
g_gamepad_last_cmd[button] = FindCmdForGamepadButton(button, g_gamepad_modifiers);
695+
if (g_gamepad_last_cmd[button] != 0)
696+
HandleCommand(g_gamepad_last_cmd[button], pressed);
697+
}
698+
688699
static void HandleVolumeAdjustment(int volume_adjustment) {
689700
#if SYSTEM_VOLUME_MIXER_AVAILABLE
690701
int current_volume = GetApplicationVolume();
@@ -748,6 +759,9 @@ static void HandleGamepadAxisInput(int gamepad_id, int axis, int value) {
748759
buttons = kSegmentToButtons[(uint8)(angle + 16 + 64) >> 5];
749760
}
750761
g_gamepad_buttons = buttons;
762+
} else if ((axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT || axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) {
763+
if (value < 12000 || value >= 16000) // hysteresis
764+
HandleGamepadInput(axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ? kGamepadBtn_L2 : kGamepadBtn_R2, value >= 12000);
751765
}
752766
}
753767

util.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ bool StringEqualsNoCase(const char *a, const char *b) {
2828
}
2929
}
3030

31-
bool StringStartsWithNoCase(const char *a, const char *b) {
32-
for (;;) {
33-
int aa = ToLower(*a++), bb = ToLower(*b++);
31+
const char *StringStartsWithNoCase(const char *a, const char *b) {
32+
for (;; a++, b++) {
33+
int aa = ToLower(*a), bb = ToLower(*b);
3434
if (bb == 0)
35-
return true;
35+
return a;
3636
if (aa != bb)
37-
return false;
37+
return NULL;
3838
}
3939
}
4040

0 commit comments

Comments
 (0)