diff options
author | jenkins4kodi <jenkins4kodi@users.noreply.github.com> | 2016-11-13 02:59:14 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-11-13 02:59:14 +0100 |
commit | 24990b5b9f334a128be75710998c4edc343fd8de (patch) | |
tree | bf93c9ff6533b1960cc27bef9df903af3c82d9f9 | |
parent | ae37dd618d91912749b3de5e749cac5b3bc05eb2 (diff) | |
parent | 921862eb77687291739e90f3850f33d7e35e62be (diff) |
Merge pull request #10910 from garbear/fix-skipping
50 files changed, 994 insertions, 110 deletions
diff --git a/Kodi.xcodeproj/project.pbxproj b/Kodi.xcodeproj/project.pbxproj index 0a3b71c7d1..7ce0ad3682 100644 --- a/Kodi.xcodeproj/project.pbxproj +++ b/Kodi.xcodeproj/project.pbxproj @@ -253,6 +253,8 @@ 5EB3113C1A978B9B00551907 /* CueInfoLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5EB3113A1A978B9B00551907 /* CueInfoLoader.cpp */; }; 5EE4F9181A9FF36F002E20F8 /* CueInfoLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5EB3113A1A978B9B00551907 /* CueInfoLoader.cpp */; }; 5EF801001A97892A0035AA4D /* ReplayGain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5EF800FE1A97892A0035AA4D /* ReplayGain.cpp */; }; + 680E23B31DD3D65200D2B766 /* GUIDialogButtonCapture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 680E23B11DD3D65200D2B766 /* GUIDialogButtonCapture.cpp */; }; + 680E23B41DD3D65200D2B766 /* GUIDialogButtonCapture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 680E23B11DD3D65200D2B766 /* GUIDialogButtonCapture.cpp */; }; 6815C1411CC7BADB000DB91A /* RumbleGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6815C13F1CC7BADB000DB91A /* RumbleGenerator.cpp */; }; 6815C1421CC7BADB000DB91A /* RumbleGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6815C13F1CC7BADB000DB91A /* RumbleGenerator.cpp */; }; 6838CF811D6665510057F17B /* DeadzoneFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6838CF7F1D6665510057F17B /* DeadzoneFilter.cpp */; }; @@ -2762,6 +2764,9 @@ 5EB3113B1A978B9B00551907 /* CueInfoLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CueInfoLoader.h; sourceTree = "<group>"; }; 5EF800FE1A97892A0035AA4D /* ReplayGain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReplayGain.cpp; sourceTree = "<group>"; }; 5EF800FF1A97892A0035AA4D /* ReplayGain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReplayGain.h; sourceTree = "<group>"; }; + 680E23B11DD3D65200D2B766 /* GUIDialogButtonCapture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GUIDialogButtonCapture.cpp; path = games/controllers/dialogs/GUIDialogButtonCapture.cpp; sourceTree = "<group>"; }; + 680E23B21DD3D65200D2B766 /* GUIDialogButtonCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GUIDialogButtonCapture.h; path = games/controllers/dialogs/GUIDialogButtonCapture.h; sourceTree = "<group>"; }; + 680E23B51DD42BF700D2B766 /* IActionMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IActionMap.h; path = joysticks/IActionMap.h; sourceTree = "<group>"; }; 6815C13F1CC7BADB000DB91A /* RumbleGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RumbleGenerator.cpp; path = joysticks/RumbleGenerator.cpp; sourceTree = "<group>"; }; 6815C1401CC7BADB000DB91A /* RumbleGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RumbleGenerator.h; path = joysticks/RumbleGenerator.h; sourceTree = "<group>"; }; 6819C8B61D76492500A60E30 /* IButtonMapCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IButtonMapCallback.h; path = joysticks/IButtonMapCallback.h; sourceTree = "<group>"; }; @@ -6149,6 +6154,24 @@ path = osx; sourceTree = "<group>"; }; + 680E23B01DD3D63600D2B766 /* dialogs */ = { + isa = PBXGroup; + children = ( + 680E23B11DD3D65200D2B766 /* GUIDialogButtonCapture.cpp */, + 680E23B21DD3D65200D2B766 /* GUIDialogButtonCapture.h */, + ); + name = dialogs; + sourceTree = "<group>"; + }; + 6827D71B1DD03E5300BE9114 /* dialogs */ = { + isa = PBXGroup; + children = ( + 6827D71C1DD03E5F00BE9114 /* GUIDialogNewJoystick.cpp */, + 6827D71D1DD03E5F00BE9114 /* GUIDialogNewJoystick.h */, + ); + name = dialogs; + sourceTree = "<group>"; + }; 68AE5BA21C92410300C4D527 /* Peripheral */ = { isa = PBXGroup; children = ( @@ -6169,6 +6192,7 @@ 68AE5BAD1C9241DF00C4D527 /* DefaultJoystick.h */, 68AE5BAE1C9241DF00C4D527 /* DriverPrimitive.cpp */, 68AE5BAF1C9241DF00C4D527 /* DriverPrimitive.h */, + 680E23B51DD42BF700D2B766 /* IActionMap.h */, 68AE5BB01C9241DF00C4D527 /* IButtonMap.h */, 6819C8B61D76492500A60E30 /* IButtonMapCallback.h */, 68AE5BB11C9241DF00C4D527 /* IButtonMapper.h */, @@ -6234,6 +6258,7 @@ 68AE5BFA1C92433900C4D527 /* controllers */ = { isa = PBXGroup; children = ( + 680E23B01DD3D63600D2B766 /* dialogs */, 68AE5BFB1C92435200C4D527 /* guicontrols */, 68AE5BFC1C92436500C4D527 /* windows */, 68AE5C231C9243A000C4D527 /* Controller.cpp */, @@ -9831,6 +9856,7 @@ E38E1FAE0D25F9FD00618676 /* DVDSubtitleParserSubrip.cpp in Sources */, E38E1FAF0D25F9FD00618676 /* DVDSubtitleStream.cpp in Sources */, E38E1FC50D25F9FD00618676 /* AudioDecoder.cpp in Sources */, + 680E23B31DD3D65200D2B766 /* GUIDialogButtonCapture.cpp in Sources */, E38E1FC70D25F9FD00618676 /* CodecFactory.cpp in Sources */, E38E1FE90D25F9FD00618676 /* LinuxRendererGL.cpp in Sources */, E38E1FEC0D25F9FD00618676 /* RenderManager.cpp in Sources */, @@ -10973,6 +10999,7 @@ E4991198174E5CF600741B6D /* AEEncoderFFmpeg.cpp in Sources */, E49911A6174E5CFE00741B6D /* AEBitstreamPacker.cpp in Sources */, E49911A8174E5CFE00741B6D /* AEChannelInfo.cpp in Sources */, + 680E23B41DD3D65200D2B766 /* GUIDialogButtonCapture.cpp in Sources */, E49911AA174E5CFE00741B6D /* AEDeviceInfo.cpp in Sources */, E49911AB174E5CFE00741B6D /* AELimiter.cpp in Sources */, E49911AC174E5CFE00741B6D /* AEPackIEC61937.cpp in Sources */, diff --git a/Makefile.in b/Makefile.in index f500e9de49..0d70e9b122 100644 --- a/Makefile.in +++ b/Makefile.in @@ -54,6 +54,7 @@ DIRECTORY_ARCHIVES=$(VideoPlayer_ARCHIVES) \ xbmc/filesystem/VideoDatabaseDirectory/videodatabasedirectory.a \ xbmc/filesystem/filesystem.a \ xbmc/games/controllers/controllers.a \ + xbmc/games/controllers/dialogs/controllerdialogs.a \ xbmc/games/controllers/guicontrols/controllerguicontrols.a \ xbmc/games/controllers/windows/controllerwindows.a \ xbmc/guilib/guilib.a \ diff --git a/addons/kodi.peripheral/addon.xml b/addons/kodi.peripheral/addon.xml index 47ae0b84ef..7dbd763262 100644 --- a/addons/kodi.peripheral/addon.xml +++ b/addons/kodi.peripheral/addon.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<addon id="kodi.peripheral" version="1.1.0" provider-name="Team-Kodi"> - <backwards-compatibility abi="1.1.0"/> +<addon id="kodi.peripheral" version="1.2.0" provider-name="Team-Kodi"> + <backwards-compatibility abi="1.2.0"/> <requires> <import addon="xbmc.core" version="0.1.0"/> </requires> diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index 7b6da409dd..fd225ac110 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -16032,7 +16032,31 @@ msgctxt "#35012" msgid "A new controller has been detected. Configuration can be done at any time in \"Settings -> System Settings -> Input\". Would you like to configure it now?" msgstr "" -#empty strings from id 35013 to 35048 +#. Label of the button to fix the bug where buttons are skipped in the controller dialog +#: addons/skin.estuary/1080i/DialogGameControllers.xml +msgctxt "#35013" +msgid "Fix skipping" +msgstr "" + +#. Help text of the button to fix the bug where buttons are skipped in the controller dialog. %s - list of disabled buttons and axes +#: +msgctxt "#35014" +msgid "Some controllers have buttons and axes that interfere with mapping. Press these now to disable them:[CR]%s" +msgstr "" + +#. Name of a controller button, e.g. Button 1. %d - button index +#: +msgctxt "#35015" +msgid "Button %d" +msgstr "" + +#. Name of a controller axis, e.g. Axis 2. %d - axis index +#: +msgctxt "#35016" +msgid "Axis %d" +msgstr "" + +#empty strings from id 35017 to 35048 #. Name of game add-ons category #: xbmc/filesystem/AddonsDirectory.cpp diff --git a/addons/skin.estuary/1080i/DialogGameControllers.xml b/addons/skin.estuary/1080i/DialogGameControllers.xml index db3c0f1535..37e6c13392 100644 --- a/addons/skin.estuary/1080i/DialogGameControllers.xml +++ b/addons/skin.estuary/1080i/DialogGameControllers.xml @@ -152,6 +152,11 @@ <param name="id" value="17" /> <param name="label" value="$LOCALIZE[10043]" /> </include> + <include content="DefaultDialogButton"> + <param name="width" value="350" /> + <param name="id" value="21" /> + <param name="label" value="$LOCALIZE[35013]" /> + </include> </control> </control> </controls> diff --git a/project/cmake/treedata/common/games.txt b/project/cmake/treedata/common/games.txt index e4e8b890c6..b622f5a56b 100644 --- a/project/cmake/treedata/common/games.txt +++ b/project/cmake/treedata/common/games.txt @@ -1,3 +1,4 @@ xbmc/games/controllers games/controllers +xbmc/games/controllers/dialogs games/controllers/dialogs xbmc/games/controllers/guicontrols games/controllers/guicontrols xbmc/games/controllers/windows games/controllers/windows diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_dll.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_dll.h index 772269a640..f9d7482b1a 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_dll.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_dll.h @@ -158,12 +158,45 @@ extern "C" * @brief Add or update joystick features * @param joystick The device's joystick properties; unknown values may be left at their default * @param controller_id The game controller profile being updated - * @param feature_count The number of features int the features array + * @param feature_count The number of features in the features array * @param features The array of features * @return PERIPHERAL_NO_ERROR if successful */ PERIPHERAL_ERROR MapFeatures(const JOYSTICK_INFO* joystick, const char* controller_id, - unsigned int feature_count, JOYSTICK_FEATURE* features); + unsigned int feature_count, const JOYSTICK_FEATURE* features); + + /*! + * @brief Get the driver primitives that should be ignored while mapping the device + * @param joystick The device's joystick properties; unknown values may be left at their default + * @param primitive_count The number of features allocated for the primitives array + * @param primitives The array of allocated driver primitives to be ignored + * @return PERIPHERAL_NO_ERROR if successful; array must be freed using + * FreePrimitives() in this case + */ + PERIPHERAL_ERROR GetIgnoredPrimitives(const JOYSTICK_INFO* joystick, + unsigned int* primitive_count, + JOYSTICK_DRIVER_PRIMITIVE** primitives); + + /*! + * @brief Free the memory allocated in GetIgnoredPrimitives() + * + * Must be called if GetIgnoredPrimitives() returns PERIPHERAL_NO_ERROR. + * + * @param primitive_count The number of driver primitives allocated for the primitives array + * @param primitives The array of allocated driver primitives + */ + void FreePrimitives(unsigned int primitive_count, JOYSTICK_DRIVER_PRIMITIVE* primitives); + + /*! + * @brief Set the list of driver primitives that are ignored for the device + * @param joystick The device's joystick properties; unknown values may be left at their default + * @param primitive_count The number of driver features in the primitives array + * @param primitives The array of driver primitives to ignore + * @return PERIPHERAL_NO_ERROR if successful + */ + PERIPHERAL_ERROR SetIgnoredPrimitives(const JOYSTICK_INFO* joystick, + unsigned int primitive_count, + const JOYSTICK_DRIVER_PRIMITIVE* primitives); /*! * @brief Save the button map for the given joystick @@ -172,6 +205,12 @@ extern "C" void SaveButtonMap(const JOYSTICK_INFO* joystick); /*! + * @brief Revert the button map to the last time it was loaded or committed to disk + * @param joystick The device's joystick properties + */ + void RevertButtonMap(const JOYSTICK_INFO* joystick); + + /*! * @brief Reset the button map for the given joystick and controller profile ID * @param joystick The device's joystick properties * @param controller_id The game controller profile being reset @@ -208,7 +247,11 @@ extern "C" pClient->GetFeatures = GetFeatures; pClient->FreeFeatures = FreeFeatures; pClient->MapFeatures = MapFeatures; + pClient->GetIgnoredPrimitives = GetIgnoredPrimitives; + pClient->FreePrimitives = FreePrimitives; + pClient->SetIgnoredPrimitives = SetIgnoredPrimitives; pClient->SaveButtonMap = SaveButtonMap; + pClient->RevertButtonMap = RevertButtonMap; pClient->ResetButtonMap = ResetButtonMap; pClient->PowerOffJoystick = PowerOffJoystick; #endif diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_types.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_types.h index 47e4fa9472..2df5622a14 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_types.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_types.h @@ -51,10 +51,10 @@ #endif /* current Peripheral API version */ -#define PERIPHERAL_API_VERSION "1.1.0" +#define PERIPHERAL_API_VERSION "1.2.0" /* min. Peripheral API version */ -#define PERIPHERAL_MIN_API_VERSION "1.1.0" +#define PERIPHERAL_MIN_API_VERSION "1.2.0" /* indicates a joystick has no preference for port number */ #define NO_PORT_REQUESTED (-1) @@ -304,8 +304,12 @@ extern "C" void (__cdecl* FreeJoystickInfo)(JOYSTICK_INFO*); PERIPHERAL_ERROR (__cdecl* GetFeatures)(const JOYSTICK_INFO*, const char*, unsigned int*, JOYSTICK_FEATURE**); void (__cdecl* FreeFeatures)(unsigned int, JOYSTICK_FEATURE*); - PERIPHERAL_ERROR (__cdecl* MapFeatures)(const JOYSTICK_INFO*, const char*, unsigned int, JOYSTICK_FEATURE*); + PERIPHERAL_ERROR (__cdecl* MapFeatures)(const JOYSTICK_INFO*, const char*, unsigned int, const JOYSTICK_FEATURE*); + PERIPHERAL_ERROR (__cdecl* GetIgnoredPrimitives)(const JOYSTICK_INFO*, unsigned int*, JOYSTICK_DRIVER_PRIMITIVE**); + void (__cdecl* FreePrimitives)(unsigned int, JOYSTICK_DRIVER_PRIMITIVE*); + PERIPHERAL_ERROR (__cdecl* SetIgnoredPrimitives)(const JOYSTICK_INFO*, unsigned int, const JOYSTICK_DRIVER_PRIMITIVE*); void (__cdecl* SaveButtonMap)(const JOYSTICK_INFO*); + void (__cdecl* RevertButtonMap)(const JOYSTICK_INFO*); void (__cdecl* ResetButtonMap)(const JOYSTICK_INFO*, const char*); void (__cdecl* PowerOffJoystick)(unsigned int); ///} diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_utils.hpp b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_utils.hpp index 79df0c5232..f01180b1dc 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_utils.hpp +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/kodi_peripheral_utils.hpp @@ -533,6 +533,11 @@ namespace ADDON } } + static void FreeStruct(JOYSTICK_DRIVER_PRIMITIVE& primitive) + { + (void)primitive; + } + private: JOYSTICK_DRIVER_PRIMITIVE_TYPE m_type; unsigned int m_driverIndex; @@ -540,6 +545,8 @@ namespace ADDON JOYSTICK_DRIVER_SEMIAXIS_DIRECTION m_semiAxisDirection; }; + typedef PeripheralVector<DriverPrimitive, JOYSTICK_DRIVER_PRIMITIVE> DriverPrimitives; + /*! * ADDON::JoystickFeature * diff --git a/xbmc/dialogs/GUIDialogOK.cpp b/xbmc/dialogs/GUIDialogOK.cpp index 91fc8fe385..f920cd4c33 100644 --- a/xbmc/dialogs/GUIDialogOK.cpp +++ b/xbmc/dialogs/GUIDialogOK.cpp @@ -47,27 +47,29 @@ bool CGUIDialogOK::OnMessage(CGUIMessage& message) } // \brief Show CGUIDialogOK dialog, then wait for user to dismiss it. -void CGUIDialogOK::ShowAndGetInput(CVariant heading, CVariant text) +bool CGUIDialogOK::ShowAndGetInput(CVariant heading, CVariant text) { CGUIDialogOK *dialog = (CGUIDialogOK *)g_windowManager.GetWindow(WINDOW_DIALOG_OK); if (!dialog) - return; + return false; dialog->SetHeading(heading); dialog->SetText(text); dialog->Open(); + return dialog->IsConfirmed(); } // \brief Show CGUIDialogOK dialog, then wait for user to dismiss it. -void CGUIDialogOK::ShowAndGetInput(CVariant heading, CVariant line0, CVariant line1, CVariant line2) +bool CGUIDialogOK::ShowAndGetInput(CVariant heading, CVariant line0, CVariant line1, CVariant line2) { CGUIDialogOK *dialog = (CGUIDialogOK *)g_windowManager.GetWindow(WINDOW_DIALOG_OK); if (!dialog) - return; + return false; dialog->SetHeading(heading); dialog->SetLine(0, line0); dialog->SetLine(1, line1); dialog->SetLine(2, line2); dialog->Open(); + return dialog->IsConfirmed(); } void CGUIDialogOK::OnInitWindow() diff --git a/xbmc/dialogs/GUIDialogOK.h b/xbmc/dialogs/GUIDialogOK.h index 3ee5c58327..85e2959b19 100644 --- a/xbmc/dialogs/GUIDialogOK.h +++ b/xbmc/dialogs/GUIDialogOK.h @@ -32,8 +32,8 @@ public: CGUIDialogOK(void); virtual ~CGUIDialogOK(void); virtual bool OnMessage(CGUIMessage& message); - static void ShowAndGetInput(CVariant heading, CVariant text); - static void ShowAndGetInput(CVariant heading, CVariant line0, CVariant line1, CVariant line2); + static bool ShowAndGetInput(CVariant heading, CVariant text); + static bool ShowAndGetInput(CVariant heading, CVariant line0, CVariant line1, CVariant line2); protected: virtual void OnInitWindow(); virtual int GetDefaultLabelID(int controlId) const; diff --git a/xbmc/games/controllers/dialogs/CMakeLists.txt b/xbmc/games/controllers/dialogs/CMakeLists.txt new file mode 100644 index 0000000000..436712ca8c --- /dev/null +++ b/xbmc/games/controllers/dialogs/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SOURCES GUIDialogButtonCapture.cpp) + +set(HEADERS GUIDialogButtonCapture.h) + +core_add_library(games_controller_dialogs) diff --git a/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp new file mode 100644 index 0000000000..3d3d817c4c --- /dev/null +++ b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 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 "GUIDialogButtonCapture.h" +#include "dialogs/GUIDialogOK.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "guilib/WindowIDs.h" +#include "input/joysticks/IActionMap.h" +#include "input/joysticks/IButtonMap.h" +#include "input/joysticks/IButtonMapCallback.h" +#include "input/joysticks/JoystickUtils.h" +#include "input/Key.h" +#include "utils/log.h" +#include "peripherals/Peripherals.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" + +#include <algorithm> +#include <iterator> + +using namespace GAME; + +CGUIDialogButtonCapture::CGUIDialogButtonCapture(const std::string& controllerId) : + CThread("ButtonCaptureDlg"), + m_controllerId(controllerId) +{ +} + +void CGUIDialogButtonCapture::Show() +{ + using namespace KODI::MESSAGING; + + if (!IsRunning()) + { + InstallHooks(); + + Create(); + + bool bAccepted = CGUIDialogOK::ShowAndGetInput(CVariant{ 35013 }, CVariant{ GetDialogText() }); // "Fix skipping" + + StopThread(false); + + m_captureEvent.Set(); + + if (ButtonMapCallback()) + { + if (bAccepted) + { + // See documentation of IButtonMapCallback::ResetIgnoredPrimitives() + // for why this call is needed + if (m_capturedPrimitives.empty()) + ButtonMapCallback()->ResetIgnoredPrimitives(); + + ButtonMapCallback()->SaveButtonMap(); + } + else + ButtonMapCallback()->RevertButtonMap(); + } + + RemoveHooks(); + } +} + +void CGUIDialogButtonCapture::Process() +{ + while (!m_bStop) + { + m_captureEvent.Wait(); + + if (m_bStop) + break; + + //! @todo Move to rendering thread when there is a rendering thread + auto dialog = dynamic_cast<CGUIDialogOK*>(g_windowManager.GetWindow(WINDOW_DIALOG_OK)); + if (dialog) + dialog->SetText(GetDialogText()); + } +} + +bool CGUIDialogButtonCapture::MapPrimitive(JOYSTICK::IButtonMap* buttonMap, + JOYSTICK::IActionMap* actionMap, + const JOYSTICK::CDriverPrimitive& primitive) +{ + if (m_bStop) + return false; + + // First check to see if driver primitive closes the dialog + if (actionMap) + { + std::string feature; + if (buttonMap->GetFeature(primitive, feature)) + { + switch (actionMap->GetActionID(feature)) + { + case ACTION_SELECT_ITEM: + case ACTION_NAV_BACK: + case ACTION_PREVIOUS_MENU: + return false; + default: + break; + } + } + } + + // Check if we have already started capturing primitives for a device + const bool bHasDevice = !m_deviceName.empty(); + + // If a primitive comes from a different device, ignore it + if (bHasDevice && m_deviceName != buttonMap->DeviceName()) + { + CLog::Log(LOGDEBUG, "%s: ignoring input from device %s", m_controllerId.c_str(), buttonMap->DeviceName().c_str()); + return false; + } + + if (!bHasDevice) + { + CLog::Log(LOGDEBUG, "%s: capturing input for device %s", m_controllerId.c_str(), buttonMap->DeviceName().c_str()); + m_deviceName = buttonMap->DeviceName(); + } + + if (AddPrimitive(primitive)) + { + buttonMap->SetIgnoredPrimitives(m_capturedPrimitives); + m_captureEvent.Set(); + } + + return true; +} + +bool CGUIDialogButtonCapture::AddPrimitive(const JOYSTICK::CDriverPrimitive& primitive) +{ + bool bValid = false; + + if (primitive.Type() == JOYSTICK::PRIMITIVE_TYPE::BUTTON) + { + bValid = std::find(m_capturedPrimitives.begin(), m_capturedPrimitives.end(), primitive) == m_capturedPrimitives.end(); + } + else if (primitive.Type() == JOYSTICK::PRIMITIVE_TYPE::SEMIAXIS) + { + // Don't need to do anything if opposite semiaxis is already captured + JOYSTICK::CDriverPrimitive opposite(primitive.Index(), primitive.SemiAxisDirection() * -1); + + bValid = std::find(m_capturedPrimitives.begin(), m_capturedPrimitives.end(), primitive) == m_capturedPrimitives.end() && + std::find(m_capturedPrimitives.begin(), m_capturedPrimitives.end(), opposite) == m_capturedPrimitives.end(); + } + + if (bValid) + { + m_capturedPrimitives.emplace_back(primitive); + return true; + } + + return false; +} + +std::string CGUIDialogButtonCapture::GetDialogText() +{ + // "Some controllers have buttons and axes that interfere with mapping. Press + // these now to disable them:[CR][CR]%s" + std::string dialogText = g_localizeStrings.Get(35014); + + std::vector<std::string> primitives; + + std::transform(m_capturedPrimitives.begin(), m_capturedPrimitives.end(), std::back_inserter(primitives), + [](const JOYSTICK::CDriverPrimitive& primitive) + { + return GetPrimitiveName(primitive); + }); + + return StringUtils::Format(dialogText.c_str(), StringUtils::Join(primitives, " | ").c_str()); +} + +void CGUIDialogButtonCapture::InstallHooks(void) +{ + using namespace PERIPHERALS; + + g_peripherals.RegisterJoystickButtonMapper(this); + g_peripherals.RegisterObserver(this); +} + +void CGUIDialogButtonCapture::RemoveHooks(void) +{ + using namespace PERIPHERALS; + + g_peripherals.UnregisterObserver(this); + g_peripherals.UnregisterJoystickButtonMapper(this); +} + +void CGUIDialogButtonCapture::Notify(const Observable& obs, const ObservableMessage msg) +{ + using namespace PERIPHERALS; + + switch (msg) + { + case ObservableMessagePeripheralsChanged: + { + g_peripherals.UnregisterJoystickButtonMapper(this); + g_peripherals.RegisterJoystickButtonMapper(this); + break; + } + default: + break; + } +} + +std::string CGUIDialogButtonCapture::GetPrimitiveName(const JOYSTICK::CDriverPrimitive& primitive) +{ + std::string primitiveTemplate; + + switch (primitive.Type()) + { + case JOYSTICK::PRIMITIVE_TYPE::BUTTON: + primitiveTemplate = g_localizeStrings.Get(35015); // "Button %d" + break; + case JOYSTICK::PRIMITIVE_TYPE::SEMIAXIS: + primitiveTemplate = g_localizeStrings.Get(35016); // "Axis %d" + break; + default: break; + } + + return StringUtils::Format(primitiveTemplate.c_str(), primitive.Index()); +} diff --git a/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h new file mode 100644 index 0000000000..3cf6503218 --- /dev/null +++ b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 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/IButtonMapper.h" +#include "threads/Event.h" +#include "threads/Thread.h" +#include "utils/Observer.h" + +#include <vector> + +namespace GAME +{ + class CGUIDialogButtonCapture : public JOYSTICK::IButtonMapper, + public Observer, + protected CThread + { + public: + CGUIDialogButtonCapture(const std::string& controllerId); + + // implementation of IButtonMapper + virtual std::string ControllerID(void) const override { return m_controllerId; } + virtual bool NeedsCooldown(void) const override { return false; } + virtual bool MapPrimitive(JOYSTICK::IButtonMap* buttonMap, + JOYSTICK::IActionMap* actionMap, + const JOYSTICK::CDriverPrimitive& primitive) override; + + // implementation of Observer + virtual void Notify(const Observable &obs, const ObservableMessage msg) override; + + void Show(); + + protected: + // implementation of CThread + virtual void Process() override; + + private: + bool AddPrimitive(const JOYSTICK::CDriverPrimitive& primitive); + + std::string GetDialogText(); + + void InstallHooks(); + void RemoveHooks(); + + static std::string GetPrimitiveName(const JOYSTICK::CDriverPrimitive& primitive); + + // Construction parameters + const std::string m_controllerId; + + // Button capture parameters + std::string m_deviceName; + std::vector<JOYSTICK::CDriverPrimitive> m_capturedPrimitives; + CEvent m_captureEvent; + }; +} diff --git a/xbmc/games/controllers/dialogs/Makefile b/xbmc/games/controllers/dialogs/Makefile new file mode 100644 index 0000000000..18739a2d5b --- /dev/null +++ b/xbmc/games/controllers/dialogs/Makefile @@ -0,0 +1,6 @@ +SRCS=GUIDialogButtonCapture.cpp \ + +LIB=controllerdialogs.a + +include ../../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp b/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp index 372a335b0f..85d49d1859 100644 --- a/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp +++ b/xbmc/games/controllers/windows/GUIConfigurationWizard.cpp @@ -32,6 +32,7 @@ using namespace GAME; #define ESC_KEY_CODE 27 +#define SKIPPING_DETECTION_MS 200 CGUIConfigurationWizard::CGUIConfigurationWizard() : CThread("GUIConfigurationWizard") @@ -44,9 +45,10 @@ void CGUIConfigurationWizard::InitializeState(void) m_currentButton = nullptr; m_currentDirection = JOYSTICK::ANALOG_STICK_DIRECTION::UNKNOWN; m_history.clear(); + m_lastMappingActionMs = 0; } -void CGUIConfigurationWizard::Run(const std::string& strControllerId, const std::vector<IFeatureButton*>& buttons) +void CGUIConfigurationWizard::Run(const std::string& strControllerId, const std::vector<IFeatureButton*>& buttons, IConfigurationWizardCallback* callback) { Abort(); @@ -56,6 +58,7 @@ void CGUIConfigurationWizard::Run(const std::string& strControllerId, const std: // Set Run() parameters m_strControllerId = strControllerId; m_buttons = buttons; + m_callback = callback; // Initialize state variables InitializeState(); @@ -92,6 +95,8 @@ void CGUIConfigurationWizard::Process(void) { CLog::Log(LOGDEBUG, "Starting configuration wizard"); + m_lastMappingActionMs = XbmcThreads::SystemClockMillis(); + InstallHooks(); { @@ -123,7 +128,8 @@ void CGUIConfigurationWizard::Process(void) break; } - m_currentButton = nullptr; + // Finished mapping + InitializeState(); } if (ButtonMapCallback()) @@ -134,7 +140,9 @@ void CGUIConfigurationWizard::Process(void) CLog::Log(LOGDEBUG, "Configuration wizard ended"); } -bool CGUIConfigurationWizard::MapPrimitive(JOYSTICK::IButtonMap* buttonMap, const JOYSTICK::CDriverPrimitive& primitive) +bool CGUIConfigurationWizard::MapPrimitive(JOYSTICK::IButtonMap* buttonMap, + JOYSTICK::IActionMap* actionMap, + const JOYSTICK::CDriverPrimitive& primitive) { using namespace JOYSTICK; @@ -151,6 +159,10 @@ bool CGUIConfigurationWizard::MapPrimitive(JOYSTICK::IButtonMap* buttonMap, cons // Primitive has already been mapped this round, ignore it bHandled = true; } + else if (buttonMap->IsIgnored(primitive)) + { + bHandled = true; + } else { // Get the current state of the thread @@ -165,6 +177,10 @@ bool CGUIConfigurationWizard::MapPrimitive(JOYSTICK::IButtonMap* buttonMap, cons if (currentButton) { const CControllerFeature& feature = currentButton->Feature(); + + CLog::Log(LOGDEBUG, "%s: mapping feature \"%s\" for device %s", + m_strControllerId.c_str(), feature.Name().c_str(), buttonMap->DeviceName().c_str()); + switch (feature.Type()) { case FEATURE_TYPE::SCALAR: @@ -186,6 +202,17 @@ bool CGUIConfigurationWizard::MapPrimitive(JOYSTICK::IButtonMap* buttonMap, cons if (bHandled) { m_history.insert(primitive); + + // Detect button skipping + unsigned int elapsed = XbmcThreads::SystemClockMillis() - m_lastMappingActionMs; + if (elapsed <= SKIPPING_DETECTION_MS) + { + CLog::Log(LOGDEBUG, "%s: Possible skip detected after %ums", m_strControllerId.c_str(), elapsed); + if (m_callback) + m_callback->OnSkipDetected(); + } + m_lastMappingActionMs = XbmcThreads::SystemClockMillis(); + m_inputEvent.Set(); } } diff --git a/xbmc/games/controllers/windows/GUIConfigurationWizard.h b/xbmc/games/controllers/windows/GUIConfigurationWizard.h index b615ce190f..bc463762a2 100644 --- a/xbmc/games/controllers/windows/GUIConfigurationWizard.h +++ b/xbmc/games/controllers/windows/GUIConfigurationWizard.h @@ -46,13 +46,16 @@ namespace GAME virtual ~CGUIConfigurationWizard(void) { } // implementation of IConfigurationWizard - virtual void Run(const std::string& strControllerId, const std::vector<IFeatureButton*>& buttons) override; + virtual void Run(const std::string& strControllerId, const std::vector<IFeatureButton*>& buttons, IConfigurationWizardCallback* callback) override; virtual void OnUnfocus(IFeatureButton* button) override; virtual bool Abort(bool bWait = true) override; // implementation of IButtonMapper virtual std::string ControllerID(void) const override { return m_strControllerId; } - virtual bool MapPrimitive(JOYSTICK::IButtonMap* buttonMap, const JOYSTICK::CDriverPrimitive& primitive) override; + virtual bool NeedsCooldown(void) const override { return true; } + virtual bool MapPrimitive(JOYSTICK::IButtonMap* buttonMap, + JOYSTICK::IActionMap* actionMap, + const JOYSTICK::CDriverPrimitive& primitive) override; // implementation of IKeyboardHandler virtual bool OnKeyPress(const CKey& key) override; @@ -74,11 +77,13 @@ namespace GAME // Run() parameters std::string m_strControllerId; std::vector<IFeatureButton*> m_buttons; + IConfigurationWizardCallback* m_callback; // State variables and mutex IFeatureButton* m_currentButton; JOYSTICK::ANALOG_STICK_DIRECTION m_currentDirection; std::set<JOYSTICK::CDriverPrimitive> m_history; // History to avoid repeated features + unsigned int m_lastMappingActionMs; // The last mapping action, or 0 if not currently mapping CCriticalSection m_stateMutex; // Synchronization event diff --git a/xbmc/games/controllers/windows/GUIControllerDefines.h b/xbmc/games/controllers/windows/GUIControllerDefines.h index 54e1e28ac6..232dd91f16 100644 --- a/xbmc/games/controllers/windows/GUIControllerDefines.h +++ b/xbmc/games/controllers/windows/GUIControllerDefines.h @@ -36,6 +36,7 @@ #define CONTROL_CLOSE_BUTTON 18 #define CONTROL_RESET_BUTTON 19 #define CONTROL_GET_MORE 20 +#define CONTROL_FIX_SKIPPING 21 #define CONTROL_GAME_CONTROLLER 31 #define MAX_CONTROLLER_COUNT 100 // large enough diff --git a/xbmc/games/controllers/windows/GUIControllerList.cpp b/xbmc/games/controllers/windows/GUIControllerList.cpp index 655c3ebc74..650d004082 100644 --- a/xbmc/games/controllers/windows/GUIControllerList.cpp +++ b/xbmc/games/controllers/windows/GUIControllerList.cpp @@ -30,6 +30,7 @@ #include "addons/AddonManager.h" #include "dialogs/GUIDialogYesNo.h" #include "games/controllers/Controller.h" +#include "games/controllers/dialogs/GUIDialogButtonCapture.h" #include "games/controllers/guicontrols/GUIControllerButton.h" #include "games/controllers/guicontrols/GUIGameController.h" #include "guilib/GUIButtonControl.h" @@ -142,6 +143,17 @@ void CGUIControllerList::ResetController(void) } } +void CGUIControllerList::ShowButtonCaptureDialog(void) +{ + if (0 <= m_focusedController && m_focusedController < (int)m_controllers.size()) + { + const std::string strControllerId = m_controllers[m_focusedController]->ID(); + + CGUIDialogButtonCapture dialog(strControllerId); + dialog.Show(); + } +} + void CGUIControllerList::OnEvent(const ADDON::AddonEvent& event) { if (typeid(event) == typeid(ADDON::AddonEvents::InstalledChanged)) diff --git a/xbmc/games/controllers/windows/GUIControllerList.h b/xbmc/games/controllers/windows/GUIControllerList.h index fe31d44b1c..4059dccf4b 100644 --- a/xbmc/games/controllers/windows/GUIControllerList.h +++ b/xbmc/games/controllers/windows/GUIControllerList.h @@ -49,6 +49,7 @@ namespace GAME virtual void OnSelect(unsigned int controllerIndex) override; virtual int GetFocusedController() const override { return m_focusedController; } virtual void ResetController(void) override; + virtual void ShowButtonCaptureDialog() override; private: bool RefreshControllers(void); diff --git a/xbmc/games/controllers/windows/GUIControllerWindow.cpp b/xbmc/games/controllers/windows/GUIControllerWindow.cpp index e5c0973f63..37c1717b6a 100644 --- a/xbmc/games/controllers/windows/GUIControllerWindow.cpp +++ b/xbmc/games/controllers/windows/GUIControllerWindow.cpp @@ -125,6 +125,10 @@ bool CGUIControllerWindow::OnMessage(CGUIMessage& message) ShowHelp(); return true; } + else if (controlId == CONTROL_FIX_SKIPPING) + { + ShowButtonCaptureDialog(); + } else if (CONTROL_CONTROLLER_BUTTONS_START <= controlId && controlId < CONTROL_CONTROLLER_BUTTONS_END) { OnControllerSelected(controlId - CONTROL_CONTROLLER_BUTTONS_START); @@ -322,3 +326,9 @@ void CGUIControllerWindow::ShowHelp(void) // <help text> CGUIDialogOK::ShowAndGetInput(CVariant{10043}, CVariant{35055}); } + +void CGUIControllerWindow::ShowButtonCaptureDialog(void) +{ + if (m_controllerList) + m_controllerList->ShowButtonCaptureDialog(); +} diff --git a/xbmc/games/controllers/windows/GUIControllerWindow.h b/xbmc/games/controllers/windows/GUIControllerWindow.h index 00a1c1eca1..22efa0cbb2 100644 --- a/xbmc/games/controllers/windows/GUIControllerWindow.h +++ b/xbmc/games/controllers/windows/GUIControllerWindow.h @@ -55,6 +55,7 @@ namespace GAME void GetMoreControllers(void); void ResetController(void); void ShowHelp(void); + void ShowButtonCaptureDialog(void); IControllerList* m_controllerList; IFeatureList* m_featureList; diff --git a/xbmc/games/controllers/windows/GUIFeatureList.cpp b/xbmc/games/controllers/windows/GUIFeatureList.cpp index 4aa114c870..0bcd937e3e 100644 --- a/xbmc/games/controllers/windows/GUIFeatureList.cpp +++ b/xbmc/games/controllers/windows/GUIFeatureList.cpp @@ -30,7 +30,9 @@ #include "guilib/GUIControlGroupList.h" #include "guilib/GUIImage.h" #include "guilib/GUILabelControl.h" +#include "guilib/GUIMessage.h" #include "guilib/GUIWindow.h" +#include "messaging/ApplicationMessenger.h" using namespace GAME; @@ -145,7 +147,12 @@ void CGUIFeatureList::OnSelect(unsigned int index) buttons.push_back(control); } - m_wizard->Run(m_controller->ID(), buttons); + m_wizard->Run(m_controller->ID(), buttons, this); +} + +void CGUIFeatureList::OnSkipDetected() +{ + //! @todo } IFeatureButton* CGUIFeatureList::GetButtonControl(unsigned int featureIndex) diff --git a/xbmc/games/controllers/windows/GUIFeatureList.h b/xbmc/games/controllers/windows/GUIFeatureList.h index e5ed44d649..bfb618a943 100644 --- a/xbmc/games/controllers/windows/GUIFeatureList.h +++ b/xbmc/games/controllers/windows/GUIFeatureList.h @@ -31,7 +31,8 @@ class CGUIWindow; namespace GAME { - class CGUIFeatureList : public IFeatureList + class CGUIFeatureList : public IFeatureList, + public IConfigurationWizardCallback { public: CGUIFeatureList(CGUIWindow* window); @@ -44,6 +45,9 @@ namespace GAME virtual void OnFocus(unsigned int index) override { } virtual void OnSelect(unsigned int index) override; + // implementation of IConfigurationWizardCallback + virtual void OnSkipDetected() override; + private: IFeatureButton* GetButtonControl(unsigned int featureIndex); diff --git a/xbmc/games/controllers/windows/IConfigurationWindow.h b/xbmc/games/controllers/windows/IConfigurationWindow.h index 5f57a5bb8f..90fef58342 100644 --- a/xbmc/games/controllers/windows/IConfigurationWindow.h +++ b/xbmc/games/controllers/windows/IConfigurationWindow.h @@ -97,6 +97,11 @@ namespace GAME * \brief Reset the focused controller */ virtual void ResetController(void) = 0; + + /*! + * \brief Show a dialog for capturing input interfering with the button mapping process + */ + virtual void ShowButtonCaptureDialog() = 0; }; /*! @@ -182,6 +187,20 @@ namespace GAME }; /*! + * \brief Callback handler passed to the button mapping wizard + */ + class IConfigurationWizardCallback + { + public: + virtual ~IConfigurationWizardCallback() = default; + + /*! + * \brief Called when a "skip" is detected, defined as two mapping commands within a short duration + */ + virtual void OnSkipDetected() = 0; + }; + + /*! * \brief A wizard to direct user input */ class IConfigurationWizard @@ -193,7 +212,7 @@ namespace GAME * \brief Start the wizard at the specified feature * \param featureIndex The index of the feature to start at */ - virtual void Run(const std::string& strControllerId, const std::vector<IFeatureButton*>& buttons) = 0; + virtual void Run(const std::string& strControllerId, const std::vector<IFeatureButton*>& buttons, IConfigurationWizardCallback* callback) = 0; /*! * \brief Callback for feature losing focus diff --git a/xbmc/input/joysticks/CMakeLists.txt b/xbmc/input/joysticks/CMakeLists.txt index 6a11726d7d..ac5450df91 100644 --- a/xbmc/input/joysticks/CMakeLists.txt +++ b/xbmc/input/joysticks/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES DeadzoneFilter.cpp set(HEADERS DeadzoneFilter.h DefaultJoystick.h DriverPrimitive.h + IActionMap.h IButtonMap.h IButtonMapCallback.h IButtonMapper.h diff --git a/xbmc/input/joysticks/DefaultJoystick.cpp b/xbmc/input/joysticks/DefaultJoystick.cpp index 56634190e5..cd2cfc19f8 100644 --- a/xbmc/input/joysticks/DefaultJoystick.cpp +++ b/xbmc/input/joysticks/DefaultJoystick.cpp @@ -129,6 +129,16 @@ bool CDefaultJoystick::OnAccelerometerMotion(const FeatureName& feature, float x return false; //! @todo implement } +int CDefaultJoystick::GetActionID(const FeatureName& feature) +{ + const unsigned int keyId = GetKeyID(feature); + + if (keyId > 0) + return m_handler->GetActionID(keyId); + + return ACTION_NONE; +} + bool CDefaultJoystick::ActivateDirection(const FeatureName& feature, float magnitude, ANALOG_STICK_DIRECTION dir, unsigned int motionTimeMs) { // Calculate the button key ID and input type for the analog stick's direction diff --git a/xbmc/input/joysticks/DefaultJoystick.h b/xbmc/input/joysticks/DefaultJoystick.h index 2a8a48ae37..b103e7ddbb 100644 --- a/xbmc/input/joysticks/DefaultJoystick.h +++ b/xbmc/input/joysticks/DefaultJoystick.h @@ -19,6 +19,7 @@ */ #pragma once +#include "IActionMap.h" #include "IInputHandler.h" #include "JoystickTypes.h" #include "RumbleGenerator.h" @@ -40,7 +41,8 @@ namespace JOYSTICK * * \sa IInputHandler */ - class CDefaultJoystick : public JOYSTICK::IInputHandler + class CDefaultJoystick : public JOYSTICK::IInputHandler, + public IActionMap { public: CDefaultJoystick(void); @@ -58,6 +60,9 @@ namespace JOYSTICK virtual bool OnAnalogStickMotion(const FeatureName& feature, float x, float y, unsigned int motionTimeMs = 0) override; virtual bool OnAccelerometerMotion(const FeatureName& feature, float x, float y, float z) override; + // implementation of IActionMap + virtual int GetActionID(const FeatureName& feature) override; + // Forward rumble commands to rumble generator void NotifyUser(void) { m_rumbleGenerator.NotifyUser(InputReceiver()); } bool TestRumble(void) { return m_rumbleGenerator.DoTest(InputReceiver()); } diff --git a/xbmc/input/joysticks/IActionMap.h b/xbmc/input/joysticks/IActionMap.h new file mode 100644 index 0000000000..c70ef219dc --- /dev/null +++ b/xbmc/input/joysticks/IActionMap.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 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 "input/Key.h" + +namespace JOYSTICK +{ + class CDriverPrimitive; + + /*! + * \brief Interface for translating features to action IDs + */ + class IActionMap + { + public: + virtual ~IActionMap() = default; + + /*! + * \brief Get the action ID mapped to the specified feature + * + * \param feature The feature to look up + * + * \return The action ID from Key.h, or ACTION_NONE if no action is mapped + * to the specified key + */ + virtual int GetActionID(const FeatureName& feature) = 0; + }; +} diff --git a/xbmc/input/joysticks/IButtonMap.h b/xbmc/input/joysticks/IButtonMap.h index 0d365b66ab..2da7705269 100644 --- a/xbmc/input/joysticks/IButtonMap.h +++ b/xbmc/input/joysticks/IButtonMap.h @@ -23,6 +23,7 @@ #include "JoystickTypes.h" #include <string> +#include <vector> namespace JOYSTICK { @@ -48,6 +49,13 @@ namespace JOYSTICK virtual std::string ControllerID(void) const = 0; /*! + * \brief The name of the peripheral associated with this button map + * + * \return The peripheral's name + */ + virtual std::string DeviceName(void) const = 0; + + /*! * \brief Load the button map into memory * * \return True if button map is ready to start translating buttons, false otherwise @@ -197,8 +205,34 @@ namespace JOYSTICK ) = 0; /*! + * \brief Set a list of driver primitives to be ignored + * + * This is necessary to prevent features from interfering with the button + * mapping process. This includes accelerometers, as well as PS4 triggers + * which send both a button press and an analog value. + * + * \param primitives The driver primitives to be ignored + */ + virtual void SetIgnoredPrimitives(const std::vector<CDriverPrimitive>& primitives) = 0; + + /*! + * \brief Check if a primitive is in the list of primitives to be ignored + * + * \param primitive The primitive to check + * + * \return True if the primitive should be ignored in the mapping process + */ + virtual bool IsIgnored(const CDriverPrimitive& primitive) = 0; + + /*! * \brief Save the button map */ virtual void SaveButtonMap() = 0; + + /*! + * \brief Revert changes to the button map since the last time it was loaded + * or commited to disk + */ + virtual void RevertButtonMap() = 0; }; } diff --git a/xbmc/input/joysticks/IButtonMapCallback.h b/xbmc/input/joysticks/IButtonMapCallback.h index 1ad2658605..4d22f91ba4 100644 --- a/xbmc/input/joysticks/IButtonMapCallback.h +++ b/xbmc/input/joysticks/IButtonMapCallback.h @@ -1,37 +1,55 @@ /* -* Copyright (C) 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/>. -* -*/ + * Copyright (C) 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 namespace JOYSTICK { /*! - * \brief Interface for handling button maps - */ + * \brief Interface for handling button maps + */ class IButtonMapCallback { public: virtual ~IButtonMapCallback() = default; /*! - * \brief Save the button map - */ + * \brief Save the button map + */ virtual void SaveButtonMap() = 0; + + /*! + * \brief Clear the list of ignored driver primitives + * + * Called if the user begins capturing primitives to be ignored, and + * no primitives are captured before the dialog is accepted by the user. + * + * In this case, the button mapper won't have been given access to the + * button map, so a callback is needed to indicate that no primitives were + * captured and the user accepted this. + */ + virtual void ResetIgnoredPrimitives() = 0; + + /*! + * \brief Revert changes to the button map since the last time it was loaded + * or commited to disk + */ + virtual void RevertButtonMap() = 0; }; } diff --git a/xbmc/input/joysticks/IButtonMapper.h b/xbmc/input/joysticks/IButtonMapper.h index 43e89b6b81..0b7285be8a 100644 --- a/xbmc/input/joysticks/IButtonMapper.h +++ b/xbmc/input/joysticks/IButtonMapper.h @@ -24,6 +24,7 @@ namespace JOYSTICK { class CDriverPrimitive; + class IActionMap; class IButtonMap; class IButtonMapCallback; @@ -50,14 +51,24 @@ namespace JOYSTICK virtual std::string ControllerID(void) const = 0; /*! + * \brief Return true if the button mapper wants a cooldown between button + * mapping commands + * + * \return True to only send button mapping commands that occur after a small + * timeout from the previous command. + */ + virtual bool NeedsCooldown(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 + * \param actionMap An interface capable of translating driver primitives to Kodi actions + * \param primitive The driver primitive * - * \return True if action was mapped to a feature + * \return True if driver primitive was mapped to a feature */ - virtual bool MapPrimitive(IButtonMap* buttonMap, const CDriverPrimitive& primitive) = 0; + virtual bool MapPrimitive(IButtonMap* buttonMap, IActionMap* actionMap, const CDriverPrimitive& primitive) = 0; // Button map callback interface void SetButtonMapCallback(IButtonMapCallback* callback) { m_callback = callback; } diff --git a/xbmc/input/joysticks/IDriverReceiver.h b/xbmc/input/joysticks/IDriverReceiver.h index c1a2a2083f..1a8cccb142 100644 --- a/xbmc/input/joysticks/IDriverReceiver.h +++ b/xbmc/input/joysticks/IDriverReceiver.h @@ -1,42 +1,42 @@ /* -* Copyright (C) 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/>. -* -*/ + * Copyright (C) 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 namespace JOYSTICK { /*! - * \brief Interface for sending input events to joystick drivers - */ + * \brief Interface for sending input events to joystick drivers + */ class IDriverReceiver { public: virtual ~IDriverReceiver(void) { } /*! - * \brief Set the value of a rumble motor - * - * \param motorIndex The driver index of the motor to rumble - * \param magnitude The motor's new magnitude of vibration in the closed interval [0, 1] - * - * \return True if the event was handled otherwise false - */ + * \brief Set the value of a rumble motor + * + * \param motorIndex The driver index of the motor to rumble + * \param magnitude The motor's new magnitude of vibration in the closed interval [0, 1] + * + * \return True if the event was handled otherwise false + */ virtual bool SetMotorState(unsigned int motorIndex, float magnitude) = 0; }; } diff --git a/xbmc/input/joysticks/IInputHandler.h b/xbmc/input/joysticks/IInputHandler.h index ae7f571422..6780609c54 100644 --- a/xbmc/input/joysticks/IInputHandler.h +++ b/xbmc/input/joysticks/IInputHandler.h @@ -77,14 +77,14 @@ namespace JOYSTICK virtual bool OnButtonPress(const FeatureName& feature, bool bPressed) = 0; /*! - * \brief A digital button has been pressed for more than one event frame - * - * \param feature The feature being held - * \param holdTimeMs The time elapsed since the initial press (ms) - * - * If OnButtonPress() returns true for the initial press, then this callback - * is invoked on subsequent frames until the button is released. - */ + * \brief A digital button has been pressed for more than one event frame + * + * \param feature The feature being held + * \param holdTimeMs The time elapsed since the initial press (ms) + * + * If OnButtonPress() returns true for the initial press, then this callback + * is invoked on subsequent frames until the button is released. + */ virtual void OnButtonHold(const FeatureName& feature, unsigned int holdTimeMs) = 0; /*! diff --git a/xbmc/input/joysticks/IKeymapHandler.h b/xbmc/input/joysticks/IKeymapHandler.h index 6f25b2ba56..562aa3d498 100644 --- a/xbmc/input/joysticks/IKeymapHandler.h +++ b/xbmc/input/joysticks/IKeymapHandler.h @@ -45,6 +45,16 @@ namespace JOYSTICK virtual INPUT_TYPE GetInputType(unsigned int keyId) const = 0; /*! + * \brief Get the action ID mapped to the specified key ID + * + * \param keyId The key ID from Key.h + * + * \return The action ID, or ACTION_NONE if no action is mapped to the + * specified key + */ + virtual int GetActionID(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 diff --git a/xbmc/input/joysticks/KeymapHandler.cpp b/xbmc/input/joysticks/KeymapHandler.cpp index c1bedbf01e..859bfcd467 100644 --- a/xbmc/input/joysticks/KeymapHandler.cpp +++ b/xbmc/input/joysticks/KeymapHandler.cpp @@ -63,6 +63,16 @@ INPUT_TYPE CKeymapHandler::GetInputType(unsigned int keyId) const return INPUT_TYPE::UNKNOWN; } +int CKeymapHandler::GetActionID(unsigned int keyId) const +{ + CAction action(ACTION_NONE); + + if (keyId != 0) + action = CButtonTranslator::GetInstance().GetAction(g_windowManager.GetActiveWindowID(), CKey(keyId)); + + return action.GetID(); +} + void CKeymapHandler::OnDigitalKey(unsigned int keyId, bool bPressed, unsigned int holdTimeMs /* = 0 */) { if (keyId != 0) diff --git a/xbmc/input/joysticks/KeymapHandler.h b/xbmc/input/joysticks/KeymapHandler.h index b5f2034ac1..ca27bccf96 100644 --- a/xbmc/input/joysticks/KeymapHandler.h +++ b/xbmc/input/joysticks/KeymapHandler.h @@ -34,6 +34,7 @@ namespace JOYSTICK // implementation of IKeymapHandler virtual INPUT_TYPE GetInputType(unsigned int keyId) const override; + virtual int GetActionID(unsigned int keyId) const override; virtual void OnDigitalKey(unsigned int keyId, bool bPressed, unsigned int holdTimeMs = 0) override; virtual void OnAnalogKey(unsigned int keyId, float magnitude) override; diff --git a/xbmc/input/joysticks/dialogs/GUIDialogNewJoystick.cpp b/xbmc/input/joysticks/dialogs/GUIDialogNewJoystick.cpp index 849b99fdae..1817d02917 100644 --- a/xbmc/input/joysticks/dialogs/GUIDialogNewJoystick.cpp +++ b/xbmc/input/joysticks/dialogs/GUIDialogNewJoystick.cpp @@ -39,7 +39,7 @@ void CGUIDialogNewJoystick::ShowAsync() bShow = false; else if (!CSettings::GetInstance().GetBool(CSettings::SETTING_INPUT_ASKNEWCONTROLLERS)) bShow = false; - else if (g_windowManager.GetActiveWindow() == WINDOW_DIALOG_GAME_CONTROLLERS) + else if (g_windowManager.IsWindowActive(WINDOW_DIALOG_GAME_CONTROLLERS, false)) bShow = false; if (bShow) diff --git a/xbmc/input/joysticks/generic/ButtonMapping.cpp b/xbmc/input/joysticks/generic/ButtonMapping.cpp index 99ed5a5c32..cd73dc61be 100644 --- a/xbmc/input/joysticks/generic/ButtonMapping.cpp +++ b/xbmc/input/joysticks/generic/ButtonMapping.cpp @@ -37,9 +37,10 @@ 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 -CButtonMapping::CButtonMapping(IButtonMapper* buttonMapper, IButtonMap* buttonMap) +CButtonMapping::CButtonMapping(IButtonMapper* buttonMapper, IButtonMap* buttonMap, IActionMap* actionMap) : m_buttonMapper(buttonMapper), m_buttonMap(buttonMap), + m_actionMap(actionMap), m_lastAction(0) { assert(m_buttonMapper != NULL); @@ -53,8 +54,7 @@ bool CButtonMapping::OnButtonMotion(unsigned int buttonIndex, bool bPressed) CDriverPrimitive buttonPrimitive(PRIMITIVE_TYPE::BUTTON, buttonIndex); if (buttonPrimitive.IsValid()) { - MapPrimitive(buttonPrimitive); - return true; + return MapPrimitive(buttonPrimitive); } } @@ -118,22 +118,48 @@ void CButtonMapping::SaveButtonMap() m_buttonMap->SaveButtonMap(); } -void CButtonMapping::MapPrimitive(const CDriverPrimitive& primitive) +void CButtonMapping::ResetIgnoredPrimitives() { + std::vector<CDriverPrimitive> empty; + m_buttonMap->SetIgnoredPrimitives(empty); +} + +void CButtonMapping::RevertButtonMap() +{ + m_buttonMap->RevertButtonMap(); +} + +bool CButtonMapping::MapPrimitive(const CDriverPrimitive& primitive) +{ + bool bHandled = false; + const unsigned int now = SystemClockMillis(); - bool bTimeoutElapsed = (now >= m_lastAction + MAPPING_COOLDOWN_MS); + bool bTimeoutElapsed = true; + + if (m_buttonMapper->NeedsCooldown()) + bTimeoutElapsed = (now >= m_lastAction + MAPPING_COOLDOWN_MS); + if (bTimeoutElapsed) { - m_lastAction = SystemClockMillis(); - m_buttonMapper->MapPrimitive(m_buttonMap, primitive); + bHandled = m_buttonMapper->MapPrimitive(m_buttonMap, m_actionMap, primitive); + + if (bHandled) + m_lastAction = SystemClockMillis(); + } + else if (m_buttonMap->IsIgnored(primitive)) + { + bHandled = true; } 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()); + bHandled = true; } + + return bHandled; } void CButtonMapping::Activate(const CDriverPrimitive& semiaxis) diff --git a/xbmc/input/joysticks/generic/ButtonMapping.h b/xbmc/input/joysticks/generic/ButtonMapping.h index 9d91abbe3a..d81cec7335 100644 --- a/xbmc/input/joysticks/generic/ButtonMapping.h +++ b/xbmc/input/joysticks/generic/ButtonMapping.h @@ -27,6 +27,7 @@ namespace JOYSTICK { + class IActionMap; class IButtonMap; class IButtonMapper; @@ -49,7 +50,7 @@ namespace JOYSTICK * \param buttonMapper Carries out button-mapping commands using <buttonMap> * \param buttonMap The button map given to <buttonMapper> on each command */ - CButtonMapping(IButtonMapper* buttonMapper, IButtonMap* buttonMap); + CButtonMapping(IButtonMapper* buttonMapper, IButtonMap* buttonMap, IActionMap* actionMap); virtual ~CButtonMapping(void) { } @@ -61,9 +62,11 @@ namespace JOYSTICK // implementation of IButtonMapCallback virtual void SaveButtonMap() override; + virtual void ResetIgnoredPrimitives() override; + virtual void RevertButtonMap() override; private: - void MapPrimitive(const CDriverPrimitive& primitive); + bool MapPrimitive(const CDriverPrimitive& primitive); void Activate(const CDriverPrimitive& semiAxis); void Deactivate(const CDriverPrimitive& semiAxis); @@ -71,6 +74,7 @@ namespace JOYSTICK IButtonMapper* const m_buttonMapper; IButtonMap* const m_buttonMap; + IActionMap* const m_actionMap; struct ActivatedAxis { diff --git a/xbmc/peripherals/addons/AddonButtonMap.cpp b/xbmc/peripherals/addons/AddonButtonMap.cpp index 6fff54ce33..5ff375f4bc 100644 --- a/xbmc/peripherals/addons/AddonButtonMap.cpp +++ b/xbmc/peripherals/addons/AddonButtonMap.cpp @@ -24,6 +24,7 @@ #include "peripherals/devices/Peripheral.h" #include "utils/log.h" +#include <algorithm> #include <assert.h> #include <vector> @@ -47,14 +48,23 @@ CAddonButtonMap::~CAddonButtonMap(void) addon->UnregisterButtonMap(this); } +std::string CAddonButtonMap::DeviceName(void) const +{ + return m_device->DeviceName(); +} + bool CAddonButtonMap::Load(void) { FeatureMap features; DriverMap driverMap; + PrimitiveVector ignoredPrimitives; bool bSuccess = false; if (auto addon = m_addon.lock()) - bSuccess = addon->GetFeatures(m_device, m_strControllerId, features); + { + bSuccess |= addon->GetFeatures(m_device, m_strControllerId, features); + bSuccess |= addon->GetIgnoredPrimitives(m_device, ignoredPrimitives); + } // GetFeatures() was changed to always return false if no features were // retrieved. Check here, just in case its contract is changed or violated in @@ -71,6 +81,7 @@ bool CAddonButtonMap::Load(void) CSingleLock lock(m_mutex); m_features = std::move(features); m_driverMap = std::move(driverMap); + m_ignoredPrimitives = std::move(CPeripheralAddonTranslator::TranslatePrimitives(ignoredPrimitives)); } return true; @@ -197,6 +208,8 @@ void CAddonButtonMap::AddAnalogStick(const FeatureName& feature, if (auto addon = m_addon.lock()) addon->MapFeature(m_device, m_strControllerId, analogStick); + // Because each direction is mapped individually, we need to refresh the + // feature each time a new direction is mapped. if (bModified) Load(); } @@ -242,8 +255,17 @@ void CAddonButtonMap::AddAccelerometer(const FeatureName& feature, if (auto addon = m_addon.lock()) addon->MapFeature(m_device, m_strControllerId, accelerometer); +} - Load(); +void CAddonButtonMap::SetIgnoredPrimitives(const std::vector<JOYSTICK::CDriverPrimitive>& primitives) +{ + if (auto addon = m_addon.lock()) + addon->SetIgnoredPrimitives(m_device, CPeripheralAddonTranslator::TranslatePrimitives(primitives)); +} + +bool CAddonButtonMap::IsIgnored(const JOYSTICK::CDriverPrimitive& primitive) +{ + return std::find(m_ignoredPrimitives.begin(), m_ignoredPrimitives.end(), primitive) != m_ignoredPrimitives.end(); } void CAddonButtonMap::SaveButtonMap() @@ -252,6 +274,12 @@ void CAddonButtonMap::SaveButtonMap() addon->SaveButtonMap(m_device); } +void CAddonButtonMap::RevertButtonMap() +{ + if (auto addon = m_addon.lock()) + addon->RevertButtonMap(m_device); +} + CAddonButtonMap::DriverMap CAddonButtonMap::CreateLookupTable(const FeatureMap& features) { using namespace JOYSTICK; diff --git a/xbmc/peripherals/addons/AddonButtonMap.h b/xbmc/peripherals/addons/AddonButtonMap.h index 1f2965ab4e..b8bec15db7 100644 --- a/xbmc/peripherals/addons/AddonButtonMap.h +++ b/xbmc/peripherals/addons/AddonButtonMap.h @@ -41,6 +41,8 @@ namespace PERIPHERALS // Implementation of IButtonMap virtual std::string ControllerID(void) const override { return m_strControllerId; } + virtual std::string DeviceName(void) const override; + virtual bool Load(void) override; virtual void Reset(void) override; @@ -92,10 +94,17 @@ namespace PERIPHERALS const JOYSTICK::CDriverPrimitive& positiveZ ) override; + virtual void SetIgnoredPrimitives(const std::vector<JOYSTICK::CDriverPrimitive>& primitives) override; + + virtual bool IsIgnored(const JOYSTICK::CDriverPrimitive& primitive) override; + virtual void SaveButtonMap() override; + virtual void RevertButtonMap() override; + private: typedef std::map<JOYSTICK::CDriverPrimitive, JOYSTICK::FeatureName> DriverMap; + typedef std::vector<JOYSTICK::CDriverPrimitive> JoystickPrimitiveVector; // Utility functions static DriverMap CreateLookupTable(const FeatureMap& features); @@ -107,6 +116,7 @@ namespace PERIPHERALS const std::string m_strControllerId; FeatureMap m_features; DriverMap m_driverMap; + JoystickPrimitiveVector m_ignoredPrimitives; CCriticalSection m_mutex; }; } diff --git a/xbmc/peripherals/addons/AddonButtonMapping.cpp b/xbmc/peripherals/addons/AddonButtonMapping.cpp index b5f25e167a..006572259b 100644 --- a/xbmc/peripherals/addons/AddonButtonMapping.cpp +++ b/xbmc/peripherals/addons/AddonButtonMapping.cpp @@ -40,7 +40,7 @@ CAddonButtonMapping::CAddonButtonMapping(CPeripheral* peripheral, IButtonMapper* m_buttonMap.reset(new CAddonButtonMap(peripheral, addon, mapper->ControllerID())); if (m_buttonMap->Load()) { - m_driverHandler.reset(new CButtonMapping(mapper, m_buttonMap.get())); + m_buttonMapping.reset(new CButtonMapping(mapper, m_buttonMap.get(), peripheral->GetActionMap())); // Allow the mapper to save our button map mapper->SetButtonMapCallback(this); @@ -52,42 +52,54 @@ CAddonButtonMapping::CAddonButtonMapping(CPeripheral* peripheral, IButtonMapper* CAddonButtonMapping::~CAddonButtonMapping(void) { - m_driverHandler.reset(); + m_buttonMapping.reset(); m_buttonMap.reset(); } bool CAddonButtonMapping::OnButtonMotion(unsigned int buttonIndex, bool bPressed) { - if (m_driverHandler) - return m_driverHandler->OnButtonMotion(buttonIndex, bPressed); + if (m_buttonMapping) + return m_buttonMapping->OnButtonMotion(buttonIndex, bPressed); return false; } bool CAddonButtonMapping::OnHatMotion(unsigned int hatIndex, HAT_STATE state) { - if (m_driverHandler) - return m_driverHandler->OnHatMotion(hatIndex, state); + if (m_buttonMapping) + return m_buttonMapping->OnHatMotion(hatIndex, state); return false; } bool CAddonButtonMapping::OnAxisMotion(unsigned int axisIndex, float position) { - if (m_driverHandler) - return m_driverHandler->OnAxisMotion(axisIndex, position); + if (m_buttonMapping) + return m_buttonMapping->OnAxisMotion(axisIndex, position); return false; } void CAddonButtonMapping::ProcessAxisMotions(void) { - if (m_driverHandler) - m_driverHandler->ProcessAxisMotions(); + if (m_buttonMapping) + m_buttonMapping->ProcessAxisMotions(); } void CAddonButtonMapping::SaveButtonMap() { - if (m_buttonMap) - m_buttonMap->SaveButtonMap(); + if (m_buttonMapping) + m_buttonMapping->SaveButtonMap(); +} + +void CAddonButtonMapping::ResetIgnoredPrimitives() +{ + if (m_buttonMapping) + m_buttonMapping->ResetIgnoredPrimitives(); +} + +void CAddonButtonMapping::RevertButtonMap() +{ + if (m_buttonMapping) + m_buttonMapping->RevertButtonMap(); } diff --git a/xbmc/peripherals/addons/AddonButtonMapping.h b/xbmc/peripherals/addons/AddonButtonMapping.h index 83f69cf02f..5b16e338e9 100644 --- a/xbmc/peripherals/addons/AddonButtonMapping.h +++ b/xbmc/peripherals/addons/AddonButtonMapping.h @@ -26,6 +26,7 @@ namespace JOYSTICK { + class CButtonMapping; class IButtonMap; class IButtonMapper; } @@ -50,9 +51,11 @@ namespace PERIPHERALS // implementation of IButtonMapCallback virtual void SaveButtonMap() override; + virtual void ResetIgnoredPrimitives() override; + virtual void RevertButtonMap() override; private: - std::unique_ptr<JOYSTICK::IDriverHandler> m_driverHandler; + std::unique_ptr<JOYSTICK::CButtonMapping> m_buttonMapping; std::unique_ptr<JOYSTICK::IButtonMap> m_buttonMap; }; } diff --git a/xbmc/peripherals/addons/PeripheralAddon.cpp b/xbmc/peripherals/addons/PeripheralAddon.cpp index 1fce113e9b..5fe442a263 100644 --- a/xbmc/peripherals/addons/PeripheralAddon.cpp +++ b/xbmc/peripherals/addons/PeripheralAddon.cpp @@ -585,12 +585,85 @@ bool CPeripheralAddon::MapFeature(const CPeripheral* device, 1, &addonFeature), "MapFeatures()"); } catch (std::exception &e) { LogException(e, "MapFeatures()"); return false; } + return retVal == PERIPHERAL_NO_ERROR; +} + +bool CPeripheralAddon::GetIgnoredPrimitives(const CPeripheral* device, PrimitiveVector& primitives) +{ + if (!m_bProvidesButtonMaps) + return false; + + PERIPHERAL_ERROR retVal; + + ADDON::Joystick joystickInfo; + GetJoystickInfo(device, joystickInfo); + + JOYSTICK_INFO joystickStruct; + joystickInfo.ToStruct(joystickStruct); + + unsigned int primitiveCount = 0; + JOYSTICK_DRIVER_PRIMITIVE* pPrimitives = nullptr; + + try + { + LogError(retVal = m_pStruct->GetIgnoredPrimitives(&joystickStruct, &primitiveCount, + &pPrimitives), "GetIgnoredPrimitives()"); + } + catch (std::exception &e) + { + LogException(e, "GetIgnoredPrimitives()"); + return false; + } + if (retVal == PERIPHERAL_NO_ERROR) { - // Notify observing button maps - RefreshButtonMaps(device->DeviceName()); + for (unsigned int i = 0; i < primitiveCount; i++) + primitives.emplace_back(pPrimitives[i]); + + try + { + m_pStruct->FreePrimitives(primitiveCount, pPrimitives); + } + catch (std::exception &e) + { + LogException(e, "FreePrimitives()"); + } + + return true; + } + + return false; + +} + +bool CPeripheralAddon::SetIgnoredPrimitives(const CPeripheral* device, const PrimitiveVector& primitives) +{ + if (!m_bProvidesButtonMaps) + return false; + + PERIPHERAL_ERROR retVal; + + ADDON::Joystick joystickInfo; + GetJoystickInfo(device, joystickInfo); + + JOYSTICK_INFO joystickStruct; + joystickInfo.ToStruct(joystickStruct); + + JOYSTICK_DRIVER_PRIMITIVE* addonPrimitives = nullptr; + ADDON::DriverPrimitives::ToStructs(primitives, &addonPrimitives); + + try + { + LogError(retVal = m_pStruct->SetIgnoredPrimitives(&joystickStruct, + primitives.size(), addonPrimitives), "SetIgnoredPrimitives()"); + } + catch (std::exception &e) + { + LogException(e, "SetIgnoredPrimitives()"); return false; } + ADDON::DriverPrimitives::FreeStructs(primitives.size(), addonPrimitives); + return retVal == PERIPHERAL_NO_ERROR; } @@ -607,6 +680,24 @@ void CPeripheralAddon::SaveButtonMap(const CPeripheral* device) try { m_pStruct->SaveButtonMap(&joystickStruct); } catch (std::exception &e) { LogException(e, "SaveMap()"); return; } + + // Notify observing button maps + RefreshButtonMaps(device->DeviceName()); +} + +void CPeripheralAddon::RevertButtonMap(const CPeripheral* device) +{ + if (!m_bProvidesButtonMaps) + return; + + ADDON::Joystick joystickInfo; + GetJoystickInfo(device, joystickInfo); + + JOYSTICK_INFO joystickStruct; + joystickInfo.ToStruct(joystickStruct); + + try { m_pStruct->RevertButtonMap(&joystickStruct); } + catch (std::exception &e) { LogException(e, "RevertMap()"); return; } } void CPeripheralAddon::ResetButtonMap(const CPeripheral* device, const std::string& strControllerId) diff --git a/xbmc/peripherals/addons/PeripheralAddon.h b/xbmc/peripherals/addons/PeripheralAddon.h index 612b646ad6..3139402d0d 100644 --- a/xbmc/peripherals/addons/PeripheralAddon.h +++ b/xbmc/peripherals/addons/PeripheralAddon.h @@ -41,6 +41,7 @@ namespace PERIPHERALS class CPeripheral; class CPeripheralJoystick; + typedef std::vector<ADDON::DriverPrimitive> PrimitiveVector; typedef std::map<JOYSTICK::FeatureName, ADDON::JoystickFeature> FeatureMap; class CPeripheralAddon : public ADDON::CAddonDll<DllPeripheral, PeripheralAddon, PERIPHERAL_PROPERTIES> @@ -84,7 +85,10 @@ namespace PERIPHERALS bool HasButtonMaps(void) const { return m_bProvidesButtonMaps; } bool GetFeatures(const CPeripheral* device, const std::string& strControllerId, FeatureMap& features); bool MapFeature(const CPeripheral* device, const std::string& strControllerId, const ADDON::JoystickFeature& feature); + bool GetIgnoredPrimitives(const CPeripheral* device, PrimitiveVector& primitives); + bool SetIgnoredPrimitives(const CPeripheral* device, const PrimitiveVector& primitives); void SaveButtonMap(const CPeripheral* device); + void RevertButtonMap(const CPeripheral* device); void ResetButtonMap(const CPeripheral* device, const std::string& strControllerId); void PowerOffJoystick(unsigned int index); //@} diff --git a/xbmc/peripherals/addons/PeripheralAddonTranslator.cpp b/xbmc/peripherals/addons/PeripheralAddonTranslator.cpp index 2c00677f62..7d9b296bf6 100644 --- a/xbmc/peripherals/addons/PeripheralAddonTranslator.cpp +++ b/xbmc/peripherals/addons/PeripheralAddonTranslator.cpp @@ -21,6 +21,9 @@ #include "PeripheralAddonTranslator.h" #include "input/joysticks/JoystickUtils.h" +#include <algorithm> +#include <iterator> + using namespace JOYSTICK; using namespace PERIPHERALS; @@ -121,6 +124,28 @@ ADDON::DriverPrimitive CPeripheralAddonTranslator::TranslatePrimitive(const CDri return retVal; } +std::vector<JOYSTICK::CDriverPrimitive> CPeripheralAddonTranslator::TranslatePrimitives(const std::vector<ADDON::DriverPrimitive>& primitives) +{ + std::vector<JOYSTICK::CDriverPrimitive> ret; + std::transform(primitives.begin(), primitives.end(), std::back_inserter(ret), + [](const ADDON::DriverPrimitive& primitive) + { + return CPeripheralAddonTranslator::TranslatePrimitive(primitive); + }); + return ret; +} + +std::vector<ADDON::DriverPrimitive> CPeripheralAddonTranslator::TranslatePrimitives(const std::vector<JOYSTICK::CDriverPrimitive>& primitives) +{ + std::vector<ADDON::DriverPrimitive> ret; + std::transform(primitives.begin(), primitives.end(), std::back_inserter(ret), + [](const JOYSTICK::CDriverPrimitive& primitive) + { + return CPeripheralAddonTranslator::TranslatePrimitive(primitive); + }); + return ret; +} + HAT_DIRECTION CPeripheralAddonTranslator::TranslateHatDirection(JOYSTICK_DRIVER_HAT_DIRECTION dir) { switch (dir) diff --git a/xbmc/peripherals/addons/PeripheralAddonTranslator.h b/xbmc/peripherals/addons/PeripheralAddonTranslator.h index a2ccee82c3..1cdb339403 100644 --- a/xbmc/peripherals/addons/PeripheralAddonTranslator.h +++ b/xbmc/peripherals/addons/PeripheralAddonTranslator.h @@ -24,6 +24,8 @@ #include "input/joysticks/DriverPrimitive.h" #include "input/joysticks/JoystickTypes.h" +#include <vector> + namespace PERIPHERALS { class CPeripheralAddonTranslator @@ -34,6 +36,9 @@ namespace PERIPHERALS static JOYSTICK::CDriverPrimitive TranslatePrimitive(const ADDON::DriverPrimitive& primitive); static ADDON::DriverPrimitive TranslatePrimitive(const JOYSTICK::CDriverPrimitive& primitive); + static std::vector<JOYSTICK::CDriverPrimitive> TranslatePrimitives(const std::vector<ADDON::DriverPrimitive>& primitives); + static std::vector<ADDON::DriverPrimitive> TranslatePrimitives(const std::vector<JOYSTICK::CDriverPrimitive>& primitives); + static JOYSTICK::HAT_DIRECTION TranslateHatDirection(JOYSTICK_DRIVER_HAT_DIRECTION dir); static JOYSTICK_DRIVER_HAT_DIRECTION TranslateHatDirection(JOYSTICK::HAT_DIRECTION dir); diff --git a/xbmc/peripherals/devices/Peripheral.h b/xbmc/peripherals/devices/Peripheral.h index 4be500bb5f..f31edd56ec 100644 --- a/xbmc/peripherals/devices/Peripheral.h +++ b/xbmc/peripherals/devices/Peripheral.h @@ -30,6 +30,7 @@ class CSetting; namespace JOYSTICK { + class IActionMap; class IButtonMapper; class IDriverHandler; class IDriverReceiver; @@ -202,6 +203,8 @@ namespace PERIPHERALS virtual JOYSTICK::IDriverReceiver* GetDriverReceiver() { return nullptr; } + virtual JOYSTICK::IActionMap* GetActionMap() { return nullptr; } + protected: virtual void ClearSettings(void); diff --git a/xbmc/peripherals/devices/PeripheralJoystick.h b/xbmc/peripherals/devices/PeripheralJoystick.h index a6d24370ba..72e0bb2c65 100644 --- a/xbmc/peripherals/devices/PeripheralJoystick.h +++ b/xbmc/peripherals/devices/PeripheralJoystick.h @@ -57,6 +57,7 @@ namespace PERIPHERALS virtual void RegisterJoystickDriverHandler(IDriverHandler* handler, bool bPromiscuous) override; virtual void UnregisterJoystickDriverHandler(IDriverHandler* handler) override; virtual JOYSTICK::IDriverReceiver* GetDriverReceiver() override { return this; } + virtual JOYSTICK::IActionMap* GetActionMap() override { return &m_defaultInputHandler; } // implementation of IDriverHandler virtual bool OnButtonMotion(unsigned int buttonIndex, bool bPressed) override; |