diff options
32 files changed, 2849 insertions, 1 deletions
diff --git a/Makefile.in b/Makefile.in index 1c559cdb18..6f6745d827 100644 --- a/Makefile.in +++ b/Makefile.in @@ -153,6 +153,8 @@ INSTALL_FILTER += .*repository\.pvr-ios\.xbmc\.org.* INSTALL_FILTER += .*repository\.pvr-win32\.xbmc\.org.* INSTALL_FILTER += .*repository\.pvr-osx.*\.xbmc\.org.* ifeq (@USE_ANDROID@,1) +DIRECTORY_ARCHIVES += xbmc/input/joysticks/input_joysticks.a +DIRECTORY_ARCHIVES += xbmc/input/joysticks/generic/input_joysticks_generic.a DIRECTORY_ARCHIVES += xbmc/input/linux/input_linux.a DIRECTORY_ARCHIVES += xbmc/input/touch/input_touch.a DIRECTORY_ARCHIVES += xbmc/input/touch/generic/input_touch_generic.a @@ -161,6 +163,8 @@ DIRECTORY_ARCHIVES += xbmc/powermanagement/android/powermanagement_android.a DIRECTORY_ARCHIVES += xbmc/storage/android/storage_android.a DIRECTORY_ARCHIVES += xbmc/windowing/X11/windowing_X11.a else +DIRECTORY_ARCHIVES += xbmc/input/joysticks/input_joysticks.a +DIRECTORY_ARCHIVES += xbmc/input/joysticks/generic/input_joysticks_generic.a DIRECTORY_ARCHIVES += xbmc/input/linux/input_linux.a DIRECTORY_ARCHIVES += xbmc/input/touch/input_touch.a DIRECTORY_ARCHIVES += xbmc/input/touch/generic/input_touch_generic.a diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj index 89b73fbe67..90a292c7ad 100644 --- a/project/VS2010Express/XBMC.vcxproj +++ b/project/VS2010Express/XBMC.vcxproj @@ -561,6 +561,13 @@ <ClCompile Include="..\..\xbmc\input\InertialScrollingHandler.cpp" /> <ClCompile Include="..\..\xbmc\input\InputCodingTableKorean.cpp" /> <ClCompile Include="..\..\xbmc\input\InputManager.cpp" /> + <ClCompile Include="..\..\xbmc\input\joysticks\DriverPrimitive.cpp" /> + <ClCompile Include="..\..\xbmc\input\joysticks\generic\GenericJoystickButtonMapping.cpp" /> + <ClCompile Include="..\..\xbmc\input\joysticks\generic\GenericJoystickFeatureHandling.cpp" /> + <ClCompile Include="..\..\xbmc\input\joysticks\generic\GenericJoystickInputHandling.cpp" /> + <ClCompile Include="..\..\xbmc\input\joysticks\JoystickMonitor.cpp" /> + <ClCompile Include="..\..\xbmc\input\joysticks\JoystickTranslator.cpp" /> + <ClCompile Include="..\..\xbmc\input\joysticks\KeymapHandler.cpp" /> <ClCompile Include="..\..\xbmc\input\Key.cpp" /> <ClCompile Include="..\..\xbmc\input\InputCodingTableBaiduPY.cpp" /> <ClCompile Include="..\..\xbmc\input\InputCodingTableBasePY.cpp" /> @@ -1079,6 +1086,20 @@ <ClInclude Include="..\..\xbmc\input\InputCodingTableKorean.h" /> <ClInclude Include="..\..\xbmc\InfoScanner.h" /> <ClInclude Include="..\..\xbmc\input\InputManager.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\DriverPrimitive.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\generic\GenericJoystickButtonMapping.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\generic\GenericJoystickFeatureHandling.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\generic\GenericJoystickInputHandling.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\IJoystickButtonMap.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\IJoystickButtonMapper.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\IJoystickDriverHandler.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\IJoystickInputHandler.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\IKeymapHandler.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\JoystickMonitor.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\JoystickTranslator.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\JoystickTypes.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\JoystickUtils.h" /> + <ClInclude Include="..\..\xbmc\input\joysticks\KeymapHandler.h" /> <ClInclude Include="..\..\xbmc\input\Key.h" /> <ClInclude Include="..\..\xbmc\input\KeyboardLayoutManager.h" /> <ClInclude Include="..\..\xbmc\input\InputCodingTable.h" /> diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters index c6d6ac0238..c0df7a9354 100644 --- a/project/VS2010Express/XBMC.vcxproj.filters +++ b/project/VS2010Express/XBMC.vcxproj.filters @@ -421,6 +421,12 @@ <Filter Include="addons\binary\interfaces\api1\PVR"> <UniqueIdentifier>{c3708f40-3139-4ee9-b8f6-c6bcc22bb1c3}</UniqueIdentifier> </Filter> + <Filter Include="input\joysticks"> + <UniqueIdentifier>{5d8049b8-4689-4ff0-bf4f-1f0a308e5b44}</UniqueIdentifier> + </Filter> + <Filter Include="input\joysticks\generic"> + <UniqueIdentifier>{80a8356f-f3be-46b5-be2e-714a42764ee1}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="..\..\xbmc\win32\pch.cpp"> @@ -3328,6 +3334,27 @@ <ClCompile Include="..\..\xbmc\addons\binary\interfaces\AddonInterfaces.cpp"> <Filter>addons\binary\interfaces</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\input\joysticks\DriverPrimitive.cpp"> + <Filter>input\joysticks</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\input\joysticks\JoystickMonitor.cpp"> + <Filter>input\joysticks</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\input\joysticks\JoystickTranslator.cpp"> + <Filter>input\joysticks</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\input\joysticks\KeymapHandler.cpp"> + <Filter>input\joysticks</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\input\joysticks\generic\GenericJoystickButtonMapping.cpp"> + <Filter>input\joysticks\generic</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\input\joysticks\generic\GenericJoystickInputHandling.cpp"> + <Filter>input\joysticks\generic</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\input\joysticks\generic\GenericJoystickFeatureHandling.cpp"> + <Filter>input\joysticks\generic</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\xbmc\win32\pch.h"> @@ -6434,6 +6461,48 @@ <ClInclude Include="..\..\xbmc\addons\binary\interfaces\IAddonInterface.h"> <Filter>addons\binary\interfaces</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\DriverPrimitive.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\IJoystickButtonMap.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\IJoystickButtonMapper.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\IJoystickDriverHandler.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\IJoystickInputHandler.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\IKeymapHandler.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\JoystickMonitor.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\JoystickTranslator.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\JoystickTypes.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\JoystickUtils.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\KeymapHandler.h"> + <Filter>input\joysticks</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\generic\GenericJoystickButtonMapping.h"> + <Filter>input\joysticks\generic</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\generic\GenericJoystickInputHandling.h"> + <Filter>input\joysticks\generic</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\input\joysticks\generic\GenericJoystickFeatureHandling.h"> + <Filter>input\joysticks\generic</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc"> diff --git a/system/keymaps/joystick.xml b/system/keymaps/joystick.xml new file mode 100644 index 0000000000..976223edcc --- /dev/null +++ b/system/keymaps/joystick.xml @@ -0,0 +1,306 @@ +<?xml version="1.0" encoding="UTF-8"?> +<keymap> + <global> + <joystick> + <a>Select</a> + <b>Back</b> + <x>ContextMenu</x> + <y>FullScreen</y> + <start>ActivateWindow(PlayerControls)</start> + <back>Back</back> + <guide>ActivateWindow(Home)</guide> + <up>Up</up> + <down>Down</down> + <right>Right</right> + <left>Left</left> + <leftthumb>Screenshot</leftthumb> + <rightthumb>ActivateWindow(ShutdownMenu)</rightthumb> + <lefttrigger>ScrollUp</lefttrigger> + <righttrigger>ScrollDown</righttrigger> + <leftbumper>ScrollUp</leftbumper> + <rightbumper>ScrollDown</rightbumper> + <leftstickleft>Left</leftstickleft> + <leftstickright>Right</leftstickright> + <leftstickup>Up</leftstickup> + <leftstickdown>Down</leftstickdown> + <rightstickleft>VolumeDown</rightstickleft> + <rightstickright>VolumeUp</rightstickright> + <rightstickup>VolumeUp</rightstickup> + <rightstickdown>VolumeDown</rightstickdown> + </joystick> + </global> + <Home> + <joystick> + <start>Skin.ToggleSetting(HomeViewToggle)</start> + </joystick> + </Home> + <MyFiles> + <joystick> + <rightbumper>Highlight</rightbumper> + </joystick> + </MyFiles> + <MyMusicPlaylist> + <joystick> + <leftbumper>Delete</leftbumper> + </joystick> + </MyMusicPlaylist> + <MyMusicFiles> + </MyMusicFiles> + <MyMusicLibrary> + </MyMusicLibrary> + <FullscreenVideo> + <joystick> + <a>Pause</a> + <b>Stop</b> + <x>OSD</x> + <y>FullScreen</y> + <start>Info</start> + <back>Seek(-7)</back> + <guide>ActivateWindow(Home)</guide> + <up>ChapterOrBigStepForward</up> + <down>ChapterOrBigStepBack</down> + <right>StepForward</right> + <left>StepBack</left> + <leftthumb>ShowSubtitles</leftthumb> + <rightthumb>AspectRatio</rightthumb> + <lefttrigger>AnalogRewind</lefttrigger> + <righttrigger>AnalogFastForward</righttrigger> + <leftbumper>AnalogRewind</leftbumper> + <rightbumper>AnalogFastForward</rightbumper> + <leftstickleft>AnalogSeekBack</leftstickleft> + <leftstickright>AnalogSeekForward</leftstickright> + <leftstickup>AnalogSeekForward</leftstickup> + <leftstickdown>AnalogSeekBack</leftstickdown> + <rightstickleft>VolumeDown</rightstickleft> + <rightstickright>VolumeUp</rightstickright> + <rightstickup>VolumeUp</rightstickup> + <rightstickdown>VolumeDown</rightstickdown> + </joystick> + </FullscreenVideo> + <FullscreenLiveTV> + <joystick> + <up>ChannelUp</up> + <down>ChannelDown</down> + <left>StepBack</left> + <right>StepForward</right> + </joystick> + </FullscreenLiveTV> + <FullscreenRadio> + <joystick> + <up>ChannelUp</up> + <down>ChannelDown</down> + <left>StepBack</left> + <right>StepForward</right> + </joystick> + </FullscreenRadio> + <FullscreenInfo> + <joystick> + <b>Close</b> + <x>OSD</x> + <start>Close</start> + <lefttrigger>AnalogRewind</lefttrigger> + <righttrigger>AnalogFastForward</righttrigger> + <leftbumper>AnalogRewind</leftbumper> + <rightbumper>AnalogFastForward</rightbumper> + </joystick> + </FullscreenInfo> + <PlayerControls> + <joystick> + <x>Close</x> + <leftthumb>Close</leftthumb> + <rightthumb>Close</rightthumb> + </joystick> + </PlayerControls> + <Visualisation> + <joystick> + <a>Pause</a> + <b>Stop</b> + <x>ActivateWindow(VisualisationSettings)</x> + <y>ActivateWindow(VisualisationPresetList)</y> + <start>Info</start> + <rightthumb>ActivateWindow(MusicOSD)</rightthumb> + <up>SkipNext</up> + <down>SkipPrevious</down> + <left>StepBack</left> + <right>StepForward</right> + <lefttrigger>AnalogRewind</lefttrigger> + <righttrigger>AnalogFastForward</righttrigger> + <leftbumper>AnalogRewind</leftbumper> + <rightbumper>AnalogFastForward</rightbumper> + <leftstickleft>PreviousPreset</leftstickleft> + <leftstickright>NextPreset</leftstickright> + </joystick> + </Visualisation> + <MusicOSD> + <joystick> + <b>Close</b> + <start>Info</start> + </joystick> + </MusicOSD> + <VisualisationSettings> + <joystick> + <b>Close</b> + </joystick> + </VisualisationSettings> + <VisualisationPresetList> + <joystick> + <b>Close</b> + </joystick> + </VisualisationPresetList> + <SlideShow> + <joystick> + <a>Pause</a> + <b>Stop</b> + <y>ZoomNormal</y> + <leftbumper>Rotate</leftbumper> + <rightbumper>CodecInfo</rightbumper> + <up>ZoomIn</up> + <down>ZoomOut</down> + <left>PreviousPicture</left> + <right>NextPicture</right> + <leftstickleft>AnalogMoveX</leftstickleft> + <leftstickright>AnalogMoveX</leftstickright> + <leftstickup>AnalogMoveY</leftstickup> + <leftstickdown>AnalogMoveY</leftstickdown> + <lefttrigger>ZoomOut</lefttrigger> + <righttrigger>ZoomIn</righttrigger> + </joystick> + </SlideShow> + <ScreenCalibration> + <joystick> + <x>ResetCalibration</x> + <leftbumper>NextResolution</leftbumper> + <rightbumper>NextCalibration</rightbumper> + </joystick> + </ScreenCalibration> + <GUICalibration> + <joystick> + <x>ResetCalibration</x> + <leftbumper>NextResolution</leftbumper> + <rightbumper>NextCalibration</rightbumper> + </joystick> + </GUICalibration> + <VideoOSD> + <joystick> + <b>Close</b> + </joystick> + </VideoOSD> + <VideoMenu> + <joystick> + <b>Stop</b> + <x>OSD</x> + <leftbumper>AspectRatio</leftbumper> + <start>Info</start> + </joystick> + </VideoMenu> + <OSDVideoSettings> + <joystick> + <leftbumper>AspectRatio</leftbumper> + <x>Close</x> + </joystick> + </OSDVideoSettings> + <OSDAudioSettings> + <joystick> + <leftbumper>AspectRatio</leftbumper> + <x>Close</x> + </joystick> + </OSDAudioSettings> + <VideoBookmarks> + <joystick> + <leftbumper>Delete</leftbumper> + </joystick> + </VideoBookmarks> + <MyVideoLibrary> + </MyVideoLibrary> + <MyVideoFiles> + </MyVideoFiles> + <MyVideoPlaylist> + <joystick> + <leftbumper>Delete</leftbumper> + </joystick> + </MyVideoPlaylist> + <VirtualKeyboard> + <joystick> + <b>BackSpace</b> + <y>Symbols</y> + <leftbumper>Shift</leftbumper> + <leftthumb>Enter</leftthumb> + <lefttrigger>CursorLeft</lefttrigger> + <righttrigger>CursorRight</righttrigger> + </joystick> + </VirtualKeyboard> + <ContextMenu> + <joystick> + <b>Close</b> + </joystick> + </ContextMenu> + <Scripts> + <joystick> + <x>ContextMenu</x> + </joystick> + </Scripts> + <Settings> + <joystick> + <b>PreviousMenu</b> + </joystick> + </Settings> + <AddonInformation> + <joystick> + <b>Close</b> + </joystick> + </AddonInformation> + <AddonSettings> + <joystick> + <b>Close</b> + </joystick> + </AddonSettings> + <TextViewer> + <joystick> + <b>Close</b> + </joystick> + </TextViewer> + <shutdownmenu> + <joystick> + <b>PreviousMenu</b> + <leftthumb>PreviousMenu</leftthumb> + </joystick> + </shutdownmenu> + <submenu> + <joystick> + <b>PreviousMenu</b> + </joystick> + </submenu> + <MusicInformation> + <joystick> + <b>Close</b> + </joystick> + </MusicInformation> + <MovieInformation> + <joystick> + <b>Close</b> + </joystick> + </MovieInformation> + <NumericInput> + <joystick> + <b>BackSpace</b> + <leftthumb>Enter</leftthumb> + </joystick> + </NumericInput> + <GamepadInput> + <joystick> + <leftthumb>Stop</leftthumb> + </joystick> + </GamepadInput> + <LockSettings> + <joystick> + <b>PreviousMenu</b> + <leftthumb>Close</leftthumb> + </joystick> + </LockSettings> + <ProfileSettings> + <joystick> + <b>PreviousMenu</b> + <leftthumb>Close</leftthumb> + </joystick> + </ProfileSettings> +</keymap> diff --git a/xbmc/input/ButtonTranslator.cpp b/xbmc/input/ButtonTranslator.cpp index 01bfc72c79..ca9c179d3a 100644 --- a/xbmc/input/ButtonTranslator.cpp +++ b/xbmc/input/ButtonTranslator.cpp @@ -976,7 +976,7 @@ void CButtonTranslator::MapWindowActions(TiXmlNode *pWindow, int windowID) TiXmlNode* pDevice; - const char* types[] = {"gamepad", "remote", "universalremote", "keyboard", "mouse", "appcommand", NULL}; + const char* types[] = {"gamepad", "remote", "universalremote", "keyboard", "mouse", "appcommand", "joystick", NULL}; for (int i = 0; types[i]; ++i) { std::string type(types[i]); @@ -1009,6 +1009,8 @@ void CButtonTranslator::MapWindowActions(TiXmlNode *pWindow, int windowID) buttonCode = TranslateMouseCommand(pButton); else if (type == "appcommand") buttonCode = TranslateAppCommand(pButton->Value()); + else if (type == "joystick") + buttonCode = TranslateJoystickString(pButton->Value()); if (buttonCode) { @@ -1493,3 +1495,41 @@ int CButtonTranslator::GetTouchActionCode(int window, int action, std::string &a actionString = touchIt->second.strID; return touchIt->second.id; } + +uint32_t CButtonTranslator::TranslateJoystickString(const char *szButton) +{ + if (!szButton) + return 0; + uint32_t buttonCode = 0; + std::string strButton = szButton; + StringUtils::ToLower(strButton); + + 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 CLog::Log(LOGERROR, "Joystick Translator: Can't find button %s", strButton.c_str()); + + return buttonCode; +} diff --git a/xbmc/input/ButtonTranslator.h b/xbmc/input/ButtonTranslator.h index d4f4178711..f1904a5ae2 100644 --- a/xbmc/input/ButtonTranslator.h +++ b/xbmc/input/ButtonTranslator.h @@ -130,6 +130,7 @@ private: static uint32_t TranslateGamepadString(const char *szButton); static uint32_t TranslateRemoteString(const char *szButton); static uint32_t TranslateUniversalRemoteString(const char *szButton); + static uint32_t TranslateJoystickString(const char *szButton); static uint32_t TranslateKeyboardString(const char *szButton); static uint32_t TranslateKeyboardButton(TiXmlElement *pButton); diff --git a/xbmc/input/InputManager.cpp b/xbmc/input/InputManager.cpp index 69e6c31853..bc8b3a740d 100644 --- a/xbmc/input/InputManager.cpp +++ b/xbmc/input/InputManager.cpp @@ -260,6 +260,35 @@ bool CInputManager::ProcessEventServer(int windowId, float frameTime) return false; } +void CInputManager::ProcessQueuedActions() +{ + std::vector<CAction> queuedActions; + { + CSingleLock lock(m_actionMutex); + queuedActions.swap(m_queuedActions); + } + + for (const CAction& action : queuedActions) + g_application.OnAction(action); +} + +void CInputManager::QueueAction(const CAction& action) +{ + CSingleLock lock(m_actionMutex); + + // Avoid dispatching multiple analog actions per frame with the same ID + if (action.IsAnalog()) + { + m_queuedActions.erase(std::remove_if(m_queuedActions.begin(), m_queuedActions.end(), + [&action](const CAction& queuedAction) + { + return action.GetID() == queuedAction.GetID(); + }), m_queuedActions.end()); + } + + m_queuedActions.push_back(action); +} + bool CInputManager::Process(int windowId, float frameTime) { #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE) @@ -271,6 +300,7 @@ bool CInputManager::Process(int windowId, float frameTime) ProcessRemote(windowId); ProcessEventServer(windowId, frameTime); ProcessPeripherals(frameTime); + ProcessQueuedActions(); return true; } diff --git a/xbmc/input/InputManager.h b/xbmc/input/InputManager.h index f801efce3c..9afe96b25a 100644 --- a/xbmc/input/InputManager.h +++ b/xbmc/input/InputManager.h @@ -34,6 +34,7 @@ #include "input/KeyboardStat.h" #include "input/MouseStat.h" #include "settings/lib/ISettingCallback.h" +#include "threads/CriticalSection.h" class CKey; @@ -79,6 +80,14 @@ public: */ bool ProcessPeripherals(float frameTime); + /*! \brief Dispatch actions queued since the last call to Process() + */ + void ProcessQueuedActions(); + + /*! \brief Queue an action to be processed on the next call to Process() + */ + void QueueAction(const CAction& action); + /*! \brief Process all inputs * * \param windowId Currently active window @@ -240,4 +249,7 @@ private: #if defined(HAS_EVENT_SERVER) std::map<std::string, std::map<int, float> > m_lastAxisMap; #endif + + std::vector<CAction> m_queuedActions; + CCriticalSection m_actionMutex; }; diff --git a/xbmc/input/Key.h b/xbmc/input/Key.h index 5e68505788..5af7415804 100644 --- a/xbmc/input/Key.h +++ b/xbmc/input/Key.h @@ -31,6 +31,9 @@ // XBIRRemote.h // XINPUT_IR_REMOTE-* +/* + * EventServer "gamepad" keys based on original Xbox controller + */ // Analogue - don't change order #define KEY_BUTTON_A 256 #define KEY_BUTTON_B 257 @@ -69,6 +72,35 @@ #define KEY_BUTTON_LEFT_THUMB_STICK_LEFT 282 #define KEY_BUTTON_LEFT_THUMB_STICK_RIGHT 283 +/* + * 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/joysticks/DriverPrimitive.cpp b/xbmc/input/joysticks/DriverPrimitive.cpp new file mode 100644 index 0000000000..70bd2e0d9b --- /dev/null +++ b/xbmc/input/joysticks/DriverPrimitive.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ + +#include "DriverPrimitive.h" + +using namespace JOYSTICK; + +CDriverPrimitive::CDriverPrimitive(void) + : m_type(), + m_driverIndex(0), + m_hatDirection(), + m_semiAxisDirection() +{ +} + +CDriverPrimitive::CDriverPrimitive(unsigned int buttonIndex) + : m_type(BUTTON), + m_driverIndex(buttonIndex), + m_hatDirection(), + m_semiAxisDirection() +{ +} + +CDriverPrimitive::CDriverPrimitive(unsigned int hatIndex, HAT_DIRECTION direction) + : m_type(HAT), + m_driverIndex(hatIndex), + m_hatDirection(direction), + m_semiAxisDirection() +{ +} + +CDriverPrimitive::CDriverPrimitive(unsigned int axisIndex, SEMIAXIS_DIRECTION direction) + : m_type(SEMIAXIS), + m_driverIndex(axisIndex), + m_hatDirection(), + m_semiAxisDirection(direction) +{ +} + +bool CDriverPrimitive::operator==(const CDriverPrimitive& rhs) const +{ + if (m_type == rhs.m_type) + { + switch (m_type) + { + case BUTTON: + return m_driverIndex == rhs.m_driverIndex; + case HAT: + return m_driverIndex == rhs.m_driverIndex && m_hatDirection == rhs.m_hatDirection; + case SEMIAXIS: + return m_driverIndex == rhs.m_driverIndex && m_semiAxisDirection == rhs.m_semiAxisDirection; + default: + return true; + } + } + return false; +} + +bool CDriverPrimitive::operator<(const CDriverPrimitive& rhs) const +{ + if (m_type < rhs.m_type) return true; + if (m_type > rhs.m_type) return false; + + // Driver index is common to all valid primitives + if (m_type != UNKNOWN) + { + if (m_driverIndex < rhs.m_driverIndex) return true; + if (m_driverIndex > rhs.m_driverIndex) return false; + } + + if (m_type == HAT) + { + if (m_hatDirection < rhs.m_hatDirection) return true; + if (m_hatDirection > rhs.m_hatDirection) return false; + } + + if (m_type == SEMIAXIS) + { + if (m_semiAxisDirection < rhs.m_semiAxisDirection) return true; + if (m_semiAxisDirection > rhs.m_semiAxisDirection) return false; + } + + return false; +} + +bool CDriverPrimitive::IsValid(void) const +{ + if (m_type == BUTTON) + return true; + + if (m_type == HAT) + { + return m_hatDirection == HAT_DIRECTION::UP || + m_hatDirection == HAT_DIRECTION::DOWN || + m_hatDirection == HAT_DIRECTION::RIGHT || + m_hatDirection == HAT_DIRECTION::LEFT; + } + + if (m_type == SEMIAXIS) + { + return m_semiAxisDirection == SEMIAXIS_DIRECTION::POSITIVE || + m_semiAxisDirection == SEMIAXIS_DIRECTION::NEGATIVE; + } + + return false; +} diff --git a/xbmc/input/joysticks/DriverPrimitive.h b/xbmc/input/joysticks/DriverPrimitive.h new file mode 100644 index 0000000000..626a368bf5 --- /dev/null +++ b/xbmc/input/joysticks/DriverPrimitive.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "JoystickTypes.h" + +#include <stdint.h> + +namespace JOYSTICK +{ + /*! + * \brief Basic driver element associated with input events + * + * Driver input (bools, floats and enums) is split into primitives that better + * map to the physical features on a joystick. + * + * A bool obviously only maps to a single feature, so it is a driver + * primitive. Here, these are called "buttons". + * + * A hat enum encodes the state of the four hat directions. Each direction + * can map to a different feature, so a hat enum consists of four driver + * primitives called "hat directions". + * + * A float is a little trickier. Trivially, it can map to an analog stick or + * trigger. However, DirectInput combines two triggers onto a single axis. + * Therefore, the axis is split into two primitives called "semiaxes". + * + * The type determines the fields in use: + * + * Button: + * - driver index + * + * Hat direction: + * - driver index + * - hat direction (up/right/down/left) + * + * Semiaxis: + * - driver index + * - semiaxis direction (positive/negative) + * + * For more info, see "Chapter 2. Joystick drivers" in the documentation + * thread: http://forum.kodi.tv/showthread.php?tid=257764 + */ + class CDriverPrimitive + { + public: + /*! + * \brief Type of driver primitive + */ + enum PrimitiveType + { + UNKNOWN = 0, // primitive has no type (invalid) + BUTTON, // a digital button + HAT, // one of the four direction arrows on a D-pad + SEMIAXIS, // the positive or negative half of an axis + }; + + /*! + * \brief Construct an invalid driver primitive + */ + CDriverPrimitive(void); + + /*! + * \brief Construct a driver primitive representing a button + */ + CDriverPrimitive(unsigned int buttonIndex); + + /*! + * \brief Construct a driver primitive representing one of the four + * direction arrows on a dpad + */ + CDriverPrimitive(unsigned int hatIndex, HAT_DIRECTION direction); + + /*! + * \brief Construct a driver primitive representing the positive or negative + * half of an axis + */ + CDriverPrimitive(unsigned int axisIndex, SEMIAXIS_DIRECTION direction); + + bool operator==(const CDriverPrimitive& rhs) const; + bool operator<(const CDriverPrimitive& rhs) const; + + bool operator!=(const CDriverPrimitive& rhs) const { return !operator==(rhs); } + bool operator>(const CDriverPrimitive& rhs) const { return !(operator<(rhs) || operator==(rhs)); } + bool operator<=(const CDriverPrimitive& rhs) const { return operator<(rhs) || operator==(rhs); } + bool operator>=(const CDriverPrimitive& rhs) const { return !operator<(rhs); } + + /*! + * \brief The type of driver primitive + */ + PrimitiveType Type(void) const { return m_type; } + + /*! + * \brief The index used by the driver (valid for all types) + */ + unsigned int Index(void) const { return m_driverIndex; } + + /*! + * \brief The direction arrow (valid for hat directions) + */ + HAT_DIRECTION HatDirection(void) const { return m_hatDirection; } + + /*! + * \brief The semiaxis direction (valid for semiaxes) + */ + SEMIAXIS_DIRECTION SemiAxisDirection(void) const { return m_semiAxisDirection; } + + /*! + * \brief Test if an driver primitive is valid + * + * A driver primitive is valid if it has a known type and: + * + * 1) for hats, it is a cardinal direction + * 2) for semi-axes, it is a positive or negative direction + */ + bool IsValid(void) const; + + private: + PrimitiveType m_type; + unsigned int m_driverIndex; + HAT_DIRECTION m_hatDirection; + SEMIAXIS_DIRECTION m_semiAxisDirection; + }; +} diff --git a/xbmc/input/joysticks/IJoystickButtonMap.h b/xbmc/input/joysticks/IJoystickButtonMap.h new file mode 100644 index 0000000000..744cc9edf9 --- /dev/null +++ b/xbmc/input/joysticks/IJoystickButtonMap.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "DriverPrimitive.h" +#include "JoystickTypes.h" + +#include <string> + +namespace JOYSTICK +{ + /*! + * \brief Button map interface to translate between the driver's raw + * button/hat/axis elements and physical joystick features. + * + * \sa IJoystickButtonMapper + */ + class IJoystickButtonMap + { + public: + virtual ~IJoystickButtonMap(void) { } + + /*! + * \brief The add-on ID of the game controller associated with this button map + * + * The controller ID provided by the implementation serves as the context + * for the feature names below. + * + * \return The ID of this button map's game controller add-on + */ + virtual std::string ControllerID(void) const = 0; + + /*! + * \brief Load the button map into memory + * + * \return True if button map is ready to start translating buttons, false otherwise + */ + virtual bool Load(void) = 0; + + /*! + * \brief Reset the button map to its defaults, or clear button map if no defaults + */ + virtual void Reset(void) = 0; + + /*! + * \brief Get the feature associated with a driver primitive + * + * Multiple primitives can be mapped to the same feature. For example, + * analog sticks use one primitive for each direction. + * + * \param primitive The driver primitive (a button, hat direction or semi-axis) + * \param feature The name of the resolved joystick feature, or + * invalid if false is returned + * + * \return True if the driver primitive is associated with a feature, false otherwise + */ + virtual bool GetFeature( + const CDriverPrimitive& primitive, + FeatureName& feature + ) = 0; + + /*! + * \brief Get the type of the feature for the given name + * + * \param feature The feature to look up + * + * \return The feature's type + */ + virtual FEATURE_TYPE GetFeatureType(const FeatureName& feature) = 0; + + /*! + * \brief Get the driver primitive for a scalar feature + * + * When a feature can be represented by a single driver primitive, it is + * called a scalar feature. + * + * - This includes buttons and triggers, because they can be mapped to a + * single button/hat/semiaxis + * + * - This does not include analog sticks, because they require two axes + * and four driver primitives (one for each semiaxis) + * + * \param feature Must be a scalar feature (a feature that only + * requires a single driver primitive) + * \param primitive The resolved driver primitive + * + * \return True if the feature resolved to a driver primitive, false if the + * feature didn't resolve or isn't a scalar feature + */ + virtual bool GetScalar( + const FeatureName& feature, + CDriverPrimitive& primitive + ) = 0; + + /*! + * \brief Add or update a scalar feature + * + * \param feature Must be a scalar feature + * \param primitive The feature's driver primitive + * + * \return True if the feature was updated, false if the feature is + * unchanged or failure occurs + */ + virtual bool AddScalar( + const FeatureName& feature, + const CDriverPrimitive& primitive + ) = 0; + + /*! + * \brief Get an analog stick from the button map + * + * \param feature Must be an analog stick or this will return false + * \param up The primitive mapped to the up direction (possibly unknown) + * \param down The primitive mapped to the down direction (possibly unknown) + * \param right The primitive mapped to the right direction (possibly unknown) + * \param left The primitive mapped to the left direction (possibly unknown) + * + * \return True if the feature resolved to an analog stick with at least 1 known direction + */ + virtual bool GetAnalogStick( + const FeatureName& feature, + CDriverPrimitive& up, + CDriverPrimitive& down, + CDriverPrimitive& right, + CDriverPrimitive& left + ) = 0; + + /*! + * \brief Add or update an analog stick + * + * \param feature Must be an analog stick or this will return false + * \param up The driver primitive for the up direction + * \param down The driver primitive for the down direction + * \param right The driver primitive for the right direction + * \param left The driver primitive for the left direction + * + * It is not required that these primitives be axes. If a primitive is a + * semiaxis, its opposite should point to the same axis index but with + * opposite direction. + * + * \return True if the analog stick was updated, false otherwise + */ + virtual bool AddAnalogStick( + const FeatureName& feature, + const CDriverPrimitive& up, + const CDriverPrimitive& down, + const CDriverPrimitive& right, + const CDriverPrimitive& left + ) = 0; + + /*! + * \brief Get an accelerometer from the button map + * + * \param feature Must be an accelerometer or this will return false + * \param positiveX The semiaxis mapped to the positive X direction (possibly unknown) + * \param positiveY The semiaxis mapped to the positive Y direction (possibly unknown) + * \param positiveZ The semiaxis mapped to the positive Z direction (possibly unknown) + * + * \return True if the feature resolved to an accelerometer with at least 1 known axis + */ + virtual bool GetAccelerometer( + const FeatureName& feature, + CDriverPrimitive& positiveX, + CDriverPrimitive& positiveY, + CDriverPrimitive& positiveZ + ) = 0; + + /*! + * \brief Get or update an accelerometer + * + * \param feature Must be an accelerometer or this will return false + * \param positiveX The semiaxis corresponding to the positive X direction + * \param positiveY The semiaxis corresponding to the positive Y direction + * \param positiveZ The semiaxis corresponding to the positive Z direction + * + * The driver primitives must be mapped to a semiaxis or this function will fail. + * + * \return True if the accelerometer was updated, false if unchanged or failure occurred + */ + virtual bool AddAccelerometer( + const FeatureName& feature, + const CDriverPrimitive& positiveX, + const CDriverPrimitive& positiveY, + const CDriverPrimitive& positiveZ + ) = 0; + }; +} diff --git a/xbmc/input/joysticks/IJoystickButtonMapper.h b/xbmc/input/joysticks/IJoystickButtonMapper.h new file mode 100644 index 0000000000..2f73059421 --- /dev/null +++ b/xbmc/input/joysticks/IJoystickButtonMapper.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include <string> + +namespace JOYSTICK +{ + class CDriverPrimitive; + class IJoystickButtonMap; + + /*! + * \ingroup joysticks + * + * \brief Button mapper interface to assign the driver's raw button/hat/axis + * elements to physical joystick features using a provided button map. + * + * \sa IJoystickButtonMap + */ + class IJoystickButtonMapper + { + public: + virtual ~IJoystickButtonMapper(void) { } + + /*! + * \brief The add-on ID of the game controller associated with this button mapper + * + * \return The ID of the add-on extending kodi.game.controller + */ + virtual std::string ControllerID(void) const = 0; + + /*! + * \brief Handle button/hat press or axis threshold + * + * \param buttonMap The button map being manipulated + * \param primitive The source of the action + * + * \return True if action was mapped to a feature + */ + virtual bool MapPrimitive(IJoystickButtonMap* buttonMap, const CDriverPrimitive& primitive) = 0; + }; +} diff --git a/xbmc/input/joysticks/IJoystickDriverHandler.h b/xbmc/input/joysticks/IJoystickDriverHandler.h new file mode 100644 index 0000000000..d60faecced --- /dev/null +++ b/xbmc/input/joysticks/IJoystickDriverHandler.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "JoystickTypes.h" + +namespace JOYSTICK +{ + /*! + * \brief Interface defining methods to handle joystick events for raw driver + * elements (buttons, hats, axes) + */ + class IJoystickDriverHandler + { + public: + virtual ~IJoystickDriverHandler(void) { } + + /*! + * \brief Handle button motion + * + * \param buttonIndex The index of the button as reported by the driver + * \param bPressed true for press motion, false for release motion + * + * \return True if a press was handled, false otherwise + */ + virtual bool OnButtonMotion(unsigned int buttonIndex, bool bPressed) = 0; + + /*! + * \brief Handle hat motion + * + * \param hatIndex The index of the hat as reported by the driver + * \param state The direction the hat is now being pressed + * + * \return True if the new direction was handled, false otherwise + */ + virtual bool OnHatMotion(unsigned int hatIndex, HAT_STATE state) = 0; + + /*! + * \brief Handle axis motion + * + * If a joystick feature requires multiple axes (analog sticks, accelerometers), + * they can be buffered for later processing. + * + * \param axisIndex The index of the axis as reported by the driver + * \param position The position of the axis in the closed interval [-1.0, 1.0] + * + * \return True if the motion was handled, false otherwise + */ + virtual bool OnAxisMotion(unsigned int axisIndex, float position) = 0; + + /*! + * \brief Handle buffered axis positions for features that require multiple axes + * + * ProcessAxisMotions() is called at the end of the frame when all axis motions + * have been reported. This has several uses, including: + * + * - Combining multiple axes into a single analog stick or accelerometer event + * - Imitating an analog feature with a digital button so that events can be + * dispatched every frame. + */ + virtual void ProcessAxisMotions(void) = 0; + }; +} diff --git a/xbmc/input/joysticks/IJoystickInputHandler.h b/xbmc/input/joysticks/IJoystickInputHandler.h new file mode 100644 index 0000000000..a3d8e9cf70 --- /dev/null +++ b/xbmc/input/joysticks/IJoystickInputHandler.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "JoystickTypes.h" + +#include <string> + +namespace JOYSTICK +{ + /*! + * \brief Interface for handling input events for game controllers + */ + class IJoystickInputHandler + { + public: + virtual ~IJoystickInputHandler(void) { } + + /*! + * \brief The add-on ID of the game controller associated with this input handler + * + * \return The ID of the add-on extending kodi.game.controller + */ + virtual std::string ControllerID(void) const = 0; + + virtual bool HasFeature(const FeatureName& feature) const = 0; + + /*! + * \brief Get the type of input handled by the specified feature + * + * \return INPUT_TYPE::DIGITAL for digital buttons, INPUT::ANALOG for analog + * buttons, or INPUT::UNKNOWN otherwise + */ + virtual INPUT_TYPE GetInputType(const FeatureName& feature) const = 0; + + /*! + * \brief A digital button has been pressed or released + * + * \param feature The feature being pressed + * \param bPressed True if pressed, false if released + * + * \return True if the event was handled otherwise false + */ + virtual bool OnButtonPress(const FeatureName& feature, bool bPressed) = 0; + + /*! + * \brief An analog button (trigger or a pressure-sensitive button) has changed state + * + * \param feature The feature changing state + * \param magnitude The button pressure or trigger travel distance in the + * closed interval [0, 1] + * + * \return True if the event was handled otherwise false + */ + virtual bool OnButtonMotion(const FeatureName& feature, float magnitude) = 0; + + /*! + * \brief An analog stick has moved + * + * \param feature The analog stick being moved + * \param x The x coordinate in the closed interval [-1, 1] + * \param y The y coordinate in the closed interval [-1, 1] + * + * \return True if the event was handled otherwise false + */ + virtual bool OnAnalogStickMotion(const FeatureName& feature, float x, float y) = 0; + + /*! + * \brief An accelerometer's state has changed + * + * \param feature The accelerometer being accelerated + * \param x The x coordinate in the closed interval [-1, 1] + * \param y The y coordinate in the closed interval [-1, 1] + * \param z The z coordinate in the closed interval [-1, 1] + * + * \return True if the event was handled otherwise false + */ + virtual bool OnAccelerometerMotion(const FeatureName& feature, float x, float y, float z) { return false; } + }; +} diff --git a/xbmc/input/joysticks/IKeymapHandler.h b/xbmc/input/joysticks/IKeymapHandler.h new file mode 100644 index 0000000000..75de4f51e7 --- /dev/null +++ b/xbmc/input/joysticks/IKeymapHandler.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "JoystickTypes.h" + +namespace 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(void) { } + + /*! + * \brief Get the type of action mapped to the specified key ID + * + * \param keyId The key ID from Key.h + * + * \return The type of action mapped to keyId, or INPUT_TYPE::UNKNOWN if + * no action is mapped to the specified key + */ + virtual INPUT_TYPE GetInputType(unsigned int keyId) const = 0; + + /*! + * \brief A key mapped to a digital action has been pressed or released + * + * \param keyId The key ID from Key.h + * \param bPressed true if the key's button/axis is activated, false if deactivated + */ + virtual void OnDigitalKey(unsigned int keyId, bool bPressed) = 0; + + /*! + * \brief Callback for keys mapped to analog actions + * + * \param keyId The button key ID from Key.h + * \param magnitude The amount of the analog action + * + * If keyId is not mapped to an analog action, no action need be taken + */ + virtual void OnAnalogKey(unsigned int buttonKeyId, float magnitude) = 0; + }; +} diff --git a/xbmc/input/joysticks/JoystickMonitor.cpp b/xbmc/input/joysticks/JoystickMonitor.cpp new file mode 100644 index 0000000000..6de84abf43 --- /dev/null +++ b/xbmc/input/joysticks/JoystickMonitor.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ + +#include "JoystickMonitor.h" +#include "Application.h" +#include "input/InputManager.h" + +using namespace JOYSTICK; + +bool CJoystickMonitor::OnButtonMotion(unsigned int buttonIndex, bool bPressed) +{ + if (bPressed) + { + CInputManager::GetInstance().SetMouseActive(false); + return ResetTimers(); + } + + return false; +} + +bool CJoystickMonitor::OnHatMotion(unsigned int hatIndex, HAT_STATE state) +{ + if (state != HAT_STATE::UNPRESSED) + { + CInputManager::GetInstance().SetMouseActive(false); + return ResetTimers(); + } + + return false; +} + +bool CJoystickMonitor::OnAxisMotion(unsigned int axisIndex, float position) +{ + if (position) + { + CInputManager::GetInstance().SetMouseActive(false); + return ResetTimers(); + } + + return false; +} + +bool CJoystickMonitor::ResetTimers(void) +{ + g_application.ResetSystemIdleTimer(); + g_application.ResetScreenSaver(); + return g_application.WakeUpScreenSaverAndDPMS(); +} diff --git a/xbmc/input/joysticks/JoystickMonitor.h b/xbmc/input/joysticks/JoystickMonitor.h new file mode 100644 index 0000000000..6bfca8b069 --- /dev/null +++ b/xbmc/input/joysticks/JoystickMonitor.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "IJoystickDriverHandler.h" + +namespace JOYSTICK +{ + /*! + * \brief Monitors joystick input and resets screensaver/shutdown timers + * whenever motion occurs. + */ + class CJoystickMonitor : public IJoystickDriverHandler + { + public: + // implementation of IJoystickDriverHandler + virtual bool OnButtonMotion(unsigned int buttonIndex, bool bPressed) override; + virtual bool OnHatMotion(unsigned int hatIndex, HAT_STATE state) override; + virtual bool OnAxisMotion(unsigned int axisIndex, float position) override; + virtual void ProcessAxisMotions(void) override { } + + private: + /*! + * \brief Reset screensaver and shutdown timers + * \return True if the application was woken from screensaver + */ + bool ResetTimers(void); + }; +} diff --git a/xbmc/input/joysticks/JoystickTranslator.cpp b/xbmc/input/joysticks/JoystickTranslator.cpp new file mode 100644 index 0000000000..b0faee3c54 --- /dev/null +++ b/xbmc/input/joysticks/JoystickTranslator.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ + +#include "JoystickTranslator.h" + +using namespace JOYSTICK; + +const char* CJoystickTranslator::HatStateToString(HAT_STATE state) +{ + switch (state) + { + case HAT_STATE::UP: return "UP"; + case HAT_STATE::DOWN: return "DOWN"; + case HAT_STATE::RIGHT: return "RIGHT"; + case HAT_STATE::LEFT: return "LEFT"; + case HAT_STATE::RIGHTUP: return "UP RIGHT"; + case HAT_STATE::RIGHTDOWN: return "DOWN RIGHT"; + case HAT_STATE::LEFTUP: return "UP LEFT"; + case HAT_STATE::LEFTDOWN: return "DOWN LEFT"; + case HAT_STATE::UNPRESSED: + default: + break; + } + + return "RELEASED"; +} + +SEMIAXIS_DIRECTION CJoystickTranslator::PositionToSemiAxisDirection(float position) +{ + if (position > 0) return SEMIAXIS_DIRECTION::POSITIVE; + else if (position < 0) return SEMIAXIS_DIRECTION::NEGATIVE; + + return SEMIAXIS_DIRECTION::ZERO; +} + +CARDINAL_DIRECTION CJoystickTranslator::VectorToCardinalDirection(float x, float y) +{ + if (y >= x && y > -x) return CARDINAL_DIRECTION::UP; + else if (y < x && y >= -x) return CARDINAL_DIRECTION::RIGHT; + else if (y <= x && y < -x) return CARDINAL_DIRECTION::DOWN; + else if (y > x && y <= -x) return CARDINAL_DIRECTION::LEFT; + + return CARDINAL_DIRECTION::UNKNOWN; +} diff --git a/xbmc/input/joysticks/JoystickTranslator.h b/xbmc/input/joysticks/JoystickTranslator.h new file mode 100644 index 0000000000..659deb31dd --- /dev/null +++ b/xbmc/input/joysticks/JoystickTranslator.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "JoystickTypes.h" + +namespace JOYSTICK +{ + class CJoystickTranslator + { + public: + /*! + * \brief Translate a hat state to a string representation + * + * \param state The hat state + * + * \return A capitalized string representation, or "RELEASED" if the hat is centered. + */ + static const char* HatStateToString(HAT_STATE state); + + /*! + * \brief Get the semi-axis direction containing the specified position + * + * \param position The position of the axis + * + * \return POSITIVE, NEGATIVE, or UNKNOWN if position is 0 + */ + static SEMIAXIS_DIRECTION PositionToSemiAxisDirection(float position); + + /*! + * \brief Get the closest cardinal direction to the given vector + * + * Ties are resolved in the clockwise direction: (0.5, 0.5) will resolve to + * RIGHT. + * + * \param x The x component of the vector + * \param y The y component of the vector + * + * \return The closest cardinal directon (up, down, right or left), or unknown + * if x and y are both 0. + */ + static CARDINAL_DIRECTION VectorToCardinalDirection(float x, float y); + }; +} diff --git a/xbmc/input/joysticks/JoystickTypes.h b/xbmc/input/joysticks/JoystickTypes.h new file mode 100644 index 0000000000..fee25bdb44 --- /dev/null +++ b/xbmc/input/joysticks/JoystickTypes.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include <string> + +namespace JOYSTICK +{ + /*! + * \brief Name of a physical feature belonging to the joystick + */ + typedef std::string FeatureName; + + /*! + * \brief Types of features used in the joystick library + * + * Available types: + * + * 1) scalar[1] + * 2) analog stick + * 3) accelerometer + * + * [1] All three driver primitives (buttons, hats and axes) have a state that + * can be represented using a single scalar value. For this reason, + * features that map to a single primitive are called "scalar features". + */ + enum class FEATURE_TYPE + { + UNKNOWN, + SCALAR, + ANALOG_STICK, + ACCELEROMETER, + }; + + /*! + * \brief Direction arrows on the hat (directional pad) + */ + enum class HAT_DIRECTION + { + UNKNOWN = 0x0, + UP = 0x1, + DOWN = 0x2, + RIGHT = 0x4, + LEFT = 0x8, + }; + + /*! + * \brief Generic typedef for cardinal directions + */ + typedef HAT_DIRECTION CARDINAL_DIRECTION; + + /*! + * \brief States in which a hat can be + */ + enum class HAT_STATE + { + UNPRESSED = 0x0, /*!< @brief no directions are pressed */ + UP = 0x1, /*!< @brief only up is pressed */ + DOWN = 0x2, /*!< @brief only down is pressed */ + RIGHT = 0x4, /*!< @brief only right is pressed */ + LEFT = 0x8, /*!< @brief only left is pressed */ + RIGHTUP = RIGHT | UP, + RIGHTDOWN = RIGHT | DOWN, + LEFTUP = LEFT | UP, + LEFTDOWN = LEFT | DOWN, + }; + + /*! + * \brief Generic typedef for intercardinal directions + */ + typedef HAT_STATE INTERCARDINAL_DIRECTION; + + /*! + * \brief Directions in which a semiaxis can point + */ + enum class SEMIAXIS_DIRECTION + { + NEGATIVE = -1, // semiaxis lies in the interval [-1.0, 0.0] + ZERO = 0, // semiaxis is unknown or invalid + POSITIVE = 1, // semiaxis lies in the interval [0.0, 1.0] + }; + + /*! + * \brief Types of input available for scalar features + */ + enum class INPUT_TYPE + { + UNKNOWN, + DIGITAL, + ANALOG, + }; +} diff --git a/xbmc/input/joysticks/JoystickUtils.h b/xbmc/input/joysticks/JoystickUtils.h new file mode 100644 index 0000000000..8d2c0b0bbf --- /dev/null +++ b/xbmc/input/joysticks/JoystickUtils.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "JoystickTypes.h" + +inline JOYSTICK::HAT_DIRECTION& operator|=(JOYSTICK::HAT_DIRECTION& lhs, JOYSTICK::HAT_DIRECTION rhs) +{ + return lhs = static_cast<JOYSTICK::HAT_DIRECTION>(static_cast<int>(lhs) | static_cast<int>(rhs)); +} + +inline JOYSTICK::HAT_STATE& operator|=(JOYSTICK::HAT_STATE& lhs, JOYSTICK::HAT_STATE rhs) +{ + return lhs = static_cast<JOYSTICK::HAT_STATE>(static_cast<int>(lhs) | static_cast<int>(rhs)); +} + +inline bool operator&(JOYSTICK::HAT_STATE lhs, JOYSTICK::HAT_DIRECTION rhs) +{ + return (static_cast<int>(lhs) & static_cast<int>(rhs)) ? true : false; +} + +inline JOYSTICK::SEMIAXIS_DIRECTION operator*(JOYSTICK::SEMIAXIS_DIRECTION lhs, int rhs) +{ + return static_cast<JOYSTICK::SEMIAXIS_DIRECTION>(static_cast<int>(lhs) * rhs); +} + +inline float operator*(float lhs, JOYSTICK::SEMIAXIS_DIRECTION rhs) +{ + return lhs * static_cast<int>(rhs); +} diff --git a/xbmc/input/joysticks/KeymapHandler.cpp b/xbmc/input/joysticks/KeymapHandler.cpp new file mode 100644 index 0000000000..3574de45e4 --- /dev/null +++ b/xbmc/input/joysticks/KeymapHandler.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2015-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ + +#include "KeymapHandler.h" +#include "guilib/GUIWindowManager.h" +#include "input/ButtonTranslator.h" +#include "input/InputManager.h" +#include "input/Key.h" + +#include <algorithm> + +using namespace KODI; +using namespace MESSAGING; + +#define HOLD_TIMEOUT_MS 500 +#define REPEAT_TIMEOUT_MS 50 + +using namespace JOYSTICK; + +CKeymapHandler::CKeymapHandler(void) + : CThread("KeymapHandler"), + m_state(STATE_UNPRESSED), + m_lastButtonPress(0) +{ + Create(false); +} + +CKeymapHandler::~CKeymapHandler(void) +{ + StopThread(true); +} + +INPUT_TYPE CKeymapHandler::GetInputType(unsigned int keyId) const +{ + if (keyId != 0) + { + CAction action(CButtonTranslator::GetInstance().GetAction(g_windowManager.GetActiveWindowID(), CKey(keyId))); + if (action.GetID() > 0) + { + if (action.IsAnalog()) + return INPUT_TYPE::ANALOG; + else + return INPUT_TYPE::DIGITAL; + } + } + + return INPUT_TYPE::UNKNOWN; +} + +void CKeymapHandler::OnDigitalKey(unsigned int keyId, bool bPressed) +{ + if (keyId != 0) + { + CSingleLock lock(m_digitalMutex); + + if (bPressed && !IsPressed(keyId)) + ProcessButtonPress(keyId); + else if (!bPressed && IsPressed(keyId)) + ProcessButtonRelease(keyId); + } +} + +void CKeymapHandler::OnAnalogKey(unsigned int keyId, float magnitude) +{ + if (keyId != 0) + SendAnalogAction(keyId, magnitude); +} + +void CKeymapHandler::Process() +{ + unsigned int holdStartTime = 0; + unsigned int pressedButton = 0; + + while (!m_bStop) + { + switch (m_state) + { + case STATE_UNPRESSED: + { + // Wait for button press + WaitResponse waitResponse = AbortableWait(m_pressEvent); + + CSingleLock lock(m_digitalMutex); + + if (waitResponse == WAIT_SIGNALED && m_lastButtonPress != 0) + { + pressedButton = m_lastButtonPress; + m_state = STATE_BUTTON_PRESSED; + } + break; + } + + case STATE_BUTTON_PRESSED: + { + holdStartTime = XbmcThreads::SystemClockMillis(); + + // Wait for hold time to elapse + WaitResponse waitResponse = AbortableWait(m_pressEvent, HOLD_TIMEOUT_MS); + + CSingleLock lock(m_digitalMutex); + + if (m_lastButtonPress == 0) + { + m_state = STATE_UNPRESSED; + } + else if (waitResponse == WAIT_SIGNALED || m_lastButtonPress != pressedButton) + { + pressedButton = m_lastButtonPress; + // m_state is unchanged + } + else if (waitResponse == WAIT_TIMEDOUT) + { + m_state = STATE_BUTTON_HELD; + } + break; + } + + case STATE_BUTTON_HELD: + { + const unsigned int holdTimeMs = XbmcThreads::SystemClockMillis() - holdStartTime; + SendDigitalAction(pressedButton, holdTimeMs); + + // Wait for repeat time to elapse + WaitResponse waitResponse = AbortableWait(m_pressEvent, REPEAT_TIMEOUT_MS); + + CSingleLock lock(m_digitalMutex); + + if (m_lastButtonPress == 0) + { + m_state = STATE_UNPRESSED; + } + else if (waitResponse == WAIT_SIGNALED || m_lastButtonPress != pressedButton) + { + pressedButton = m_lastButtonPress; + m_state = STATE_BUTTON_PRESSED; + } + break; + } + + default: + break; + } + } +} + +bool CKeymapHandler::ProcessButtonPress(unsigned int keyId) +{ + m_pressedButtons.push_back(keyId); + + if (SendDigitalAction(keyId)) + { + m_lastButtonPress = keyId; + m_pressEvent.Set(); + return true; + } + + return false; +} + +void CKeymapHandler::ProcessButtonRelease(unsigned int keyId) +{ + m_pressedButtons.erase(std::remove(m_pressedButtons.begin(), m_pressedButtons.end(), keyId), m_pressedButtons.end()); + + if (keyId == m_lastButtonPress || m_pressedButtons.empty()) + { + m_lastButtonPress = 0; + m_pressEvent.Set(); + } +} + +bool CKeymapHandler::IsPressed(unsigned int keyId) const +{ + return std::find(m_pressedButtons.begin(), m_pressedButtons.end(), keyId) != m_pressedButtons.end(); +} + +bool CKeymapHandler::SendDigitalAction(unsigned int keyId, unsigned int holdTimeMs /* = 0 */) +{ + CAction action(CButtonTranslator::GetInstance().GetAction(g_windowManager.GetActiveWindowID(), CKey(keyId, holdTimeMs))); + if (action.GetID() > 0) + { + CInputManager::GetInstance().QueueAction(action); + return true; + } + + return false; +} + +bool CKeymapHandler::SendAnalogAction(unsigned int keyId, float magnitude) +{ + CAction action(CButtonTranslator::GetInstance().GetAction(g_windowManager.GetActiveWindowID(), CKey(keyId))); + if (action.GetID() > 0) + { + CAction actionWithAmount(action.GetID(), magnitude, 0.0f, action.GetName()); + CInputManager::GetInstance().QueueAction(actionWithAmount); + return true; + } + + return false; +} diff --git a/xbmc/input/joysticks/KeymapHandler.h b/xbmc/input/joysticks/KeymapHandler.h new file mode 100644 index 0000000000..4d718c1627 --- /dev/null +++ b/xbmc/input/joysticks/KeymapHandler.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "input/joysticks/IKeymapHandler.h" +#include "threads/CriticalSection.h" +#include "threads/Event.h" +#include "threads/Thread.h" + +#include <vector> + +namespace JOYSTICK +{ + class CKeymapHandler : public IKeymapHandler, + protected CThread + { + public: + CKeymapHandler(void); + + virtual ~CKeymapHandler(void); + + // implementation of IKeymapHandler + virtual INPUT_TYPE GetInputType(unsigned int keyId) const override; + virtual void OnDigitalKey(unsigned int keyId, bool bPressed) override; + virtual void OnAnalogKey(unsigned int keyId, float magnitude) override; + + protected: + // implementation of CThread + virtual void Process(void) override; + + private: + enum BUTTON_STATE + { + STATE_UNPRESSED, + STATE_BUTTON_PRESSED, + STATE_BUTTON_HELD, + }; + + bool ProcessButtonPress(unsigned int keyId); + void ProcessButtonRelease(unsigned int keyId); + bool IsPressed(unsigned int keyId) const; + + static bool SendDigitalAction(unsigned int keyId, unsigned int holdTimeMs = 0); + static bool SendAnalogAction(unsigned int keyId, float magnitude); + + BUTTON_STATE m_state; + unsigned int m_lastButtonPress; + std::vector<unsigned int> m_pressedButtons; + CEvent m_pressEvent; + CCriticalSection m_digitalMutex; + }; +} diff --git a/xbmc/input/joysticks/Makefile b/xbmc/input/joysticks/Makefile new file mode 100644 index 0000000000..03b4fca88d --- /dev/null +++ b/xbmc/input/joysticks/Makefile @@ -0,0 +1,9 @@ +SRCS=DriverPrimitive.cpp \ + JoystickMonitor.cpp \ + JoystickTranslator.cpp \ + KeymapHandler.cpp \ + +LIB=input_joysticks.a + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/input/joysticks/generic/GenericJoystickButtonMapping.cpp b/xbmc/input/joysticks/generic/GenericJoystickButtonMapping.cpp new file mode 100644 index 0000000000..8432066598 --- /dev/null +++ b/xbmc/input/joysticks/generic/GenericJoystickButtonMapping.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ + +#include "GenericJoystickButtonMapping.h" +#include "input/joysticks/DriverPrimitive.h" +#include "input/joysticks/IJoystickButtonMapper.h" +#include "input/joysticks/JoystickTranslator.h" +#include "input/joysticks/JoystickUtils.h" +#include "threads/SystemClock.h" +#include "utils/log.h" + +#include <algorithm> +#include <assert.h> +#include <cmath> + +using namespace JOYSTICK; +using namespace XbmcThreads; + +#define MAPPING_COOLDOWN_MS 50 // Guard against repeated input +#define AXIS_THRESHOLD 0.75f // Axis must exceed this value to be mapped + +CGenericJoystickButtonMapping::CGenericJoystickButtonMapping(IJoystickButtonMapper* buttonMapper, IJoystickButtonMap* buttonMap) + : m_buttonMapper(buttonMapper), + m_buttonMap(buttonMap), + m_lastAction(0) +{ + assert(m_buttonMapper != NULL); + assert(m_buttonMap != NULL); +} + +bool CGenericJoystickButtonMapping::OnButtonMotion(unsigned int buttonIndex, bool bPressed) +{ + if (bPressed) + { + CDriverPrimitive buttonPrimitive(buttonIndex); + if (buttonPrimitive.IsValid()) + { + MapPrimitive(buttonPrimitive); + return true; + } + } + + return false; +} + +bool CGenericJoystickButtonMapping::OnHatMotion(unsigned int hatIndex, HAT_STATE state) +{ + CDriverPrimitive hatPrimitive(hatIndex, static_cast<HAT_DIRECTION>(state)); + if (hatPrimitive.IsValid()) + { + MapPrimitive(hatPrimitive); + return true; + } + + return false; +} + +bool CGenericJoystickButtonMapping::OnAxisMotion(unsigned int axisIndex, float position) +{ + SEMIAXIS_DIRECTION dir = CJoystickTranslator::PositionToSemiAxisDirection(position); + + CDriverPrimitive axis(axisIndex, dir); + CDriverPrimitive oppositeAxis(axisIndex, dir * -1); + + if (position == 0.0f) + { + Deactivate(axis); + Deactivate(oppositeAxis); + } + else + { + Deactivate(oppositeAxis); + + if (std::abs(position) >= AXIS_THRESHOLD) + Activate(axis); + else + Deactivate(axis); + } + + return true; +} + +void CGenericJoystickButtonMapping::ProcessAxisMotions(void) +{ + for (std::vector<ActivatedAxis>::iterator it = m_activatedAxes.begin(); it != m_activatedAxes.end(); ++it) + { + ActivatedAxis& semiaxis = *it; + + // Only emit once + if (!semiaxis.bEmitted) + { + semiaxis.bEmitted = true; + MapPrimitive(semiaxis.driverPrimitive); + } + } +} + +void CGenericJoystickButtonMapping::MapPrimitive(const CDriverPrimitive& primitive) +{ + const unsigned int now = SystemClockMillis(); + + bool bTimeoutElapsed = (now >= m_lastAction + MAPPING_COOLDOWN_MS); + if (bTimeoutElapsed) + { + m_lastAction = SystemClockMillis(); + m_buttonMapper->MapPrimitive(m_buttonMap, primitive); + } + else + { + const unsigned int elapsed = now - m_lastAction; + CLog::Log(LOGDEBUG, "Button mapping: rapid input after %ums dropped for profile \"%s\"", + elapsed, m_buttonMapper->ControllerID().c_str()); + } +} + +void CGenericJoystickButtonMapping::Activate(const CDriverPrimitive& semiaxis) +{ + if (!IsActive(semiaxis)) + m_activatedAxes.push_back(ActivatedAxis{semiaxis}); +} + +void CGenericJoystickButtonMapping::Deactivate(const CDriverPrimitive& semiaxis) +{ + m_activatedAxes.erase(std::remove_if(m_activatedAxes.begin(), m_activatedAxes.end(), + [&semiaxis](const ActivatedAxis& axis) + { + return semiaxis == axis.driverPrimitive; + }), m_activatedAxes.end()); +} + +bool CGenericJoystickButtonMapping::IsActive(const CDriverPrimitive& semiaxis) +{ + return std::find_if(m_activatedAxes.begin(), m_activatedAxes.end(), + [&semiaxis](const ActivatedAxis& axis) + { + return semiaxis == axis.driverPrimitive; + }) != m_activatedAxes.end(); +} diff --git a/xbmc/input/joysticks/generic/GenericJoystickButtonMapping.h b/xbmc/input/joysticks/generic/GenericJoystickButtonMapping.h new file mode 100644 index 0000000000..4bbb4c4847 --- /dev/null +++ b/xbmc/input/joysticks/generic/GenericJoystickButtonMapping.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "input/joysticks/DriverPrimitive.h" +#include "input/joysticks/IJoystickDriverHandler.h" + +#include <vector> + +namespace JOYSTICK +{ + class IJoystickButtonMap; + class IJoystickButtonMapper; + + /* + * \brief Generic implementation of a class that provides button mapping by + * translating driver events to button mapping commands + * + * Button mapping commands are invoked instantly for buttons and hats. + * + * Button mapping commands are deferred for a short while after an axis is + * activated, and only one command will be invoked per activation. + */ + class CGenericJoystickButtonMapping : public IJoystickDriverHandler + { + public: + /* + * \brief Constructor for CGenericJoystickButtonMapping + * + * \param buttonMapper Carries out button-mapping commands using <buttonMap> + * \param buttonMap The button map given to <buttonMapper> on each command + */ + CGenericJoystickButtonMapping(IJoystickButtonMapper* buttonMapper, IJoystickButtonMap* buttonMap); + + virtual ~CGenericJoystickButtonMapping(void) { } + + // implementation of IJoystickDriverHandler + virtual bool OnButtonMotion(unsigned int buttonIndex, bool bPressed) override; + virtual bool OnHatMotion(unsigned int hatIndex, HAT_STATE state) override; + virtual bool OnAxisMotion(unsigned int axisIndex, float position) override; + virtual void ProcessAxisMotions(void) override; + + private: + void MapPrimitive(const CDriverPrimitive& primitive); + + void Activate(const CDriverPrimitive& semiAxis); + void Deactivate(const CDriverPrimitive& semiAxis); + bool IsActive(const CDriverPrimitive& semiAxis); + + IJoystickButtonMapper* const m_buttonMapper; + IJoystickButtonMap* const m_buttonMap; + + struct ActivatedAxis + { + CDriverPrimitive driverPrimitive; + bool bEmitted; // true if this axis has emited a button-mapping command + }; + + std::vector<ActivatedAxis> m_activatedAxes; + unsigned int m_lastAction; + }; +} diff --git a/xbmc/input/joysticks/generic/GenericJoystickFeatureHandling.cpp b/xbmc/input/joysticks/generic/GenericJoystickFeatureHandling.cpp new file mode 100644 index 0000000000..d82ac7ca53 --- /dev/null +++ b/xbmc/input/joysticks/generic/GenericJoystickFeatureHandling.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ + +#include "GenericJoystickFeatureHandling.h" +#include "input/joysticks/DriverPrimitive.h" +#include "input/joysticks/IJoystickButtonMap.h" +#include "input/joysticks/IJoystickInputHandler.h" +#include "utils/log.h" + +using namespace JOYSTICK; + +#define ANALOG_DIGITAL_THRESHOLD 0.5f + +// --- CJoystickFeature -------------------------------------------------------- + +CJoystickFeature::CJoystickFeature(const FeatureName& name, IJoystickInputHandler* handler, IJoystickButtonMap* buttonMap) : + m_name(name), + m_handler(handler), + m_buttonMap(buttonMap) +{ +} + +// --- CScalarFeature ---------------------------------------------------------- + +CScalarFeature::CScalarFeature(const FeatureName& name, IJoystickInputHandler* handler, IJoystickButtonMap* buttonMap) : + CJoystickFeature(name, handler, buttonMap), + m_inputType(handler->GetInputType(name)), + m_bDigitalState(false), + m_analogState(0.0f) +{ +} + +bool CScalarFeature::OnDigitalMotion(const CDriverPrimitive& source, bool bPressed) +{ + bool bHandled = false; + + if (m_inputType == INPUT_TYPE::DIGITAL) + { + if (m_bDigitalState != bPressed) + { + m_bDigitalState = bPressed; + bHandled = OnDigitalMotion(bPressed); + } + } + else if (m_inputType == INPUT_TYPE::ANALOG) + { + bHandled = OnAnalogMotion(source, bPressed ? 1.0f : 0.0f); + } + + return bHandled; +} + +bool CScalarFeature::OnAnalogMotion(const CDriverPrimitive& source, float magnitude) +{ + bool bHandled = false; + + if (m_inputType == INPUT_TYPE::DIGITAL) + { + bHandled = OnDigitalMotion(source, magnitude >= ANALOG_DIGITAL_THRESHOLD); + } + else if (m_inputType == INPUT_TYPE::ANALOG) + { + if (m_analogState != 0.0f || magnitude != 0.0f) + { + m_analogState = magnitude; + bHandled = OnAnalogMotion(magnitude); + } + } + + return bHandled; +} + +bool CScalarFeature::OnDigitalMotion(bool bPressed) +{ + CLog::Log(LOGDEBUG, "Feature [ %s ] on %s %s", + m_name.c_str(), m_handler->ControllerID().c_str(), bPressed ? "pressed" : "released"); + + return m_handler->OnButtonPress(m_name, bPressed); +} + +bool CScalarFeature::OnAnalogMotion(float magnitude) +{ + const bool bActivated = (magnitude != 0.0f); + + 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 m_handler->OnButtonMotion(m_name, magnitude); +} + +// --- CAnalogStick ------------------------------------------------------------ + +CAnalogStick::CAnalogStick(const FeatureName& name, IJoystickInputHandler* handler, IJoystickButtonMap* buttonMap) : + CJoystickFeature(name, handler, buttonMap), + m_vertState(0.0f), + m_horizState(0.0f) +{ +} + +bool CAnalogStick::OnDigitalMotion(const CDriverPrimitive& source, bool bPressed) +{ + return OnAnalogMotion(source, bPressed ? 1.0f : 0.0f); +} + +bool CAnalogStick::OnAnalogMotion(const CDriverPrimitive& source, float magnitude) +{ + CDriverPrimitive up; + CDriverPrimitive down; + CDriverPrimitive right; + CDriverPrimitive left; + + m_buttonMap->GetAnalogStick(m_name, up, down, right, left); + + if (source == up) + m_vertAxis.SetPositiveDistance(magnitude); + else if (source == down) + m_vertAxis.SetNegativeDistance(magnitude); + else if (source == right) + m_horizAxis.SetPositiveDistance(magnitude); + else if (source == left) + m_horizAxis.SetNegativeDistance(magnitude); + else + { + // Just in case, avoid sticking + m_vertAxis.Reset(); + m_horizAxis.Reset(); + } + + return true; +} + +void CAnalogStick::ProcessMotions(void) +{ + const float newVertState = m_vertAxis.GetPosition(); + const float newHorizState = m_horizAxis.GetPosition(); + + if (m_vertState != 0 || m_horizState != 0 || + newVertState != 0 || newHorizState != 0) + { + m_vertState = newVertState; + m_horizState = newHorizState; + m_handler->OnAnalogStickMotion(m_name, newHorizState, newVertState); + } +} + +// --- CAccelerometer ---------------------------------------------------------- + +CAccelerometer::CAccelerometer(const FeatureName& name, IJoystickInputHandler* handler, IJoystickButtonMap* buttonMap) : + CJoystickFeature(name, handler, buttonMap), + m_xAxisState(0.0f), + m_yAxisState(0.0f), + m_zAxisState(0.0f) +{ +} + +bool CAccelerometer::OnDigitalMotion(const CDriverPrimitive& source, bool bPressed) +{ + return OnAnalogMotion(source, bPressed ? 1.0f : 0.0f); +} + +bool CAccelerometer::OnAnalogMotion(const CDriverPrimitive& source, float magnitude) +{ + CDriverPrimitive positiveX; + CDriverPrimitive positiveY; + CDriverPrimitive positiveZ; + + m_buttonMap->GetAccelerometer(m_name, positiveX, positiveY, positiveZ); + + if (source == positiveX) + m_xAxis.SetPositiveDistance(magnitude); + else if (source == positiveY) + m_yAxis.SetPositiveDistance(magnitude); + else if (source == positiveZ) + m_zAxis.SetPositiveDistance(magnitude); + else + { + // Just in case, avoid sticking + m_xAxis.Reset(); + m_xAxis.Reset(); + m_yAxis.Reset(); + } + + return true; +} + +void CAccelerometer::ProcessMotions(void) +{ + const float newXAxis = m_xAxis.GetPosition(); + const float newYAxis = m_yAxis.GetPosition(); + const float newZAxis = m_zAxis.GetPosition(); + + if (m_xAxisState != 0 || m_yAxisState != 0 || m_zAxisState != 0 || + newXAxis != 0 || newYAxis != 0 || newZAxis) + { + m_xAxisState = newXAxis; + m_yAxisState = newYAxis; + m_zAxisState = newZAxis; + m_handler->OnAccelerometerMotion(m_name, newXAxis, newYAxis, newZAxis); + } +} diff --git a/xbmc/input/joysticks/generic/GenericJoystickFeatureHandling.h b/xbmc/input/joysticks/generic/GenericJoystickFeatureHandling.h new file mode 100644 index 0000000000..077f0f02da --- /dev/null +++ b/xbmc/input/joysticks/generic/GenericJoystickFeatureHandling.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "input/joysticks/JoystickTypes.h" + +#include <memory> + +namespace JOYSTICK +{ + class CDriverPrimitive; + class IJoystickInputHandler; + class IJoystickButtonMap; + + class CJoystickFeature; + typedef std::shared_ptr<CJoystickFeature> FeaturePtr; + + /*! + * \brief Base class for joystick features + * + * See list of feature types in JoystickTypes.h. + */ + class CJoystickFeature + { + public: + CJoystickFeature(const FeatureName& name, IJoystickInputHandler* handler, IJoystickButtonMap* buttonMap); + virtual ~CJoystickFeature(void) { } + + /*! + * \brief A digital motion has occured + * + * \param source The source of the motion. Must be digital (button or hat) + * \param bPressed True for press motion, false for release motion + * + * \return true if the motion was handled, false otherwise + */ + virtual bool OnDigitalMotion(const CDriverPrimitive& source, bool bPressed) = 0; + + /*! + * \brief An analog motion has occured + * + * \param source The source of the motion. Must be a semiaxis + * \param magnitude The magnitude of the press or motion in the interval [0.0, 1.0] + * + * For semiaxes, the magnitude is the force or travel distance in the + * direction of the semiaxis. If the value is in the opposite direction, + * the magnitude is 0.0. + * + * For example, if the analog stick goes left, the negative semiaxis will + * have a value of 1.0 and the positive semiaxis will have a value of 0.0. + */ + virtual bool OnAnalogMotion(const CDriverPrimitive& source, float magnitude) = 0; + + /*! + * \brief Process the motions that have occured since the last invocation + * + * This allows features with motion on multiple driver primitives to call + * their handler once all driver primitives are accounted for. + */ + virtual void ProcessMotions(void) = 0; + + protected: + const FeatureName m_name; + IJoystickInputHandler* const m_handler; + IJoystickButtonMap* const m_buttonMap; + }; + + class CScalarFeature : public CJoystickFeature + { + public: + CScalarFeature(const FeatureName& name, IJoystickInputHandler* handler, IJoystickButtonMap* buttonMap); + virtual ~CScalarFeature(void) { } + + // implementation of CJoystickFeature + virtual bool OnDigitalMotion(const CDriverPrimitive& source, bool bPressed) override; + virtual bool OnAnalogMotion(const CDriverPrimitive& source, float magnitude) override; + virtual void ProcessMotions(void) override { } // Actions are dispatched immediately + + private: + bool OnDigitalMotion(bool bPressed); + bool OnAnalogMotion(float magnitude); + + const INPUT_TYPE m_inputType; + bool m_bDigitalState; + float m_analogState; + }; + + /*! + * \brief Axis of a feature (analog stick, accelerometer, etc) + * + * Axes are composed of two driver primitives, one for the positive semiaxis + * and one for the negative semiaxis. + * + * This effectively means that an axis is two-dimensional, with each dimension + * either: + * + * - a digital value (0.0 or 1.0) + * - an analog value (continuous in the interval [0.0, 1.0]) + */ + class CFeatureAxis + { + public: + CFeatureAxis(void) { Reset(); } + + /*! + * \brief Set value of positive axis + */ + void SetPositiveDistance(float distance) { m_positiveDistance = distance; } + + /*! + * \brief Set value of negative axis + */ + void SetNegativeDistance(float distance) { m_negativeDistance = distance; } + + /*! + * \brief Get the final value of this axis. + * + * This axis is two-dimensional, so we need to compress these into a single + * dimension. This is done by subtracting the negative from the positive. + * Some examples: + * + * Positive axis: 1.0 (User presses right or analog stick moves right) + * Negative axis: 0.0 + * ------------------- + * Pos - Neg: 1.0 (Emulated analog stick moves right) + * + * + * Positive axis: 0.0 + * Negative axis: 1.0 (User presses left or analog stick moves left) + * ------------------- + * Pos - Neg: -1.0 (Emulated analog stick moves left) + * + * + * Positive axis: 1.0 (User presses both buttons) + * Negative axis: 1.0 + * ------------------- + * Pos - Neg: 0.0 (Emulated analog stick is centered) + * + */ + float GetPosition(void) const { return m_positiveDistance - m_negativeDistance; } + + /*! + * \brief Reset both positive and negative values to zero + */ + void Reset(void) { m_positiveDistance = m_negativeDistance = 0.0f; } + + protected: + float m_positiveDistance; + float m_negativeDistance; + }; + + class CAnalogStick : public CJoystickFeature + { + public: + CAnalogStick(const FeatureName& name, IJoystickInputHandler* handler, IJoystickButtonMap* buttonMap); + virtual ~CAnalogStick(void) { } + + // implementation of CJoystickFeature + virtual bool OnDigitalMotion(const CDriverPrimitive& source, bool bPressed) override; + virtual bool OnAnalogMotion(const CDriverPrimitive& source, float magnitude) override; + virtual void ProcessMotions(void) override; + + protected: + CFeatureAxis m_vertAxis; + CFeatureAxis m_horizAxis; + + float m_vertState; + float m_horizState; + }; + + class CAccelerometer : public CJoystickFeature + { + public: + CAccelerometer(const FeatureName& name, IJoystickInputHandler* handler, IJoystickButtonMap* buttonMap); + virtual ~CAccelerometer(void) { } + + // implementation of CJoystickFeature + virtual bool OnDigitalMotion(const CDriverPrimitive& source, bool bPressed) override; + virtual bool OnAnalogMotion(const CDriverPrimitive& source, float magnitude) override; + virtual void ProcessMotions(void) override; + + protected: + CFeatureAxis m_xAxis; + CFeatureAxis m_yAxis; + CFeatureAxis m_zAxis; + + float m_xAxisState; + float m_yAxisState; + float m_zAxisState; + }; +} diff --git a/xbmc/input/joysticks/generic/GenericJoystickInputHandling.cpp b/xbmc/input/joysticks/generic/GenericJoystickInputHandling.cpp new file mode 100644 index 0000000000..f25767b74d --- /dev/null +++ b/xbmc/input/joysticks/generic/GenericJoystickInputHandling.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ + +#include "GenericJoystickInputHandling.h" +#include "input/joysticks/DriverPrimitive.h" +#include "input/joysticks/IJoystickButtonMap.h" +#include "input/joysticks/IJoystickInputHandler.h" +#include "input/joysticks/JoystickUtils.h" + +using namespace JOYSTICK; + +CGenericJoystickInputHandling::CGenericJoystickInputHandling(IJoystickInputHandler* handler, IJoystickButtonMap* buttonMap) + : m_handler(handler), + m_buttonMap(buttonMap) +{ +} + +CGenericJoystickInputHandling::~CGenericJoystickInputHandling(void) +{ +} + +bool CGenericJoystickInputHandling::OnButtonMotion(unsigned int buttonIndex, bool bPressed) +{ + return OnDigitalMotion(CDriverPrimitive(buttonIndex), bPressed); +} + +bool CGenericJoystickInputHandling::OnHatMotion(unsigned int hatIndex, HAT_STATE state) +{ + bool bHandled = false; + + bHandled |= OnDigitalMotion(CDriverPrimitive(hatIndex, HAT_DIRECTION::UP), state & HAT_DIRECTION::UP); + bHandled |= OnDigitalMotion(CDriverPrimitive(hatIndex, HAT_DIRECTION::RIGHT), state & HAT_DIRECTION::RIGHT); + bHandled |= OnDigitalMotion(CDriverPrimitive(hatIndex, HAT_DIRECTION::DOWN), state & HAT_DIRECTION::DOWN); + bHandled |= OnDigitalMotion(CDriverPrimitive(hatIndex, HAT_DIRECTION::LEFT), state & HAT_DIRECTION::LEFT); + + return bHandled; +} + +bool CGenericJoystickInputHandling::OnAxisMotion(unsigned int axisIndex, float position) +{ + bool bHandled = false; + + CDriverPrimitive positiveSemiaxis(axisIndex, SEMIAXIS_DIRECTION::POSITIVE); + CDriverPrimitive negativeSemiaxis(axisIndex, SEMIAXIS_DIRECTION::NEGATIVE); + + bHandled |= OnAnalogMotion(positiveSemiaxis, position > 0.0f ? position : 0.0f); + bHandled |= OnAnalogMotion(negativeSemiaxis, position < 0.0f ? -position : 0.0f); + + return bHandled; +} + +void CGenericJoystickInputHandling::ProcessAxisMotions(void) +{ + for (std::map<FeatureName, FeaturePtr>::iterator it = m_features.begin(); it != m_features.end(); ++it) + it->second->ProcessMotions(); +} + +bool CGenericJoystickInputHandling::OnDigitalMotion(const CDriverPrimitive& source, bool bPressed) +{ + bool bHandled = false; + + FeatureName featureName; + if (m_buttonMap->GetFeature(source, featureName)) + { + FeaturePtr& feature = m_features[featureName]; + + if (!feature) + feature = FeaturePtr(CreateFeature(featureName)); + + if (feature) + bHandled = feature->OnDigitalMotion(source, bPressed); + } + + return bHandled; +} + +bool CGenericJoystickInputHandling::OnAnalogMotion(const CDriverPrimitive& source, float magnitude) +{ + bool bHandled = false; + + FeatureName featureName; + if (m_buttonMap->GetFeature(source, featureName)) + { + FeaturePtr& feature = m_features[featureName]; + + if (!feature) + feature = FeaturePtr(CreateFeature(featureName)); + + if (feature) + bHandled = feature->OnAnalogMotion(source, magnitude); + } + + return bHandled; +} + +CJoystickFeature* CGenericJoystickInputHandling::CreateFeature(const FeatureName& featureName) +{ + CJoystickFeature* feature = nullptr; + + switch (m_buttonMap->GetFeatureType(featureName)) + { + case FEATURE_TYPE::SCALAR: + { + feature = new CScalarFeature(featureName, m_handler, m_buttonMap); + break; + } + case FEATURE_TYPE::ANALOG_STICK: + { + feature = new CAnalogStick(featureName, m_handler, m_buttonMap); + break; + } + case FEATURE_TYPE::ACCELEROMETER: + { + feature = new CAccelerometer(featureName, m_handler, m_buttonMap); + break; + } + default: + break; + } + + return feature; +} diff --git a/xbmc/input/joysticks/generic/GenericJoystickInputHandling.h b/xbmc/input/joysticks/generic/GenericJoystickInputHandling.h new file mode 100644 index 0000000000..da0a338865 --- /dev/null +++ b/xbmc/input/joysticks/generic/GenericJoystickInputHandling.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014-2016 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 + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "GenericJoystickFeatureHandling.h" +#include "input/joysticks/IJoystickDriverHandler.h" +#include "input/joysticks/JoystickTypes.h" + +#include <map> + +namespace JOYSTICK +{ + class CDriverPrimitive; + class IJoystickInputHandler; + class IJoystickButtonMap; + + /*! + * \brief Class to translate input from the driver into higher-level features + * + * Raw driver input arrives for three elements: buttons, hats and axes. When + * driver input is handled by this class, it translates the raw driver + * elements into physical joystick features, such as buttons, analog sticks, + * etc. + * + * A button map is used to translate driver primitives to controller features. + * The button map has been abstracted away behind the IJoystickButtonMap + * interface so that it can be provided by an add-on. + */ + class CGenericJoystickInputHandling : public IJoystickDriverHandler + { + public: + CGenericJoystickInputHandling(IJoystickInputHandler* handler, IJoystickButtonMap* buttonMap); + + virtual ~CGenericJoystickInputHandling(void); + + // implementation of IJoystickDriverHandler + virtual bool OnButtonMotion(unsigned int buttonIndex, bool bPressed) override; + virtual bool OnHatMotion(unsigned int hatIndex, HAT_STATE state) override; + virtual bool OnAxisMotion(unsigned int axisIndex, float position) override; + virtual void ProcessAxisMotions(void) override; + + private: + bool OnDigitalMotion(const CDriverPrimitive& source, bool bPressed); + bool OnAnalogMotion(const CDriverPrimitive& source, float magnitude); + + CJoystickFeature* CreateFeature(const FeatureName& featureName); + + IJoystickInputHandler* const m_handler; + IJoystickButtonMap* const m_buttonMap; + + std::map<FeatureName, FeaturePtr> m_features; + }; +} diff --git a/xbmc/input/joysticks/generic/Makefile b/xbmc/input/joysticks/generic/Makefile new file mode 100644 index 0000000000..f7f64fa112 --- /dev/null +++ b/xbmc/input/joysticks/generic/Makefile @@ -0,0 +1,8 @@ +SRCS=GenericJoystickButtonMapping.cpp \ + GenericJoystickFeatureHandling.cpp \ + GenericJoystickInputHandling.cpp \ + +LIB=input_joysticks_generic.a + +include ../../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) |