diff --git a/es-core/src/InputConfig.cpp b/es-core/src/InputConfig.cpp index eb5e59cecd..1a092f08f1 100644 --- a/es-core/src/InputConfig.cpp +++ b/es-core/src/InputConfig.cpp @@ -50,7 +50,10 @@ std::string toLower(std::string str) } //end util functions -InputConfig::InputConfig(int deviceId, const std::string& deviceName, const std::string& deviceGUID) : mDeviceId(deviceId), mDeviceName(deviceName), mDeviceGUID(deviceGUID) +InputConfig::InputConfig(int deviceId, + const std::string& deviceName, + const std::string& deviceGUID, + InputButtonLayout buttonLayout) : mDeviceId(deviceId), mDeviceName(deviceName), mDeviceGUID(deviceGUID), mButtonLayout(buttonLayout) { } diff --git a/es-core/src/InputConfig.h b/es-core/src/InputConfig.h index 4969d8ff02..c1f1705952 100644 --- a/es-core/src/InputConfig.h +++ b/es-core/src/InputConfig.h @@ -24,6 +24,13 @@ enum InputType TYPE_COUNT }; +enum InputButtonLayout +{ + BUTTON_LAYOUT_DEFAULT, // In the style of an SNES controller + BUTTON_LAYOUT_PLAYSTATION, + BUTTON_LAYOUT_XBOX, +}; + struct Input { public: @@ -96,7 +103,10 @@ struct Input class InputConfig { public: - InputConfig(int deviceId, const std::string& deviceName, const std::string& deviceGUID); + InputConfig(int deviceId, + const std::string& deviceName, + const std::string& deviceGUID, + InputButtonLayout buttonLayout = BUTTON_LAYOUT_DEFAULT); void clear(); void mapInput(const std::string& name, Input input); @@ -105,6 +115,7 @@ class InputConfig inline int getDeviceId() const { return mDeviceId; }; inline const std::string& getDeviceName() { return mDeviceName; } inline const std::string& getDeviceGUIDString() { return mDeviceGUID; } + inline InputButtonLayout getButtonLayout() const { return mButtonLayout; } //Returns true if Input is mapped to this name, false otherwise. bool isMappedTo(const std::string& name, Input input); @@ -127,6 +138,7 @@ class InputConfig const int mDeviceId; const std::string mDeviceName; const std::string mDeviceGUID; + const InputButtonLayout mButtonLayout; }; #endif // ES_CORE_INPUT_CONFIG_H diff --git a/es-core/src/InputManager.cpp b/es-core/src/InputManager.cpp index 53e38f3373..04c32ea96f 100644 --- a/es-core/src/InputManager.cpp +++ b/es-core/src/InputManager.cpp @@ -1,5 +1,6 @@ #include "InputManager.h" +#include "resources/ResourceManager.h" #include "utils/FileSystemUtil.h" #include "CECInput.h" #include "Log.h" @@ -31,7 +32,7 @@ int SDL_USER_CECBUTTONUP = -1; InputManager* InputManager::mInstance = NULL; -InputManager::InputManager() : mKeyboardInputConfig(NULL) +InputManager::InputManager() : mKeyboardInputConfig(NULL), mLastUsedKeyboardOrController(DEVICE_KEYBOARD) { } @@ -62,7 +63,7 @@ void InputManager::init() SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI, "0"); #endif #endif - SDL_InitSubSystem(SDL_INIT_JOYSTICK); + SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); SDL_JoystickEventState(SDL_ENABLE); // first, open all currently present joysticks @@ -98,8 +99,36 @@ void InputManager::addJoystickByDeviceIndex(int id) char guid[65]; SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, 65); + InputButtonLayout buttonLayout = BUTTON_LAYOUT_DEFAULT; + switch(SDL_GameControllerTypeForIndex(id)) + { + case SDL_CONTROLLER_TYPE_PS3: + case SDL_CONTROLLER_TYPE_PS4: + case SDL_CONTROLLER_TYPE_PS5: + buttonLayout = BUTTON_LAYOUT_PLAYSTATION; + break; + + case SDL_CONTROLLER_TYPE_XBOX360: + case SDL_CONTROLLER_TYPE_XBOXONE: + case SDL_CONTROLLER_TYPE_AMAZON_LUNA: + case SDL_CONTROLLER_TYPE_GOOGLE_STADIA: + case SDL_CONTROLLER_TYPE_NVIDIA_SHIELD: + buttonLayout = BUTTON_LAYOUT_XBOX; + break; + + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT: + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT: + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR: + break; + + case SDL_CONTROLLER_TYPE_UNKNOWN: + case SDL_CONTROLLER_TYPE_VIRTUAL: + break; + } + // create the InputConfig - mInputConfigs[joyId] = new InputConfig(joyId, SDL_JoystickName(joy), guid); + mInputConfigs[joyId] = new InputConfig(joyId, SDL_JoystickName(joy), guid, buttonLayout); if(!loadInputConfig(mInputConfigs[joyId])) { 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) int numAxes = SDL_JoystickNumAxes(joy); mPrevAxisValues[joyId] = new int[numAxes]; std::fill(mPrevAxisValues[joyId], mPrevAxisValues[joyId] + numAxes, 0); //initialize array to 0 + + mLastUsedKeyboardOrController = joyId; } void InputManager::removeJoystickByJoystickID(SDL_JoystickID joyId) @@ -172,7 +203,7 @@ void InputManager::deinit() CECInput::deinit(); SDL_JoystickEventState(SDL_DISABLE); - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); } int InputManager::getNumJoysticks() { return (int)mJoysticks.size(); } @@ -198,6 +229,9 @@ int InputManager::getButtonCountByDevice(SDL_JoystickID id) InputConfig* InputManager::getInputConfigByDevice(int device) { + if(device != DEVICE_CEC) + mLastUsedKeyboardOrController = device; + if(device == DEVICE_KEYBOARD) return mKeyboardInputConfig; else if(device == DEVICE_CEC) @@ -206,6 +240,18 @@ InputConfig* InputManager::getInputConfigByDevice(int device) return mInputConfigs[device]; } +InputConfig* InputManager::getInputConfigForLastUsedDevice() const +{ + if(mLastUsedKeyboardOrController == DEVICE_KEYBOARD) + return mKeyboardInputConfig; + + const auto it = mInputConfigs.find(mLastUsedKeyboardOrController); + if(it != mInputConfigs.end()) + return it->second; + + return nullptr; // Could happen if last used controller was unplugged +} + bool InputManager::parseEvent(const SDL_Event& ev, Window* window) { bool causedEvent = false; diff --git a/es-core/src/InputManager.h b/es-core/src/InputManager.h index 388358d217..d7f85b50d4 100644 --- a/es-core/src/InputManager.h +++ b/es-core/src/InputManager.h @@ -26,6 +26,7 @@ class InputManager std::map mInputConfigs; InputConfig* mKeyboardInputConfig; InputConfig* mCECInputConfig; + int mLastUsedKeyboardOrController; std::map mPrevAxisValues; @@ -56,6 +57,7 @@ class InputManager std::string getDeviceGUIDString(int deviceId); InputConfig* getInputConfigByDevice(int deviceId); + InputConfig* getInputConfigForLastUsedDevice() const; bool parseEvent(const SDL_Event& ev, Window* window); }; diff --git a/es-core/src/components/HelpComponent.cpp b/es-core/src/components/HelpComponent.cpp index 1d7ef55291..82dfd56c39 100644 --- a/es-core/src/components/HelpComponent.cpp +++ b/es-core/src/components/HelpComponent.cpp @@ -3,10 +3,10 @@ #include "components/ComponentGrid.h" #include "components/ImageComponent.h" #include "components/TextComponent.h" -#include "resources/TextureResource.h" #include "utils/StringUtil.h" #include "Log.h" #include "Settings.h" +#include "InputManager.h" #define OFFSET_X 12 // move the entire thing right by this amount (px) #define OFFSET_Y 12 // move the entire thing up by this amount (px) @@ -14,7 +14,7 @@ #define ICON_TEXT_SPACING 8 // space between [icon] and [text] (px) #define ENTRY_SPACING 16 // space between [text] and next [icon] (px) -static const std::map ICON_PATH_MAP { +static const HelpComponent::IconPathMap DEFAULT_ICON_PATH_MAP { { "up/down", ":/help/dpad_updown.svg" }, { "left/right", ":/help/dpad_leftright.svg" }, { "up/down/left/right", ":/help/dpad_all.svg" }, @@ -29,6 +29,20 @@ static const std::map ICON_PATH_MAP { { "select", ":/help/button_select.svg" } }; +static const HelpComponent::IconPathMap NO_ICON_OVERRIDES {}; +static const HelpComponent::IconPathMap XBOX_ICON_OVERRIDES { + { "a", ":/help/button_b.svg" }, + { "b", ":/help/button_a.svg" }, + { "x", ":/help/button_y.svg" }, + { "y", ":/help/button_x.svg" }, +}; +static const HelpComponent::IconPathMap PLAYSTATION_ICON_OVERRIDES { + { "a", ":/help/button_circle.svg" }, + { "b", ":/help/button_cross.svg" }, + { "x", ":/help/button_triangle.svg" }, + { "y", ":/help/button_square.svg" }, +}; + HelpComponent::HelpComponent(Window* window) : GuiComponent(window) { } @@ -67,12 +81,15 @@ void HelpComponent::updateGrid() std::vector< std::shared_ptr > icons; std::vector< std::shared_ptr > labels; + const auto& iconOverrides = + getIconOverridesForInput(InputManager::getInstance()->getInputConfigForLastUsedDevice()); + float width = 0; const float height = Math::round(font->getLetterHeight() * 1.25f); for(auto it = mPrompts.cbegin(); it != mPrompts.cend(); it++) { auto icon = std::make_shared(mWindow); - icon->setImage(getIconTexture(it->first.c_str())); + icon->setImage(getIconTexture(it->first, iconOverrides)); icon->setColorShift(mStyle.iconColor); icon->setResize(0, height); icons.push_back(icon); @@ -100,26 +117,51 @@ void HelpComponent::updateGrid() mGrid->setOrigin(mStyle.origin); } -std::shared_ptr HelpComponent::getIconTexture(const char* name) +const HelpComponent::IconPathMap& HelpComponent::getIconOverridesForInput(InputConfig* inputConfig) { - auto it = mIconCache.find(name); - if(it != mIconCache.cend()) - return it->second; + if(!inputConfig) + return NO_ICON_OVERRIDES; - auto pathLookup = ICON_PATH_MAP.find(name); - if(pathLookup == ICON_PATH_MAP.cend()) + switch(inputConfig->getButtonLayout()) { - LOG(LogError) << "Unknown help icon \"" << name << "\"!"; - return nullptr; + case BUTTON_LAYOUT_PLAYSTATION: + return PLAYSTATION_ICON_OVERRIDES; + + case BUTTON_LAYOUT_XBOX: + return XBOX_ICON_OVERRIDES; + + case BUTTON_LAYOUT_DEFAULT: + break; } + + return NO_ICON_OVERRIDES; +} + +std::shared_ptr HelpComponent::getIconTexture(const std::string& name, const IconPathMap& iconOverrides) +{ + auto pathLookup = iconOverrides.find(name); + if(pathLookup == iconOverrides.cend()) + { + pathLookup = DEFAULT_ICON_PATH_MAP.find(name); + if(pathLookup == DEFAULT_ICON_PATH_MAP.cend()) + { + LOG(LogError) << "Unknown help icon \"" << name << "\"!"; + return nullptr; + } + } + + auto it = mIconCache.find(pathLookup->second); + if(it != mIconCache.cend()) + return it->second; + if(!ResourceManager::getInstance()->fileExists(pathLookup->second)) { - LOG(LogError) << "Help icon \"" << name << "\" - corresponding image file \"" << pathLookup->second << "\" misisng!"; + LOG(LogError) << "Help icon \"" << name << "\" - corresponding image file \"" << pathLookup->second << "\" missing!"; return nullptr; } std::shared_ptr tex = TextureResource::get(pathLookup->second); - mIconCache[std::string(name)] = tex; + mIconCache[pathLookup->second] = tex; return tex; } diff --git a/es-core/src/components/HelpComponent.h b/es-core/src/components/HelpComponent.h index e9c5d2e07d..4a65d1e214 100644 --- a/es-core/src/components/HelpComponent.h +++ b/es-core/src/components/HelpComponent.h @@ -4,6 +4,9 @@ #include "GuiComponent.h" #include "HelpStyle.h" +#include "resources/TextureResource.h" + +#include class ComponentGrid; class ImageComponent; @@ -22,8 +25,11 @@ class HelpComponent : public GuiComponent void setStyle(const HelpStyle& style); + using IconPathMap = std::map; + private: - std::shared_ptr getIconTexture(const char* name); + const IconPathMap& getIconOverridesForInput(InputConfig* inputConfig); + std::shared_ptr getIconTexture(const std::string& name, const IconPathMap& iconOverrides); std::map< std::string, std::shared_ptr > mIconCache; std::shared_ptr mGrid;