mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-12-23 01:59:43 +01:00
afbcb3f867
It's kind of a bummer that L/R don't actually do anything... we should add ZL/ZR support at some point. Also note that GameCube binds X to 'back' rather than B, this will be fixed by SDL_ActionSet for 2.5.
420 lines
12 KiB
C++
420 lines
12 KiB
C++
#include "ButtonGlyphs.h"
|
|
|
|
#include <SDL.h>
|
|
|
|
#include "Game.h"
|
|
#include "Localization.h"
|
|
#include "UTF8.h"
|
|
|
|
extern "C"
|
|
{
|
|
|
|
typedef enum
|
|
{
|
|
GLYPH_NINTENDO_DECK_A, // Note that for the Deck, the icons are same as Nintendo but the layout is the same as Xbox
|
|
GLYPH_NINTENDO_DECK_B,
|
|
GLYPH_NINTENDO_DECK_X,
|
|
GLYPH_NINTENDO_DECK_Y,
|
|
GLYPH_NINTENDO_PLUS,
|
|
GLYPH_NINTENDO_MINUS,
|
|
GLYPH_NINTENDO_L,
|
|
GLYPH_NINTENDO_R,
|
|
GLYPH_NINTENDO_ZL,
|
|
GLYPH_NINTENDO_ZR,
|
|
GLYPH_NINTENDO_XBOX_LSTICK,
|
|
GLYPH_NINTENDO_XBOX_RSTICK,
|
|
GLYPH_NINTENDO_SL,
|
|
GLYPH_NINTENDO_SR,
|
|
GLYPH_GENERIC_L,
|
|
GLYPH_GENERIC_R,
|
|
|
|
GLYPH_PLAYSTATION_CIRCLE,
|
|
GLYPH_PLAYSTATION_CROSS,
|
|
GLYPH_PLAYSTATION_TRIANGLE,
|
|
GLYPH_PLAYSTATION_SQUARE,
|
|
GLYPH_PLAYSTATION_START,
|
|
GLYPH_PLAYSTATION_OPTIONS,
|
|
GLYPH_PLAYSTATION_DECK_L1,
|
|
GLYPH_PLAYSTATION_DECK_R1,
|
|
GLYPH_PLAYSTATION_DECK_L2,
|
|
GLYPH_PLAYSTATION_DECK_R2,
|
|
GLYPH_PLAYSTATION_DECK_L3,
|
|
GLYPH_PLAYSTATION_DECK_R3,
|
|
GLYPH_DECK_L4,
|
|
GLYPH_DECK_R4,
|
|
GLYPH_DECK_L5,
|
|
GLYPH_DECK_R5,
|
|
|
|
GLYPH_XBOX_B,
|
|
GLYPH_XBOX_A,
|
|
GLYPH_XBOX_Y,
|
|
GLYPH_XBOX_X,
|
|
GLYPH_XBOX_DECK_VIEW,
|
|
GLYPH_XBOX_DECK_MENU,
|
|
GLYPH_XBOX_LB,
|
|
GLYPH_XBOX_RB,
|
|
GLYPH_XBOX_LT,
|
|
GLYPH_XBOX_RT,
|
|
GLYPH_NINTENDO_GENERIC_ACTIONRIGHT,
|
|
GLYPH_NINTENDO_GENERIC_ACTIONDOWN,
|
|
GLYPH_NINTENDO_GENERIC_ACTIONUP,
|
|
GLYPH_NINTENDO_GENERIC_ACTIONLEFT,
|
|
GLYPH_NINTENDO_GENERIC_STICK,
|
|
GLYPH_UNKNOWN,
|
|
|
|
/* Added after 2.4 */
|
|
GLYPH_NINTENDO_GAMECUBE_A,
|
|
GLYPH_NINTENDO_GAMECUBE_B,
|
|
GLYPH_NINTENDO_GAMECUBE_X,
|
|
GLYPH_NINTENDO_GAMECUBE_Y,
|
|
GLYPH_NINTENDO_GAMECUBE_L,
|
|
GLYPH_NINTENDO_GAMECUBE_R,
|
|
GLYPH_NINTENDO_GAMECUBE_Z,
|
|
|
|
GLYPH_TOTAL
|
|
}
|
|
ButtonGlyphKey;
|
|
|
|
static char glyph[GLYPH_TOTAL][5];
|
|
|
|
typedef enum
|
|
{
|
|
LAYOUT_NINTENDO_SWITCH_PRO,
|
|
LAYOUT_NINTENDO_SWITCH_JOYCON_L,
|
|
LAYOUT_NINTENDO_SWITCH_JOYCON_R,
|
|
LAYOUT_DECK,
|
|
LAYOUT_PLAYSTATION,
|
|
LAYOUT_XBOX,
|
|
LAYOUT_GENERIC,
|
|
|
|
/* Added after 2.4 */
|
|
LAYOUT_GAMECUBE,
|
|
|
|
LAYOUT_TOTAL
|
|
}
|
|
ButtonGlyphLayout;
|
|
|
|
/* SDL provides Xbox buttons, we'd like to show the correct
|
|
* (controller-specific) glyphs or labels for those... */
|
|
static const char* glyph_layout[LAYOUT_TOTAL][SDL_CONTROLLER_BUTTON_RIGHTSHOULDER + 1] = {
|
|
{ // NINTENDO_SWITCH_PRO
|
|
glyph[GLYPH_NINTENDO_DECK_B], glyph[GLYPH_NINTENDO_DECK_A],
|
|
glyph[GLYPH_NINTENDO_DECK_Y], glyph[GLYPH_NINTENDO_DECK_X],
|
|
glyph[GLYPH_NINTENDO_MINUS], "HOME", glyph[GLYPH_NINTENDO_PLUS],
|
|
glyph[GLYPH_NINTENDO_XBOX_LSTICK], glyph[GLYPH_NINTENDO_XBOX_RSTICK],
|
|
glyph[GLYPH_NINTENDO_L], glyph[GLYPH_NINTENDO_R]
|
|
},
|
|
{ // NINTENDO_SWITCH_JOYCON_L
|
|
glyph[GLYPH_NINTENDO_GENERIC_ACTIONDOWN], glyph[GLYPH_NINTENDO_GENERIC_ACTIONRIGHT],
|
|
glyph[GLYPH_NINTENDO_GENERIC_ACTIONLEFT], glyph[GLYPH_NINTENDO_GENERIC_ACTIONUP],
|
|
"CAPTURE", "GUIDE", glyph[GLYPH_NINTENDO_MINUS],
|
|
glyph[GLYPH_NINTENDO_GENERIC_STICK], glyph[GLYPH_NINTENDO_XBOX_RSTICK],
|
|
glyph[GLYPH_NINTENDO_SL], glyph[GLYPH_NINTENDO_SR]
|
|
},
|
|
{ // NINTENDO_SWITCH_JOYCON_R
|
|
glyph[GLYPH_NINTENDO_GENERIC_ACTIONDOWN], glyph[GLYPH_NINTENDO_GENERIC_ACTIONRIGHT],
|
|
glyph[GLYPH_NINTENDO_GENERIC_ACTIONLEFT], glyph[GLYPH_NINTENDO_GENERIC_ACTIONUP],
|
|
"HOME", "GUIDE", glyph[GLYPH_NINTENDO_PLUS],
|
|
glyph[GLYPH_NINTENDO_GENERIC_STICK], glyph[GLYPH_NINTENDO_XBOX_RSTICK],
|
|
glyph[GLYPH_NINTENDO_SL], glyph[GLYPH_NINTENDO_SR]
|
|
},
|
|
{ // DECK
|
|
glyph[GLYPH_NINTENDO_DECK_A], glyph[GLYPH_NINTENDO_DECK_B],
|
|
glyph[GLYPH_NINTENDO_DECK_X], glyph[GLYPH_NINTENDO_DECK_Y],
|
|
glyph[GLYPH_XBOX_DECK_VIEW], "GUIDE", glyph[GLYPH_XBOX_DECK_MENU],
|
|
glyph[GLYPH_PLAYSTATION_DECK_L3], glyph[GLYPH_PLAYSTATION_DECK_R3],
|
|
glyph[GLYPH_PLAYSTATION_DECK_L1], glyph[GLYPH_PLAYSTATION_DECK_R1]
|
|
},
|
|
{ // PLAYSTATION
|
|
glyph[GLYPH_PLAYSTATION_CROSS], glyph[GLYPH_PLAYSTATION_CIRCLE],
|
|
glyph[GLYPH_PLAYSTATION_SQUARE], glyph[GLYPH_PLAYSTATION_TRIANGLE],
|
|
glyph[GLYPH_PLAYSTATION_OPTIONS], "PS", glyph[GLYPH_PLAYSTATION_START],
|
|
glyph[GLYPH_PLAYSTATION_DECK_L3], glyph[GLYPH_PLAYSTATION_DECK_R3],
|
|
glyph[GLYPH_PLAYSTATION_DECK_L1], glyph[GLYPH_PLAYSTATION_DECK_R1]
|
|
},
|
|
{ // XBOX
|
|
glyph[GLYPH_XBOX_A], glyph[GLYPH_XBOX_B],
|
|
glyph[GLYPH_XBOX_X], glyph[GLYPH_XBOX_Y],
|
|
glyph[GLYPH_XBOX_DECK_VIEW], "GUIDE", glyph[GLYPH_XBOX_DECK_MENU],
|
|
glyph[GLYPH_NINTENDO_XBOX_LSTICK], glyph[GLYPH_NINTENDO_XBOX_RSTICK],
|
|
glyph[GLYPH_XBOX_LB], glyph[GLYPH_XBOX_RB]
|
|
},
|
|
{ // GENERIC
|
|
glyph[GLYPH_NINTENDO_GENERIC_ACTIONDOWN], glyph[GLYPH_NINTENDO_GENERIC_ACTIONRIGHT],
|
|
glyph[GLYPH_NINTENDO_GENERIC_ACTIONLEFT], glyph[GLYPH_NINTENDO_GENERIC_ACTIONUP],
|
|
"SELECT", "GUIDE", "START",
|
|
glyph[GLYPH_NINTENDO_XBOX_LSTICK], glyph[GLYPH_NINTENDO_XBOX_RSTICK],
|
|
glyph[GLYPH_GENERIC_L], glyph[GLYPH_GENERIC_R]
|
|
},
|
|
{ // GAMECUBE
|
|
glyph[GLYPH_NINTENDO_GAMECUBE_A], glyph[GLYPH_NINTENDO_GAMECUBE_X],
|
|
glyph[GLYPH_NINTENDO_GAMECUBE_B], glyph[GLYPH_NINTENDO_GAMECUBE_Y],
|
|
glyph[GLYPH_UNKNOWN], glyph[GLYPH_UNKNOWN], "START",
|
|
glyph[GLYPH_UNKNOWN], glyph[GLYPH_UNKNOWN],
|
|
glyph[GLYPH_UNKNOWN], glyph[GLYPH_NINTENDO_GAMECUBE_Z]
|
|
}
|
|
};
|
|
|
|
static bool keyboard_is_active = true;
|
|
static ButtonGlyphLayout layout = LAYOUT_GENERIC;
|
|
|
|
void BUTTONGLYPHS_init(void)
|
|
{
|
|
/* Set glyph array to strings for all the button glyph codepoints (U+EBxx) */
|
|
for (int i = 0; i < GLYPH_TOTAL; i++)
|
|
{
|
|
SDL_strlcpy(glyph[i], UTF8_encode(0xEB00+i).bytes, sizeof(glyph[i]));
|
|
}
|
|
}
|
|
|
|
bool BUTTONGLYPHS_keyboard_is_available(void)
|
|
{
|
|
/* Returns true if it makes sense to show button hints that are only available
|
|
* on keyboards (like press M to mute), false if we're on a console. */
|
|
|
|
if (BUTTONGLYPHS_keyboard_is_active())
|
|
{
|
|
/* The keyboard is active, so there HAS to be a keyboard available */
|
|
return true;
|
|
}
|
|
|
|
#ifdef __ANDROID__
|
|
return false;
|
|
#else
|
|
return !SDL_GetHintBoolean("SteamDeck", SDL_FALSE);
|
|
#endif
|
|
}
|
|
|
|
bool BUTTONGLYPHS_keyboard_is_active(void)
|
|
{
|
|
/* Returns true if, not only do we have a keyboard available, but it's also the
|
|
* active input method. (So, show keyboard keys, if false, show controller glyphs) */
|
|
return keyboard_is_active;
|
|
}
|
|
|
|
void BUTTONGLYPHS_keyboard_set_active(bool active)
|
|
{
|
|
keyboard_is_active = active;
|
|
}
|
|
|
|
void BUTTONGLYPHS_update_layout(SDL_GameController *c)
|
|
{
|
|
Uint16 vendor = SDL_GameControllerGetVendor(c);
|
|
Uint16 product = SDL_GameControllerGetProduct(c);
|
|
|
|
if (vendor == 0x054c)
|
|
{
|
|
layout = LAYOUT_PLAYSTATION;
|
|
}
|
|
else if (vendor == 0x28de)
|
|
{
|
|
/* Steam Virtual Gamepads can hypothetically tell us that the physical
|
|
* device is a PlayStation controller, so try to catch that scenario */
|
|
SDL_GameControllerType gct = SDL_GameControllerGetType(c);
|
|
if ( gct == SDL_CONTROLLER_TYPE_PS3 ||
|
|
gct == SDL_CONTROLLER_TYPE_PS4 ||
|
|
gct == SDL_CONTROLLER_TYPE_PS5 )
|
|
{
|
|
layout = LAYOUT_PLAYSTATION;
|
|
}
|
|
else
|
|
{
|
|
layout = LAYOUT_DECK;
|
|
}
|
|
}
|
|
else if (vendor == 0x057e)
|
|
{
|
|
if (product == 0x2006)
|
|
{
|
|
layout = LAYOUT_NINTENDO_SWITCH_JOYCON_L;
|
|
}
|
|
else if (product == 0x2007)
|
|
{
|
|
layout = LAYOUT_NINTENDO_SWITCH_JOYCON_R;
|
|
}
|
|
else if (product == 0x0337)
|
|
{
|
|
layout = LAYOUT_GAMECUBE;
|
|
}
|
|
else
|
|
{
|
|
layout = LAYOUT_NINTENDO_SWITCH_PRO;
|
|
}
|
|
}
|
|
else if (vendor == 0x2dc8) /* 8BitDo */
|
|
{
|
|
if ( product == 0x2002 || /* Ultimate Wired Controller for Xbox */
|
|
product == 0x3106 ) /* Ultimate Wireless / Pro 2 Wired Controller */
|
|
{
|
|
layout = LAYOUT_XBOX;
|
|
}
|
|
else
|
|
{
|
|
layout = LAYOUT_NINTENDO_SWITCH_PRO;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* For now we assume Xbox (0x045e), Generic will be used when
|
|
* migrating to SDL_ActionSet
|
|
*/
|
|
layout = LAYOUT_XBOX;
|
|
}
|
|
}
|
|
|
|
const char* BUTTONGLYPHS_get_wasd_text(void)
|
|
{
|
|
/* Returns the string to use in Welcome Aboard */
|
|
if (BUTTONGLYPHS_keyboard_is_active())
|
|
{
|
|
return loc::gettext("Press arrow keys or WASD to move");
|
|
}
|
|
return loc::gettext("Press left/right to move");
|
|
}
|
|
|
|
static const char* sdlbutton_to_glyph(const SDL_GameControllerButton button)
|
|
{
|
|
if (button > SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
|
|
{
|
|
SDL_assert(0 && "Unhandled button!");
|
|
return glyph[GLYPH_UNKNOWN];
|
|
}
|
|
|
|
return glyph_layout[layout][button];
|
|
}
|
|
|
|
static const char* glyph_for_vector(
|
|
const std::vector<SDL_GameControllerButton>& buttons,
|
|
const int index
|
|
) {
|
|
if (index < 0 || index >= (int) buttons.size())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return sdlbutton_to_glyph(buttons[index]);
|
|
}
|
|
|
|
const char* BUTTONGLYPHS_get_button(const ActionSet actionset, const Action action, int binding)
|
|
{
|
|
/* Given a specific action (like INTERACT in-game),
|
|
* return either a (localized) keyboard key string like "ENTER" or "E",
|
|
* or a controller button glyph from the table above like glyph[GLYPH_XBOX_Y],
|
|
* to fill into strings like "Press {button} to activate terminal".
|
|
*
|
|
* Normally, set binding = -1. This will return the best keyboard key OR controller glyph.
|
|
*
|
|
* If binding >= 0, select a specific CONTROLLER binding glyph,
|
|
* or NULL if the index is higher than the max binding index. */
|
|
|
|
bool show_controller = binding >= 0 || !BUTTONGLYPHS_keyboard_is_active();
|
|
if (binding < 0)
|
|
{
|
|
binding = 0;
|
|
}
|
|
|
|
switch (actionset)
|
|
{
|
|
case ActionSet_Menu:
|
|
switch (action.Menu)
|
|
{
|
|
case Action_Menu_Accept:
|
|
if (show_controller)
|
|
{
|
|
return glyph_for_vector(game.controllerButton_flip, binding);
|
|
}
|
|
return loc::gettext("ACTION");
|
|
}
|
|
break;
|
|
case ActionSet_InGame:
|
|
switch (action.InGame)
|
|
{
|
|
case Action_InGame_ACTION:
|
|
if (show_controller)
|
|
{
|
|
return glyph_for_vector(game.controllerButton_flip, binding);
|
|
}
|
|
return loc::gettext("ACTION");
|
|
|
|
case Action_InGame_Interact:
|
|
if (show_controller)
|
|
{
|
|
/* FIXME: this really does depend on the Enter/E speedrunner option...
|
|
* This is messy, but let's not show the wrong thing here... */
|
|
if (game.separate_interact)
|
|
{
|
|
return glyph_for_vector(game.controllerButton_interact, binding);
|
|
}
|
|
return glyph_for_vector(game.controllerButton_map, binding);
|
|
}
|
|
if (game.separate_interact)
|
|
{
|
|
return "E";
|
|
}
|
|
return loc::gettext("ENTER");
|
|
|
|
case Action_InGame_Map:
|
|
if (show_controller)
|
|
{
|
|
return glyph_for_vector(game.controllerButton_map, binding);
|
|
}
|
|
return loc::gettext("ENTER");
|
|
|
|
case Action_InGame_Esc:
|
|
if (show_controller)
|
|
{
|
|
return glyph_for_vector(game.controllerButton_esc, binding);
|
|
}
|
|
return loc::gettext("ESC");
|
|
|
|
case Action_InGame_Restart:
|
|
if (show_controller)
|
|
{
|
|
return glyph_for_vector(game.controllerButton_restart, binding);
|
|
}
|
|
return "R";
|
|
}
|
|
break;
|
|
}
|
|
|
|
SDL_assert(0 && "Trying to get label/glyph for unknown action!");
|
|
return glyph[GLYPH_UNKNOWN];
|
|
}
|
|
|
|
char* BUTTONGLYPHS_get_all_gamepad_buttons(
|
|
char* buffer,
|
|
size_t buffer_len,
|
|
const ActionSet actionset,
|
|
const int action
|
|
) {
|
|
/* Gives a list of all controller bindings, for in the menu */
|
|
Action union_action;
|
|
union_action.intval = action;
|
|
buffer[0] = '\0';
|
|
size_t cur = 0;
|
|
const char* glyph;
|
|
int binding = 0;
|
|
while ((glyph = BUTTONGLYPHS_get_button(actionset, union_action, binding)))
|
|
{
|
|
if (binding > 0 && buffer_len >= 1)
|
|
{
|
|
buffer[cur] = '/';
|
|
cur++;
|
|
buffer_len--;
|
|
}
|
|
|
|
size_t glyph_len = SDL_strlcpy(&buffer[cur], glyph, buffer_len);
|
|
if (glyph_len >= buffer_len)
|
|
{
|
|
// Truncation occurred, we're done
|
|
return buffer;
|
|
}
|
|
cur += glyph_len;
|
|
buffer_len -= glyph_len;
|
|
|
|
binding++;
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
} // extern "C"
|