diff options
Diffstat (limited to '')
-rw-r--r-- | xbmc/input/joysticks/keymaps/CMakeLists.txt | 11 | ||||
-rw-r--r-- | xbmc/input/joysticks/keymaps/KeyHandler.cpp | 290 | ||||
-rw-r--r-- | xbmc/input/joysticks/keymaps/KeyHandler.h | 114 | ||||
-rw-r--r-- | xbmc/input/joysticks/keymaps/KeymapHandler.cpp | 278 | ||||
-rw-r--r-- | xbmc/input/joysticks/keymaps/KeymapHandler.h | 108 | ||||
-rw-r--r-- | xbmc/input/joysticks/keymaps/KeymapHandling.cpp | 104 | ||||
-rw-r--r-- | xbmc/input/joysticks/keymaps/KeymapHandling.h | 76 |
7 files changed, 981 insertions, 0 deletions
diff --git a/xbmc/input/joysticks/keymaps/CMakeLists.txt b/xbmc/input/joysticks/keymaps/CMakeLists.txt new file mode 100644 index 0000000..c854b36 --- /dev/null +++ b/xbmc/input/joysticks/keymaps/CMakeLists.txt @@ -0,0 +1,11 @@ +set(SOURCES KeyHandler.cpp + KeymapHandler.cpp + KeymapHandling.cpp +) + +set(HEADERS KeyHandler.h + KeymapHandler.h + KeymapHandling.h +) + +core_add_library(input_joystick_keymaps) diff --git a/xbmc/input/joysticks/keymaps/KeyHandler.cpp b/xbmc/input/joysticks/keymaps/KeyHandler.cpp new file mode 100644 index 0000000..cd0f30a --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeyHandler.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "KeyHandler.h" + +#include "input/IKeymap.h" +#include "input/actions/ActionIDs.h" +#include "input/actions/ActionTranslator.h" +#include "input/joysticks/JoystickUtils.h" +#include "input/joysticks/interfaces/IKeymapHandler.h" +#include "interfaces/IActionListener.h" + +#include <algorithm> +#include <assert.h> + +using namespace KODI; +using namespace JOYSTICK; + +#define DIGITAL_ANALOG_THRESHOLD 0.5f + +#define HOLD_TIMEOUT_MS 500 +#define REPEAT_TIMEOUT_MS 50 + +CKeyHandler::CKeyHandler(const std::string& keyName, + IActionListener* actionHandler, + const IKeymap* keymap, + IKeymapHandler* keymapHandler) + : m_keyName(keyName), + m_actionHandler(actionHandler), + m_keymap(keymap), + m_keymapHandler(keymapHandler) +{ + assert(m_actionHandler != nullptr); + assert(m_keymap != nullptr); + assert(m_keymapHandler != nullptr); + + Reset(); +} + +void CKeyHandler::Reset() +{ + m_bHeld = false; + m_magnitude = 0.0f; + m_holdStartTimeMs = 0; + m_lastHoldTimeMs = 0; + m_bActionSent = false; + m_lastActionMs = 0; + m_activeWindowId = -1; + m_lastAction = CAction(); +} + +bool CKeyHandler::OnDigitalMotion(bool bPressed, unsigned int holdTimeMs) +{ + return OnAnalogMotion(bPressed ? 1.0f : 0.0f, holdTimeMs); +} + +bool CKeyHandler::OnAnalogMotion(float magnitude, unsigned int motionTimeMs) +{ + // Don't send deactivation event more than once + if (m_magnitude == 0.0f && magnitude == 0.0f) + return false; + + // Get actions for the key + const auto& actionGroup = m_keymap->GetActions(m_keyName); + const int windowId = actionGroup.windowId; + const auto& actions = actionGroup.actions; + + // Calculate press state + const bool bPressed = IsPressed(magnitude); + const bool bJustPressed = bPressed && !m_bHeld; + + if (bJustPressed) + { + // Reset key if just pressed + Reset(); + + // Record hold start time if just pressed + m_holdStartTimeMs = motionTimeMs; + + // Record window ID + if (windowId >= 0) + m_activeWindowId = windowId; + } + + // Calculate holdtime relative to when magnitude crossed the threshold + unsigned int holdTimeMs = 0; + if (bPressed) + holdTimeMs = motionTimeMs - m_holdStartTimeMs; + + // Give priority to actions with hotkeys + std::vector<const KeymapAction*> actionsWithHotkeys; + + for (const auto& action : actions) + { + if (!action.hotkeys.empty()) + actionsWithHotkeys.emplace_back(&action); + } + + CAction dispatchAction = + ProcessActions(std::move(actionsWithHotkeys), windowId, magnitude, holdTimeMs); + + // If that failed, try again with all actions + if (dispatchAction.GetID() == ACTION_NONE) + { + std::vector<const KeymapAction*> allActions; + + allActions.reserve(actions.size()); + for (const auto& action : actions) + allActions.emplace_back(&action); + + dispatchAction = ProcessActions(std::move(allActions), windowId, magnitude, holdTimeMs); + } + + // If specific action was sent last frame but not this one, send a release event + if (dispatchAction.GetID() != m_lastAction.GetID()) + { + if (CActionTranslator::IsAnalog(m_lastAction.GetID()) && m_lastAction.GetAmount() > 0.0f) + { + m_lastAction.ClearAmount(); + m_actionHandler->OnAction(m_lastAction); + } + } + + // Dispatch action + bool bHandled = false; + if (dispatchAction.GetID() != ACTION_NONE) + { + m_actionHandler->OnAction(dispatchAction); + bHandled = true; + } + + m_bHeld = bPressed; + m_magnitude = magnitude; + m_lastHoldTimeMs = holdTimeMs; + m_lastAction = dispatchAction; + + return bHandled; +} + +CAction CKeyHandler::ProcessActions(std::vector<const KeymapAction*> actions, + int windowId, + float magnitude, + unsigned int holdTimeMs) +{ + CAction dispatchAction; + + // Filter out actions without pressed hotkeys + actions.erase(std::remove_if(actions.begin(), actions.end(), + [this](const KeymapAction* action) { + return !m_keymapHandler->HotkeysPressed(action->hotkeys); + }), + actions.end()); + + if (actions.empty()) + return false; + + // Actions are sorted by holdtime, so the final action is the one with the + // greatest holdtime + const KeymapAction& finalAction = **actions.rbegin(); + const unsigned int maxHoldTimeMs = finalAction.holdTimeMs; + + const bool bHasDelay = (maxHoldTimeMs > 0); + if (!bHasDelay) + { + dispatchAction = ProcessAction(finalAction, windowId, magnitude, holdTimeMs); + } + else + { + // If holdtime has exceeded the last action, execute it now + if (holdTimeMs >= finalAction.holdTimeMs) + { + // Force holdtime to zero for the initial press + if (!m_bActionSent) + holdTimeMs = 0; + else + holdTimeMs -= finalAction.holdTimeMs; + + dispatchAction = ProcessAction(finalAction, windowId, magnitude, holdTimeMs); + } + else + { + // Calculate press state + const bool bPressed = IsPressed(magnitude); + const bool bJustReleased = m_bHeld && !bPressed; + + // If button was just released, send a release action + if (bJustReleased) + dispatchAction = ProcessRelease(actions, windowId); + } + } + + return dispatchAction; +} + +CAction CKeyHandler::ProcessRelease(std::vector<const KeymapAction*> actions, int windowId) +{ + CAction dispatchAction; + + // Use previous holdtime from before button release + const unsigned int holdTimeMs = m_lastHoldTimeMs; + + // Send an action on release if one occurs before the holdtime + for (auto it = actions.begin(); it != actions.end();) + { + const KeymapAction& action = **it; + + unsigned int thisHoldTime = (*it)->holdTimeMs; + + ++it; + if (it == actions.end()) + break; + + unsigned int nextHoldTime = (*it)->holdTimeMs; + + if (thisHoldTime <= holdTimeMs && holdTimeMs < nextHoldTime) + { + dispatchAction = ProcessAction(action, windowId, 1.0f, 0); + break; + } + } + + return dispatchAction; +} + +CAction CKeyHandler::ProcessAction(const KeymapAction& action, + int windowId, + float magnitude, + unsigned int holdTimeMs) +{ + CAction dispatchAction; + + bool bSendAction = false; + + if (windowId != m_activeWindowId) + { + // Don't send actions if the window has changed since being pressed + } + else if (CActionTranslator::IsAnalog(action.actionId)) + { + bSendAction = true; + } + else if (IsPressed(magnitude)) + { + // Dispatch action if button was pressed this frame + if (holdTimeMs == 0) + bSendAction = true; + else + bSendAction = SendRepeatAction(holdTimeMs); + } + + if (bSendAction) + { + const CAction guiAction(action.actionId, magnitude, 0.0f, action.actionString, holdTimeMs); + m_keymapHandler->OnPress(m_keyName); + m_bActionSent = true; + m_lastActionMs = holdTimeMs; + dispatchAction = guiAction; + } + + return dispatchAction; +} + +bool CKeyHandler::SendRepeatAction(unsigned int holdTimeMs) +{ + bool bSendRepeat = true; + + // Don't send a repeat action if the last key has changed + if (m_keymapHandler->GetLastPressed() != m_keyName) + bSendRepeat = false; + + // Ensure initial timeout has elapsed + else if (holdTimeMs < HOLD_TIMEOUT_MS) + bSendRepeat = false; + + // Ensure repeat timeout has elapsed + else if (holdTimeMs < m_lastActionMs + REPEAT_TIMEOUT_MS) + bSendRepeat = false; + + return bSendRepeat; +} + +bool CKeyHandler::IsPressed(float magnitude) +{ + return magnitude >= DIGITAL_ANALOG_THRESHOLD; +} diff --git a/xbmc/input/joysticks/keymaps/KeyHandler.h b/xbmc/input/joysticks/keymaps/KeyHandler.h new file mode 100644 index 0000000..3b5a46c --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeyHandler.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "input/actions/Action.h" +#include "input/joysticks/JoystickTypes.h" +#include "input/joysticks/interfaces/IKeyHandler.h" + +#include <map> +#include <string> +#include <vector> + +class CAction; +class IActionListener; +class IKeymap; + +namespace KODI +{ +namespace JOYSTICK +{ +class IKeymapHandler; + +/*! + * \ingroup joystick + * \brief + */ +class CKeyHandler : public IKeyHandler +{ +public: + CKeyHandler(const std::string& keyName, + IActionListener* actionHandler, + const IKeymap* keymap, + IKeymapHandler* keymapHandler); + + ~CKeyHandler() override = default; + + // implementation of IKeyHandler + bool IsPressed() const override { return m_bHeld; } + bool OnDigitalMotion(bool bPressed, unsigned int holdTimeMs) override; + bool OnAnalogMotion(float magnitude, unsigned int motionTimeMs) override; + +private: + void Reset(); + + /*! + * \brief Process actions to see if an action should be dispatched + * + * \param actions All actions from the keymap defined for the current window + * \param windowId The current window ID + * \param magnitude The magnitude or distance of the feature being handled + * \param holdTimeMs The time which the feature has been past the hold threshold + * + * \return The action to dispatch, or action with ID ACTION_NONE if no action should be dispatched + */ + CAction ProcessActions(std::vector<const KeymapAction*> actions, + int windowId, + float magnitude, + unsigned int holdTimeMs); + + /*! + * \brief Process actions after release event to see if an action should be dispatched + * + * \param actions All actions from the keymap defined for the current window + * \param windowId The current window ID + * + * \return The action to dispatch, or action with ID ACTION_NONE if no action should be dispatched + */ + CAction ProcessRelease(std::vector<const KeymapAction*> actions, int windowId); + + /*! + * \brief Process an action to see if it should be dispatched + * + * \param action The action chosen to be dispatched + * \param windowId The current window ID + * \param magnitude The magnitude or distance of the feature being handled + * \param holdTimeMs The time which the feature has been past the hold threshold + * + * \return The action to dispatch, or action with ID ACTION_NONE if no action should be dispatched + */ + CAction ProcessAction(const KeymapAction& action, + int windowId, + float magnitude, + unsigned int holdTimeMs); + + // Check criteria for sending a repeat action + bool SendRepeatAction(unsigned int holdTimeMs); + + // Helper function + static bool IsPressed(float magnitude); + + // Construction parameters + const std::string m_keyName; + IActionListener* const m_actionHandler; + const IKeymap* const m_keymap; + IKeymapHandler* const m_keymapHandler; + + // State variables + bool m_bHeld; + float m_magnitude; + unsigned int m_holdStartTimeMs; + unsigned int m_lastHoldTimeMs; + bool m_bActionSent; + unsigned int m_lastActionMs; + int m_activeWindowId = -1; // Window that activated the key + CAction m_lastAction; +}; +} // namespace JOYSTICK +} // namespace KODI diff --git a/xbmc/input/joysticks/keymaps/KeymapHandler.cpp b/xbmc/input/joysticks/keymaps/KeymapHandler.cpp new file mode 100644 index 0000000..24e2a0b --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeymapHandler.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "KeymapHandler.h" + +#include "KeyHandler.h" +#include "games/controllers/Controller.h" +#include "input/IKeymap.h" +#include "input/IKeymapEnvironment.h" +#include "input/InputTranslator.h" +#include "input/joysticks/JoystickEasterEgg.h" +#include "input/joysticks/JoystickTranslator.h" +#include "input/joysticks/JoystickUtils.h" +#include "input/joysticks/interfaces/IKeyHandler.h" + +#include <algorithm> +#include <assert.h> +#include <cmath> +#include <utility> + +using namespace KODI; +using namespace JOYSTICK; + +CKeymapHandler::CKeymapHandler(IActionListener* actionHandler, const IKeymap* keymap) + : m_actionHandler(actionHandler), m_keymap(keymap) +{ + assert(m_actionHandler != nullptr); + assert(m_keymap != nullptr); + + if (m_keymap->Environment()->UseEasterEgg()) + m_easterEgg.reset(new CJoystickEasterEgg(ControllerID())); +} + +bool CKeymapHandler::HotkeysPressed(const std::set<std::string>& keyNames) const +{ + bool bHotkeysPressed = true; + + for (const auto& hotkey : keyNames) + { + auto it = m_keyHandlers.find(hotkey); + if (it == m_keyHandlers.end() || !it->second->IsPressed()) + { + bHotkeysPressed = false; + break; + } + } + + return bHotkeysPressed; +} + +std::string CKeymapHandler::ControllerID() const +{ + return m_keymap->ControllerID(); +} + +bool CKeymapHandler::AcceptsInput(const FeatureName& feature) const +{ + if (HasAction(CJoystickUtils::MakeKeyName(feature))) + return true; + + for (auto dir : CJoystickUtils::GetAnalogStickDirections()) + { + if (HasAction(CJoystickUtils::MakeKeyName(feature, dir))) + return true; + } + + return false; +} + +bool CKeymapHandler::OnButtonPress(const FeatureName& feature, bool bPressed) +{ + if (bPressed && m_easterEgg && m_easterEgg->OnButtonPress(feature)) + return true; + + const std::string keyName = CJoystickUtils::MakeKeyName(feature); + + IKeyHandler* handler = GetKeyHandler(keyName); + return handler->OnDigitalMotion(bPressed, 0); +} + +void CKeymapHandler::OnButtonHold(const FeatureName& feature, unsigned int holdTimeMs) +{ + if (m_easterEgg && m_easterEgg->IsCapturing()) + return; + + const std::string keyName = CJoystickUtils::MakeKeyName(feature); + + IKeyHandler* handler = GetKeyHandler(keyName); + handler->OnDigitalMotion(true, holdTimeMs); +} + +bool CKeymapHandler::OnButtonMotion(const FeatureName& feature, + float magnitude, + unsigned int motionTimeMs) +{ + const std::string keyName = CJoystickUtils::MakeKeyName(feature); + + IKeyHandler* handler = GetKeyHandler(keyName); + return handler->OnAnalogMotion(magnitude, motionTimeMs); +} + +bool CKeymapHandler::OnAnalogStickMotion(const FeatureName& feature, + float x, + float y, + unsigned int motionTimeMs) +{ + using namespace INPUT; + + bool bHandled = false; + + // Calculate the direction of the stick's position + const ANALOG_STICK_DIRECTION analogStickDir = CInputTranslator::VectorToCardinalDirection(x, y); + + // Calculate the magnitude projected onto that direction + const float magnitude = std::max(std::fabs(x), std::fabs(y)); + + // Deactivate directions in which the stick is not pointing first + for (auto dir : CJoystickUtils::GetAnalogStickDirections()) + { + if (dir != analogStickDir) + DeactivateDirection(feature, dir); + } + + // Now activate direction the analog stick is pointing + if (analogStickDir != ANALOG_STICK_DIRECTION::NONE) + bHandled = ActivateDirection(feature, magnitude, analogStickDir, motionTimeMs); + + return bHandled; +} + +bool CKeymapHandler::OnWheelMotion(const FeatureName& feature, + float position, + unsigned int motionTimeMs) +{ + bool bHandled = false; + + // Calculate the direction of the wheel's position + const WHEEL_DIRECTION direction = CJoystickTranslator::PositionToWheelDirection(position); + + // Calculate the magnitude projected onto that direction + const float magnitude = std::fabs(position); + + // Deactivate directions in which the wheel is not pointing first + for (auto dir : CJoystickUtils::GetWheelDirections()) + { + if (dir != direction) + DeactivateDirection(feature, dir); + } + + // Now activate direction in which the wheel is positioned + if (direction != WHEEL_DIRECTION::NONE) + bHandled = ActivateDirection(feature, magnitude, direction, motionTimeMs); + + return bHandled; +} + +bool CKeymapHandler::OnThrottleMotion(const FeatureName& feature, + float position, + unsigned int motionTimeMs) +{ + bool bHandled = false; + + // Calculate the direction of the throttle's position + const THROTTLE_DIRECTION direction = CJoystickTranslator::PositionToThrottleDirection(position); + + // Calculate the magnitude projected onto that direction + const float magnitude = std::fabs(position); + + // Deactivate directions in which the throttle is not pointing first + for (auto dir : CJoystickUtils::GetThrottleDirections()) + { + if (dir != direction) + DeactivateDirection(feature, dir); + } + + // Now activate direction in which the throttle is positioned + if (direction != THROTTLE_DIRECTION::NONE) + bHandled = ActivateDirection(feature, magnitude, direction, motionTimeMs); + + return bHandled; +} + +bool CKeymapHandler::OnAccelerometerMotion(const FeatureName& feature, float x, float y, float z) +{ + return false; //! @todo implement +} + +bool CKeymapHandler::ActivateDirection(const FeatureName& feature, + float magnitude, + ANALOG_STICK_DIRECTION dir, + unsigned int motionTimeMs) +{ + const std::string keyName = CJoystickUtils::MakeKeyName(feature, dir); + + IKeyHandler* handler = GetKeyHandler(keyName); + return handler->OnAnalogMotion(magnitude, motionTimeMs); +} + +void CKeymapHandler::DeactivateDirection(const FeatureName& feature, ANALOG_STICK_DIRECTION dir) +{ + const std::string keyName = CJoystickUtils::MakeKeyName(feature, dir); + + IKeyHandler* handler = GetKeyHandler(keyName); + handler->OnAnalogMotion(0.0f, 0); +} + +bool CKeymapHandler::ActivateDirection(const FeatureName& feature, + float magnitude, + WHEEL_DIRECTION dir, + unsigned int motionTimeMs) +{ + const std::string keyName = CJoystickUtils::MakeKeyName(feature, dir); + + IKeyHandler* handler = GetKeyHandler(keyName); + return handler->OnAnalogMotion(magnitude, motionTimeMs); +} + +void CKeymapHandler::DeactivateDirection(const FeatureName& feature, WHEEL_DIRECTION dir) +{ + const std::string keyName = CJoystickUtils::MakeKeyName(feature, dir); + + IKeyHandler* handler = GetKeyHandler(keyName); + handler->OnAnalogMotion(0.0f, 0); +} + +bool CKeymapHandler::ActivateDirection(const FeatureName& feature, + float magnitude, + THROTTLE_DIRECTION dir, + unsigned int motionTimeMs) +{ + const std::string keyName = CJoystickUtils::MakeKeyName(feature, dir); + + IKeyHandler* handler = GetKeyHandler(keyName); + return handler->OnAnalogMotion(magnitude, motionTimeMs); +} + +void CKeymapHandler::DeactivateDirection(const FeatureName& feature, THROTTLE_DIRECTION dir) +{ + const std::string keyName = CJoystickUtils::MakeKeyName(feature, dir); + + IKeyHandler* handler = GetKeyHandler(keyName); + handler->OnAnalogMotion(0.0f, 0); +} + +IKeyHandler* CKeymapHandler::GetKeyHandler(const std::string& keyName) +{ + auto it = m_keyHandlers.find(keyName); + if (it == m_keyHandlers.end()) + { + std::unique_ptr<IKeyHandler> handler(new CKeyHandler(keyName, m_actionHandler, m_keymap, this)); + m_keyHandlers.insert(std::make_pair(keyName, std::move(handler))); + it = m_keyHandlers.find(keyName); + } + + return it->second.get(); +} + +bool CKeymapHandler::HasAction(const std::string& keyName) const +{ + bool bHasAction = false; + + const auto& actions = m_keymap->GetActions(keyName).actions; + for (const auto& action : actions) + { + if (HotkeysPressed(action.hotkeys)) + { + bHasAction = true; + break; + } + } + + return bHasAction; +} diff --git a/xbmc/input/joysticks/keymaps/KeymapHandler.h b/xbmc/input/joysticks/keymaps/KeymapHandler.h new file mode 100644 index 0000000..a2b1d4f --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeymapHandler.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "input/joysticks/JoystickTypes.h" +#include "input/joysticks/interfaces/IButtonSequence.h" +#include "input/joysticks/interfaces/IInputHandler.h" +#include "input/joysticks/interfaces/IKeymapHandler.h" + +#include <map> +#include <memory> +#include <string> + +class IActionListener; +class IKeymap; + +namespace KODI +{ +namespace JOYSTICK +{ +class IKeyHandler; + +/*! + * \ingroup joystick + * \brief + */ +class CKeymapHandler : public IKeymapHandler, public IInputHandler +{ +public: + CKeymapHandler(IActionListener* actionHandler, const IKeymap* keymap); + + ~CKeymapHandler() override = default; + + // implementation of IKeymapHandler + bool HotkeysPressed(const std::set<std::string>& keyNames) const override; + std::string GetLastPressed() const override { return m_lastPressed; } + void OnPress(const std::string& keyName) override { m_lastPressed = keyName; } + + // implementation of IInputHandler + std::string ControllerID() const override; + bool HasFeature(const FeatureName& feature) const override { return true; } + bool AcceptsInput(const FeatureName& feature) const override; + bool OnButtonPress(const FeatureName& feature, bool bPressed) override; + void OnButtonHold(const FeatureName& feature, unsigned int holdTimeMs) override; + bool OnButtonMotion(const FeatureName& feature, + float magnitude, + unsigned int motionTimeMs) override; + bool OnAnalogStickMotion(const FeatureName& feature, + float x, + float y, + unsigned int motionTimeMs) override; + bool OnAccelerometerMotion(const FeatureName& feature, float x, float y, float z) override; + bool OnWheelMotion(const FeatureName& feature, + float position, + unsigned int motionTimeMs) override; + bool OnThrottleMotion(const FeatureName& feature, + float position, + unsigned int motionTimeMs) override; + void OnInputFrame() override {} + +protected: + // Keep track of cheat code presses + std::unique_ptr<IButtonSequence> m_easterEgg; + +private: + // Analog stick helper functions + bool ActivateDirection(const FeatureName& feature, + float magnitude, + ANALOG_STICK_DIRECTION dir, + unsigned int motionTimeMs); + void DeactivateDirection(const FeatureName& feature, ANALOG_STICK_DIRECTION dir); + + // Wheel helper functions + bool ActivateDirection(const FeatureName& feature, + float magnitude, + WHEEL_DIRECTION dir, + unsigned int motionTimeMs); + void DeactivateDirection(const FeatureName& feature, WHEEL_DIRECTION dir); + + // Throttle helper functions + bool ActivateDirection(const FeatureName& feature, + float magnitude, + THROTTLE_DIRECTION dir, + unsigned int motionTimeMs); + void DeactivateDirection(const FeatureName& feature, THROTTLE_DIRECTION dir); + + // Helper functions + IKeyHandler* GetKeyHandler(const std::string& keyName); + bool HasAction(const std::string& keyName) const; + + // Construction parameters + IActionListener* const m_actionHandler; + const IKeymap* const m_keymap; + + // Handlers for individual keys + std::map<std::string, std::unique_ptr<IKeyHandler>> m_keyHandlers; // Key name -> handler + + // Last pressed key + std::string m_lastPressed; +}; +} // namespace JOYSTICK +} // namespace KODI diff --git a/xbmc/input/joysticks/keymaps/KeymapHandling.cpp b/xbmc/input/joysticks/keymaps/KeymapHandling.cpp new file mode 100644 index 0000000..b87ea68 --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeymapHandling.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "KeymapHandling.h" + +#include "KeymapHandler.h" +#include "ServiceBroker.h" +#include "input/ButtonTranslator.h" +#include "input/InputManager.h" +#include "input/Keymap.h" +#include "input/joysticks/interfaces/IInputHandler.h" +#include "input/joysticks/interfaces/IInputProvider.h" + +#include <algorithm> +#include <utility> + +using namespace KODI; +using namespace JOYSTICK; + +CKeymapHandling::CKeymapHandling(IInputProvider* inputProvider, + bool pPromiscuous, + const IKeymapEnvironment* environment) + : m_inputProvider(inputProvider), m_pPromiscuous(pPromiscuous), m_environment(environment) +{ + LoadKeymaps(); + CServiceBroker::GetInputManager().RegisterObserver(this); +} + +CKeymapHandling::~CKeymapHandling() +{ + CServiceBroker::GetInputManager().UnregisterObserver(this); + UnloadKeymaps(); +} + +IInputReceiver* CKeymapHandling::GetInputReceiver(const std::string& controllerId) const +{ + auto it = std::find_if(m_inputHandlers.begin(), m_inputHandlers.end(), + [&controllerId](const std::unique_ptr<IInputHandler>& inputHandler) { + return inputHandler->ControllerID() == controllerId; + }); + + if (it != m_inputHandlers.end()) + return (*it)->InputReceiver(); + + return nullptr; +} + +IKeymap* CKeymapHandling::GetKeymap(const std::string& controllerId) const +{ + auto it = std::find_if(m_keymaps.begin(), m_keymaps.end(), + [&controllerId](const std::unique_ptr<IKeymap>& keymap) { + return keymap->ControllerID() == controllerId; + }); + + if (it != m_keymaps.end()) + return it->get(); + + return nullptr; +} + +void CKeymapHandling::Notify(const Observable& obs, const ObservableMessage msg) +{ + if (msg == ObservableMessageButtonMapsChanged) + LoadKeymaps(); +} + +void CKeymapHandling::LoadKeymaps() +{ + UnloadKeymaps(); + + auto& inputManager = CServiceBroker::GetInputManager(); + + for (auto& windowKeymap : inputManager.GetJoystickKeymaps()) + { + // Create keymap + std::unique_ptr<IKeymap> keymap(new CKeymap(std::move(windowKeymap), m_environment)); + + // Create keymap handler + std::unique_ptr<IInputHandler> inputHandler(new CKeymapHandler(&inputManager, keymap.get())); + + // Register the handler with the input provider + m_inputProvider->RegisterInputHandler(inputHandler.get(), m_pPromiscuous); + + // Save the keymap and handler + m_keymaps.emplace_back(std::move(keymap)); + m_inputHandlers.emplace_back(std::move(inputHandler)); + } +} + +void CKeymapHandling::UnloadKeymaps() +{ + if (m_inputProvider != nullptr) + { + for (auto it = m_inputHandlers.rbegin(); it != m_inputHandlers.rend(); ++it) + m_inputProvider->UnregisterInputHandler(it->get()); + } + m_inputHandlers.clear(); + m_keymaps.clear(); +} diff --git a/xbmc/input/joysticks/keymaps/KeymapHandling.h b/xbmc/input/joysticks/keymaps/KeymapHandling.h new file mode 100644 index 0000000..fdc64b7 --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeymapHandling.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "utils/Observer.h" + +#include <memory> +#include <string> +#include <vector> + +class IKeymap; +class IKeymapEnvironment; + +namespace KODI +{ +namespace JOYSTICK +{ +class IInputHandler; +class IInputProvider; +class IInputReceiver; + +/*! + * \ingroup joystick + * \brief + */ +class CKeymapHandling : public Observer +{ +public: + CKeymapHandling(IInputProvider* inputProvider, + bool pPromiscuous, + const IKeymapEnvironment* environment); + + ~CKeymapHandling() override; + + /*! + * \brief Unregister the input provider + * + * Call this if the input provider is invalidated, such as if a user + * disconnects a controller. This prevents accessing the invalidated + * input provider when keymaps are unloaded upon destruction. + */ + void UnregisterInputProvider() { m_inputProvider = nullptr; } + + /*! + * \brief + */ + IInputReceiver* GetInputReceiver(const std::string& controllerId) const; + + /*! + * \brief + */ + IKeymap* GetKeymap(const std::string& controllerId) const; + + // implementation of Observer + void Notify(const Observable& obs, const ObservableMessage msg) override; + +private: + void LoadKeymaps(); + void UnloadKeymaps(); + + // Construction parameter + IInputProvider* m_inputProvider; + const bool m_pPromiscuous; + const IKeymapEnvironment* const m_environment; + + std::vector<std::unique_ptr<IKeymap>> m_keymaps; + std::vector<std::unique_ptr<IInputHandler>> m_inputHandlers; +}; +} // namespace JOYSTICK +} // namespace KODI |