Skip to content

Commit 819ecaf

Browse files
committed
HelpComponent: Display different help icons depending on controller
Before this commit, all buttons shown in the help component would use the SNES layout, which wouldn't match when having plugged in an Xbox or Sony Playstation controller. If there are different kinds of controllers plugged in, the input mapping shown will be of the last used or inserted. The controller identification is done via the SDL gamecontroller API. The intention here is to help solving #442, or at least most of it.
1 parent 0c4b42d commit 819ecaf

File tree

6 files changed

+131
-20
lines changed

6 files changed

+131
-20
lines changed

es-core/src/InputConfig.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ std::string toLower(std::string str)
5050
}
5151
//end util functions
5252

53-
InputConfig::InputConfig(int deviceId, const std::string& deviceName, const std::string& deviceGUID) : mDeviceId(deviceId), mDeviceName(deviceName), mDeviceGUID(deviceGUID)
53+
InputConfig::InputConfig(int deviceId,
54+
const std::string& deviceName,
55+
const std::string& deviceGUID,
56+
InputButtonLayout buttonLayout) : mDeviceId(deviceId), mDeviceName(deviceName), mDeviceGUID(deviceGUID), mButtonLayout(buttonLayout)
5457
{
5558
}
5659

es-core/src/InputConfig.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ enum InputType
2424
TYPE_COUNT
2525
};
2626

27+
enum InputButtonLayout
28+
{
29+
BUTTON_LAYOUT_DEFAULT, // In the style of an SNES controller
30+
BUTTON_LAYOUT_PLAYSTATION,
31+
BUTTON_LAYOUT_XBOX,
32+
};
33+
2734
struct Input
2835
{
2936
public:
@@ -96,7 +103,10 @@ struct Input
96103
class InputConfig
97104
{
98105
public:
99-
InputConfig(int deviceId, const std::string& deviceName, const std::string& deviceGUID);
106+
InputConfig(int deviceId,
107+
const std::string& deviceName,
108+
const std::string& deviceGUID,
109+
InputButtonLayout buttonLayout = BUTTON_LAYOUT_DEFAULT);
100110

101111
void clear();
102112
void mapInput(const std::string& name, Input input);
@@ -105,6 +115,7 @@ class InputConfig
105115
inline int getDeviceId() const { return mDeviceId; };
106116
inline const std::string& getDeviceName() { return mDeviceName; }
107117
inline const std::string& getDeviceGUIDString() { return mDeviceGUID; }
118+
inline InputButtonLayout getButtonLayout() const { return mButtonLayout; }
108119

109120
//Returns true if Input is mapped to this name, false otherwise.
110121
bool isMappedTo(const std::string& name, Input input);
@@ -127,6 +138,7 @@ class InputConfig
127138
const int mDeviceId;
128139
const std::string mDeviceName;
129140
const std::string mDeviceGUID;
141+
const InputButtonLayout mButtonLayout;
130142
};
131143

132144
#endif // ES_CORE_INPUT_CONFIG_H

es-core/src/InputManager.cpp

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "InputManager.h"
22

3+
#include "resources/ResourceManager.h"
34
#include "utils/FileSystemUtil.h"
45
#include "CECInput.h"
56
#include "Log.h"
@@ -31,7 +32,7 @@ int SDL_USER_CECBUTTONUP = -1;
3132

3233
InputManager* InputManager::mInstance = NULL;
3334

34-
InputManager::InputManager() : mKeyboardInputConfig(NULL)
35+
InputManager::InputManager() : mKeyboardInputConfig(NULL), mLastUsedKeyboardOrController(DEVICE_KEYBOARD)
3536
{
3637
}
3738

@@ -62,7 +63,7 @@ void InputManager::init()
6263
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI, "0");
6364
#endif
6465
#endif
65-
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
66+
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
6667
SDL_JoystickEventState(SDL_ENABLE);
6768

6869
// first, open all currently present joysticks
@@ -98,8 +99,36 @@ void InputManager::addJoystickByDeviceIndex(int id)
9899
char guid[65];
99100
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, 65);
100101

102+
InputButtonLayout buttonLayout = BUTTON_LAYOUT_DEFAULT;
103+
switch(SDL_GameControllerTypeForIndex(id))
104+
{
105+
case SDL_CONTROLLER_TYPE_PS3:
106+
case SDL_CONTROLLER_TYPE_PS4:
107+
case SDL_CONTROLLER_TYPE_PS5:
108+
buttonLayout = BUTTON_LAYOUT_PLAYSTATION;
109+
break;
110+
111+
case SDL_CONTROLLER_TYPE_XBOX360:
112+
case SDL_CONTROLLER_TYPE_XBOXONE:
113+
case SDL_CONTROLLER_TYPE_AMAZON_LUNA:
114+
case SDL_CONTROLLER_TYPE_GOOGLE_STADIA:
115+
case SDL_CONTROLLER_TYPE_NVIDIA_SHIELD:
116+
buttonLayout = BUTTON_LAYOUT_XBOX;
117+
break;
118+
119+
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
120+
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
121+
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
122+
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
123+
break;
124+
125+
case SDL_CONTROLLER_TYPE_UNKNOWN:
126+
case SDL_CONTROLLER_TYPE_VIRTUAL:
127+
break;
128+
}
129+
101130
// create the InputConfig
102-
mInputConfigs[joyId] = new InputConfig(joyId, SDL_JoystickName(joy), guid);
131+
mInputConfigs[joyId] = new InputConfig(joyId, SDL_JoystickName(joy), guid, buttonLayout);
103132
if(!loadInputConfig(mInputConfigs[joyId]))
104133
{
105134
LOG(LogInfo) << "Added unconfigured joystick '" << SDL_JoystickName(joy) << "' (GUID: " << guid << ", instance ID: " << joyId << ", device index: " << id << ").";
@@ -111,6 +140,8 @@ void InputManager::addJoystickByDeviceIndex(int id)
111140
int numAxes = SDL_JoystickNumAxes(joy);
112141
mPrevAxisValues[joyId] = new int[numAxes];
113142
std::fill(mPrevAxisValues[joyId], mPrevAxisValues[joyId] + numAxes, 0); //initialize array to 0
143+
144+
mLastUsedKeyboardOrController = joyId;
114145
}
115146

116147
void InputManager::removeJoystickByJoystickID(SDL_JoystickID joyId)
@@ -172,7 +203,7 @@ void InputManager::deinit()
172203
CECInput::deinit();
173204

174205
SDL_JoystickEventState(SDL_DISABLE);
175-
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
206+
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
176207
}
177208

178209
int InputManager::getNumJoysticks() { return (int)mJoysticks.size(); }
@@ -198,6 +229,9 @@ int InputManager::getButtonCountByDevice(SDL_JoystickID id)
198229

199230
InputConfig* InputManager::getInputConfigByDevice(int device)
200231
{
232+
if(device != DEVICE_CEC)
233+
mLastUsedKeyboardOrController = device;
234+
201235
if(device == DEVICE_KEYBOARD)
202236
return mKeyboardInputConfig;
203237
else if(device == DEVICE_CEC)
@@ -206,6 +240,18 @@ InputConfig* InputManager::getInputConfigByDevice(int device)
206240
return mInputConfigs[device];
207241
}
208242

243+
InputConfig* InputManager::getInputConfigForLastUsedDevice() const
244+
{
245+
if(mLastUsedKeyboardOrController == DEVICE_KEYBOARD)
246+
return mKeyboardInputConfig;
247+
248+
const auto it = mInputConfigs.find(mLastUsedKeyboardOrController);
249+
if(it != mInputConfigs.end())
250+
return it->second;
251+
252+
return nullptr; // Could happen if last used controller was unplugged
253+
}
254+
209255
bool InputManager::parseEvent(const SDL_Event& ev, Window* window)
210256
{
211257
bool causedEvent = false;

es-core/src/InputManager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class InputManager
2626
std::map<SDL_JoystickID, InputConfig*> mInputConfigs;
2727
InputConfig* mKeyboardInputConfig;
2828
InputConfig* mCECInputConfig;
29+
int mLastUsedKeyboardOrController;
2930

3031
std::map<SDL_JoystickID, int*> mPrevAxisValues;
3132

@@ -56,6 +57,7 @@ class InputManager
5657
std::string getDeviceGUIDString(int deviceId);
5758

5859
InputConfig* getInputConfigByDevice(int deviceId);
60+
InputConfig* getInputConfigForLastUsedDevice() const;
5961

6062
bool parseEvent(const SDL_Event& ev, Window* window);
6163
};

es-core/src/components/HelpComponent.cpp

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@
33
#include "components/ComponentGrid.h"
44
#include "components/ImageComponent.h"
55
#include "components/TextComponent.h"
6-
#include "resources/TextureResource.h"
76
#include "utils/StringUtil.h"
87
#include "Log.h"
98
#include "Settings.h"
9+
#include "InputManager.h"
1010

1111
#define OFFSET_X 12 // move the entire thing right by this amount (px)
1212
#define OFFSET_Y 12 // move the entire thing up by this amount (px)
1313

1414
#define ICON_TEXT_SPACING 8 // space between [icon] and [text] (px)
1515
#define ENTRY_SPACING 16 // space between [text] and next [icon] (px)
1616

17-
static const std::map<std::string, const char*> ICON_PATH_MAP {
17+
static const HelpComponent::IconPathMap DEFAULT_ICON_PATH_MAP {
1818
{ "up/down", ":/help/dpad_updown.svg" },
1919
{ "left/right", ":/help/dpad_leftright.svg" },
2020
{ "up/down/left/right", ":/help/dpad_all.svg" },
@@ -29,6 +29,20 @@ static const std::map<std::string, const char*> ICON_PATH_MAP {
2929
{ "select", ":/help/button_select.svg" }
3030
};
3131

32+
static const HelpComponent::IconPathMap NO_ICON_OVERRIDES {};
33+
static const HelpComponent::IconPathMap XBOX_ICON_OVERRIDES {
34+
{ "a", ":/help/button_b.svg" },
35+
{ "b", ":/help/button_a.svg" },
36+
{ "x", ":/help/button_y.svg" },
37+
{ "y", ":/help/button_x.svg" },
38+
};
39+
static const HelpComponent::IconPathMap PLAYSTATION_ICON_OVERRIDES {
40+
{ "a", ":/help/button_circle.svg" },
41+
{ "b", ":/help/button_cross.svg" },
42+
{ "x", ":/help/button_triangle.svg" },
43+
{ "y", ":/help/button_square.svg" },
44+
};
45+
3246
HelpComponent::HelpComponent(Window* window) : GuiComponent(window)
3347
{
3448
}
@@ -67,12 +81,15 @@ void HelpComponent::updateGrid()
6781
std::vector< std::shared_ptr<ImageComponent> > icons;
6882
std::vector< std::shared_ptr<TextComponent> > labels;
6983

84+
const auto& iconOverrides =
85+
getIconOverridesForInput(InputManager::getInstance()->getInputConfigForLastUsedDevice());
86+
7087
float width = 0;
7188
const float height = Math::round(font->getLetterHeight() * 1.25f);
7289
for(auto it = mPrompts.cbegin(); it != mPrompts.cend(); it++)
7390
{
7491
auto icon = std::make_shared<ImageComponent>(mWindow);
75-
icon->setImage(getIconTexture(it->first.c_str()));
92+
icon->setImage(getIconTexture(it->first, iconOverrides));
7693
icon->setColorShift(mStyle.iconColor);
7794
icon->setResize(0, height);
7895
icons.push_back(icon);
@@ -100,26 +117,51 @@ void HelpComponent::updateGrid()
100117
mGrid->setOrigin(mStyle.origin);
101118
}
102119

103-
std::shared_ptr<TextureResource> HelpComponent::getIconTexture(const char* name)
120+
const HelpComponent::IconPathMap& HelpComponent::getIconOverridesForInput(InputConfig* inputConfig)
104121
{
105-
auto it = mIconCache.find(name);
106-
if(it != mIconCache.cend())
107-
return it->second;
122+
if(!inputConfig)
123+
return NO_ICON_OVERRIDES;
108124

109-
auto pathLookup = ICON_PATH_MAP.find(name);
110-
if(pathLookup == ICON_PATH_MAP.cend())
125+
switch(inputConfig->getButtonLayout())
111126
{
112-
LOG(LogError) << "Unknown help icon \"" << name << "\"!";
113-
return nullptr;
127+
case BUTTON_LAYOUT_PLAYSTATION:
128+
return PLAYSTATION_ICON_OVERRIDES;
129+
130+
case BUTTON_LAYOUT_XBOX:
131+
return XBOX_ICON_OVERRIDES;
132+
133+
case BUTTON_LAYOUT_DEFAULT:
134+
break;
114135
}
136+
137+
return NO_ICON_OVERRIDES;
138+
}
139+
140+
std::shared_ptr<TextureResource> HelpComponent::getIconTexture(const std::string& name, const IconPathMap& iconOverrides)
141+
{
142+
auto pathLookup = iconOverrides.find(name);
143+
if(pathLookup == iconOverrides.cend())
144+
{
145+
pathLookup = DEFAULT_ICON_PATH_MAP.find(name);
146+
if(pathLookup == DEFAULT_ICON_PATH_MAP.cend())
147+
{
148+
LOG(LogError) << "Unknown help icon \"" << name << "\"!";
149+
return nullptr;
150+
}
151+
}
152+
153+
auto it = mIconCache.find(pathLookup->second);
154+
if(it != mIconCache.cend())
155+
return it->second;
156+
115157
if(!ResourceManager::getInstance()->fileExists(pathLookup->second))
116158
{
117-
LOG(LogError) << "Help icon \"" << name << "\" - corresponding image file \"" << pathLookup->second << "\" misisng!";
159+
LOG(LogError) << "Help icon \"" << name << "\" - corresponding image file \"" << pathLookup->second << "\" missing!";
118160
return nullptr;
119161
}
120162

121163
std::shared_ptr<TextureResource> tex = TextureResource::get(pathLookup->second);
122-
mIconCache[std::string(name)] = tex;
164+
mIconCache[pathLookup->second] = tex;
123165
return tex;
124166
}
125167

es-core/src/components/HelpComponent.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
#include "GuiComponent.h"
66
#include "HelpStyle.h"
7+
#include "resources/TextureResource.h"
8+
9+
#include <string>
710

811
class ComponentGrid;
912
class ImageComponent;
@@ -22,8 +25,11 @@ class HelpComponent : public GuiComponent
2225

2326
void setStyle(const HelpStyle& style);
2427

28+
using IconPathMap = std::map<std::string /*name*/, std::string /*path*/>;
29+
2530
private:
26-
std::shared_ptr<TextureResource> getIconTexture(const char* name);
31+
const IconPathMap& getIconOverridesForInput(InputConfig* inputConfig);
32+
std::shared_ptr<TextureResource> getIconTexture(const std::string& name, const IconPathMap& iconOverrides);
2733
std::map< std::string, std::shared_ptr<TextureResource> > mIconCache;
2834

2935
std::shared_ptr<ComponentGrid> mGrid;

0 commit comments

Comments
 (0)