diff --git a/port/include/input.h b/port/include/input.h index e62d6cc30..a3859c5d3 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -91,6 +91,18 @@ s32 inputButtonPressed(s32 idx, u16 contbtn); // if bind is -1, picks a bind slot automatically void inputKeyBind(s32 idx, u32 ck, s32 bind, u32 vk); +// get VK_ value from human-readable name +s32 inputGetKeyByName(const char *name); + +// get human-readable name from VK_ value +const char *inputGetKeyName(s32 vk); + +// get CK_ value from human-readable name +s32 inputGetContKeyByName(const char *name); + +// get human-readable name from CK_ value +const char *inputGetContKeyName(u32 ck); + // strength is 0 .. 1; 0 strength turns it off void inputRumble(s32 idx, f32 strength, f32 time); @@ -113,4 +125,7 @@ void inputMouseGetScaledDelta(f32 *dx, f32 *dy); // call this every frame void inputUpdate(void); +// call this before configSave() +void inputSaveConfig(void); + #endif diff --git a/port/src/input.c b/port/src/input.c index 0a2e0e164..38bc1f005 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -53,6 +55,81 @@ static s32 deadzone[4] = { static s32 stickCButtons = 1; +static const char *ckNames[CK_TOTAL_COUNT] = { + "R_CBUTTONS", + "L_CBUTTONS", + "D_CBUTTONS", + "U_CBUTTONS", + "R_TRIG", + "L_TRIG", + "X_BUTTON", + "Y_BUTTON", + "R_JPAD", + "L_JPAD", + "D_JPAD", + "U_JPAD", + "START_BUTTON", + "Z_TRIG", + "B_BUTTON", + "A_BUTTON", + "STICK_XNEG", + "STICK_XPOS", + "STICK_YNEG", + "STICK_YPOS", +}; + +static const char *vkPunctNames[] = { + "MINUS", "EQUALS", "LEFTBRACKET", "RIGHTBRACKET", "BACKSLASH", + "HASH", "SEMICOLON", "APOSTROPHE", "GRAVE", "COMMA", "PERIOD", "SLASH" +}; + +static const char *vkMouseNames[] = { + "MOUSE_LEFT", + "MOUSE_MIDDLE", + "MOUSE_RIGHT", + "MOUSE_X1", + "MOUSE_X2", + "MOUSE_WHEEL_UP", + "MOUSE_WHEEL_DN", +}; + +static const char *vkJoyNames[] = { + "JOY1_A", + "JOY1_B", + "JOY1_X", + "JOY1_Y", + "JOY1_BACK", + "JOY1_GUIDE", + "JOY1_START", + "JOY1_LSTICK", + "JOY1_RSTICK", + "JOY1_LSHOULDER", + "JOY1_RSHOULDER", + "JOY1_DPAD_UP", + "JOY1_DPAD_DOWN", + "JOY1_DPAD_LEFT", + "JOY1_DPAD_RIGHT", + "JOY1_BUTTON_15", + "JOY1_BUTTON_16", + "JOY1_BUTTON_17", + "JOY1_BUTTON_18", + "JOY1_BUTTON_19", + "JOY1_TOUCHPAD", + "JOY1_BUTTON_21", + "JOY1_BUTTON_22", + "JOY1_BUTTON_23", + "JOY1_BUTTON_24", + "JOY1_BUTTON_25", + "JOY1_BUTTON_26", + "JOY1_BUTTON_27", + "JOY1_BUTTON_28", + "JOY1_BUTTON_29", + "JOY1_LTRIGGER", + "JOY1_RTRIGGER", +}; + +static char vkNames[VK_TOTAL_COUNT][64]; + void inputSetDefaultKeyBinds(void) { // TODO: make VK constants for all these @@ -179,6 +256,143 @@ static int inputEventFilter(void *data, SDL_Event *event) return 0; } +static inline void inputGetScancodeName(const SDL_Scancode sc, char *out, size_t len) +{ + const char *scname = SDL_GetScancodeName(sc); + if (scname) { + strncpy(out, scname, len - 1); + for (u32 i = 0; i < len && out[i]; ++i) { + if (out[i] == ' ') { + out[i] = '_'; + } else { + out[i] = toupper(out[i]); + } + } + } else { + snprintf(out, len, "KEY%d", (s32)sc); + } +} + +static inline void inputInitKeyNames(void) +{ + for (SDL_Scancode key = SDL_SCANCODE_A; key <= SDL_SCANCODE_SPACE; ++key) { + inputGetScancodeName(key, vkNames[key], sizeof(vkNames[key])); + } + + // special characters + for (SDL_Scancode key = SDL_SCANCODE_MINUS; key < SDL_SCANCODE_CAPSLOCK; ++key) { + strcpy(vkNames[key], vkPunctNames[key - SDL_SCANCODE_MINUS]); + } + + for (SDL_Scancode key = SDL_SCANCODE_CAPSLOCK; key <= SDL_SCANCODE_NUMLOCKCLEAR; ++key) { + inputGetScancodeName(key, vkNames[key], sizeof(vkNames[key])); + } + + // keypad names + strcpy(vkNames[SDL_SCANCODE_KP_DIVIDE], "KP_DIVIDE"); + strcpy(vkNames[SDL_SCANCODE_KP_MULTIPLY], "KP_MULTIPLY"); + strcpy(vkNames[SDL_SCANCODE_KP_MINUS], "KP_MINUS"); + strcpy(vkNames[SDL_SCANCODE_KP_PLUS], "KP_PLUS"); + strcpy(vkNames[SDL_SCANCODE_KP_ENTER], "KP_ENTER"); + strcpy(vkNames[SDL_SCANCODE_KP_PERIOD], "KP_PERIOD"); + strcpy(vkNames[SDL_SCANCODE_KP_EQUALS], "KP_EQUALS"); + for (SDL_Scancode key = SDL_SCANCODE_KP_1; key < SDL_SCANCODE_KP_0; ++key) { + char tmp[8] = "KP_1"; + tmp[3] = '1' + (key - SDL_SCANCODE_KP_1); + strcpy(vkNames[key], tmp); + } + + for (SDL_Scancode key = SDL_SCANCODE_LCTRL; key <= SDL_SCANCODE_RGUI; ++key) { + inputGetScancodeName(key, vkNames[key], sizeof(vkNames[key])); + } + + // mouse names + for (u32 vk = VK_MOUSE_BEGIN; vk < VK_JOY1_BEGIN; ++vk) { + strcpy(vkNames[vk], vkMouseNames[vk - VK_MOUSE_BEGIN]); + } + + // joystick names + for (u32 vk = VK_JOY1_BEGIN; vk < VK_TOTAL_COUNT; ++vk) { + const u32 jidx = (vk - VK_JOY1_BEGIN) / INPUT_MAX_CONTROLLER_BUTTONS; + const u32 jbtn = (vk - VK_JOY1_BEGIN) % INPUT_MAX_CONTROLLER_BUTTONS; + strcpy(vkNames[vk], vkJoyNames[jbtn]); + vkNames[vk][3] = '1' + jidx; + } +} + +static inline void inputSaveBinds(void) +{ + char bindstr[256]; + char keyname[256]; + char secname[] = "Input.Player1.Binds"; + + bindstr[sizeof(bindstr) - 1] = '\0'; + + for (s32 i = 0; i < MAXCONTROLLERS; ++i) { + secname[12] = '1' + i; + for (u32 ck = 0; ck < CK_TOTAL_COUNT; ++ck) { + snprintf(keyname, sizeof(keyname), "%s.%s", secname, inputGetContKeyName(ck)); + bindstr[0] = '\0'; + for (s32 b = 0; b < MAX_BINDS; ++b) { + if (binds[i][ck][b]) { + if (b) { + strncat(bindstr, ", ", sizeof(bindstr) - 1); + } + strncat(bindstr, inputGetKeyName(binds[i][ck][b]), sizeof(bindstr) - 1); + } + } + configSetString(keyname, bindstr[0] ? bindstr : "NONE"); + } + } +} + +static inline void inputParseBindString(const s32 ctrl, const u32 ck, char *bindstr) +{ + // unbind all first + memset(binds[ctrl][ck], 0, sizeof(binds[ctrl][ck])); + + if (!strcasecmp(bindstr, "NONE")) { + // explicitly nothing bound + return; + } + + const char *tok = strtok(bindstr, ", "); + while (tok) { + if (tok[0]) { + const s32 vk = inputGetKeyByName(tok); + if (vk > 0) { + inputKeyBind(ctrl, ck, -1, vk); + } + } + tok = strtok(NULL, ", "); + } +} + +static inline void inputLoadBinds(void) +{ + char secname[] = "Input.Player1.Binds"; + char keyname[256]; + char bindstr[256]; + + for (s32 i = 0; i < MAXCONTROLLERS; ++i) { + secname[12] = '1' + i; + for (u32 ck = 0; ck < CK_TOTAL_COUNT; ++ck) { + snprintf(keyname, sizeof(keyname), "%s.%s", secname, inputGetContKeyName(ck)); + const char *cfgstr = configGetString(keyname, ""); + if (cfgstr[0]) { + strncpy(bindstr, cfgstr, sizeof(bindstr) - 1); + bindstr[sizeof(bindstr) - 1] = '\0'; + inputParseBindString(i, ck, bindstr); + } + } + } +} + +void inputSaveConfig(void) +{ + inputSaveBinds(); +} + s32 inputInit(void) { if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC)) { @@ -208,6 +422,8 @@ s32 inputInit(void) // since the main event loop is elsewhere, we can receive some events we need using a watcher SDL_AddEventWatch(inputEventFilter, NULL); + inputInitKeyNames(); + inputSetDefaultKeyBinds(); mouseEnabled = configGetInt("Input.MouseEnabled", 1); @@ -241,6 +457,8 @@ s32 inputInit(void) connectedMask = overrideMask; } + inputLoadBinds(); + return connectedMask; } @@ -507,3 +725,67 @@ void inputMouseGetScaledDelta(f32 *dx, f32 *dy) if (dx) *dx = mdx; if (dy) *dy = mdy; } + +const char *inputGetContKeyName(u32 ck) +{ + if (ck >= CK_TOTAL_COUNT) { + return ""; + } + return ckNames[ck]; +} + +s32 inputGetContKeyByName(const char *name) +{ + for (u32 i = 0; i < CK_TOTAL_COUNT; ++i) { + if (!strcmp(name, ckNames[i])) { + return i; + } + } + fprintf(stderr, "unknown bind name: `%s`\n", name); + return -1; +} + +const char *inputGetKeyName(s32 vk) +{ + if (vk < 0 || vk >= VK_TOTAL_COUNT) { + vk = 0; + } + if (!vkNames[vk][0]) { + snprintf(vkNames[vk], sizeof(vkNames[vk]), "UNKNOWN%d", vk); + } + return vkNames[vk]; +} + +s32 inputGetKeyByName(const char *name) +{ + s32 start = 0; + s32 end = 0; + + if (!strncmp(name, "JOY", 3) && isdigit(name[3])) { + const s32 idx = name[3] - '1'; + if (idx >= 0 && idx < INPUT_MAX_CONTROLLERS) { + start = VK_JOY1_BEGIN + idx * INPUT_MAX_CONTROLLER_BUTTONS; + end = start + INPUT_MAX_CONTROLLER_BUTTONS; + } + } else if (!strncmp(name, "MOUSE", 5)) { + start = VK_MOUSE_BEGIN; + end = VK_JOY1_BEGIN; + } else if (!strncmp(name, "UNKNOWN", 7) && isdigit(name[7])) { + const s32 key = atoi(name + 7); + if (key >= 0 && key < VK_TOTAL_COUNT) { + return key; + } + } else { + end = VK_MOUSE_BEGIN; + } + + for (s32 i = start; i < end; ++i) { + if (!strcmp(vkNames[i], name)) { + return i; + } + } + + fprintf(stderr, "unknown key name: `%s`\n", name); + + return -1; +} diff --git a/port/src/main.c b/port/src/main.c index 4a0178dec..4791dcb95 100644 --- a/port/src/main.c +++ b/port/src/main.c @@ -54,6 +54,7 @@ void bootCreateSched(void) static void cleanup(void) { + inputSaveConfig(); configSave(CONFIG_FNAME); // TODO: actually shut down all subsystems }