diff --git a/cmake/treedata/common/subdirs.txt b/cmake/treedata/common/subdirs.txt index 6c073ac9a805a..ee4e83c7b126a 100644 --- a/cmake/treedata/common/subdirs.txt +++ b/cmake/treedata/common/subdirs.txt @@ -16,6 +16,7 @@ xbmc/input input xbmc/input/joysticks input/joysticks xbmc/input/joysticks/dialogs input/joysticks/dialogs xbmc/input/joysticks/generic input/joysticks/generic +xbmc/input/joysticks/keymaps input/joysticks/keymaps xbmc/input/keyboard input/keyboard xbmc/input/keyboard/generic input/keyboard/generic xbmc/input/mouse input/mouse diff --git a/system/keymaps/joystick.xml b/system/keymaps/joystick.xml index 48a7d463783cf..f28bae0f60259 100644 --- a/system/keymaps/joystick.xml +++ b/system/keymaps/joystick.xml @@ -1,34 +1,54 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Select + ContextMenu Back ContextMenu FullScreen ActivateWindow(PlayerControls) - PreviousMenu ActivateWindow(Home) Up Down @@ -40,14 +60,14 @@ ScrollDown ScrollUp ScrollDown - Left - Right - Up - Down - VolumeDown - VolumeUp - VolumeUp - VolumeDown + Left + Right + Up + Down + VolumeDown + VolumeUp + VolumeUp + VolumeDown @@ -69,8 +89,8 @@ OSD FullScreen Info - Seek(-7) - ActivateWindow(Home) + OSD + OSD ChapterOrBigStepForward ChapterOrBigStepBack StepForward @@ -81,25 +101,63 @@ AnalogFastForward AnalogRewind AnalogFastForward - AnalogSeekBack - AnalogSeekForward - AnalogSeekForward - AnalogSeekBack - VolumeDown - VolumeUp - VolumeUp - VolumeDown + AnalogSeekBack + AnalogSeekForward + noop + noop OSD - OSD - OSD + OSD + + VolumeDown + VolumeUp + VolumeUp + VolumeDown + + + + + + Screenshot + PlayerControl(Reset) + OSD + FullScreen + Stop + Save + Load + AnalogFastForward + AnalogRewind + SaveSlotIncrease + SaveSlotDecrease + SaveSlotDecrease + SaveSlotIncrease + OSD ChannelUp ChannelDown StepBack @@ -108,6 +166,7 @@ + OSD ChannelUp ChannelDown StepBack @@ -148,8 +207,10 @@ AnalogFastForward AnalogRewind AnalogFastForward - PreviousPreset - NextPreset + PreviousPreset + NextPreset + noop + noop @@ -174,10 +235,10 @@ ZoomOut PreviousPicture NextPicture - AnalogMoveX - AnalogMoveX - AnalogMoveY - AnalogMoveY + AnalogMoveX + AnalogMoveX + AnalogMoveY + AnalogMoveY ZoomOut ZoomIn @@ -235,6 +296,7 @@ + Shift BackSpace Symbols Shift diff --git a/xbmc/AppParamParser.cpp b/xbmc/AppParamParser.cpp index 7e4d42e308c41..0bf3a93a16267 100644 --- a/xbmc/AppParamParser.cpp +++ b/xbmc/AppParamParser.cpp @@ -56,13 +56,13 @@ void CAppParamParser::Parse(const char* const* argv, int nArgs) { if ((argv[next][0] != '-') && (argv[next][0] == '/')) { - CInputManager::GetInstance().SetRemoteControlName(argv[next]); + CServiceBroker::GetInputManager().SetRemoteControlName(argv[next]); i++; } } } else if (strnicmp(argv[i], "-n", 2) == 0 || strnicmp(argv[i], "--nolirc", 8) == 0) - CInputManager::GetInstance().DisableRemoteControl(); + CServiceBroker::GetInputManager().DisableRemoteControl(); if (stricmp(argv[i], "-d") == 0) { diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 652babf6ea39f..7bc8ceeb12001 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -94,6 +94,7 @@ #include "utils/CPUInfo.h" #include "utils/log.h" #include "utils/SeekHandler.h" +#include "ServiceBroker.h" #include "input/KeyboardLayoutManager.h" @@ -350,7 +351,7 @@ bool CApplication::OnEvent(XBMC_Event& newEvent) g_application.OnAction(CAction(ACTION_MOUSE_MOVE, 0, static_cast(newEvent.focus.x), static_cast(newEvent.focus.y), 0, 0)); break; default: - return CInputManager::GetInstance().OnEvent(newEvent); + return CServiceBroker::GetInputManager().OnEvent(newEvent); } return true; } @@ -644,9 +645,6 @@ bool CApplication::Create() m_replayGainSettings.iNoGainPreAmp = m_ServiceManager->GetSettings().GetInt(CSettings::SETTING_MUSICPLAYER_REPLAYGAINNOGAINPREAMP); m_replayGainSettings.bAvoidClipping = m_ServiceManager->GetSettings().GetBool(CSettings::SETTING_MUSICPLAYER_REPLAYGAINAVOIDCLIPPING); - // Create the Mouse, Keyboard and Remote - CInputManager::GetInstance().InitializeInputs(); - // load the keyboard layouts if (!CKeyboardLayoutManager::GetInstance().Load()) { @@ -736,7 +734,7 @@ bool CApplication::CreateGUI() // The key mappings may already have been loaded by a peripheral CLog::Log(LOGINFO, "load keymapping"); - if (!CButtonTranslator::GetInstance().Load()) + if (!CServiceBroker::GetInputManager().LoadKeymaps()) return false; RESOLUTION_INFO info = g_graphicsContext.GetResInfo(); @@ -1923,7 +1921,7 @@ bool CApplication::OnAppCommand(const CAction &action) uint32_t appcmd = action.GetID(); CKey key(appcmd | KEY_APPCOMMAND, (unsigned int) 0); int iWin = g_windowManager.GetActiveWindow() & WINDOW_ID_MASK; - CAction appcmdaction = CButtonTranslator::GetInstance().GetAction(iWin, key); + CAction appcmdaction = CServiceBroker::GetInputManager().GetAction(iWin, key); // If we couldn't find an action return false to indicate we have not // handled this appcommand @@ -1962,8 +1960,7 @@ bool CApplication::OnAction(const CAction &action) } if (action.IsMouse()) - CInputManager::GetInstance().SetMouseActive(true); - + CServiceBroker::GetInputManager().SetMouseActive(true); if (action.GetID() == ACTION_CREATE_EPISODE_BOOKMARK) { @@ -2023,10 +2020,7 @@ bool CApplication::OnAction(const CAction &action) // reload keymaps if (action.GetID() == ACTION_RELOAD_KEYMAPS) - { - CButtonTranslator::GetInstance().Clear(); - CButtonTranslator::GetInstance().Load(); - } + CServiceBroker::GetInputManager().ReloadKeymaps(); // show info : Shows the current video or song information if (action.GetID() == ACTION_SHOW_INFO) @@ -2671,7 +2665,7 @@ void CApplication::FrameMove(bool processEvents, bool processGUI) } CWinEvents::MessagePump(); - CInputManager::GetInstance().Process(g_windowManager.GetActiveWindowID(), frameTime); + CServiceBroker::GetInputManager().Process(g_windowManager.GetActiveWindowID(), frameTime); if (processGUI && m_renderGUI) { @@ -2765,7 +2759,7 @@ bool CApplication::Cleanup() g_LangCodeExpander.Clear(); g_charsetConverter.clear(); g_directoryCache.Clear(); - CButtonTranslator::GetInstance().Clear(); + //CServiceBroker::GetInputManager().ClearKeymaps(); //! @todo #ifdef HAS_EVENT_SERVER CEventServer::RemoveInstance(); #endif @@ -2905,7 +2899,7 @@ void CApplication::Stop(int exitCode) m_ServiceManager->DestroyAudioEngine(); CLog::Log(LOGNOTICE, "closing down remote control service"); - CInputManager::GetInstance().DisableRemoteControl(); + CServiceBroker::GetInputManager().DisableRemoteControl(); // unregister ffmpeg lock manager call back av_lockmgr_register(NULL); diff --git a/xbmc/ServiceBroker.cpp b/xbmc/ServiceBroker.cpp index 811cf964fd716..ce7dd82685b99 100644 --- a/xbmc/ServiceBroker.cpp +++ b/xbmc/ServiceBroker.cpp @@ -105,6 +105,11 @@ ADDON::CServiceAddonManager& CServiceBroker::GetServiceAddons() return g_application.m_ServiceManager->GetServiceAddons(); } +CInputManager& CServiceBroker::GetInputManager() +{ + return g_application.m_ServiceManager->GetInputManager(); +} + bool CServiceBroker::IsBinaryAddonCacheUp() { return g_application.m_ServiceManager->init_level > 1; diff --git a/xbmc/ServiceBroker.h b/xbmc/ServiceBroker.h index b24154218b00b..f05bf1309328d 100644 --- a/xbmc/ServiceBroker.h +++ b/xbmc/ServiceBroker.h @@ -53,6 +53,7 @@ class CDataCacheCore; class CSettings; class IAE; class CFavouritesService; +class CInputManager; namespace KODI { @@ -86,5 +87,6 @@ class CServiceBroker static PERIPHERALS::CPeripherals& GetPeripherals(); static CFavouritesService& GetFavouritesService(); static ADDON::CServiceAddonManager& GetServiceAddons(); + static CInputManager& GetInputManager(); static bool IsBinaryAddonCacheUp(); }; diff --git a/xbmc/ServiceManager.cpp b/xbmc/ServiceManager.cpp index c97495593b2fa..47b25601f7664 100644 --- a/xbmc/ServiceManager.cpp +++ b/xbmc/ServiceManager.cpp @@ -31,6 +31,7 @@ #include "PlayListPlayer.h" #include "profiles/ProfilesManager.h" #include "utils/log.h" +#include "input/InputManager.h" #include "interfaces/AnnouncementManager.h" #include "interfaces/generic/ScriptInvocationManager.h" #include "interfaces/python/XBPython.h" @@ -41,7 +42,8 @@ using namespace KODI; CServiceManager::CServiceManager() : m_gameServices(new GAME::CGameServices), - m_peripherals(new PERIPHERALS::CPeripherals) + m_peripherals(new PERIPHERALS::CPeripherals), + m_inputManager(new CInputManager) { } @@ -100,6 +102,8 @@ bool CServiceManager::Init2() m_contextMenuManager.reset(new CContextMenuManager(*m_addonMgr.get())); m_serviceAddons.reset(new ADDON::CServiceAddonManager(*m_addonMgr)); + m_inputManager->InitializeInputs(); + init_level = 2; return true; } @@ -149,6 +153,7 @@ void CServiceManager::Deinit() m_serviceAddons.reset(); m_gameServices->Deinit(); m_peripherals.reset(); + //m_inputManager->Deinitialize(); //! @todo m_contextMenuManager.reset(); m_favouritesService.reset(); m_vfsAddonCache.reset(); @@ -254,6 +259,11 @@ CFavouritesService& CServiceManager::GetFavouritesService() return *m_favouritesService; } +CInputManager& CServiceManager::GetInputManager() +{ + return *m_inputManager; +} + // deleters for unique_ptr void CServiceManager::delete_dataCacheCore::operator()(CDataCacheCore *p) const { diff --git a/xbmc/ServiceManager.h b/xbmc/ServiceManager.h index b5941e10287b0..ac091bed295bd 100644 --- a/xbmc/ServiceManager.h +++ b/xbmc/ServiceManager.h @@ -72,6 +72,8 @@ namespace PERIPHERALS class CPeripherals; } +class CInputManager; + class CServiceManager { public: @@ -109,6 +111,7 @@ class CServiceManager CSettings& GetSettings(); CFavouritesService& GetFavouritesService(); + CInputManager &GetInputManager(); protected: struct delete_dataCacheCore @@ -150,4 +153,5 @@ class CServiceManager std::unique_ptr m_gameServices; std::unique_ptr m_peripherals; std::unique_ptr m_favouritesService; + std::unique_ptr m_inputManager; }; diff --git a/xbmc/cores/ExternalPlayer/ExternalPlayer.cpp b/xbmc/cores/ExternalPlayer/ExternalPlayer.cpp index 45700410ac9a2..81f49e7f69ef6 100644 --- a/xbmc/cores/ExternalPlayer/ExternalPlayer.cpp +++ b/xbmc/cores/ExternalPlayer/ExternalPlayer.cpp @@ -461,13 +461,13 @@ bool CExternalPlayer::ExecuteAppLinux(const char* strSwitches) { CLog::Log(LOGNOTICE, "%s: %s", __FUNCTION__, strSwitches); - bool remoteUsed = CInputManager::GetInstance().IsRemoteControlEnabled(); - CInputManager::GetInstance().DisableRemoteControl(); + bool remoteUsed = CServiceBroker::GetInputManager().IsRemoteControlEnabled(); + CServiceBroker::GetInputManager().DisableRemoteControl(); int ret = system(strSwitches); if (remoteUsed) - CInputManager::GetInstance().EnableRemoteControl(); + CServiceBroker::GetInputManager().EnableRemoteControl(); if (ret != 0) { diff --git a/xbmc/games/addons/GameClient.cpp b/xbmc/games/addons/GameClient.cpp index 97ad1d4157a17..c3b438bef934b 100644 --- a/xbmc/games/addons/GameClient.cpp +++ b/xbmc/games/addons/GameClient.cpp @@ -39,6 +39,7 @@ #include "guilib/GUIWindowManager.h" #include "guilib/WindowIDs.h" #include "input/joysticks/JoystickTypes.h" +#include "input/InputManager.h" #include "messaging/ApplicationMessenger.h" #include "peripherals/Peripherals.h" #include "profiles/ProfilesManager.h" @@ -881,7 +882,7 @@ bool CGameClient::SetRumble(unsigned int port, const std::string& feature, float void CGameClient::OpenKeyboard(void) { - m_keyboard.reset(new CGameClientKeyboard(this, &m_struct.toAddon)); + m_keyboard.reset(new CGameClientKeyboard(this, &m_struct.toAddon, &CServiceBroker::GetInputManager())); } void CGameClient::CloseKeyboard(void) @@ -891,7 +892,7 @@ void CGameClient::CloseKeyboard(void) void CGameClient::OpenMouse(void) { - m_mouse.reset(new CGameClientMouse(this, &m_struct.toAddon)); + m_mouse.reset(new CGameClientMouse(this, &m_struct.toAddon, &CServiceBroker::GetInputManager())); CSingleLock lock(m_critSection); diff --git a/xbmc/games/addons/GameClientJoystick.h b/xbmc/games/addons/GameClientJoystick.h index da30554da3a90..92b2db9b33c37 100644 --- a/xbmc/games/addons/GameClientJoystick.h +++ b/xbmc/games/addons/GameClientJoystick.h @@ -54,7 +54,6 @@ namespace GAME virtual std::string ControllerID(void) const override; virtual bool HasFeature(const std::string& feature) const override; virtual bool AcceptsInput(const std::string &feature) const override; - virtual unsigned int GetDelayMs(const std::string& feature) const override { return 0; } virtual bool OnButtonPress(const std::string& feature, bool bPressed) override; virtual void OnButtonHold(const std::string& feature, unsigned int holdTimeMs) override { } virtual bool OnButtonMotion(const std::string& feature, float magnitude, unsigned int motionTimeMs) override; diff --git a/xbmc/games/addons/GameClientKeyboard.cpp b/xbmc/games/addons/GameClientKeyboard.cpp index 7e8fe807147c7..86b6fc4bb5743 100644 --- a/xbmc/games/addons/GameClientKeyboard.cpp +++ b/xbmc/games/addons/GameClientKeyboard.cpp @@ -22,7 +22,7 @@ #include "GameClient.h" #include "GameClientTranslator.h" #include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h" -#include "input/InputManager.h" +#include "input/keyboard/IKeyboardInputProvider.h" #include "input/Key.h" #include "utils/log.h" @@ -31,16 +31,17 @@ using namespace GAME; #define BUTTON_INDEX_MASK 0x01ff -CGameClientKeyboard::CGameClientKeyboard(const CGameClient* gameClient, const KodiToAddonFuncTable_Game* dllStruct) : +CGameClientKeyboard::CGameClientKeyboard(const CGameClient* gameClient, const KodiToAddonFuncTable_Game* dllStruct, KEYBOARD::IKeyboardInputProvider *inputProvider) : m_gameClient(gameClient), - m_dllStruct(dllStruct) + m_dllStruct(dllStruct), + m_inputProvider(inputProvider) { - CInputManager::GetInstance().RegisterKeyboardHandler(this); + m_inputProvider->RegisterKeyboardHandler(this); } CGameClientKeyboard::~CGameClientKeyboard() { - CInputManager::GetInstance().UnregisterKeyboardHandler(this); + m_inputProvider->UnregisterKeyboardHandler(this); } bool CGameClientKeyboard::OnKeyPress(const CKey& key) diff --git a/xbmc/games/addons/GameClientKeyboard.h b/xbmc/games/addons/GameClientKeyboard.h index 8b7ea51ea89c0..6e5789535dafc 100644 --- a/xbmc/games/addons/GameClientKeyboard.h +++ b/xbmc/games/addons/GameClientKeyboard.h @@ -25,6 +25,11 @@ struct KodiToAddonFuncTable_Game; namespace KODI { +namespace KEYBOARD +{ + class IKeyboardInputProvider; +} + namespace GAME { class CGameClient; @@ -42,8 +47,9 @@ namespace GAME * \brief Constructor registers for keyboard events at CInputManager. * \param gameClient The game client implementation. * \param dllStruct The emulator or game to which the events are sent. + * \param inputProvider The interface providing us with keyboard input. */ - CGameClientKeyboard(const CGameClient* gameClient, const KodiToAddonFuncTable_Game* dllStruct); + CGameClientKeyboard(const CGameClient* gameClient, const KodiToAddonFuncTable_Game* dllStruct, KEYBOARD::IKeyboardInputProvider *inputProvider); /*! * \brief Destructor unregisters from keyboard events from CInputManager. @@ -58,6 +64,7 @@ namespace GAME // Construction parameters const CGameClient* const m_gameClient; const KodiToAddonFuncTable_Game* const m_dllStruct; + KEYBOARD::IKeyboardInputProvider *const m_inputProvider; }; } } diff --git a/xbmc/games/addons/GameClientMouse.cpp b/xbmc/games/addons/GameClientMouse.cpp index e1dc74ba2375c..f2e49a19290a3 100644 --- a/xbmc/games/addons/GameClientMouse.cpp +++ b/xbmc/games/addons/GameClientMouse.cpp @@ -22,23 +22,24 @@ #include "GameClient.h" #include "GameClientTranslator.h" #include "addons/kodi-addon-dev-kit/include/kodi/kodi_game_types.h" -#include "input/InputManager.h" +#include "input/mouse/IMouseInputProvider.h" #include "input/Key.h" #include "utils/log.h" using namespace KODI; using namespace GAME; -CGameClientMouse::CGameClientMouse(const CGameClient* gameClient, const KodiToAddonFuncTable_Game* dllStruct) : +CGameClientMouse::CGameClientMouse(const CGameClient* gameClient, const KodiToAddonFuncTable_Game* dllStruct, MOUSE::IMouseInputProvider *inputProvider) : m_gameClient(gameClient), m_dllStruct(dllStruct), - m_controllerId(CInputManager::GetInstance().RegisterMouseHandler(this)) + m_inputProvider(inputProvider), + m_controllerId(inputProvider->RegisterMouseHandler(this)) { } CGameClientMouse::~CGameClientMouse() { - CInputManager::GetInstance().UnregisterMouseHandler(this); + m_inputProvider->UnregisterMouseHandler(this); } std::string CGameClientMouse::ControllerID(void) const diff --git a/xbmc/games/addons/GameClientMouse.h b/xbmc/games/addons/GameClientMouse.h index fbb9d36814157..46a871566de20 100644 --- a/xbmc/games/addons/GameClientMouse.h +++ b/xbmc/games/addons/GameClientMouse.h @@ -25,6 +25,11 @@ struct KodiToAddonFuncTable_Game; namespace KODI { +namespace MOUSE +{ + class IMouseInputProvider; +} + namespace GAME { class CGameClient; @@ -42,8 +47,9 @@ namespace GAME * \brief Constructor registers for mouse events at CInputManager. * \param gameClient The game client implementation. * \param dllStruct The emulator or game to which the events are sent. + * \param inputProvider The interface providing us with mouse input. */ - CGameClientMouse(const CGameClient* gameClient, const KodiToAddonFuncTable_Game* dllStruct); + CGameClientMouse(const CGameClient* gameClient, const KodiToAddonFuncTable_Game* dllStruct, MOUSE::IMouseInputProvider *inputProvider); /*! * \brief Destructor unregisters from mouse events from CInputManager. @@ -60,6 +66,7 @@ namespace GAME // Construction parameters const CGameClient* const m_gameClient; const KodiToAddonFuncTable_Game* const m_dllStruct; + MOUSE::IMouseInputProvider *const m_inputProvider; const std::string m_controllerId; }; } diff --git a/xbmc/games/controllers/Controller.cpp b/xbmc/games/controllers/Controller.cpp index dff7d8b073a34..f5face6b4888d 100644 --- a/xbmc/games/controllers/Controller.cpp +++ b/xbmc/games/controllers/Controller.cpp @@ -65,6 +65,16 @@ void CController::GetFeatures(std::vector& features, } } +JOYSTICK::FEATURE_TYPE CController::GetFeatureType(const std::string &feature) const +{ + for (auto it = m_layout.Features().begin(); it != m_layout.Features().end(); ++it) + { + if (feature == it->Name()) + return it->Type(); + } + return JOYSTICK::FEATURE_TYPE::UNKNOWN; +} + JOYSTICK::INPUT_TYPE CController::GetInputType(const std::string& feature) const { for (auto it = m_layout.Features().begin(); it != m_layout.Features().end(); ++it) diff --git a/xbmc/games/controllers/Controller.h b/xbmc/games/controllers/Controller.h index a2df1f90a93b3..4f8dc32f1ab24 100644 --- a/xbmc/games/controllers/Controller.h +++ b/xbmc/games/controllers/Controller.h @@ -47,6 +47,7 @@ class CController : public ADDON::CAddon std::string Label(void); std::string ImagePath(void) const; void GetFeatures(std::vector& features, FEATURE_TYPE type = FEATURE_TYPE::UNKNOWN) const; + JOYSTICK::FEATURE_TYPE GetFeatureType(const std::string &feature) const; JOYSTICK::INPUT_TYPE GetInputType(const std::string& feature) const; bool LoadLayout(void); diff --git a/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.cpp b/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.cpp index fb74519fa1398..07be9dff2c320 100644 --- a/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.cpp +++ b/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.cpp @@ -52,7 +52,7 @@ std::string CGUIDialogAxisDetection::GetDialogHeader() } bool CGUIDialogAxisDetection::MapPrimitiveInternal(JOYSTICK::IButtonMap* buttonMap, - JOYSTICK::IActionMap* actionMap, + IKeymap* keymap, const JOYSTICK::CDriverPrimitive& primitive) { if (primitive.Type() == JOYSTICK::PRIMITIVE_TYPE::SEMIAXIS) diff --git a/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h b/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h index 82f2ccc2306c3..a1cfb0eae0568 100644 --- a/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h +++ b/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h @@ -44,7 +44,7 @@ namespace GAME virtual std::string GetDialogText() override; virtual std::string GetDialogHeader() override; virtual bool MapPrimitiveInternal(JOYSTICK::IButtonMap* buttonMap, - JOYSTICK::IActionMap* actionMap, + IKeymap* keymap, const JOYSTICK::CDriverPrimitive& primitive) override; virtual void OnClose(bool bAccepted) override { } diff --git a/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp index 2db79f0bcee6c..eed7f099040e7 100644 --- a/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp +++ b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp @@ -23,10 +23,10 @@ #include "guilib/GUIWindowManager.h" #include "guilib/WindowIDs.h" #include "input/joysticks/JoystickIDs.h" -#include "input/joysticks/IActionMap.h" #include "input/joysticks/IButtonMap.h" #include "input/joysticks/IButtonMapCallback.h" #include "input/joysticks/JoystickUtils.h" +#include "input/IKeymap.h" #include "input/ActionIDs.h" #include "peripherals/Peripherals.h" #include "utils/Variant.h" @@ -85,31 +85,35 @@ void CGUIDialogButtonCapture::Process() } bool CGUIDialogButtonCapture::MapPrimitive(JOYSTICK::IButtonMap* buttonMap, - JOYSTICK::IActionMap* actionMap, + IKeymap* keymap, const JOYSTICK::CDriverPrimitive& primitive) { if (m_bStop) return false; // First check to see if driver primitive closes the dialog - if (actionMap && actionMap->ControllerID() == buttonMap->ControllerID()) + if (keymap && keymap->ControllerID() == buttonMap->ControllerID()) { std::string feature; if (buttonMap->GetFeature(primitive, feature)) { - switch (actionMap->GetActionID(feature)) + const auto &actions = keymap->GetActions(JOYSTICK::CJoystickUtils::MakeKeyName(feature)); + if (!actions.empty()) { + switch (actions.begin()->actionId) + { case ACTION_SELECT_ITEM: case ACTION_NAV_BACK: case ACTION_PREVIOUS_MENU: return false; default: break; + } } } } - return MapPrimitiveInternal(buttonMap, actionMap, primitive); + return MapPrimitiveInternal(buttonMap, keymap, primitive); } void CGUIDialogButtonCapture::InstallHooks(void) diff --git a/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h index 90dcfc596b2e0..9504646aed44c 100644 --- a/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h +++ b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h @@ -46,7 +46,7 @@ namespace GAME virtual bool Emulation(void) const override { return false; } virtual unsigned int ControllerNumber(void) const override { return 0; } virtual bool MapPrimitive(JOYSTICK::IButtonMap* buttonMap, - JOYSTICK::IActionMap* actionMap, + IKeymap* keymap, const JOYSTICK::CDriverPrimitive& primitive) override; virtual void OnEventFrame(const JOYSTICK::IButtonMap* buttonMap, bool bMotion) override { } virtual void OnLateAxis(const JOYSTICK::IButtonMap* buttonMap, unsigned int axisIndex) override { } @@ -66,7 +66,7 @@ namespace GAME virtual std::string GetDialogText() = 0; virtual std::string GetDialogHeader() = 0; virtual bool MapPrimitiveInternal(JOYSTICK::IButtonMap* buttonMap, - JOYSTICK::IActionMap* actionMap, + IKeymap* keymap, const JOYSTICK::CDriverPrimitive& primitive) = 0; virtual void OnClose(bool bAccepted) = 0; diff --git a/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.cpp b/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.cpp index a7673404388a4..59cb56769fa44 100644 --- a/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.cpp +++ b/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.cpp @@ -56,7 +56,7 @@ std::string CGUIDialogIgnoreInput::GetDialogHeader() } bool CGUIDialogIgnoreInput::MapPrimitiveInternal(JOYSTICK::IButtonMap* buttonMap, - JOYSTICK::IActionMap* actionMap, + IKeymap* keymap, const JOYSTICK::CDriverPrimitive& primitive) { // Check if we have already started capturing primitives for a device diff --git a/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h b/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h index ca4ebe96e6038..0f85eb014e0ed 100644 --- a/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h +++ b/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h @@ -41,7 +41,7 @@ namespace GAME virtual std::string GetDialogText() override; virtual std::string GetDialogHeader() override; virtual bool MapPrimitiveInternal(JOYSTICK::IButtonMap* buttonMap, - JOYSTICK::IActionMap* actionMap, + IKeymap* keymap, const JOYSTICK::CDriverPrimitive& primitive) override; void OnClose(bool bAccepted) override; diff --git a/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp b/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp index 5b6e6a3a1b430..f8c952d1414d0 100644 --- a/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp +++ b/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp @@ -25,8 +25,10 @@ #include "games/controllers/ControllerFeature.h" #include "input/joysticks/IButtonMap.h" #include "input/joysticks/IButtonMapCallback.h" +#include "input/joysticks/JoystickUtils.h" #include "input/keyboard/KeymapActionMap.h" #include "input/InputManager.h" +#include "input/IKeymap.h" #include "peripherals/Peripherals.h" #include "threads/SingleLock.h" #include "utils/log.h" @@ -188,7 +190,7 @@ void CGUIConfigurationWizard::Process(void) } bool CGUIConfigurationWizard::MapPrimitive(JOYSTICK::IButtonMap* buttonMap, - JOYSTICK::IActionMap* actionMap, + IKeymap* keymap, const JOYSTICK::CDriverPrimitive& primitive) { using namespace JOYSTICK; @@ -199,6 +201,47 @@ bool CGUIConfigurationWizard::MapPrimitive(JOYSTICK::IButtonMap* buttonMap, if (primitive.Type() == PRIMITIVE_TYPE::BUTTON && primitive.Index() == ESC_KEY_CODE) { + bool bIsCancelAction = false; + + //! @todo This only succeeds for game.controller.default; no actions are + // currently defined for other controllers + if (keymap) + { + std::string feature; + if (buttonMap->GetFeature(primitive, feature)) + { + const auto &actions = keymap->GetActions(CJoystickUtils::MakeKeyName(feature)); + if (!actions.empty()) + { + //! @todo Handle multiple actions mapped to the same key + switch (actions.begin()->actionId) + { + case ACTION_NAV_BACK: + case ACTION_PREVIOUS_MENU: + bIsCancelAction = true; + break; + default: + break; + } + } + } + } + + if (bIsCancelAction) + { + CLog::Log(LOGDEBUG, "%s: device \"%s\" is cancelling prompt", buttonMap->ControllerID().c_str(), buttonMap->DeviceName().c_str()); + Abort(false); + } + else + CLog::Log(LOGDEBUG, "%s: ignoring input for device \"%s\"", buttonMap->ControllerID().c_str(), buttonMap->DeviceName().c_str()); + + // Discard input + bHandled = true; + } + else if (primitive.Type() == PRIMITIVE_TYPE::BUTTON && + primitive.Index() == ESC_KEY_CODE) + { + // Handle esc key bHandled = Abort(false); } else if (m_history.find(primitive) != m_history.end()) @@ -341,17 +384,17 @@ void CGUIConfigurationWizard::InstallHooks(void) // If we're not using emulation, allow keyboard input to abort prompt if (!m_bEmulation) - CInputManager::GetInstance().RegisterKeyboardHandler(this); + CServiceBroker::GetInputManager().RegisterKeyboardHandler(this); - CInputManager::GetInstance().RegisterMouseHandler(this); + CServiceBroker::GetInputManager().RegisterMouseHandler(this); } void CGUIConfigurationWizard::RemoveHooks(void) { - CInputManager::GetInstance().UnregisterMouseHandler(this); + CServiceBroker::GetInputManager().UnregisterMouseHandler(this); if (!m_bEmulation) - CInputManager::GetInstance().UnregisterKeyboardHandler(this); + CServiceBroker::GetInputManager().UnregisterKeyboardHandler(this); CServiceBroker::GetPeripherals().UnregisterObserver(this); CServiceBroker::GetPeripherals().UnregisterJoystickButtonMapper(this); diff --git a/xbmc/games/controllers/windows/GUIConfigurationWizard.h b/xbmc/games/controllers/windows/GUIConfigurationWizard.h index 50ef5bef1a204..8f27009bc4f54 100644 --- a/xbmc/games/controllers/windows/GUIConfigurationWizard.h +++ b/xbmc/games/controllers/windows/GUIConfigurationWizard.h @@ -66,7 +66,7 @@ namespace GAME virtual bool Emulation(void) const override { return m_bEmulation; } virtual unsigned int ControllerNumber(void) const override { return m_controllerNumber; } virtual bool MapPrimitive(JOYSTICK::IButtonMap* buttonMap, - JOYSTICK::IActionMap* actionMap, + IKeymap* keymap, const JOYSTICK::CDriverPrimitive& primitive) override; virtual void OnEventFrame(const JOYSTICK::IButtonMap* buttonMap, bool bMotion) override; virtual void OnLateAxis(const JOYSTICK::IButtonMap* buttonMap, unsigned int axisIndex) override; diff --git a/xbmc/games/ports/CMakeLists.txt b/xbmc/games/ports/CMakeLists.txt index 9771b272f99ea..9bdd9de0c0dc9 100644 --- a/xbmc/games/ports/CMakeLists.txt +++ b/xbmc/games/ports/CMakeLists.txt @@ -1,12 +1,10 @@ set(SOURCES InputSink.cpp Port.cpp - PortInput.cpp PortManager.cpp PortMapper.cpp) set(HEADERS InputSink.h Port.h - PortInput.h PortManager.h PortMapper.h PortTypes.h) diff --git a/xbmc/games/ports/InputSink.cpp b/xbmc/games/ports/InputSink.cpp index c978f818bff82..5e544ed2ed7b4 100644 --- a/xbmc/games/ports/InputSink.cpp +++ b/xbmc/games/ports/InputSink.cpp @@ -39,3 +39,23 @@ bool CInputSink::AcceptsInput(const std::string& feature) const { return m_gameClient.AcceptsInput(); } + +bool CInputSink::OnButtonPress(const std::string& feature, bool bPressed) +{ + return true; +} + +bool CInputSink::OnButtonMotion(const std::string& feature, float magnitude, unsigned int motionTimeMs) +{ + return true; +} + +bool CInputSink::OnAnalogStickMotion(const std::string& feature, float x, float y, unsigned int motionTimeMs) +{ + return true; +} + +bool CInputSink::OnAccelerometerMotion(const std::string& feature, float x, float y, float z) +{ + return true; +} diff --git a/xbmc/games/ports/InputSink.h b/xbmc/games/ports/InputSink.h index 35ab13739c273..2d542a7d938a2 100644 --- a/xbmc/games/ports/InputSink.h +++ b/xbmc/games/ports/InputSink.h @@ -39,12 +39,11 @@ namespace GAME virtual std::string ControllerID(void) const override; virtual bool HasFeature(const std::string& feature) const override { return true; } virtual bool AcceptsInput(const std::string& feature) const override; - virtual unsigned int GetDelayMs(const std::string& feature) const override { return 0; } - virtual bool OnButtonPress(const std::string& feature, bool bPressed) override { return true; } + virtual bool OnButtonPress(const std::string& feature, bool bPressed) override; virtual void OnButtonHold(const std::string& feature, unsigned int holdTimeMs) override { } - virtual bool OnButtonMotion(const std::string& feature, float magnitude, unsigned int motionTimeMs) override { return true; } - virtual bool OnAnalogStickMotion(const std::string& feature, float x, float y, unsigned int motionTimeMs) override { return true; } - virtual bool OnAccelerometerMotion(const std::string& feature, float x, float y, float z) override { return true; } + virtual bool OnButtonMotion(const std::string& feature, float magnitude, unsigned int motionTimeMs) override; + virtual bool OnAnalogStickMotion(const std::string& feature, float x, float y, unsigned int motionTimeMs) override; + virtual bool OnAccelerometerMotion(const std::string& feature, float x, float y, float z) override; private: const CGameClient &m_gameClient; diff --git a/xbmc/games/ports/Port.cpp b/xbmc/games/ports/Port.cpp index 3919ffd4a6957..9778a76efce8b 100644 --- a/xbmc/games/ports/Port.cpp +++ b/xbmc/games/ports/Port.cpp @@ -19,8 +19,10 @@ */ #include "Port.h" -#include "PortInput.h" #include "InputSink.h" +#include "games/addons/GameClient.h" +#include "guilib/WindowIDs.h" +#include "input/joysticks/keymaps/KeymapHandling.h" #include "peripherals/devices/Peripheral.h" using namespace KODI; @@ -28,7 +30,7 @@ using namespace GAME; CPort::CPort(JOYSTICK::IInputHandler *gameInput, CGameClient &gameClient) : m_gameInput(gameInput), - m_appInput(new CPortInput(gameClient)), + m_gameClient(gameClient), m_inputSink(new CInputSink(gameClient)) { } @@ -39,21 +41,73 @@ CPort::~CPort() void CPort::RegisterInput(JOYSTICK::IInputProvider *provider) { - // Register GUI input as promiscuous to not block any input from the game - provider->RegisterInputHandler(m_appInput.get(), true); - // Give input sink the lowest priority by registering it before the other - // non-promiscuous input handlers + // input handlers provider->RegisterInputHandler(m_inputSink.get(), false); // Register input handler - provider->RegisterInputHandler(m_gameInput, false); + provider->RegisterInputHandler(this, false); + + // Register GUI input + m_appInput.reset(new JOYSTICK::CKeymapHandling(provider, false, this)); } void CPort::UnregisterInput(JOYSTICK::IInputProvider *provider) { // Unregister in reverse order - provider->UnregisterInputHandler(m_gameInput); + m_appInput.reset(); + provider->UnregisterInputHandler(this); provider->UnregisterInputHandler(m_inputSink.get()); - provider->UnregisterInputHandler(m_appInput.get()); +} + +std::string CPort::ControllerID() const +{ + return m_gameInput->ControllerID(); +} + +bool CPort::AcceptsInput(const std::string& feature) const +{ + return m_gameClient.AcceptsInput(); +} + +bool CPort::OnButtonPress(const std::string& feature, bool bPressed) +{ + if (bPressed && !m_gameClient.AcceptsInput()) + return false; + + return m_gameInput->OnButtonPress(feature, bPressed); +} + +void CPort::OnButtonHold(const std::string& feature, unsigned int holdTimeMs) +{ + m_gameInput->OnButtonHold(feature, holdTimeMs); +} + +bool CPort::OnButtonMotion(const std::string& feature, float magnitude, unsigned int motionTimeMs) +{ + if (magnitude > 0.0f && !m_gameClient.AcceptsInput()) + return false; + + return m_gameInput->OnButtonMotion(feature, magnitude, motionTimeMs); +} + +bool CPort::OnAnalogStickMotion(const std::string& feature, float x, float y, unsigned int motionTimeMs) +{ + if ((x != 0.0f || y != 0.0f) && !m_gameClient.AcceptsInput()) + return false; + + return m_gameInput->OnAnalogStickMotion(feature, x, y, motionTimeMs); +} + +bool CPort::OnAccelerometerMotion(const std::string& feature, float x, float y, float z) +{ + if (!m_gameClient.AcceptsInput()) + return false; + + return m_gameInput->OnAccelerometerMotion(feature, x, y, z); +} + +int CPort::GetWindowID() const +{ + return WINDOW_FULLSCREEN_GAME; } diff --git a/xbmc/games/ports/Port.h b/xbmc/games/ports/Port.h index 58fa54238537a..e957a0bed80d6 100644 --- a/xbmc/games/ports/Port.h +++ b/xbmc/games/ports/Port.h @@ -19,18 +19,16 @@ */ #pragma once -#include +#include "input/joysticks/IInputHandler.h" +#include "input/KeymapEnvironment.h" -namespace PERIPHERALS -{ - class CPeripheral; -} +#include namespace KODI { namespace JOYSTICK { - class IInputHandler; + class CKeymapHandling; class IInputProvider; } @@ -38,7 +36,8 @@ namespace GAME { class CGameClient; - class CPort + class CPort : public JOYSTICK::IInputHandler, + public IKeymapEnvironment { public: CPort(JOYSTICK::IInputHandler* gameInput, CGameClient& gameClient); @@ -49,9 +48,30 @@ namespace GAME JOYSTICK::IInputHandler *InputHandler() { return m_gameInput; } + // Implementation of IInputHandler + virtual std::string ControllerID() const override; + virtual bool HasFeature(const std::string& feature) const override { return true; } + virtual bool AcceptsInput(const std::string& feature) const override; + virtual bool OnButtonPress(const std::string& feature, bool bPressed) override; + virtual void OnButtonHold(const std::string& feature, unsigned int holdTimeMs) override; + virtual bool OnButtonMotion(const std::string& feature, float magnitude, unsigned int motionTimeMs) override; + virtual bool OnAnalogStickMotion(const std::string& feature, float x, float y, unsigned int motionTimeMs) override; + virtual bool OnAccelerometerMotion(const std::string& feature, float x, float y, float z) override; + + // Implementation of IKeymapEnvironment + virtual int GetWindowID() const override; + virtual int GetFallthrough(int windowId) const override { return -1; } + virtual bool UseGlobalFallthrough() const override { return false; } + private: + // Construction parameters JOYSTICK::IInputHandler* const m_gameInput; - std::unique_ptr m_appInput; + CGameClient& m_gameClient; + + // Handles input to Kodi + std::unique_ptr m_appInput; + + // Prevents input falling through to Kodi when not handled by the game std::unique_ptr m_inputSink; }; } diff --git a/xbmc/games/ports/PortInput.h b/xbmc/games/ports/PortInput.h deleted file mode 100644 index cfb50e67f0130..0000000000000 --- a/xbmc/games/ports/PortInput.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2017 Team Kodi - * http://kodi.tv - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; see the file COPYING. If not, see - * . - * - */ -#pragma once - -#include "input/joysticks/DefaultController.h" - -#include - -namespace KODI -{ -namespace GAME -{ - class CGameClient; - - /*! - * \ingroup games - * \brief Monitors for keymap actions in the background during gameplay - * - * This class inherits from the default controller that Kodi uses for - * normal input. However, it overrides some functions of CDefaultJoystick - * to customize the input handling behavior: - * - * - GetWindowID(): - * This forces the keymap handler to use the FullscreenGame window - * - * - GetFallthrough(): - * This prevents the keymap handler from falling through to - * FullscreenVideo or the global keymap when looking up keys. - * - * This class is registered as a promiscuous input handler, so it receives - * all input but doesn't block any handled input from reaching the game - * client. - */ - class CPortInput : public JOYSTICK::CDefaultController - { - public: - CPortInput(CGameClient &gameClient); - - virtual ~CPortInput() = default; - - // Implementation of IInputHandler via CDefaultController - virtual bool AcceptsInput(const std::string &feature) const override; - - // Implementation of CDefaultJoystick via CDefaultController - virtual int GetWindowID() const; - virtual bool GetFallthrough() const { return false; } - - private: - const CGameClient &m_gameClient; - }; -} -} diff --git a/xbmc/guilib/GUIControl.cpp b/xbmc/guilib/GUIControl.cpp index 8446e0f2b17c7..1e061a5717b39 100644 --- a/xbmc/guilib/GUIControl.cpp +++ b/xbmc/guilib/GUIControl.cpp @@ -28,6 +28,7 @@ #include "input/MouseStat.h" #include "input/InputManager.h" #include "input/Key.h" +#include "ServiceBroker.h" CGUIControl::CGUIControl() : m_hitColor(0xffffffff), @@ -584,8 +585,8 @@ EVENT_RESULT CGUIControl::SendMouseEvent(const CPoint &point, const CMouseEvent // override this function to implement custom mouse behaviour bool CGUIControl::OnMouseOver(const CPoint &point) { - if (CInputManager::GetInstance().GetMouseState() != MOUSE_STATE_DRAG) - CInputManager::GetInstance().SetMouseState(MOUSE_STATE_FOCUS); + if (CServiceBroker::GetInputManager().GetMouseState() != MOUSE_STATE_DRAG) + CServiceBroker::GetInputManager().SetMouseState(MOUSE_STATE_FOCUS); if (!CanFocus()) return false; if (!HasFocus()) { diff --git a/xbmc/guilib/GraphicContext.cpp b/xbmc/guilib/GraphicContext.cpp index 6065bb4bc31ba..f1d04fc17eb26 100644 --- a/xbmc/guilib/GraphicContext.cpp +++ b/xbmc/guilib/GraphicContext.cpp @@ -32,6 +32,7 @@ #include "TextureManager.h" #include "input/InputManager.h" #include "GUIWindowManager.h" +#include "ServiceBroker.h" using namespace KODI::MESSAGING; @@ -440,7 +441,7 @@ void CGraphicContext::SetVideoResolutionInternal(RESOLUTION res, bool forceUpdat SetStereoView(RENDER_STEREO_VIEW_OFF); // update anyone that relies on sizing information - CInputManager::GetInstance().SetMouseResolution(info_org.iWidth, info_org.iHeight, 1, 1); + CServiceBroker::GetInputManager().SetMouseResolution(info_org.iWidth, info_org.iHeight, 1, 1); g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESIZE); Unlock(); diff --git a/xbmc/input/ButtonTranslator.cpp b/xbmc/input/ButtonTranslator.cpp index f222b88461a39..de12d9da62cee 100644 --- a/xbmc/input/ButtonTranslator.cpp +++ b/xbmc/input/ButtonTranslator.cpp @@ -29,64 +29,63 @@ #include "CustomControllerTranslator.h" #include "GamepadTranslator.h" #include "IRTranslator.h" -#include "JoystickTranslator.h" +#include "IButtonMapper.h" +#include "Key.h" #include "KeyboardTranslator.h" #include "MouseTranslator.h" -#include "TouchTranslator.h" #include "WindowTranslator.h" #include "FileItem.h" #include "filesystem/Directory.h" #include "guilib/WindowIDs.h" -#include "input/Key.h" #include "Util.h" #include "utils/log.h" #include "utils/XBMCTinyXML.h" -CButtonTranslator& CButtonTranslator::GetInstance() +using namespace KODI; + +CButtonTranslator::CButtonTranslator() { - static CButtonTranslator sl_instance; - return sl_instance; } -CButtonTranslator::CButtonTranslator() : - m_customControllerTranslator(new CCustomControllerTranslator), - m_irTranslator(new CIRTranslator), - m_touchTranslator(new CTouchTranslator) +CButtonTranslator::~CButtonTranslator() { } // Add the supplied device name to the list of connected devices -void CButtonTranslator::AddDevice(std::string& strDevice) +bool CButtonTranslator::AddDevice(const std::string& strDevice) { // Only add the device if it isn't already in the list if (m_deviceList.find(strDevice) != m_deviceList.end()) - return; + return false; // Add the device m_deviceList.insert(strDevice); // New device added so reload the key mappings Load(); + + return true; } -void CButtonTranslator::RemoveDevice(std::string& strDevice) +bool CButtonTranslator::RemoveDevice(const std::string& strDevice) { // Find the device auto it = m_deviceList.find(strDevice); if (it == m_deviceList.end()) - return; + return false; // Remove the device m_deviceList.erase(it); // Device removed so reload the key mappings Load(); + + return true; } -bool CButtonTranslator::Load(bool AlwaysLoad) +bool CButtonTranslator::Load() { - m_translatorMap.clear(); - m_customControllerTranslator->Clear(); + Clear(); // Directories to search for keymaps. They're applied in this order, // so keymaps in profile/keymaps/ override e.g. system/keymaps @@ -142,8 +141,6 @@ bool CButtonTranslator::Load(bool AlwaysLoad) return false; } - m_irTranslator->Load(); - // Done! return true; } @@ -196,58 +193,6 @@ bool CButtonTranslator::LoadKeymap(const std::string &keymapPath) return true; } -int CButtonTranslator::TranslateLircRemoteString(const std::string &szDevice, const std::string &szButton) -{ - return m_irTranslator->TranslateButton(szDevice, szButton); -} - -bool CButtonTranslator::TranslateCustomControllerString(int windowId, const std::string& controllerName, int buttonId, int& action, std::string& strAction) -{ - unsigned int actionId = ACTION_NONE; - - // Try to get the action from the current window - if (!m_customControllerTranslator->TranslateString(windowId, controllerName, buttonId, actionId, strAction)) - { - // If it's invalid, try to get it from a fallback window or the global map - int fallbackWindow = CWindowTranslator::GetFallbackWindow(windowId); - if (fallbackWindow > -1) - m_customControllerTranslator->TranslateString(fallbackWindow, controllerName, buttonId, actionId, strAction); - - // Still no valid action? Use global map - if (action == ACTION_NONE) - m_customControllerTranslator->TranslateString(-1, controllerName, buttonId, actionId, strAction); - } - - if (actionId != ACTION_NONE) - { - action = actionId; - return true; - } - - return false; -} - -bool CButtonTranslator::TranslateTouchAction(int window, int touchAction, int touchPointers, int &action, std::string &actionString) -{ - if (touchAction < 0) - return false; - - unsigned int actionId = ACTION_NONE; - - if (!m_touchTranslator->TranslateAction(window, touchAction, touchPointers, actionId, actionString)) - { - int fallbackWindow = CWindowTranslator::GetFallbackWindow(window); - if (fallbackWindow > -1) - m_touchTranslator->TranslateAction(fallbackWindow, touchAction, touchPointers, actionId, actionString); - - if (actionId == ACTION_NONE) - m_touchTranslator->TranslateAction(-1, touchAction, touchPointers, actionId, actionString); - } - - action = actionId; - return actionId != ACTION_NONE; -} - CAction CButtonTranslator::GetAction(int window, const CKey &key, bool fallback) { std::string strAction; @@ -317,50 +262,6 @@ bool CButtonTranslator::HasLongpressMapping(int window, const CKey &key) return false; } -unsigned int CButtonTranslator::GetHoldTimeMs(int window, const CKey &key, bool fallback /* = true */) -{ - unsigned int holdtimeMs = 0; - - std::map::const_iterator it = m_translatorMap.find(window); - if (it != m_translatorMap.end()) - { - uint32_t code = key.GetButtonCode(); - - buttonMap::const_iterator it2 = (*it).second.find(code); - - if (it2 != (*it).second.end()) - { - holdtimeMs = (*it2).second.holdtimeMs; - } - else if (fallback) - { - //! @todo Refactor fallback logic - int fallbackWindow = CWindowTranslator::GetFallbackWindow(window); - if (fallbackWindow > -1) - holdtimeMs = GetHoldTimeMs(fallbackWindow, key, false); - else - { - // still no valid action? use global map - holdtimeMs = GetHoldTimeMs(-1, key, false); - } - } - } - else if (fallback) - { - //! @todo Refactor fallback logic - int fallbackWindow = CWindowTranslator::GetFallbackWindow(window); - if (fallbackWindow > -1) - holdtimeMs = GetHoldTimeMs(fallbackWindow, key, false); - else - { - // still no valid action? use global map - holdtimeMs = GetHoldTimeMs(-1, key, false); - } - } - - return holdtimeMs; -} - unsigned int CButtonTranslator::GetActionCode(int window, const CKey &key, std::string &strAction) const { uint32_t code = key.GetButtonCode(); @@ -401,7 +302,7 @@ unsigned int CButtonTranslator::GetActionCode(int window, const CKey &key, std:: return action; } -void CButtonTranslator::MapAction(uint32_t buttonCode, const std::string &szAction, unsigned int holdtimeMs, buttonMap &map) +void CButtonTranslator::MapAction(uint32_t buttonCode, const std::string &szAction, buttonMap &map) { unsigned int action = ACTION_NONE; if (!CActionTranslator::TranslateString(szAction, action) || buttonCode == 0) @@ -419,7 +320,6 @@ void CButtonTranslator::MapAction(uint32_t buttonCode, const std::string &szActi CButtonAction button; button.id = action; button.strID = szAction; - button.holdtimeMs = holdtimeMs; map.insert(std::pair(buttonCode, button)); } } @@ -431,7 +331,7 @@ void CButtonTranslator::MapWindowActions(const TiXmlNode *pWindow, int windowID) const TiXmlNode *pDevice; - static const std::vector types = {"gamepad", "remote", "universalremote", "keyboard", "mouse", "appcommand", "joystick"}; + static const std::vector types = {"gamepad", "remote", "universalremote", "keyboard", "mouse", "appcommand"}; for (const auto& type : types) { @@ -452,7 +352,6 @@ void CButtonTranslator::MapWindowActions(const TiXmlNode *pWindow, int windowID) while (pButton != nullptr) { uint32_t buttonCode = 0; - unsigned int holdtimeMs = 0; if (type == "gamepad") buttonCode = CGamepadTranslator::TranslateString(pButton->Value()); @@ -466,13 +365,11 @@ void CButtonTranslator::MapWindowActions(const TiXmlNode *pWindow, int windowID) buttonCode = CMouseTranslator::TranslateCommand(pButton); else if (type == "appcommand") buttonCode = CAppTranslator::TranslateAppCommand(pButton->Value()); - else if (type == "joystick") - buttonCode = CJoystickTranslator::TranslateButton(pDevice, pButton, holdtimeMs); if (buttonCode != 0) { if (pButton->FirstChild() && pButton->FirstChild()->Value()[0]) - MapAction(buttonCode, pButton->FirstChild()->Value(), holdtimeMs, map); + MapAction(buttonCode, pButton->FirstChild()->Value(), map); else { buttonMap::iterator it = map.find(buttonCode); @@ -492,20 +389,18 @@ void CButtonTranslator::MapWindowActions(const TiXmlNode *pWindow, int windowID) } } - // map touch actions - pDevice = pWindow->FirstChild("touch"); - while (pDevice != nullptr) + for (auto it : m_buttonMappers) { - m_touchTranslator->MapActions(windowID, pDevice); - pDevice = pDevice->NextSibling("touch"); - } + const std::string &device = it.first; + IButtonMapper *mapper = it.second; - // map custom controller actions - pDevice = pWindow->FirstChild("customcontroller"); - while (pDevice != nullptr) - { - m_customControllerTranslator->MapActions(windowID, pDevice); - pDevice = pDevice->NextSibling("customcontroller"); + // Map device actions + pDevice = pWindow->FirstChild(device); + while (pDevice != nullptr) + { + mapper->MapActions(windowID, pDevice); + pDevice = pDevice->NextSibling(device); + } } } @@ -513,7 +408,23 @@ void CButtonTranslator::Clear() { m_translatorMap.clear(); - m_irTranslator->Clear(); - m_customControllerTranslator->Clear(); - m_touchTranslator->Clear(); + for (auto it : m_buttonMappers) + it.second->Clear(); +} + +void CButtonTranslator::RegisterMapper(const std::string &device, IButtonMapper *mapper) +{ + m_buttonMappers[device] = mapper; +} + +void CButtonTranslator::UnregisterMapper(IButtonMapper *mapper) +{ + for (auto it = m_buttonMappers.begin(); it != m_buttonMappers.end(); ++it) + { + if (it->second == mapper) + { + m_buttonMappers.erase(it); + break; + } + } } diff --git a/xbmc/input/ButtonTranslator.h b/xbmc/input/ButtonTranslator.h index 442a739df3cb6..449954eb06b6f 100644 --- a/xbmc/input/ButtonTranslator.h +++ b/xbmc/input/ButtonTranslator.h @@ -33,8 +33,9 @@ class CKey; class TiXmlNode; class CCustomControllerTranslator; -class CIRTranslator; class CTouchTranslator; +class IButtonMapper; +class IWindowKeymap; /// singleton class to map from buttons to actions /// Warning: _not_ threadsafe! @@ -44,23 +45,18 @@ class CButtonTranslator friend class EVENTCLIENT::CEventButtonState; #endif -private: - //private construction, and no assignments; use the provided singleton methods +public: CButtonTranslator(); CButtonTranslator(const CButtonTranslator&) = delete; CButtonTranslator const& operator=(CButtonTranslator const&) = delete; - virtual ~CButtonTranslator() = default; - -public: - ///access to singleton - static CButtonTranslator& GetInstance(); + virtual ~CButtonTranslator(); // Add/remove a HID device with custom mappings - void AddDevice(std::string& strDevice); - void RemoveDevice(std::string& strDevice); + bool AddDevice(const std::string& strDevice); + bool RemoveDevice(const std::string& strDevice); /// loads Keymap.xml - bool Load(bool AlwaysLoad = false); + bool Load(); /// clears the maps void Clear(); @@ -72,13 +68,6 @@ class CButtonTranslator */ bool HasLongpressMapping(int window, const CKey &key); - /*! \brief Get the "holdtime" parameter if specified for this key - \param window id of the current window - \param key to search a mapping for - \return the holdtime in ms, or 0 if no holdtime was specified - */ - unsigned int GetHoldTimeMs(int window, const CKey &key, bool fallback = true); - /*! \brief Obtain the action configured for a given window and key \param window the window id \param key the key to query the action for @@ -93,18 +82,14 @@ class CButtonTranslator */ CAction GetGlobalAction(const CKey &key); - int TranslateLircRemoteString(const std::string &szDevice, const std::string &szButton); - - bool TranslateCustomControllerString(int windowId, const std::string& controllerName, int buttonId, int& action, std::string& strAction); - - bool TranslateTouchAction(int window, int touchAction, int touchPointers, int &action, std::string &actionString); + void RegisterMapper(const std::string &device, IButtonMapper *mapper); + void UnregisterMapper(IButtonMapper *mapper); private: struct CButtonAction { unsigned int id; std::string strID; // needed for "ActivateWindow()" type actions - unsigned int holdtimeMs; }; typedef std::multimap buttonMap; // our button map to fill in @@ -118,11 +103,9 @@ class CButtonTranslator unsigned int GetActionCode(int window, const CKey &key, std::string &strAction) const; void MapWindowActions(const TiXmlNode *pWindow, int wWindowID); - void MapAction(uint32_t buttonCode, const std::string &szAction, unsigned int holdtimeMs, buttonMap &map); + void MapAction(uint32_t buttonCode, const std::string &szAction, buttonMap &map); bool LoadKeymap(const std::string &keymapPath); - std::unique_ptr m_customControllerTranslator; - std::unique_ptr m_irTranslator; - std::unique_ptr m_touchTranslator; + std::map m_buttonMappers; }; diff --git a/xbmc/input/CMakeLists.txt b/xbmc/input/CMakeLists.txt index 4974a2276812a..1dd3902827122 100644 --- a/xbmc/input/CMakeLists.txt +++ b/xbmc/input/CMakeLists.txt @@ -11,15 +11,18 @@ set(SOURCES Action.cpp InputCodingTableKorean.cpp InputManager.cpp IRTranslator.cpp - JoystickTranslator.cpp + JoystickMapper.cpp Key.cpp KeyboardLayout.cpp KeyboardLayoutManager.cpp KeyboardStat.cpp KeyboardTranslator.cpp + Keymap.cpp + KeymapEnvironment.cpp MouseStat.cpp MouseTranslator.cpp TouchTranslator.cpp + WindowKeymap.cpp WindowTranslator.cpp XBMC_keytable.cpp) @@ -30,6 +33,9 @@ set(HEADERS Action.h ButtonTranslator.h CustomControllerTranslator.h GamepadTranslator.h + IButtonMapper.h + IKeymap.h + IKeymapEnvironment.h InertialScrollingHandler.h InputCodingTable.h InputCodingTableBaiduPY.h @@ -38,16 +44,19 @@ set(HEADERS Action.h InputCodingTableKorean.h InputManager.h IRTranslator.h - JoystickTranslator.h + JoystickMapper.h Key.h KeyboardLayout.h KeyboardLayoutManager.h KeyboardStat.h KeyboardTranslator.h + Keymap.h + KeymapEnvironment.h MouseStat.h MouseTranslator.h TouchTranslator.h WindowTranslator.h + WindowKeymap.h XBIRRemote.h XBMC_keyboard.h XBMC_keysym.h diff --git a/xbmc/input/CustomControllerTranslator.cpp b/xbmc/input/CustomControllerTranslator.cpp index fac1b10365461..90910e3c22517 100644 --- a/xbmc/input/CustomControllerTranslator.cpp +++ b/xbmc/input/CustomControllerTranslator.cpp @@ -21,6 +21,7 @@ #include "CustomControllerTranslator.h" #include "ActionIDs.h" #include "ActionTranslator.h" +#include "WindowTranslator.h" //! @todo #include "utils/log.h" #include "utils/XBMCTinyXML.h" @@ -73,6 +74,32 @@ void CCustomControllerTranslator::Clear() m_customControllersMap.clear(); } +bool CCustomControllerTranslator::TranslateCustomControllerString(int windowId, const std::string& controllerName, int buttonId, int& action, std::string& strAction) +{ + unsigned int actionId = ACTION_NONE; + + // Try to get the action from the current window + if (!TranslateString(windowId, controllerName, buttonId, actionId, strAction)) + { + // If it's invalid, try to get it from a fallback window or the global map + int fallbackWindow = CWindowTranslator::GetFallbackWindow(windowId); + if (fallbackWindow > -1) + TranslateString(fallbackWindow, controllerName, buttonId, actionId, strAction); + + // Still no valid action? Use global map + if (action == ACTION_NONE) + TranslateString(-1, controllerName, buttonId, actionId, strAction); + } + + if (actionId != ACTION_NONE) + { + action = actionId; + return true; + } + + return false; +} + bool CCustomControllerTranslator::TranslateString(int windowId, const std::string& controllerName, int buttonId, unsigned int& actionId, std::string& strAction) { // Resolve the correct custom controller diff --git a/xbmc/input/CustomControllerTranslator.h b/xbmc/input/CustomControllerTranslator.h index 7da3c88ad7f52..4de3f45dd1e18 100644 --- a/xbmc/input/CustomControllerTranslator.h +++ b/xbmc/input/CustomControllerTranslator.h @@ -19,23 +19,27 @@ */ #pragma once +#include "IButtonMapper.h" + #include #include class TiXmlNode; -class CCustomControllerTranslator +class CCustomControllerTranslator : public IButtonMapper { public: CCustomControllerTranslator() = default; - void MapActions(int windowID, const TiXmlNode *pCustomController); + // implementation of IButtonMapper + virtual void MapActions(int windowID, const TiXmlNode *pDevice) override; + virtual void Clear() override; - void Clear(); + bool TranslateCustomControllerString(int windowId, const std::string& controllerName, int buttonId, int& action, std::string& strAction); +private: bool TranslateString(int windowId, const std::string& controllerName, int buttonId, unsigned int& actionId, std::string& strAction); -private: // Maps button id to action using CustomControllerButtonMap = std::map; diff --git a/xbmc/input/JoystickTranslator.h b/xbmc/input/IButtonMapper.h similarity index 76% rename from xbmc/input/JoystickTranslator.h rename to xbmc/input/IButtonMapper.h index e28f3fbe77da9..022f468dadd9d 100644 --- a/xbmc/input/JoystickTranslator.h +++ b/xbmc/input/IButtonMapper.h @@ -19,13 +19,17 @@ */ #pragma once -#include - -class TiXmlElement; class TiXmlNode; -class CJoystickTranslator +/*! + * \brief Interface for classes that can map buttons to Kodi actions + */ +class IButtonMapper { public: - static uint32_t TranslateButton(const TiXmlNode* pDevice, const TiXmlElement *pButton, unsigned int& holdtimeMs); + virtual ~IButtonMapper() = default; + + virtual void MapActions(int windowId, const TiXmlNode *pDevice) = 0; + + virtual void Clear() = 0; }; diff --git a/xbmc/input/IKeymap.h b/xbmc/input/IKeymap.h new file mode 100644 index 0000000000000..cd8b7bbd23dea --- /dev/null +++ b/xbmc/input/IKeymap.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "input/joysticks/JoystickTypes.h" + +#include + +class IKeymapEnvironment; + +/*! + * \brief Interface for mapping buttons to Kodi actions + * + * \sa CJoystickUtils::MakeKeyName + */ +class IKeymap +{ +public: + virtual ~IKeymap() = default; + + /*! + * \brief The controller ID + * + * This is required because key names are specific to each controller + */ + virtual std::string ControllerID() const = 0; + + /*! + * \brief Access properties of the keymapping environment + */ + virtual const IKeymapEnvironment *Environment() const = 0; + + /*! + * \brief Get the actions for a given key name + * + * \param keyName The key name created by CJoystickUtils::MakeKeyName() + * + * \return A list of actions associated with the given key + */ + virtual const KODI::JOYSTICK::KeymapActions& GetActions(const std::string& keyName) const = 0; +}; + +/*! + * \brief Interface for mapping buttons to Kodi actions for specific windows + * + * \sa CJoystickUtils::MakeKeyName + */ +class IWindowKeymap +{ +public: + virtual ~IWindowKeymap() = default; + + /*! + * \brief The controller ID + * + * This is required because key names are specific to each controller + */ + virtual std::string ControllerID() const = 0; + + /*! + * \brief Add an action to the keymap for a given key name and window ID + * + * \param windowId The window ID to look up + * \param keyName The key name created by CJoystickUtils::MakeKeyName() + * \param action The action to map + */ + virtual void MapAction(int windowId, const std::string &keyName, KODI::JOYSTICK::KeymapAction action) = 0; + + /*! + * \brief Get the actions for a given key name and window ID + * + * \param windowId The window ID to look up + * \param keyName The key name created by CJoystickUtils::MakeKeyName() + * + * \return A list of actions associated with the given key for the given window + */ + virtual const KODI::JOYSTICK::KeymapActions& GetActions(int windowId, const std::string& keyName) const = 0; +}; diff --git a/xbmc/input/IKeymapEnvironment.h b/xbmc/input/IKeymapEnvironment.h new file mode 100644 index 0000000000000..1eaf7f82ac3a0 --- /dev/null +++ b/xbmc/input/IKeymapEnvironment.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +/*! + * \brief Customizes the environment in which keymapping is performed + * + * By overriding GetWindowID() and GetFallthrough(), an agent can customize + * the behavior of the keymap by forcing a window and preventing the use of + * a fallback window, respectively. + * + * An agent can also inform the keymap that it isn't accepting input currently, + * allowing the input to fall through to the next input handler. + */ +class IKeymapEnvironment +{ +public: + virtual ~IKeymapEnvironment() = default; + + /*! + * \brief Get the window ID for which actions should be translated + * + * \return The window ID + */ + virtual int GetWindowID() const = 0; + + /*! + * \brief Get the fallthrough window to when a key definition is missing + * + * \param windowId The window ID + * + * \return The window ID, or -1 for no fallthrough + */ + virtual int GetFallthrough(int windowId) const = 0; + + /*! + * \brief Specify if the global keymap should be used when the window and + * fallback window are undefined + */ + virtual bool UseGlobalFallthrough() const = 0; +}; diff --git a/xbmc/input/InputManager.cpp b/xbmc/input/InputManager.cpp index f56770c5ef256..9152b246eb050 100644 --- a/xbmc/input/InputManager.cpp +++ b/xbmc/input/InputManager.cpp @@ -22,7 +22,12 @@ #include "Application.h" #include "ServiceBroker.h" +#include "CustomControllerTranslator.h" #include "InputManager.h" +#include "IRTranslator.h" +#include "JoystickMapper.h" +#include "KeymapEnvironment.h" +#include "TouchTranslator.h" #include "input/keyboard/IKeyboardHandler.h" #include "input/mouse/generic/MouseInputHandling.h" #include "input/mouse/IMouseDriverHandler.h" @@ -74,21 +79,29 @@ using namespace KODI; using namespace MESSAGING; CInputManager::CInputManager() : + m_keymapEnvironment(new CKeymapEnvironment), + m_buttonTranslator(new CButtonTranslator), + m_irTranslator(new CIRTranslator), + m_customControllerTranslator(new CCustomControllerTranslator), + m_touchTranslator(new CTouchTranslator), + m_joystickTranslator(new CJoystickMapper), m_mouseButtonMap(new MOUSE::CMouseWindowingButtonMap), m_keyboardEasterEgg(new KEYBOARD::CKeyboardEasterEgg) { + m_buttonTranslator->RegisterMapper("touch", m_touchTranslator.get()); + m_buttonTranslator->RegisterMapper("customcontroller", m_customControllerTranslator.get()); + m_buttonTranslator->RegisterMapper("joystick", m_joystickTranslator.get()); + RegisterKeyboardHandler(m_keyboardEasterEgg.get()); } CInputManager::~CInputManager() { UnregisterKeyboardHandler(m_keyboardEasterEgg.get()); -} -CInputManager& CInputManager::GetInstance() -{ - static CInputManager inputManager; - return inputManager; + m_buttonTranslator->UnregisterMapper(m_touchTranslator.get()); + m_buttonTranslator->UnregisterMapper(m_customControllerTranslator.get()); + m_buttonTranslator->UnregisterMapper(m_joystickTranslator.get()); } void CInputManager::InitializeInputs() @@ -103,6 +116,10 @@ void CInputManager::InitializeInputs() m_Mouse.SetEnabled(CServiceBroker::GetSettings().GetBool(CSettings::SETTING_INPUT_ENABLEMOUSE)); } +void CInputManager::Deinitialize() +{ +} + void CInputManager::SetEnabledJoystick(bool enabled /* = true */) { //! @todo @@ -150,7 +167,7 @@ bool CInputManager::ProcessMouse(int windowId) // Retrieve the corresponding action CKey key(mousekey, (unsigned int)0); - CAction mouseaction = CButtonTranslator::GetInstance().GetAction(windowId, key); + CAction mouseaction = m_buttonTranslator->GetAction(windowId, key); // Deactivate mouse if non-mouse action if (!mouseaction.IsMouse()) @@ -234,7 +251,7 @@ bool CInputManager::ProcessEventServer(int windowId, float frameTime) std::string actionName; // Translate using custom controller translator. - if (CButtonTranslator::GetInstance().TranslateCustomControllerString(windowId, strMapName, wKeyID, actionID, actionName)) + if (m_customControllerTranslator->TranslateCustomControllerString(windowId, strMapName, wKeyID, actionID, actionName)) { // break screensaver g_application.ResetSystemIdleTimer(); @@ -364,7 +381,7 @@ bool CInputManager::OnEvent(XBMC_Event& newEvent) // Do not repeat long presses break; } - if (!CButtonTranslator::GetInstance().HasLongpressMapping(g_windowManager.GetActiveWindowID(), key)) + if (!m_buttonTranslator->HasLongpressMapping(g_windowManager.GetActiveWindowID(), key)) { m_LastKey.Reset(); OnKey(key); @@ -442,7 +459,7 @@ bool CInputManager::OnEvent(XBMC_Event& newEvent) else { int iWin = g_windowManager.GetActiveWindowID(); - CButtonTranslator::GetInstance().TranslateTouchAction(iWin, newEvent.touch.action, newEvent.touch.pointers, actionId, actionString); + m_touchTranslator->TranslateTouchAction(iWin, newEvent.touch.action, newEvent.touch.pointers, actionId, actionString); } if (actionId <= 0) @@ -496,7 +513,7 @@ bool CInputManager::OnKey(const CKey& key) // this will be checked for certain keycodes that need // special handling if the screensaver is active - CAction action = CButtonTranslator::GetInstance().GetAction(iWin, key); + CAction action = m_buttonTranslator->GetAction(iWin, key); // a key has been pressed. // reset Idle Timer @@ -559,7 +576,7 @@ bool CInputManager::OnKey(const CKey& key) { // use the virtualkeyboard section of the keymap, and send keyboard-specific or navigation // actions through if that's what they are - CAction action = CButtonTranslator::GetInstance().GetAction(WINDOW_DIALOG_KEYBOARD, key); + CAction action = m_buttonTranslator->GetAction(WINDOW_DIALOG_KEYBOARD, key); if (!(action.GetID() == ACTION_MOVE_LEFT || action.GetID() == ACTION_MOVE_RIGHT || action.GetID() == ACTION_MOVE_UP || @@ -571,7 +588,7 @@ bool CInputManager::OnKey(const CKey& key) action.GetID() == ACTION_VOICE_RECOGNIZE)) { // the action isn't plain navigation - check for a keyboard-specific keymap - action = CButtonTranslator::GetInstance().GetAction(WINDOW_DIALOG_KEYBOARD, key, false); + action = m_buttonTranslator->GetAction(WINDOW_DIALOG_KEYBOARD, key, false); if (!(action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9) || action.GetID() == ACTION_BACKSPACE || action.GetID() == ACTION_SHIFT || @@ -620,10 +637,10 @@ bool CInputManager::OnKey(const CKey& key) if (key.GetFromService()) { if (key.GetButtonCode() != KEY_INVALID) - action = CButtonTranslator::GetInstance().GetAction(iWin, key); + action = m_buttonTranslator->GetAction(iWin, key); } else - action = CButtonTranslator::GetInstance().GetAction(iWin, key); + action = m_buttonTranslator->GetAction(iWin, key); } if (!key.IsAnalogButton()) CLog::LogF(LOGDEBUG, "%s pressed, action is %s", m_Keyboard.GetKeyName((int)key.GetButtonCode()).c_str(), action.GetName().c_str()); @@ -868,6 +885,84 @@ bool CInputManager::OnAction(const CAction& action) return false; } +bool CInputManager::LoadKeymaps() +{ + bool bSuccess = false; + + if (m_buttonTranslator->Load()) + { + m_irTranslator->Load(); + bSuccess = true; + } + + SetChanged(); + NotifyObservers(ObservableMessageButtonMapsChanged); + + return bSuccess; +} + +bool CInputManager::ReloadKeymaps() +{ + return LoadKeymaps(); +} + +void CInputManager::ClearKeymaps() +{ + m_buttonTranslator->Clear(); + m_irTranslator->Clear(); + + SetChanged(); + NotifyObservers(ObservableMessageButtonMapsChanged); +} + +void CInputManager::AddKeymap(const std::string &keymap) +{ + if (m_buttonTranslator->AddDevice(keymap)) + { + SetChanged(); + NotifyObservers(ObservableMessageButtonMapsChanged); + } +} + +void CInputManager::RemoveKeymap(const std::string &keymap) +{ + if (m_buttonTranslator->RemoveDevice(keymap)) + { + SetChanged(); + NotifyObservers(ObservableMessageButtonMapsChanged); + } +} + +CAction CInputManager::GetAction(int window, const CKey &key, bool fallback /* = true */) +{ + return m_buttonTranslator->GetAction(window, key, fallback); +} + +CAction CInputManager::GetGlobalAction(const CKey &key) +{ + return m_buttonTranslator->GetGlobalAction(key); +} + +bool CInputManager::TranslateCustomControllerString(int windowId, const std::string& controllerName, int buttonId, int& action, std::string& strAction) +{ + return m_customControllerTranslator->TranslateCustomControllerString(windowId, controllerName, buttonId, action, strAction); +} + +bool CInputManager::TranslateTouchAction(int windowId, int touchAction, int touchPointers, int &action, std::string &actionString) +{ + return m_touchTranslator->TranslateTouchAction(windowId, touchAction, touchPointers, action, actionString); +} + +std::vector> CInputManager::GetJoystickKeymaps() const +{ + return m_joystickTranslator->GetJoystickKeymaps(); +} + +int CInputManager::TranslateLircRemoteString(const std::string &szDevice, const std::string &szButton) +{ + return m_irTranslator->TranslateButton(szDevice, szButton); +} + void CInputManager::RegisterKeyboardHandler(KEYBOARD::IKeyboardHandler* handler) { if (std::find(m_keyboardHandlers.begin(), m_keyboardHandlers.end(), handler) == m_keyboardHandlers.end()) diff --git a/xbmc/input/InputManager.h b/xbmc/input/InputManager.h index 3f1871345a3c5..2fded224a90df 100644 --- a/xbmc/input/InputManager.h +++ b/xbmc/input/InputManager.h @@ -31,14 +31,25 @@ #include "input/windows/IRServerSuite.h" #endif +#include "Action.h" #include "windowing/XBMC_events.h" +#include "input/keyboard/IKeyboardInputProvider.h" +#include "input/mouse/IMouseInputProvider.h" #include "input/KeyboardStat.h" #include "input/MouseStat.h" #include "interfaces/IActionListener.h" #include "settings/lib/ISettingCallback.h" #include "threads/CriticalSection.h" +#include "utils/Observer.h" +class CButtonTranslator; +class CCustomControllerTranslator; +class CIRTranslator; +class CJoystickMapper; class CKey; +class CTouchTranslator; +class IKeymapEnvironment; +class IWindowKeymap; namespace KODI { @@ -69,19 +80,17 @@ namespace MOUSE * \copydoc mouse */ class CInputManager : public ISettingCallback, - public IActionListener + public IActionListener, + public KODI::KEYBOARD::IKeyboardInputProvider, + public KODI::MOUSE::IMouseInputProvider, + public Observable { -private: +public: CInputManager(); - CInputManager(const CInputManager&); - CInputManager const& operator=(CInputManager const&); + CInputManager(const CInputManager&) = delete; + CInputManager const& operator=(CInputManager const&) = delete; ~CInputManager() override; -public: - /*! \brief static method to get the current instance of the class. Creates a new instance the first time it's called. - */ - static CInputManager& GetInstance(); - /*! \brief decode an input event from remote controls. \param windowId Currently active window @@ -124,6 +133,11 @@ class CInputManager : public ISettingCallback, */ void InitializeInputs(); + /*! + * \brief Deinitialize input and keymaps + */ + void Deinitialize(); + /*! \brief Enable or disable the joystick * * \param enabled true to enable joystick, false to disable @@ -235,37 +249,54 @@ class CInputManager : public ISettingCallback, */ int ExecuteBuiltin(const std::string& execute, const std::vector& params); - // implementation of ISettingCallback - void OnSettingChanged(std::shared_ptr setting) override; + // Button translation + bool LoadKeymaps(); + bool ReloadKeymaps(); + void ClearKeymaps(); + void AddKeymap(const std::string &keymap); + void RemoveKeymap(const std::string &keymap); - // implementation of IActionListener - bool OnAction(const CAction& action) override; + const IKeymapEnvironment *KeymapEnvironment() const { return m_keymapEnvironment.get(); } - /*! \brief Registers a handler to be called on keyboard input (e.g a game client). + /*! \brief Obtain the action configured for a given window and key * - * \param handler The handler to call on keyboard input. - */ - void RegisterKeyboardHandler(KODI::KEYBOARD::IKeyboardHandler* handler); - - /*! \brief Unregisters handler from keyboard input. + * \param window the window id + * \param key the key to query the action for + * \param fallback if no action is directly configured for the given window, obtain the action from fallback window, if exists or from global config as last resort * - * \param[in] handler The handler to unregister from keyboard input. + * \return the action matching the key */ - void UnregisterKeyboardHandler(KODI::KEYBOARD::IKeyboardHandler* handler); + CAction GetAction(int window, const CKey &key, bool fallback = true); - /*! \brief Registers a handler to be called on mouse input (e.g a game client). + /*! \brief Obtain the global action configured for a given key * - * \param handler The handler to call on mouse input. - * \return[in] The controller ID that serves as a context for incoming events. - * \sa IMouseButtonMap - */ - std::string RegisterMouseHandler(KODI::MOUSE::IMouseInputHandler* handler); - - /*! \brief Unregisters handler from mouse input. + * \param key the key to query the action for * - * \param[in] handler The handler to unregister from mouse input. + * \return the global action */ - void UnregisterMouseHandler(KODI::MOUSE::IMouseInputHandler* handler); + CAction GetGlobalAction(const CKey &key); + + bool TranslateCustomControllerString(int windowId, const std::string& controllerName, int buttonId, int& action, std::string& strAction); + + bool TranslateTouchAction(int windowId, int touchAction, int touchPointers, int &action, std::string &actionString); + + std::vector> GetJoystickKeymaps() const; + + int TranslateLircRemoteString(const std::string &szDevice, const std::string &szButton); + + // implementation of ISettingCallback + virtual void OnSettingChanged(std::shared_ptr setting) override; + + // implementation of IActionListener + virtual bool OnAction(const CAction& action) override; + + // implementation of IKeyboardInputProvider + virtual void RegisterKeyboardHandler(KODI::KEYBOARD::IKeyboardHandler* handler) override; + virtual void UnregisterKeyboardHandler(KODI::KEYBOARD::IKeyboardHandler* handler) override; + + // implementation of IMouseInputProvider + virtual std::string RegisterMouseHandler(KODI::MOUSE::IMouseInputHandler* handler) override; + virtual void UnregisterMouseHandler(KODI::MOUSE::IMouseInputHandler* handler) override; private: @@ -325,6 +356,14 @@ class CInputManager : public ISettingCallback, std::vector m_queuedActions; CCriticalSection m_actionMutex; + // Button translation + std::unique_ptr m_keymapEnvironment; + std::unique_ptr m_buttonTranslator; + std::unique_ptr m_irTranslator; + std::unique_ptr m_customControllerTranslator; + std::unique_ptr m_touchTranslator; + std::unique_ptr m_joystickTranslator; + std::vector m_keyboardHandlers; struct MouseHandlerHandle diff --git a/xbmc/input/JoystickMapper.cpp b/xbmc/input/JoystickMapper.cpp new file mode 100644 index 0000000000000..0be08bd3b783f --- /dev/null +++ b/xbmc/input/JoystickMapper.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "JoystickMapper.h" +#include "ActionIDs.h" +#include "ActionTranslator.h" +#include "input/joysticks/JoystickIDs.h" +#include "input/joysticks/JoystickTranslator.h" +#include "input/joysticks/JoystickUtils.h" +#include "input/WindowKeymap.h" +#include "utils/StringUtils.h" +#include "utils/XBMCTinyXML.h" + +#include +#include +#include + +using namespace KODI; + +#define JOYSTICK_XML_NODE_PROFILE "profile" +#define JOYSTICK_XML_ATTR_DIRECTION "direction" +#define JOYSTICK_XML_ATTR_HOLDTIME "holdtime" +#define JOYSTICK_XML_ATTR_HOTKEY "hotkey" + +CJoystickMapper::~CJoystickMapper() +{ +} + +void CJoystickMapper::MapActions(int windowID, const TiXmlNode *pDevice) +{ + std::string controllerId; + DeserializeJoystickNode(pDevice, controllerId); + + const TiXmlElement *pButton = pDevice->FirstChildElement(); + while (pButton != nullptr) + { + std::string feature; + JOYSTICK::ANALOG_STICK_DIRECTION dir; + unsigned int holdtimeMs; + std::set hotkeys; + std::string actionString; + if (DeserializeButton(pButton, feature, dir, holdtimeMs, hotkeys, actionString)) + { + // Update Controller IDs + if (std::find(m_controllerIds.begin(), m_controllerIds.end(), controllerId) == m_controllerIds.end()) + m_controllerIds.emplace_back(controllerId); + + // Find/create keymap + auto &keymap = m_joystickKeymaps[controllerId]; + if (!keymap) + keymap.reset(new CWindowKeymap(controllerId)); + + // Update keymap + unsigned int actionId = ACTION_NONE; + if (CActionTranslator::TranslateString(actionString, actionId)) + { + JOYSTICK::KeymapAction action = { + actionId, + std::move(actionString), + holdtimeMs, + std::move(hotkeys), + }; + keymap->MapAction(windowID, JOYSTICK::CJoystickUtils::MakeKeyName(feature, dir), std::move(action)); + } + } + pButton = pButton->NextSiblingElement(); + } +} + +void CJoystickMapper::Clear() +{ + m_joystickKeymaps.clear(); + m_controllerIds.clear(); +} + +std::vector> CJoystickMapper::GetJoystickKeymaps() const +{ + std::vector> keymaps; + + for (const auto &controllerId : m_controllerIds) + { + auto it = m_joystickKeymaps.find(controllerId); + if (it != m_joystickKeymaps.end()) + keymaps.emplace_back(it->second); + } + + return keymaps; +} + +void CJoystickMapper::DeserializeJoystickNode(const TiXmlNode* pDevice, std::string &controllerId) +{ + controllerId = DEFAULT_CONTROLLER_ID; + + const TiXmlElement* deviceElem = pDevice->ToElement(); + if (deviceElem != nullptr) + deviceElem->QueryValueAttribute(JOYSTICK_XML_NODE_PROFILE, &controllerId); +} + +bool CJoystickMapper::DeserializeButton(const TiXmlElement *pButton, std::string &feature, JOYSTICK::ANALOG_STICK_DIRECTION &dir, unsigned int& holdtimeMs, std::set& hotkeys, std::string &actionStr) +{ + const char *szButton = pButton->Value(); + if (szButton != nullptr) + { + const char *szAction = pButton->FirstChild()->Value(); + if (szAction != nullptr) + { + feature = szButton; + StringUtils::ToLower(feature); + actionStr = szAction; + } + } + + if (!feature.empty() && !actionStr.empty()) + { + // Handle direction + dir = JOYSTICK::ANALOG_STICK_DIRECTION::UNKNOWN; + const char *szDirection = pButton->Attribute(JOYSTICK_XML_ATTR_DIRECTION); + if (szDirection != nullptr) + dir = JOYSTICK::CJoystickTranslator::TranslateDirection(szDirection); + + // Process holdtime parameter + holdtimeMs = 0; + std::string strHoldTime; + if (pButton->QueryValueAttribute(JOYSTICK_XML_ATTR_HOLDTIME, &strHoldTime) == TIXML_SUCCESS) + { + std::stringstream ss(std::move(strHoldTime)); + ss >> holdtimeMs; + } + + // Process hotkeys + hotkeys.clear(); + std::string strHotkeys; + if (pButton->QueryValueAttribute(JOYSTICK_XML_ATTR_HOTKEY, &strHotkeys) == TIXML_SUCCESS) + { + std::vector vecHotkeys = StringUtils::Split(strHotkeys, ","); + for (auto& hotkey : vecHotkeys) + hotkeys.insert(std::move(hotkey)); + } + + return true; + } + + return false; +} diff --git a/xbmc/input/JoystickMapper.h b/xbmc/input/JoystickMapper.h new file mode 100644 index 0000000000000..a65d483d44836 --- /dev/null +++ b/xbmc/input/JoystickMapper.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "IButtonMapper.h" +#include "input/joysticks/JoystickTypes.h" + +#include +#include +#include +#include +#include + +class IWindowKeymap; +class TiXmlElement; +class TiXmlNode; + +class CJoystickMapper : public IButtonMapper +{ +public: + CJoystickMapper() = default; + virtual ~CJoystickMapper(); + + // implementation of IButtonMapper + virtual void MapActions(int windowID, const TiXmlNode *pDevice) override; + virtual void Clear() override; + + std::vector> GetJoystickKeymaps() const; + +private: + void DeserializeJoystickNode(const TiXmlNode* pDevice, std::string &controllerId); + bool DeserializeButton(const TiXmlElement *pButton, std::string &feature, KODI::JOYSTICK::ANALOG_STICK_DIRECTION &dir, unsigned int& holdtimeMs, std::set& hotkeys, std::string &actionStr); + + using ControllerID = std::string; + std::map> m_joystickKeymaps; + + std::vector m_controllerIds; +}; diff --git a/xbmc/input/JoystickTranslator.cpp b/xbmc/input/JoystickTranslator.cpp deleted file mode 100644 index 212b171c44f1e..0000000000000 --- a/xbmc/input/JoystickTranslator.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2017 Team Kodi - * http://kodi.tv - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; see the file COPYING. If not, see - * . - * - */ - -#include "JoystickTranslator.h" -#include "Key.h" -#include "input/joysticks/JoystickIDs.h" -#include "utils/log.h" -#include "utils/StringUtils.h" -#include "utils/XBMCTinyXML.h" - -#include - -uint32_t CJoystickTranslator::TranslateButton(const TiXmlNode* pDevice, const TiXmlElement *pButton, unsigned int& holdtimeMs) -{ - std::string controllerId = DEFAULT_CONTROLLER_ID; - - const TiXmlElement* deviceElem = pDevice->ToElement(); - if (deviceElem != nullptr) - deviceElem->QueryValueAttribute("profile", &controllerId); - - holdtimeMs = 0; - - const char *szButton = pButton->Value(); - if (szButton == nullptr) - return 0; - - std::string strButton = szButton; - StringUtils::ToLower(strButton); - - uint32_t buttonCode = 0; - if (controllerId == DEFAULT_CONTROLLER_ID) - { - if (strButton == "a") buttonCode = KEY_JOYSTICK_BUTTON_A; - else if (strButton == "b") buttonCode = KEY_JOYSTICK_BUTTON_B; - else if (strButton == "x") buttonCode = KEY_JOYSTICK_BUTTON_X; - else if (strButton == "y") buttonCode = KEY_JOYSTICK_BUTTON_Y; - else if (strButton == "start") buttonCode = KEY_JOYSTICK_BUTTON_START; - else if (strButton == "back") buttonCode = KEY_JOYSTICK_BUTTON_BACK; - else if (strButton == "left") buttonCode = KEY_JOYSTICK_BUTTON_DPAD_LEFT; - else if (strButton == "right") buttonCode = KEY_JOYSTICK_BUTTON_DPAD_RIGHT; - else if (strButton == "up") buttonCode = KEY_JOYSTICK_BUTTON_DPAD_UP; - else if (strButton == "down") buttonCode = KEY_JOYSTICK_BUTTON_DPAD_DOWN; - else if (strButton == "leftthumb") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_STICK_BUTTON; - else if (strButton == "rightthumb") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_STICK_BUTTON; - else if (strButton == "leftstickup") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_UP; - else if (strButton == "leftstickdown") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_DOWN; - else if (strButton == "leftstickleft") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_LEFT; - else if (strButton == "leftstickright") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_RIGHT; - else if (strButton == "rightstickup") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_UP; - else if (strButton == "rightstickdown") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_DOWN; - else if (strButton == "rightstickleft") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_LEFT; - else if (strButton == "rightstickright") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_RIGHT; - else if (strButton == "lefttrigger") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_TRIGGER; - else if (strButton == "righttrigger") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_TRIGGER; - else if (strButton == "leftbumper") buttonCode = KEY_JOYSTICK_BUTTON_LEFT_SHOULDER; - else if (strButton == "rightbumper") buttonCode = KEY_JOYSTICK_BUTTON_RIGHT_SHOULDER; - else if (strButton == "guide") buttonCode = KEY_JOYSTICK_BUTTON_GUIDE; - } - else if (controllerId == DEFAULT_REMOTE_ID) - { - if (strButton == "ok") buttonCode = KEY_REMOTE_BUTTON_OK; - else if (strButton == "back") buttonCode = KEY_REMOTE_BUTTON_BACK; - else if (strButton == "up") buttonCode = KEY_REMOTE_BUTTON_UP; - else if (strButton == "down") buttonCode = KEY_REMOTE_BUTTON_DOWN; - else if (strButton == "left") buttonCode = KEY_REMOTE_BUTTON_LEFT; - else if (strButton == "right") buttonCode = KEY_REMOTE_BUTTON_RIGHT; - else if (strButton == "home") buttonCode = KEY_REMOTE_BUTTON_HOME; - } - - if (buttonCode == 0) - { - CLog::Log(LOGERROR, "Joystick Translator: Can't find button %s for controller %s", szButton, controllerId.c_str()); - } - else - { - // Process holdtime parameter - std::string strHoldTime; - if (pButton->QueryValueAttribute("holdtime", &strHoldTime) == TIXML_SUCCESS) - { - std::stringstream ss(std::move(strHoldTime)); - ss >> holdtimeMs; - } - } - - return buttonCode; -} diff --git a/xbmc/input/Key.h b/xbmc/input/Key.h index 83892ca9f555f..916b6895532bb 100644 --- a/xbmc/input/Key.h +++ b/xbmc/input/Key.h @@ -76,46 +76,6 @@ #define KEY_BUTTON_LEFT_THUMB_STICK_LEFT 282 #define KEY_BUTTON_LEFT_THUMB_STICK_RIGHT 283 -/* - * joystick.xml keys for remote control emulation - */ -#define KEY_REMOTE_BUTTON_OK 310 -#define KEY_REMOTE_BUTTON_BACK 311 -#define KEY_REMOTE_BUTTON_UP 312 -#define KEY_REMOTE_BUTTON_DOWN 313 -#define KEY_REMOTE_BUTTON_RIGHT 314 -#define KEY_REMOTE_BUTTON_LEFT 315 -#define KEY_REMOTE_BUTTON_HOME 316 - -/* - * joystick.xml keys based on Xbox 360 controller - */ -#define KEY_JOYSTICK_BUTTON_A 284 -#define KEY_JOYSTICK_BUTTON_B 285 -#define KEY_JOYSTICK_BUTTON_X 286 -#define KEY_JOYSTICK_BUTTON_Y 287 -#define KEY_JOYSTICK_BUTTON_LEFT_SHOULDER 288 -#define KEY_JOYSTICK_BUTTON_RIGHT_SHOULDER 289 -#define KEY_JOYSTICK_BUTTON_LEFT_TRIGGER 290 -#define KEY_JOYSTICK_BUTTON_RIGHT_TRIGGER 291 -#define KEY_JOYSTICK_BUTTON_LEFT_STICK_BUTTON 292 -#define KEY_JOYSTICK_BUTTON_RIGHT_STICK_BUTTON 293 -#define KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_UP 294 -#define KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_DOWN 295 -#define KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_LEFT 296 -#define KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_RIGHT 297 -#define KEY_JOYSTICK_BUTTON_DPAD_UP 298 -#define KEY_JOYSTICK_BUTTON_DPAD_DOWN 299 -#define KEY_JOYSTICK_BUTTON_DPAD_LEFT 300 -#define KEY_JOYSTICK_BUTTON_DPAD_RIGHT 301 -#define KEY_JOYSTICK_BUTTON_START 302 -#define KEY_JOYSTICK_BUTTON_BACK 303 -#define KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_UP 304 -#define KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_DOWN 305 -#define KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_LEFT 306 -#define KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_RIGHT 307 -#define KEY_JOYSTICK_BUTTON_GUIDE 308 - // 0xF000 -> 0xF200 is reserved for the keyboard; a keyboard press is either #define KEY_VKEY 0xF000 // a virtual key/functional key e.g. cursor left #define KEY_ASCII 0xF100 // a printable character in the range of TRUE ASCII (from 0 to 127) // FIXME make it clean and pure unicode! remove the need for KEY_ASCII diff --git a/xbmc/input/Keymap.cpp b/xbmc/input/Keymap.cpp new file mode 100644 index 0000000000000..eb30b4b56bdec --- /dev/null +++ b/xbmc/input/Keymap.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "Keymap.h" +#include "IKeymapEnvironment.h" + +using namespace KODI; + +CKeymap::CKeymap(std::shared_ptr keymap, const IKeymapEnvironment *environment) : + m_keymap(std::move(keymap)), + m_environment(environment) +{ +} + +std::string CKeymap::ControllerID() const +{ + return m_keymap->ControllerID(); +} + +const JOYSTICK::KeymapActions &CKeymap::GetActions(const std::string& keyName) const +{ + const int windowId = m_environment->GetWindowID(); + const JOYSTICK::KeymapActions& actions = m_keymap->GetActions(windowId, keyName); + if (!actions.empty()) + return actions; + + const int fallbackWindowId = m_environment->GetFallthrough(windowId); + if (fallbackWindowId >= 0) + { + const JOYSTICK::KeymapActions& fallbackActions = m_keymap->GetActions(fallbackWindowId, keyName); + if (!fallbackActions.empty()) + return fallbackActions; + } + + if (m_environment->UseGlobalFallthrough()) + { + const JOYSTICK::KeymapActions& globalActions = m_keymap->GetActions(-1, keyName); + if (!globalActions.empty()) + return globalActions; + } + + static const JOYSTICK::KeymapActions empty; + return empty; +} diff --git a/xbmc/input/Keymap.h b/xbmc/input/Keymap.h new file mode 100644 index 0000000000000..1610a72717a2c --- /dev/null +++ b/xbmc/input/Keymap.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "IKeymap.h" + +#include + +class IKeymapEnvironment; + +class CKeymap : public IKeymap +{ +public: + CKeymap(std::shared_ptr keymap, const IKeymapEnvironment *environment); + + // implementation of IKeymap + virtual std::string ControllerID() const override ; + virtual const IKeymapEnvironment *Environment() const override { return m_environment; } + const KODI::JOYSTICK::KeymapActions &GetActions(const std::string& keyName) const override; + +private: + // Construction parameters + const std::shared_ptr m_keymap; + const IKeymapEnvironment *const m_environment; +}; diff --git a/xbmc/games/ports/PortInput.cpp b/xbmc/input/KeymapEnvironment.cpp similarity index 65% rename from xbmc/games/ports/PortInput.cpp rename to xbmc/input/KeymapEnvironment.cpp index 3c12f8b2a8c06..5c80243b6834e 100644 --- a/xbmc/games/ports/PortInput.cpp +++ b/xbmc/input/KeymapEnvironment.cpp @@ -18,24 +18,17 @@ * */ -#include "PortInput.h" -#include "games/addons/GameClient.h" -#include "guilib/WindowIDs.h" +#include "KeymapEnvironment.h" +#include "WindowTranslator.h" +#include "guilib/GUIWindowManager.h" +#include "Application.h" -using namespace KODI; -using namespace GAME; - -CPortInput::CPortInput(CGameClient &gameClient) : - m_gameClient(gameClient) -{ -} - -bool CPortInput::AcceptsInput(const std::string &feature) const +int CKeymapEnvironment::GetWindowID() const { - return m_gameClient.AcceptsInput(); + return g_windowManager.GetActiveWindowID(); } -int CPortInput::GetWindowID() const +int CKeymapEnvironment::GetFallthrough(int windowId) const { - return WINDOW_FULLSCREEN_GAME; + return CWindowTranslator::GetFallbackWindow(windowId); } diff --git a/xbmc/input/joysticks/DefaultRemote.h b/xbmc/input/KeymapEnvironment.h similarity index 62% rename from xbmc/input/joysticks/DefaultRemote.h rename to xbmc/input/KeymapEnvironment.h index 915e5bdea2d61..252a8802cabc9 100644 --- a/xbmc/input/joysticks/DefaultRemote.h +++ b/xbmc/input/KeymapEnvironment.h @@ -19,23 +19,15 @@ */ #pragma once -#include "DefaultJoystick.h" +#include "IKeymapEnvironment.h" -namespace KODI +class CKeymapEnvironment : public IKeymapEnvironment { -namespace JOYSTICK -{ - class CDefaultRemote : public CDefaultJoystick - { - public: - CDefaultRemote(void); - - virtual ~CDefaultRemote(void) = default; +public: + virtual ~CKeymapEnvironment() = default; - protected: - // implementation of CDefaultJoystick - virtual std::string GetControllerID() const override; - virtual unsigned int GetKeyID(const FeatureName& feature, ANALOG_STICK_DIRECTION dir = ANALOG_STICK_DIRECTION::UNKNOWN) const override; - }; -} -} + // implementation of IKeymapEnvironment + virtual int GetWindowID() const override; + virtual int GetFallthrough(int windowId) const override; + virtual bool UseGlobalFallthrough() const { return true; } +}; diff --git a/xbmc/input/TouchTranslator.cpp b/xbmc/input/TouchTranslator.cpp index 7c8a0d3132fda..8ee500dab259e 100644 --- a/xbmc/input/TouchTranslator.cpp +++ b/xbmc/input/TouchTranslator.cpp @@ -21,6 +21,7 @@ #include "TouchTranslator.h" #include "ActionIDs.h" #include "ActionTranslator.h" +#include "WindowTranslator.h" //! @todo #include "utils/log.h" #include "utils/StringUtils.h" #include "utils/XBMCTinyXML.h" @@ -95,6 +96,27 @@ void CTouchTranslator::Clear() m_touchMap.clear(); } +bool CTouchTranslator::TranslateTouchAction(int window, int touchAction, int touchPointers, int &action, std::string &actionString) +{ + if (touchAction < 0) + return false; + + unsigned int actionId = ACTION_NONE; + + if (!TranslateAction(window, touchAction, touchPointers, actionId, actionString)) + { + int fallbackWindow = CWindowTranslator::GetFallbackWindow(window); + if (fallbackWindow > -1) + TranslateAction(fallbackWindow, touchAction, touchPointers, actionId, actionString); + + if (actionId == ACTION_NONE) + TranslateAction(-1, touchAction, touchPointers, actionId, actionString); + } + + action = actionId; + return actionId != ACTION_NONE; +} + bool CTouchTranslator::TranslateAction(int window, unsigned int touchCommand, int touchPointers, unsigned int &actionId, std::string &actionString) { unsigned int touchActionKey = GetTouchActionKey(touchCommand, touchPointers); diff --git a/xbmc/input/TouchTranslator.h b/xbmc/input/TouchTranslator.h index 692be6681c501..1584319be5667 100644 --- a/xbmc/input/TouchTranslator.h +++ b/xbmc/input/TouchTranslator.h @@ -19,24 +19,27 @@ */ #pragma once +#include "IButtonMapper.h" + #include #include class TiXmlElement; -class TiXmlNode; -class CTouchTranslator +class CTouchTranslator : public IButtonMapper { public: CTouchTranslator() = default; - void MapActions(int windowID, const TiXmlNode *pTouch); + // implementation of IButtonMapper + virtual void MapActions(int windowID, const TiXmlNode *bDevice) override; + virtual void Clear() override; - void Clear(); + bool TranslateTouchAction(int window, int touchAction, int touchPointers, int &action, std::string &actionString); +private: bool TranslateAction(int window, unsigned int touchCommand, int touchPointers, unsigned int &actionId, std::string &actionString); -private: struct CTouchAction { unsigned int actionId; diff --git a/xbmc/input/WindowKeymap.cpp b/xbmc/input/WindowKeymap.cpp new file mode 100644 index 0000000000000..00e19279f88de --- /dev/null +++ b/xbmc/input/WindowKeymap.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "WindowKeymap.h" + +using namespace KODI; + +CWindowKeymap::CWindowKeymap(const std::string &controllerId) : + m_controllerId(controllerId) +{ +} + +void CWindowKeymap::MapAction(int windowId, const std::string &keyName, JOYSTICK::KeymapAction action) +{ + m_windowKeymap[windowId][keyName].insert(std::move(action)); +} + +const JOYSTICK::KeymapActions &CWindowKeymap::GetActions(int windowId, const std::string& keyName) const +{ + auto it = m_windowKeymap.find(windowId); + if (it != m_windowKeymap.end()) + { + auto& keymap = it->second; + auto it2 = keymap.find(keyName); + if (it2 != keymap.end()) + return it2->second; + } + + static const JOYSTICK::KeymapActions empty; + return empty; +} diff --git a/xbmc/input/WindowKeymap.h b/xbmc/input/WindowKeymap.h new file mode 100644 index 0000000000000..ff148438b183c --- /dev/null +++ b/xbmc/input/WindowKeymap.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "IKeymap.h" +#include "input/joysticks/JoystickTypes.h" + +#include +#include + +class CWindowKeymap : public IWindowKeymap +{ +public: + CWindowKeymap(const std::string &controllerId); + + // implementation of IWindowKeymap + virtual std::string ControllerID() const override { return m_controllerId; } + virtual void MapAction(int windowId, const std::string &keyName, KODI::JOYSTICK::KeymapAction action) override; + virtual const KODI::JOYSTICK::KeymapActions &GetActions(int windowId, const std::string& keyName) const override; + +private: + // Construction parameter + const std::string m_controllerId; + + using KeyName = std::string; + using Keymap = std::map; + + using WindowID = int; + using WindowMap = std::map; + + WindowMap m_windowKeymap; +}; diff --git a/xbmc/input/joysticks/CMakeLists.txt b/xbmc/input/joysticks/CMakeLists.txt index 2fd7be0c797fa..1fee11a0312e8 100644 --- a/xbmc/input/joysticks/CMakeLists.txt +++ b/xbmc/input/joysticks/CMakeLists.txt @@ -1,20 +1,15 @@ set(SOURCES DeadzoneFilter.cpp - DefaultController.cpp - DefaultJoystick.cpp - DefaultRemote.cpp DriverPrimitive.cpp JoystickEasterEgg.cpp JoystickMonitor.cpp JoystickTranslator.cpp - KeymapHandler.cpp + JoystickUtils.cpp RumbleGenerator.cpp) -set(HEADERS DeadzoneFilter.h - DefaultController.h - DefaultJoystick.h - DefaultRemote.h +set(HEADERS interfaces/IKeyHandler.h + interfaces/IKeymapHandler.h + DeadzoneFilter.h DriverPrimitive.h - IActionMap.h IButtonMap.h IButtonMapCallback.h IButtonMapper.h @@ -24,14 +19,12 @@ set(HEADERS DeadzoneFilter.h IInputHandler.h IInputProvider.h IInputReceiver.h - IKeymapHandler.h JoystickEasterEgg.h JoystickIDs.h JoystickMonitor.h JoystickTranslator.h JoystickTypes.h JoystickUtils.h - KeymapHandler.h RumbleGenerator.h) core_add_library(input_joystick) diff --git a/xbmc/input/joysticks/DefaultController.cpp b/xbmc/input/joysticks/DefaultController.cpp deleted file mode 100644 index 19ae16f4ca102..0000000000000 --- a/xbmc/input/joysticks/DefaultController.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2017 Team Kodi - * http://kodi.tv - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; see the file COPYING. If not, see - * . - * - */ - -#include "DefaultController.h" -#include "JoystickEasterEgg.h" -#include "JoystickIDs.h" -#include "input/Key.h" - -using namespace KODI; -using namespace JOYSTICK; - -CDefaultController::CDefaultController(void) : - m_rumbleGenerator(GetControllerID()) -{ - // initialize CDefaultJoystick - m_easterEgg.reset(new CJoystickEasterEgg(GetControllerID())); -} - -std::string CDefaultController::GetControllerID(void) const -{ - return DEFAULT_CONTROLLER_ID; -} - -unsigned int CDefaultController::GetKeyID(const FeatureName& feature, ANALOG_STICK_DIRECTION dir /* = ANALOG_STICK_DIRECTION::UNKNOWN */) const -{ - if (feature == "a") return KEY_JOYSTICK_BUTTON_A; - else if (feature == "b") return KEY_JOYSTICK_BUTTON_B; - else if (feature == "x") return KEY_JOYSTICK_BUTTON_X; - else if (feature == "y") return KEY_JOYSTICK_BUTTON_Y; - else if (feature == "start") return KEY_JOYSTICK_BUTTON_START; - else if (feature == "back") return KEY_JOYSTICK_BUTTON_BACK; - else if (feature == "guide") return KEY_JOYSTICK_BUTTON_GUIDE; - else if (feature == "leftbumper") return KEY_JOYSTICK_BUTTON_LEFT_SHOULDER; - else if (feature == "rightbumper") return KEY_JOYSTICK_BUTTON_RIGHT_SHOULDER; - else if (feature == "leftthumb") return KEY_JOYSTICK_BUTTON_LEFT_STICK_BUTTON; - else if (feature == "rightthumb") return KEY_JOYSTICK_BUTTON_RIGHT_STICK_BUTTON; - else if (feature == "up") return KEY_JOYSTICK_BUTTON_DPAD_UP; - else if (feature == "down") return KEY_JOYSTICK_BUTTON_DPAD_DOWN; - else if (feature == "right") return KEY_JOYSTICK_BUTTON_DPAD_RIGHT; - else if (feature == "left") return KEY_JOYSTICK_BUTTON_DPAD_LEFT; - else if (feature == "lefttrigger") return KEY_JOYSTICK_BUTTON_LEFT_TRIGGER; - else if (feature == "righttrigger") return KEY_JOYSTICK_BUTTON_RIGHT_TRIGGER; - else if (feature == "leftstick") - { - switch (dir) - { - case ANALOG_STICK_DIRECTION::UP: return KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_UP; - case ANALOG_STICK_DIRECTION::DOWN: return KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_DOWN; - case ANALOG_STICK_DIRECTION::RIGHT: return KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_RIGHT; - case ANALOG_STICK_DIRECTION::LEFT: return KEY_JOYSTICK_BUTTON_LEFT_THUMB_STICK_LEFT; - default: - break; - } - } - else if (feature == "rightstick") - { - switch (dir) - { - case ANALOG_STICK_DIRECTION::UP: return KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_UP; - case ANALOG_STICK_DIRECTION::DOWN: return KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_DOWN; - case ANALOG_STICK_DIRECTION::RIGHT: return KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_RIGHT; - case ANALOG_STICK_DIRECTION::LEFT: return KEY_JOYSTICK_BUTTON_RIGHT_THUMB_STICK_LEFT; - default: - break; - } - } - else if (feature == "accelerometer") return 0; //! @todo implement - - return 0; -} diff --git a/xbmc/input/joysticks/DefaultController.h b/xbmc/input/joysticks/DefaultController.h deleted file mode 100644 index c54813027b0fd..0000000000000 --- a/xbmc/input/joysticks/DefaultController.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2017 Team Kodi - * http://kodi.tv - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; see the file COPYING. If not, see - * . - * - */ -#pragma once - -#include "DefaultJoystick.h" -#include "RumbleGenerator.h" - -namespace KODI -{ -namespace JOYSTICK -{ - class CDefaultController : public CDefaultJoystick - { - public: - CDefaultController(void); - - virtual ~CDefaultController(void) = default; - - // Forward rumble commands to rumble generator - void NotifyUser(void) { m_rumbleGenerator.NotifyUser(InputReceiver()); } - bool TestRumble(void) { return m_rumbleGenerator.DoTest(InputReceiver()); } - void AbortRumble() { m_rumbleGenerator.AbortRumble(); } - - protected: - // implementation of CDefaultJoystick - virtual std::string GetControllerID() const; - virtual unsigned int GetKeyID(const FeatureName& feature, ANALOG_STICK_DIRECTION dir = ANALOG_STICK_DIRECTION::UNKNOWN) const override; - - private: - // Rumble functionality - CRumbleGenerator m_rumbleGenerator; - }; -} -} diff --git a/xbmc/input/joysticks/DefaultJoystick.cpp b/xbmc/input/joysticks/DefaultJoystick.cpp deleted file mode 100644 index a6c83be64bf70..0000000000000 --- a/xbmc/input/joysticks/DefaultJoystick.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2014-2017 Team Kodi - * http://kodi.tv - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; see the file COPYING. If not, see - * . - * - */ - -#include "DefaultJoystick.h" -#include "KeymapHandler.h" -#include "JoystickEasterEgg.h" -#include "JoystickIDs.h" -#include "JoystickTranslator.h" -#include "guilib/GUIWindowManager.h" -#include "input/InputManager.h" -#include "input/Key.h" -#include "Application.h" - -#include -#include -#include - -#define ANALOG_DIGITAL_THRESHOLD 0.5f - -using namespace KODI; -using namespace JOYSTICK; - -CDefaultJoystick::CDefaultJoystick(void) : - m_handler(new CKeymapHandler(&CInputManager::GetInstance())) -{ -} - -CDefaultJoystick::~CDefaultJoystick(void) -{ - delete m_handler; -} - -std::string CDefaultJoystick::ControllerID(void) const -{ - return GetControllerID(); -} - -bool CDefaultJoystick::HasFeature(const FeatureName& feature) const -{ - if (GetKeyID(feature) != 0) - return true; - - // Try analog stick directions - if (GetKeyID(feature, ANALOG_STICK_DIRECTION::UP) != 0 || - GetKeyID(feature, ANALOG_STICK_DIRECTION::DOWN) != 0 || - GetKeyID(feature, ANALOG_STICK_DIRECTION::RIGHT) != 0 || - GetKeyID(feature, ANALOG_STICK_DIRECTION::LEFT) != 0) - return true; - - return false; -} - -bool CDefaultJoystick::AcceptsInput(const FeatureName &feature) const -{ - return true; -} - -unsigned int CDefaultJoystick::GetDelayMs(const FeatureName& feature) const -{ - return m_handler->GetHoldTimeMs(GetKeyID(feature), GetWindowID(), GetFallthrough()); -} - -bool CDefaultJoystick::OnButtonPress(const FeatureName& feature, bool bPressed) -{ - if (bPressed && m_easterEgg && m_easterEgg->OnButtonPress(feature)) - return true; - - const unsigned int keyId = GetKeyID(feature); - const int windowId = GetWindowID(); - const bool bFallthrough = GetFallthrough(); - - m_handler->OnDigitalKey(keyId, windowId, bFallthrough, bPressed); - return true; -} - -void CDefaultJoystick::OnButtonHold(const FeatureName& feature, unsigned int holdTimeMs) -{ - const unsigned int keyId = GetKeyID(feature); - const int windowId = GetWindowID(); - const bool bFallthrough = GetFallthrough(); - - m_handler->OnDigitalKey(keyId, windowId, bFallthrough, true, holdTimeMs); -} - -bool CDefaultJoystick::OnButtonMotion(const FeatureName& feature, float magnitude, unsigned int motionTimeMs) -{ - const unsigned int keyId = GetKeyID(feature); - const int windowId = GetWindowID(); - const bool bFallthrough = GetFallthrough(); - - m_handler->OnAnalogKey(keyId, windowId, bFallthrough, magnitude, motionTimeMs); - return true; -} - -bool CDefaultJoystick::OnAnalogStickMotion(const FeatureName& feature, float x, float y, unsigned int motionTimeMs) -{ - bool bHandled = false; - - // Calculate the direction of the stick's position - const ANALOG_STICK_DIRECTION analogStickDir = CJoystickTranslator::VectorToAnalogStickDirection(x, y); - - // Calculate the magnitude projected onto that direction - const float magnitude = std::max(std::abs(x), std::abs(y)); - - // Deactivate directions in which the stick is not pointing first - for (ANALOG_STICK_DIRECTION dir : GetDirections()) - { - if (dir != analogStickDir) - DeactivateDirection(feature, dir); - } - - // Now activate direction the analog stick is pointing - if (magnitude != 0.0f) - bHandled = ActivateDirection(feature, magnitude, analogStickDir, motionTimeMs); - - return bHandled; -} - -bool CDefaultJoystick::OnAccelerometerMotion(const FeatureName& feature, float x, float y, float z) -{ - return false; //! @todo implement -} - -unsigned int CDefaultJoystick::GetActionID(const FeatureName& feature) -{ - const unsigned int keyId = GetKeyID(feature); - const int windowId = GetWindowID(); - const bool bFallthrough = GetFallthrough(); - - if (keyId > 0) - return m_handler->GetActionID(keyId, windowId, bFallthrough); - - return ACTION_NONE; -} - -int CDefaultJoystick::GetWindowID() const -{ - return g_windowManager.GetActiveWindowID(); -} - -bool CDefaultJoystick::ActivateDirection(const FeatureName& feature, float magnitude, ANALOG_STICK_DIRECTION dir, unsigned int motionTimeMs) -{ - bool bHandled = false; - - // Calculate the button key ID and input type for the analog stick's direction - const unsigned int keyId = GetKeyID(feature, dir); - const int windowId = GetWindowID(); - const bool bFallthrough = GetFallthrough(); - - m_handler->OnAnalogKey(keyId, windowId, bFallthrough, magnitude, motionTimeMs); - bHandled = true; - - if (bHandled) - m_currentDirections[feature] = dir; - - return bHandled; -} - -void CDefaultJoystick::DeactivateDirection(const FeatureName& feature, ANALOG_STICK_DIRECTION dir) -{ - if (m_currentDirections[feature] == dir) - { - // Calculate the button key ID and input type for this direction - const unsigned int keyId = GetKeyID(feature, dir); - const int windowId = GetWindowID(); - const bool bFallthrough = GetFallthrough(); - - m_handler->OnAnalogKey(keyId, windowId, bFallthrough, 0.0f, 0); - - m_currentDirections[feature] = ANALOG_STICK_DIRECTION::UNKNOWN; - } -} - -const std::vector& CDefaultJoystick::GetDirections(void) -{ - static std::vector directions; - if (directions.empty()) - { - directions.push_back(ANALOG_STICK_DIRECTION::UP); - directions.push_back(ANALOG_STICK_DIRECTION::DOWN); - directions.push_back(ANALOG_STICK_DIRECTION::RIGHT); - directions.push_back(ANALOG_STICK_DIRECTION::LEFT); - } - return directions; -} diff --git a/xbmc/input/joysticks/DefaultJoystick.h b/xbmc/input/joysticks/DefaultJoystick.h deleted file mode 100644 index d1647152f8fa9..0000000000000 --- a/xbmc/input/joysticks/DefaultJoystick.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2014-2017 Team Kodi - * http://kodi.tv - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; see the file COPYING. If not, see - * . - * - */ -#pragma once - -#include "IActionMap.h" -#include "IInputHandler.h" -#include "JoystickTypes.h" - -#include -#include -#include - -namespace KODI -{ -namespace JOYSTICK -{ - class IKeymapHandler; - class IButtonSequence; - - /*! - * \ingroup joystick - * \brief Implementation of IInputHandler for Kodi input - * - * \sa IInputHandler - */ - class CDefaultJoystick : public IInputHandler, - public IActionMap - { - public: - CDefaultJoystick(void); - - virtual ~CDefaultJoystick(void); - - // implementation of IInputHandler - virtual std::string ControllerID(void) const override; - virtual bool HasFeature(const FeatureName& feature) const override; - virtual bool AcceptsInput(const FeatureName &feature) const override; - virtual unsigned int GetDelayMs(const FeatureName& feature) const override; - virtual bool OnButtonPress(const FeatureName& feature, bool bPressed) override; - virtual void OnButtonHold(const FeatureName& feature, unsigned int holdTimeMs) override; - virtual bool OnButtonMotion(const FeatureName& feature, float magnitude, unsigned int motionTimeMs) override; - virtual bool OnAnalogStickMotion(const FeatureName& feature, float x, float y, unsigned int motionTimeMs) override; - virtual bool OnAccelerometerMotion(const FeatureName& feature, float x, float y, float z) override; - - // implementation of IActionMap - virtual unsigned int GetActionID(const FeatureName& feature) override; - - protected: - /*! - * \brief Get the controller ID of this implementation - */ - virtual std::string GetControllerID() const = 0; - - /*! - * \brief Get the keymap key, as defined in Key.h, for the specified - * joystick feature/direction - * - * \param feature The name of the feature on the default controller - * \param[optional] dir The direction (used for analog sticks) - * - * \return The key ID, or 0 if unknown - */ - virtual unsigned int GetKeyID(const FeatureName& feature, ANALOG_STICK_DIRECTION dir = ANALOG_STICK_DIRECTION::UNKNOWN) const = 0; - - /*! - * \brief Get the window ID that should be used in the keymap lookup - * - * A subclass can override this to force a key entry from a particular - * window. - * - * \return A window ID from WindowIDs.h - */ - virtual int GetWindowID() const; - - /*! - * \brief Check if we should use the fallthrough window when looking up keys - * - * \return True if we should use a key from a fallthrough window or the - * global keymap - */ - virtual bool GetFallthrough() const { return true; } - - /*! - * \brief Keep track of cheat code presses - * - * Child classes should initialize this with the appropriate controller ID. - */ - std::unique_ptr m_easterEgg; - - private: - bool ActivateDirection(const FeatureName& feature, float magnitude, ANALOG_STICK_DIRECTION dir, unsigned int motionTimeMs); - void DeactivateDirection(const FeatureName& feature, ANALOG_STICK_DIRECTION dir); - - /*! - * \brief Return a vector of the four cardinal directions - */ - static const std::vector& GetDirections(void); - - // Handler to process joystick input to Kodi actions - IKeymapHandler* const m_handler; - - // State variables used to process joystick input - std::map m_currentDirections; // Analog stick name -> direction - }; -} -} diff --git a/xbmc/input/joysticks/DefaultRemote.cpp b/xbmc/input/joysticks/DefaultRemote.cpp deleted file mode 100644 index 004b48c545049..0000000000000 --- a/xbmc/input/joysticks/DefaultRemote.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2017 Team Kodi - * http://kodi.tv - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; see the file COPYING. If not, see - * . - * - */ - -#include "DefaultRemote.h" -#include "JoystickEasterEgg.h" -#include "JoystickIDs.h" -#include "input/Key.h" - -using namespace KODI; -using namespace JOYSTICK; - -CDefaultRemote::CDefaultRemote(void) -{ - // initialize CDefaultJoystick - m_easterEgg.reset(new CJoystickEasterEgg(GetControllerID())); -} - -std::string CDefaultRemote::GetControllerID(void) const -{ - return DEFAULT_REMOTE_ID; -} - -unsigned int CDefaultRemote::GetKeyID(const FeatureName& feature, ANALOG_STICK_DIRECTION dir /* = ANALOG_STICK_DIRECTION::UNKNOWN */) const -{ - if (feature == "ok") return KEY_REMOTE_BUTTON_OK; - else if (feature == "back") return KEY_REMOTE_BUTTON_BACK; - else if (feature == "up") return KEY_REMOTE_BUTTON_UP; - else if (feature == "down") return KEY_REMOTE_BUTTON_DOWN; - else if (feature == "right") return KEY_REMOTE_BUTTON_RIGHT; - else if (feature == "left") return KEY_REMOTE_BUTTON_LEFT; - else if (feature == "home") return KEY_REMOTE_BUTTON_HOME; - - return 0; -} diff --git a/xbmc/input/joysticks/IButtonMapper.h b/xbmc/input/joysticks/IButtonMapper.h index 943301b7997d6..c2f454953633c 100644 --- a/xbmc/input/joysticks/IButtonMapper.h +++ b/xbmc/input/joysticks/IButtonMapper.h @@ -22,12 +22,13 @@ #include #include +class IKeymap; + namespace KODI { namespace JOYSTICK { class CDriverPrimitive; - class IActionMap; class IButtonMap; class IButtonMapCallback; @@ -81,14 +82,14 @@ namespace JOYSTICK * \brief Handle button/hat press or axis threshold * * \param buttonMap The button map being manipulated - * \param actionMap An interface capable of translating driver primitives to Kodi actions + * \param keymap An interface capable of translating features to Kodi actions * \param primitive The driver primitive * * Called in the same thread as \ref IButtonMapper::OnFrame. * * \return True if driver primitive was mapped to a feature */ - virtual bool MapPrimitive(IButtonMap* buttonMap, IActionMap* actionMap, const CDriverPrimitive& primitive) = 0; + virtual bool MapPrimitive(IButtonMap* buttonMap, IKeymap* keyMap, const CDriverPrimitive& primitive) = 0; /*! * \brief Called once per event frame to notify the implementation of motion status diff --git a/xbmc/input/joysticks/IInputHandler.h b/xbmc/input/joysticks/IInputHandler.h index 3323dae2b2807..5501e0aefa685 100644 --- a/xbmc/input/joysticks/IInputHandler.h +++ b/xbmc/input/joysticks/IInputHandler.h @@ -57,22 +57,17 @@ namespace JOYSTICK virtual bool HasFeature(const FeatureName& feature) const = 0; /*! - * \brief Return true if the input handler is currently accepting input + * \brief Return true if the input handler is currently accepting input for the + * given feature * * \param feature A feature belonging to the controller specified by ControllerID() * * \return True if the feature is currently accepting input, false otherwise - */ - virtual bool AcceptsInput(const FeatureName &feature) const = 0; - - /*! - * \brief Get the delay before this feature should be processed - * - * \param feature The feature * - * \return The duration this feature must be held before sending events + * This does not prevent the input events from being called, but can return + * false to indicate that input wasn't handled for the specified feature. */ - virtual unsigned int GetDelayMs(const FeatureName& feature) const = 0; + virtual bool AcceptsInput(const FeatureName &feature) const = 0; /*! * \brief A digital button has been pressed or released diff --git a/xbmc/input/joysticks/IKeymapHandler.h b/xbmc/input/joysticks/IKeymapHandler.h deleted file mode 100644 index 151473f98b921..0000000000000 --- a/xbmc/input/joysticks/IKeymapHandler.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2015-2017 Team Kodi - * http://kodi.tv - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; see the file COPYING. If not, see - * . - * - */ -#pragma once - -#include "JoystickTypes.h" - -namespace KODI -{ -namespace JOYSTICK -{ - /*! - * \ingroup joystick - * \brief Interface for handling keymap keys - * - * Keys can be mapped to analog actions (e.g. "AnalogSeekForward") or digital - * actions (e.g. "Up"). - */ - class IKeymapHandler - { - public: - virtual ~IKeymapHandler() = default; - - /*! - * \brief Get the action ID mapped to the specified key ID - * - * \param keyId The key ID from Key.h - * \param windowId The window ID from WindowIDs.h - * \param bFallthrough Use a key from an underlying window or global keymap - * - * \return The action ID, or ACTION_NONE if no action is mapped to the - * specified key - */ - virtual unsigned int GetActionID(unsigned int keyId, int windowId, bool bFallthrough) const = 0; - - /*! - * \brief Get the time required to hold the button before calling OnDigitalKey() - * - * \param keyId The key ID from Key.h - * \param windowId The window ID from WindowIDs.h - * \param bFallthrough Use a key from an underlying window or global keymap - * - * \return The hold time, in ms - */ - virtual unsigned int GetHoldTimeMs(unsigned int keyId, int windowId, bool bFallthrough) const = 0; - - /*! - * \brief A key mapped to a digital action has been pressed or released - * - * \param keyId The key ID from Key.h - * \param windowId The window ID from WindowIDs.h - * \param bFallthrough Use a key from an underlying window or global keymap - * \param bPressed true if the key's button/axis is activated, false if deactivated - * \param holdTimeMs The held time in ms for pressed buttons, or 0 for released - */ - virtual void OnDigitalKey(unsigned int keyId, int windowId, bool bPressed, bool bFallthrough, unsigned int holdTimeMs = 0) = 0; - - /*! - * \brief Callback for keys mapped to analog actions - * - * \param keyId The button key ID from Key.h - * \param windowId The window ID from WindowIDs.h - * \param bFallthrough Use a key from an underlying window or global keymap - * \param magnitude The amount of the analog action - * \param motionTimeMs The time elapsed since the magnitude was 0 - * - * If keyId is not mapped to an analog action, no action need be taken - */ - virtual void OnAnalogKey(unsigned int keyId, int windowId, bool bFallthrough, float magnitude, unsigned int motionTimeMs) = 0; - }; -} -} diff --git a/xbmc/input/joysticks/JoystickMonitor.cpp b/xbmc/input/joysticks/JoystickMonitor.cpp index 2d31fbd0e1cfc..f581a914992b6 100644 --- a/xbmc/input/joysticks/JoystickMonitor.cpp +++ b/xbmc/input/joysticks/JoystickMonitor.cpp @@ -21,6 +21,7 @@ #include "JoystickMonitor.h" #include "Application.h" #include "input/InputManager.h" +#include "ServiceBroker.h" using namespace KODI; using namespace JOYSTICK; @@ -29,7 +30,7 @@ bool CJoystickMonitor::OnButtonMotion(unsigned int buttonIndex, bool bPressed) { if (bPressed) { - CInputManager::GetInstance().SetMouseActive(false); + CServiceBroker::GetInputManager().SetMouseActive(false); return ResetTimers(); } @@ -40,7 +41,7 @@ bool CJoystickMonitor::OnHatMotion(unsigned int hatIndex, HAT_STATE state) { if (state != HAT_STATE::UNPRESSED) { - CInputManager::GetInstance().SetMouseActive(false); + CServiceBroker::GetInputManager().SetMouseActive(false); return ResetTimers(); } @@ -51,7 +52,7 @@ bool CJoystickMonitor::OnAxisMotion(unsigned int axisIndex, float position, int { if (position) { - CInputManager::GetInstance().SetMouseActive(false); + CServiceBroker::GetInputManager().SetMouseActive(false); return ResetTimers(); } diff --git a/xbmc/input/joysticks/JoystickTranslator.cpp b/xbmc/input/joysticks/JoystickTranslator.cpp index 7cf3098505e90..f1d2b47126279 100644 --- a/xbmc/input/joysticks/JoystickTranslator.cpp +++ b/xbmc/input/joysticks/JoystickTranslator.cpp @@ -46,6 +46,31 @@ const char* CJoystickTranslator::HatStateToString(HAT_STATE state) return "RELEASED"; } +const char* CJoystickTranslator::TranslateDirection(ANALOG_STICK_DIRECTION dir) +{ + switch (dir) + { + case ANALOG_STICK_DIRECTION::UP: return "up"; + case ANALOG_STICK_DIRECTION::DOWN: return "down"; + case ANALOG_STICK_DIRECTION::RIGHT: return "right"; + case ANALOG_STICK_DIRECTION::LEFT: return "left"; + default: + break; + } + + return ""; +} + +ANALOG_STICK_DIRECTION CJoystickTranslator::TranslateDirection(const std::string &dir) +{ + if (dir == "up") return ANALOG_STICK_DIRECTION::UP; + if (dir == "down") return ANALOG_STICK_DIRECTION::DOWN; + if (dir == "right") return ANALOG_STICK_DIRECTION::RIGHT; + if (dir == "left") return ANALOG_STICK_DIRECTION::LEFT; + + return ANALOG_STICK_DIRECTION::UNKNOWN; +} + SEMIAXIS_DIRECTION CJoystickTranslator::PositionToSemiAxisDirection(float position) { if (position > 0) return SEMIAXIS_DIRECTION::POSITIVE; diff --git a/xbmc/input/joysticks/JoystickTranslator.h b/xbmc/input/joysticks/JoystickTranslator.h index c687c486eb9e4..13717c236e4d7 100644 --- a/xbmc/input/joysticks/JoystickTranslator.h +++ b/xbmc/input/joysticks/JoystickTranslator.h @@ -42,6 +42,24 @@ namespace JOYSTICK */ static const char* HatStateToString(HAT_STATE state); + /*! + * \brief Translate an analog stick direction to a lower-case string + * + * \param dir The analog stick direction + * + * \return A lower-case string representation, or "" if the direction is invalid + */ + static const char* TranslateDirection(ANALOG_STICK_DIRECTION dir); + + /*! + * \brief Translate an analog stick direction string to an enum value + * + * \param dir The analog stick direction + * + * \return The translated direction, or ANALOG_STICK_DIRECTION::UNKNOWN if unknown + */ + static ANALOG_STICK_DIRECTION TranslateDirection(const std::string &dir); + /*! * \brief Get the semi-axis direction containing the specified position * diff --git a/xbmc/input/joysticks/JoystickTypes.h b/xbmc/input/joysticks/JoystickTypes.h index eb1026f022a16..a7994803876f0 100644 --- a/xbmc/input/joysticks/JoystickTypes.h +++ b/xbmc/input/joysticks/JoystickTypes.h @@ -24,6 +24,7 @@ \ingroup joystick */ +#include #include namespace KODI @@ -138,5 +139,28 @@ namespace JOYSTICK SEMIAXIS, // the positive or negative half of an axis MOTOR, // a rumble motor }; + + /*! + * \ingroup joystick + * \brief Action entry in joystick.xml + */ + struct KeymapAction + { + unsigned int actionId; + std::string actionString; + unsigned int holdTimeMs; + std::set hotkeys; + + bool operator<(const KeymapAction &rhs) const + { + return holdTimeMs < rhs.holdTimeMs; + } + }; + + /*! + * \ingroup joystick + * \brief Container that sorts action entries by their holdtime + */ + using KeymapActions = std::set; } } diff --git a/xbmc/input/joysticks/JoystickUtils.cpp b/xbmc/input/joysticks/JoystickUtils.cpp new file mode 100644 index 0000000000000..a5a4f7e37b245 --- /dev/null +++ b/xbmc/input/joysticks/JoystickUtils.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "JoystickUtils.h" +#include "JoystickTranslator.h" +#include "utils/StringUtils.h" + +using namespace KODI; +using namespace JOYSTICK; + +std::string CJoystickUtils::MakeKeyName(const FeatureName &feature, ANALOG_STICK_DIRECTION dir /* = ANALOG_STICK_DIRECTION::UNKNOWN */) +{ + std::string keyName = feature; + + if (dir != ANALOG_STICK_DIRECTION::UNKNOWN) + keyName += CJoystickTranslator::TranslateDirection(dir); + + return keyName; +} + +const std::vector &CJoystickUtils::GetDirections() +{ + static std::vector directions; + if (directions.empty()) + { + directions.push_back(ANALOG_STICK_DIRECTION::UP); + directions.push_back(ANALOG_STICK_DIRECTION::DOWN); + directions.push_back(ANALOG_STICK_DIRECTION::RIGHT); + directions.push_back(ANALOG_STICK_DIRECTION::LEFT); + } + return directions; +} diff --git a/xbmc/input/joysticks/JoystickUtils.h b/xbmc/input/joysticks/JoystickUtils.h index 70ef5ef2529f4..cb7d7b1c38e5b 100644 --- a/xbmc/input/joysticks/JoystickUtils.h +++ b/xbmc/input/joysticks/JoystickUtils.h @@ -21,6 +21,9 @@ #include "JoystickTypes.h" +#include +#include + /// \ingroup joystick /// \{ @@ -54,6 +57,27 @@ inline float operator*(float lhs, SEMIAXIS_DIRECTION rhs) return lhs * static_cast(rhs); } +class CJoystickUtils +{ +public: + /*! + * \brief Create a key name used to index an action in the keymap + * + * \param feature The feature name + * \param dir The direction for analog sticks, or ignored otherwise + * + * \return A valid name for a key in the joystick keymap + * + * \sa ParseKeyName() + */ + static std::string MakeKeyName(const FeatureName &feature, ANALOG_STICK_DIRECTION dir = ANALOG_STICK_DIRECTION::UNKNOWN); + + /*! + * \brief Return a vector of the four cardinal directions + */ + static const std::vector &GetDirections(); +}; + } } diff --git a/xbmc/input/joysticks/KeymapHandler.cpp b/xbmc/input/joysticks/KeymapHandler.cpp deleted file mode 100644 index 1048783e8ec71..0000000000000 --- a/xbmc/input/joysticks/KeymapHandler.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2015-2017 Team Kodi - * http://kodi.tv - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; see the file COPYING. If not, see - * . - * - */ - -#include "KeymapHandler.h" -#include "input/Action.h" -#include "input/ButtonTranslator.h" -#include "input/InputManager.h" -#include "input/Key.h" -#include "interfaces/IActionListener.h" -#include "utils/log.h" - -#include - -using namespace KODI; -using namespace JOYSTICK; - -#define DIGITAL_ANALOG_THRESHOLD 0.5f - -#define HOLD_TIMEOUT_MS 500 -#define REPEAT_TIMEOUT_MS 50 - -CKeymapHandler::CKeymapHandler(IActionListener *actionHandler) : - m_actionHandler(actionHandler), - m_lastButtonPress(0), - m_lastDigitalActionMs(0) -{ -} - -CKeymapHandler::~CKeymapHandler(void) -{ -} - -unsigned int CKeymapHandler::GetActionID(unsigned int keyId, int windowId, bool bFallthrough) const -{ - CAction action(ACTION_NONE); - - if (keyId != 0) - action = CButtonTranslator::GetInstance().GetAction(windowId, CKey(keyId), bFallthrough); - - //! @todo make CAction::GetID() return unsigned - if (action.GetID() >= 0) - return action.GetID(); - else - return ACTION_NONE; -} - -unsigned int CKeymapHandler::GetHoldTimeMs(unsigned int keyId, int windowId, bool bFallthrough) const -{ - return CButtonTranslator::GetInstance().GetHoldTimeMs(windowId, CKey(keyId), bFallthrough); -} - -void CKeymapHandler::OnDigitalKey(unsigned int keyId, int windowId, bool bFallthrough, bool bPressed, unsigned int holdTimeMs /* = 0 */) -{ - CAction action(ACTION_NONE); - - if (keyId != 0) - action = CButtonTranslator::GetInstance().GetAction(windowId, CKey(keyId, holdTimeMs), bFallthrough); - - if (action.GetID() != ACTION_NONE) - { - if (action.IsAnalog()) - { - OnAnalogKey(keyId, windowId, bFallthrough, bPressed ? 1.0f : 0.0f, 0); - } - else - { - if (bPressed) - SendDigitalAction(action); - else - ProcessButtonRelease(keyId); - } - } -} - -void CKeymapHandler::OnAnalogKey(unsigned int keyId, int windowId, bool bFallthrough, float magnitude, unsigned int motionTimeMs) -{ - CAction action(ACTION_NONE); - - if (keyId != 0) - action = CButtonTranslator::GetInstance().GetAction(windowId, CKey(keyId), bFallthrough); - - if (action.GetID() != ACTION_NONE) - { - if (action.IsAnalog()) - { - CAction actionWithAmount(action.GetID(), magnitude, 0.0f, action.GetName()); - m_actionHandler->OnAction(actionWithAmount); - } - else - { - unsigned int holdTimeMs = 0; - - const bool bIsPressed = (magnitude >= DIGITAL_ANALOG_THRESHOLD); - if (bIsPressed) - { - const bool bIsHeld = (m_holdStartTimes.find(keyId) != m_holdStartTimes.end()); - if (bIsHeld) - holdTimeMs = motionTimeMs - m_holdStartTimes[keyId]; - else - m_holdStartTimes[keyId] = motionTimeMs; - } - else - { - m_holdStartTimes.erase(keyId); - } - - OnDigitalKey(keyId, windowId, bFallthrough, bIsPressed, holdTimeMs); - } - } -} - -void CKeymapHandler::SendDigitalAction(const CAction& action) -{ - const unsigned int keyId = action.GetButtonCode(); - const unsigned int holdTimeMs = action.GetHoldTime(); - - if (!IsPressed(keyId)) - { - m_pressedButtons.push_back(keyId); - - // Only dispatch action if button was pressed this frame - if (holdTimeMs == 0) - { - if (m_actionHandler->OnAction(action)) - { - m_lastButtonPress = keyId; - m_lastDigitalActionMs = holdTimeMs; - } - } - } - else if (keyId == m_lastButtonPress && holdTimeMs > HOLD_TIMEOUT_MS) - { - if (holdTimeMs > m_lastDigitalActionMs + REPEAT_TIMEOUT_MS) - { - m_actionHandler->OnAction(action); - m_lastDigitalActionMs = holdTimeMs; - } - } -} - -void CKeymapHandler::ProcessButtonRelease(unsigned int keyId) -{ - m_pressedButtons.erase(std::remove(m_pressedButtons.begin(), m_pressedButtons.end(), keyId), m_pressedButtons.end()); - - // Update last button press if the button was released - if (keyId == m_lastButtonPress) - m_lastButtonPress = 0; - - // If all buttons are depressed, m_lastButtonPress must be 0 - if (m_pressedButtons.empty() && m_lastButtonPress != 0) - { - CLog::Log(LOGDEBUG, "ERROR: invalid state in CKeymapHandler!"); - m_lastButtonPress = 0; - } -} - -bool CKeymapHandler::IsPressed(unsigned int keyId) const -{ - return std::find(m_pressedButtons.begin(), m_pressedButtons.end(), keyId) != m_pressedButtons.end(); -} diff --git a/xbmc/input/joysticks/KeymapHandler.h b/xbmc/input/joysticks/KeymapHandler.h deleted file mode 100644 index 8af6a632b325b..0000000000000 --- a/xbmc/input/joysticks/KeymapHandler.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2015-2017 Team Kodi - * http://kodi.tv - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this Program; see the file COPYING. If not, see - * . - * - */ -#pragma once - -#include "input/joysticks/IKeymapHandler.h" - -#include -#include - -class CAction; -class IActionListener; - -namespace KODI -{ -namespace JOYSTICK -{ - /*! - * Handles keymaps - */ - class CKeymapHandler : public IKeymapHandler - { - public: - CKeymapHandler(IActionListener *actionHandler); - - virtual ~CKeymapHandler(void); - - // implementation of IKeymapHandler - virtual unsigned int GetActionID(unsigned int keyId, int windowId, bool bFallthrough) const override; - virtual unsigned int GetHoldTimeMs(unsigned int keyId, int windowId, bool bFallthrough) const override; - virtual void OnDigitalKey(unsigned int keyId, int windowId, bool bFallthrough, bool bPressed, unsigned int holdTimeMs = 0) override; - virtual void OnAnalogKey(unsigned int keyId, int windowId, bool bFallthrough, float magnitude, unsigned int motionTimeMs) override; - - private: - static INPUT_TYPE GetInputType(unsigned int keyId, int windowId, bool bFallthrough); - void SendDigitalAction(const CAction& action); - void ProcessButtonRelease(unsigned int keyId); - bool IsPressed(unsigned int keyId) const; - - // Construction parameter - IActionListener* const m_actionHandler; - - unsigned int m_lastButtonPress; - unsigned int m_lastDigitalActionMs; - std::vector m_pressedButtons; - std::map m_holdStartTimes; // Key ID -> hold start time (ms) - }; -} -} diff --git a/xbmc/input/joysticks/RumbleGenerator.cpp b/xbmc/input/joysticks/RumbleGenerator.cpp index 706af9f3467d5..a9662c92b1826 100644 --- a/xbmc/input/joysticks/RumbleGenerator.cpp +++ b/xbmc/input/joysticks/RumbleGenerator.cpp @@ -22,6 +22,7 @@ #include "games/controllers/Controller.h" #include "games/GameServices.h" #include "input/joysticks/IInputReceiver.h" +#include "input/joysticks/JoystickIDs.h" #include "ServiceBroker.h" #include @@ -35,14 +36,19 @@ using namespace KODI; using namespace JOYSTICK; -CRumbleGenerator::CRumbleGenerator(const std::string& controllerId) : +CRumbleGenerator::CRumbleGenerator() : CThread("RumbleGenerator"), - m_motors(GetMotors(controllerId)), + m_motors(GetMotors(ControllerID())), m_receiver(nullptr), m_type(RUMBLE_UNKNOWN) { } +std::string CRumbleGenerator::ControllerID() const +{ + return DEFAULT_CONTROLLER_ID; +} + void CRumbleGenerator::NotifyUser(IInputReceiver* receiver) { if (receiver && !m_motors.empty()) diff --git a/xbmc/input/joysticks/RumbleGenerator.h b/xbmc/input/joysticks/RumbleGenerator.h index 5856ac83f6745..424452fafa485 100644 --- a/xbmc/input/joysticks/RumbleGenerator.h +++ b/xbmc/input/joysticks/RumbleGenerator.h @@ -21,6 +21,9 @@ #include "threads/Thread.h" +#include +#include + namespace KODI { namespace JOYSTICK @@ -30,10 +33,12 @@ namespace JOYSTICK class CRumbleGenerator : public CThread { public: - CRumbleGenerator(const std::string& controllerId); + CRumbleGenerator(); virtual ~CRumbleGenerator(void) { AbortRumble(); } + std::string ControllerID() const; + void NotifyUser(IInputReceiver* receiver); bool DoTest(IInputReceiver* receiver); diff --git a/xbmc/input/joysticks/generic/ButtonMapping.cpp b/xbmc/input/joysticks/generic/ButtonMapping.cpp index 6d1b5e4e81374..12f11cebc93bf 100644 --- a/xbmc/input/joysticks/generic/ButtonMapping.cpp +++ b/xbmc/input/joysticks/generic/ButtonMapping.cpp @@ -23,10 +23,11 @@ #include "games/controllers/Controller.h" #include "games/controllers/ControllerFeature.h" #include "input/joysticks/DriverPrimitive.h" -#include "input/joysticks/IActionMap.h" #include "input/joysticks/IButtonMap.h" #include "input/joysticks/IButtonMapper.h" #include "input/joysticks/JoystickTranslator.h" +#include "input/joysticks/JoystickUtils.h" +#include "input/IKeymap.h" #include "input/Key.h" #include "threads/SystemClock.h" #include "utils/log.h" @@ -244,10 +245,10 @@ void CAxisDetector::DetectType(float position) // --- CButtonMapping ---------------------------------------------------------- -CButtonMapping::CButtonMapping(IButtonMapper* buttonMapper, IButtonMap* buttonMap, IActionMap* actionMap) : +CButtonMapping::CButtonMapping(IButtonMapper* buttonMapper, IButtonMap* buttonMap, IKeymap* keymap) : m_buttonMapper(buttonMapper), m_buttonMap(buttonMap), - m_actionMap(actionMap), + m_keymap(keymap), m_lastAction(0), m_frameCount(0) { @@ -256,17 +257,23 @@ CButtonMapping::CButtonMapping(IButtonMapper* buttonMapper, IButtonMap* buttonMa // Make sure axes mapped to Select are centered before they can be mapped. // This ensures that they are not immediately mapped to the first button. - if (m_actionMap && m_actionMap->ControllerID() == m_buttonMap->ControllerID()) + if (m_keymap) { using namespace GAME; CGameServices& gameServices = CServiceBroker::GetGameServices(); - ControllerPtr controller = gameServices.GetController(m_actionMap->ControllerID()); + ControllerPtr controller = gameServices.GetController(m_keymap->ControllerID()); const auto& features = controller->Layout().Features(); for (const auto& feature : features) { - if (m_actionMap->GetActionID(feature.Name()) != ACTION_SELECT_ITEM) + bool bIsSelectAction = false; + + const auto &actions = m_keymap->GetActions(CJoystickUtils::MakeKeyName(feature.Name())); + if (!actions.empty() && actions.begin()->actionId == ACTION_SELECT_ITEM) + bIsSelectAction = true; + + if (!bIsSelectAction) continue; CDriverPrimitive primitive; @@ -341,7 +348,7 @@ bool CButtonMapping::MapPrimitive(const CDriverPrimitive& primitive) if (bTimeoutElapsed) { - bHandled = m_buttonMapper->MapPrimitive(m_buttonMap, m_actionMap, primitive); + bHandled = m_buttonMapper->MapPrimitive(m_buttonMap, m_keymap, primitive); if (bHandled) m_lastAction = SystemClockMillis(); diff --git a/xbmc/input/joysticks/generic/ButtonMapping.h b/xbmc/input/joysticks/generic/ButtonMapping.h index af26d60007c5d..c3f97eb020405 100644 --- a/xbmc/input/joysticks/generic/ButtonMapping.h +++ b/xbmc/input/joysticks/generic/ButtonMapping.h @@ -26,11 +26,15 @@ #include #include +class IKeymap; + namespace KODI { namespace JOYSTICK { class CButtonMapping; + class IButtonMap; + class IButtonMapper; class CButtonDetector { @@ -188,10 +192,6 @@ namespace JOYSTICK unsigned int m_activationTimeMs; // only used to delay anomalous trigger mapping to detect full range }; - class IActionMap; - class IButtonMap; - class IButtonMapper; - /*! * \ingroup joystick * \brief Generic implementation of a class that provides button mapping by @@ -213,7 +213,7 @@ namespace JOYSTICK * \param buttonMapper Carries out button-mapping commands using * \param buttonMap The button map given to on each command */ - CButtonMapping(IButtonMapper* buttonMapper, IButtonMap* buttonMap, IActionMap* actionMap); + CButtonMapping(IButtonMapper* buttonMapper, IButtonMap* buttonMap, IKeymap* keymap); virtual ~CButtonMapping() = default; @@ -253,7 +253,7 @@ namespace JOYSTICK // Construction parameters IButtonMapper* const m_buttonMapper; IButtonMap* const m_buttonMap; - IActionMap* const m_actionMap; + IKeymap* const m_keymap; std::map m_buttons; std::map m_hats; diff --git a/xbmc/input/joysticks/generic/FeatureHandling.cpp b/xbmc/input/joysticks/generic/FeatureHandling.cpp index 9d0d963f1f869..3969dd76067ed 100644 --- a/xbmc/input/joysticks/generic/FeatureHandling.cpp +++ b/xbmc/input/joysticks/generic/FeatureHandling.cpp @@ -64,12 +64,11 @@ bool CJoystickFeature::AcceptsInput(bool bActivation) CScalarFeature::CScalarFeature(const FeatureName& name, IInputHandler* handler, IButtonMap* buttonMap) : CJoystickFeature(name, handler, buttonMap), + m_inputType(INPUT_TYPE::UNKNOWN), m_bDigitalState(false), - m_bDigitalHandled(false), - m_bDigitalPressSent(false), m_motionStartTimeMs(0), m_analogState(0.0f), - m_analogEvent(false), + m_bActivated(false), m_bDiscrete(true) { GAME::ControllerPtr controller = CServiceBroker::GetGameServices().GetController(handler->ControllerID()); @@ -79,97 +78,45 @@ CScalarFeature::CScalarFeature(const FeatureName& name, IInputHandler* handler, bool CScalarFeature::OnDigitalMotion(const CDriverPrimitive& source, bool bPressed) { - if (!AcceptsInput(bPressed)) - return false; + bool bHandled = false; if (m_inputType == INPUT_TYPE::DIGITAL) - OnDigitalMotion(bPressed); + bHandled = OnDigitalMotion(bPressed); else if (m_inputType == INPUT_TYPE::ANALOG) - OnAnalogMotion(bPressed ? 1.0f : 0.0f); + bHandled = OnAnalogMotion(bPressed ? 1.0f : 0.0f); - return true; + return bHandled && AcceptsInput(bPressed); } bool CScalarFeature::OnAnalogMotion(const CDriverPrimitive& source, float magnitude) { + // Update activated status + if (magnitude > 0.0f) + m_bActivated = true; + // Update discrete status if (magnitude != 0.0f && magnitude != 1.0f) m_bDiscrete = false; - if (!AcceptsInput(magnitude != 0.0f)) - return false; + bool bHandled = false; if (m_inputType == INPUT_TYPE::DIGITAL) - OnDigitalMotion(magnitude >= ANALOG_DIGITAL_THRESHOLD); + bHandled = OnDigitalMotion(magnitude >= ANALOG_DIGITAL_THRESHOLD); else if (m_inputType == INPUT_TYPE::ANALOG) - OnAnalogMotion(magnitude); + bHandled = OnAnalogMotion(magnitude); - return true; + return bHandled && AcceptsInput(magnitude > 0.0f); } void CScalarFeature::ProcessMotions(void) { - if (m_analogEvent) - { - float magnitude = m_analogState; - - // Calculate time elapsed since motion began - const unsigned int elapsed = XbmcThreads::SystemClockMillis() - m_motionStartTimeMs; - - // If analog value is discrete, ramp up magnitude - if (m_bDiscrete) - { - if (elapsed < DISCRETE_ANALOG_RAMPUP_TIME_MS) - { - magnitude *= static_cast(elapsed) / DISCRETE_ANALOG_RAMPUP_TIME_MS; - if (magnitude < DISCRETE_ANALOG_START_VALUE) - magnitude = DISCRETE_ANALOG_START_VALUE; - } - } - - m_handler->OnButtonMotion(m_name, magnitude, elapsed); - - // Disable sending events after feature is reset - if (m_analogState == 0.0f) - { - m_analogEvent = false; - m_motionStartTimeMs = 0; - } - } - else if (m_bDigitalState) - { - if (m_motionStartTimeMs == 0) - { - // Button was just pressed, record start time and exit (button press - // event was already sent this frame) - m_motionStartTimeMs = XbmcThreads::SystemClockMillis(); - } - else - { - // Calculate time elapsed - const unsigned int elapsed = XbmcThreads::SystemClockMillis() - m_motionStartTimeMs; - - // Calculate holdtime, if any - const unsigned int holdtimeMs = m_handler->GetDelayMs(m_name); - - // Only process button events if holdtime has elapsed - if (elapsed >= holdtimeMs) - { - if (m_bDigitalHandled) - { - m_handler->OnButtonHold(m_name, elapsed - holdtimeMs); - } - else if (!m_bDigitalPressSent) - { - m_bDigitalHandled = m_handler->OnButtonPress(m_name, true); - m_bDigitalPressSent = true; - } - } - } - } + if (m_inputType == INPUT_TYPE::DIGITAL && m_bDigitalState) + ProcessDigitalMotion(); + else if (m_inputType == INPUT_TYPE::ANALOG) + ProcessAnalogMotion(); } -void CScalarFeature::OnDigitalMotion(bool bPressed) +bool CScalarFeature::OnDigitalMotion(bool bPressed) { if (m_bDigitalState != bPressed) { @@ -179,53 +126,73 @@ void CScalarFeature::OnDigitalMotion(bool bPressed) CLog::Log(LOGDEBUG, "FEATURE [ %s ] on %s %s", m_name.c_str(), m_handler->ControllerID().c_str(), bPressed ? "pressed" : "released"); - if (bPressed) - { - // Don't send event if a holdtime was specified - bool bHasHoldtime = false; - - if (m_handler->GetDelayMs(m_name) != 0) - bHasHoldtime = true; - - if (bHasHoldtime) - { - m_bDigitalHandled = false; - m_bDigitalPressSent = false; - } - else - { - m_bDigitalHandled = m_handler->OnButtonPress(m_name, true); - m_bDigitalPressSent = true; - } - } - else - { - m_handler->OnButtonPress(m_name, false); - m_bDigitalHandled = false; - m_bDigitalPressSent = false; - } + return m_handler->OnButtonPress(m_name, bPressed); } + + return false; } -void CScalarFeature::OnAnalogMotion(float magnitude) +bool CScalarFeature::OnAnalogMotion(float magnitude) { const bool bActivated = (magnitude != 0.0f); - if (m_analogState != 0.0f || magnitude != 0.0f) + // Update analog state + m_analogState = magnitude; + + // Update motion time + if (!bActivated) + m_motionStartTimeMs = 0; + else if (m_motionStartTimeMs == 0) + m_motionStartTimeMs = XbmcThreads::SystemClockMillis(); + + // Log activation/deactivation + if (m_bDigitalState != bActivated) + { + m_bDigitalState = bActivated; + CLog::Log(LOGDEBUG, "FEATURE [ %s ] on %s %s", m_name.c_str(), m_handler->ControllerID().c_str(), + bActivated ? "activated" : "deactivated"); + } + + return true; +} + +void CScalarFeature::ProcessDigitalMotion() +{ + if (m_motionStartTimeMs == 0) + { + // Button was just pressed, record start time and exit (button press event + // was already sent this frame) + m_motionStartTimeMs = XbmcThreads::SystemClockMillis(); + } + else { - m_analogState = magnitude; - m_analogEvent = true; - if (m_motionStartTimeMs == 0) - m_motionStartTimeMs = XbmcThreads::SystemClockMillis(); + // Button has been pressed more than one event frame + const unsigned int elapsed = XbmcThreads::SystemClockMillis() - m_motionStartTimeMs; + m_handler->OnButtonHold(m_name, elapsed); + } +} + +void CScalarFeature::ProcessAnalogMotion() +{ + float magnitude = m_analogState; + + // Calculate time elapsed since motion began + unsigned int elapsed = 0; + if (m_motionStartTimeMs > 0) + elapsed = XbmcThreads::SystemClockMillis() - m_motionStartTimeMs; - // Log activation/deactivation - if (m_bDigitalState != bActivated) + // If analog value is discrete, ramp up magnitude + if (m_bActivated && m_bDiscrete) + { + if (elapsed < DISCRETE_ANALOG_RAMPUP_TIME_MS) { - m_bDigitalState = bActivated; - CLog::Log(LOGDEBUG, "FEATURE [ %s ] on %s %s", m_name.c_str(), m_handler->ControllerID().c_str(), - bActivated ? "activated" : "deactivated"); + magnitude *= static_cast(elapsed) / DISCRETE_ANALOG_RAMPUP_TIME_MS; + if (magnitude < DISCRETE_ANALOG_START_VALUE) + magnitude = DISCRETE_ANALOG_START_VALUE; } } + + m_handler->OnButtonMotion(m_name, magnitude, elapsed); } // --- CAnalogStick ------------------------------------------------------------ @@ -245,9 +212,6 @@ bool CAnalogStick::OnDigitalMotion(const CDriverPrimitive& source, bool bPressed bool CAnalogStick::OnAnalogMotion(const CDriverPrimitive& source, float magnitude) { - if (!AcceptsInput(magnitude != 0.0f)) - return false; - ANALOG_STICK_DIRECTION direction = ANALOG_STICK_DIRECTION::UNKNOWN; std::vector dirs = { @@ -267,19 +231,25 @@ bool CAnalogStick::OnAnalogMotion(const CDriverPrimitive& source, float magnitud } } + bool bHandled = false; + switch (direction) { case ANALOG_STICK_DIRECTION::UP: m_vertAxis.SetPositiveDistance(magnitude); + bHandled = true; break; case ANALOG_STICK_DIRECTION::DOWN: m_vertAxis.SetNegativeDistance(magnitude); + bHandled = true; break; case ANALOG_STICK_DIRECTION::RIGHT: m_horizAxis.SetPositiveDistance(magnitude); + bHandled = true; break; case ANALOG_STICK_DIRECTION::LEFT: m_horizAxis.SetNegativeDistance(magnitude); + bHandled = true; break; default: // Just in case, avoid sticking @@ -288,7 +258,7 @@ bool CAnalogStick::OnAnalogMotion(const CDriverPrimitive& source, float magnitud break; } - return true; + return bHandled && AcceptsInput(magnitude != 0.0f); } void CAnalogStick::ProcessMotions(void) @@ -349,8 +319,7 @@ bool CAccelerometer::OnDigitalMotion(const CDriverPrimitive& source, bool bPress bool CAccelerometer::OnAnalogMotion(const CDriverPrimitive& source, float magnitude) { - if (!AcceptsInput(magnitude != 0.0f)) - return false; + bool bHandled = false; CDriverPrimitive positiveX; CDriverPrimitive positiveY; @@ -359,11 +328,20 @@ bool CAccelerometer::OnAnalogMotion(const CDriverPrimitive& source, float magnit m_buttonMap->GetAccelerometer(m_name, positiveX, positiveY, positiveZ); if (source == positiveX) + { m_xAxis.SetPositiveDistance(magnitude); + bHandled = true; + } else if (source == positiveY) + { m_yAxis.SetPositiveDistance(magnitude); + bHandled = true; + } else if (source == positiveZ) + { m_zAxis.SetPositiveDistance(magnitude); + bHandled = true; + } else { // Just in case, avoid sticking @@ -372,7 +350,7 @@ bool CAccelerometer::OnAnalogMotion(const CDriverPrimitive& source, float magnit m_yAxis.Reset(); } - return true; + return bHandled && AcceptsInput(true); } void CAccelerometer::ProcessMotions(void) diff --git a/xbmc/input/joysticks/generic/FeatureHandling.h b/xbmc/input/joysticks/generic/FeatureHandling.h index 819091318fbbe..0e4f33db3463c 100644 --- a/xbmc/input/joysticks/generic/FeatureHandling.h +++ b/xbmc/input/joysticks/generic/FeatureHandling.h @@ -108,17 +108,21 @@ namespace JOYSTICK virtual void ProcessMotions(void) override; private: - void OnDigitalMotion(bool bPressed); - void OnAnalogMotion(float magnitude); + bool OnDigitalMotion(bool bPressed); + bool OnAnalogMotion(float magnitude); + void ProcessDigitalMotion(); + void ProcessAnalogMotion(); + + // State variables INPUT_TYPE m_inputType = INPUT_TYPE::UNKNOWN; bool m_bDigitalState; - bool m_bDigitalHandled; - bool m_bDigitalPressSent; unsigned int m_motionStartTimeMs; - float m_analogState; - bool m_analogEvent; - bool m_bDiscrete; + + // Analog state variables + float m_analogState; // The current magnitude + float m_bActivated; // Set to true when first activated (magnitude > 0.0) + bool m_bDiscrete; // Set to false when a non-discrete axis is detected }; /*! diff --git a/xbmc/input/joysticks/interfaces/IKeyHandler.h b/xbmc/input/joysticks/interfaces/IKeyHandler.h new file mode 100644 index 0000000000000..1d45546c9e885 --- /dev/null +++ b/xbmc/input/joysticks/interfaces/IKeyHandler.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015-2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "input/joysticks/JoystickTypes.h" + +namespace KODI +{ +namespace JOYSTICK +{ + /*! + * \ingroup joystick + * \brief Interface for handling keymap keys + * + * Keys can be mapped to analog actions (e.g. "AnalogSeekForward") or digital + * actions (e.g. "Up"). + */ + class IKeyHandler + { + public: + virtual ~IKeyHandler() = default; + + /*! + * \brief Return true if the key is "pressed" (has a magnitude greater + * than 0.5) + * + * \return True if the key is "pressed", false otherwise + */ + virtual bool IsPressed() const = 0; + + /*! + * \brief A key mapped to a digital feature has been pressed or released + * + * \param bPressed true if the key's button/axis is activated, false if deactivated + * \param holdTimeMs The held time in ms for pressed buttons, or 0 for released + * + * \return True if the key is mapped to an action, false otherwise + */ + virtual bool OnDigitalMotion(bool bPressed, unsigned int holdTimeMs) = 0; + + /*! + * \brief Callback for keys mapped to analog features + * + * \param magnitude The amount of the analog action + * \param motionTimeMs The time since the magnitude was 0 + * + * \return True if the key is mapped to an action, false otherwise + */ + virtual bool OnAnalogMotion(float magnitude, unsigned int motionTimeMs) = 0; + }; +} +} diff --git a/xbmc/input/joysticks/IActionMap.h b/xbmc/input/joysticks/interfaces/IKeymapHandler.h similarity index 50% rename from xbmc/input/joysticks/IActionMap.h rename to xbmc/input/joysticks/interfaces/IKeymapHandler.h index f4aaa63a91d89..3a070a9310d9a 100644 --- a/xbmc/input/joysticks/IActionMap.h +++ b/xbmc/input/joysticks/interfaces/IKeymapHandler.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2017 Team Kodi + * Copyright (C) 2017 Team Kodi * http://kodi.tv * * This Program is free software; you can redistribute it and/or modify @@ -19,43 +19,45 @@ */ #pragma once -#include "JoystickTypes.h" -#include "input/Key.h" +#include +#include namespace KODI { namespace JOYSTICK { - class CDriverPrimitive; - /*! * \ingroup joystick - * \brief Interface for translating features to action IDs + * \brief Interface for a class working with a keymap */ - class IActionMap + class IKeymapHandler { public: - virtual ~IActionMap() = default; - + virtual ~IKeymapHandler() = default; + /*! - * \brief The add-on ID of the game controller associated with this action map + * \brief Get the pressed state of the given keys * - * The controller ID provided by the implementation serves as the context - * for the feature names below. + * \param keyNames The key names * - * \return The ID of this action map's game controller add-on + * \return True if all keys are pressed or no keys are given, false otherwise */ - virtual std::string ControllerID(void) const = 0; + virtual bool HotkeysPressed(const std::set &keyNames) const = 0; /*! - * \brief Get the action ID mapped to the specified feature + * \brief Get the key name of the last button pressed * - * \param feature The feature to look up + * \return The key name of the last-pressed button, or empty if no button + * is pressed + */ + virtual std::string GetLastPressed() const = 0; + + /*! + * \brief Called when a key has emitted an action after bring pressed * - * \return The action ID from Key.h, or ACTION_NONE if no action is mapped - * to the specified feature + * \param keyName the key name that emitted the action */ - virtual unsigned int GetActionID(const FeatureName& feature) = 0; + virtual void OnPress(const std::string& keyName) = 0; }; } } diff --git a/xbmc/input/joysticks/keymaps/CMakeLists.txt b/xbmc/input/joysticks/keymaps/CMakeLists.txt new file mode 100644 index 0000000000000..c854b3622b1c7 --- /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 0000000000000..c3340010ed94f --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeyHandler.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "KeyHandler.h" +#include "input/joysticks/interfaces/IKeymapHandler.h" +#include "input/joysticks/JoystickUtils.h" +#include "input/Action.h" +#include "input/ActionTranslator.h" +#include "input/IKeymap.h" +#include "input/IKeymapEnvironment.h" +#include "interfaces/IActionListener.h" + +#include +#include + +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; +} + +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; + + // 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; + } + + // Calculate holdtime relative to when magnitude crossed the threshold + unsigned int holdTimeMs = 0; + if (bPressed) + holdTimeMs = motionTimeMs - m_holdStartTimeMs; + + // Get actions for the key + const auto &actions = m_keymap->GetActions(m_keyName); + + // Give priority to actions with hotkeys + std::vector actionsWithHotkeys; + + for (const auto &action : actions) + { + if (!action.hotkeys.empty()) + actionsWithHotkeys.emplace_back(&action); + } + + bool bHandled = HandleActions(std::move(actionsWithHotkeys), magnitude, holdTimeMs); + + // If that failed, try again with all actions + if (!bHandled) + { + std::vector allActions; + + for (const auto &action : actions) + allActions.emplace_back(&action); + + bHandled = HandleActions(std::move(allActions), magnitude, holdTimeMs); + } + + m_bHeld = bPressed; + m_magnitude = magnitude; + m_lastHoldTimeMs = holdTimeMs; + + return bHandled; +} + +bool CKeyHandler::HandleActions(std::vector actions, float magnitude, unsigned int holdTimeMs) +{ + // 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; + + bool bHandled = 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) + { + bHandled = HandleAction(finalAction, 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; + + bHandled = HandleAction(finalAction, 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) + bHandled = HandleRelease(actions); + } + } + + return bHandled; +} + +bool CKeyHandler::HandleRelease(std::vector actions) +{ + bool bHandled = false; + + // 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) + { + bHandled = HandleAction(action, 1.0f, 0); + break; + } + } + + return bHandled; +} + +bool CKeyHandler::HandleAction(const KeymapAction& action, float magnitude, unsigned int holdTimeMs) +{ + bool bSendAction = false; + + 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_actionHandler->OnAction(guiAction); + m_keymapHandler->OnPress(m_keyName); + m_bActionSent = true; + m_lastActionMs = holdTimeMs; + } + + return m_bActionSent; +} + +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 0000000000000..54725de57fd87 --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeyHandler.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "input/joysticks/interfaces/IKeyHandler.h" +#include "input/joysticks/JoystickTypes.h" + +#include +#include +#include + +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); + + virtual ~CKeyHandler() = default; + + // implementation of IKeyHandler + virtual bool IsPressed() const override { return m_bHeld; } + virtual bool OnDigitalMotion(bool bPressed, unsigned int holdTimeMs) override; + virtual bool OnAnalogMotion(float magnitude, unsigned int motionTimeMs) override; + + private: + void Reset(); + + bool HandleActions(std::vector actions, float magnitude, unsigned int holdTimeMs); + bool HandleRelease(std::vector actions); + + bool HandleAction(const KeymapAction& action, 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; + }; +} +} diff --git a/xbmc/input/joysticks/keymaps/KeymapHandler.cpp b/xbmc/input/joysticks/keymaps/KeymapHandler.cpp new file mode 100644 index 0000000000000..e6a58296d5e4c --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeymapHandler.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "KeymapHandler.h" +#include "KeyHandler.h" +#include "games/controllers/Controller.h" +#include "games/GameServices.h" +#include "input/joysticks/interfaces/IKeyHandler.h" +#include "input/joysticks/JoystickTranslator.h" +#include "input/joysticks/JoystickUtils.h" +#include "input/IKeymap.h" +#include "input/IKeymapEnvironment.h" + +#include +#include +#include +#include + +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); +} + +bool CKeymapHandler::HotkeysPressed(const std::set &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::GetDirections()) + { + if (HasAction(CJoystickUtils::MakeKeyName(feature, dir))) + return true; + } + + return false; +} + +bool CKeymapHandler::OnButtonPress(const FeatureName& feature, bool bPressed) +{ + 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) +{ + 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) +{ + bool bHandled = false; + + // Calculate the direction of the stick's position + const ANALOG_STICK_DIRECTION analogStickDir = CJoystickTranslator::VectorToAnalogStickDirection(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::GetDirections()) + { + if (dir != analogStickDir) + DeactivateDirection(feature, dir); + } + + // Now activate direction the analog stick is pointing + if (analogStickDir != ANALOG_STICK_DIRECTION::UNKNOWN) + bHandled = ActivateDirection(feature, magnitude, analogStickDir, 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); +} + +IKeyHandler *CKeymapHandler::GetKeyHandler(const std::string &keyName) +{ + auto it = m_keyHandlers.find(keyName); + if (it == m_keyHandlers.end()) + { + std::unique_ptr 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); + 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 0000000000000..dfa05d7aa8340 --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeymapHandler.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "input/joysticks/IInputHandler.h" +#include "input/joysticks/interfaces/IKeymapHandler.h" +#include "input/joysticks/JoystickTypes.h" + +#include +#include +#include + +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); + + virtual ~CKeymapHandler() = default; + + // implementation of IKeymapHandler + virtual bool HotkeysPressed(const std::set& keyNames) const override; + virtual std::string GetLastPressed() const override { return m_lastPressed; } + virtual void OnPress(const std::string& keyName) override { m_lastPressed = keyName; } + + // implementation of IInputHandler + virtual std::string ControllerID() const override; + virtual bool HasFeature(const FeatureName& feature) const override { return true; } + virtual bool AcceptsInput(const FeatureName& feature) const override; + virtual bool OnButtonPress(const FeatureName& feature, bool bPressed) override; + virtual void OnButtonHold(const FeatureName& feature, unsigned int holdTimeMs) override; + virtual bool OnButtonMotion(const FeatureName& feature, float magnitude, unsigned int motionTimeMs) override; + virtual bool OnAnalogStickMotion(const FeatureName& feature, float x, float y, unsigned int motionTimeMs) override; + virtual bool OnAccelerometerMotion(const FeatureName& feature, float x, float y, float z) override; + + 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); + + // 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> m_keyHandlers; // Key name -> handler + + // Last pressed key + std::string m_lastPressed; + }; +} +} diff --git a/xbmc/input/joysticks/keymaps/KeymapHandling.cpp b/xbmc/input/joysticks/keymaps/KeymapHandling.cpp new file mode 100644 index 0000000000000..bff29f9896d0a --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeymapHandling.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "KeymapHandling.h" +#include "KeymapHandler.h" +#include "input/joysticks/IInputHandler.h" +#include "input/joysticks/IInputProvider.h" +#include "input/Keymap.h" +#include "input/ButtonTranslator.h" +#include "input/InputManager.h" +#include "ServiceBroker.h" + +#include +#include + +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 &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 &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 keymap(new CKeymap(std::move(windowKeymap), m_environment)); + + // Create keymap handler + std::unique_ptr 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() +{ + 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 0000000000000..58bc18fbb1ce7 --- /dev/null +++ b/xbmc/input/joysticks/keymaps/KeymapHandling.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "utils/Observer.h" + +#include +#include +#include + +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); + + virtual ~CKeymapHandling(); + + /*! + * \brief + */ + IInputReceiver *GetInputReceiver(const std::string &controllerId) const; + + /*! + * \brief + */ + IKeymap *GetKeymap(const std::string &controllerId) const; + + // implementation of Observer + virtual void Notify(const Observable &obs, const ObservableMessage msg) override; + + private: + void LoadKeymaps(); + void UnloadKeymaps(); + + // Construction parameter + IInputProvider *const m_inputProvider; + const bool m_pPromiscuous; + const IKeymapEnvironment *const m_environment; + + std::vector> m_keymaps; + std::vector> m_inputHandlers; + }; +} +} diff --git a/xbmc/input/keyboard/CMakeLists.txt b/xbmc/input/keyboard/CMakeLists.txt index 69d84b8d3a722..517dab53c564a 100644 --- a/xbmc/input/keyboard/CMakeLists.txt +++ b/xbmc/input/keyboard/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCES KeyboardEasterEgg.cpp set(HEADERS IActionMap.h IKeyboardHandler.h + IKeyboardInputProvider.h KeyboardEasterEgg.h KeymapActionMap.h ) diff --git a/xbmc/input/keyboard/IKeyboardInputProvider.h b/xbmc/input/keyboard/IKeyboardInputProvider.h new file mode 100644 index 0000000000000..eeb7d8719fc5b --- /dev/null +++ b/xbmc/input/keyboard/IKeyboardInputProvider.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +namespace KODI +{ +namespace KEYBOARD +{ + class IKeyboardHandler; + + /*! + * \ingroup mouse + * \brief Interface for classes that can provide mouse input + */ + class IKeyboardInputProvider + { + public: + virtual ~IKeyboardInputProvider() = default; + + /*! + * \brief Registers a handler to be called on keyboard input + * + * \param handler The handler to receive keyboard input provided by this class + */ + virtual void RegisterKeyboardHandler(IKeyboardHandler* handler) = 0; + + /*! + * \brief Unregisters handler from keyboard input + * + * \param handler The handler that was receiving keyboard input + */ + virtual void UnregisterKeyboardHandler(IKeyboardHandler* handler) = 0; + }; +} +} diff --git a/xbmc/input/keyboard/KeymapActionMap.cpp b/xbmc/input/keyboard/KeymapActionMap.cpp index 41fe927186104..ec46d6b1341bb 100644 --- a/xbmc/input/keyboard/KeymapActionMap.cpp +++ b/xbmc/input/keyboard/KeymapActionMap.cpp @@ -21,14 +21,15 @@ #include "KeymapActionMap.h" #include "guilib/GUIWindowManager.h" #include "input/Action.h" -#include "input/ButtonTranslator.h" +#include "input/InputManager.h" #include "input/Key.h" +#include "ServiceBroker.h" using namespace KODI; using namespace KEYBOARD; unsigned int CKeymapActionMap::GetActionID(const CKey& key) { - CAction action = CButtonTranslator::GetInstance().GetAction(g_windowManager.GetActiveWindowID(), key); + CAction action = CServiceBroker::GetInputManager().GetAction(g_windowManager.GetActiveWindowID(), key); return action.GetID(); } diff --git a/xbmc/input/linux/LIRC.cpp b/xbmc/input/linux/LIRC.cpp index 9b6c9f9efcc5d..82e5cb13f51ff 100644 --- a/xbmc/input/linux/LIRC.cpp +++ b/xbmc/input/linux/LIRC.cpp @@ -32,12 +32,13 @@ #ifdef HAVE_INOTIFY #include #endif -#include "input/ButtonTranslator.h" +#include "input/InputManager.h" #include "linux/PlatformDefs.h" #include "utils/log.h" #include "settings/AdvancedSettings.h" #include "utils/TimeUtils.h" #include "threads/SingleLock.h" +#include "ServiceBroker.h" CRemoteControl::CRemoteControl() : CThread("RemoteControl") @@ -255,7 +256,7 @@ void CRemoteControl::Update() buttonName[ buttonNameLen - 2 ] = '\0'; } - m_button = CButtonTranslator::GetInstance().TranslateLircRemoteString(deviceName, buttonName); + m_button = CServiceBroker::GetInputManager().TranslateLircRemoteString(deviceName, buttonName); char *end = NULL; long repeat = strtol(repeatStr, &end, 16); diff --git a/xbmc/input/mouse/CMakeLists.txt b/xbmc/input/mouse/CMakeLists.txt index 5a2f7b4560fd8..a99651611b898 100644 --- a/xbmc/input/mouse/CMakeLists.txt +++ b/xbmc/input/mouse/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCES MouseWindowingButtonMap.cpp) set(HEADERS IMouseButtonMap.h IMouseDriverHandler.h IMouseInputHandler.h + IMouseInputProvider.h MouseWindowingButtonMap.h) core_add_library(input_mouse) diff --git a/xbmc/input/mouse/IMouseInputProvider.h b/xbmc/input/mouse/IMouseInputProvider.h new file mode 100644 index 0000000000000..b2069aa1e6cb3 --- /dev/null +++ b/xbmc/input/mouse/IMouseInputProvider.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +namespace KODI +{ +namespace MOUSE +{ + class IMouseInputHandler; + + /*! + * \ingroup mouse + * \brief Interface for classes that can provide mouse input + */ + class IMouseInputProvider + { + public: + virtual ~IMouseInputProvider() = default; + + /*! + * \brief Registers a handler to be called on mouse input + * + * \param handler The handler to receive mouse input provided by this class + * + * \return The controller ID that serves as a context for incoming mouse events + * + * \sa IMouseButtonMap + */ + virtual std::string RegisterMouseHandler(IMouseInputHandler* handler) = 0; + + /*! + * \brief Unregisters handler from mouse input + * + * \param[in] handler The handler that was receiving mouse input + */ + virtual void UnregisterMouseHandler(IMouseInputHandler* handler) = 0; + }; +} +} diff --git a/xbmc/input/windows/IRServerSuite.cpp b/xbmc/input/windows/IRServerSuite.cpp index 880150a5d4ab1..3df7df5cc2943 100644 --- a/xbmc/input/windows/IRServerSuite.cpp +++ b/xbmc/input/windows/IRServerSuite.cpp @@ -20,8 +20,9 @@ #include "IRServerSuite.h" #include "IrssMessage.h" -#include "input/ButtonTranslator.h" +#include "input/InputManager.h" #include "utils/log.h" +#include "ServiceBroker.h" #include #define IRSS_PORT 24000 @@ -357,9 +358,9 @@ bool CRemoteControl::HandleRemoteEvent(CIrssMessage& message) deviceName[devicenamelength] = '\0'; keycode[keycodelength] = '\0'; //translate to a buttoncode xbmc understands - m_button = CButtonTranslator::GetInstance().TranslateLircRemoteString(deviceName, keycode); + m_button = CServiceBroker::GetInputManager().TranslateLircRemoteString(deviceName, keycode); CLog::Log(LOGDEBUG, "IRServerSuite, RemoteEvent: %s %s", deviceName, keycode); - + delete[] deviceName; delete[] keycode; return true; diff --git a/xbmc/interfaces/builtins/Builtins.cpp b/xbmc/interfaces/builtins/Builtins.cpp index b17730308654c..861d838ab56d6 100644 --- a/xbmc/interfaces/builtins/Builtins.cpp +++ b/xbmc/interfaces/builtins/Builtins.cpp @@ -94,7 +94,7 @@ bool CBuiltins::HasCommand(const std::string& execString) CUtil::SplitExecFunction(execString, function, parameters); StringUtils::ToLower(function); - if (CInputManager::GetInstance().HasBuiltin(function)) + if (CServiceBroker::GetInputManager().HasBuiltin(function)) return true; const auto& it = m_command.find(function); @@ -174,5 +174,5 @@ int CBuiltins::Execute(const std::string& execString) } } else - return CInputManager::GetInstance().ExecuteBuiltin(execute, params); + return CServiceBroker::GetInputManager().ExecuteBuiltin(execute, params); } diff --git a/xbmc/music/dialogs/GUIDialogMusicOSD.cpp b/xbmc/music/dialogs/GUIDialogMusicOSD.cpp index 30fa55dfa7114..7a1066a338cdd 100644 --- a/xbmc/music/dialogs/GUIDialogMusicOSD.cpp +++ b/xbmc/music/dialogs/GUIDialogMusicOSD.cpp @@ -31,6 +31,7 @@ #include "xbmc/FileItem.h" #include "xbmc/music/tags/MusicInfoTag.h" #include "xbmc/music/MusicDatabase.h" +#include "ServiceBroker.h" #define CONTROL_VIS_BUTTON 500 #define CONTROL_LOCK_BUTTON 501 @@ -132,7 +133,7 @@ void CGUIDialogMusicOSD::FrameMove() if (m_autoClosing) { // check for movement of mouse or a submenu open - if (CInputManager::GetInstance().IsMouseActive() || + if (CServiceBroker::GetInputManager().IsMouseActive() || g_windowManager.IsWindowActive(WINDOW_DIALOG_VIS_SETTINGS) || g_windowManager.IsWindowActive(WINDOW_DIALOG_VIS_PRESET_LIST) || g_windowManager.IsWindowActive(WINDOW_DIALOG_AUDIO_DSP_OSD_SETTINGS) || diff --git a/xbmc/music/windows/GUIWindowVisualisation.cpp b/xbmc/music/windows/GUIWindowVisualisation.cpp index c24cb7b8cd553..ce303ca62633d 100644 --- a/xbmc/music/windows/GUIWindowVisualisation.cpp +++ b/xbmc/music/windows/GUIWindowVisualisation.cpp @@ -26,7 +26,7 @@ #include "GUIUserMessages.h" #include "GUIInfoManager.h" #include "guilib/GUIWindowManager.h" -#include "input/ButtonTranslator.h" +#include "input/InputManager.h" #include "input/Key.h" #include "settings/AdvancedSettings.h" #include "settings/Settings.h" @@ -49,7 +49,7 @@ bool CGUIWindowVisualisation::OnAction(const CAction &action) { if (CServiceBroker::GetSettings().GetBool(CSettings::SETTING_PVRPLAYBACK_CONFIRMCHANNELSWITCH) && g_infoManager.IsPlayerChannelPreviewActive() && - (action.GetID() == ACTION_SELECT_ITEM || CButtonTranslator::GetInstance().GetGlobalAction(action.GetButtonCode()).GetID() == ACTION_SELECT_ITEM)) + (action.GetID() == ACTION_SELECT_ITEM || CServiceBroker::GetInputManager().GetGlobalAction(action.GetButtonCode()).GetID() == ACTION_SELECT_ITEM)) { // If confirm channel switch is active, channel preview is currently shown // and the button that caused this action matches (global) action "Select" (OK) diff --git a/xbmc/network/EventClient.cpp b/xbmc/network/EventClient.cpp index 6ad15fb832f49..343a960d8a6e7 100644 --- a/xbmc/network/EventClient.cpp +++ b/xbmc/network/EventClient.cpp @@ -26,8 +26,8 @@ #include "EventClient.h" #include "EventPacket.h" #include "threads/SingleLock.h" -#include "input/ButtonTranslator.h" #include "input/GamepadTranslator.h" +#include "input/InputManager.h" #include "input/IRTranslator.h" #include "input/KeyboardTranslator.h" #include @@ -40,6 +40,7 @@ #include "input/Key.h" #include "guilib/LocalizeStrings.h" #include "utils/StringUtils.h" +#include "ServiceBroker.h" using namespace EVENTCLIENT; using namespace EVENTPACKET; @@ -94,7 +95,7 @@ void CEventButtonState::Load() { #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE) std::string lircDevice = m_mapName.substr(3); - m_iKeyCode = CButtonTranslator::GetInstance().TranslateLircRemoteString( lircDevice.c_str(), + m_iKeyCode = CServiceBroker::GetInputManager().TranslateLircRemoteString( lircDevice.c_str(), m_buttonName.c_str() ); #else CLog::Log(LOGERROR, "ES: LIRC support not enabled"); diff --git a/xbmc/peripherals/addons/AddonButtonMapping.cpp b/xbmc/peripherals/addons/AddonButtonMapping.cpp index abc2322db1f31..0ef744803b273 100644 --- a/xbmc/peripherals/addons/AddonButtonMapping.cpp +++ b/xbmc/peripherals/addons/AddonButtonMapping.cpp @@ -39,10 +39,12 @@ CAddonButtonMapping::CAddonButtonMapping(CPeripherals& manager, CPeripheral* per } else { - m_buttonMap.reset(new CAddonButtonMap(peripheral, addon, mapper->ControllerID())); + const std::string controllerId = mapper->ControllerID(); + m_buttonMap.reset(new CAddonButtonMap(peripheral, addon, controllerId)); if (m_buttonMap->Load()) { - m_buttonMapping.reset(new CButtonMapping(mapper, m_buttonMap.get(), peripheral->GetActionMap())); + IKeymap *keymap = peripheral->GetKeymap(controllerId); + m_buttonMapping.reset(new CButtonMapping(mapper, m_buttonMap.get(), keymap)); // Allow the mapper to save our button map mapper->SetButtonMapCallback(peripheral->DeviceName(), this); diff --git a/xbmc/peripherals/devices/Peripheral.h b/xbmc/peripherals/devices/Peripheral.h index 7e42d4db1c947..8e5c20800f6aa 100644 --- a/xbmc/peripherals/devices/Peripheral.h +++ b/xbmc/peripherals/devices/Peripheral.h @@ -29,12 +29,12 @@ class TiXmlDocument; class CSetting; +class IKeymap; namespace KODI { namespace JOYSTICK { - class IActionMap; class IButtonMapper; class IDriverHandler; class IDriverReceiver; @@ -210,7 +210,7 @@ namespace PERIPHERALS virtual KODI::JOYSTICK::IDriverReceiver* GetDriverReceiver() { return nullptr; } - virtual KODI::JOYSTICK::IActionMap* GetActionMap() { return nullptr; } + virtual IKeymap *GetKeymap(const std::string &controllerId) { return nullptr; } protected: virtual void ClearSettings(void); diff --git a/xbmc/peripherals/devices/PeripheralHID.cpp b/xbmc/peripherals/devices/PeripheralHID.cpp index 285fccfc450a2..7636978a56cdb 100644 --- a/xbmc/peripherals/devices/PeripheralHID.cpp +++ b/xbmc/peripherals/devices/PeripheralHID.cpp @@ -21,7 +21,8 @@ #include "PeripheralHID.h" #include "utils/log.h" #include "guilib/LocalizeStrings.h" -#include "input/ButtonTranslator.h" +#include "input/InputManager.h" +#include "ServiceBroker.h" using namespace PERIPHERALS; @@ -37,7 +38,7 @@ CPeripheralHID::~CPeripheralHID(void) if (!m_strKeymap.empty() && !GetSettingBool("do_not_use_custom_keymap")) { CLog::Log(LOGDEBUG, "%s - switching active keymapping to: default", __FUNCTION__); - CButtonTranslator::GetInstance().RemoveDevice(m_strKeymap); + CServiceBroker::GetInputManager().RemoveKeymap(m_strKeymap); } } @@ -65,12 +66,12 @@ bool CPeripheralHID::InitialiseFeature(const PeripheralFeature feature) if (bKeymapEnabled) { CLog::Log(LOGDEBUG, "%s - adding keymapping for: %s", __FUNCTION__, m_strKeymap.c_str()); - CButtonTranslator::GetInstance().AddDevice(m_strKeymap); + CServiceBroker::GetInputManager().AddKeymap(m_strKeymap); } else if (!bKeymapEnabled) { CLog::Log(LOGDEBUG, "%s - removing keymapping for: %s", __FUNCTION__, m_strKeymap.c_str()); - CButtonTranslator::GetInstance().RemoveDevice(m_strKeymap); + CServiceBroker::GetInputManager().RemoveKeymap(m_strKeymap); } } diff --git a/xbmc/peripherals/devices/PeripheralJoystick.cpp b/xbmc/peripherals/devices/PeripheralJoystick.cpp index 6fc6f93ed501f..0b15b15de5d5c 100644 --- a/xbmc/peripherals/devices/PeripheralJoystick.cpp +++ b/xbmc/peripherals/devices/PeripheralJoystick.cpp @@ -19,9 +19,12 @@ */ #include "PeripheralJoystick.h" +#include "input/joysticks/keymaps/KeymapHandling.h" #include "input/joysticks/DeadzoneFilter.h" #include "input/joysticks/JoystickIDs.h" #include "input/joysticks/JoystickTranslator.h" +#include "input/joysticks/RumbleGenerator.h" +#include "input/InputManager.h" #include "peripherals/Peripherals.h" #include "peripherals/addons/AddonButtonMap.h" #include "peripherals/bus/android/PeripheralBusAndroid.h" @@ -29,6 +32,7 @@ #include "threads/SingleLock.h" #include "utils/log.h" #include "Application.h" +#include "ServiceBroker.h" #include @@ -43,7 +47,8 @@ CPeripheralJoystick::CPeripheralJoystick(CPeripherals& manager, const Peripheral m_hatCount(0), m_axisCount(0), m_motorCount(0), - m_supportsPowerOff(false) + m_supportsPowerOff(false), + m_rumbleGenerator(new CRumbleGenerator) { m_features.push_back(FEATURE_JOYSTICK); // FEATURE_RUMBLE conditionally added via SetMotorCount() @@ -51,11 +56,12 @@ CPeripheralJoystick::CPeripheralJoystick(CPeripherals& manager, const Peripheral CPeripheralJoystick::~CPeripheralJoystick(void) { + m_rumbleGenerator->AbortRumble(); + UnregisterJoystickDriverHandler(&m_joystickMonitor); + m_rumbleGenerator->AbortRumble(); + m_appInput.reset(); m_deadzoneFilter.reset(); m_buttonMap.reset(); - m_defaultController.AbortRumble(); - UnregisterInputHandler(&m_defaultController); - UnregisterJoystickDriverHandler(&m_joystickMonitor); } bool CPeripheralJoystick::InitialiseFeature(const PeripheralFeature feature) @@ -76,7 +82,7 @@ bool CPeripheralJoystick::InitialiseFeature(const PeripheralFeature feature) InitializeDeadzoneFiltering(); // Give joystick monitor priority over default controller - RegisterInputHandler(&m_defaultController, false); + m_appInput.reset(new CKeymapHandling(this, false, CServiceBroker::GetInputManager().KeymapEnvironment())); RegisterJoystickDriverHandler(&m_joystickMonitor, false); } } @@ -118,7 +124,8 @@ void CPeripheralJoystick::InitializeDeadzoneFiltering() void CPeripheralJoystick::OnUserNotification() { - m_defaultController.NotifyUser(); + IInputReceiver *inputReceiver = m_appInput->GetInputReceiver(m_rumbleGenerator->ControllerID()); + m_rumbleGenerator->NotifyUser(inputReceiver); } bool CPeripheralJoystick::TestFeature(PeripheralFeature feature) @@ -128,8 +135,11 @@ bool CPeripheralJoystick::TestFeature(PeripheralFeature feature) switch (feature) { case FEATURE_RUMBLE: - bSuccess = m_defaultController.TestRumble(); + { + IInputReceiver *inputReceiver = m_appInput->GetInputReceiver(m_rumbleGenerator->ControllerID()); + bSuccess = m_rumbleGenerator->DoTest(inputReceiver); break; + } case FEATURE_POWER_OFF: if (m_supportsPowerOff) { @@ -168,6 +178,11 @@ void CPeripheralJoystick::UnregisterJoystickDriverHandler(IDriverHandler* handle }), m_driverHandlers.end()); } +IKeymap *CPeripheralJoystick::GetKeymap(const std::string &controllerId) +{ + return m_appInput->GetKeymap(controllerId); +} + bool CPeripheralJoystick::OnButtonMotion(unsigned int buttonIndex, bool bPressed) { CLog::Log(LOGDEBUG, "BUTTON [ %u ] on \"%s\" %s", buttonIndex, diff --git a/xbmc/peripherals/devices/PeripheralJoystick.h b/xbmc/peripherals/devices/PeripheralJoystick.h index 1bc9e81cdf7e5..f8186cffc585f 100644 --- a/xbmc/peripherals/devices/PeripheralJoystick.h +++ b/xbmc/peripherals/devices/PeripheralJoystick.h @@ -20,7 +20,6 @@ #pragma once #include "Peripheral.h" -#include "input/joysticks/DefaultController.h" #include "input/joysticks/IDriverReceiver.h" #include "input/joysticks/JoystickMonitor.h" #include "input/joysticks/JoystickTypes.h" @@ -37,6 +36,8 @@ namespace KODI namespace JOYSTICK { class CDeadzoneFilter; + class CKeymapHandling; + class CRumbleGenerator; class IButtonMap; class IDriverHandler; } @@ -61,7 +62,7 @@ namespace PERIPHERALS void RegisterJoystickDriverHandler(KODI::JOYSTICK::IDriverHandler* handler, bool bPromiscuous) override; void UnregisterJoystickDriverHandler(KODI::JOYSTICK::IDriverHandler* handler) override; KODI::JOYSTICK::IDriverReceiver* GetDriverReceiver() override { return this; } - KODI::JOYSTICK::IActionMap* GetActionMap() override { return &m_defaultController; } + IKeymap *GetKeymap(const std::string &controllerId) override; bool OnButtonMotion(unsigned int buttonIndex, bool bPressed); bool OnHatMotion(unsigned int hatIndex, KODI::JOYSTICK::HAT_STATE state); @@ -125,7 +126,8 @@ namespace PERIPHERALS unsigned int m_axisCount; unsigned int m_motorCount; bool m_supportsPowerOff; - KODI::JOYSTICK::CDefaultController m_defaultController; + std::unique_ptr m_appInput; + std::unique_ptr m_rumbleGenerator; KODI::JOYSTICK::CJoystickMonitor m_joystickMonitor; std::unique_ptr m_buttonMap; std::unique_ptr m_deadzoneFilter; diff --git a/xbmc/peripherals/devices/PeripheralJoystickEmulation.cpp b/xbmc/peripherals/devices/PeripheralJoystickEmulation.cpp index 80cfb60d4b064..d97d05404b44f 100644 --- a/xbmc/peripherals/devices/PeripheralJoystickEmulation.cpp +++ b/xbmc/peripherals/devices/PeripheralJoystickEmulation.cpp @@ -22,6 +22,7 @@ #include "input/keyboard/generic/JoystickEmulation.h" #include "input/InputManager.h" #include "threads/SingleLock.h" +#include "ServiceBroker.h" #include @@ -36,7 +37,7 @@ CPeripheralJoystickEmulation::CPeripheralJoystickEmulation(CPeripherals& manager CPeripheralJoystickEmulation::~CPeripheralJoystickEmulation(void) { - CInputManager::GetInstance().UnregisterKeyboardHandler(this); + CServiceBroker::GetInputManager().UnregisterKeyboardHandler(this); } bool CPeripheralJoystickEmulation::InitialiseFeature(const PeripheralFeature feature) @@ -47,7 +48,7 @@ bool CPeripheralJoystickEmulation::InitialiseFeature(const PeripheralFeature fea { if (feature == FEATURE_JOYSTICK) { - CInputManager::GetInstance().RegisterKeyboardHandler(this); + CServiceBroker::GetInputManager().RegisterKeyboardHandler(this); } bSuccess = true; } diff --git a/xbmc/profiles/ProfilesManager.cpp b/xbmc/profiles/ProfilesManager.cpp index 5e0b6d8f814eb..2a11ca773ef5e 100644 --- a/xbmc/profiles/ProfilesManager.cpp +++ b/xbmc/profiles/ProfilesManager.cpp @@ -40,7 +40,6 @@ #include "filesystem/SpecialProtocol.h" #include "guilib/GUIWindowManager.h" #include "guilib/LocalizeStrings.h" -#include "input/ButtonTranslator.h" #include "input/InputManager.h" #include "settings/Settings.h" #if !defined(TARGET_WINDOWS) && defined(HAS_DVD_DRIVE) @@ -266,9 +265,9 @@ bool CProfilesManager::LoadProfile(size_t index) CreateProfileFolders(); CDatabaseManager::GetInstance().Initialize(); - CButtonTranslator::GetInstance().Load(true); + CServiceBroker::GetInputManager().LoadKeymaps(); - CInputManager::GetInstance().SetMouseEnabled(CServiceBroker::GetSettings().GetBool(CSettings::SETTING_INPUT_ENABLEMOUSE)); + CServiceBroker::GetInputManager().SetMouseEnabled(CServiceBroker::GetSettings().GetBool(CSettings::SETTING_INPUT_ENABLEMOUSE)); g_infoManager.ResetCache(); g_infoManager.ResetLibraryBools(); diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp index 3fc90f8e169fe..51bfefcc44297 100644 --- a/xbmc/settings/Settings.cpp +++ b/xbmc/settings/Settings.cpp @@ -821,7 +821,7 @@ void CSettings::UninitializeISettingsHandlers() GetSettingsManager()->UnregisterCallback(&g_charsetConverter); GetSettingsManager()->UnregisterCallback(&g_graphicsContext); GetSettingsManager()->UnregisterCallback(&g_langInfo); - GetSettingsManager()->UnregisterCallback(&CInputManager::GetInstance()); + GetSettingsManager()->UnregisterCallback(&CServiceBroker::GetInputManager()); GetSettingsManager()->UnregisterCallback(&CNetworkServices::GetInstance()); GetSettingsManager()->UnregisterCallback(&g_passwordManager); GetSettingsManager()->UnregisterCallback(&CRssManager::GetInstance()); @@ -980,7 +980,7 @@ void CSettings::InitializeISettingCallbacks() settingSet.clear(); settingSet.insert(CSettings::SETTING_INPUT_ENABLEMOUSE); - GetSettingsManager()->RegisterCallback(&CInputManager::GetInstance(), settingSet); + GetSettingsManager()->RegisterCallback(&CServiceBroker::GetInputManager(), settingSet); settingSet.clear(); settingSet.insert(CSettings::SETTING_SERVICES_WEBSERVER); @@ -1088,7 +1088,7 @@ void CSettings::UninitializeISettingCallbacks() GetSettingsManager()->UnregisterCallback(&g_charsetConverter); GetSettingsManager()->UnregisterCallback(&g_graphicsContext); GetSettingsManager()->UnregisterCallback(&g_langInfo); - GetSettingsManager()->UnregisterCallback(&CInputManager::GetInstance()); + GetSettingsManager()->UnregisterCallback(&CServiceBroker::GetInputManager()); GetSettingsManager()->UnregisterCallback(&CNetworkServices::GetInstance()); GetSettingsManager()->UnregisterCallback(&g_passwordManager); GetSettingsManager()->UnregisterCallback(&CRssManager::GetInstance()); diff --git a/xbmc/utils/Observer.h b/xbmc/utils/Observer.h index 49eec5b8fc2de..f8ea1f8c74082 100644 --- a/xbmc/utils/Observer.h +++ b/xbmc/utils/Observer.h @@ -46,6 +46,7 @@ typedef enum ObservableMessageManagerStopped, ObservableMessagePortsChanged, ObservableMessageSettingsChanged, + ObservableMessageButtonMapsChanged, } ObservableMessage; class Observer diff --git a/xbmc/video/dialogs/GUIDialogVideoOSD.cpp b/xbmc/video/dialogs/GUIDialogVideoOSD.cpp index 628f32290e4e9..34547c43b6800 100644 --- a/xbmc/video/dialogs/GUIDialogVideoOSD.cpp +++ b/xbmc/video/dialogs/GUIDialogVideoOSD.cpp @@ -24,6 +24,7 @@ #include "guilib/GUIWindowManager.h" #include "input/Key.h" #include "input/InputManager.h" +#include "ServiceBroker.h" using namespace PVR; @@ -42,7 +43,7 @@ void CGUIDialogVideoOSD::FrameMove() if (m_autoClosing) { // check for movement of mouse or a submenu open - if (CInputManager::GetInstance().IsMouseActive() + if (CServiceBroker::GetInputManager().IsMouseActive() || g_windowManager.IsWindowActive(WINDOW_DIALOG_AUDIO_OSD_SETTINGS) || g_windowManager.IsWindowActive(WINDOW_DIALOG_AUDIO_DSP_OSD_SETTINGS) || g_windowManager.IsWindowActive(WINDOW_DIALOG_VIDEO_OSD_SETTINGS) diff --git a/xbmc/video/windows/GUIWindowFullScreen.cpp b/xbmc/video/windows/GUIWindowFullScreen.cpp index d861a74c5eec8..d1cad59d4ffc5 100644 --- a/xbmc/video/windows/GUIWindowFullScreen.cpp +++ b/xbmc/video/windows/GUIWindowFullScreen.cpp @@ -41,7 +41,7 @@ #include "threads/SingleLock.h" #include "utils/StringUtils.h" #include "XBDateTime.h" -#include "input/ButtonTranslator.h" +#include "input/InputManager.h" #include "windowing/WindowingFactory.h" #include "cores/IPlayer.h" #include "guiinfo/GUIInfoLabels.h" @@ -111,7 +111,7 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action) { if (CServiceBroker::GetSettings().GetBool(CSettings::SETTING_PVRPLAYBACK_CONFIRMCHANNELSWITCH) && g_infoManager.IsPlayerChannelPreviewActive() && - (action.GetID() == ACTION_SELECT_ITEM || CButtonTranslator::GetInstance().GetGlobalAction(action.GetButtonCode()).GetID() == ACTION_SELECT_ITEM)) + (action.GetID() == ACTION_SELECT_ITEM || CServiceBroker::GetInputManager().GetGlobalAction(action.GetButtonCode()).GetID() == ACTION_SELECT_ITEM)) { // If confirm channel switch is active, channel preview is currently shown // and the button that caused this action matches (global) action "Select" (OK) diff --git a/xbmc/windowing/X11/WinEventsX11.cpp b/xbmc/windowing/X11/WinEventsX11.cpp index 2ae26e9735a77..d6e11b65d5b53 100644 --- a/xbmc/windowing/X11/WinEventsX11.cpp +++ b/xbmc/windowing/X11/WinEventsX11.cpp @@ -37,6 +37,7 @@ #include "guilib/GUIWindowManager.h" #include "input/MouseStat.h" #include "input/InputManager.h" +#include "ServiceBroker.h" using namespace KODI::MESSAGING; @@ -517,7 +518,7 @@ bool CWinEventsX11Imp::MessagePump() // lose mouse coverage case LeaveNotify: { - CInputManager::GetInstance().SetMouseActive(false); + CServiceBroker::GetInputManager().SetMouseActive(false); break; } diff --git a/xbmc/windowing/X11/WinSystemX11.cpp b/xbmc/windowing/X11/WinSystemX11.cpp index 082250895405e..9ddc7fb921c32 100644 --- a/xbmc/windowing/X11/WinSystemX11.cpp +++ b/xbmc/windowing/X11/WinSystemX11.cpp @@ -628,7 +628,7 @@ bool CWinSystemX11::SetWindow(int width, int height, bool fullscreen, const std: if (!m_mainWindow) { - CInputManager::GetInstance().SetMouseActive(false); + CServiceBroker::GetInputManager().SetMouseActive(false); } if (m_mainWindow && ((m_bFullScreen != fullscreen) || m_currentOutput.compare(output) != 0 || m_windowDirty)) @@ -658,7 +658,7 @@ bool CWinSystemX11::SetWindow(int width, int height, bool fullscreen, const std: } } - CInputManager::GetInstance().SetMouseActive(false); + CServiceBroker::GetInputManager().SetMouseActive(false); OnLostDevice(); DestroyWindow(); m_windowDirty = true; diff --git a/xbmc/windowing/osx/WinEventsSDL.cpp b/xbmc/windowing/osx/WinEventsSDL.cpp index d881231cf2412..d7fd8aa798520 100644 --- a/xbmc/windowing/osx/WinEventsSDL.cpp +++ b/xbmc/windowing/osx/WinEventsSDL.cpp @@ -31,6 +31,7 @@ #include "input/MouseStat.h" #include "windowing/WindowingFactory.h" #include "platform/darwin/osx/CocoaInterface.h" +#include "ServiceBroker.h" using namespace KODI::MESSAGING; @@ -131,7 +132,7 @@ bool CWinEventsSDL::MessagePump() { if (0 == (SDL_GetAppState() & SDL_APPMOUSEFOCUS)) { - CInputManager::GetInstance().SetMouseActive(false); + CServiceBroker::GetInputManager().SetMouseActive(false); // See CApplication::ProcessSlow() for a description as to why we call Cocoa_HideMouse. // this is here to restore the pointer when toggling back to window mode from fullscreen. Cocoa_ShowMouse(); diff --git a/xbmc/windows/GUIWindowFileManager.cpp b/xbmc/windows/GUIWindowFileManager.cpp index c812734bb7edb..6badba1319004 100644 --- a/xbmc/windows/GUIWindowFileManager.cpp +++ b/xbmc/windows/GUIWindowFileManager.cpp @@ -294,7 +294,7 @@ bool CGUIWindowFileManager::OnMessage(CGUIMessage& message) if (iAction == ACTION_HIGHLIGHT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) { OnMark(list, iItem); - if (!CInputManager::GetInstance().IsMouseActive()) + if (!CServiceBroker::GetInputManager().IsMouseActive()) { //move to next item CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), iControl, iItem + 1); diff --git a/xbmc/windows/GUIWindowPointer.cpp b/xbmc/windows/GUIWindowPointer.cpp index 8e99ff323221c..9f6836fdacbf4 100644 --- a/xbmc/windows/GUIWindowPointer.cpp +++ b/xbmc/windows/GUIWindowPointer.cpp @@ -22,6 +22,8 @@ #include "input/MouseStat.h" #include "input/InputManager.h" #include "windowing/WindowingFactory.h" +#include "ServiceBroker.h" + #define ID_POINTER 10 CGUIWindowPointer::CGUIWindowPointer(void) @@ -58,7 +60,7 @@ void CGUIWindowPointer::UpdateVisibility() { if(g_Windowing.HasCursor()) { - if (CInputManager::GetInstance().IsMouseActive()) + if (CServiceBroker::GetInputManager().IsMouseActive()) Open(); else Close(); @@ -80,14 +82,14 @@ void CGUIWindowPointer::OnWindowLoaded() void CGUIWindowPointer::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) { - bool active = CInputManager::GetInstance().IsMouseActive(); + bool active = CServiceBroker::GetInputManager().IsMouseActive(); if (active != m_active) { MarkDirtyRegion(); m_active = active; } - MousePosition pos = CInputManager::GetInstance().GetMousePosition(); + MousePosition pos = CServiceBroker::GetInputManager().GetMousePosition(); SetPosition((float)pos.x, (float)pos.y); - SetPointer(CInputManager::GetInstance().GetMouseState()); + SetPointer(CServiceBroker::GetInputManager().GetMouseState()); return CGUIWindow::Process(currentTime, dirtyregions); }