From ff6e116c53b1243412d5a5614d60d82378db9473 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Thu, 11 Aug 2016 16:23:26 -0700 Subject: [PATCH 01/28] [buttonmapper] Derive joystick features from relationships between other controllers --- CMakeLists.txt | 4 +- src/api/IJoystickInterface.h | 9 - src/api/JoystickManager.cpp | 15 +- src/api/JoystickManager.h | 12 +- .../ButtonMapTranslator.cpp | 0 .../ButtonMapTranslator.h | 0 src/buttonmapper/ButtonMapTypes.h | 63 ++++++ src/buttonmapper/ButtonMapper.cpp | 131 ++++++++++++ src/buttonmapper/ButtonMapper.h | 61 ++++++ src/buttonmapper/ControllerMapper.cpp | 197 ++++++++++++++++++ src/buttonmapper/ControllerMapper.h | 51 +++++ src/storage/ButtonMap.cpp | 12 +- src/storage/ButtonMap.h | 9 +- src/storage/IDatabase.h | 28 ++- src/storage/JustABunchOfFiles.cpp | 40 ++-- src/storage/JustABunchOfFiles.h | 16 +- src/storage/StorageManager.cpp | 47 ++--- src/storage/StorageManager.h | 16 +- .../{ButtonMapTypes.h => StorageTypes.h} | 10 +- src/storage/api/DatabaseJoystickAPI.cpp | 9 +- src/storage/api/DatabaseJoystickAPI.h | 8 +- src/storage/xml/ButtonMapXml.cpp | 2 +- src/storage/xml/DatabaseXml.cpp | 4 +- src/storage/xml/DatabaseXml.h | 2 +- 24 files changed, 615 insertions(+), 131 deletions(-) rename src/{storage => buttonmapper}/ButtonMapTranslator.cpp (100%) rename src/{storage => buttonmapper}/ButtonMapTranslator.h (100%) create mode 100644 src/buttonmapper/ButtonMapTypes.h create mode 100644 src/buttonmapper/ButtonMapper.cpp create mode 100644 src/buttonmapper/ButtonMapper.h create mode 100644 src/buttonmapper/ControllerMapper.cpp create mode 100644 src/buttonmapper/ControllerMapper.h rename src/storage/{ButtonMapTypes.h => StorageTypes.h} (77%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e6697508..4aa03ddb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,9 @@ set(JOYSTICK_SOURCES src/addon.cpp src/api/JoystickManager.cpp src/api/JoystickTranslator.cpp src/api/PeripheralScanner.cpp + src/buttonmapper/ButtonMapTranslator.cpp + src/buttonmapper/ButtonMapper.cpp + src/buttonmapper/ControllerMapper.cpp src/filesystem/DirectoryCache.cpp src/filesystem/DirectoryUtils.cpp src/filesystem/Filesystem.cpp @@ -42,7 +45,6 @@ set(JOYSTICK_SOURCES src/addon.cpp src/log/LogConsole.cpp src/settings/Settings.cpp src/storage/ButtonMap.cpp - src/storage/ButtonMapTranslator.cpp src/storage/Device.cpp src/storage/JustABunchOfFiles.cpp src/storage/StorageManager.cpp diff --git a/src/api/IJoystickInterface.h b/src/api/IJoystickInterface.h index 253f69e3..786d68ed 100644 --- a/src/api/IJoystickInterface.h +++ b/src/api/IJoystickInterface.h @@ -19,7 +19,6 @@ #pragma once #include "JoystickTypes.h" -#include "storage/ButtonMapTypes.h" // for FeatureVector #include @@ -55,13 +54,5 @@ namespace JOYSTICK * \return true if the scan completed successfully, even if no results are found */ virtual bool ScanForJoysticks(JoystickVector& joysticks) = 0; - - /*! - * \brief Get the feature mapping known to the interface - * - * \param controllerId The add-on ID of a controller profile - * \param[out] features The features known to the interface (such as motors) - */ - virtual void GetFeatures(const std::string& controllerId, FeatureVector& features) { } }; } diff --git a/src/api/JoystickManager.cpp b/src/api/JoystickManager.cpp index 60a51c54..9a2f3aa5 100644 --- a/src/api/JoystickManager.cpp +++ b/src/api/JoystickManager.cpp @@ -248,16 +248,11 @@ void CJoystickManager::TriggerScan(void) m_scanner->TriggerScan(); } -void CJoystickManager::GetFeatures(const std::string& provider, const std::string& controllerId, FeatureVector& features) +const ButtonMap& CJoystickManager::GetButtonMap(const std::string& provider) { - CLockObject lock(m_interfacesMutex); + static ButtonMap empty; - for (std::vector::iterator it = m_interfaces.begin(); it != m_interfaces.end(); ++it) - { - if ((*it)->Name() == provider) - { - (*it)->GetFeatures(controllerId, features); - break; - } - } + // TODO + + return empty; } diff --git a/src/api/JoystickManager.h b/src/api/JoystickManager.h index ccec3bb9..8af98be5 100644 --- a/src/api/JoystickManager.h +++ b/src/api/JoystickManager.h @@ -20,7 +20,7 @@ #pragma once #include "JoystickTypes.h" -#include "storage/ButtonMapTypes.h" // for FeatureVector +#include "buttonmapper/ButtonMapTypes.h" #include "kodi_peripheral_utils.hpp" #include "p8-platform/threads/mutex.h" @@ -94,13 +94,13 @@ namespace JOYSTICK void TriggerScan(void); /*! - * \brief Get the feature mapping known to an interface + * \brief Get the button map known to the interface * - * \param provider Name of the joystick interface - * \param controllerId Add-on ID of the controller profile being requested - * \param[out] features The features, if any are known to the interface + * \param provider Name of the joystick interface + * + * \return A button map populated with hard-coded features for the interface */ - void GetFeatures(const std::string& provider, const std::string& controllerId, FeatureVector& features); + const ButtonMap& GetButtonMap(const std::string& provider); private: IScannerCallback* m_scanner; diff --git a/src/storage/ButtonMapTranslator.cpp b/src/buttonmapper/ButtonMapTranslator.cpp similarity index 100% rename from src/storage/ButtonMapTranslator.cpp rename to src/buttonmapper/ButtonMapTranslator.cpp diff --git a/src/storage/ButtonMapTranslator.h b/src/buttonmapper/ButtonMapTranslator.h similarity index 100% rename from src/storage/ButtonMapTranslator.h rename to src/buttonmapper/ButtonMapTranslator.h diff --git a/src/buttonmapper/ButtonMapTypes.h b/src/buttonmapper/ButtonMapTypes.h new file mode 100644 index 00000000..058d8d39 --- /dev/null +++ b/src/buttonmapper/ButtonMapTypes.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace ADDON +{ + class JoystickFeature; +}; + +namespace JOYSTICK +{ + typedef std::string ControllerID; + typedef std::string FeatureName; + + typedef std::vector FeatureVector; + typedef std::map ButtonMap; + + typedef std::pair FeatureMapItem; // From feature -> To feature + typedef std::map FeatureOccurrences; // Feature map item -> occurrence count + + struct ControllerMapItem + { + std::string fromController; + std::string toController; + FeatureOccurrences featureMap; + + bool operator<(const ControllerMapItem& other) const + { + if (fromController < other.fromController) return true; + if (fromController > other.fromController) return false; + + if (toController < other.toController) return true; + if (toController > other.toController) return false; + + return false; + } + }; + + typedef std::set ControllerMap; +} diff --git a/src/buttonmapper/ButtonMapper.cpp b/src/buttonmapper/ButtonMapper.cpp new file mode 100644 index 00000000..84d621df --- /dev/null +++ b/src/buttonmapper/ButtonMapper.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "ButtonMapper.h" +#include "storage/IDatabase.h" + +#include "kodi_peripheral_utils.hpp" + +#include +#include + +using namespace JOYSTICK; + +void CButtonMapper::RegisterDatabase(const DatabasePtr& database) +{ + if (std::find(m_databases.begin(), m_databases.end(), database) == m_databases.end()) + m_databases.push_back(database); +} + +void CButtonMapper::UnregisterDatabase(const DatabasePtr& database) +{ + m_databases.erase(std::remove(m_databases.begin(), m_databases.end(), database), m_databases.end()); +} + +bool CButtonMapper::GetFeatures(const ADDON::Joystick& joystick, + const std::string& strControllerId, + FeatureVector& features) +{ + // Accumulate available button maps for this device + ButtonMap accumulatedMap = GetButtonMap(joystick); + + GetFeatures(std::move(accumulatedMap), strControllerId, features); + + return !features.empty(); +} + +ButtonMap CButtonMapper::GetButtonMap(const ADDON::Joystick& joystick) const +{ + ButtonMap accumulatedMap; + + for (DatabaseVector::const_iterator it = m_databases.begin(); it != m_databases.end(); ++it) + { + const ButtonMap& buttonMap = (*it)->GetButtonMap(joystick); + MergeButtonMap(accumulatedMap, buttonMap); + } + + return accumulatedMap; +} + +void CButtonMapper::MergeButtonMap(ButtonMap& knownMap, const ButtonMap& newFeatures) +{ + for (auto it = newFeatures.begin(); it != newFeatures.end(); ++it) + { + const std::string& controllerId = it->first; + const FeatureVector& features = it->second; + + MergeFeatures(knownMap[controllerId], features); + } +} + +void CButtonMapper::MergeFeatures(FeatureVector& features, const FeatureVector& newFeatures) +{ + for (const ADDON::JoystickFeature& newFeature : newFeatures) + { + const bool bFound = std::find_if(features.begin(), features.end(), + [newFeature](const ADDON::JoystickFeature& feature) + { + return feature.Name() == newFeature.Name(); + }) != features.end(); + + if (!bFound) + features.push_back(newFeature); + } +} + +bool CButtonMapper::GetFeatures(ButtonMap&& buttonMap, const std::string& controllerId, FeatureVector& features) +{ + // Try to get a button map for the specified controller profile + auto itController = buttonMap.find(controllerId); + if (itController != buttonMap.end()) + features.swap(itController->second); + + // Try to derive a button map from relations between controller profiles + if (features.empty()) + DeriveFeatures(controllerId, buttonMap, features); + + return !features.empty(); +} + +void CButtonMapper::DeriveFeatures(const std::string& toController, const ButtonMap& buttonMap, FeatureVector& transformedFeatures) +{ + // Obtain an iterator to the controller profile with the highest count of features defined + unsigned int maxFeatures = 0; + auto maxFeaturesIt = buttonMap.end(); + + for (auto it = buttonMap.begin(); it != buttonMap.end(); ++it) + { + const unsigned int featureCount = it->second.size(); + if (featureCount > maxFeatures) + { + maxFeatures = featureCount; + maxFeaturesIt = it; + } + } + + if (maxFeaturesIt != buttonMap.end()) + { + // Transform the controller profile with the most features to the specified controller + const std::string& fromController = maxFeaturesIt->first; + const FeatureVector& features = maxFeaturesIt->second; + + m_controllerMapper.TransformFeatures(fromController, toController, features, transformedFeatures); + } +} diff --git a/src/buttonmapper/ButtonMapper.h b/src/buttonmapper/ButtonMapper.h new file mode 100644 index 00000000..b961ebd0 --- /dev/null +++ b/src/buttonmapper/ButtonMapper.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "ButtonMapTypes.h" +#include "ControllerMapper.h" +#include "storage/StorageTypes.h" + +#include + +namespace ADDON +{ + class Joystick; +}; + +namespace JOYSTICK +{ + class IDatabase; + class IDatabaseCallbacks; + + class CButtonMapper + { + public: + CButtonMapper() = default; + ~CButtonMapper() = default; + + void RegisterDatabase(const DatabasePtr& database); + void UnregisterDatabase(const DatabasePtr& database); + + bool GetFeatures(const ADDON::Joystick& joystick, const std::string& strDeviceId, FeatureVector& features); + + IDatabaseCallbacks* GetCallbacks() { return &m_controllerMapper; } + + private: + ButtonMap GetButtonMap(const ADDON::Joystick& joystick) const; + static void MergeButtonMap(ButtonMap& knownMap, const ButtonMap& newFeatures); + static void MergeFeatures(FeatureVector& features, const FeatureVector& newFeatures); + bool GetFeatures(ButtonMap&& buttonMap, const std::string& controllerId, FeatureVector& features); + void DeriveFeatures(const std::string& toController, const ButtonMap& buttonMap, FeatureVector& transformedFeatures); + + DatabaseVector m_databases; + CControllerMapper m_controllerMapper; + }; +} diff --git a/src/buttonmapper/ControllerMapper.cpp b/src/buttonmapper/ControllerMapper.cpp new file mode 100644 index 00000000..90a1763e --- /dev/null +++ b/src/buttonmapper/ControllerMapper.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "ControllerMapper.h" + +#include "kodi_peripheral_utils.hpp" + +#include + +using namespace JOYSTICK; + +void CControllerMapper::OnAdd(const CDevice& driverInfo, const ButtonMap& buttonMap) +{ + bool bChanged = false; + + for (auto itTo = buttonMap.begin(); itTo != buttonMap.end(); ++itTo) + { + // Only allow controller map items where "from" compares before "to" + for (auto itFrom = buttonMap.begin(); itFrom->first < itTo->first; ++itFrom) + { + bChanged |= AddControllerMap(itFrom->first, itFrom->second, itTo->first, itTo->second); + } + } + + // Clear cache + if (bChanged) + m_reducedMap.clear(); +} + +bool CControllerMapper::AddControllerMap(const std::string& controllerFrom, const FeatureVector& featuresFrom, + const std::string& controllerTo, const FeatureVector& featuresTo) +{ + bool bChanged = false; + + ControllerMapItem needle = { controllerFrom, controllerTo }; + + auto it = m_map.find(needle); + if (it == m_map.end()) + { + m_map.insert(needle); + it = m_map.find(needle); + } + + ControllerMapItem& controllerItem = const_cast(*it); + + for (auto itFromFeature = featuresFrom.begin(); itFromFeature != featuresFrom.end(); ++itFromFeature) + { + auto itToFeature = std::find_if(featuresTo.begin(), featuresTo.end(), + [&itFromFeature](const ADDON::JoystickFeature& feature) + { + if (itFromFeature->Type() == feature.Type()) + { + switch (feature.Type()) + { + case JOYSTICK_FEATURE_TYPE_SCALAR: + case JOYSTICK_FEATURE_TYPE_MOTOR: + { + return itFromFeature->Primitive() == feature.Primitive(); + } + case JOYSTICK_FEATURE_TYPE_ANALOG_STICK: + { + return itFromFeature->Up() == feature.Up() && + itFromFeature->Down() == feature.Down() && + itFromFeature->Right() == feature.Right() && + itFromFeature->Left() == feature.Left(); + } + case JOYSTICK_FEATURE_TYPE_ACCELEROMETER: + { + return itFromFeature->PositiveX() == feature.PositiveX() && + itFromFeature->PositiveY() == feature.PositiveY() && + itFromFeature->PositiveZ() == feature.PositiveZ(); + } + default: + break; + } + } + return false; + }); + + if (itToFeature != featuresTo.end()) + { + FeatureMapItem featureMapItem = { itFromFeature->Name(), itToFeature->Name() }; + ++controllerItem.featureMap[featureMapItem]; + bChanged = true; + } + } + + return bChanged; +} + +void CControllerMapper::TransformFeatures(const std::string& fromController, + const std::string& toController, + const FeatureVector& features, + FeatureVector& transformedFeatures) +{ + bool bSwap = (fromController >= toController); + + ControllerMapItem needle = { bSwap ? toController : fromController, + bSwap ? fromController : toController }; + + const FeatureOccurrences& featureMap = GetFeatureMap(needle, bSwap); + + for (auto itMap = featureMap.begin(); itMap != featureMap.end(); ++itMap) + { + const std::string& fromFeature = bSwap ? itMap->first.second : itMap->first.first; + const std::string& toFeature = bSwap ? itMap->first.first : itMap->first.second; + + auto itFrom = std::find_if(features.begin(), features.end(), + [fromFeature](const ADDON::JoystickFeature& feature) + { + return feature.Name() == fromFeature; + }); + + if (itFrom != features.end()) + { + ADDON::JoystickFeature transformedFeature(*itFrom); + transformedFeature.SetName(toFeature); + transformedFeatures.emplace_back(std::move(transformedFeature)); + } + } +} + +const FeatureOccurrences& CControllerMapper::GetFeatureMap(const ControllerMapItem& needle, bool bSwap) +{ + ControllerMap::iterator it = m_reducedMap.find(needle); + if (it == m_reducedMap.end()) + { + m_reducedMap.insert(needle); + it = m_reducedMap.find(needle); + } + + ControllerMapItem& controllerItem = const_cast(*it); + + FeatureOccurrences& featureMap = controllerItem.featureMap; + + if (featureMap.empty()) + { + auto it = m_map.find(needle); + if (it != m_map.end()) + { + const ControllerMapItem& model = *it; + ReduceModel(model.featureMap, featureMap, bSwap); + } + } + + return featureMap; +} + +void CControllerMapper::ReduceModel(const FeatureOccurrences& model, FeatureOccurrences& result, bool bSwap) +{ + // First pass, calculate max occurrence counts + std::map maxFeatureCounts; + for (auto it = model.begin(); it != model.end(); ++it) + { + const std::string& fromFeature = it->first.first; + const std::string& toFeature = it->first.second; + const std::string& feature = bSwap ? toFeature : fromFeature; + const unsigned int count = it->second; + + unsigned int& maxCount = maxFeatureCounts[feature]; + if (count > maxCount) + maxCount = count; + } + + // Second pass, assign features with max occurrences to result + for (auto it = model.begin(); it != model.end(); ++it) + { + const std::string& fromFeature = it->first.first; + const std::string& toFeature = it->first.second; + const std::string& feature = bSwap ? toFeature : fromFeature; + const unsigned int count = it->second; + + unsigned int& targetCount = maxFeatureCounts[feature]; + if (targetCount == count) + { + result[std::make_pair(fromFeature, toFeature)] = 1; + targetCount = 0; + } + } +} diff --git a/src/buttonmapper/ControllerMapper.h b/src/buttonmapper/ControllerMapper.h new file mode 100644 index 00000000..6b783c7e --- /dev/null +++ b/src/buttonmapper/ControllerMapper.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Garrett Brown + * Copyright (C) 2015 Team XBMC + * + * 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 XBMC; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "ButtonMapTypes.h" +#include "storage/IDatabase.h" + +namespace JOYSTICK +{ + class CControllerMapper : public IDatabaseCallbacks + { + public: + CControllerMapper() = default; + virtual ~CControllerMapper() = default; + + // implementation of IDatabaseCallbacks + virtual void OnAdd(const CDevice& driverInfo, const ButtonMap& buttonMap); + + void TransformFeatures(const std::string& fromController, + const std::string& toController, + const FeatureVector& features, + FeatureVector& transformedFeatures); + + private: + bool AddControllerMap(const std::string& controllerFrom, const FeatureVector& featuresFrom, + const std::string& controllerTo, const FeatureVector& featuresTo); + + const FeatureOccurrences& GetFeatureMap(const ControllerMapItem& needle, bool bSwap); + void ReduceModel(const FeatureOccurrences& model, FeatureOccurrences& result, bool bSwap); + + ControllerMap m_map; + ControllerMap m_reducedMap; + }; +} diff --git a/src/storage/ButtonMap.cpp b/src/storage/ButtonMap.cpp index 37efcaa6..bd7ae980 100644 --- a/src/storage/ButtonMap.cpp +++ b/src/storage/ButtonMap.cpp @@ -39,18 +39,10 @@ CButtonMap::CButtonMap(const std::string& strResourcePath, const CDevice& device { } -bool CButtonMap::GetFeatures(const std::string& controllerId, FeatureVector& features) +const ButtonMap& CButtonMap::GetButtonMap() { Refresh(); - - ButtonMap::const_iterator it = m_buttonMap.find(controllerId); - if (it != m_buttonMap.end()) - { - features = it->second; - return true; - } - - return false; + return m_buttonMap; } bool CButtonMap::MapFeatures(const std::string& controllerId, const FeatureVector& features) diff --git a/src/storage/ButtonMap.h b/src/storage/ButtonMap.h index 367649a5..05f813ec 100644 --- a/src/storage/ButtonMap.h +++ b/src/storage/ButtonMap.h @@ -19,8 +19,8 @@ */ #pragma once -#include "ButtonMapTypes.h" #include "Device.h" +#include "buttonmapper/ButtonMapTypes.h" #include @@ -38,7 +38,9 @@ namespace JOYSTICK const CDevice& Device(void) const { return m_device; } - bool GetFeatures(const std::string& controllerId, FeatureVector& features); + const ButtonMap& GetButtonMap(); + + void GetControllerMap(ControllerMap& controllerMap); bool MapFeatures(const std::string& controllerId, const FeatureVector& features); @@ -50,9 +52,6 @@ namespace JOYSTICK virtual bool Load(void) = 0; virtual bool Save(void) const = 0; - typedef std::string ControllerID; - typedef std::map ButtonMap; - const std::string m_strResourcePath; CDevice m_device; ButtonMap m_buttonMap; diff --git a/src/storage/IDatabase.h b/src/storage/IDatabase.h index 87916667..4cc7ac35 100644 --- a/src/storage/IDatabase.h +++ b/src/storage/IDatabase.h @@ -19,37 +19,53 @@ */ #pragma once -#include "ButtonMapTypes.h" +#include "buttonmapper/ButtonMapTypes.h" #include +namespace ADDON +{ + class Joystick; +} + namespace JOYSTICK { class CDevice; + class IDatabaseCallbacks + { + public: + virtual ~IDatabaseCallbacks() = default; + + virtual void OnAdd(const CDevice& driverInfo, const ButtonMap& buttonMap) = 0; + }; + class IDatabase { public: + IDatabase(IDatabaseCallbacks* callbacks) : m_callbacks(callbacks) { } + virtual ~IDatabase(void) { } /*! * \copydoc CStorageManager::GetFeatures() */ - virtual bool GetFeatures(const CDevice& driverInfo, - const std::string& controllerId, - FeatureVector& features) = 0; + virtual const ButtonMap& GetButtonMap(const ADDON::Joystick& driverInfo) = 0; /*! * \copydoc CStorageManager::MapFeatures() */ - virtual bool MapFeatures(const CDevice& driverInfo, + virtual bool MapFeatures(const ADDON::Joystick& driverInfo, const std::string& controllerId, const FeatureVector& features) = 0; /*! * \copydoc CStorageManager::ResetButtonMap() */ - virtual bool ResetButtonMap(const CDevice& driverInfo, + virtual bool ResetButtonMap(const ADDON::Joystick& driverInfo, const std::string& controllerId) = 0; + + protected: + IDatabaseCallbacks* const m_callbacks; }; } diff --git a/src/storage/JustABunchOfFiles.cpp b/src/storage/JustABunchOfFiles.cpp index 904dfab1..a20b3729 100644 --- a/src/storage/JustABunchOfFiles.cpp +++ b/src/storage/JustABunchOfFiles.cpp @@ -79,7 +79,11 @@ void CResources::RemoveResource(const std::string& strPath) // --- CJustABunchOfFiles ------------------------------------------------------ -CJustABunchOfFiles::CJustABunchOfFiles(const std::string& strResourcePath, const std::string& strExtension, bool bReadWrite) : +CJustABunchOfFiles::CJustABunchOfFiles(const std::string& strResourcePath, + const std::string& strExtension, + bool bReadWrite, + IDatabaseCallbacks* callbacks) : + IDatabase(callbacks), m_strResourcePath(strResourcePath), m_strExtension(strExtension), m_bReadWrite(bReadWrite) @@ -95,41 +99,45 @@ CJustABunchOfFiles::~CJustABunchOfFiles(void) m_directoryCache.Deinitialize(); } -bool CJustABunchOfFiles::GetFeatures(const CDevice& driverInfo, - const std::string& controllerId, - FeatureVector& features) +const ButtonMap& CJustABunchOfFiles::GetButtonMap(const ADDON::Joystick& driverInfo) { + static ButtonMap empty; + + CDevice device(driverInfo); + CLockObject lock(m_mutex); // Update index IndexDirectory(m_strResourcePath, FOLDER_DEPTH); - CButtonMap* resource = m_resources.GetResource(driverInfo); + CButtonMap* resource = m_resources.GetResource(device); if (resource) - return resource->GetFeatures(controllerId, features); + return resource->GetButtonMap(); - return false; + return empty; } -bool CJustABunchOfFiles::MapFeatures(const CDevice& driverInfo, +bool CJustABunchOfFiles::MapFeatures(const ADDON::Joystick& driverInfo, const std::string& controllerId, const FeatureVector& features) { if (!m_bReadWrite) return false; + CDevice device(driverInfo); + CLockObject lock(m_mutex); - CButtonMap* resource = m_resources.GetResource(driverInfo); + CButtonMap* resource = m_resources.GetResource(device); if (resource == nullptr) { // Resource doesn't exist yet, try to create it now std::string resourcePath; - GetResourcePath(driverInfo, resourcePath); + GetResourcePath(device, resourcePath); - resource = CreateResource(resourcePath, driverInfo); + resource = CreateResource(resourcePath, device); if (!m_resources.AddResource(resource)) { delete resource; @@ -143,14 +151,16 @@ bool CJustABunchOfFiles::MapFeatures(const CDevice& driverInfo, return false; } -bool CJustABunchOfFiles::ResetButtonMap(const CDevice& driverInfo, const std::string& controllerId) +bool CJustABunchOfFiles::ResetButtonMap(const ADDON::Joystick& driverInfo, const std::string& controllerId) { if (!m_bReadWrite) return false; + CDevice device(driverInfo); + CLockObject lock(m_mutex); - CButtonMap* resource = m_resources.GetResource(driverInfo); + CButtonMap* resource = m_resources.GetResource(device); if (resource) return resource->ResetButtonMap(controllerId); @@ -198,7 +208,9 @@ void CJustABunchOfFiles::OnAdd(const ADDON::CVFSDirEntry& item) // Load device info resource->Refresh(); - if (!m_resources.AddResource(resource)) + if (m_resources.AddResource(resource)) + m_callbacks->OnAdd(resource->Device(), resource->GetButtonMap()); + else delete resource; } } diff --git a/src/storage/JustABunchOfFiles.h b/src/storage/JustABunchOfFiles.h index 6d3cb290..86bd2612 100644 --- a/src/storage/JustABunchOfFiles.h +++ b/src/storage/JustABunchOfFiles.h @@ -54,17 +54,19 @@ namespace JOYSTICK public IDirectoryCacheCallback { public: - CJustABunchOfFiles(const std::string& strResourcePath, const std::string& strExtension, bool bReadWrite); + CJustABunchOfFiles(const std::string& strResourcePath, + const std::string& strExtension, + bool bReadWrite, + IDatabaseCallbacks* callbacks); + virtual ~CJustABunchOfFiles(void); // implementation of IDatabase - virtual bool GetFeatures(const CDevice& driverInfo, - const std::string& controllerId, - FeatureVector& features) override; - virtual bool MapFeatures(const CDevice& driverInfo, + virtual const ButtonMap& GetButtonMap(const ADDON::Joystick& driverInfo) override; + virtual bool MapFeatures(const ADDON::Joystick& driverInfo, const std::string& controllerId, const FeatureVector& features) override; - virtual bool ResetButtonMap(const CDevice& driverInfo, + virtual bool ResetButtonMap(const ADDON::Joystick& driverInfo, const std::string& controllerId) override; // implementation of IDirectoryCacheCallback @@ -78,7 +80,7 @@ namespace JOYSTICK private: /*! - * \brief Subcursively index a path, enumerating the folder and updating + * \brief Recursively index a path, enumerating the folder and updating * the directory cache */ void IndexDirectory(const std::string& path, unsigned int folderDepth); diff --git a/src/storage/StorageManager.cpp b/src/storage/StorageManager.cpp index 6f270a70..885f53d1 100644 --- a/src/storage/StorageManager.cpp +++ b/src/storage/StorageManager.cpp @@ -28,8 +28,8 @@ #include "utils/StringUtils.h" #include "libKODI_peripheral.h" - -#include +#include "kodi_peripheral_types.h" +#include "kodi_peripheral_utils.hpp" using namespace JOYSTICK; @@ -78,12 +78,15 @@ bool CStorageManager::Initialize(ADDON::CHelper_libKODI_peripheral* peripheralLi // Ensure button map path exists in user data CStorageUtils::EnsureDirectoryExists(strUserPath); - m_databases.push_back(DatabasePtr(new CDatabaseXml(strUserPath, true))); - //m_databases.push_back(DatabasePtr(new CDatabaseRetroArch(strUserPath, true))); // TODO - m_databases.push_back(DatabasePtr(new CDatabaseXml(strAddonPath, false))); + m_databases.push_back(DatabasePtr(new CDatabaseXml(strUserPath, true, m_buttonMapper.GetCallbacks()))); + //m_databases.push_back(DatabasePtr(new CDatabaseRetroArch(strUserPath, true, &m_controllerMapper))); // TODO + m_databases.push_back(DatabasePtr(new CDatabaseXml(strAddonPath, false, m_buttonMapper.GetCallbacks()))); //m_databases.push_back(DatabasePtr(new CDatabaseRetroArch(strAddonPath, false))); // TODO - m_databases.push_back(DatabasePtr(new CDatabaseJoystickAPI)); + m_databases.push_back(DatabasePtr(new CDatabaseJoystickAPI(m_buttonMapper.GetCallbacks()))); + + for (auto& database : m_databases) + m_buttonMapper.RegisterDatabase(database); return true; } @@ -97,14 +100,7 @@ bool CStorageManager::GetFeatures(const ADDON::Joystick& joystick, const std::string& strControllerId, FeatureVector& features) { - CDevice deviceInfo(joystick); - - for (DatabaseVector::const_iterator it = m_databases.begin(); it != m_databases.end(); ++it) - { - FeatureVector newFeatures; - if ((*it)->GetFeatures(deviceInfo, strControllerId, newFeatures)) - MergeFeatures(features, newFeatures); - } + m_buttonMapper.GetFeatures(joystick, strControllerId, features); return !features.empty(); } @@ -113,24 +109,20 @@ bool CStorageManager::MapFeatures(const ADDON::Joystick& joystick, const std::string& strControllerId, const FeatureVector& features) { - CDevice deviceInfo(joystick); - bool bModified = false; for (DatabaseVector::const_iterator it = m_databases.begin(); it != m_databases.end(); ++it) - bModified |= (*it)->MapFeatures(deviceInfo, strControllerId, features); + bModified |= (*it)->MapFeatures(joystick, strControllerId, features); return bModified; } bool CStorageManager::ResetButtonMap(const ADDON::Joystick& joystick, const std::string& strControllerId) { - CDevice deviceInfo(joystick); - bool bModified = false; for (DatabaseVector::const_iterator it = m_databases.begin(); it != m_databases.end(); ++it) - bModified |= (*it)->ResetButtonMap(deviceInfo, strControllerId); + bModified |= (*it)->ResetButtonMap(joystick, strControllerId); return bModified; } @@ -141,18 +133,3 @@ void CStorageManager::RefreshButtonMaps(const std::string& strDeviceName /* = "" if (m_peripheralLib) m_peripheralLib->RefreshButtonMaps(strDeviceName); } - -void CStorageManager::MergeFeatures(FeatureVector& features, const FeatureVector& newFeatures) -{ - for (const ADDON::JoystickFeature& newFeature : newFeatures) - { - const bool bFound = std::find_if(features.begin(), features.end(), - [newFeature](const ADDON::JoystickFeature& feature) - { - return feature.Name() == newFeature.Name(); - }) != features.end(); - - if (!bFound) - features.push_back(newFeature); - } -} diff --git a/src/storage/StorageManager.h b/src/storage/StorageManager.h index 78f2fbe4..f8bb7557 100644 --- a/src/storage/StorageManager.h +++ b/src/storage/StorageManager.h @@ -19,18 +19,19 @@ */ #pragma once -#include "ButtonMapTypes.h" - -#include "kodi_peripheral_types.h" -#include "kodi_peripheral_utils.hpp" +#include "StorageTypes.h" +#include "buttonmapper/ButtonMapper.h" +#include "buttonmapper/ButtonMapTypes.h" #include -#include + +struct PERIPHERAL_PROPERTIES; namespace ADDON { class CHelper_libKODI_peripheral; } namespace JOYSTICK { + class CDevice; class IDatabase; class CStorageManager @@ -104,10 +105,9 @@ namespace JOYSTICK void RefreshButtonMaps(const std::string& strDeviceName = ""); private: - static void MergeFeatures(FeatureVector& features, const FeatureVector& newFeatures); - ADDON::CHelper_libKODI_peripheral* m_peripheralLib; - DatabaseVector m_databases; + DatabaseVector m_databases; + CButtonMapper m_buttonMapper; }; } diff --git a/src/storage/ButtonMapTypes.h b/src/storage/StorageTypes.h similarity index 77% rename from src/storage/ButtonMapTypes.h rename to src/storage/StorageTypes.h index 88ed89fb..ce2ad4ce 100644 --- a/src/storage/ButtonMapTypes.h +++ b/src/storage/StorageTypes.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2015 Garrett Brown - * Copyright (C) 2015 Team XBMC + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi * * 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 @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with XBMC; see the file COPYING. If not, see + * along with this Program; see the file COPYING. If not, see * . */ #pragma once @@ -21,12 +21,8 @@ #include #include -namespace ADDON { class JoystickFeature; } - namespace JOYSTICK { - typedef std::vector FeatureVector; - class IDatabase; typedef std::shared_ptr DatabasePtr; typedef std::vector DatabaseVector; diff --git a/src/storage/api/DatabaseJoystickAPI.cpp b/src/storage/api/DatabaseJoystickAPI.cpp index 62476119..b1314bd6 100644 --- a/src/storage/api/DatabaseJoystickAPI.cpp +++ b/src/storage/api/DatabaseJoystickAPI.cpp @@ -24,18 +24,17 @@ using namespace JOYSTICK; -bool CDatabaseJoystickAPI::GetFeatures(const CDevice& driverInfo, const std::string& controllerId, FeatureVector& features) +const ButtonMap& CDatabaseJoystickAPI::GetButtonMap(const ADDON::Joystick& driverInfo) { - CJoystickManager::Get().GetFeatures(driverInfo.Provider(), controllerId, features); - return !features.empty(); + return CJoystickManager::Get().GetButtonMap(driverInfo.Provider()); } -bool CDatabaseJoystickAPI::MapFeatures(const CDevice& driverInfo, const std::string& controllerId, const FeatureVector& features) +bool CDatabaseJoystickAPI::MapFeatures(const ADDON::Joystick& driverInfo, const std::string& controllerId, const FeatureVector& features) { return false; } -bool CDatabaseJoystickAPI::ResetButtonMap(const CDevice& driverInfo, const std::string& controllerId) +bool CDatabaseJoystickAPI::ResetButtonMap(const ADDON::Joystick& driverInfo, const std::string& controllerId) { return false; } diff --git a/src/storage/api/DatabaseJoystickAPI.h b/src/storage/api/DatabaseJoystickAPI.h index 2de273e2..9212e457 100644 --- a/src/storage/api/DatabaseJoystickAPI.h +++ b/src/storage/api/DatabaseJoystickAPI.h @@ -26,13 +26,13 @@ namespace JOYSTICK class CDatabaseJoystickAPI : public IDatabase { public: - CDatabaseJoystickAPI(void) = default; + CDatabaseJoystickAPI(IDatabaseCallbacks* callbacks) : IDatabase(callbacks) { } virtual ~CDatabaseJoystickAPI(void) { } // implementation of IDatabase - virtual bool GetFeatures(const CDevice& driverInfo, const std::string& controllerId, FeatureVector& features) override; - virtual bool MapFeatures(const CDevice& driverInfo, const std::string& controllerId, const FeatureVector& features) override; - virtual bool ResetButtonMap(const CDevice& driverInfo, const std::string& controllerId) override; + virtual const ButtonMap& GetButtonMap(const ADDON::Joystick& driverInfo) override; + virtual bool MapFeatures(const ADDON::Joystick& driverInfo, const std::string& controllerId, const FeatureVector& features) override; + virtual bool ResetButtonMap(const ADDON::Joystick& driverInfo, const std::string& controllerId) override; }; } diff --git a/src/storage/xml/ButtonMapXml.cpp b/src/storage/xml/ButtonMapXml.cpp index abdd20c2..a62f39fa 100644 --- a/src/storage/xml/ButtonMapXml.cpp +++ b/src/storage/xml/ButtonMapXml.cpp @@ -20,8 +20,8 @@ #include "ButtonMapXml.h" #include "DeviceXml.h" +#include "buttonmapper/ButtonMapTranslator.h" #include "storage/ButtonMapDefinitions.h" -#include "storage/ButtonMapTranslator.h" #include "log/Log.h" #include "tinyxml.h" diff --git a/src/storage/xml/DatabaseXml.cpp b/src/storage/xml/DatabaseXml.cpp index 032314dd..bbb1751b 100644 --- a/src/storage/xml/DatabaseXml.cpp +++ b/src/storage/xml/DatabaseXml.cpp @@ -24,8 +24,8 @@ using namespace JOYSTICK; -CDatabaseXml::CDatabaseXml(const std::string& strBasePath, bool bReadWrite) : - CJustABunchOfFiles(strBasePath + "/" RESOURCE_XML_FOLDER, RESOURCE_XML_EXTENSION, bReadWrite) +CDatabaseXml::CDatabaseXml(const std::string& strBasePath, bool bReadWrite, IDatabaseCallbacks* callbacks) : + CJustABunchOfFiles(strBasePath + "/" RESOURCE_XML_FOLDER, RESOURCE_XML_EXTENSION, bReadWrite, callbacks) { } diff --git a/src/storage/xml/DatabaseXml.h b/src/storage/xml/DatabaseXml.h index 8939a90e..42522e24 100644 --- a/src/storage/xml/DatabaseXml.h +++ b/src/storage/xml/DatabaseXml.h @@ -31,7 +31,7 @@ namespace JOYSTICK class CDatabaseXml : public CJustABunchOfFiles { public: - CDatabaseXml(const std::string& strBasePath, bool bReadWrite); + CDatabaseXml(const std::string& strBasePath, bool bReadWrite, IDatabaseCallbacks* callbacks); virtual ~CDatabaseXml(void) { } From 6addf0de42839b9581e43f8515deecda2af9bb95 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Fri, 12 Aug 2016 13:42:05 -0700 Subject: [PATCH 02/28] [storage] Switch to smart pointers for CDevice objects As an additional change, devices are only processed once in CControllerMapper. --- src/buttonmapper/ControllerMapper.cpp | 8 ++- src/buttonmapper/ControllerMapper.h | 9 +++- src/storage/ButtonMap.cpp | 9 +++- src/storage/ButtonMap.h | 10 ++-- src/storage/Device.cpp | 46 ----------------- src/storage/Device.h | 28 ----------- src/storage/IDatabase.h | 3 +- src/storage/JustABunchOfFiles.cpp | 71 ++++++++++++++++----------- src/storage/JustABunchOfFiles.h | 8 ++- src/storage/StorageTypes.h | 6 +++ src/storage/StorageUtils.cpp | 43 ++++++++++++++++ src/storage/StorageUtils.h | 33 +++++++++++++ src/storage/xml/ButtonMapXml.cpp | 21 +++++--- src/storage/xml/ButtonMapXml.h | 2 +- src/storage/xml/DatabaseXml.cpp | 2 +- src/storage/xml/DatabaseXml.h | 2 +- 16 files changed, 176 insertions(+), 125 deletions(-) diff --git a/src/buttonmapper/ControllerMapper.cpp b/src/buttonmapper/ControllerMapper.cpp index 90a1763e..3d441492 100644 --- a/src/buttonmapper/ControllerMapper.cpp +++ b/src/buttonmapper/ControllerMapper.cpp @@ -26,8 +26,14 @@ using namespace JOYSTICK; -void CControllerMapper::OnAdd(const CDevice& driverInfo, const ButtonMap& buttonMap) +void CControllerMapper::OnAdd(const DevicePtr& driverInfo, const ButtonMap& buttonMap) { + // Skip devices we've already encountered. + if (m_observedDevices.find(driverInfo) == m_observedDevices.end()) + m_observedDevices.insert(driverInfo); + else + return; + bool bChanged = false; for (auto itTo = buttonMap.begin(); itTo != buttonMap.end(); ++itTo) diff --git a/src/buttonmapper/ControllerMapper.h b/src/buttonmapper/ControllerMapper.h index 6b783c7e..a99b027e 100644 --- a/src/buttonmapper/ControllerMapper.h +++ b/src/buttonmapper/ControllerMapper.h @@ -21,6 +21,12 @@ #include "ButtonMapTypes.h" #include "storage/IDatabase.h" +#include "storage/StorageTypes.h" + +namespace ADDON +{ + class Joystick; +} namespace JOYSTICK { @@ -31,7 +37,7 @@ namespace JOYSTICK virtual ~CControllerMapper() = default; // implementation of IDatabaseCallbacks - virtual void OnAdd(const CDevice& driverInfo, const ButtonMap& buttonMap); + virtual void OnAdd(const DevicePtr& driverInfo, const ButtonMap& buttonMap); void TransformFeatures(const std::string& fromController, const std::string& toController, @@ -47,5 +53,6 @@ namespace JOYSTICK ControllerMap m_map; ControllerMap m_reducedMap; + DeviceSet m_observedDevices; }; } diff --git a/src/storage/ButtonMap.cpp b/src/storage/ButtonMap.cpp index bd7ae980..02825b9b 100644 --- a/src/storage/ButtonMap.cpp +++ b/src/storage/ButtonMap.cpp @@ -19,6 +19,7 @@ */ #include "ButtonMap.h" +#include "Device.h" #include "p8-platform/util/timeutils.h" @@ -28,17 +29,23 @@ using namespace JOYSTICK; CButtonMap::CButtonMap(const std::string& strResourcePath) : m_strResourcePath(strResourcePath), + m_device(std::move(std::make_shared())), m_timestamp(-1) { } -CButtonMap::CButtonMap(const std::string& strResourcePath, const CDevice& device) : +CButtonMap::CButtonMap(const std::string& strResourcePath, const DevicePtr& device) : m_strResourcePath(strResourcePath), m_device(device), m_timestamp(-1) { } +bool CButtonMap::IsValid(void) const +{ + return m_device->IsValid(); +} + const ButtonMap& CButtonMap::GetButtonMap() { Refresh(); diff --git a/src/storage/ButtonMap.h b/src/storage/ButtonMap.h index 05f813ec..2731b42c 100644 --- a/src/storage/ButtonMap.h +++ b/src/storage/ButtonMap.h @@ -19,7 +19,7 @@ */ #pragma once -#include "Device.h" +#include "StorageTypes.h" #include "buttonmapper/ButtonMapTypes.h" #include @@ -30,13 +30,15 @@ namespace JOYSTICK { public: CButtonMap(const std::string& strResourcePath); - CButtonMap(const std::string& strResourcePath, const CDevice& device); + CButtonMap(const std::string& strResourcePath, const DevicePtr& device); virtual ~CButtonMap(void) { } const std::string& Path(void) const { return m_strResourcePath; } - const CDevice& Device(void) const { return m_device; } + const DevicePtr& Device(void) const { return m_device; } + + bool IsValid(void) const; const ButtonMap& GetButtonMap(); @@ -53,7 +55,7 @@ namespace JOYSTICK virtual bool Save(void) const = 0; const std::string m_strResourcePath; - CDevice m_device; + DevicePtr m_device; ButtonMap m_buttonMap; private: diff --git a/src/storage/Device.cpp b/src/storage/Device.cpp index 20c01143..4ca4b858 100644 --- a/src/storage/Device.cpp +++ b/src/storage/Device.cpp @@ -19,12 +19,6 @@ */ #include "Device.h" -#include "StorageUtils.h" -#include "utils/StringUtils.h" - -#include -#include - using namespace JOYSTICK; CDevice::CDevice(const ADDON::Joystick& joystick) : @@ -135,43 +129,3 @@ void CDevice::MergeProperties(const CDevice& record) SetIndex(record.Index()); } - -std::string CDevice::RootFileName(void) const -{ - std::string baseFilename = StringUtils::MakeSafeUrl(Name()); - - // Combine successive runs of underscores (fits more information in smaller - // space) - baseFilename.erase(std::unique(baseFilename.begin(), baseFilename.end(), - [](char a, char b) - { - return a == '_' && b == '_'; - }), baseFilename.end()); - - // Limit filename to a sane number of characters. - if (baseFilename.length() > 50) - baseFilename.erase(baseFilename.begin() + 50, baseFilename.end()); - - // Trim trailing underscores left over from chopping the string - baseFilename = StringUtils::Trim(baseFilename, "_"); - - // Append remaining properties - std::stringstream filename; - - filename << baseFilename; - if (IsVidPidKnown()) - { - filename << "_v" << CStorageUtils::FormatHexString(VendorID()); - filename << "_p" << CStorageUtils::FormatHexString(ProductID()); - } - if (ButtonCount() != 0) - filename << "_" << ButtonCount() << "b"; - if (HatCount() != 0) - filename << "_" << HatCount() << "h"; - if (AxisCount() != 0) - filename << "_" << AxisCount() << "a"; - if (Index() != 0) - filename << "_" << Index(); - - return filename.str(); -} diff --git a/src/storage/Device.h b/src/storage/Device.h index a76ed69b..01edf1b1 100644 --- a/src/storage/Device.h +++ b/src/storage/Device.h @@ -76,33 +76,5 @@ namespace JOYSTICK * \brief Merge valid (known) properties of given record into this record */ void MergeProperties(const CDevice& record); - - /*! - * \brief Utility function: Build a filename out of the record's properties - * - * \return A sensible filename, lacking an extension (which can be added by - * the caller) - * - * The filename is derived from driver properties. An example joystick - * filename is: - * - * Gamepad_F310_v1133_p1133_15b_6a - * - * where: - * - * - "Gamepad_F301" is the name reported by the driver - * - "v1133_p1133" is the USB VID/PID if known - * - "15b_6a" is the button/hat/axis count if known - * - * An example keyboard filename is: - * - * Keyboard_1 - * - * where: - * - * - "Keyboard" is the name given to the keyboard by Kodi's peripheral subsystem - * - `"1" is the player number (for arcade cabinets that use keyboard drivers) - */ - std::string RootFileName(void) const; }; } diff --git a/src/storage/IDatabase.h b/src/storage/IDatabase.h index 4cc7ac35..ecbffbad 100644 --- a/src/storage/IDatabase.h +++ b/src/storage/IDatabase.h @@ -19,6 +19,7 @@ */ #pragma once +#include "StorageTypes.h" #include "buttonmapper/ButtonMapTypes.h" #include @@ -37,7 +38,7 @@ namespace JOYSTICK public: virtual ~IDatabaseCallbacks() = default; - virtual void OnAdd(const CDevice& driverInfo, const ButtonMap& buttonMap) = 0; + virtual void OnAdd(const DevicePtr& driverInfo, const ButtonMap& buttonMap) = 0; }; class IDatabase diff --git a/src/storage/JustABunchOfFiles.cpp b/src/storage/JustABunchOfFiles.cpp index a20b3729..bc7de5cd 100644 --- a/src/storage/JustABunchOfFiles.cpp +++ b/src/storage/JustABunchOfFiles.cpp @@ -40,26 +40,36 @@ CResources::~CResources(void) delete it->second; } +DevicePtr CResources::GetDevice(const CDevice& deviceInfo) +{ + DevicePtr device; + + auto itDevice = m_devices.find(deviceInfo); + if (itDevice != m_devices.end()) + device = itDevice->second; + + return device; +} + CButtonMap* CResources::GetResource(const CDevice& deviceInfo) { - ResourceMap::iterator itResource = m_resources.find(deviceInfo); + CButtonMap* buttonMap = nullptr; + + auto itResource = m_resources.find(deviceInfo); if (itResource != m_resources.end()) - return itResource->second; + buttonMap = itResource->second; - return nullptr; + return buttonMap; } bool CResources::AddResource(CButtonMap* resource) { - if (resource != nullptr) + if (resource != nullptr && resource->IsValid()) { - if (resource->Device().IsValid()) - { - CButtonMap* oldResource = m_resources[resource->Device()]; - delete oldResource; - m_resources[resource->Device()] = resource; - return true; - } + CButtonMap* oldResource = m_resources[*resource->Device()]; + delete oldResource; + m_resources[*resource->Device()] = resource; + return true; } return false; } @@ -103,14 +113,12 @@ const ButtonMap& CJustABunchOfFiles::GetButtonMap(const ADDON::Joystick& driverI { static ButtonMap empty; - CDevice device(driverInfo); - CLockObject lock(m_mutex); // Update index IndexDirectory(m_strResourcePath, FOLDER_DEPTH); - CButtonMap* resource = m_resources.GetResource(device); + CButtonMap* resource = m_resources.GetResource(driverInfo); if (resource) return resource->GetButtonMap(); @@ -125,23 +133,22 @@ bool CJustABunchOfFiles::MapFeatures(const ADDON::Joystick& driverInfo, if (!m_bReadWrite) return false; - CDevice device(driverInfo); - CLockObject lock(m_mutex); - CButtonMap* resource = m_resources.GetResource(device); - + CButtonMap* resource = m_resources.GetResource(driverInfo); if (resource == nullptr) { // Resource doesn't exist yet, try to create it now std::string resourcePath; - GetResourcePath(device, resourcePath); - - resource = CreateResource(resourcePath, device); - if (!m_resources.AddResource(resource)) + if (GetResourcePath(driverInfo, resourcePath)) { - delete resource; - resource = nullptr; + DevicePtr device = std::make_shared(driverInfo); + resource = CreateResource(resourcePath, device); + if (!m_resources.AddResource(resource)) + { + delete resource; + resource = nullptr; + } } } @@ -203,13 +210,17 @@ void CJustABunchOfFiles::OnAdd(const ADDON::CVFSDirEntry& item) { if (!item.IsFolder()) { + // TODO: Switch to unique_ptr or shared_ptr CButtonMap* resource = CreateResource(item.Path()); // Load device info - resource->Refresh(); - - if (m_resources.AddResource(resource)) - m_callbacks->OnAdd(resource->Device(), resource->GetButtonMap()); + if (resource && resource->Refresh()) + { + if (m_resources.AddResource(resource)) + m_callbacks->OnAdd(resource->Device(), resource->GetButtonMap()); + else + delete resource; + } else delete resource; } @@ -220,13 +231,13 @@ void CJustABunchOfFiles::OnRemove(const ADDON::CVFSDirEntry& item) m_resources.RemoveResource(item.Path()); } -bool CJustABunchOfFiles::GetResourcePath(const CDevice& deviceInfo, std::string& resourcePath) const +bool CJustABunchOfFiles::GetResourcePath(const ADDON::Joystick& deviceInfo, std::string& resourcePath) const { // Calculate folder path std::string strFolder = m_strResourcePath + "/" + deviceInfo.Provider(); // Calculate resource path - resourcePath = strFolder + "/" + deviceInfo.RootFileName() + m_strExtension; + resourcePath = strFolder + "/" + CStorageUtils::RootFileName(deviceInfo) + m_strExtension; // Ensure folder path exists return CStorageUtils::EnsureDirectoryExists(strFolder); diff --git a/src/storage/JustABunchOfFiles.h b/src/storage/JustABunchOfFiles.h index 86bd2612..bdc5ef2e 100644 --- a/src/storage/JustABunchOfFiles.h +++ b/src/storage/JustABunchOfFiles.h @@ -40,13 +40,17 @@ namespace JOYSTICK CResources(void) { } ~CResources(void); + DevicePtr GetDevice(const CDevice& deviceInfo); + CButtonMap* GetResource(const CDevice& deviceInfo); bool AddResource(CButtonMap* resource); void RemoveResource(const std::string& strPath); private: + typedef std::map DeviceMap; typedef std::map ResourceMap; + DeviceMap m_devices; ResourceMap m_resources; }; @@ -76,7 +80,7 @@ namespace JOYSTICK protected: // Interface for child class to provide virtual CButtonMap* CreateResource(const std::string& resourcePath) = 0; - virtual CButtonMap* CreateResource(const std::string& resourcePath, const CDevice& driverInfo) = 0; + virtual CButtonMap* CreateResource(const std::string& resourcePath, const DevicePtr& driverInfo) = 0; private: /*! @@ -92,7 +96,7 @@ namespace JOYSTICK * * \return true if the path exists or was created */ - bool GetResourcePath(const CDevice& deviceInfo, std::string& resourcePath) const; + bool GetResourcePath(const ADDON::Joystick& deviceInfo, std::string& resourcePath) const; const std::string m_strResourcePath; const std::string m_strExtension; diff --git a/src/storage/StorageTypes.h b/src/storage/StorageTypes.h index ce2ad4ce..c5dcd11f 100644 --- a/src/storage/StorageTypes.h +++ b/src/storage/StorageTypes.h @@ -19,10 +19,16 @@ #pragma once #include +#include #include namespace JOYSTICK { + class CDevice; + typedef std::shared_ptr DevicePtr; + typedef std::vector DeviceVector; + typedef std::set DeviceSet; + class IDatabase; typedef std::shared_ptr DatabasePtr; typedef std::vector DatabaseVector; diff --git a/src/storage/StorageUtils.cpp b/src/storage/StorageUtils.cpp index 0944b0c2..b6f02594 100644 --- a/src/storage/StorageUtils.cpp +++ b/src/storage/StorageUtils.cpp @@ -19,11 +19,14 @@ */ #include "StorageUtils.h" +#include "Device.h" #include "filesystem/DirectoryUtils.h" #include "log/Log.h" #include "utils/StringUtils.h" +#include #include +#include #include using namespace JOYSTICK; @@ -50,6 +53,46 @@ bool CStorageUtils::EnsureDirectoryExists(const std::string& path) return true; } +std::string CStorageUtils::RootFileName(const ADDON::Joystick& device) +{ + std::string baseFilename = StringUtils::MakeSafeUrl(device.Name()); + + // Combine successive runs of underscores (fits more information in smaller + // space) + baseFilename.erase(std::unique(baseFilename.begin(), baseFilename.end(), + [](char a, char b) + { + return a == '_' && b == '_'; + }), baseFilename.end()); + + // Limit filename to a sane number of characters. + if (baseFilename.length() > 50) + baseFilename.erase(baseFilename.begin() + 50, baseFilename.end()); + + // Trim trailing underscores left over from chopping the string + baseFilename = StringUtils::Trim(baseFilename, "_"); + + // Append remaining properties + std::stringstream filename; + + filename << baseFilename; + if (device.IsVidPidKnown()) + { + filename << "_v" << CStorageUtils::FormatHexString(device.VendorID()); + filename << "_p" << CStorageUtils::FormatHexString(device.ProductID()); + } + if (device.ButtonCount() != 0) + filename << "_" << device.ButtonCount() << "b"; + if (device.HatCount() != 0) + filename << "_" << device.HatCount() << "h"; + if (device.AxisCount() != 0) + filename << "_" << device.AxisCount() << "a"; + if (device.Index() != 0) + filename << "_" << device.Index(); + + return filename.str(); +} + int CStorageUtils::HexStringToInt(const char* strHex) { int iVal; diff --git a/src/storage/StorageUtils.h b/src/storage/StorageUtils.h index ecffb3b1..42d03746 100644 --- a/src/storage/StorageUtils.h +++ b/src/storage/StorageUtils.h @@ -22,6 +22,11 @@ #include #include +namespace ADDON +{ + class Joystick; +} + namespace JOYSTICK { class CStorageUtils @@ -29,6 +34,34 @@ namespace JOYSTICK public: static bool EnsureDirectoryExists(const std::string& path); + /*! + * \brief Utility function: Build a filename out of the record's properties + * + * \return A sensible filename, lacking an extension (which can be added by + * the caller) + * + * The filename is derived from driver properties. An example joystick + * filename is: + * + * Gamepad_F310_v1133_p1133_15b_6a + * + * where: + * + * - "Gamepad_F301" is the name reported by the driver + * - "v1133_p1133" is the USB VID/PID if known + * - "15b_6a" is the button/hat/axis count if known + * + * An example keyboard filename is: + * + * Keyboard_1 + * + * where: + * + * - "Keyboard" is the name given to the keyboard by Kodi's peripheral subsystem + * - `"1" is the player number (for arcade cabinets that use keyboard drivers) + */ + static std::string RootFileName(const ADDON::Joystick& device); + /*! * From PeripheralTypes.h of Kodi */ diff --git a/src/storage/xml/ButtonMapXml.cpp b/src/storage/xml/ButtonMapXml.cpp index a62f39fa..4e8b56f5 100644 --- a/src/storage/xml/ButtonMapXml.cpp +++ b/src/storage/xml/ButtonMapXml.cpp @@ -22,6 +22,7 @@ #include "DeviceXml.h" #include "buttonmapper/ButtonMapTranslator.h" #include "storage/ButtonMapDefinitions.h" +#include "storage/Device.h" #include "log/Log.h" #include "tinyxml.h" @@ -38,7 +39,7 @@ CButtonMapXml::CButtonMapXml(const std::string& strResourcePath) : { } -CButtonMapXml::CButtonMapXml(const std::string& strResourcePath, const CDevice& device) : +CButtonMapXml::CButtonMapXml(const std::string& strResourcePath, const DevicePtr& device) : CButtonMap(strResourcePath, device) { } @@ -67,14 +68,18 @@ bool CButtonMapXml::Load(void) return false; } - if (!CDeviceXml::Deserialize(pDevice, m_device)) - return false; + // Don't overwrite valid device + if (!m_device->IsValid()) + { + if (!CDeviceXml::Deserialize(pDevice, *m_device)) + return false; + } const TiXmlElement* pController = pDevice->FirstChildElement(BUTTONMAP_XML_ELEM_CONTROLLER); if (!pController) { - esyslog("Device \"%s\": can't find <%s> tag", m_device.Name().c_str(), BUTTONMAP_XML_ELEM_CONTROLLER); + esyslog("Device \"%s\": can't find <%s> tag", m_device->Name().c_str(), BUTTONMAP_XML_ELEM_CONTROLLER); return false; } @@ -86,7 +91,7 @@ bool CButtonMapXml::Load(void) const char* id = pController->Attribute(BUTTONMAP_XML_ATTR_CONTROLLER_ID); if (!id) { - esyslog("Device \"%s\": <%s> tag has no attribute \"%s\"", m_device.Name().c_str(), + esyslog("Device \"%s\": <%s> tag has no attribute \"%s\"", m_device->Name().c_str(), BUTTONMAP_XML_ELEM_CONTROLLER, BUTTONMAP_XML_ATTR_CONTROLLER_ID); return false; } @@ -97,7 +102,7 @@ bool CButtonMapXml::Load(void) if (features.empty()) { - esyslog("Device \"%s\" has no features for controller %s", m_device.Name().c_str(), id); + esyslog("Device \"%s\" has no features for controller %s", m_device->Name().c_str(), id); } else { @@ -108,7 +113,7 @@ bool CButtonMapXml::Load(void) pController = pController->NextSiblingElement(BUTTONMAP_XML_ELEM_CONTROLLER); } - dsyslog("Loaded device \"%s\" with %u controller profiles and %u total features", m_device.Name().c_str(), m_buttonMap.size(), totalFeatureCount); + dsyslog("Loaded device \"%s\" with %u controller profiles and %u total features", m_device->Name().c_str(), m_buttonMap.size(), totalFeatureCount); return true; } @@ -138,7 +143,7 @@ bool CButtonMapXml::Save(void) const if (deviceElem == NULL) return false; - CDeviceXml::Serialize(m_device, deviceElem); + CDeviceXml::Serialize(*m_device, deviceElem); if (!SerializeButtonMaps(deviceElem)) return false; diff --git a/src/storage/xml/ButtonMapXml.h b/src/storage/xml/ButtonMapXml.h index 4a5434cf..0a260670 100644 --- a/src/storage/xml/ButtonMapXml.h +++ b/src/storage/xml/ButtonMapXml.h @@ -39,7 +39,7 @@ namespace JOYSTICK { public: CButtonMapXml(const std::string& strResourcePath); - CButtonMapXml(const std::string& strResourcePath, const CDevice& device); + CButtonMapXml(const std::string& strResourcePath, const DevicePtr& device); virtual ~CButtonMapXml(void) { } diff --git a/src/storage/xml/DatabaseXml.cpp b/src/storage/xml/DatabaseXml.cpp index bbb1751b..339b51e2 100644 --- a/src/storage/xml/DatabaseXml.cpp +++ b/src/storage/xml/DatabaseXml.cpp @@ -34,7 +34,7 @@ CButtonMap* CDatabaseXml::CreateResource(const std::string& resourcePath) return new CButtonMapXml(resourcePath); } -CButtonMap* CDatabaseXml::CreateResource(const std::string& resourcePath, const CDevice& deviceInfo) +CButtonMap* CDatabaseXml::CreateResource(const std::string& resourcePath, const DevicePtr& deviceInfo) { return new CButtonMapXml(resourcePath, deviceInfo); } diff --git a/src/storage/xml/DatabaseXml.h b/src/storage/xml/DatabaseXml.h index 42522e24..ae2cc451 100644 --- a/src/storage/xml/DatabaseXml.h +++ b/src/storage/xml/DatabaseXml.h @@ -38,6 +38,6 @@ namespace JOYSTICK protected: // implementation of CJustABunchOfFiles virtual CButtonMap* CreateResource(const std::string& resourcePath) override; - virtual CButtonMap* CreateResource(const std::string& resourcePath, const CDevice& deviceInfo) override; + virtual CButtonMap* CreateResource(const std::string& resourcePath, const DevicePtr& deviceInfo) override; }; } From 3d11553b409e1b7fa3a08e105223b0dd8ef44e08 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Fri, 12 Aug 2016 13:58:49 -0700 Subject: [PATCH 03/28] [buttonmapper] Add runtime check on manditory condition --- src/buttonmapper/ControllerMapper.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/buttonmapper/ControllerMapper.cpp b/src/buttonmapper/ControllerMapper.cpp index 3d441492..41f09b7b 100644 --- a/src/buttonmapper/ControllerMapper.cpp +++ b/src/buttonmapper/ControllerMapper.cpp @@ -23,6 +23,7 @@ #include "kodi_peripheral_utils.hpp" #include +#include using namespace JOYSTICK; @@ -55,6 +56,8 @@ bool CControllerMapper::AddControllerMap(const std::string& controllerFrom, cons { bool bChanged = false; + assert(controllerFrom < controllerTo); + ControllerMapItem needle = { controllerFrom, controllerTo }; auto it = m_map.find(needle); From ed237b89c83ec5764d7fd444975fd08b55cdba91 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Fri, 12 Aug 2016 16:36:12 -0700 Subject: [PATCH 04/28] [buttonmapping] Build a separate model for each driver geometry This also adds separate models for joystick families, but this is unimplemented until loading from the joystick family XML is finished. --- CMakeLists.txt | 6 +- src/buttonmapper/ButtonMapTypes.h | 29 ++++- src/buttonmapper/ButtonMapper.cpp | 32 ++--- src/buttonmapper/ButtonMapper.h | 10 +- src/buttonmapper/ControllerMapper.cpp | 157 +++++++++--------------- src/buttonmapper/ControllerMapper.h | 29 +++-- src/buttonmapper/ControllerModel.cpp | 71 +++++++++++ src/buttonmapper/ControllerModel.h | 44 +++++++ src/buttonmapper/DriverGeometry.cpp | 51 ++++++++ src/buttonmapper/DriverGeometry.h | 41 +++++++ src/buttonmapper/JoystickFamily.cpp | 69 +++++++++++ src/buttonmapper/JoystickFamily.h | 59 +++++++++ src/storage/xml/JoystickFamiliesXml.cpp | 32 +++++ src/storage/xml/JoystickFamiliesXml.h | 31 +++++ 14 files changed, 526 insertions(+), 135 deletions(-) create mode 100644 src/buttonmapper/ControllerModel.cpp create mode 100644 src/buttonmapper/ControllerModel.h create mode 100644 src/buttonmapper/DriverGeometry.cpp create mode 100644 src/buttonmapper/DriverGeometry.h create mode 100644 src/buttonmapper/JoystickFamily.cpp create mode 100644 src/buttonmapper/JoystickFamily.h create mode 100644 src/storage/xml/JoystickFamiliesXml.cpp create mode 100644 src/storage/xml/JoystickFamiliesXml.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aa03ddb..b35c442f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,9 +29,12 @@ set(JOYSTICK_SOURCES src/addon.cpp src/api/JoystickManager.cpp src/api/JoystickTranslator.cpp src/api/PeripheralScanner.cpp - src/buttonmapper/ButtonMapTranslator.cpp src/buttonmapper/ButtonMapper.cpp + src/buttonmapper/ButtonMapTranslator.cpp src/buttonmapper/ControllerMapper.cpp + src/buttonmapper/ControllerModel.cpp + src/buttonmapper/DriverGeometry.cpp + src/buttonmapper/JoystickFamily.cpp src/filesystem/DirectoryCache.cpp src/filesystem/DirectoryUtils.cpp src/filesystem/Filesystem.cpp @@ -53,6 +56,7 @@ set(JOYSTICK_SOURCES src/addon.cpp src/storage/xml/ButtonMapXml.cpp src/storage/xml/DatabaseXml.cpp src/storage/xml/DeviceXml.cpp + src/storage/xml/JoystickFamiliesXml.cpp src/utils/StringUtils.cpp) check_include_files("syslog.h" HAVE_SYSLOG) diff --git a/src/buttonmapper/ButtonMapTypes.h b/src/buttonmapper/ButtonMapTypes.h index 058d8d39..3e343a29 100644 --- a/src/buttonmapper/ButtonMapTypes.h +++ b/src/buttonmapper/ButtonMapTypes.h @@ -38,14 +38,29 @@ namespace JOYSTICK typedef std::vector FeatureVector; typedef std::map ButtonMap; - typedef std::pair FeatureMapItem; // From feature -> To feature - typedef std::map FeatureOccurrences; // Feature map item -> occurrence count + struct FeatureMapItem + { + std::string fromFeature; + std::string toFeature; + + bool operator<(const FeatureMapItem& other) const + { + if (fromFeature < other.fromFeature) return true; + if (fromFeature > other.fromFeature) return false; + + if (toFeature < other.toFeature) return true; + if (toFeature > other.toFeature) return false; + + return false; + } + }; + + typedef std::map FeatureOccurrences; struct ControllerMapItem { std::string fromController; std::string toController; - FeatureOccurrences featureMap; bool operator<(const ControllerMapItem& other) const { @@ -59,5 +74,11 @@ namespace JOYSTICK } }; - typedef std::set ControllerMap; + typedef std::map ControllerMap; + + typedef std::string FamilyName; + typedef std::string JoystickName; + + typedef std::map> JoystickFamilyMap; + } diff --git a/src/buttonmapper/ButtonMapper.cpp b/src/buttonmapper/ButtonMapper.cpp index 84d621df..a937e7a9 100644 --- a/src/buttonmapper/ButtonMapper.cpp +++ b/src/buttonmapper/ButtonMapper.cpp @@ -28,17 +28,6 @@ using namespace JOYSTICK; -void CButtonMapper::RegisterDatabase(const DatabasePtr& database) -{ - if (std::find(m_databases.begin(), m_databases.end(), database) == m_databases.end()) - m_databases.push_back(database); -} - -void CButtonMapper::UnregisterDatabase(const DatabasePtr& database) -{ - m_databases.erase(std::remove(m_databases.begin(), m_databases.end(), database), m_databases.end()); -} - bool CButtonMapper::GetFeatures(const ADDON::Joystick& joystick, const std::string& strControllerId, FeatureVector& features) @@ -46,7 +35,7 @@ bool CButtonMapper::GetFeatures(const ADDON::Joystick& joystick, // Accumulate available button maps for this device ButtonMap accumulatedMap = GetButtonMap(joystick); - GetFeatures(std::move(accumulatedMap), strControllerId, features); + GetFeatures(joystick, std::move(accumulatedMap), strControllerId, features); return !features.empty(); } @@ -90,7 +79,7 @@ void CButtonMapper::MergeFeatures(FeatureVector& features, const FeatureVector& } } -bool CButtonMapper::GetFeatures(ButtonMap&& buttonMap, const std::string& controllerId, FeatureVector& features) +bool CButtonMapper::GetFeatures(const ADDON::Joystick& joystick, ButtonMap&& buttonMap, const std::string& controllerId, FeatureVector& features) { // Try to get a button map for the specified controller profile auto itController = buttonMap.find(controllerId); @@ -99,12 +88,12 @@ bool CButtonMapper::GetFeatures(ButtonMap&& buttonMap, const std::string& contro // Try to derive a button map from relations between controller profiles if (features.empty()) - DeriveFeatures(controllerId, buttonMap, features); + DeriveFeatures(joystick, controllerId, buttonMap, features); return !features.empty(); } -void CButtonMapper::DeriveFeatures(const std::string& toController, const ButtonMap& buttonMap, FeatureVector& transformedFeatures) +void CButtonMapper::DeriveFeatures(const ADDON::Joystick& joystick, const std::string& toController, const ButtonMap& buttonMap, FeatureVector& transformedFeatures) { // Obtain an iterator to the controller profile with the highest count of features defined unsigned int maxFeatures = 0; @@ -126,6 +115,17 @@ void CButtonMapper::DeriveFeatures(const std::string& toController, const Button const std::string& fromController = maxFeaturesIt->first; const FeatureVector& features = maxFeaturesIt->second; - m_controllerMapper.TransformFeatures(fromController, toController, features, transformedFeatures); + m_controllerMapper.TransformFeatures(joystick, fromController, toController, features, transformedFeatures); } } + +void CButtonMapper::RegisterDatabase(const DatabasePtr& database) +{ + if (std::find(m_databases.begin(), m_databases.end(), database) == m_databases.end()) + m_databases.push_back(database); +} + +void CButtonMapper::UnregisterDatabase(const DatabasePtr& database) +{ + m_databases.erase(std::remove(m_databases.begin(), m_databases.end(), database), m_databases.end()); +} diff --git a/src/buttonmapper/ButtonMapper.h b/src/buttonmapper/ButtonMapper.h index b961ebd0..385330e3 100644 --- a/src/buttonmapper/ButtonMapper.h +++ b/src/buttonmapper/ButtonMapper.h @@ -41,19 +41,19 @@ namespace JOYSTICK CButtonMapper() = default; ~CButtonMapper() = default; - void RegisterDatabase(const DatabasePtr& database); - void UnregisterDatabase(const DatabasePtr& database); - bool GetFeatures(const ADDON::Joystick& joystick, const std::string& strDeviceId, FeatureVector& features); IDatabaseCallbacks* GetCallbacks() { return &m_controllerMapper; } + void RegisterDatabase(const DatabasePtr& database); + void UnregisterDatabase(const DatabasePtr& database); + private: ButtonMap GetButtonMap(const ADDON::Joystick& joystick) const; static void MergeButtonMap(ButtonMap& knownMap, const ButtonMap& newFeatures); static void MergeFeatures(FeatureVector& features, const FeatureVector& newFeatures); - bool GetFeatures(ButtonMap&& buttonMap, const std::string& controllerId, FeatureVector& features); - void DeriveFeatures(const std::string& toController, const ButtonMap& buttonMap, FeatureVector& transformedFeatures); + bool GetFeatures(const ADDON::Joystick& joystick, ButtonMap&& buttonMap, const std::string& controllerId, FeatureVector& features); + void DeriveFeatures(const ADDON::Joystick& joystick, const std::string& toController, const ButtonMap& buttonMap, FeatureVector& transformedFeatures); DatabaseVector m_databases; CControllerMapper m_controllerMapper; diff --git a/src/buttonmapper/ControllerMapper.cpp b/src/buttonmapper/ControllerMapper.cpp index 41f09b7b..99b7131e 100644 --- a/src/buttonmapper/ControllerMapper.cpp +++ b/src/buttonmapper/ControllerMapper.cpp @@ -19,10 +19,12 @@ */ #include "ControllerMapper.h" +#include "storage/Device.h" #include "kodi_peripheral_utils.hpp" #include +#include #include using namespace JOYSTICK; @@ -35,23 +37,27 @@ void CControllerMapper::OnAdd(const DevicePtr& driverInfo, const ButtonMap& butt else return; - bool bChanged = false; + CJoystickFamily family(driverInfo->Name(), driverInfo->Provider()); + CDriverGeometry geometry(driverInfo->ButtonCount(), + driverInfo->HatCount(), + driverInfo->AxisCount()); + + CControllerModel& familyModel = m_familyModels[family]; + CControllerModel& geometryModel = m_geometryModels[geometry]; for (auto itTo = buttonMap.begin(); itTo != buttonMap.end(); ++itTo) { // Only allow controller map items where "from" compares before "to" for (auto itFrom = buttonMap.begin(); itFrom->first < itTo->first; ++itFrom) { - bChanged |= AddControllerMap(itFrom->first, itFrom->second, itTo->first, itTo->second); + AddControllerMap(familyModel, itFrom->first, itFrom->second, itTo->first, itTo->second); + AddControllerMap(geometryModel, itFrom->first, itFrom->second, itTo->first, itTo->second); } } - - // Clear cache - if (bChanged) - m_reducedMap.clear(); } -bool CControllerMapper::AddControllerMap(const std::string& controllerFrom, const FeatureVector& featuresFrom, +bool CControllerMapper::AddControllerMap(CControllerModel& model, + const std::string& controllerFrom, const FeatureVector& featuresFrom, const std::string& controllerTo, const FeatureVector& featuresTo) { bool bChanged = false; @@ -60,41 +66,37 @@ bool CControllerMapper::AddControllerMap(const std::string& controllerFrom, cons ControllerMapItem needle = { controllerFrom, controllerTo }; - auto it = m_map.find(needle); - if (it == m_map.end()) - { - m_map.insert(needle); - it = m_map.find(needle); - } - - ControllerMapItem& controllerItem = const_cast(*it); + ControllerMap& controllerMap = model.GetMap(); + FeatureOccurrences& featureMap = controllerMap[needle]; for (auto itFromFeature = featuresFrom.begin(); itFromFeature != featuresFrom.end(); ++itFromFeature) { + const ADDON::JoystickFeature& fromFeature = *itFromFeature; + auto itToFeature = std::find_if(featuresTo.begin(), featuresTo.end(), - [&itFromFeature](const ADDON::JoystickFeature& feature) + [&fromFeature](const ADDON::JoystickFeature& feature) { - if (itFromFeature->Type() == feature.Type()) + if (fromFeature.Type() == feature.Type()) { switch (feature.Type()) { case JOYSTICK_FEATURE_TYPE_SCALAR: case JOYSTICK_FEATURE_TYPE_MOTOR: { - return itFromFeature->Primitive() == feature.Primitive(); + return fromFeature.Primitive() == feature.Primitive(); } case JOYSTICK_FEATURE_TYPE_ANALOG_STICK: { - return itFromFeature->Up() == feature.Up() && - itFromFeature->Down() == feature.Down() && - itFromFeature->Right() == feature.Right() && - itFromFeature->Left() == feature.Left(); + return fromFeature.Up() == feature.Up() && + fromFeature.Down() == feature.Down() && + fromFeature.Right() == feature.Right() && + fromFeature.Left() == feature.Left(); } case JOYSTICK_FEATURE_TYPE_ACCELEROMETER: { - return itFromFeature->PositiveX() == feature.PositiveX() && - itFromFeature->PositiveY() == feature.PositiveY() && - itFromFeature->PositiveZ() == feature.PositiveZ(); + return fromFeature.PositiveX() == feature.PositiveX() && + fromFeature.PositiveY() == feature.PositiveY() && + fromFeature.PositiveZ() == feature.PositiveZ(); } default: break; @@ -105,16 +107,20 @@ bool CControllerMapper::AddControllerMap(const std::string& controllerFrom, cons if (itToFeature != featuresTo.end()) { - FeatureMapItem featureMapItem = { itFromFeature->Name(), itToFeature->Name() }; - ++controllerItem.featureMap[featureMapItem]; + FeatureMapItem featureMapItem = { fromFeature.Name(), itToFeature->Name() }; + ++featureMap[featureMapItem]; bChanged = true; } } + if (bChanged) + model.Reset(); + return bChanged; } -void CControllerMapper::TransformFeatures(const std::string& fromController, +void CControllerMapper::TransformFeatures(const ADDON::Joystick& driverInfo, + const std::string& fromController, const std::string& toController, const FeatureVector& features, FeatureVector& transformedFeatures) @@ -124,83 +130,40 @@ void CControllerMapper::TransformFeatures(const std::string& fromController, ControllerMapItem needle = { bSwap ? toController : fromController, bSwap ? fromController : toController }; - const FeatureOccurrences& featureMap = GetFeatureMap(needle, bSwap); - - for (auto itMap = featureMap.begin(); itMap != featureMap.end(); ++itMap) - { - const std::string& fromFeature = bSwap ? itMap->first.second : itMap->first.first; - const std::string& toFeature = bSwap ? itMap->first.first : itMap->first.second; + CJoystickFamily family(driverInfo.Name(), driverInfo.Provider()); + CDriverGeometry geometry(driverInfo.ButtonCount(), + driverInfo.HatCount(), + driverInfo.AxisCount()); - auto itFrom = std::find_if(features.begin(), features.end(), - [fromFeature](const ADDON::JoystickFeature& feature) - { - return feature.Name() == fromFeature; - }); + CControllerModel& familyModel = m_familyModels[family]; + CControllerModel& geometryModel = m_geometryModels[geometry]; - if (itFrom != features.end()) - { - ADDON::JoystickFeature transformedFeature(*itFrom); - transformedFeature.SetName(toFeature); - transformedFeatures.emplace_back(std::move(transformedFeature)); - } - } -} + std::array models = { &familyModel, &geometryModel }; -const FeatureOccurrences& CControllerMapper::GetFeatureMap(const ControllerMapItem& needle, bool bSwap) -{ - ControllerMap::iterator it = m_reducedMap.find(needle); - if (it == m_reducedMap.end()) + for (CControllerModel* model : models) { - m_reducedMap.insert(needle); - it = m_reducedMap.find(needle); - } - - ControllerMapItem& controllerItem = const_cast(*it); + const FeatureOccurrences& normalizedFeatures = model->GetNormalizedFeatures(needle, bSwap); - FeatureOccurrences& featureMap = controllerItem.featureMap; - - if (featureMap.empty()) - { - auto it = m_map.find(needle); - if (it != m_map.end()) + for (auto itMap = normalizedFeatures.begin(); itMap != normalizedFeatures.end(); ++itMap) { - const ControllerMapItem& model = *it; - ReduceModel(model.featureMap, featureMap, bSwap); - } - } - - return featureMap; -} + const std::string& fromFeature = bSwap ? itMap->first.toFeature : itMap->first.fromFeature; + const std::string& toFeature = bSwap ? itMap->first.fromFeature : itMap->first.toFeature; -void CControllerMapper::ReduceModel(const FeatureOccurrences& model, FeatureOccurrences& result, bool bSwap) -{ - // First pass, calculate max occurrence counts - std::map maxFeatureCounts; - for (auto it = model.begin(); it != model.end(); ++it) - { - const std::string& fromFeature = it->first.first; - const std::string& toFeature = it->first.second; - const std::string& feature = bSwap ? toFeature : fromFeature; - const unsigned int count = it->second; - - unsigned int& maxCount = maxFeatureCounts[feature]; - if (count > maxCount) - maxCount = count; - } - - // Second pass, assign features with max occurrences to result - for (auto it = model.begin(); it != model.end(); ++it) - { - const std::string& fromFeature = it->first.first; - const std::string& toFeature = it->first.second; - const std::string& feature = bSwap ? toFeature : fromFeature; - const unsigned int count = it->second; + auto itFrom = std::find_if(features.begin(), features.end(), + [fromFeature](const ADDON::JoystickFeature& feature) + { + return feature.Name() == fromFeature; + }); - unsigned int& targetCount = maxFeatureCounts[feature]; - if (targetCount == count) - { - result[std::make_pair(fromFeature, toFeature)] = 1; - targetCount = 0; + if (itFrom != features.end()) + { + ADDON::JoystickFeature transformedFeature(*itFrom); + transformedFeature.SetName(toFeature); + transformedFeatures.emplace_back(std::move(transformedFeature)); + } } + + if (!transformedFeatures.empty()) + break; } } diff --git a/src/buttonmapper/ControllerMapper.h b/src/buttonmapper/ControllerMapper.h index a99b027e..b8850316 100644 --- a/src/buttonmapper/ControllerMapper.h +++ b/src/buttonmapper/ControllerMapper.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2015 Garrett Brown - * Copyright (C) 2015 Team XBMC + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi * * 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 @@ -8,18 +8,21 @@ * 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 + * but WITHOUT ANY WARRANTY; without even the implied warranty oftypedef std::map * 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 XBMC; see the file COPYING. If not, see + * along with this Program; see the file COPYING. If not, see * . * */ #pragma once #include "ButtonMapTypes.h" +#include "ControllerModel.h" +#include "DriverGeometry.h" +#include "JoystickFamily.h" #include "storage/IDatabase.h" #include "storage/StorageTypes.h" @@ -39,20 +42,22 @@ namespace JOYSTICK // implementation of IDatabaseCallbacks virtual void OnAdd(const DevicePtr& driverInfo, const ButtonMap& buttonMap); - void TransformFeatures(const std::string& fromController, + void TransformFeatures(const ADDON::Joystick& driverInfo, + const std::string& fromController, const std::string& toController, const FeatureVector& features, FeatureVector& transformedFeatures); private: - bool AddControllerMap(const std::string& controllerFrom, const FeatureVector& featuresFrom, - const std::string& controllerTo, const FeatureVector& featuresTo); + static bool AddControllerMap(CControllerModel& model, + const std::string& controllerFrom, const FeatureVector& featuresFrom, + const std::string& controllerTo, const FeatureVector& featuresTo); - const FeatureOccurrences& GetFeatureMap(const ControllerMapItem& needle, bool bSwap); - void ReduceModel(const FeatureOccurrences& model, FeatureOccurrences& result, bool bSwap); + typedef std::map FamilyMap; + typedef std::map GeomoetryMap; - ControllerMap m_map; - ControllerMap m_reducedMap; - DeviceSet m_observedDevices; + FamilyMap m_familyModels; + GeomoetryMap m_geometryModels; + DeviceSet m_observedDevices; }; } diff --git a/src/buttonmapper/ControllerModel.cpp b/src/buttonmapper/ControllerModel.cpp new file mode 100644 index 00000000..729e0e29 --- /dev/null +++ b/src/buttonmapper/ControllerModel.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "ControllerModel.h" + +using namespace JOYSTICK; + +void CControllerModel::Reset() +{ + m_reducedMap.clear(); +} + +const FeatureOccurrences& CControllerModel::GetNormalizedFeatures(const ControllerMapItem& needle, bool bSwap) +{ + FeatureOccurrences& result = m_reducedMap[needle]; + if (result.empty()) + NormalizeFeatures(m_map[needle], result, bSwap); + + return result; +} + +void CControllerModel::NormalizeFeatures(const FeatureOccurrences& features, FeatureOccurrences& result, bool bSwap) +{ + // First pass, calculate max occurrence counts + std::map maxFeatureCounts; + for (auto it = features.begin(); it != features.end(); ++it) + { + const std::string& fromFeature = it->first.fromFeature; + const std::string& toFeature = it->first.toFeature; + const std::string& lookupFeature = bSwap ? toFeature : fromFeature; + const unsigned int count = it->second; + + unsigned int& maxCount = maxFeatureCounts[lookupFeature]; + if (count > maxCount) + maxCount = count; + } + + // Second pass, assign features with max occurrences to result + for (auto it = features.begin(); it != features.end(); ++it) + { + const std::string& fromFeature = it->first.fromFeature; + const std::string& toFeature = it->first.toFeature; + const std::string& lookupFeature = bSwap ? toFeature : fromFeature; + const unsigned int count = it->second; + + unsigned int& targetCount = maxFeatureCounts[lookupFeature]; + if (targetCount == count) + { + FeatureMapItem featurePair = { fromFeature, toFeature }; + result[featurePair] = 1; + targetCount = 0; + } + } +} diff --git a/src/buttonmapper/ControllerModel.h b/src/buttonmapper/ControllerModel.h new file mode 100644 index 00000000..d24a1528 --- /dev/null +++ b/src/buttonmapper/ControllerModel.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "ButtonMapTypes.h" + +namespace JOYSTICK +{ + class CControllerModel + { + public: + void Reset(); + + ControllerMap& GetMap() { return m_map; } + const ControllerMap& GetMap() const { return m_map; } + + const FeatureOccurrences& GetNormalizedFeatures(const ControllerMapItem& needle, bool bSwap); + + private: + void Normalize(const ControllerMapItem& needle, bool bSwap); + + static void NormalizeFeatures(const FeatureOccurrences& feature, FeatureOccurrences& result, bool bSwap); + + ControllerMap m_map; + ControllerMap m_reducedMap; + }; +} diff --git a/src/buttonmapper/DriverGeometry.cpp b/src/buttonmapper/DriverGeometry.cpp new file mode 100644 index 00000000..9178bb1c --- /dev/null +++ b/src/buttonmapper/DriverGeometry.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "DriverGeometry.h" + +using namespace JOYSTICK; + +CDriverGeometry::CDriverGeometry(unsigned int buttonCount, unsigned int hatCount, unsigned int axisCount) : + m_buttonCount(buttonCount), + m_hatCount(hatCount), + m_axisCount(axisCount) +{ +} + +CDriverGeometry::CDriverGeometry(const CDriverGeometry& other) : + m_buttonCount(other.ButtonCount()), + m_hatCount(other.HatCount()), + m_axisCount(other.AxisCount()) +{ +} + +bool CDriverGeometry::operator<(const CDriverGeometry& other) const +{ + if (m_buttonCount < other.m_buttonCount) return true; + if (m_buttonCount > other.m_buttonCount) return false; + + if (m_hatCount < other.m_hatCount) return true; + if (m_hatCount > other.m_hatCount) return false; + + if (m_axisCount < other.m_axisCount) return true; + if (m_axisCount > other.m_axisCount) return false; + + return false; +} diff --git a/src/buttonmapper/DriverGeometry.h b/src/buttonmapper/DriverGeometry.h new file mode 100644 index 00000000..34a09b6d --- /dev/null +++ b/src/buttonmapper/DriverGeometry.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 Garrett Brown + * Copyright (C) 2015 Team XBMC + * + * 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 XBMC; see the file COPYING. If not, see + * . + * + */ +#pragma once + +namespace JOYSTICK +{ + class CDriverGeometry + { + public: + CDriverGeometry(unsigned int buttonCount, unsigned int hatCount, unsigned int axisCount); + CDriverGeometry(const CDriverGeometry& other); + + bool operator<(const CDriverGeometry& other) const; + + unsigned int ButtonCount() const { return m_buttonCount; } + unsigned int HatCount() const { return m_hatCount; } + unsigned int AxisCount() const { return m_axisCount; } + + private: + const unsigned int m_buttonCount; + const unsigned int m_hatCount; + const unsigned int m_axisCount; + }; +} diff --git a/src/buttonmapper/JoystickFamily.cpp b/src/buttonmapper/JoystickFamily.cpp new file mode 100644 index 00000000..b736a8f1 --- /dev/null +++ b/src/buttonmapper/JoystickFamily.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "JoystickFamily.h" +#include "storage/xml/JoystickFamiliesXml.h" + +using namespace JOYSTICK; + +// --- CJoystickFamily --------------------------------------------------------- + +CJoystickFamily::CJoystickFamily(const std::string& name, const std::string& provider) : + m_familyName(CJoystickFamilyManager::Get().GetFamily(name, provider)) +{ +} + +CJoystickFamily::CJoystickFamily(const CJoystickFamily& other) : + m_familyName(other.m_familyName) +{ +} + +bool CJoystickFamily::operator<(const CJoystickFamily& other) const +{ + return m_familyName < other.m_familyName; +} + +// --- CJoystickFamilyManager -------------------------------------------------- + +CJoystickFamilyManager& CJoystickFamilyManager::Get() +{ + static CJoystickFamilyManager instance; + return instance; +} + +bool CJoystickFamilyManager::Load() +{ + m_families = CJoystickFamiliesXml::LoadFamilies(); + return !m_families.empty(); +} + +const std::string& CJoystickFamilyManager::GetFamily(const std::string& name, const std::string& provider) const +{ + static std::string empty; + + for (auto it = m_families.begin(); it != m_families.end(); ++it) + { + const std::set& joystickNames = it->second; + if (joystickNames.find(name) != joystickNames.end()) + return it->first; + } + + return empty; +} diff --git a/src/buttonmapper/JoystickFamily.h b/src/buttonmapper/JoystickFamily.h new file mode 100644 index 00000000..45e80e33 --- /dev/null +++ b/src/buttonmapper/JoystickFamily.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "ButtonMapTypes.h" + +#include +#include +#include + +namespace JOYSTICK +{ + class CJoystickFamily + { + public: + CJoystickFamily(const std::string& name, const std::string& provider); + CJoystickFamily(const CJoystickFamily& other); + + bool operator<(const CJoystickFamily& other) const; + + const std::string& Name() const { return m_familyName; } + + private: + const std::string m_familyName; + }; + + class CJoystickFamilyManager + { + private: + CJoystickFamilyManager() = default; + + public: + static CJoystickFamilyManager& Get(); + + bool Load(); + + const std::string& GetFamily(const std::string& name, const std::string& provider) const; + + private: + JoystickFamilyMap m_families; + }; +} diff --git a/src/storage/xml/JoystickFamiliesXml.cpp b/src/storage/xml/JoystickFamiliesXml.cpp new file mode 100644 index 00000000..25fdde78 --- /dev/null +++ b/src/storage/xml/JoystickFamiliesXml.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "JoystickFamiliesXml.h" + +using namespace JOYSTICK; + +JoystickFamilyMap CJoystickFamiliesXml::LoadFamilies() +{ + JoystickFamilyMap result; + + // TODO + + return result; +} diff --git a/src/storage/xml/JoystickFamiliesXml.h b/src/storage/xml/JoystickFamiliesXml.h new file mode 100644 index 00000000..f55733a5 --- /dev/null +++ b/src/storage/xml/JoystickFamiliesXml.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "buttonmapper/ButtonMapTypes.h" + +namespace JOYSTICK +{ + class CJoystickFamiliesXml + { + public: + static JoystickFamilyMap LoadFamilies(); + }; +} From 1b92f2f39ad2f4c9d5936f32e71082291da76449 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Fri, 12 Aug 2016 16:58:11 -0700 Subject: [PATCH 05/28] [buttonmapper] Ignore invalid keys --- src/buttonmapper/ControllerMapper.cpp | 6 ++++-- src/buttonmapper/DriverGeometry.cpp | 7 +++++++ src/buttonmapper/DriverGeometry.h | 2 ++ src/buttonmapper/JoystickFamily.h | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/buttonmapper/ControllerMapper.cpp b/src/buttonmapper/ControllerMapper.cpp index 99b7131e..2d82e3f5 100644 --- a/src/buttonmapper/ControllerMapper.cpp +++ b/src/buttonmapper/ControllerMapper.cpp @@ -50,8 +50,10 @@ void CControllerMapper::OnAdd(const DevicePtr& driverInfo, const ButtonMap& butt // Only allow controller map items where "from" compares before "to" for (auto itFrom = buttonMap.begin(); itFrom->first < itTo->first; ++itFrom) { - AddControllerMap(familyModel, itFrom->first, itFrom->second, itTo->first, itTo->second); - AddControllerMap(geometryModel, itFrom->first, itFrom->second, itTo->first, itTo->second); + if (family.IsValid()) + AddControllerMap(familyModel, itFrom->first, itFrom->second, itTo->first, itTo->second); + if (geometry.IsValid()) + AddControllerMap(geometryModel, itFrom->first, itFrom->second, itTo->first, itTo->second); } } } diff --git a/src/buttonmapper/DriverGeometry.cpp b/src/buttonmapper/DriverGeometry.cpp index 9178bb1c..704e2efa 100644 --- a/src/buttonmapper/DriverGeometry.cpp +++ b/src/buttonmapper/DriverGeometry.cpp @@ -36,6 +36,13 @@ CDriverGeometry::CDriverGeometry(const CDriverGeometry& other) : { } +bool CDriverGeometry::IsValid() const +{ + return m_buttonCount != 0 || + m_hatCount != 0 || + m_axisCount != 0; +} + bool CDriverGeometry::operator<(const CDriverGeometry& other) const { if (m_buttonCount < other.m_buttonCount) return true; diff --git a/src/buttonmapper/DriverGeometry.h b/src/buttonmapper/DriverGeometry.h index 34a09b6d..f95eb6e6 100644 --- a/src/buttonmapper/DriverGeometry.h +++ b/src/buttonmapper/DriverGeometry.h @@ -27,6 +27,8 @@ namespace JOYSTICK CDriverGeometry(unsigned int buttonCount, unsigned int hatCount, unsigned int axisCount); CDriverGeometry(const CDriverGeometry& other); + bool IsValid() const; + bool operator<(const CDriverGeometry& other) const; unsigned int ButtonCount() const { return m_buttonCount; } diff --git a/src/buttonmapper/JoystickFamily.h b/src/buttonmapper/JoystickFamily.h index 45e80e33..081e8f49 100644 --- a/src/buttonmapper/JoystickFamily.h +++ b/src/buttonmapper/JoystickFamily.h @@ -37,6 +37,8 @@ namespace JOYSTICK const std::string& Name() const { return m_familyName; } + bool IsValid() const { return !m_familyName.empty(); } + private: const std::string m_familyName; }; From 8653627456ecfbcfcc7f0adaf867eaad0e48e896 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Fri, 12 Aug 2016 20:21:34 -0700 Subject: [PATCH 06/28] [storage] Fix button maps getting erased after ff6e116 --- src/storage/JustABunchOfFiles.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/storage/JustABunchOfFiles.cpp b/src/storage/JustABunchOfFiles.cpp index bc7de5cd..c8dcd8b0 100644 --- a/src/storage/JustABunchOfFiles.cpp +++ b/src/storage/JustABunchOfFiles.cpp @@ -69,6 +69,7 @@ bool CResources::AddResource(CButtonMap* resource) CButtonMap* oldResource = m_resources[*resource->Device()]; delete oldResource; m_resources[*resource->Device()] = resource; + m_devices[*resource->Device()] = resource->Device(); return true; } return false; From e6dd6d61e79e177838772de9650839cde1cf5597 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Mon, 15 Aug 2016 19:53:34 -0700 Subject: [PATCH 07/28] [storage] Update peripheral API: Fix broken derivation when interface knows about motors --- peripheral.joystick/addon.xml.in | 2 +- src/buttonmapper/ButtonMapper.cpp | 30 ++++++++++++++++++++++++++++-- src/buttonmapper/ButtonMapper.h | 7 +++++-- src/storage/StorageManager.cpp | 21 ++++++++++++++++----- src/storage/StorageManager.h | 15 ++++++++++----- 5 files changed, 60 insertions(+), 15 deletions(-) diff --git a/peripheral.joystick/addon.xml.in b/peripheral.joystick/addon.xml.in index ac7b7154..135d63c7 100644 --- a/peripheral.joystick/addon.xml.in +++ b/peripheral.joystick/addon.xml.in @@ -6,7 +6,7 @@ provider-name="Team-Kodi"> - + #include using namespace JOYSTICK; +CButtonMapper::CButtonMapper(ADDON::CHelper_libKODI_peripheral* peripheralLib) : + m_peripheralLib(peripheralLib) +{ +} + +CButtonMapper::~CButtonMapper() +{ +} + bool CButtonMapper::GetFeatures(const ADDON::Joystick& joystick, const std::string& strControllerId, FeatureVector& features) @@ -86,9 +96,25 @@ bool CButtonMapper::GetFeatures(const ADDON::Joystick& joystick, ButtonMap&& but if (itController != buttonMap.end()) features.swap(itController->second); - // Try to derive a button map from relations between controller profiles + bool bNeedsFeatures = false; + if (features.empty()) - DeriveFeatures(joystick, controllerId, buttonMap, features); + bNeedsFeatures = true; + + if (m_peripheralLib) + { + unsigned int featureCount = m_peripheralLib->FeatureCount(controllerId); + if (featureCount > 0) + bNeedsFeatures = (features.size() < featureCount); + } + + // Try to derive a button map from relations between controller profiles + if (bNeedsFeatures) + { + FeatureVector derivedFeatures; + DeriveFeatures(joystick, controllerId, buttonMap, derivedFeatures); + MergeFeatures(features, derivedFeatures); + } return !features.empty(); } diff --git a/src/buttonmapper/ButtonMapper.h b/src/buttonmapper/ButtonMapper.h index 385330e3..f691024a 100644 --- a/src/buttonmapper/ButtonMapper.h +++ b/src/buttonmapper/ButtonMapper.h @@ -27,6 +27,7 @@ namespace ADDON { + class CHelper_libKODI_peripheral; class Joystick; }; @@ -38,8 +39,8 @@ namespace JOYSTICK class CButtonMapper { public: - CButtonMapper() = default; - ~CButtonMapper() = default; + CButtonMapper(ADDON::CHelper_libKODI_peripheral* peripheralLib); + ~CButtonMapper(); bool GetFeatures(const ADDON::Joystick& joystick, const std::string& strDeviceId, FeatureVector& features); @@ -57,5 +58,7 @@ namespace JOYSTICK DatabaseVector m_databases; CControllerMapper m_controllerMapper; + + ADDON::CHelper_libKODI_peripheral* m_peripheralLib; }; } diff --git a/src/storage/StorageManager.cpp b/src/storage/StorageManager.cpp index 885f53d1..c6a76f3a 100644 --- a/src/storage/StorageManager.cpp +++ b/src/storage/StorageManager.cpp @@ -21,6 +21,7 @@ #include "StorageManager.h" #include "JustABunchOfFiles.h" #include "StorageUtils.h" +#include "buttonmapper/ButtonMapper.h" #include "log/Log.h" #include "storage/api/DatabaseJoystickAPI.h" //#include "storage/retroarch/DatabaseRetroarch.h" // TODO @@ -45,6 +46,11 @@ CStorageManager::CStorageManager(void) : { } +CStorageManager::~CStorageManager() +{ + Deinitialize(); +} + CStorageManager& CStorageManager::Get(void) { static CStorageManager _instance; @@ -62,6 +68,8 @@ bool CStorageManager::Initialize(ADDON::CHelper_libKODI_peripheral* peripheralLi m_peripheralLib = peripheralLib; + m_buttonMapper.reset(new CButtonMapper(peripheralLib)); + // Remove slash at end StringUtils::TrimRight(strUserPath, "\\/"); StringUtils::TrimRight(strAddonPath, "\\/"); @@ -78,21 +86,23 @@ bool CStorageManager::Initialize(ADDON::CHelper_libKODI_peripheral* peripheralLi // Ensure button map path exists in user data CStorageUtils::EnsureDirectoryExists(strUserPath); - m_databases.push_back(DatabasePtr(new CDatabaseXml(strUserPath, true, m_buttonMapper.GetCallbacks()))); + m_databases.push_back(DatabasePtr(new CDatabaseXml(strUserPath, true, m_buttonMapper->GetCallbacks()))); //m_databases.push_back(DatabasePtr(new CDatabaseRetroArch(strUserPath, true, &m_controllerMapper))); // TODO - m_databases.push_back(DatabasePtr(new CDatabaseXml(strAddonPath, false, m_buttonMapper.GetCallbacks()))); + m_databases.push_back(DatabasePtr(new CDatabaseXml(strAddonPath, false, m_buttonMapper->GetCallbacks()))); //m_databases.push_back(DatabasePtr(new CDatabaseRetroArch(strAddonPath, false))); // TODO - m_databases.push_back(DatabasePtr(new CDatabaseJoystickAPI(m_buttonMapper.GetCallbacks()))); + m_databases.push_back(DatabasePtr(new CDatabaseJoystickAPI(m_buttonMapper->GetCallbacks()))); for (auto& database : m_databases) - m_buttonMapper.RegisterDatabase(database); + m_buttonMapper->RegisterDatabase(database); return true; } void CStorageManager::Deinitialize(void) { + m_databases.clear(); + m_buttonMapper.reset(); m_peripheralLib = NULL; } @@ -100,7 +110,8 @@ bool CStorageManager::GetFeatures(const ADDON::Joystick& joystick, const std::string& strControllerId, FeatureVector& features) { - m_buttonMapper.GetFeatures(joystick, strControllerId, features); + if (m_buttonMapper) + m_buttonMapper->GetFeatures(joystick, strControllerId, features); return !features.empty(); } diff --git a/src/storage/StorageManager.h b/src/storage/StorageManager.h index f8bb7557..ada17e73 100644 --- a/src/storage/StorageManager.h +++ b/src/storage/StorageManager.h @@ -20,17 +20,22 @@ #pragma once #include "StorageTypes.h" -#include "buttonmapper/ButtonMapper.h" #include "buttonmapper/ButtonMapTypes.h" +#include #include struct PERIPHERAL_PROPERTIES; -namespace ADDON { class CHelper_libKODI_peripheral; } +namespace ADDON +{ + class CHelper_libKODI_peripheral; + class Joystick; +} namespace JOYSTICK { + class CButtonMapper; class CDevice; class IDatabase; @@ -42,7 +47,7 @@ namespace JOYSTICK public: static CStorageManager& Get(void); - ~CStorageManager(void) { Deinitialize(); } + ~CStorageManager(void); /*! * \brief Initialize storage manager @@ -107,7 +112,7 @@ namespace JOYSTICK private: ADDON::CHelper_libKODI_peripheral* m_peripheralLib; - DatabaseVector m_databases; - CButtonMapper m_buttonMapper; + DatabaseVector m_databases; + std::unique_ptr m_buttonMapper; }; } From f96654f5e01b81027d2962939a32d2418fe83fb4 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Mon, 15 Aug 2016 20:51:43 -0700 Subject: [PATCH 08/28] [api] Restore ability for interfaces to map motors --- src/api/IJoystickInterface.h | 12 ++++++++++++ src/api/JoystickManager.cpp | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/api/IJoystickInterface.h b/src/api/IJoystickInterface.h index 786d68ed..7a3f3b1e 100644 --- a/src/api/IJoystickInterface.h +++ b/src/api/IJoystickInterface.h @@ -19,6 +19,7 @@ #pragma once #include "JoystickTypes.h" +#include "buttonmapper/ButtonMapTypes.h" #include @@ -54,5 +55,16 @@ namespace JOYSTICK * \return true if the scan completed successfully, even if no results are found */ virtual bool ScanForJoysticks(JoystickVector& joysticks) = 0; + + /*! + * \brief Get the button map known to the interface + * + * \return A button map populated with hard-coded features for the interface + */ + virtual const ButtonMap& GetButtonMap() + { + static ButtonMap empty; + return empty; + } }; } diff --git a/src/api/JoystickManager.cpp b/src/api/JoystickManager.cpp index 9a2f3aa5..d611bd7f 100644 --- a/src/api/JoystickManager.cpp +++ b/src/api/JoystickManager.cpp @@ -252,7 +252,13 @@ const ButtonMap& CJoystickManager::GetButtonMap(const std::string& provider) { static ButtonMap empty; - // TODO + CLockObject lock(m_interfacesMutex); + + for (auto interface : m_interfaces) + { + if (interface->Name() == provider) + return interface->GetButtonMap(); + } return empty; } From 50f1ea4348ee20fadaeb876d66405b7979e9c798 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Mon, 15 Aug 2016 18:28:15 -0700 Subject: [PATCH 09/28] [udev] Add udev implementation udev is now the default implementation on linux. If udev is not found at build time, SDL will be used. If SDL is not available, the deprecated Linux Joystick API will be used. --- CMakeLists.txt | 14 + cmake/Findudev.cmake | 77 ++++++ src/api/Joystick.cpp | 5 +- src/api/JoystickManager.cpp | 12 +- src/api/JoystickTypes.h | 1 + src/api/udev/JoystickInterfaceUdev.cpp | 129 ++++++++++ src/api/udev/JoystickInterfaceUdev.h | 69 +++++ src/api/udev/JoystickUdev.cpp | 339 +++++++++++++++++++++++++ src/api/udev/JoystickUdev.h | 104 ++++++++ 9 files changed, 746 insertions(+), 4 deletions(-) create mode 100644 cmake/Findudev.cmake create mode 100644 src/api/udev/JoystickInterfaceUdev.cpp create mode 100644 src/api/udev/JoystickInterfaceUdev.h create mode 100644 src/api/udev/JoystickUdev.cpp create mode 100644 src/api/udev/JoystickUdev.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b35c442f..b7269df7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,20 @@ if("${CORE_SYSTEM_NAME}" STREQUAL "windows") src/api/xinput/XInputDLL.cpp) endif() +# --- udev --------------------------------------------------------------------- + +find_package(udev REQUIRED) +if(UDEV_FOUND) + include_directories(${UDEV_INCLUDE_DIRS}) + + add_definitions(-DHAVE_UDEV) + + list(APPEND JOYSTICK_SOURCES src/api/udev/JoystickInterfaceUdev.cpp + src/api/udev/JoystickUdev.cpp) + + list(APPEND DEPLIBS ${UDEV_LIBRARIES}) +endif() + # ------------------------------------------------------------------------------ build_addon(peripheral.joystick JOYSTICK DEPLIBS) diff --git a/cmake/Findudev.cmake b/cmake/Findudev.cmake new file mode 100644 index 00000000..ce454d57 --- /dev/null +++ b/cmake/Findudev.cmake @@ -0,0 +1,77 @@ +# - try to find the udev library +# +# Cache Variables: (probably not for direct use in your scripts) +# UDEV_INCLUDE_DIR +# UDEV_SOURCE_DIR +# UDEV_LIBRARY +# +# Non-cache variables you might use in your CMakeLists.txt: +# UDEV_FOUND +# UDEV_INCLUDE_DIRS +# UDEV_LIBRARIES +# +# Requires these CMake modules: +# FindPackageHandleStandardArgs (known included with CMake >=2.6.2) +# +# Original Author: +# 2014 Kevin M. Godby +# +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(UDEV_ROOT_DIR + "${UDEV_ROOT_DIR}" + CACHE + PATH + "Directory to search for udev") + +find_package(PkgConfig QUIET) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_LIBUDEV libudev) +endif() + +find_library(UDEV_LIBRARY + NAMES + udev + PATHS + ${PC_LIBUDEV_LIBRARY_DIRS} + ${PC_LIBUDEV_LIBDIR} + HINTS + "${UDEV_ROOT_DIR}" + PATH_SUFFIXES + lib + ) + +get_filename_component(_libdir "${UDEV_LIBRARY}" PATH) + +find_path(UDEV_INCLUDE_DIR + NAMES + libudev.h + PATHS + ${PC_LIBUDEV_INCLUDE_DIRS} + ${PC_LIBUDEV_INCLUDEDIR} + HINTS + "${_libdir}" + "${_libdir}/.." + "${UDEV_ROOT_DIR}" + PATH_SUFFIXES + include + ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(UDEV + DEFAULT_MSG + UDEV_LIBRARY + UDEV_INCLUDE_DIR + ) + +if(UDEV_FOUND) + list(APPEND UDEV_LIBRARIES ${UDEV_LIBRARY}) + list(APPEND UDEV_INCLUDE_DIRS ${UDEV_INCLUDE_DIR}) + mark_as_advanced(UDEV_ROOT_DIR) +endif() + +mark_as_advanced(UDEV_INCLUDE_DIR + UDEV_LIBRARY) + diff --git a/src/api/Joystick.cpp b/src/api/Joystick.cpp index d8bbf39e..f9e82089 100644 --- a/src/api/Joystick.cpp +++ b/src/api/Joystick.cpp @@ -207,7 +207,10 @@ void CJoystick::SetAxisValue(unsigned int axisIndex, JOYSTICK_STATE_AXIS axisVal void CJoystick::SetAxisValue(unsigned int axisIndex, long value, long maxAxisAmount) { - SetAxisValue(axisIndex, (float)value / (float)maxAxisAmount); + if (maxAxisAmount != 0) + SetAxisValue(axisIndex, (float)value / (float)maxAxisAmount); + else + SetAxisValue(axisIndex, 0.0f); } void CJoystick::UpdateTimers(void) diff --git a/src/api/JoystickManager.cpp b/src/api/JoystickManager.cpp index d611bd7f..db9d580d 100644 --- a/src/api/JoystickManager.cpp +++ b/src/api/JoystickManager.cpp @@ -36,6 +36,9 @@ #if defined(HAVE_COCOA) #include "cocoa/JoystickInterfaceCocoa.h" #endif +#if defined(HAVE_UDEV) + #include "udev/JoystickInterfaceUdev.h" +#endif #include "log/Log.h" #include "utils/CommonMacros.h" @@ -107,10 +110,12 @@ bool CJoystickManager::Initialize(IScannerCallback* scanner) #if defined(HAVE_XINPUT) m_interfaces.push_back(new CJoystickInterfaceXInput); #endif -#if defined(HAVE_LINUX_JOYSTICK) - m_interfaces.push_back(new CJoystickInterfaceLinux); +#if defined(HAVE_UDEV) + m_interfaces.push_back(new CJoystickInterfaceUdev); #elif defined(HAVE_SDL) m_interfaces.push_back(new CJoystickInterfaceSDL); +#elif defined(HAVE_LINUX_JOYSTICK) + m_interfaces.push_back(new CJoystickInterfaceLinux); #endif #if defined(HAVE_COCOA) m_interfaces.push_back(new CJoystickInterfaceCocoa); @@ -191,7 +196,8 @@ bool CJoystickManager::PerformJoystickScan(JoystickVector& joysticks) joysticks.erase(std::remove_if(joysticks.begin(), joysticks.end(), [](const JoystickPtr& joystick) { - return joystick->Provider() == INTERFACE_LINUX && + return (joystick->Provider() == INTERFACE_LINUX || + joystick->Provider() == INTERFACE_UDEV) && (joystick->Name() == "Xbox 360 Wireless Receiver" || joystick->Name() == "Xbox 360 Wireless Receiver (XBOX)") && joystick->ActivateTimeMs() < 0; diff --git a/src/api/JoystickTypes.h b/src/api/JoystickTypes.h index f420629c..cba94dc0 100644 --- a/src/api/JoystickTypes.h +++ b/src/api/JoystickTypes.h @@ -25,6 +25,7 @@ #define INTERFACE_DIRECTINPUT "directinput" #define INTERFACE_LINUX "linux" #define INTERFACE_SDL "sdl" +#define INTERFACE_UDEV "udev" #define INTERFACE_XINPUT "xinput" namespace JOYSTICK diff --git a/src/api/udev/JoystickInterfaceUdev.cpp b/src/api/udev/JoystickInterfaceUdev.cpp new file mode 100644 index 00000000..4aedf988 --- /dev/null +++ b/src/api/udev/JoystickInterfaceUdev.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "JoystickInterfaceUdev.h" +#include "JoystickUdev.h" +#include "api/JoystickTypes.h" + +#include +#include + +using namespace JOYSTICK; + +ButtonMap CJoystickInterfaceUdev::m_buttonMap = { + std::make_pair("game.controller.default", FeatureVector{ + ADDON::JoystickFeature("leftmotor", JOYSTICK_FEATURE_TYPE_MOTOR), + ADDON::JoystickFeature("rightmotor", JOYSTICK_FEATURE_TYPE_MOTOR), + }), + std::make_pair("game.controller.ps", FeatureVector{ + ADDON::JoystickFeature("strongmotor", JOYSTICK_FEATURE_TYPE_MOTOR), + ADDON::JoystickFeature("weakmotor", JOYSTICK_FEATURE_TYPE_MOTOR), + }), +}; + +CJoystickInterfaceUdev::CJoystickInterfaceUdev() : + m_udev(nullptr), + m_udev_mon(nullptr) +{ +} + +const char* CJoystickInterfaceUdev::Name() const +{ + return INTERFACE_UDEV; +} + +bool CJoystickInterfaceUdev::Initialize() +{ + m_udev = udev_new(); + if (!m_udev) + return false; + + m_udev_mon = udev_monitor_new_from_netlink(m_udev, "udev"); + if (m_udev_mon) + { + udev_monitor_filter_add_match_subsystem_devtype(m_udev_mon, "input", nullptr); + udev_monitor_enable_receiving(m_udev_mon); + } + + return true; +} + +void CJoystickInterfaceUdev::Deinitialize() +{ + if (m_udev_mon) + { + udev_monitor_unref(m_udev_mon); + m_udev_mon = nullptr; + } + + if (m_udev) + { + udev_unref(m_udev); + m_udev = nullptr; + } +} + +bool CJoystickInterfaceUdev::ScanForJoysticks(JoystickVector& joysticks) +{ + if (!m_udev) + return false; + + struct udev_enumerate* enumerate = udev_enumerate_new(m_udev); + if (enumerate == nullptr) + { + Deinitialize(); + return false; + } + + udev_enumerate_add_match_property(enumerate, "ID_INPUT_JOYSTICK", "1"); + udev_enumerate_scan_devices(enumerate); + + struct udev_list_entry* devs = udev_enumerate_get_list_entry(enumerate); + for (struct udev_list_entry* item = devs; item != nullptr; item = udev_list_entry_get_next(item)) + { + const char* name = udev_list_entry_get_name(item); + struct udev_device* dev = udev_device_new_from_syspath(m_udev, name); + const char* devnode = udev_device_get_devnode(dev); + + if (devnode != nullptr) + { + JoystickPtr joystick = JoystickPtr(new CJoystickUdev(dev, devnode)); + joysticks.push_back(joystick); + } + + udev_device_unref(dev); + } + + udev_enumerate_unref(enumerate); + return true; +} + +const ButtonMap& CJoystickInterfaceUdev::GetButtonMap() +{ + auto& dflt = m_buttonMap["game.controller.default"]; + dflt[CJoystickUdev::MOTOR_STRONG].SetPrimitive(ADDON::DriverPrimitive::CreateMotor(CJoystickUdev::MOTOR_STRONG)); + dflt[CJoystickUdev::MOTOR_WEAK].SetPrimitive(ADDON::DriverPrimitive::CreateMotor(CJoystickUdev::MOTOR_WEAK)); + + auto& ps = m_buttonMap["game.controller.ps"]; + ps[CJoystickUdev::MOTOR_STRONG].SetPrimitive(ADDON::DriverPrimitive::CreateMotor(CJoystickUdev::MOTOR_STRONG)); + ps[CJoystickUdev::MOTOR_WEAK].SetPrimitive(ADDON::DriverPrimitive::CreateMotor(CJoystickUdev::MOTOR_WEAK)); + + return m_buttonMap; +} diff --git a/src/api/udev/JoystickInterfaceUdev.h b/src/api/udev/JoystickInterfaceUdev.h new file mode 100644 index 00000000..14193e99 --- /dev/null +++ b/src/api/udev/JoystickInterfaceUdev.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +/* + * This is a derivative work based on udev_joypad.c in the RetroArch project. + */ + +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2015 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#include "api/IJoystickInterface.h" + +struct udev; +struct udev_device; +struct udev_monitor; + +namespace JOYSTICK +{ + class CJoystickInterfaceUdev : public IJoystickInterface + { + public: + CJoystickInterfaceUdev(); + virtual ~CJoystickInterfaceUdev() { Deinitialize(); } + + // implementation of IJoystickInterface + virtual const char* Name() const override; + virtual bool Initialize() override; + virtual void Deinitialize() override; + virtual bool ScanForJoysticks(JoystickVector& joysticks) override; + virtual const ButtonMap& GetButtonMap() override; + + private: + udev* m_udev; + udev_monitor* m_udev_mon; + + static ButtonMap m_buttonMap; + }; +} diff --git a/src/api/udev/JoystickUdev.cpp b/src/api/udev/JoystickUdev.cpp new file mode 100644 index 00000000..128f2304 --- /dev/null +++ b/src/api/udev/JoystickUdev.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ + +#include "JoystickUdev.h" +#include "api/JoystickTypes.h" +#include "log/Log.h" + +#include +#include +#include +#include +#include +#include + +using namespace JOYSTICK; + +#ifndef INVALID_FD + #define INVALID_FD (-1) +#endif + +// From RetroArch +#define test_bit(nr, addr) \ + (((1UL << ((nr) % (sizeof(long) * CHAR_BIT))) & ((addr)[(nr) / (sizeof(long) * CHAR_BIT)])) != 0) + +// From RetroArch +#define NBITS(x) ((((x) - 1) / (sizeof(long) * CHAR_BIT)) + 1) + +// From RetroArch +static inline int16_t compute_axis(const input_absinfo& info, int value) +{ + int range = info.maximum - info.minimum; + int axis = (value - info.minimum) * 0xffffll / range - 0x7fffll; + + if (axis > 0x7fff) + return 0x7fff; + else if (axis < -0x7fff) + return -0x7fff; + return axis; +} + +CJoystickUdev::CJoystickUdev(udev_device* dev, const char* path) + : CJoystick(INTERFACE_UDEV), + m_dev(dev), + m_path(path), + m_deviceNumber(0), + m_fd(INVALID_FD), + m_bInitialized(false), + m_has_set_ff(false), + m_effect(-1) +{ + // Must initialize in the constructor to fill out joystick properties + Initialize(); +} + +bool CJoystickUdev::Equals(const CJoystick* rhs) const +{ + const CJoystickUdev* rhsUdev = dynamic_cast(rhs); + if (rhsUdev == nullptr) + return false; + + return m_deviceNumber == rhsUdev->m_deviceNumber; +} + +bool CJoystickUdev::Initialize(void) +{ + if (!m_bInitialized) + { + if (!OpenJoystick()) + return false; + + if (!GetProperties()) + return false; + + if (!CJoystick::Initialize()) + return false; + + m_bInitialized = true; + } + + return m_bInitialized; +} + +void CJoystickUdev::Deinitialize(void) +{ + if (m_fd >= 0) + { + close(m_fd); + m_fd = INVALID_FD; + } + + CJoystick::Deinitialize(); +} + +bool CJoystickUdev::ScanEvents(void) +{ + input_event events[32]; + + if (m_fd < 0) + return false; + + int len; + while ((len = read(m_fd, events, sizeof(events))) > 0) + { + len /= sizeof(*events); + for (unsigned int i = 0; i < static_cast(len); i++) + { + const input_event& event = events[i]; + + int code = event.code; + + switch (event.type) + { + case EV_KEY: + { + if (code >= BTN_MISC || (code >= KEY_UP && code <= KEY_DOWN)) + { + auto it = m_button_bind.find(code); + if (it != m_button_bind.end()) + { + const unsigned int buttonIndex = it->second; + SetButtonValue(buttonIndex, event.value ? JOYSTICK_STATE_BUTTON_PRESSED : JOYSTICK_STATE_BUTTON_UNPRESSED); + } + } + break; + } + case EV_ABS: + { + if (code < ABS_MISC) + { + auto it = m_axes_bind.find(code); + if (it != m_axes_bind.end()) + { + const unsigned int axisIndex = it->second.axisIndex; + const input_absinfo& info = it->second.axisInfo; + + if (event.value >= 0) + SetAxisValue(axisIndex, event.value, info.maximum); + else + SetAxisValue(axisIndex, event.value, -info.minimum); + } + } + break; + } + default: + break; + } + } + } + + return true; +} + +bool CJoystickUdev::OpenJoystick() +{ + unsigned long evbit[NBITS(EV_MAX)] = { }; + unsigned long keybit[NBITS(KEY_MAX)] = { }; + unsigned long absbit[NBITS(ABS_MAX)] = { }; + + m_fd = open(m_path.c_str(), O_RDWR | O_NONBLOCK); + + if (m_fd < 0) + return false; + + if (ioctl(m_fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) + return false; + + // Has to at least support EV_KEY interface + if (!test_bit(EV_KEY, evbit)) + return false; + + return true; +} + +bool CJoystickUdev::GetProperties() +{ + unsigned long keybit[NBITS(KEY_MAX)] = { }; + unsigned long absbit[NBITS(ABS_MAX)] = { }; + unsigned long ffbit[NBITS(FF_MAX)] = { }; + + char name[64] = { }; + if (ioctl(m_fd, EVIOCGNAME(sizeof(name)), name) < 0) + { + esyslog("[udev]: Failed to get pad name"); + return false; + } + SetName(name); + + // Don't worry about unref'ing the parent + struct udev_device* parent = udev_device_get_parent_with_subsystem_devtype(m_dev, "usb", "usb_device"); + + const char* buf; + if ((buf = udev_device_get_sysattr_value(parent, "idVendor")) != nullptr) + SetVendorID(strtol(buf, NULL, 16)); + + if ((buf = udev_device_get_sysattr_value(parent, "idProduct")) != nullptr) + SetProductID(strtol(buf, NULL, 16)); + + struct stat st; + if (fstat(m_fd, &st) < 0) + { + esyslog("[udev]: Failed to add pad: %s", m_path.c_str()); + return false; + } + m_deviceNumber = st.st_rdev; + + if ((ioctl(m_fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || + (ioctl(m_fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) + { + esyslog("[udev]: Failed to add pad: %s", m_path.c_str()); + return false; + } + + // Go through all possible keycodes, check if they are used, and map them to + // button/axes/hat indices + unsigned int buttons = 0; + for (unsigned int i = KEY_UP; i <= KEY_DOWN; i++) + { + if (test_bit(i, keybit)) + m_button_bind[i] = buttons++; + } + for (unsigned int i = BTN_MISC; i < KEY_MAX; i++) + { + if (test_bit(i, keybit)) + m_button_bind[i] = buttons++; + } + SetButtonCount(m_button_bind.size()); + + unsigned int axes = 0; + for (unsigned i = 0; i < ABS_MISC; i++) + { + if (test_bit(i, absbit)) + { + input_absinfo abs; + if (ioctl(m_fd, EVIOCGABS(i), &abs) < 0) + continue; + + if (abs.maximum > abs.minimum) + m_axes_bind[i] = { axes++, abs }; + } + } + SetAxisCount(m_axes_bind.size()); + + // Check for rumble features + if (ioctl(m_fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) + { + unsigned int num_effects; + if (ioctl(m_fd, EVIOCGEFFECTS, &num_effects) >= 0) + SetMotorCount(std::min(num_effects, static_cast(MOTOR_COUNT))); + } + + return true; +} + +bool CJoystickUdev::SetMotor(unsigned int motorIndex, float magnitude) +{ + if (!m_bInitialized) + return false; + + if (motorIndex >= MotorCount() || magnitude < 0.0f) + return false; + + uint16_t strength = std::min(0xffff, static_cast(magnitude * 0xffff)); + + if (strength < 0.01f) + strength = 0.0f; + + bool bChanged = false; + + if (strength != m_motors[motorIndex].strength) + bChanged = true; + + if (!bChanged) + return true; + + uint32_t oldStrength = m_motors[MOTOR_STRONG].strength + m_motors[MOTOR_WEAK].strength; + + m_motors[motorIndex].strength = strength; + + uint32_t newStrength = m_motors[MOTOR_STRONG].strength + m_motors[MOTOR_WEAK].strength; + + if (newStrength > 0) + { + // Create new or update old playing state + struct ff_effect e = { }; + + int old_effect = m_has_set_ff ? m_effect : -1; + + e.type = FF_RUMBLE; + e.id = old_effect; + e.u.rumble.strong_magnitude = m_motors[MOTOR_STRONG].strength; + e.u.rumble.weak_magnitude = m_motors[MOTOR_WEAK].strength; + + if (ioctl(m_fd, EVIOCSFF, &e) < 0) + { + esyslog("Failed to set rumble effect on \"%s\"", Name().c_str()); + return false; + } + + m_effect = e.id; + m_has_set_ff = true; + } + + bool bWasPlaying = !!oldStrength; + bool bIsPlaying = !!newStrength; + + if (bWasPlaying != bIsPlaying) + { + struct input_event play = { { } }; + + play.type = EV_FF; + play.code = m_effect; + play.value = bIsPlaying; + + if (write(m_fd, &play, sizeof(play)) < (ssize_t)sizeof(play)) + { + esyslog("[udev]: Failed to play rumble effect on \"%s\"", Name().c_str()); + return false; + } + } + + return true; +} diff --git a/src/api/udev/JoystickUdev.h b/src/api/udev/JoystickUdev.h new file mode 100644 index 00000000..ad7492ab --- /dev/null +++ b/src/api/udev/JoystickUdev.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +/* + * Derived from udev_joypad.c in the RetroArch project. + */ + +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2015 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#include "api/Joystick.h" + +#include +#include + +struct udev_device; + +namespace JOYSTICK +{ + class CJoystickUdev : public CJoystick + { + public: + enum + { + MOTOR_STRONG = 0, + MOTOR_WEAK = 1, + MOTOR_COUNT = 2, + }; + + CJoystickUdev(udev_device* dev, const char* path); + virtual ~CJoystickUdev(void) { Deinitialize(); } + + // implementation of CJoystick + virtual bool Equals(const CJoystick* rhs) const override; + virtual bool Initialize(void) override; + virtual void Deinitialize(void) override; + + protected: + // implementation of CJoystick + virtual bool ScanEvents(void) override; + bool SetMotor(unsigned int motorIndex, float magnitude); + + private: + struct Motor + { + uint16_t strength; + uint16_t configured_strength; + }; + + struct Axis + { + unsigned int axisIndex; + input_absinfo axisInfo; + }; + + bool OpenJoystick(); + bool GetProperties(); + + // Udev properties + udev_device* m_dev; + std::string m_path; + dev_t m_deviceNumber; + int m_fd; + bool m_bInitialized; + bool m_has_set_ff; + int m_effect; + + // Joystick properties + std::map m_button_bind; // Maps keycodes -> button + std::map m_axes_bind; // Maps keycodes -> axis and axis info + Motor m_motors[MOTOR_COUNT]; + }; +} From ce0cab7cc99e94ab5bd16f91e92b3cc567503743 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Tue, 16 Aug 2016 09:56:58 -0700 Subject: [PATCH 10/28] [buttonmapper] Add joystick families from Kodi's old joystick keymaps --- CMakeLists.txt | 2 +- .../joystickfamilies/joystickfamilies.xml | 133 ++++++++++++++++++ src/buttonmapper/ButtonMapper.cpp | 23 ++- src/buttonmapper/ButtonMapper.h | 14 +- ...erMapper.cpp => ControllerTransformer.cpp} | 37 +++-- ...rollerMapper.h => ControllerTransformer.h} | 16 ++- src/buttonmapper/JoystickFamily.cpp | 16 ++- src/buttonmapper/JoystickFamily.h | 19 +-- src/storage/JustABunchOfFiles.cpp | 2 +- src/storage/StorageDefinitions.h | 29 ++++ src/storage/StorageManager.cpp | 24 ++-- src/storage/StorageManager.h | 2 + src/storage/{ => xml}/ButtonMapDefinitions.h | 10 +- src/storage/xml/ButtonMapXml.cpp | 8 +- src/storage/xml/DatabaseXml.cpp | 2 +- src/storage/xml/DeviceXml.cpp | 6 +- src/storage/xml/JoystickFamiliesXml.cpp | 82 ++++++++++- src/storage/xml/JoystickFamiliesXml.h | 8 +- src/storage/xml/JoystickFamilyDefinitions.h | 29 ++++ 19 files changed, 390 insertions(+), 72 deletions(-) create mode 100644 peripheral.joystick/resources/joystickfamilies/joystickfamilies.xml rename src/buttonmapper/{ControllerMapper.cpp => ControllerTransformer.cpp} (79%) rename src/buttonmapper/{ControllerMapper.h => ControllerTransformer.h} (83%) create mode 100644 src/storage/StorageDefinitions.h rename src/storage/{ => xml}/ButtonMapDefinitions.h (86%) create mode 100644 src/storage/xml/JoystickFamilyDefinitions.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b7269df7..17046915 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,8 +31,8 @@ set(JOYSTICK_SOURCES src/addon.cpp src/api/PeripheralScanner.cpp src/buttonmapper/ButtonMapper.cpp src/buttonmapper/ButtonMapTranslator.cpp - src/buttonmapper/ControllerMapper.cpp src/buttonmapper/ControllerModel.cpp + src/buttonmapper/ControllerTransformer.cpp src/buttonmapper/DriverGeometry.cpp src/buttonmapper/JoystickFamily.cpp src/filesystem/DirectoryCache.cpp diff --git a/peripheral.joystick/resources/joystickfamilies/joystickfamilies.xml b/peripheral.joystick/resources/joystickfamilies/joystickfamilies.xml new file mode 100644 index 00000000..8be3eeeb --- /dev/null +++ b/peripheral.joystick/resources/joystickfamilies/joystickfamilies.xml @@ -0,0 +1,133 @@ + + + + Alienware Alienware Dual Compatible Game Pad + + + AppleRemote + + + Harmony + + + STD AxisPad + Interact AxisPad + + + Logitech Logitech Cordless RumblePad 2 + Logitech Cordless RumblePad 2 + Logitech RumblePad 2 USB + + + Microsoft X-Box 360 pad + Xbox 360 Wireless Receiver (XBOX) + Xbox 360 Wireless Receiver + Xbox Gamepad (userspace driver) + Thrustmaster Gamepad GP XID + Logitech Gamepad F310 + Logitech Gamepad F510 + Logitech Gamepad F710 + Generic X-Box pad + Logitech Chillstream Controller + Mad Catz Wired Xbox 360 Controller + Mad Catz Street Fighter IV FightStick SE + Mad Catz Xbox 360 Controller + Mad Catz Street Fighter IV FightPad + Mad Catz Wired Xbox 360 Controller (SFIV) + Mad Catz Beat Pad + Mad Catz Xbox controller - MW2 + Mad Catz JOYTECH NEO SE Advanced GamePad + Saitek Cyborg Rumble Pad - PC/Xbox 360 + Saitek P3200 Rumble Pad - PC/Xbox 360 + Super SFIV FightStick TE S + HSM3 Xbox360 dancepad + Afterglow AX.1 Gamepad for Xbox 360 + Pelican PL-3601 'TSZ' Wired Xbox 360 Controller + Afterglow Gamepad for Xbox 360 + Rock Candy Gamepad for Xbox 360 + Logic3 Controller + Logic3 Controller + Hori Fighting Stick EX2 + Hori Real Arcade Pro.EX + Honey Bee Xbox360 dancepad + PDP AFTERGLOW AX.1 + RedOctane Guitar Hero X-plorer + BigBen Interactive XBOX 360 Controller + Razer Sabertooth + Power A Mini Pro Elite + Xbox Airflo wired controller + Batarang Xbox 360 controller + Joytech Neo-Se Take2 + Razer Onza Tournament Edition + Razer Onza Classic Edition + Razer Sabertooth + Harmonix Rock Band Guitar + Harmonix Rock Band Drumkit + Mad Catz Xbox 360 Controller + MLG Pro Circuit Controller (Xbox) + Street Fighter IV FightPad + Street Fighter IV FightStick TE + Harmonix Xbox 360 Controller + Gamestop Xbox 360 Controller + Tron Xbox 360 controller + Razer Atrox Arcade Stick + PowerA MINI PROEX Controller + Xbox Airflo wired controller + Hori XBOX 360 EX 2 with Turbo + Hori Real Arcade Pro VX-SA + Hori SOULCALIBUR V Stick + Thrustmaster, Inc. GPX Controller + Thrustmaster Ferrari 458 Racing Wheel + Microsoft X-Box One pad + + + Afterglow Gamepad for Xbox 360 (Controller) + Controller (Gamepad F310) + Controller (Gamepad for Xbox 360) + Controller (Rumble Gamepad F510) + Controller (Wireless Gamepad F710) + Controller (Xbox 360 Wireless Receiver for Windows) + Controller (Xbox wireless receiver for windows) + Controller (XBOX360 GAMEPAD) + Controller (Batarang wired controller (XBOX)) + Wireless Gamepad F710 (Controller) + XBOX 360 For Windows + XBOX 360 For Windows (Controller) + Xbox 360 Wireless Controller + Xbox Receiver for Windows (Wireless Controller) + Xbox wireless receiver for windows (Controller) + Gamepad F310 (Controller) + Razer Sabertooth Elite (Controller) + Controller (Razer Sabertooth Elite) + Controller (XBOX One For Windows) + + + Microsoft Xbox Controller S + Mad Catz MicroCON + Logitech Xbox Cordless Controller + Microsoft X-Box pad (Japan) + + + WiiRemote + + + Nintendo Wii Remote Pro Controller + + + OUYA Game Controller + + + PLAYSTATION(R)3 Controller + PS3 Controller + Sony Computer Entertainment Wireless Controller + + + PLAYSTATION(R)3 Remote Keyboard + PS3 Remote Keyboard + MoSart PS3 Remote Keyboard + + + Wireless Controller + + + diff --git a/src/buttonmapper/ButtonMapper.cpp b/src/buttonmapper/ButtonMapper.cpp index 80b90aa8..71476818 100644 --- a/src/buttonmapper/ButtonMapper.cpp +++ b/src/buttonmapper/ButtonMapper.cpp @@ -19,6 +19,7 @@ */ #include "ButtonMapper.h" +#include "ControllerTransformer.h" #include "storage/IDatabase.h" #include "kodi_peripheral_utils.hpp" @@ -38,6 +39,23 @@ CButtonMapper::~CButtonMapper() { } +bool CButtonMapper::Initialize(CJoystickFamilyManager& familyManager) +{ + m_controllerTransformer.reset(new CControllerTransformer(familyManager)); + return true; +} + +void CButtonMapper::Deinitialize() +{ + m_controllerTransformer.reset(); + m_databases.clear(); +} + +IDatabaseCallbacks* CButtonMapper::GetCallbacks() +{ + return m_controllerTransformer.get(); +} + bool CButtonMapper::GetFeatures(const ADDON::Joystick& joystick, const std::string& strControllerId, FeatureVector& features) @@ -121,6 +139,9 @@ bool CButtonMapper::GetFeatures(const ADDON::Joystick& joystick, ButtonMap&& but void CButtonMapper::DeriveFeatures(const ADDON::Joystick& joystick, const std::string& toController, const ButtonMap& buttonMap, FeatureVector& transformedFeatures) { + if (!m_controllerTransformer) + return; + // Obtain an iterator to the controller profile with the highest count of features defined unsigned int maxFeatures = 0; auto maxFeaturesIt = buttonMap.end(); @@ -141,7 +162,7 @@ void CButtonMapper::DeriveFeatures(const ADDON::Joystick& joystick, const std::s const std::string& fromController = maxFeaturesIt->first; const FeatureVector& features = maxFeaturesIt->second; - m_controllerMapper.TransformFeatures(joystick, fromController, toController, features, transformedFeatures); + m_controllerTransformer->TransformFeatures(joystick, fromController, toController, features, transformedFeatures); } } diff --git a/src/buttonmapper/ButtonMapper.h b/src/buttonmapper/ButtonMapper.h index f691024a..e73cca2d 100644 --- a/src/buttonmapper/ButtonMapper.h +++ b/src/buttonmapper/ButtonMapper.h @@ -20,9 +20,9 @@ #pragma once #include "ButtonMapTypes.h" -#include "ControllerMapper.h" #include "storage/StorageTypes.h" +#include #include namespace ADDON @@ -33,7 +33,8 @@ namespace ADDON namespace JOYSTICK { - class IDatabase; + class CControllerTransformer; + class CJoystickFamilyManager; class IDatabaseCallbacks; class CButtonMapper @@ -42,9 +43,12 @@ namespace JOYSTICK CButtonMapper(ADDON::CHelper_libKODI_peripheral* peripheralLib); ~CButtonMapper(); - bool GetFeatures(const ADDON::Joystick& joystick, const std::string& strDeviceId, FeatureVector& features); + bool Initialize(CJoystickFamilyManager& familyManager); + void Deinitialize(); + + IDatabaseCallbacks* GetCallbacks(); - IDatabaseCallbacks* GetCallbacks() { return &m_controllerMapper; } + bool GetFeatures(const ADDON::Joystick& joystick, const std::string& strDeviceId, FeatureVector& features); void RegisterDatabase(const DatabasePtr& database); void UnregisterDatabase(const DatabasePtr& database); @@ -57,7 +61,7 @@ namespace JOYSTICK void DeriveFeatures(const ADDON::Joystick& joystick, const std::string& toController, const ButtonMap& buttonMap, FeatureVector& transformedFeatures); DatabaseVector m_databases; - CControllerMapper m_controllerMapper; + std::unique_ptr m_controllerTransformer; ADDON::CHelper_libKODI_peripheral* m_peripheralLib; }; diff --git a/src/buttonmapper/ControllerMapper.cpp b/src/buttonmapper/ControllerTransformer.cpp similarity index 79% rename from src/buttonmapper/ControllerMapper.cpp rename to src/buttonmapper/ControllerTransformer.cpp index 2d82e3f5..d7dff96c 100644 --- a/src/buttonmapper/ControllerMapper.cpp +++ b/src/buttonmapper/ControllerTransformer.cpp @@ -18,7 +18,7 @@ * */ -#include "ControllerMapper.h" +#include "ControllerTransformer.h" #include "storage/Device.h" #include "kodi_peripheral_utils.hpp" @@ -29,7 +29,16 @@ using namespace JOYSTICK; -void CControllerMapper::OnAdd(const DevicePtr& driverInfo, const ButtonMap& buttonMap) +CControllerTransformer::CControllerTransformer(CJoystickFamilyManager& familyManager) : + m_familyManager(familyManager) +{ +} + +CControllerTransformer::~CControllerTransformer() +{ +} + +void CControllerTransformer::OnAdd(const DevicePtr& driverInfo, const ButtonMap& buttonMap) { // Skip devices we've already encountered. if (m_observedDevices.find(driverInfo) == m_observedDevices.end()) @@ -37,7 +46,9 @@ void CControllerMapper::OnAdd(const DevicePtr& driverInfo, const ButtonMap& butt else return; - CJoystickFamily family(driverInfo->Name(), driverInfo->Provider()); + const std::string& familyName = m_familyManager.GetFamily(driverInfo->Name(), driverInfo->Provider()); + + CJoystickFamily family(familyName); CDriverGeometry geometry(driverInfo->ButtonCount(), driverInfo->HatCount(), driverInfo->AxisCount()); @@ -58,9 +69,9 @@ void CControllerMapper::OnAdd(const DevicePtr& driverInfo, const ButtonMap& butt } } -bool CControllerMapper::AddControllerMap(CControllerModel& model, - const std::string& controllerFrom, const FeatureVector& featuresFrom, - const std::string& controllerTo, const FeatureVector& featuresTo) +bool CControllerTransformer::AddControllerMap(CControllerModel& model, + const std::string& controllerFrom, const FeatureVector& featuresFrom, + const std::string& controllerTo, const FeatureVector& featuresTo) { bool bChanged = false; @@ -121,18 +132,20 @@ bool CControllerMapper::AddControllerMap(CControllerModel& model, return bChanged; } -void CControllerMapper::TransformFeatures(const ADDON::Joystick& driverInfo, - const std::string& fromController, - const std::string& toController, - const FeatureVector& features, - FeatureVector& transformedFeatures) +void CControllerTransformer::TransformFeatures(const ADDON::Joystick& driverInfo, + const std::string& fromController, + const std::string& toController, + const FeatureVector& features, + FeatureVector& transformedFeatures) { bool bSwap = (fromController >= toController); ControllerMapItem needle = { bSwap ? toController : fromController, bSwap ? fromController : toController }; - CJoystickFamily family(driverInfo.Name(), driverInfo.Provider()); + const std::string& familyName = m_familyManager.GetFamily(driverInfo.Name(), driverInfo.Provider()); + + CJoystickFamily family(familyName); CDriverGeometry geometry(driverInfo.ButtonCount(), driverInfo.HatCount(), driverInfo.AxisCount()); diff --git a/src/buttonmapper/ControllerMapper.h b/src/buttonmapper/ControllerTransformer.h similarity index 83% rename from src/buttonmapper/ControllerMapper.h rename to src/buttonmapper/ControllerTransformer.h index b8850316..ceb35a7a 100644 --- a/src/buttonmapper/ControllerMapper.h +++ b/src/buttonmapper/ControllerTransformer.h @@ -33,11 +33,14 @@ namespace ADDON namespace JOYSTICK { - class CControllerMapper : public IDatabaseCallbacks + class CJoystickFamilyManager; + + class CControllerTransformer : public IDatabaseCallbacks { public: - CControllerMapper() = default; - virtual ~CControllerMapper() = default; + CControllerTransformer(CJoystickFamilyManager& familyManager); + + virtual ~CControllerTransformer(); // implementation of IDatabaseCallbacks virtual void OnAdd(const DevicePtr& driverInfo, const ButtonMap& buttonMap); @@ -56,8 +59,9 @@ namespace JOYSTICK typedef std::map FamilyMap; typedef std::map GeomoetryMap; - FamilyMap m_familyModels; - GeomoetryMap m_geometryModels; - DeviceSet m_observedDevices; + FamilyMap m_familyModels; + GeomoetryMap m_geometryModels; + DeviceSet m_observedDevices; + CJoystickFamilyManager& m_familyManager; }; } diff --git a/src/buttonmapper/JoystickFamily.cpp b/src/buttonmapper/JoystickFamily.cpp index b736a8f1..7219caaa 100644 --- a/src/buttonmapper/JoystickFamily.cpp +++ b/src/buttonmapper/JoystickFamily.cpp @@ -20,13 +20,14 @@ #include "JoystickFamily.h" #include "storage/xml/JoystickFamiliesXml.h" +#include "storage/xml/JoystickFamilyDefinitions.h" using namespace JOYSTICK; // --- CJoystickFamily --------------------------------------------------------- -CJoystickFamily::CJoystickFamily(const std::string& name, const std::string& provider) : - m_familyName(CJoystickFamilyManager::Get().GetFamily(name, provider)) +CJoystickFamily::CJoystickFamily(const std::string& familyName) : + m_familyName(familyName) { } @@ -42,15 +43,16 @@ bool CJoystickFamily::operator<(const CJoystickFamily& other) const // --- CJoystickFamilyManager -------------------------------------------------- -CJoystickFamilyManager& CJoystickFamilyManager::Get() +bool CJoystickFamilyManager::Initialize(const std::string& addonPath) { - static CJoystickFamilyManager instance; - return instance; + std::string path = addonPath + "/" JOYSTICK_FAMILIES_FOLDER "/" JOYSTICK_FAMILIES_RESOURCE; + return LoadFamilies(path); } -bool CJoystickFamilyManager::Load() +bool CJoystickFamilyManager::LoadFamilies(const std::string& path) { - m_families = CJoystickFamiliesXml::LoadFamilies(); + CJoystickFamiliesXml::LoadFamilies(path, m_families); + return !m_families.empty(); } diff --git a/src/buttonmapper/JoystickFamily.h b/src/buttonmapper/JoystickFamily.h index 081e8f49..2507c53f 100644 --- a/src/buttonmapper/JoystickFamily.h +++ b/src/buttonmapper/JoystickFamily.h @@ -27,35 +27,38 @@ namespace JOYSTICK { + // --- CJoystickFamily ------------------------------------------------------- + class CJoystickFamily { public: - CJoystickFamily(const std::string& name, const std::string& provider); + CJoystickFamily(const std::string& familyName); CJoystickFamily(const CJoystickFamily& other); bool operator<(const CJoystickFamily& other) const; - const std::string& Name() const { return m_familyName; } - + const std::string& FamilyName() const { return m_familyName; } bool IsValid() const { return !m_familyName.empty(); } private: const std::string m_familyName; }; + // --- CJoystickFamilyManager ------------------------------------------------ + class CJoystickFamilyManager { - private: - CJoystickFamilyManager() = default; - public: - static CJoystickFamilyManager& Get(); + CJoystickFamilyManager() = default; - bool Load(); + bool Initialize(const std::string& addonPath); + void Deinitialize() { m_families.clear(); } const std::string& GetFamily(const std::string& name, const std::string& provider) const; private: + bool LoadFamilies(const std::string& path); + JoystickFamilyMap m_families; }; } diff --git a/src/storage/JustABunchOfFiles.cpp b/src/storage/JustABunchOfFiles.cpp index c8dcd8b0..54665457 100644 --- a/src/storage/JustABunchOfFiles.cpp +++ b/src/storage/JustABunchOfFiles.cpp @@ -19,7 +19,7 @@ */ #include "JustABunchOfFiles.h" -#include "ButtonMapDefinitions.h" +#include "StorageDefinitions.h" #include "StorageUtils.h" #include "filesystem/DirectoryUtils.h" #include "log/Log.h" diff --git a/src/storage/StorageDefinitions.h b/src/storage/StorageDefinitions.h new file mode 100644 index 00000000..7e30a243 --- /dev/null +++ b/src/storage/StorageDefinitions.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014-2015 Garrett Brown + * Copyright (C) 2014-2015 Team XBMC + * + * 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 XBMC; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#define RESOURCE_XML_EXTENSION ".xml" +#define RESOURCE_RETROARCH_EXTENSION ".cfg" + +#define RESOURCE_XML_FOLDER "xml" +#define RESOURCE_RETROARCH_FOLDER "retroarch" + +#define DEVICES_XML_ROOT "devices" +#define DEVICES_XML_ELEM_DEVICE "device" diff --git a/src/storage/StorageManager.cpp b/src/storage/StorageManager.cpp index c6a76f3a..80b4ad98 100644 --- a/src/storage/StorageManager.cpp +++ b/src/storage/StorageManager.cpp @@ -42,7 +42,7 @@ using namespace JOYSTICK; #define BUTTONMAP_FOLDER "buttonmaps" CStorageManager::CStorageManager(void) : - m_peripheralLib(NULL) + m_peripheralLib(nullptr) { } @@ -70,6 +70,9 @@ bool CStorageManager::Initialize(ADDON::CHelper_libKODI_peripheral* peripheralLi m_buttonMapper.reset(new CButtonMapper(peripheralLib)); + if (!m_buttonMapper->Initialize(m_familyManager)) + return false; + // Remove slash at end StringUtils::TrimRight(strUserPath, "\\/"); StringUtils::TrimRight(strAddonPath, "\\/"); @@ -80,30 +83,33 @@ bool CStorageManager::Initialize(ADDON::CHelper_libKODI_peripheral* peripheralLi // Ensure resources path exists in user data CStorageUtils::EnsureDirectoryExists(strUserPath); - strUserPath += "/" BUTTONMAP_FOLDER; - strAddonPath += "/" BUTTONMAP_FOLDER; + std::string strUserButtonMapPath = strUserPath + "/" BUTTONMAP_FOLDER; + std::string strAddonButtonMapPath = strAddonPath + "/" BUTTONMAP_FOLDER; // Ensure button map path exists in user data - CStorageUtils::EnsureDirectoryExists(strUserPath); + CStorageUtils::EnsureDirectoryExists(strUserButtonMapPath); - m_databases.push_back(DatabasePtr(new CDatabaseXml(strUserPath, true, m_buttonMapper->GetCallbacks()))); - //m_databases.push_back(DatabasePtr(new CDatabaseRetroArch(strUserPath, true, &m_controllerMapper))); // TODO - m_databases.push_back(DatabasePtr(new CDatabaseXml(strAddonPath, false, m_buttonMapper->GetCallbacks()))); - //m_databases.push_back(DatabasePtr(new CDatabaseRetroArch(strAddonPath, false))); // TODO + m_databases.push_back(DatabasePtr(new CDatabaseXml(strUserButtonMapPath, true, m_buttonMapper->GetCallbacks()))); + //m_databases.push_back(DatabasePtr(new CDatabaseRetroArch(strUserButtonMapPath, true, &m_controllerMapper))); // TODO + m_databases.push_back(DatabasePtr(new CDatabaseXml(strAddonButtonMapPath, false, m_buttonMapper->GetCallbacks()))); + //m_databases.push_back(DatabasePtr(new CDatabaseRetroArch(strAddonButtonMapPath, false))); // TODO m_databases.push_back(DatabasePtr(new CDatabaseJoystickAPI(m_buttonMapper->GetCallbacks()))); for (auto& database : m_databases) m_buttonMapper->RegisterDatabase(database); + m_familyManager.Initialize(strAddonPath); + return true; } void CStorageManager::Deinitialize(void) { + m_familyManager.Deinitialize(); m_databases.clear(); m_buttonMapper.reset(); - m_peripheralLib = NULL; + m_peripheralLib = nullptr; } bool CStorageManager::GetFeatures(const ADDON::Joystick& joystick, diff --git a/src/storage/StorageManager.h b/src/storage/StorageManager.h index ada17e73..6d2b217d 100644 --- a/src/storage/StorageManager.h +++ b/src/storage/StorageManager.h @@ -21,6 +21,7 @@ #include "StorageTypes.h" #include "buttonmapper/ButtonMapTypes.h" +#include "buttonmapper/JoystickFamily.h" #include #include @@ -114,5 +115,6 @@ namespace JOYSTICK DatabaseVector m_databases; std::unique_ptr m_buttonMapper; + CJoystickFamilyManager m_familyManager; }; } diff --git a/src/storage/ButtonMapDefinitions.h b/src/storage/xml/ButtonMapDefinitions.h similarity index 86% rename from src/storage/ButtonMapDefinitions.h rename to src/storage/xml/ButtonMapDefinitions.h index 1013a5ab..332f6bcf 100644 --- a/src/storage/ButtonMapDefinitions.h +++ b/src/storage/xml/ButtonMapDefinitions.h @@ -19,16 +19,8 @@ */ #pragma once -#define RESOURCE_XML_EXTENSION ".xml" -#define RESOURCE_RETROARCH_EXTENSION ".cfg" - -#define RESOURCE_XML_FOLDER "xml" -#define RESOURCE_RETROARCH_FOLDER "retroarch" - -#define DEVICES_XML_ROOT "devices" -#define DEVICES_XML_ELEM_DEVICE "device" - #define BUTTONMAP_XML_ROOT "buttonmap" +#define BUTTONMAP_XML_ELEM_DEVICE "device" #define BUTTONMAP_XML_ELEM_CONTROLLER "controller" #define BUTTONMAP_XML_ELEM_FEATURE "feature" diff --git a/src/storage/xml/ButtonMapXml.cpp b/src/storage/xml/ButtonMapXml.cpp index 4e8b56f5..ff0e3e3c 100644 --- a/src/storage/xml/ButtonMapXml.cpp +++ b/src/storage/xml/ButtonMapXml.cpp @@ -19,9 +19,9 @@ */ #include "ButtonMapXml.h" +#include "ButtonMapDefinitions.h" #include "DeviceXml.h" #include "buttonmapper/ButtonMapTranslator.h" -#include "storage/ButtonMapDefinitions.h" #include "storage/Device.h" #include "log/Log.h" @@ -60,11 +60,11 @@ bool CButtonMapXml::Load(void) return false; } - const TiXmlElement* pDevice = pRootElement->FirstChildElement(DEVICES_XML_ELEM_DEVICE); + const TiXmlElement* pDevice = pRootElement->FirstChildElement(BUTTONMAP_XML_ELEM_DEVICE); if (!pDevice) { - esyslog("Can't find <%s> tag", DEVICES_XML_ELEM_DEVICE); + esyslog("Can't find <%s> tag", BUTTONMAP_XML_ELEM_DEVICE); return false; } @@ -134,7 +134,7 @@ bool CButtonMapXml::Save(void) const if (!pElem) return false; - TiXmlElement deviceElement(DEVICES_XML_ELEM_DEVICE); + TiXmlElement deviceElement(BUTTONMAP_XML_ELEM_DEVICE); TiXmlNode* deviceNode = pElem->InsertEndChild(deviceElement); if (deviceNode == NULL) return false; diff --git a/src/storage/xml/DatabaseXml.cpp b/src/storage/xml/DatabaseXml.cpp index 339b51e2..64276ad5 100644 --- a/src/storage/xml/DatabaseXml.cpp +++ b/src/storage/xml/DatabaseXml.cpp @@ -20,7 +20,7 @@ #include "DatabaseXml.h" #include "ButtonMapXml.h" -#include "storage/ButtonMapDefinitions.h" +#include "storage/StorageDefinitions.h" using namespace JOYSTICK; diff --git a/src/storage/xml/DeviceXml.cpp b/src/storage/xml/DeviceXml.cpp index 9833cf4c..fc5bbcd5 100644 --- a/src/storage/xml/DeviceXml.cpp +++ b/src/storage/xml/DeviceXml.cpp @@ -19,7 +19,7 @@ */ #include "DeviceXml.h" -#include "storage/ButtonMapDefinitions.h" +#include "ButtonMapDefinitions.h" #include "storage/Device.h" #include "storage/StorageUtils.h" #include "log/Log.h" @@ -55,7 +55,7 @@ bool CDeviceXml::Deserialize(const TiXmlElement* pElement, CDevice& record) const char* name = pElement->Attribute(BUTTONMAP_XML_ATTR_DEVICE_NAME); if (!name) { - esyslog("<%s> tag has no \"%s\" attribute", DEVICES_XML_ELEM_DEVICE, BUTTONMAP_XML_ATTR_DEVICE_NAME); + esyslog("<%s> tag has no \"%s\" attribute", BUTTONMAP_XML_ELEM_DEVICE, BUTTONMAP_XML_ATTR_DEVICE_NAME); return false; } record.SetName(name); @@ -63,7 +63,7 @@ bool CDeviceXml::Deserialize(const TiXmlElement* pElement, CDevice& record) const char* provider = pElement->Attribute(BUTTONMAP_XML_ATTR_DEVICE_PROVIDER); if (!provider) { - esyslog("<%s> tag has no \"%s\" attribute", DEVICES_XML_ELEM_DEVICE, BUTTONMAP_XML_ATTR_DEVICE_PROVIDER); + esyslog("<%s> tag has no \"%s\" attribute", BUTTONMAP_XML_ELEM_DEVICE, BUTTONMAP_XML_ATTR_DEVICE_PROVIDER); return false; } record.SetProvider(provider); diff --git a/src/storage/xml/JoystickFamiliesXml.cpp b/src/storage/xml/JoystickFamiliesXml.cpp index 25fdde78..3601285c 100644 --- a/src/storage/xml/JoystickFamiliesXml.cpp +++ b/src/storage/xml/JoystickFamiliesXml.cpp @@ -19,14 +19,88 @@ */ #include "JoystickFamiliesXml.h" +#include "JoystickFamilyDefinitions.h" +#include "log/Log.h" + +#include "tinyxml.h" using namespace JOYSTICK; -JoystickFamilyMap CJoystickFamiliesXml::LoadFamilies() +bool CJoystickFamiliesXml::LoadFamilies(const std::string& path, JoystickFamilyMap& result) { - JoystickFamilyMap result; + TiXmlDocument xmlFile; + if (!xmlFile.LoadFile(path)) + { + esyslog("Error opening %s: %s", path.c_str(), xmlFile.ErrorDesc()); + return false; + } + + TiXmlElement* pRootElement = xmlFile.RootElement(); + if (!pRootElement || pRootElement->NoChildren() || pRootElement->ValueStr() != JOYSTICK_FAMILIES_XML_ELEM_FAMILIES) + { + esyslog("Can't find root <%s> tag", JOYSTICK_FAMILIES_XML_ELEM_FAMILIES); + return false; + } + + const TiXmlElement* pFamily = pRootElement->FirstChildElement(JOYSTICK_FAMILIES_XML_ELEM_FAMILY); + + if (pFamily == nullptr) + { + esyslog("Can't find <%s> tag", JOYSTICK_FAMILIES_XML_ELEM_FAMILY); + return false; + } + + return Deserialize(pFamily, result); +} + +bool CJoystickFamiliesXml::Deserialize(const TiXmlElement* pFamily, JoystickFamilyMap& result) +{ + // For logging purposes + unsigned int totalJoystickCount = 0; + + while (pFamily != nullptr) + { + const char* familyName = pFamily->Attribute(JOYSTICK_FAMILIES_XML_ATTR_FAMILY_NAME); + if (!familyName) + { + esyslog("<%s> tag has no attribute \"%s\"", JOYSTICK_FAMILIES_XML_ELEM_FAMILY, + JOYSTICK_FAMILIES_XML_ATTR_FAMILY_NAME); + return false; + } + + std::set& family = result[familyName]; + + const TiXmlElement* pJoystick = pFamily->FirstChildElement(JOYSTICK_FAMILIES_XML_ELEM_JOYSTICK); + + if (pJoystick == nullptr) + { + esyslog("Joystick family \"%s\": Can't find <%s> tag", familyName, JOYSTICK_FAMILIES_XML_ELEM_JOYSTICK); + return false; + } + + if (!DeserializeJoysticks(pJoystick, family)) + return false; + + totalJoystickCount += family.size(); + + pFamily = pFamily->NextSiblingElement(JOYSTICK_FAMILIES_XML_ELEM_FAMILY); + } + + dsyslog("Loaded %d joystick families with %d total joysticks", result.size(), totalJoystickCount); + + return true; +} + +bool CJoystickFamiliesXml::DeserializeJoysticks(const TiXmlElement* pJoystick, std::set& family) +{ + while (pJoystick != nullptr) + { + const std::string& joystickName = pJoystick->ValueStr(); + + family.insert(joystickName); - // TODO + pJoystick = pJoystick->NextSiblingElement(JOYSTICK_FAMILIES_XML_ELEM_JOYSTICK); + } - return result; + return true; } diff --git a/src/storage/xml/JoystickFamiliesXml.h b/src/storage/xml/JoystickFamiliesXml.h index f55733a5..f214e271 100644 --- a/src/storage/xml/JoystickFamiliesXml.h +++ b/src/storage/xml/JoystickFamiliesXml.h @@ -21,11 +21,17 @@ #include "buttonmapper/ButtonMapTypes.h" +class TiXmlElement; + namespace JOYSTICK { class CJoystickFamiliesXml { public: - static JoystickFamilyMap LoadFamilies(); + static bool LoadFamilies(const std::string& path, JoystickFamilyMap& result); + + private: + static bool Deserialize(const TiXmlElement* pFamily, JoystickFamilyMap& result); + static bool DeserializeJoysticks(const TiXmlElement* pJoystick, std::set& family); }; } diff --git a/src/storage/xml/JoystickFamilyDefinitions.h b/src/storage/xml/JoystickFamilyDefinitions.h new file mode 100644 index 00000000..7823035f --- /dev/null +++ b/src/storage/xml/JoystickFamilyDefinitions.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 Garrett Brown + * Copyright (C) 2016 Team Kodi + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this Program; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#define JOYSTICK_FAMILIES_FOLDER "joystickfamilies" +#define JOYSTICK_FAMILIES_RESOURCE "joystickfamilies.xml" + +#define JOYSTICK_FAMILIES_XML_ELEM_FAMILIES "joystickfamilies" +#define JOYSTICK_FAMILIES_XML_ELEM_FAMILY "joystickfamily" +#define JOYSTICK_FAMILIES_XML_ELEM_JOYSTICK "joystick" + +#define JOYSTICK_FAMILIES_XML_ATTR_FAMILY_NAME "name" From b0d971519f4db627b9930b4c4e7f9d903dace055 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Tue, 16 Aug 2016 10:04:58 -0700 Subject: [PATCH 11/28] [buttonmapper] Remove separate models for geometry and family This removes the addition of geometry- and family-specific models in ed237b8. --- src/buttonmapper/ControllerTransformer.cpp | 27 ++-------------------- src/buttonmapper/ControllerTransformer.h | 6 +---- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/src/buttonmapper/ControllerTransformer.cpp b/src/buttonmapper/ControllerTransformer.cpp index d7dff96c..722d7da3 100644 --- a/src/buttonmapper/ControllerTransformer.cpp +++ b/src/buttonmapper/ControllerTransformer.cpp @@ -46,25 +46,12 @@ void CControllerTransformer::OnAdd(const DevicePtr& driverInfo, const ButtonMap& else return; - const std::string& familyName = m_familyManager.GetFamily(driverInfo->Name(), driverInfo->Provider()); - - CJoystickFamily family(familyName); - CDriverGeometry geometry(driverInfo->ButtonCount(), - driverInfo->HatCount(), - driverInfo->AxisCount()); - - CControllerModel& familyModel = m_familyModels[family]; - CControllerModel& geometryModel = m_geometryModels[geometry]; - for (auto itTo = buttonMap.begin(); itTo != buttonMap.end(); ++itTo) { // Only allow controller map items where "from" compares before "to" for (auto itFrom = buttonMap.begin(); itFrom->first < itTo->first; ++itFrom) { - if (family.IsValid()) - AddControllerMap(familyModel, itFrom->first, itFrom->second, itTo->first, itTo->second); - if (geometry.IsValid()) - AddControllerMap(geometryModel, itFrom->first, itFrom->second, itTo->first, itTo->second); + AddControllerMap(m_controllerModel, itFrom->first, itFrom->second, itTo->first, itTo->second); } } } @@ -143,17 +130,7 @@ void CControllerTransformer::TransformFeatures(const ADDON::Joystick& driverInfo ControllerMapItem needle = { bSwap ? toController : fromController, bSwap ? fromController : toController }; - const std::string& familyName = m_familyManager.GetFamily(driverInfo.Name(), driverInfo.Provider()); - - CJoystickFamily family(familyName); - CDriverGeometry geometry(driverInfo.ButtonCount(), - driverInfo.HatCount(), - driverInfo.AxisCount()); - - CControllerModel& familyModel = m_familyModels[family]; - CControllerModel& geometryModel = m_geometryModels[geometry]; - - std::array models = { &familyModel, &geometryModel }; + std::array models = { &m_controllerModel }; for (CControllerModel* model : models) { diff --git a/src/buttonmapper/ControllerTransformer.h b/src/buttonmapper/ControllerTransformer.h index ceb35a7a..5a381df7 100644 --- a/src/buttonmapper/ControllerTransformer.h +++ b/src/buttonmapper/ControllerTransformer.h @@ -56,11 +56,7 @@ namespace JOYSTICK const std::string& controllerFrom, const FeatureVector& featuresFrom, const std::string& controllerTo, const FeatureVector& featuresTo); - typedef std::map FamilyMap; - typedef std::map GeomoetryMap; - - FamilyMap m_familyModels; - GeomoetryMap m_geometryModels; + CControllerModel m_controllerModel; DeviceSet m_observedDevices; CJoystickFamilyManager& m_familyManager; }; From 28e8bddb4d47014c0a14c1c3fe6cb59fcadd545f Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Tue, 16 Aug 2016 10:45:02 -0700 Subject: [PATCH 12/28] [buttonmapper] Refactor to move some code from transformer to model --- src/buttonmapper/ControllerModel.cpp | 11 ++++++++--- src/buttonmapper/ControllerModel.h | 23 +++++++++++++++++----- src/buttonmapper/ControllerTransformer.cpp | 10 ++-------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/buttonmapper/ControllerModel.cpp b/src/buttonmapper/ControllerModel.cpp index 729e0e29..6d8de7ab 100644 --- a/src/buttonmapper/ControllerModel.cpp +++ b/src/buttonmapper/ControllerModel.cpp @@ -22,13 +22,18 @@ using namespace JOYSTICK; -void CControllerModel::Reset() +void CControllerModel::AddFeatureMapping(const ControllerMapItem& controllerMapping, const FeatureMapItem& featureMapping) { - m_reducedMap.clear(); + ++m_map[controllerMapping][featureMapping]; + + // Reset the reduced model for the controller mapping + m_reducedMap[controllerMapping].clear(); } -const FeatureOccurrences& CControllerModel::GetNormalizedFeatures(const ControllerMapItem& needle, bool bSwap) +const FeatureOccurrences& CControllerModel::GetNormalizedFeatures(const ControllerMapItem& needle) { + const bool bSwap = (needle.fromController >= needle.toController); + FeatureOccurrences& result = m_reducedMap[needle]; if (result.empty()) NormalizeFeatures(m_map[needle], result, bSwap); diff --git a/src/buttonmapper/ControllerModel.h b/src/buttonmapper/ControllerModel.h index d24a1528..cb47e5bb 100644 --- a/src/buttonmapper/ControllerModel.h +++ b/src/buttonmapper/ControllerModel.h @@ -23,15 +23,28 @@ namespace JOYSTICK { + /*! + * \brief Model for how controllers map to each other + */ class CControllerModel { public: - void Reset(); + /*! + * \brief Add data to the model + * + * \param controllerMapping The from and to controllers + * \param featureMapping The from and to features + */ + void AddFeatureMapping(const ControllerMapItem& controllerMapping, const FeatureMapItem& featuremapping); - ControllerMap& GetMap() { return m_map; } - const ControllerMap& GetMap() const { return m_map; } - - const FeatureOccurrences& GetNormalizedFeatures(const ControllerMapItem& needle, bool bSwap); + /*! + * \brief Get a translation map for the specified form and to controllers + * + * \param needle The from and to controllers + * + * \return A map whose keys are from-to feature pairs and whose values are all 1 + */ + const FeatureOccurrences& GetNormalizedFeatures(const ControllerMapItem& needle); private: void Normalize(const ControllerMapItem& needle, bool bSwap); diff --git a/src/buttonmapper/ControllerTransformer.cpp b/src/buttonmapper/ControllerTransformer.cpp index 722d7da3..6e3a1413 100644 --- a/src/buttonmapper/ControllerTransformer.cpp +++ b/src/buttonmapper/ControllerTransformer.cpp @@ -66,9 +66,6 @@ bool CControllerTransformer::AddControllerMap(CControllerModel& model, ControllerMapItem needle = { controllerFrom, controllerTo }; - ControllerMap& controllerMap = model.GetMap(); - FeatureOccurrences& featureMap = controllerMap[needle]; - for (auto itFromFeature = featuresFrom.begin(); itFromFeature != featuresFrom.end(); ++itFromFeature) { const ADDON::JoystickFeature& fromFeature = *itFromFeature; @@ -108,14 +105,11 @@ bool CControllerTransformer::AddControllerMap(CControllerModel& model, if (itToFeature != featuresTo.end()) { FeatureMapItem featureMapItem = { fromFeature.Name(), itToFeature->Name() }; - ++featureMap[featureMapItem]; + model.AddFeatureMapping(needle, std::move(featureMapItem)); bChanged = true; } } - if (bChanged) - model.Reset(); - return bChanged; } @@ -134,7 +128,7 @@ void CControllerTransformer::TransformFeatures(const ADDON::Joystick& driverInfo for (CControllerModel* model : models) { - const FeatureOccurrences& normalizedFeatures = model->GetNormalizedFeatures(needle, bSwap); + const FeatureOccurrences& normalizedFeatures = model->GetNormalizedFeatures(needle); for (auto itMap = normalizedFeatures.begin(); itMap != normalizedFeatures.end(); ++itMap) { From 6c075b83e0319d967affeed031f1deee39c0b1ba Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Tue, 16 Aug 2016 11:05:59 -0700 Subject: [PATCH 13/28] [cosmetic] Improve comment in ButtonMapper.cpp --- src/buttonmapper/ButtonMapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buttonmapper/ButtonMapper.cpp b/src/buttonmapper/ButtonMapper.cpp index 71476818..48946bc4 100644 --- a/src/buttonmapper/ButtonMapper.cpp +++ b/src/buttonmapper/ButtonMapper.cpp @@ -142,7 +142,7 @@ void CButtonMapper::DeriveFeatures(const ADDON::Joystick& joystick, const std::s if (!m_controllerTransformer) return; - // Obtain an iterator to the controller profile with the highest count of features defined + // Search the button map for the controller with the highest count of features defined unsigned int maxFeatures = 0; auto maxFeaturesIt = buttonMap.end(); From bb469e91182525fc12ac152a4fe7aa20d5bdad85 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Tue, 16 Aug 2016 11:06:45 -0700 Subject: [PATCH 14/28] [buttonmapper] Small refactor: make static function a member function to simplify parameters --- src/buttonmapper/ControllerTransformer.cpp | 9 ++++----- src/buttonmapper/ControllerTransformer.h | 5 ++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/buttonmapper/ControllerTransformer.cpp b/src/buttonmapper/ControllerTransformer.cpp index 6e3a1413..915cb011 100644 --- a/src/buttonmapper/ControllerTransformer.cpp +++ b/src/buttonmapper/ControllerTransformer.cpp @@ -51,18 +51,17 @@ void CControllerTransformer::OnAdd(const DevicePtr& driverInfo, const ButtonMap& // Only allow controller map items where "from" compares before "to" for (auto itFrom = buttonMap.begin(); itFrom->first < itTo->first; ++itFrom) { - AddControllerMap(m_controllerModel, itFrom->first, itFrom->second, itTo->first, itTo->second); + AddControllerMap(itFrom->first, itFrom->second, itTo->first, itTo->second); } } } -bool CControllerTransformer::AddControllerMap(CControllerModel& model, - const std::string& controllerFrom, const FeatureVector& featuresFrom, +bool CControllerTransformer::AddControllerMap(const std::string& controllerFrom, const FeatureVector& featuresFrom, const std::string& controllerTo, const FeatureVector& featuresTo) { bool bChanged = false; - assert(controllerFrom < controllerTo); + //assert(controllerFrom < controllerTo); ControllerMapItem needle = { controllerFrom, controllerTo }; @@ -105,7 +104,7 @@ bool CControllerTransformer::AddControllerMap(CControllerModel& model, if (itToFeature != featuresTo.end()) { FeatureMapItem featureMapItem = { fromFeature.Name(), itToFeature->Name() }; - model.AddFeatureMapping(needle, std::move(featureMapItem)); + m_controllerModel.AddFeatureMapping(needle, std::move(featureMapItem)); bChanged = true; } } diff --git a/src/buttonmapper/ControllerTransformer.h b/src/buttonmapper/ControllerTransformer.h index 5a381df7..c475c691 100644 --- a/src/buttonmapper/ControllerTransformer.h +++ b/src/buttonmapper/ControllerTransformer.h @@ -52,9 +52,8 @@ namespace JOYSTICK FeatureVector& transformedFeatures); private: - static bool AddControllerMap(CControllerModel& model, - const std::string& controllerFrom, const FeatureVector& featuresFrom, - const std::string& controllerTo, const FeatureVector& featuresTo); + bool AddControllerMap(const std::string& controllerFrom, const FeatureVector& featuresFrom, + const std::string& controllerTo, const FeatureVector& featuresTo); CControllerModel m_controllerModel; DeviceSet m_observedDevices; From 5442bd7cf2be2ae2d4840e5dafb3ccc746251994 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Tue, 16 Aug 2016 14:16:12 -0700 Subject: [PATCH 15/28] Update .gitignore --- .gitignore | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index d886ce44..4336b97e 100644 --- a/.gitignore +++ b/.gitignore @@ -53,16 +53,8 @@ compile *.opensdf *.vcxproj.user -project/VS2010Express/objs -project/VS2010Express/ipch -project/VS2010Express/libs -project/VS2010Express/platform/Debug -project/VS2010Express/platform/Release -project/BuildDependencies/downloads -project/BuildDependencies/lib -project/BuildDependencies/include -addons/*/project/VS2010Express/Debug -addons/*/project/VS2010Express/Release -addons/*/*.so -addons/*/addon/*.dll -addons/*/addon/addon.xml +peripheral.joystick/project/VS2010Express/Debug +peripheral.joystick/project/VS2010Express/Release +peripheral.joystick/*.so +peripheral.joystick/*.dll +peripheral.joystick/addon.xml From 8b677b2044528b89ef14c4af8e38d942836f4fbe Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Tue, 16 Aug 2016 17:11:27 -0700 Subject: [PATCH 16/28] [buttonmapper] Uncomment assert and switch to macro The ASSERT() macro is ignored during Release builds. --- src/buttonmapper/ControllerTransformer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/buttonmapper/ControllerTransformer.cpp b/src/buttonmapper/ControllerTransformer.cpp index 915cb011..347f215d 100644 --- a/src/buttonmapper/ControllerTransformer.cpp +++ b/src/buttonmapper/ControllerTransformer.cpp @@ -20,12 +20,12 @@ #include "ControllerTransformer.h" #include "storage/Device.h" +#include "utils/CommonMacros.h" #include "kodi_peripheral_utils.hpp" #include #include -#include using namespace JOYSTICK; @@ -61,7 +61,7 @@ bool CControllerTransformer::AddControllerMap(const std::string& controllerFrom, { bool bChanged = false; - //assert(controllerFrom < controllerTo); + ASSERT(controllerFrom < controllerTo); ControllerMapItem needle = { controllerFrom, controllerTo }; From f993635f5b8f90f98c526a45cf2d93dd9afe20ad Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Wed, 17 Aug 2016 14:09:03 -0700 Subject: [PATCH 17/28] Fix compile error on windows --- src/api/JoystickManager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/api/JoystickManager.cpp b/src/api/JoystickManager.cpp index db9d580d..2bda5a93 100644 --- a/src/api/JoystickManager.cpp +++ b/src/api/JoystickManager.cpp @@ -260,10 +260,11 @@ const ButtonMap& CJoystickManager::GetButtonMap(const std::string& provider) CLockObject lock(m_interfacesMutex); - for (auto interface : m_interfaces) + // Scan for joysticks (this can take a while, don't block) + for (std::vector::iterator itInterface = m_interfaces.begin(); itInterface != m_interfaces.end(); ++itInterface) { - if (interface->Name() == provider) - return interface->GetButtonMap(); + if ((*itInterface)->Name() == provider) + return (*itInterface)->GetButtonMap(); } return empty; From f5624ca84fd99af38cc8e08694a7991d9d602b5a Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Thu, 18 Aug 2016 15:20:27 -0700 Subject: [PATCH 18/28] Update to v1.0.21 - set supports_buttonmaps capability --- peripheral.joystick/addon.xml.in | 2 +- src/addon.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/peripheral.joystick/addon.xml.in b/peripheral.joystick/addon.xml.in index 135d63c7..91fc328d 100644 --- a/peripheral.joystick/addon.xml.in +++ b/peripheral.joystick/addon.xml.in @@ -6,7 +6,7 @@ provider-name="Team-Kodi"> - + provides_joysticks = true; + pCapabilities->provides_buttonmaps = true; return PERIPHERAL_NO_ERROR; } From 513bf5cd377d57cc0967bd4842ad9c94c5e08d72 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Thu, 18 Aug 2016 15:20:54 -0700 Subject: [PATCH 19/28] Remove deadzone setting --- peripheral.joystick/resources/settings.xml | 6 ------ src/api/Joystick.cpp | 17 +---------------- src/settings/Settings.cpp | 7 ------- src/settings/Settings.h | 15 --------------- 4 files changed, 1 insertion(+), 44 deletions(-) delete mode 100644 peripheral.joystick/resources/settings.xml diff --git a/peripheral.joystick/resources/settings.xml b/peripheral.joystick/resources/settings.xml deleted file mode 100644 index 7be0f61b..00000000 --- a/peripheral.joystick/resources/settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/api/Joystick.cpp b/src/api/Joystick.cpp index f9e82089..d2f2d08a 100644 --- a/src/api/Joystick.cpp +++ b/src/api/Joystick.cpp @@ -202,7 +202,7 @@ void CJoystick::SetAxisValue(unsigned int axisIndex, JOYSTICK_STATE_AXIS axisVal axisValue = CONSTRAIN(-1.0f, axisValue, 1.0f); if (axisIndex < m_stateBuffer.axes.size()) - m_stateBuffer.axes[axisIndex] = ScaleDeadzone(m_axisFilters[axisIndex]->Filter(axisValue)); + m_stateBuffer.axes[axisIndex] = m_axisFilters[axisIndex]->Filter(axisValue); } void CJoystick::SetAxisValue(unsigned int axisIndex, long value, long maxAxisAmount) @@ -224,18 +224,3 @@ float CJoystick::NormalizeAxis(long value, long maxAxisAmount) { return 1.0f * CONSTRAIN(-maxAxisAmount, value, maxAxisAmount) / maxAxisAmount; } - -float CJoystick::ScaleDeadzone(float value) -{ - const float deadzone = CSettings::Get().Deadzone(); - - if (deadzone >= 1.0f) - return 0.0f; - - if (value > deadzone) - return (float)(value - deadzone) / (float)(1.0f - deadzone); - else if (value < -deadzone) - return (float)(value + deadzone) / (float)(1.0f - deadzone); - - return 0.0f; -} diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index 0c3b3802..2a67bda2 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -23,12 +23,10 @@ using namespace JOYSTICK; -#define SETTING_DEADZONE "deadzone" #define SETTING_RETROARCH_CONFIG "retroarchconfig" CSettings::CSettings(void) : m_bInitialized(false), - m_deadzone(0.0f), m_bGenerateRetroArchConfigs(false) { } @@ -41,11 +39,6 @@ CSettings& CSettings::Get(void) void CSettings::SetSetting(const std::string& strName, const void* value) { - if (strName == SETTING_DEADZONE) - { - m_deadzone = *static_cast(value); - dsyslog("Setting \"%s\" set to %f", SETTING_DEADZONE, m_deadzone); - } if (strName == SETTING_RETROARCH_CONFIG) { m_bGenerateRetroArchConfigs = *static_cast(value); diff --git a/src/settings/Settings.h b/src/settings/Settings.h index 0a248d8c..fdda7b83 100644 --- a/src/settings/Settings.h +++ b/src/settings/Settings.h @@ -35,21 +35,6 @@ namespace JOYSTICK bool IsInitialized(void) const { return m_bInitialized; } - /*! - * \brief The analog stick deadzone - * - * This is applied to each axis. Axis is scaled appropriately, so position - * is continuous from -1.0 to 1.0: - * - * | / 1.0 - * | / - * __|__/ - * / | - * / |--| Deadzone - * -1.0 / | - */ - float Deadzone(void) const { return m_deadzone; } - /*! * \brief Generate .cfg files compatible with RetroArch's joypad autoconfig */ From f0c5af5d35c7024512a05959320a86f2e5d6a040 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Thu, 18 Aug 2016 19:20:04 -0700 Subject: [PATCH 20/28] Serialize anomalous triggers to button map XML --- CMakeLists.txt | 2 +- ...TriggerFilter.cpp => AnomalousTrigger.cpp} | 34 +++++++---- ...lousTriggerFilter.h => AnomalousTrigger.h} | 25 +++++--- src/api/Joystick.cpp | 22 ++++++- src/api/Joystick.h | 3 + src/api/JoystickManager.cpp | 18 ++++++ src/api/JoystickManager.h | 2 + src/storage/xml/ButtonMapDefinitions.h | 6 ++ src/storage/xml/ButtonMapXml.cpp | 60 +++++++++++++++++++ src/storage/xml/ButtonMapXml.h | 3 + 10 files changed, 152 insertions(+), 23 deletions(-) rename src/api/{AnomalousTriggerFilter.cpp => AnomalousTrigger.cpp} (80%) rename src/api/{AnomalousTriggerFilter.h => AnomalousTrigger.h} (87%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17046915..863f50bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ include_directories(${INCLUDES} ${PCRE_INCLUDE_DIRS}) set(JOYSTICK_SOURCES src/addon.cpp - src/api/AnomalousTriggerFilter.cpp + src/api/AnomalousTrigger.cpp src/api/Joystick.cpp src/api/JoystickAsync.cpp src/api/JoystickInterfaceCallback.cpp diff --git a/src/api/AnomalousTriggerFilter.cpp b/src/api/AnomalousTrigger.cpp similarity index 80% rename from src/api/AnomalousTriggerFilter.cpp rename to src/api/AnomalousTrigger.cpp index e23781eb..b22300d1 100644 --- a/src/api/AnomalousTriggerFilter.cpp +++ b/src/api/AnomalousTrigger.cpp @@ -17,15 +17,15 @@ * . */ -#include "AnomalousTriggerFilter.h" +#include "AnomalousTrigger.h" #include "log/Log.h" using namespace JOYSTICK; #define ANOMOLOUS_MAGNITUDE 0.5f -CAnomalousTriggerFilter::CAnomalousTriggerFilter(unsigned int axisIndex) - : axisIndex(axisIndex), +CAnomalousTrigger::CAnomalousTrigger(unsigned int axisIndex) + : m_axisIndex(axisIndex), m_state(STATE_UNKNOWN), m_center(CENTER_ZERO), m_range(TRIGGER_RANGE_HALF), @@ -35,7 +35,7 @@ CAnomalousTriggerFilter::CAnomalousTriggerFilter(unsigned int axisIndex) { } -float CAnomalousTriggerFilter::Filter(float value) +float CAnomalousTrigger::Filter(float value) { // First, check for discrete D-pad if (m_state == STATE_UNKNOWN) @@ -51,7 +51,7 @@ float CAnomalousTriggerFilter::Filter(float value) if (m_bCenterSeen && m_bPositiveOneSeen && m_bNegativeOneSeen) { m_state = STATE_DISCRETE_DPAD; - dsyslog("Discrete D-pad detected on axis %u", axisIndex); + dsyslog("Discrete D-pad detected on axis %u", m_axisIndex); } } else @@ -71,7 +71,7 @@ float CAnomalousTriggerFilter::Filter(float value) m_center = CENTER_ZERO; if (IsAnomalousTrigger()) - dsyslog("Anomalous trigger detected on axis %u (initial value = %f)", axisIndex, value); + dsyslog("Anomalous trigger detected on axis %u (initial value = %f)", m_axisIndex, value); m_state = STATE_CENTER_KNOWN; } @@ -105,19 +105,31 @@ float CAnomalousTriggerFilter::Filter(float value) return value; } -bool CAnomalousTriggerFilter::IsAnomalousTrigger(void) +bool CAnomalousTrigger::IsAnomalousTrigger(void) const { return m_center != CENTER_ZERO; } -float CAnomalousTriggerFilter::GetCenter(AXIS_CENTER center) +int CAnomalousTrigger::GetCenter(AXIS_CENTER center) { switch (center) { - case CENTER_NEGATIVE_ONE: return -1.0f; - case CENTER_POSITIVE_ONE: return 1.0f; + case CENTER_NEGATIVE_ONE: return -1; + case CENTER_POSITIVE_ONE: return 1; default: break; } - return 0.0f; + return 0; +} + +int CAnomalousTrigger::GetRange(TRIGGER_RANGE range) +{ + switch (range) + { + case TRIGGER_RANGE_HALF: return 1; + case TRIGGER_RANGE_FULL: return 2; + default: + break; + } + return 1; } diff --git a/src/api/AnomalousTriggerFilter.h b/src/api/AnomalousTrigger.h similarity index 87% rename from src/api/AnomalousTriggerFilter.h rename to src/api/AnomalousTrigger.h index 4b946bfe..d10de676 100644 --- a/src/api/AnomalousTriggerFilter.h +++ b/src/api/AnomalousTrigger.h @@ -45,14 +45,25 @@ namespace JOYSTICK * * Triggers centered about 1.0 are transformed to travel from zero to -1.0. */ - class CAnomalousTriggerFilter : public IJoystickAxisFilter + class CAnomalousTrigger : public IJoystickAxisFilter { public: - CAnomalousTriggerFilter(unsigned int axisIndex); + CAnomalousTrigger(unsigned int axisIndex); // implementation of IJoystickAxisFilter virtual float Filter(float value) override; + /*! + * \brief Has this axis been detected as an anomalous trigger + */ + bool IsAnomalousTrigger(void) const; + + unsigned int AxisIndex(void) const { return m_axisIndex; } + + unsigned int Center(void) const { return GetCenter(m_center); } + + unsigned int Range(void) const { return GetRange(m_range); } + private: enum AXIS_STATE { @@ -95,17 +106,13 @@ namespace JOYSTICK TRIGGER_RANGE_FULL, // trigger value is in the interval [-1.0, 1.0] }; - /*! - * \brief Has this axis been detected as an anomalous trigger - */ - bool IsAnomalousTrigger(void); - /*! * \brief Helper functions */ - static float GetCenter(AXIS_CENTER center); + static int GetCenter(AXIS_CENTER center); + static int GetRange(TRIGGER_RANGE range); - const unsigned int axisIndex; + const unsigned int m_axisIndex; AXIS_STATE m_state; AXIS_CENTER m_center; TRIGGER_RANGE m_range; diff --git a/src/api/Joystick.cpp b/src/api/Joystick.cpp index d2f2d08a..2f32761b 100644 --- a/src/api/Joystick.cpp +++ b/src/api/Joystick.cpp @@ -17,8 +17,8 @@ * . */ -#include "AnomalousTriggerFilter.h" #include "Joystick.h" +#include "AnomalousTrigger.h" #include "log/Log.h" #include "settings/Settings.h" #include "utils/CommonMacros.h" @@ -82,8 +82,9 @@ bool CJoystick::Initialize(void) m_stateBuffer.axes.assign(AxisCount(), 0.0f); // Filter for anomalous triggers + m_axisFilters.reserve(AxisCount()); for (unsigned int i = 0; i < AxisCount(); i++) - m_axisFilters.push_back(new CAnomalousTriggerFilter(i)); + m_axisFilters.push_back(new CAnomalousTrigger(i)); return true; } @@ -137,6 +138,23 @@ bool CJoystick::SendEvent(const ADDON::PeripheralEvent& event) return bHandled; } +std::vector CJoystick::GetAnomalousTriggers() +{ + std::vector result; + + for (IJoystickAxisFilter* filter : m_axisFilters) + { + CAnomalousTrigger* trigger = dynamic_cast(filter); + if (!trigger) + continue; + + if (trigger->IsAnomalousTrigger()) + result.push_back(trigger); + } + + return result; +} + void CJoystick::GetButtonEvents(std::vector& events) { const std::vector& buttons = m_stateBuffer.buttons; diff --git a/src/api/Joystick.h b/src/api/Joystick.h index d0edd522..ed347088 100644 --- a/src/api/Joystick.h +++ b/src/api/Joystick.h @@ -25,6 +25,7 @@ namespace JOYSTICK { + class CAnomalousTrigger; class IJoystickAxisFilter; class CJoystick : public ADDON::Joystick @@ -90,6 +91,8 @@ namespace JOYSTICK */ virtual void PowerOff() { } + std::vector GetAnomalousTriggers(); + protected: /*! * Implemented by derived class to scan for events diff --git a/src/api/JoystickManager.cpp b/src/api/JoystickManager.cpp index 2bda5a93..ab2e9e80 100644 --- a/src/api/JoystickManager.cpp +++ b/src/api/JoystickManager.cpp @@ -219,6 +219,24 @@ JoystickPtr CJoystickManager::GetJoystick(unsigned int index) const return JoystickPtr(); } +JoystickVector CJoystickManager::GetJoysticks(const ADDON::Joystick& joystickInfo) const +{ + JoystickVector result; + + CLockObject lock(m_joystickMutex); + + for (const auto& joystick : m_joysticks) + { + if (joystick->Name() == joystickInfo.Name() && + joystick->Provider() == joystickInfo.Provider()) + { + result.push_back(joystick); + } + } + + return result; +} + bool CJoystickManager::GetEvents(std::vector& events) { CLockObject lock(m_joystickMutex); diff --git a/src/api/JoystickManager.h b/src/api/JoystickManager.h index 8af98be5..69f0c626 100644 --- a/src/api/JoystickManager.h +++ b/src/api/JoystickManager.h @@ -74,6 +74,8 @@ namespace JOYSTICK JoystickPtr GetJoystick(unsigned int index) const; + JoystickVector GetJoysticks(const ADDON::Joystick& joystickInfo) const; + /*! * \brief Get all events that have occurred since the last call to GetEvents() */ diff --git a/src/storage/xml/ButtonMapDefinitions.h b/src/storage/xml/ButtonMapDefinitions.h index 332f6bcf..b069e2e5 100644 --- a/src/storage/xml/ButtonMapDefinitions.h +++ b/src/storage/xml/ButtonMapDefinitions.h @@ -21,6 +21,8 @@ #define BUTTONMAP_XML_ROOT "buttonmap" #define BUTTONMAP_XML_ELEM_DEVICE "device" +#define BUTTONMAP_XML_ELEM_CONFIGURATION "configuration" +#define BUTTONMAP_XML_ELEM_AXIS "axis" #define BUTTONMAP_XML_ELEM_CONTROLLER "controller" #define BUTTONMAP_XML_ELEM_FEATURE "feature" @@ -50,3 +52,7 @@ #define BUTTONMAP_XML_ATTR_FEATURE_HAT "hat" #define BUTTONMAP_XML_ATTR_FEATURE_AXIS "axis" #define BUTTONMAP_XML_ATTR_FEATURE_MOTOR "motor" + +#define BUTTONMAP_XML_ATTR_AXIS_INDEX "index" +#define BUTTONMAP_XML_ATTR_AXIS_CENTER "center" +#define BUTTONMAP_XML_ATTR_AXIS_RANGE "range" diff --git a/src/storage/xml/ButtonMapXml.cpp b/src/storage/xml/ButtonMapXml.cpp index ff0e3e3c..b04b971d 100644 --- a/src/storage/xml/ButtonMapXml.cpp +++ b/src/storage/xml/ButtonMapXml.cpp @@ -21,12 +21,16 @@ #include "ButtonMapXml.h" #include "ButtonMapDefinitions.h" #include "DeviceXml.h" +#include "api/AnomalousTrigger.h" +#include "api/Joystick.h" +#include "api/JoystickManager.h" #include "buttonmapper/ButtonMapTranslator.h" #include "storage/Device.h" #include "log/Log.h" #include "tinyxml.h" +#include #include #include #include @@ -145,12 +149,68 @@ bool CButtonMapXml::Save(void) const CDeviceXml::Serialize(*m_device, deviceElem); + if (!SerializeTriggers(deviceElem)) + return false; + if (!SerializeButtonMaps(deviceElem)) return false; return xmlFile.SaveFile(m_strResourcePath); } +bool CButtonMapXml::SerializeTriggers(TiXmlElement* pElement) const +{ + std::map triggers; + + // Get triggers + JoystickVector joysticks = CJoystickManager::Get().GetJoysticks(*m_device); + for (const auto& joystick : joysticks) + { + std::vector triggerVec = joystick->GetAnomalousTriggers(); + for (CAnomalousTrigger* trigger : triggerVec) + triggers[trigger->AxisIndex()] = trigger; + } + + // Serialize triggers + if (!triggers.empty()) + { + TiXmlElement configurationElement(BUTTONMAP_XML_ELEM_CONFIGURATION); + TiXmlNode* configurationNode = pElement->InsertEndChild(configurationElement); + if (configurationNode == nullptr) + return false; + + TiXmlElement* configurationElem = configurationNode->ToElement(); + if (configurationElem == nullptr) + return false; + + for (auto itTrigger = triggers.begin(); itTrigger != triggers.end(); ++itTrigger) + { + if (!SerializeTrigger(configurationElem, itTrigger->second)) + return false; + } + } + + return true; +} + +bool CButtonMapXml::SerializeTrigger(TiXmlElement* pElement, const CAnomalousTrigger* trigger) +{ + TiXmlElement axisElement(BUTTONMAP_XML_ELEM_AXIS); + TiXmlNode* axisNode = pElement->InsertEndChild(axisElement); + if (axisNode == nullptr) + return false; + + TiXmlElement* axisElem = axisNode->ToElement(); + if (axisElem == nullptr) + return false; + + axisElem->SetAttribute(BUTTONMAP_XML_ATTR_AXIS_INDEX, trigger->AxisIndex()); + axisElem->SetAttribute(BUTTONMAP_XML_ATTR_AXIS_CENTER, trigger->Center()); + axisElem->SetAttribute(BUTTONMAP_XML_ATTR_AXIS_RANGE, trigger->Range()); + + return true; +} + bool CButtonMapXml::SerializeButtonMaps(TiXmlElement* pElement) const { for (ButtonMap::const_iterator it = m_buttonMap.begin(); it != m_buttonMap.end(); ++it) diff --git a/src/storage/xml/ButtonMapXml.h b/src/storage/xml/ButtonMapXml.h index 0a260670..b87b1067 100644 --- a/src/storage/xml/ButtonMapXml.h +++ b/src/storage/xml/ButtonMapXml.h @@ -33,6 +33,7 @@ namespace ADDON namespace JOYSTICK { + class CAnomalousTrigger; class CButtonMap; class CButtonMapXml : public CButtonMap @@ -50,11 +51,13 @@ namespace JOYSTICK private: bool SerializeButtonMaps(TiXmlElement* pElement) const; + bool SerializeTriggers(TiXmlElement* pElement) const; static bool Serialize(const FeatureVector& features, TiXmlElement* pElement); static bool Deserialize(const TiXmlElement* pElement, FeatureVector& features); static bool IsValid(const ADDON::JoystickFeature& feature); + static bool SerializeTrigger(TiXmlElement* pElement, const CAnomalousTrigger* trigger); static bool SerializeFeature(TiXmlElement* pElement, const ADDON::DriverPrimitive& primitive, const char* tagName); static bool SerializePrimitiveTag(TiXmlElement* pElement, const ADDON::DriverPrimitive& primitive, const char* tagName); static void SerializePrimitive(TiXmlElement* pElement, const ADDON::DriverPrimitive& primitive); From bd07a836ea90cca6ee7cba8cb9f0bfaf7a3ea6f4 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Thu, 18 Aug 2016 20:09:22 -0700 Subject: [PATCH 21/28] [buttonmaps/cocoa] Update button map with axis configuration --- .../xml/cocoa/Wireless_360_Controller_v045E_p028E_15b_6a.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/peripheral.joystick/resources/buttonmaps/xml/cocoa/Wireless_360_Controller_v045E_p028E_15b_6a.xml b/peripheral.joystick/resources/buttonmaps/xml/cocoa/Wireless_360_Controller_v045E_p028E_15b_6a.xml index b85c89c1..c4c5e2ea 100644 --- a/peripheral.joystick/resources/buttonmaps/xml/cocoa/Wireless_360_Controller_v045E_p028E_15b_6a.xml +++ b/peripheral.joystick/resources/buttonmaps/xml/cocoa/Wireless_360_Controller_v045E_p028E_15b_6a.xml @@ -1,6 +1,10 @@ + + + + From 9db0295533e64ed59a0eb06b763304e34c9492c0 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Thu, 18 Aug 2016 21:21:55 -0700 Subject: [PATCH 22/28] [buttonmaps/udev] Add first udev button map for wireless 360 controller --- ...eless_Receiver_XBOX_v045E_p0291_15b_6a.xml | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 peripheral.joystick/resources/buttonmaps/xml/udev/Xbox_360_Wireless_Receiver_XBOX_v045E_p0291_15b_6a.xml diff --git a/peripheral.joystick/resources/buttonmaps/xml/udev/Xbox_360_Wireless_Receiver_XBOX_v045E_p0291_15b_6a.xml b/peripheral.joystick/resources/buttonmaps/xml/udev/Xbox_360_Wireless_Receiver_XBOX_v045E_p0291_15b_6a.xml new file mode 100644 index 00000000..239d4802 --- /dev/null +++ b/peripheral.joystick/resources/buttonmaps/xml/udev/Xbox_360_Wireless_Receiver_XBOX_v045E_p0291_15b_6a.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 7a784bc8bd6f3e023b981736c7ec4f2d6543cf3f Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Thu, 18 Aug 2016 21:39:40 -0700 Subject: [PATCH 23/28] [udev] Code improvements * Fix rounding error (could cause motors to run forever) * Fix possible integer overflow * Remove unused code * Initialize motor strengths * Remove unused variable and replace struct with uint16_t --- src/api/udev/JoystickUdev.cpp | 36 ++++++++++++++--------------------- src/api/udev/JoystickUdev.h | 8 +------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/src/api/udev/JoystickUdev.cpp b/src/api/udev/JoystickUdev.cpp index 128f2304..df5dec51 100644 --- a/src/api/udev/JoystickUdev.cpp +++ b/src/api/udev/JoystickUdev.cpp @@ -42,19 +42,6 @@ using namespace JOYSTICK; // From RetroArch #define NBITS(x) ((((x) - 1) / (sizeof(long) * CHAR_BIT)) + 1) -// From RetroArch -static inline int16_t compute_axis(const input_absinfo& info, int value) -{ - int range = info.maximum - info.minimum; - int axis = (value - info.minimum) * 0xffffll / range - 0x7fffll; - - if (axis > 0x7fff) - return 0x7fff; - else if (axis < -0x7fff) - return -0x7fff; - return axis; -} - CJoystickUdev::CJoystickUdev(udev_device* dev, const char* path) : CJoystick(INTERFACE_UDEV), m_dev(dev), @@ -65,6 +52,9 @@ CJoystickUdev::CJoystickUdev(udev_device* dev, const char* path) m_has_set_ff(false), m_effect(-1) { + for (unsigned int i = 0; i < MOTOR_COUNT; i++) + m_motors[i] = 0; + // Must initialize in the constructor to fill out joystick properties Initialize(); } @@ -276,24 +266,26 @@ bool CJoystickUdev::SetMotor(unsigned int motorIndex, float magnitude) if (motorIndex >= MotorCount() || magnitude < 0.0f) return false; - uint16_t strength = std::min(0xffff, static_cast(magnitude * 0xffff)); + if (magnitude < 0.01f) + magnitude = 0.0f; - if (strength < 0.01f) - strength = 0.0f; + uint16_t strength = std::min(0xffff, static_cast(magnitude * 0xffff)); bool bChanged = false; - if (strength != m_motors[motorIndex].strength) + if (strength != m_motors[motorIndex]) bChanged = true; if (!bChanged) return true; - uint32_t oldStrength = m_motors[MOTOR_STRONG].strength + m_motors[MOTOR_WEAK].strength; + uint32_t oldStrength = static_cast(m_motors[MOTOR_STRONG]) + + static_cast(m_motors[MOTOR_WEAK]); - m_motors[motorIndex].strength = strength; + m_motors[motorIndex] = strength; - uint32_t newStrength = m_motors[MOTOR_STRONG].strength + m_motors[MOTOR_WEAK].strength; + uint32_t newStrength = static_cast(m_motors[MOTOR_STRONG]) + + static_cast(m_motors[MOTOR_WEAK]); if (newStrength > 0) { @@ -304,8 +296,8 @@ bool CJoystickUdev::SetMotor(unsigned int motorIndex, float magnitude) e.type = FF_RUMBLE; e.id = old_effect; - e.u.rumble.strong_magnitude = m_motors[MOTOR_STRONG].strength; - e.u.rumble.weak_magnitude = m_motors[MOTOR_WEAK].strength; + e.u.rumble.strong_magnitude = m_motors[MOTOR_STRONG]; + e.u.rumble.weak_magnitude = m_motors[MOTOR_WEAK]; if (ioctl(m_fd, EVIOCSFF, &e) < 0) { diff --git a/src/api/udev/JoystickUdev.h b/src/api/udev/JoystickUdev.h index ad7492ab..b19fa63a 100644 --- a/src/api/udev/JoystickUdev.h +++ b/src/api/udev/JoystickUdev.h @@ -72,12 +72,6 @@ namespace JOYSTICK bool SetMotor(unsigned int motorIndex, float magnitude); private: - struct Motor - { - uint16_t strength; - uint16_t configured_strength; - }; - struct Axis { unsigned int axisIndex; @@ -99,6 +93,6 @@ namespace JOYSTICK // Joystick properties std::map m_button_bind; // Maps keycodes -> button std::map m_axes_bind; // Maps keycodes -> axis and axis info - Motor m_motors[MOTOR_COUNT]; + uint16_t m_motors[MOTOR_COUNT]; }; } From 235062d51397da4e8c43cbe84a68a69a7465fe70 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Thu, 18 Aug 2016 22:35:00 -0700 Subject: [PATCH 24/28] [udev] Fix motors not stopping when both are stopped at the same time --- src/api/udev/JoystickUdev.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/api/udev/JoystickUdev.cpp b/src/api/udev/JoystickUdev.cpp index df5dec51..d0e62bd6 100644 --- a/src/api/udev/JoystickUdev.cpp +++ b/src/api/udev/JoystickUdev.cpp @@ -28,6 +28,7 @@ #include #include #include +#include using namespace JOYSTICK; @@ -320,6 +321,10 @@ bool CJoystickUdev::SetMotor(unsigned int motorIndex, float magnitude) play.code = m_effect; play.value = bIsPlaying; + // udev fails to stop playing if event is written too soon after last ioctl + if (!bIsPlaying) + usleep(2500); + if (write(m_fd, &play, sizeof(play)) < (ssize_t)sizeof(play)) { esyslog("[udev]: Failed to play rumble effect on \"%s\"", Name().c_str()); From b15053e02cfb5ebc286afc195d66ad4eee8ffc15 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Fri, 19 Aug 2016 10:04:02 -0700 Subject: [PATCH 25/28] [storage] v1.0.22: Sanitize loaded button maps for duplicate primitives --- src/storage/ButtonMap.cpp | 88 ++++++++++++++++++++++++++++++++++++ src/storage/ButtonMap.h | 2 + src/storage/StorageUtils.cpp | 34 ++++++++++++++ src/storage/StorageUtils.h | 3 ++ 4 files changed, 127 insertions(+) diff --git a/src/storage/ButtonMap.cpp b/src/storage/ButtonMap.cpp index 02825b9b..3c4c3010 100644 --- a/src/storage/ButtonMap.cpp +++ b/src/storage/ButtonMap.cpp @@ -20,9 +20,14 @@ #include "ButtonMap.h" #include "Device.h" +#include "log/Log.h" +#include "storage/StorageUtils.h" +#include "kodi_peripheral_utils.hpp" #include "p8-platform/util/timeutils.h" +#include + using namespace JOYSTICK; #define RESOURCE_LIFETIME_MS 2000 // 2 seconds @@ -101,8 +106,91 @@ bool CButtonMap::Refresh(void) if (!Load()) return false; + Sanitize(); + m_timestamp = now; } return true; } + +void CButtonMap::Sanitize() +{ + for (auto it = m_buttonMap.begin(); it != m_buttonMap.end(); ++it) + { + const std::string& controllerId = it->first; + FeatureVector& features = it->second; + + // Loop through features + for (unsigned int iFeature = 0; iFeature < features.size(); ++iFeature) + { + auto& feature = features[iFeature]; + + // Loop through feature's primitives + auto& primitives = feature.Primitives(); + for (unsigned int iPrimitive = 0; iPrimitive < primitives.size(); ++iPrimitive) + { + auto& primitive = primitives[iPrimitive]; + + if (primitive.Type() == JOYSTICK_DRIVER_PRIMITIVE_TYPE_UNKNOWN) + continue; + + bool bFound = false; + + // Search for prior feature with the primitive + ADDON::JoystickFeature existingFeature; + + for (unsigned int iExistingFeature = 0; iExistingFeature < iFeature; ++iExistingFeature) + { + const auto& existingPrimitives = features[iExistingFeature].Primitives(); + if (std::find(existingPrimitives.begin(), existingPrimitives.end(), primitive) != existingPrimitives.end()) + { + existingFeature = features[iExistingFeature]; + bFound = true; + break; + } + } + + if (!bFound) + { + // Search for primitive in prior primitives + for (unsigned int iExistingPrimitive = 0; iExistingPrimitive < iPrimitive; ++iExistingPrimitive) + { + if (primitives[iExistingPrimitive] == primitive) + { + bFound = true; + break; + } + } + } + + // Invalid the primitive if it has already been seen + if (bFound) + { + esyslog("%s: %s (%s) conflicts with %s (%s), skipping", + controllerId.c_str(), + CStorageUtils::PrimitiveToString(primitive).c_str(), + existingFeature.Type() != JOYSTICK_FEATURE_TYPE_UNKNOWN ? existingFeature.Name().c_str() : feature.Name().c_str(), + CStorageUtils::PrimitiveToString(primitive).c_str(), + feature.Name().c_str()); + + primitive = ADDON::DriverPrimitive(); + } + } + } + + // Erase invalid features + features.erase(std::remove_if(features.begin(), features.end(), + [](const ADDON::JoystickFeature& feature) + { + auto& primitives = feature.Primitives(); + + return std::find_if(primitives.begin(), primitives.end(), + [](const ADDON::DriverPrimitive& primitive) + { + return primitive.Type() != JOYSTICK_DRIVER_PRIMITIVE_TYPE_UNKNOWN; + }) == primitives.end(); + + }), features.end()); + } +} diff --git a/src/storage/ButtonMap.h b/src/storage/ButtonMap.h index 2731b42c..535d1aae 100644 --- a/src/storage/ButtonMap.h +++ b/src/storage/ButtonMap.h @@ -54,6 +54,8 @@ namespace JOYSTICK virtual bool Load(void) = 0; virtual bool Save(void) const = 0; + void Sanitize(); + const std::string m_strResourcePath; DevicePtr m_device; ButtonMap m_buttonMap; diff --git a/src/storage/StorageUtils.cpp b/src/storage/StorageUtils.cpp index b6f02594..f9c1d554 100644 --- a/src/storage/StorageUtils.cpp +++ b/src/storage/StorageUtils.cpp @@ -109,3 +109,37 @@ std::string CStorageUtils::FormatHexString(int iVal) return StringUtils::Format("%04X", iVal); }; + +std::string CStorageUtils::PrimitiveToString(const ADDON::DriverPrimitive& primitive) +{ + switch (primitive.Type()) + { + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_BUTTON: + return StringUtils::Format("button %u", primitive.DriverIndex()); + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_HAT_DIRECTION: + switch (primitive.HatDirection()) + { + case JOYSTICK_DRIVER_HAT_UP: + return StringUtils::Format("hat up"); + case JOYSTICK_DRIVER_HAT_RIGHT: + return StringUtils::Format("hat right"); + case JOYSTICK_DRIVER_HAT_DOWN: + return StringUtils::Format("hat down"); + case JOYSTICK_DRIVER_HAT_LEFT: + return StringUtils::Format("hat left"); + default: + break; + } + break; + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_SEMIAXIS: + return StringUtils::Format("axis %s%u", + primitive.SemiAxisDirection() == JOYSTICK_DRIVER_SEMIAXIS_POSITIVE ? "+" : "-", + primitive.DriverIndex()); + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_MOTOR: + return StringUtils::Format("motor %u", primitive.DriverIndex()); + default: + break; + } + + return ""; +} diff --git a/src/storage/StorageUtils.h b/src/storage/StorageUtils.h index 42d03746..13332c2c 100644 --- a/src/storage/StorageUtils.h +++ b/src/storage/StorageUtils.h @@ -24,6 +24,7 @@ namespace ADDON { + struct DriverPrimitive; class Joystick; } @@ -72,6 +73,8 @@ namespace JOYSTICK */ static std::string FormatHexString(int iVal); + static std::string PrimitiveToString(const ADDON::DriverPrimitive& primitive); + private: static std::set m_existingDirs; // Cache list of existing dirs }; From 9bf8e0dba1838355958b6db76cf2db06555061a3 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Fri, 19 Aug 2016 11:53:17 -0700 Subject: [PATCH 26/28] [buttonmapper] Fix merging features with duplicate driver primitives --- src/buttonmapper/ButtonMapper.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/buttonmapper/ButtonMapper.cpp b/src/buttonmapper/ButtonMapper.cpp index 48946bc4..b557abbf 100644 --- a/src/buttonmapper/ButtonMapper.cpp +++ b/src/buttonmapper/ButtonMapper.cpp @@ -96,12 +96,30 @@ void CButtonMapper::MergeFeatures(FeatureVector& features, const FeatureVector& { for (const ADDON::JoystickFeature& newFeature : newFeatures) { - const bool bFound = std::find_if(features.begin(), features.end(), - [newFeature](const ADDON::JoystickFeature& feature) + // Check for duplicate feature name + bool bFound = std::find_if(features.begin(), features.end(), + [&newFeature](const ADDON::JoystickFeature& feature) { return feature.Name() == newFeature.Name(); }) != features.end(); + // Check for duplicate driver primitives + if (!bFound) + { + const auto& newPrimitives = newFeature.Primitives(); + + bFound = std::find_if(features.begin(), features.end(), + [&newPrimitives](const ADDON::JoystickFeature& feature) + { + for (const auto& primitive : feature.Primitives()) + { + if (std::find(newPrimitives.begin(), newPrimitives.end(), primitive) != newPrimitives.end()) + return true; // Found primitive + } + return false; // Didn't find primitive + }) != features.end(); + } + if (!bFound) features.push_back(newFeature); } From 96ddf8c00cdb431648e15a45a0c26b9f820eb060 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Fri, 19 Aug 2016 11:54:14 -0700 Subject: [PATCH 27/28] [buttonmaps/xarcade] Add buttonmaps for xarcade driver https://github.com/kodi-game/peripheral.xarcade --- ...ade_Tankstick_Player_1_vAA55_p0101_14b.xml | 117 ++++++++++++++++++ ...ade_Tankstick_Player_2_vAA55_p0101_14b.xml | 117 ++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 peripheral.joystick/resources/buttonmaps/xml/xarcade/X-Arcade_Tankstick_Player_1_vAA55_p0101_14b.xml create mode 100644 peripheral.joystick/resources/buttonmaps/xml/xarcade/X-Arcade_Tankstick_Player_2_vAA55_p0101_14b.xml diff --git a/peripheral.joystick/resources/buttonmaps/xml/xarcade/X-Arcade_Tankstick_Player_1_vAA55_p0101_14b.xml b/peripheral.joystick/resources/buttonmaps/xml/xarcade/X-Arcade_Tankstick_Player_1_vAA55_p0101_14b.xml new file mode 100644 index 00000000..e381451e --- /dev/null +++ b/peripheral.joystick/resources/buttonmaps/xml/xarcade/X-Arcade_Tankstick_Player_1_vAA55_p0101_14b.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/peripheral.joystick/resources/buttonmaps/xml/xarcade/X-Arcade_Tankstick_Player_2_vAA55_p0101_14b.xml b/peripheral.joystick/resources/buttonmaps/xml/xarcade/X-Arcade_Tankstick_Player_2_vAA55_p0101_14b.xml new file mode 100644 index 00000000..0b5e2b9b --- /dev/null +++ b/peripheral.joystick/resources/buttonmaps/xml/xarcade/X-Arcade_Tankstick_Player_2_vAA55_p0101_14b.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 0fa9bfcaa823cf78396c9ea31a6014998e709df6 Mon Sep 17 00:00:00 2001 From: Garrett Brown Date: Sun, 21 Aug 2016 13:32:49 -0700 Subject: [PATCH 28/28] Revert "Remove deadzone setting" This reverts commit 513bf5cd377d57cc0967bd4842ad9c94c5e08d72. --- peripheral.joystick/resources/settings.xml | 6 ++++++ src/api/Joystick.cpp | 17 ++++++++++++++++- src/settings/Settings.cpp | 7 +++++++ src/settings/Settings.h | 15 +++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 peripheral.joystick/resources/settings.xml diff --git a/peripheral.joystick/resources/settings.xml b/peripheral.joystick/resources/settings.xml new file mode 100644 index 00000000..7be0f61b --- /dev/null +++ b/peripheral.joystick/resources/settings.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/api/Joystick.cpp b/src/api/Joystick.cpp index 2f32761b..140b0b59 100644 --- a/src/api/Joystick.cpp +++ b/src/api/Joystick.cpp @@ -220,7 +220,7 @@ void CJoystick::SetAxisValue(unsigned int axisIndex, JOYSTICK_STATE_AXIS axisVal axisValue = CONSTRAIN(-1.0f, axisValue, 1.0f); if (axisIndex < m_stateBuffer.axes.size()) - m_stateBuffer.axes[axisIndex] = m_axisFilters[axisIndex]->Filter(axisValue); + m_stateBuffer.axes[axisIndex] = ScaleDeadzone(m_axisFilters[axisIndex]->Filter(axisValue)); } void CJoystick::SetAxisValue(unsigned int axisIndex, long value, long maxAxisAmount) @@ -242,3 +242,18 @@ float CJoystick::NormalizeAxis(long value, long maxAxisAmount) { return 1.0f * CONSTRAIN(-maxAxisAmount, value, maxAxisAmount) / maxAxisAmount; } + +float CJoystick::ScaleDeadzone(float value) +{ + const float deadzone = CSettings::Get().Deadzone(); + + if (deadzone >= 1.0f) + return 0.0f; + + if (value > deadzone) + return (float)(value - deadzone) / (float)(1.0f - deadzone); + else if (value < -deadzone) + return (float)(value + deadzone) / (float)(1.0f - deadzone); + + return 0.0f; +} diff --git a/src/settings/Settings.cpp b/src/settings/Settings.cpp index 2a67bda2..0c3b3802 100644 --- a/src/settings/Settings.cpp +++ b/src/settings/Settings.cpp @@ -23,10 +23,12 @@ using namespace JOYSTICK; +#define SETTING_DEADZONE "deadzone" #define SETTING_RETROARCH_CONFIG "retroarchconfig" CSettings::CSettings(void) : m_bInitialized(false), + m_deadzone(0.0f), m_bGenerateRetroArchConfigs(false) { } @@ -39,6 +41,11 @@ CSettings& CSettings::Get(void) void CSettings::SetSetting(const std::string& strName, const void* value) { + if (strName == SETTING_DEADZONE) + { + m_deadzone = *static_cast(value); + dsyslog("Setting \"%s\" set to %f", SETTING_DEADZONE, m_deadzone); + } if (strName == SETTING_RETROARCH_CONFIG) { m_bGenerateRetroArchConfigs = *static_cast(value); diff --git a/src/settings/Settings.h b/src/settings/Settings.h index fdda7b83..0a248d8c 100644 --- a/src/settings/Settings.h +++ b/src/settings/Settings.h @@ -35,6 +35,21 @@ namespace JOYSTICK bool IsInitialized(void) const { return m_bInitialized; } + /*! + * \brief The analog stick deadzone + * + * This is applied to each axis. Axis is scaled appropriately, so position + * is continuous from -1.0 to 1.0: + * + * | / 1.0 + * | / + * __|__/ + * / | + * / |--| Deadzone + * -1.0 / | + */ + float Deadzone(void) const { return m_deadzone; } + /*! * \brief Generate .cfg files compatible with RetroArch's joypad autoconfig */