summaryrefslogtreecommitdiffstats
path: root/xbmc/input/joysticks/keymaps
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/input/joysticks/keymaps/CMakeLists.txt11
-rw-r--r--xbmc/input/joysticks/keymaps/KeyHandler.cpp290
-rw-r--r--xbmc/input/joysticks/keymaps/KeyHandler.h114
-rw-r--r--xbmc/input/joysticks/keymaps/KeymapHandler.cpp278
-rw-r--r--xbmc/input/joysticks/keymaps/KeymapHandler.h108
-rw-r--r--xbmc/input/joysticks/keymaps/KeymapHandling.cpp104
-rw-r--r--xbmc/input/joysticks/keymaps/KeymapHandling.h76
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