diff --git a/port/include/glyph.h b/port/include/glyph.h new file mode 100644 index 0000000000..3d5acf46f3 --- /dev/null +++ b/port/include/glyph.h @@ -0,0 +1,90 @@ +#ifndef _IN_GLYPH_H +#define _IN_GLYPH_H + +#include + +// Controller Icon enumeration for button icon detection +typedef enum { + CONTROLLER_ICON_GENERIC, + CONTROLLER_ICON_XBOX360, + CONTROLLER_ICON_XBOXONE, + CONTROLLER_ICON_PS3, + CONTROLLER_ICON_PS4, + CONTROLLER_ICON_PS5, + CONTROLLER_ICON_NINTENDO_SWITCH, + CONTROLLER_ICON_NINTENDO_64, + CONTROLLER_ICON_STEAM_CONTROLLER, + CONTROLLER_ICON_STEAM_DECK, +} ControllerIconType; + +// Valve Corporation VID/PID definitions +#define VALVE_VENDOR_ID 0x28de + +// Steam Controller product IDs +#define STEAM_CONTROLLER_LEGACY_PID 0x1101 // Valve Legacy Steam Controller (CHELL) +#define STEAM_CONTROLLER_WIRED_PID 0x1102 // Valve wired Steam Controller (D0G) +#define STEAM_CONTROLLER_BT_1_PID 0x1105 // Valve Bluetooth Steam Controller (D0G) +#define STEAM_CONTROLLER_BT_2_PID 0x1106 // Valve Bluetooth Steam Controller (D0G) +#define STEAM_CONTROLLER_WIRELESS_PID 0x1142 // Valve wireless Steam Controller +#define STEAM_CONTROLLER_V2_WIRED_PID 0x1201 // Valve wired Steam Controller (HEADCRAB) +#define STEAM_CONTROLLER_V2_BT_PID 0x1202 // Valve Bluetooth Steam Controller (HEADCRAB) + +// Other Valve product IDs +#define STEAM_VIRTUAL_GAMEPAD_PID 0x11ff // Steam Virtual Gamepad +#define STEAM_DECK_BUILTIN_PID 0x1205 // Valve Steam Deck Builtin + +// Check if a product ID is any Steam Controller (2015) variant +static inline int isSteamControllerPID(unsigned short productId) { + return (productId == STEAM_CONTROLLER_LEGACY_PID || + productId == STEAM_CONTROLLER_WIRED_PID || + productId == STEAM_CONTROLLER_BT_1_PID || + productId == STEAM_CONTROLLER_BT_2_PID || + productId == STEAM_CONTROLLER_WIRELESS_PID || + productId == STEAM_CONTROLLER_V2_WIRED_PID || + productId == STEAM_CONTROLLER_V2_BT_PID); +} + +// Steam Virtual Gamepad detection +static inline int getSteamVirtualControllerDetection(SDL_GameController* ctrl, int SDLControllerType) { + if (!ctrl) { + return SDLControllerType; + } + + if (SDL_GameControllerGetVendor(ctrl) != VALVE_VENDOR_ID) { + return SDLControllerType; + } + + unsigned short product = SDL_GameControllerGetProduct(ctrl); + + // Steam Virtual Gamepad - pass through the underlying controller type + if (product == STEAM_VIRTUAL_GAMEPAD_PID) { + return SDLControllerType; + } + + // Steam Deck + if (product == STEAM_DECK_BUILTIN_PID) { + return CONTROLLER_ICON_STEAM_DECK; + } + + // Steam Controller (2015) + if (isSteamControllerPID(product)) { + return CONTROLLER_ICON_STEAM_CONTROLLER; + } + + return SDLControllerType; +} + +// Generic button names for glyphs +// this will cover generalized button names for glyphs based on SDL_GamepadButton +extern const char *vkJoyDisplayNames[]; + +// Controller-specific button glyphs +// this will grab the button glyphs based on the controller type and button index +const char *glyphGetControllerButtonName(int controllerType, int buttonIndex); + +// Dynamic glyph input binding detection +// TODO: replace all baked-in button localization text with a dynamic glyph system +char* glyphInputBindingDetect(char* text, int textSize, const char* placeholder, + int controllerIndex, int controlKey, int forceController); + +#endif \ No newline at end of file diff --git a/port/include/input.h b/port/include/input.h index 6171c26961..d8370240db 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -121,6 +121,21 @@ enum mouselockmode { MLOCK_AUTO = 2 }; +enum buttonpromptmode { + GLYPH_AUTO = -1, + GLYPH_GENERIC = 0, + GLYPH_INTERNAL = 1, + GLYPH_XBOX360 = 2, + GLYPH_XBOXONE = 3, + GLYPH_PS3 = 4, + GLYPH_PS4 = 5, + GLYPH_PS5 = 6, + GLYPH_NINTENDO_SWITCH = 7, + GLYPH_NINTENDO_64 = 8, + GLYPH_STEAM_CONTROLLER = 9, + GLYPH_STEAM_DECK = 10, +}; + // returns bitmask of connected controllers or -1 if failed s32 inputInit(void); @@ -193,6 +208,9 @@ s32 inputGetKeyByName(const char *name); // get human-readable name from VK_ value const char *inputGetKeyName(s32 vk); +// get human-readable controller button display name from VK_ value +const char *inputGetButtonDisplayName(s32 vk); + // get CK_ value from human-readable name s32 inputGetContKeyByName(const char *name); @@ -249,6 +267,10 @@ s32 inputGetLastKey(void); s32 inputGetMouseLockMode(void); void inputSetMouseLockMode(s32 lockmode); +// get/set button prompt override +s32 inputGetButtonPromptOverride(s32 cidx); +void inputSetButtonPromptOverride(s32 cidx, s32 override); + // same as inputLockMouse but works only if mouse is enabled and lockmode == MLOCK_AUTO s32 inputAutoLockMouse(s32 wantlock); diff --git a/port/src/glyph.c b/port/src/glyph.c new file mode 100644 index 0000000000..49090fc5a5 --- /dev/null +++ b/port/src/glyph.c @@ -0,0 +1,329 @@ +#include +#include "glyph.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + +// Generic display names for controller buttons (maps to same indices as vkJoyNames) +const char *vkJoyDisplayNames[] = { + "SOUTH_BTN", // A button (Bottom face button) + "EAST_BTN", // B button (Right face button) + "WEST_BTN", // X button (Left face button) + "NORTH_BTN", // Y button (Top face button) + "BACK_BTN", + "GUIDE_BTN", + "START_BTN", + "LEFT_STICK_CLICK", + "RIGHT_STICK_CLICK", + "LEFT_SHOULDER", + "RIGHT_SHOULDER", + "D-PAD_UP", + "D-PAD_DOWN", + "D-PAD_LEFT", + "D-PAD_RIGHT", + "MISC1_BTN", // Additional button (e.g. Xbox Series X share button, PS5 microphone button, Switch capture button) + "RIGHT_PADDLE_1", // Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1, DualSense Edge RB button, Right Joy-Con SR button) + "LEFT_PADDLE_1", // Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3, DualSense Edge LB button, Left Joy-Con SL button) + "RIGHT_PADDLE_2", // Lower or secondary paddle, under your right hand (e.g. Xbox Elite paddle P2, DualSense Edge right Fn button, Right Joy-Con SR button) + "LEFT_PADDLE_2", // Lower or secondary paddle, under your left hand (e.g. Xbox Elite paddle P4, DualSense Edge left Fn button, Left Joy-Con SL button) + "TOUCHPAD_BTN", // PS4/PS5 touchpad button + "MISC2_BTN", + "MISC3_BTN", // Additional button (e.g. Nintendo GameCube Left Trigger click) + "MISC4_BTN", // Additional button (e.g. Nintendo GameCube Right Trigger click) + "MISC5_BTN", + "MISC6_BTN", + "BTN_26", + "BTN_27", + "BTN_28", + "BTN_29", + "LEFT_TRIG", + "RIGHT_TRIG", +}; + +// Controller-specific overrides +// These are used to map specific controller buttons to Controller-specific glyphs + +struct button_override { + int button_index; + const char *name; +}; + +// Standard glyph overrides (shared across controllers) +static const struct button_override glyph_standard[] = { + { 0, "A_BTN" }, // Bottom face button + { 1, "B_BTN" }, // Right face button + { 2, "X_BTN" }, // Left face button + { 3, "Y_BTN" }, // Top face button + { 7, "L3_STICK_CLICK" }, // Left stick press (L3) + { 8, "R3_STICK_CLICK" }, // Right stick press (R3) + { 9, "L1_SHOULDER" }, // Left shoulder (L1) + { 10, "R1_SHOULDER" }, // Right shoulder (R1) + { 15, "M1_BTN" }, // M1 button + { 16, "L4_BTN" }, // Upper left paddle (L4) + { 17, "L5_BTN" }, // Below left paddle (L5) + { 18, "R4_BTN" }, // Upper right paddle (R4) + { 19, "R5_BTN" }, // Below right paddle (R5) + { 21, "M2_BTN" }, // M2 button + { 22, "M3_BTN" }, // M3 button + { 23, "M4_BTN" }, // M4 button + { 30, "L2_TRIG" }, // Left trigger + { 31, "R2_TRIG" }, // Right trigger +}; + +// Xbox-specific overrides +static const struct button_override xbox_overrides[] = { + { 5, "XBOX_BTN" }, + { 9, "LB_SHOULDER" }, + { 10, "RB_SHOULDER" }, + { 30, "LT_TRIG" }, + { 31, "RT_TRIG" }, +}; + +// Xbox 360-specific button overrides +static const struct button_override xbox360_overrides[] = { + { 4, "BACK_BTN" }, + { 6, "START_BTN" }, +}; + +// Xbox One/Series X|S-specific button overrides +static const struct button_override xboxone_overrides[] = { + { 4, "VIEW_BTN" }, + { 6, "MENU_BTN" }, + { 15, "SHARE_BTN" }, + { 16, "PADDLE_1" }, // Xbox Elite right upper paddle + { 17, "PADDLE_2" }, // Xbox Elite left upper paddle + { 18, "PADDLE_3" }, // Xbox Elite right lower paddle + { 19, "PADDLE_4" }, // Xbox Elite left lower paddle +}; + +// PlayStation-specific overrides +static const struct button_override playstation_overrides[] = { + { 0, "CROSS_BTN" }, + { 1, "CIRCLE_BTN" }, + { 2, "SQUARE_BTN" }, + { 3, "TRIANGLE_BTN" }, + { 5, "PS_BTN" }, + { 20, "TOUCHPAD_BTN" }, +}; + +// PS3-specific button overrides +static const struct button_override ps3_overrides[] = { + { 4, "SELECT_BTN" }, + { 6, "START_BTN" }, +}; + +// PS4-specific button overrides +static const struct button_override ps4_overrides[] = { + { 4, "SHARE_BTN" }, + { 6, "OPTIONS_BTN" }, +}; + +// PS5-specific button overrides +static const struct button_override ps5_overrides[] = { + { 4, "CREATE_BTN" }, + { 6, "OPTIONS_BTN" }, + { 15, "MIC_BTN" }, + { 16, "RB_PADDLE" }, // DualSense Edge RB Button + { 17, "LB_PADDLE" }, // DualSense Edge LB Button + { 18, "RIGHT_FN_BTN" }, // DualSense Edge right Fn button + { 19, "LEFT_FN_BTN" }, // DualSense Edge left Fn button +}; + +// Nintendo Switch-specific overrides +static const struct button_override nintendoswitch_overrides[] = { + { 4, "MINUS_BTN" }, + { 5, "HOME_BTN" }, + { 6, "PLUS_BTN" }, + { 9, "L_SHOULDER" }, + { 10, "R_SHOULDER" }, + { 15, "CAPTURE_BTN" }, + { 16, "RIGHT_SR_BTN" }, // Right Joy-Con SR + { 17, "LEFT_SL_BTN" }, // Left Joy-Con SL + { 18, "RIGHT_SL_BTN" }, // Right Joy-Con SL + { 19, "LEFT_SR_BTN" }, // Left Joy-Con SR + { 30, "ZL_TRIG" }, + { 31, "ZR_TRIG" }, +}; + +// Steam Deck-specific overrides +static const struct button_override steamdeck_overrides[] = { + { 4, "VIEW_BTN" }, + { 5, "STEAM_BTN" }, + { 6, "MENU_BTN" }, +}; + +// Steam Controller-specific overrides +static const struct button_override steamcontroller_overrides[] = { + { 5, "STEAM_BTN" }, + { 16, "RG_BTN" }, // Steam Controller right grip + { 17, "LG_BTN" }, // Steam Controller left grip +}; + +// Nintendo 64-specific overrides (based on NSO N64 controller's button layout) +static const struct button_override nintendo64_overrides[] = { + { 2, "C-LEFT_BTN" }, + { 3, "C-UP_BTN" }, + { 4, "C-RIGHT_BTN" }, + { 6, "START_BTN" }, + { 9, "L_BTN" }, + { 10, "R_BTN" }, + { 30, "C-DOWN_BTN" }, + { 31, "Z_BTN" }, +}; + +// Function to override a specific button index +static const char* searchOverrides(const struct button_override* overrides, int count, int buttonIndex) { + for (const struct button_override* override = overrides; override < overrides + count; override++) { + if (override->button_index == buttonIndex) { + return override->name; + } + } + return NULL; +} + +// Function to search and provide Controller-specific button types +const char *glyphGetControllerButtonName(int controllerType, int buttonIndex) +{ + const char *result = NULL; + + switch (controllerType) { + case CONTROLLER_ICON_XBOX360: + // Xbox 360-specific overrides + result = searchOverrides(xbox360_overrides, ARRAY_SIZE(xbox360_overrides), buttonIndex); + if (result) return result; + // Xbox overrides + result = searchOverrides(xbox_overrides, ARRAY_SIZE(xbox_overrides), buttonIndex); + if (result) return result; + // Glyph standard (Face Button only) + if (buttonIndex >= 0 && buttonIndex <= 3) { + result = searchOverrides(glyph_standard, ARRAY_SIZE(glyph_standard), buttonIndex); + if (result) return result; + } + break; + + case CONTROLLER_ICON_STEAM_CONTROLLER: + // Steam Controller-specific overrides + result = searchOverrides(steamcontroller_overrides, ARRAY_SIZE(steamcontroller_overrides), buttonIndex); + if (result) return result; + // Xbox overrides (Shoulders and triggers) + result = searchOverrides(xbox_overrides, ARRAY_SIZE(xbox_overrides), buttonIndex); + if (result) return result; + // Glyph standard (Face buttons) + if (buttonIndex >= 0 && buttonIndex <= 3) { + result = searchOverrides(glyph_standard, ARRAY_SIZE(glyph_standard), buttonIndex); + if (result) return result; + } + break; + + case CONTROLLER_ICON_XBOXONE: + // Xbox One/Series X|S-specific overrides + result = searchOverrides(xboxone_overrides, ARRAY_SIZE(xboxone_overrides), buttonIndex); + if (result) return result; + // Xbox overrides + result = searchOverrides(xbox_overrides, ARRAY_SIZE(xbox_overrides), buttonIndex); + if (result) return result; + // Glyph standard (Face Button only) + if (buttonIndex >= 0 && buttonIndex <= 3) { + result = searchOverrides(glyph_standard, ARRAY_SIZE(glyph_standard), buttonIndex); + if (result) return result; + } + break; + + case CONTROLLER_ICON_PS3: + // PS3-specific overrides + result = searchOverrides(ps3_overrides, ARRAY_SIZE(ps3_overrides), buttonIndex); + if (result) return result; + // PlayStation overrides + result = searchOverrides(playstation_overrides, ARRAY_SIZE(playstation_overrides), buttonIndex); + if (result) return result; + // Glyph standard (Face, Shoulders, Triggers only) + if ((buttonIndex >= 0 && buttonIndex <= 10) || (buttonIndex >= 30 && buttonIndex <= 31)) { + result = searchOverrides(glyph_standard, ARRAY_SIZE(glyph_standard), buttonIndex); + if (result) return result; + } + break; + + case CONTROLLER_ICON_PS4: + // PS4-specific overrides + result = searchOverrides(ps4_overrides, ARRAY_SIZE(ps4_overrides), buttonIndex); + if (result) return result; + // PlayStation overrides + result = searchOverrides(playstation_overrides, ARRAY_SIZE(playstation_overrides), buttonIndex); + if (result) return result; + // Glyph standard (Face, Shoulders, Triggers only) + if ((buttonIndex >= 0 && buttonIndex <= 10) || (buttonIndex >= 30 && buttonIndex <= 31)) { + result = searchOverrides(glyph_standard, ARRAY_SIZE(glyph_standard), buttonIndex); + if (result) return result; + } + break; + + case CONTROLLER_ICON_PS5: + // PS5-specific overrides + result = searchOverrides(ps5_overrides, ARRAY_SIZE(ps5_overrides), buttonIndex); + if (result) return result; + // PlayStation overrides + result = searchOverrides(playstation_overrides, ARRAY_SIZE(playstation_overrides), buttonIndex); + if (result) return result; + // Glyph standard (Face, Shoulders, Triggers only) + if ((buttonIndex >= 0 && buttonIndex <= 10) || (buttonIndex >= 30 && buttonIndex <= 31)) { + result = searchOverrides(glyph_standard, ARRAY_SIZE(glyph_standard), buttonIndex); + if (result) return result; + } + break; + + case CONTROLLER_ICON_NINTENDO_SWITCH: + // Nintendo Switch specific overrides + result = searchOverrides(nintendoswitch_overrides, ARRAY_SIZE(nintendoswitch_overrides), buttonIndex); + if (result) return result; + // Glyph standard (swapped Face Buttons positions) + if (buttonIndex >= 0 && buttonIndex <= 3) { + int mappedIndex = buttonIndex; + switch (buttonIndex) { + case 0: mappedIndex = 1; break; // B (Bottom face button) + case 1: mappedIndex = 0; break; // A (Right face button) + case 2: mappedIndex = 3; break; // Y (Left face button) + case 3: mappedIndex = 2; break; // X (Top face button) + } + result = searchOverrides(glyph_standard, ARRAY_SIZE(glyph_standard), mappedIndex); + if (result) return result; + } + break; + + case CONTROLLER_ICON_NINTENDO_64: + // Nintendo 64-specific overrides + result = searchOverrides(nintendo64_overrides, ARRAY_SIZE(nintendo64_overrides), buttonIndex); + if (result) return result; + // Nintendo Switch specific overrides (for NSO N64 controller's Home and Capture buttons) + if (buttonIndex == 5 || buttonIndex == 15) { + result = searchOverrides(nintendoswitch_overrides, ARRAY_SIZE(nintendoswitch_overrides), buttonIndex); + if (result) return result; + } + // Glyph standard (N64 face buttons) + if (buttonIndex >= 0 && buttonIndex <= 1) { + result = searchOverrides(glyph_standard, ARRAY_SIZE(glyph_standard), buttonIndex); + if (result) return result; + } + break; + + case CONTROLLER_ICON_STEAM_DECK: + // Steam Deck-specific overrides + result = searchOverrides(steamdeck_overrides, ARRAY_SIZE(steamdeck_overrides), buttonIndex); + if (result) return result; + // Glyph standard + if ((buttonIndex >= 0 && buttonIndex <= 19) || (buttonIndex >= 30 && buttonIndex <= 31)) { + result = searchOverrides(glyph_standard, ARRAY_SIZE(glyph_standard), buttonIndex); + if (result) return result; + } + break; + + case CONTROLLER_ICON_GENERIC: + default: + break; + } + + // Fallback to Generic type + if (buttonIndex >= 0 && buttonIndex < 32) { + return vkJoyDisplayNames[buttonIndex]; + } + + return "UNKNOWN BUTTON"; +} \ No newline at end of file diff --git a/port/src/input.c b/port/src/input.c index ebf0c4667b..d88021c507 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -11,6 +11,7 @@ #include "utils.h" #include "system.h" #include "fs.h" +#include "glyph.h" #if !SDL_VERSION_ATLEAST(2, 0, 14) // this was added in 2.0.14 @@ -46,6 +47,7 @@ static SDL_GameController *pads[INPUT_MAX_CONTROLLERS]; .swapSticks = 1, \ .deviceIndex = -1, \ .cancelCButtons = 0, \ + .buttonPromptOverride = -1, \ } static struct controllercfg { @@ -58,6 +60,7 @@ static struct controllercfg { s32 swapSticks; s32 deviceIndex; s32 cancelCButtons; + s32 buttonPromptOverride; } padsCfg[INPUT_MAX_CONTROLLERS] = { CONTROLLERCFG_DEFAULT, CONTROLLERCFG_DEFAULT, @@ -1323,6 +1326,22 @@ void inputSetMouseLockMode(s32 lockmode) } } +s32 inputGetButtonPromptOverride(s32 cidx) +{ + if (cidx < 0 || cidx >= INPUT_MAX_CONTROLLERS) { + return 0; + } + return padsCfg[cidx].buttonPromptOverride; +} + +void inputSetButtonPromptOverride(s32 cidx, s32 override) +{ + if (cidx < 0 || cidx >= INPUT_MAX_CONTROLLERS) { + return; + } + padsCfg[cidx].buttonPromptOverride = override; +} + const char *inputGetContKeyName(u32 ck) { if (ck >= CK_TOTAL_COUNT) { @@ -1353,6 +1372,126 @@ const char *inputGetKeyName(s32 vk) return vkNames[vk]; } +const char *inputGetButtonDisplayName(s32 vk) +{ + if (vk < VK_JOY_BEGIN || vk >= VK_TOTAL_COUNT) { + return inputGetKeyName(vk); + } + + const u32 cidx = (vk - VK_JOY_BEGIN) / INPUT_MAX_CONTROLLER_BUTTONS; + u32 jbtn = (vk - VK_JOY_BEGIN) % INPUT_MAX_CONTROLLER_BUTTONS; + + if (jbtn >= INPUT_MAX_CONTROLLER_BUTTONS) { + return inputGetKeyName(vk); + } + + const s32 override = (cidx < INPUT_MAX_CONTROLLERS) ? padsCfg[cidx].buttonPromptOverride : GLYPH_AUTO; + + // Internal glyphs (legacy) + if (override == GLYPH_INTERNAL) { + if (jbtn < sizeof(vkJoyNames) / sizeof(vkJoyNames[0])) { + static char playerButtonName[64]; + const char *baseName = vkJoyNames[jbtn]; + snprintf(playerButtonName, sizeof(playerButtonName), "JOY%d_%s", + (int)(cidx + 1), baseName + 5); + return playerButtonName; + } + return inputGetKeyName(vk); + } + + // Nintendo 64 controller prompts + if (override == GLYPH_NINTENDO_64) { + return glyphGetControllerButtonName(CONTROLLER_ICON_NINTENDO_64, jbtn); + } + + SDL_GameController *ctrl = (cidx < INPUT_MAX_CONTROLLERS) ? pads[cidx] : NULL; + + // Glyph override function + if (override != GLYPH_AUTO) { + const ControllerIconType iconType = (ControllerIconType)(override - 1); + return glyphGetControllerButtonName(iconType, jbtn); + } + + // Auto-detect glyphs based on controller type + if (cidx < INPUT_MAX_CONTROLLERS && pads[cidx]) { + const SDL_GameControllerType type = SDL_GameControllerGetType(pads[cidx]); + + switch (type) { + case SDL_CONTROLLER_TYPE_XBOX360: + { + int iconType = getSteamVirtualControllerDetection(ctrl, CONTROLLER_ICON_XBOX360); + return glyphGetControllerButtonName(iconType, jbtn); + } + case SDL_CONTROLLER_TYPE_XBOXONE: + { + int iconType = getSteamVirtualControllerDetection(ctrl, CONTROLLER_ICON_XBOXONE); + return glyphGetControllerButtonName(iconType, jbtn); + } + case SDL_CONTROLLER_TYPE_PS3: + { + int iconType = getSteamVirtualControllerDetection(ctrl, CONTROLLER_ICON_PS3); + return glyphGetControllerButtonName(iconType, jbtn); + } + case SDL_CONTROLLER_TYPE_PS4: + { + int iconType = getSteamVirtualControllerDetection(ctrl, CONTROLLER_ICON_PS4); + return glyphGetControllerButtonName(iconType, jbtn); + } +#if SDL_VERSION_ATLEAST(2, 0, 14) + case SDL_CONTROLLER_TYPE_PS5: + { + int iconType = getSteamVirtualControllerDetection(ctrl, CONTROLLER_ICON_PS5); + return glyphGetControllerButtonName(iconType, jbtn); + } +#endif + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: +#if SDL_VERSION_ATLEAST(2, 24, 0) +#if defined(SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT: +#endif +#if defined(SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT: +#endif +#if defined(SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR) + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR: +#endif +#endif + { + int iconType = getSteamVirtualControllerDetection(ctrl, CONTROLLER_ICON_NINTENDO_SWITCH); + return glyphGetControllerButtonName(iconType, jbtn); + } + case SDL_CONTROLLER_TYPE_VIRTUAL: + case SDL_CONTROLLER_TYPE_UNKNOWN: + default: + { + int iconType = CONTROLLER_ICON_GENERIC; + if (ctrl) { + if (SDL_GameControllerGetVendor(ctrl) == VALVE_VENDOR_ID) { + Uint16 product = SDL_GameControllerGetProduct(ctrl); + + // Steam Deck + if (product == STEAM_DECK_BUILTIN_PID) { + iconType = CONTROLLER_ICON_STEAM_DECK; + } + // Steam Controller + else if (isSteamControllerPID(product)) { + iconType = CONTROLLER_ICON_STEAM_CONTROLLER; + } + // Steam Virtual Gamepad + else if (product == STEAM_VIRTUAL_GAMEPAD_PID) { + iconType = getSteamVirtualControllerDetection(ctrl, CONTROLLER_ICON_STEAM_DECK); + } + } + } + return glyphGetControllerButtonName(iconType, jbtn); + } + } + } + + // fallback to generic type if no controller is connected or recognized + return glyphGetControllerButtonName(CONTROLLER_ICON_GENERIC, jbtn); +} + s32 inputGetKeyByName(const char *name) { s32 start = 0; @@ -1536,6 +1675,7 @@ PD_CONSTRUCTOR static void inputConfigInit(void) configRegisterInt(strFmt("%s.CancelCButtons", secname), &padsCfg[c].cancelCButtons, 0, 1); configRegisterInt(strFmt("%s.SwapSticks", secname), &padsCfg[c].swapSticks, 0, 1); configRegisterInt(strFmt("%s.ControllerIndex", secname), &padsCfg[c].deviceIndex, -1, 0x7FFFFFFF); + configRegisterInt(strFmt("%s.ButtonPromptOverride", secname), &padsCfg[c].buttonPromptOverride, GLYPH_AUTO, GLYPH_STEAM_DECK); secname[13] = '.'; for (u32 ck = 0; ck < CK_TOTAL_COUNT; ++ck) { snprintf(keyname, sizeof(keyname), "%s.%s", secname, inputGetContKeyName(ck)); diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 5632153346..55375085a9 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -14,6 +14,7 @@ #include "video.h" #include "input.h" #include "config.h" +#include "glyph.h" static s32 g_ExtMenuPlayer = 0; static struct menudialogdef *g_ExtNextDialog = NULL; @@ -616,6 +617,52 @@ static MenuItemHandlerResult menuhandlerController(s32 operation, struct menuite return 0; } +static MenuItemHandlerResult menuhandlerButtonPromptOverride(s32 operation, struct menuitem *item, union handlerdata *data) +{ + static const struct { + const char *name; + s32 value; + } promptoptions [] = { + { "Auto", GLYPH_AUTO }, + { "Generic", GLYPH_GENERIC }, + { "Internal", GLYPH_INTERNAL }, + { "Xbox 360 Controller", GLYPH_XBOX360 }, + { "Xbox Wireless Controller", GLYPH_XBOXONE }, + { "DualShock 3", GLYPH_PS3 }, + { "DualShock 4", GLYPH_PS4 }, + { "DualSense", GLYPH_PS5 }, + { "Nintendo Switch Controller", GLYPH_NINTENDO_SWITCH }, + { "Nintendo 64 Controller", GLYPH_NINTENDO_64 }, + { "Steam Controller", GLYPH_STEAM_CONTROLLER }, + { "Steam Deck", GLYPH_STEAM_DECK } + }; + + switch (operation) { + case MENUOP_GETOPTIONCOUNT: + data->dropdown.value = ARRAYCOUNT(promptoptions); + break; + case MENUOP_GETOPTIONTEXT: + return (intptr_t)promptoptions[data->dropdown.value].name; + case MENUOP_SET: + inputSetButtonPromptOverride(g_ExtMenuPlayer, promptoptions[data->dropdown.value].value); + break; + case MENUOP_GETSELECTEDINDEX: + { + s32 currentValue = inputGetButtonPromptOverride(g_ExtMenuPlayer); + for (s32 i = 0; i < ARRAYCOUNT(promptoptions); i++) { + if (promptoptions[i].value == currentValue) { + data->dropdown.value = i; + return 0; + } + } + data->dropdown.value = 0; + } + break; + } + + return 0; +} + struct menuitem g_ExtendedControllerMenuItems[] = { { MENUITEMTYPE_DROPDOWN, @@ -625,6 +672,14 @@ struct menuitem g_ExtendedControllerMenuItems[] = { 0, menuhandlerController, }, + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Button Prompt Styles", + 0, + menuhandlerButtonPromptOverride, + }, { MENUITEMTYPE_CHECKBOX, 0, @@ -1771,7 +1826,7 @@ static MenuItemHandlerResult menuhandlerBind(s32 operation, struct menuitem *ite case MENUOP_GETOPTIONTEXT: binds = inputKeyGetBinds(g_ExtMenuPlayer, menuBinds[idx].ck); if (binds && binds[data->dropdown.value]) { - strncpy(keyname, inputGetKeyName(binds[data->dropdown.value]), sizeof(keyname) - 1); + strncpy(keyname, inputGetButtonDisplayName(binds[data->dropdown.value]), sizeof(keyname) - 1); for (char *p = keyname; *p; ++p) { if (*p == '_') *p = ' '; }