diff options
Diffstat (limited to 'xbmc/peripherals/addons')
-rw-r--r-- | xbmc/peripherals/addons/AddonButtonMap.cpp | 734 | ||||
-rw-r--r-- | xbmc/peripherals/addons/AddonButtonMap.h | 146 | ||||
-rw-r--r-- | xbmc/peripherals/addons/AddonButtonMapping.cpp | 139 | ||||
-rw-r--r-- | xbmc/peripherals/addons/AddonButtonMapping.h | 72 | ||||
-rw-r--r-- | xbmc/peripherals/addons/AddonInputHandling.cpp | 192 | ||||
-rw-r--r-- | xbmc/peripherals/addons/AddonInputHandling.h | 108 | ||||
-rw-r--r-- | xbmc/peripherals/addons/CMakeLists.txt | 13 | ||||
-rw-r--r-- | xbmc/peripherals/addons/PeripheralAddon.cpp | 990 | ||||
-rw-r--r-- | xbmc/peripherals/addons/PeripheralAddon.h | 178 | ||||
-rw-r--r-- | xbmc/peripherals/addons/PeripheralAddonTranslator.cpp | 449 | ||||
-rw-r--r-- | xbmc/peripherals/addons/PeripheralAddonTranslator.h | 62 |
11 files changed, 3083 insertions, 0 deletions
diff --git a/xbmc/peripherals/addons/AddonButtonMap.cpp b/xbmc/peripherals/addons/AddonButtonMap.cpp new file mode 100644 index 0000000..0d76ec9 --- /dev/null +++ b/xbmc/peripherals/addons/AddonButtonMap.cpp @@ -0,0 +1,734 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AddonButtonMap.h" + +#include "PeripheralAddonTranslator.h" +#include "input/joysticks/JoystickUtils.h" +#include "peripherals/Peripherals.h" +#include "peripherals/devices/Peripheral.h" +#include "utils/log.h" + +#include <algorithm> +#include <assert.h> +#include <mutex> +#include <vector> + +using namespace KODI; +using namespace JOYSTICK; +using namespace PERIPHERALS; + +CAddonButtonMap::CAddonButtonMap(CPeripheral* device, + const std::weak_ptr<CPeripheralAddon>& addon, + const std::string& strControllerId, + CPeripherals& manager) + : m_device(device), m_addon(addon), m_strControllerId(strControllerId), m_manager(manager) +{ + auto peripheralAddon = m_addon.lock(); + assert(peripheralAddon != nullptr); + + peripheralAddon->RegisterButtonMap(device, this); +} + +CAddonButtonMap::~CAddonButtonMap(void) +{ + if (auto addon = m_addon.lock()) + addon->UnregisterButtonMap(this); +} + +std::string CAddonButtonMap::Location(void) const +{ + return m_device->Location(); +} + +bool CAddonButtonMap::Load(void) +{ + std::string controllerAppearance; + FeatureMap features; + DriverMap driverMap; + PrimitiveVector ignoredPrimitives; + + bool bSuccess = false; + if (auto addon = m_addon.lock()) + { + bSuccess |= addon->GetFeatures(m_device, m_strControllerId, features); + bSuccess |= addon->GetIgnoredPrimitives(m_device, ignoredPrimitives); + } + + if (features.empty()) + { + // Check if we can initialize a buttonmap from the peripheral bus + PeripheralBusPtr peripheralBus = m_manager.GetBusByType(m_device->GetBusType()); + if (peripheralBus) + { + CLog::Log(LOGDEBUG, + "Buttonmap not found for {}, attempting to initialize from peripheral bus", + m_device->Location()); + if (peripheralBus->InitializeButtonMap(*m_device, *this)) + { + bSuccess = true; + + if (auto addon = m_addon.lock()) + { + addon->GetFeatures(m_device, m_strControllerId, features); + addon->GetIgnoredPrimitives(m_device, ignoredPrimitives); + } + } + } + } + + // GetFeatures() was changed to always return false if no features were + // retrieved. Check here, just in case its contract is changed or violated in + // the future. + if (bSuccess && features.empty()) + bSuccess = false; + + if (bSuccess) + driverMap = CreateLookupTable(features); + else + CLog::Log(LOGDEBUG, "Failed to load button map for \"{}\"", m_device->Location()); + + { + std::unique_lock<CCriticalSection> lock(m_mutex); + m_controllerAppearance = std::move(controllerAppearance); + m_features = std::move(features); + m_driverMap = std::move(driverMap); + m_ignoredPrimitives = CPeripheralAddonTranslator::TranslatePrimitives(ignoredPrimitives); + } + + return true; +} + +void CAddonButtonMap::Reset(void) +{ + if (auto addon = m_addon.lock()) + addon->ResetButtonMap(m_device, m_strControllerId); +} + +bool CAddonButtonMap::IsEmpty(void) const +{ + std::unique_lock<CCriticalSection> lock(m_mutex); + + return m_driverMap.empty(); +} + +std::string CAddonButtonMap::GetAppearance() const +{ + std::unique_lock<CCriticalSection> lock(m_mutex); + + return m_controllerAppearance; +} + +bool CAddonButtonMap::SetAppearance(const std::string& controllerId) const +{ + return false; +} + +bool CAddonButtonMap::GetFeature(const CDriverPrimitive& primitive, FeatureName& feature) +{ + std::unique_lock<CCriticalSection> lock(m_mutex); + + DriverMap::const_iterator it = m_driverMap.find(primitive); + if (it != m_driverMap.end()) + { + feature = it->second; + return true; + } + + return false; +} + +FEATURE_TYPE CAddonButtonMap::GetFeatureType(const FeatureName& feature) +{ + FEATURE_TYPE type = FEATURE_TYPE::UNKNOWN; + + std::unique_lock<CCriticalSection> lock(m_mutex); + + FeatureMap::const_iterator it = m_features.find(feature); + if (it != m_features.end()) + type = CPeripheralAddonTranslator::TranslateFeatureType(it->second.Type()); + + return type; +} + +bool CAddonButtonMap::GetScalar(const FeatureName& feature, CDriverPrimitive& primitive) +{ + bool retVal(false); + + std::unique_lock<CCriticalSection> lock(m_mutex); + + FeatureMap::const_iterator it = m_features.find(feature); + if (it != m_features.end()) + { + const kodi::addon::JoystickFeature& addonFeature = it->second; + + if (addonFeature.Type() == JOYSTICK_FEATURE_TYPE_SCALAR || + addonFeature.Type() == JOYSTICK_FEATURE_TYPE_MOTOR) + { + primitive = CPeripheralAddonTranslator::TranslatePrimitive( + addonFeature.Primitive(JOYSTICK_SCALAR_PRIMITIVE)); + retVal = true; + } + } + + return retVal; +} + +void CAddonButtonMap::AddScalar(const FeatureName& feature, const CDriverPrimitive& primitive) +{ + const bool bMotor = (primitive.Type() == PRIMITIVE_TYPE::MOTOR); + + kodi::addon::JoystickFeature scalar(feature, bMotor ? JOYSTICK_FEATURE_TYPE_MOTOR + : JOYSTICK_FEATURE_TYPE_SCALAR); + scalar.SetPrimitive(JOYSTICK_SCALAR_PRIMITIVE, + CPeripheralAddonTranslator::TranslatePrimitive(primitive)); + + if (auto addon = m_addon.lock()) + addon->MapFeature(m_device, m_strControllerId, scalar); +} + +bool CAddonButtonMap::GetAnalogStick(const FeatureName& feature, + JOYSTICK::ANALOG_STICK_DIRECTION direction, + JOYSTICK::CDriverPrimitive& primitive) +{ + bool retVal(false); + + std::unique_lock<CCriticalSection> lock(m_mutex); + + FeatureMap::const_iterator it = m_features.find(feature); + if (it != m_features.end()) + { + const kodi::addon::JoystickFeature& addonFeature = it->second; + + if (addonFeature.Type() == JOYSTICK_FEATURE_TYPE_ANALOG_STICK) + { + primitive = CPeripheralAddonTranslator::TranslatePrimitive( + addonFeature.Primitive(GetAnalogStickIndex(direction))); + retVal = primitive.IsValid(); + } + } + + return retVal; +} + +void CAddonButtonMap::AddAnalogStick(const FeatureName& feature, + JOYSTICK::ANALOG_STICK_DIRECTION direction, + const JOYSTICK::CDriverPrimitive& primitive) +{ + using namespace JOYSTICK; + + JOYSTICK_FEATURE_PRIMITIVE primitiveIndex = GetAnalogStickIndex(direction); + kodi::addon::DriverPrimitive addonPrimitive = + CPeripheralAddonTranslator::TranslatePrimitive(primitive); + + kodi::addon::JoystickFeature analogStick(feature, JOYSTICK_FEATURE_TYPE_ANALOG_STICK); + + { + std::unique_lock<CCriticalSection> lock(m_mutex); + auto it = m_features.find(feature); + if (it != m_features.end()) + analogStick = it->second; + } + + const bool bModified = (primitive != CPeripheralAddonTranslator::TranslatePrimitive( + analogStick.Primitive(primitiveIndex))); + if (bModified) + analogStick.SetPrimitive(primitiveIndex, addonPrimitive); + + if (auto addon = m_addon.lock()) + addon->MapFeature(m_device, m_strControllerId, analogStick); + + // Because each direction is mapped individually, we need to refresh the + // feature each time a new direction is mapped. + if (bModified) + Load(); +} + +bool CAddonButtonMap::GetRelativePointer(const FeatureName& feature, + JOYSTICK::RELATIVE_POINTER_DIRECTION direction, + JOYSTICK::CDriverPrimitive& primitive) +{ + bool retVal(false); + + std::unique_lock<CCriticalSection> lock(m_mutex); + + FeatureMap::const_iterator it = m_features.find(feature); + if (it != m_features.end()) + { + const kodi::addon::JoystickFeature& addonFeature = it->second; + + if (addonFeature.Type() == JOYSTICK_FEATURE_TYPE_RELPOINTER) + { + primitive = CPeripheralAddonTranslator::TranslatePrimitive( + addonFeature.Primitive(GetRelativePointerIndex(direction))); + retVal = primitive.IsValid(); + } + } + + return retVal; +} + +void CAddonButtonMap::AddRelativePointer(const FeatureName& feature, + JOYSTICK::RELATIVE_POINTER_DIRECTION direction, + const JOYSTICK::CDriverPrimitive& primitive) +{ + using namespace JOYSTICK; + + JOYSTICK_FEATURE_PRIMITIVE primitiveIndex = GetRelativePointerIndex(direction); + kodi::addon::DriverPrimitive addonPrimitive = + CPeripheralAddonTranslator::TranslatePrimitive(primitive); + + kodi::addon::JoystickFeature relPointer(feature, JOYSTICK_FEATURE_TYPE_RELPOINTER); + + { + std::unique_lock<CCriticalSection> lock(m_mutex); + auto it = m_features.find(feature); + if (it != m_features.end()) + relPointer = it->second; + } + + const bool bModified = (primitive != CPeripheralAddonTranslator::TranslatePrimitive( + relPointer.Primitive(primitiveIndex))); + if (bModified) + relPointer.SetPrimitive(primitiveIndex, addonPrimitive); + + if (auto addon = m_addon.lock()) + addon->MapFeature(m_device, m_strControllerId, relPointer); + + // Because each direction is mapped individually, we need to refresh the + // feature each time a new direction is mapped. + if (bModified) + Load(); +} + +bool CAddonButtonMap::GetAccelerometer(const FeatureName& feature, + CDriverPrimitive& positiveX, + CDriverPrimitive& positiveY, + CDriverPrimitive& positiveZ) +{ + bool retVal(false); + + std::unique_lock<CCriticalSection> lock(m_mutex); + + FeatureMap::const_iterator it = m_features.find(feature); + if (it != m_features.end()) + { + const kodi::addon::JoystickFeature& addonFeature = it->second; + + if (addonFeature.Type() == JOYSTICK_FEATURE_TYPE_ACCELEROMETER) + { + positiveX = CPeripheralAddonTranslator::TranslatePrimitive( + addonFeature.Primitive(JOYSTICK_ACCELEROMETER_POSITIVE_X)); + positiveY = CPeripheralAddonTranslator::TranslatePrimitive( + addonFeature.Primitive(JOYSTICK_ACCELEROMETER_POSITIVE_Y)); + positiveZ = CPeripheralAddonTranslator::TranslatePrimitive( + addonFeature.Primitive(JOYSTICK_ACCELEROMETER_POSITIVE_Z)); + retVal = true; + } + } + + return retVal; +} + +void CAddonButtonMap::AddAccelerometer(const FeatureName& feature, + const CDriverPrimitive& positiveX, + const CDriverPrimitive& positiveY, + const CDriverPrimitive& positiveZ) +{ + using namespace JOYSTICK; + + kodi::addon::JoystickFeature accelerometer(feature, JOYSTICK_FEATURE_TYPE_ACCELEROMETER); + + accelerometer.SetPrimitive(JOYSTICK_ACCELEROMETER_POSITIVE_X, + CPeripheralAddonTranslator::TranslatePrimitive(positiveX)); + accelerometer.SetPrimitive(JOYSTICK_ACCELEROMETER_POSITIVE_Y, + CPeripheralAddonTranslator::TranslatePrimitive(positiveY)); + accelerometer.SetPrimitive(JOYSTICK_ACCELEROMETER_POSITIVE_Z, + CPeripheralAddonTranslator::TranslatePrimitive(positiveZ)); + + if (auto addon = m_addon.lock()) + addon->MapFeature(m_device, m_strControllerId, accelerometer); +} + +bool CAddonButtonMap::GetWheel(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::WHEEL_DIRECTION direction, + KODI::JOYSTICK::CDriverPrimitive& primitive) +{ + bool retVal(false); + + std::unique_lock<CCriticalSection> lock(m_mutex); + + FeatureMap::const_iterator it = m_features.find(feature); + if (it != m_features.end()) + { + const kodi::addon::JoystickFeature& addonFeature = it->second; + + if (addonFeature.Type() == JOYSTICK_FEATURE_TYPE_WHEEL) + { + primitive = CPeripheralAddonTranslator::TranslatePrimitive( + addonFeature.Primitive(GetPrimitiveIndex(direction))); + retVal = primitive.IsValid(); + } + } + + return retVal; +} + +void CAddonButtonMap::AddWheel(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::WHEEL_DIRECTION direction, + const KODI::JOYSTICK::CDriverPrimitive& primitive) +{ + using namespace JOYSTICK; + + JOYSTICK_FEATURE_PRIMITIVE primitiveIndex = GetPrimitiveIndex(direction); + kodi::addon::DriverPrimitive addonPrimitive = + CPeripheralAddonTranslator::TranslatePrimitive(primitive); + + kodi::addon::JoystickFeature joystickFeature(feature, JOYSTICK_FEATURE_TYPE_WHEEL); + + { + std::unique_lock<CCriticalSection> lock(m_mutex); + auto it = m_features.find(feature); + if (it != m_features.end()) + joystickFeature = it->second; + } + + const bool bModified = (primitive != CPeripheralAddonTranslator::TranslatePrimitive( + joystickFeature.Primitive(primitiveIndex))); + if (bModified) + joystickFeature.SetPrimitive(primitiveIndex, addonPrimitive); + + if (auto addon = m_addon.lock()) + addon->MapFeature(m_device, m_strControllerId, joystickFeature); + + // Because each direction is mapped individually, we need to refresh the + // feature each time a new direction is mapped. + if (bModified) + Load(); +} + +bool CAddonButtonMap::GetThrottle(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::THROTTLE_DIRECTION direction, + KODI::JOYSTICK::CDriverPrimitive& primitive) +{ + bool retVal(false); + + std::unique_lock<CCriticalSection> lock(m_mutex); + + FeatureMap::const_iterator it = m_features.find(feature); + if (it != m_features.end()) + { + const kodi::addon::JoystickFeature& addonFeature = it->second; + + if (addonFeature.Type() == JOYSTICK_FEATURE_TYPE_THROTTLE) + { + primitive = CPeripheralAddonTranslator::TranslatePrimitive( + addonFeature.Primitive(GetPrimitiveIndex(direction))); + retVal = primitive.IsValid(); + } + } + + return retVal; +} + +void CAddonButtonMap::AddThrottle(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::THROTTLE_DIRECTION direction, + const KODI::JOYSTICK::CDriverPrimitive& primitive) +{ + using namespace JOYSTICK; + + JOYSTICK_FEATURE_PRIMITIVE primitiveIndex = GetPrimitiveIndex(direction); + kodi::addon::DriverPrimitive addonPrimitive = + CPeripheralAddonTranslator::TranslatePrimitive(primitive); + + kodi::addon::JoystickFeature joystickFeature(feature, JOYSTICK_FEATURE_TYPE_THROTTLE); + + { + std::unique_lock<CCriticalSection> lock(m_mutex); + auto it = m_features.find(feature); + if (it != m_features.end()) + joystickFeature = it->second; + } + + const bool bModified = (primitive != CPeripheralAddonTranslator::TranslatePrimitive( + joystickFeature.Primitive(primitiveIndex))); + if (bModified) + joystickFeature.SetPrimitive(primitiveIndex, addonPrimitive); + + if (auto addon = m_addon.lock()) + addon->MapFeature(m_device, m_strControllerId, joystickFeature); + + // Because each direction is mapped individually, we need to refresh the + // feature each time a new direction is mapped. + if (bModified) + Load(); +} + +bool CAddonButtonMap::GetKey(const FeatureName& feature, CDriverPrimitive& primitive) +{ + bool retVal(false); + + std::unique_lock<CCriticalSection> lock(m_mutex); + + FeatureMap::const_iterator it = m_features.find(feature); + if (it != m_features.end()) + { + const kodi::addon::JoystickFeature& addonFeature = it->second; + + if (addonFeature.Type() == JOYSTICK_FEATURE_TYPE_KEY) + { + primitive = CPeripheralAddonTranslator::TranslatePrimitive( + addonFeature.Primitive(JOYSTICK_SCALAR_PRIMITIVE)); + retVal = true; + } + } + + return retVal; +} + +void CAddonButtonMap::AddKey(const FeatureName& feature, const CDriverPrimitive& primitive) +{ + kodi::addon::JoystickFeature scalar(feature, JOYSTICK_FEATURE_TYPE_KEY); + scalar.SetPrimitive(JOYSTICK_SCALAR_PRIMITIVE, + CPeripheralAddonTranslator::TranslatePrimitive(primitive)); + + if (auto addon = m_addon.lock()) + addon->MapFeature(m_device, m_strControllerId, scalar); +} + +void CAddonButtonMap::SetIgnoredPrimitives( + const std::vector<JOYSTICK::CDriverPrimitive>& primitives) +{ + if (auto addon = m_addon.lock()) + addon->SetIgnoredPrimitives(m_device, + CPeripheralAddonTranslator::TranslatePrimitives(primitives)); +} + +bool CAddonButtonMap::IsIgnored(const JOYSTICK::CDriverPrimitive& primitive) +{ + return std::find(m_ignoredPrimitives.begin(), m_ignoredPrimitives.end(), primitive) != + m_ignoredPrimitives.end(); +} + +bool CAddonButtonMap::GetAxisProperties(unsigned int axisIndex, int& center, unsigned int& range) +{ + std::unique_lock<CCriticalSection> lock(m_mutex); + + for (const auto& it : m_driverMap) + { + const CDriverPrimitive& primitive = it.first; + + if (primitive.Type() != PRIMITIVE_TYPE::SEMIAXIS) + continue; + + if (primitive.Index() != axisIndex) + continue; + + center = primitive.Center(); + range = primitive.Range(); + return true; + } + + return false; +} + +void CAddonButtonMap::SaveButtonMap() +{ + if (auto addon = m_addon.lock()) + addon->SaveButtonMap(m_device); +} + +void CAddonButtonMap::RevertButtonMap() +{ + if (auto addon = m_addon.lock()) + addon->RevertButtonMap(m_device); +} + +CAddonButtonMap::DriverMap CAddonButtonMap::CreateLookupTable(const FeatureMap& features) +{ + using namespace JOYSTICK; + + DriverMap driverMap; + + for (const auto& it : features) + { + const kodi::addon::JoystickFeature& feature = it.second; + + switch (feature.Type()) + { + case JOYSTICK_FEATURE_TYPE_SCALAR: + case JOYSTICK_FEATURE_TYPE_KEY: + { + driverMap[CPeripheralAddonTranslator::TranslatePrimitive( + feature.Primitive(JOYSTICK_SCALAR_PRIMITIVE))] = it.first; + break; + } + + case JOYSTICK_FEATURE_TYPE_ANALOG_STICK: + { + std::vector<JOYSTICK_FEATURE_PRIMITIVE> primitives = { + JOYSTICK_ANALOG_STICK_UP, + JOYSTICK_ANALOG_STICK_DOWN, + JOYSTICK_ANALOG_STICK_RIGHT, + JOYSTICK_ANALOG_STICK_LEFT, + }; + + for (auto primitive : primitives) + driverMap[CPeripheralAddonTranslator::TranslatePrimitive(feature.Primitive(primitive))] = + it.first; + break; + } + + case JOYSTICK_FEATURE_TYPE_ACCELEROMETER: + { + std::vector<JOYSTICK_FEATURE_PRIMITIVE> primitives = { + JOYSTICK_ACCELEROMETER_POSITIVE_X, + JOYSTICK_ACCELEROMETER_POSITIVE_Y, + JOYSTICK_ACCELEROMETER_POSITIVE_Z, + }; + + for (auto primitive : primitives) + { + CDriverPrimitive translatedPrimitive = + CPeripheralAddonTranslator::TranslatePrimitive(feature.Primitive(primitive)); + driverMap[translatedPrimitive] = it.first; + + // Map opposite semiaxis + CDriverPrimitive oppositePrimitive = CDriverPrimitive( + translatedPrimitive.Index(), 0, translatedPrimitive.SemiAxisDirection() * -1, 1); + driverMap[oppositePrimitive] = it.first; + } + break; + } + + case JOYSTICK_FEATURE_TYPE_WHEEL: + { + std::vector<JOYSTICK_FEATURE_PRIMITIVE> primitives = { + JOYSTICK_WHEEL_LEFT, + JOYSTICK_WHEEL_RIGHT, + }; + + for (auto primitive : primitives) + driverMap[CPeripheralAddonTranslator::TranslatePrimitive(feature.Primitive(primitive))] = + it.first; + break; + } + + case JOYSTICK_FEATURE_TYPE_THROTTLE: + { + std::vector<JOYSTICK_FEATURE_PRIMITIVE> primitives = { + JOYSTICK_THROTTLE_UP, + JOYSTICK_THROTTLE_DOWN, + }; + + for (auto primitive : primitives) + driverMap[CPeripheralAddonTranslator::TranslatePrimitive(feature.Primitive(primitive))] = + it.first; + break; + } + + case JOYSTICK_FEATURE_TYPE_RELPOINTER: + { + std::vector<JOYSTICK_FEATURE_PRIMITIVE> primitives = { + JOYSTICK_RELPOINTER_UP, + JOYSTICK_RELPOINTER_DOWN, + JOYSTICK_RELPOINTER_RIGHT, + JOYSTICK_RELPOINTER_LEFT, + }; + + for (auto primitive : primitives) + driverMap[CPeripheralAddonTranslator::TranslatePrimitive(feature.Primitive(primitive))] = + it.first; + break; + } + + default: + break; + } + } + + return driverMap; +} + +JOYSTICK_FEATURE_PRIMITIVE CAddonButtonMap::GetAnalogStickIndex( + JOYSTICK::ANALOG_STICK_DIRECTION dir) +{ + using namespace JOYSTICK; + + switch (dir) + { + case ANALOG_STICK_DIRECTION::UP: + return JOYSTICK_ANALOG_STICK_UP; + case ANALOG_STICK_DIRECTION::DOWN: + return JOYSTICK_ANALOG_STICK_DOWN; + case ANALOG_STICK_DIRECTION::RIGHT: + return JOYSTICK_ANALOG_STICK_RIGHT; + case ANALOG_STICK_DIRECTION::LEFT: + return JOYSTICK_ANALOG_STICK_LEFT; + default: + break; + } + + return static_cast<JOYSTICK_FEATURE_PRIMITIVE>(0); +} + +JOYSTICK_FEATURE_PRIMITIVE CAddonButtonMap::GetRelativePointerIndex( + JOYSTICK::RELATIVE_POINTER_DIRECTION dir) +{ + using namespace JOYSTICK; + + switch (dir) + { + case RELATIVE_POINTER_DIRECTION::UP: + return JOYSTICK_RELPOINTER_UP; + case RELATIVE_POINTER_DIRECTION::DOWN: + return JOYSTICK_RELPOINTER_DOWN; + case RELATIVE_POINTER_DIRECTION::RIGHT: + return JOYSTICK_RELPOINTER_RIGHT; + case RELATIVE_POINTER_DIRECTION::LEFT: + return JOYSTICK_RELPOINTER_LEFT; + default: + break; + } + + return static_cast<JOYSTICK_FEATURE_PRIMITIVE>(0); +} + +JOYSTICK_FEATURE_PRIMITIVE CAddonButtonMap::GetPrimitiveIndex(JOYSTICK::WHEEL_DIRECTION dir) +{ + using namespace JOYSTICK; + + switch (dir) + { + case WHEEL_DIRECTION::RIGHT: + return JOYSTICK_WHEEL_RIGHT; + case WHEEL_DIRECTION::LEFT: + return JOYSTICK_WHEEL_LEFT; + default: + break; + } + + return static_cast<JOYSTICK_FEATURE_PRIMITIVE>(0); +} + +JOYSTICK_FEATURE_PRIMITIVE CAddonButtonMap::GetPrimitiveIndex(JOYSTICK::THROTTLE_DIRECTION dir) +{ + using namespace JOYSTICK; + + switch (dir) + { + case THROTTLE_DIRECTION::UP: + return JOYSTICK_THROTTLE_UP; + case THROTTLE_DIRECTION::DOWN: + return JOYSTICK_THROTTLE_DOWN; + default: + break; + } + + return static_cast<JOYSTICK_FEATURE_PRIMITIVE>(0); +} diff --git a/xbmc/peripherals/addons/AddonButtonMap.h b/xbmc/peripherals/addons/AddonButtonMap.h new file mode 100644 index 0000000..cade54b --- /dev/null +++ b/xbmc/peripherals/addons/AddonButtonMap.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "PeripheralAddon.h" // for FeatureMap +#include "input/joysticks/DriverPrimitive.h" +#include "input/joysticks/JoystickTypes.h" +#include "input/joysticks/interfaces/IButtonMap.h" +#include "peripherals/PeripheralTypes.h" +#include "threads/CriticalSection.h" + +namespace PERIPHERALS +{ +class CPeripheral; +class CPeripherals; + +class CAddonButtonMap : public KODI::JOYSTICK::IButtonMap +{ +public: + CAddonButtonMap(CPeripheral* device, + const std::weak_ptr<CPeripheralAddon>& addon, + const std::string& strControllerId, + CPeripherals& manager); + + ~CAddonButtonMap(void) override; + + // Implementation of IButtonMap + std::string ControllerID(void) const override { return m_strControllerId; } + + std::string Location(void) const override; + + bool Load(void) override; + + void Reset(void) override; + + bool IsEmpty(void) const override; + + std::string GetAppearance() const override; + + bool SetAppearance(const std::string& controllerId) const override; + + bool GetFeature(const KODI::JOYSTICK::CDriverPrimitive& primitive, + KODI::JOYSTICK::FeatureName& feature) override; + + KODI::JOYSTICK::FEATURE_TYPE GetFeatureType(const KODI::JOYSTICK::FeatureName& feature) override; + + bool GetScalar(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + void AddScalar(const KODI::JOYSTICK::FeatureName& feature, + const KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + bool GetAnalogStick(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::ANALOG_STICK_DIRECTION direction, + KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + void AddAnalogStick(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::ANALOG_STICK_DIRECTION direction, + const KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + bool GetRelativePointer(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::RELATIVE_POINTER_DIRECTION direction, + KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + void AddRelativePointer(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::RELATIVE_POINTER_DIRECTION direction, + const KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + bool GetAccelerometer(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::CDriverPrimitive& positiveX, + KODI::JOYSTICK::CDriverPrimitive& positiveY, + KODI::JOYSTICK::CDriverPrimitive& positiveZ) override; + + void AddAccelerometer(const KODI::JOYSTICK::FeatureName& feature, + const KODI::JOYSTICK::CDriverPrimitive& positiveX, + const KODI::JOYSTICK::CDriverPrimitive& positiveY, + const KODI::JOYSTICK::CDriverPrimitive& positiveZ) override; + + bool GetWheel(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::WHEEL_DIRECTION direction, + KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + void AddWheel(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::WHEEL_DIRECTION direction, + const KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + bool GetThrottle(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::THROTTLE_DIRECTION direction, + KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + void AddThrottle(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::THROTTLE_DIRECTION direction, + const KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + bool GetKey(const KODI::JOYSTICK::FeatureName& feature, + KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + void AddKey(const KODI::JOYSTICK::FeatureName& feature, + const KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + void SetIgnoredPrimitives( + const std::vector<KODI::JOYSTICK::CDriverPrimitive>& primitives) override; + + bool IsIgnored(const KODI::JOYSTICK::CDriverPrimitive& primitive) override; + + bool GetAxisProperties(unsigned int axisIndex, int& center, unsigned int& range) override; + + void SaveButtonMap() override; + + void RevertButtonMap() override; + +private: + typedef std::map<KODI::JOYSTICK::CDriverPrimitive, KODI::JOYSTICK::FeatureName> DriverMap; + typedef std::vector<KODI::JOYSTICK::CDriverPrimitive> JoystickPrimitiveVector; + + // Utility functions + static DriverMap CreateLookupTable(const FeatureMap& features); + + static JOYSTICK_FEATURE_PRIMITIVE GetAnalogStickIndex(KODI::JOYSTICK::ANALOG_STICK_DIRECTION dir); + static JOYSTICK_FEATURE_PRIMITIVE GetRelativePointerIndex( + KODI::JOYSTICK::RELATIVE_POINTER_DIRECTION dir); + static JOYSTICK_FEATURE_PRIMITIVE GetPrimitiveIndex(KODI::JOYSTICK::WHEEL_DIRECTION dir); + static JOYSTICK_FEATURE_PRIMITIVE GetPrimitiveIndex(KODI::JOYSTICK::THROTTLE_DIRECTION dir); + + // Construction parameters + CPeripheral* const m_device; + const std::weak_ptr<CPeripheralAddon> m_addon; + const std::string m_strControllerId; + CPeripherals& m_manager; + + // Button map state + std::string m_controllerAppearance; + FeatureMap m_features; + DriverMap m_driverMap; + JoystickPrimitiveVector m_ignoredPrimitives; + + // Synchronization parameters + mutable CCriticalSection m_mutex; +}; +} // namespace PERIPHERALS diff --git a/xbmc/peripherals/addons/AddonButtonMapping.cpp b/xbmc/peripherals/addons/AddonButtonMapping.cpp new file mode 100644 index 0000000..abf981f --- /dev/null +++ b/xbmc/peripherals/addons/AddonButtonMapping.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AddonButtonMapping.h" + +#include "input/joysticks/generic/ButtonMapping.h" +#include "input/joysticks/interfaces/IButtonMapper.h" +#include "peripherals/Peripherals.h" +#include "peripherals/addons/AddonButtonMap.h" +#include "utils/log.h" + +using namespace KODI; +using namespace JOYSTICK; +using namespace PERIPHERALS; + +CAddonButtonMapping::CAddonButtonMapping(CPeripherals& manager, + CPeripheral* peripheral, + IButtonMapper* mapper) +{ + PeripheralAddonPtr addon = manager.GetAddonWithButtonMap(peripheral); + + if (!addon) + { + CLog::Log(LOGDEBUG, "Failed to locate add-on for \"{}\"", peripheral->DeviceName()); + } + else + { + const std::string controllerId = mapper->ControllerID(); + m_buttonMap = std::make_unique<CAddonButtonMap>(peripheral, addon, controllerId, manager); + if (m_buttonMap->Load()) + { + IKeymap* keymap = peripheral->GetKeymap(controllerId); + m_buttonMapping.reset(new CButtonMapping(mapper, m_buttonMap.get(), keymap)); + + // Allow the mapper to save our button map + mapper->SetButtonMapCallback(peripheral->Location(), this); + } + else + m_buttonMap.reset(); + } +} + +CAddonButtonMapping::~CAddonButtonMapping(void) +{ + m_buttonMapping.reset(); + m_buttonMap.reset(); +} + +bool CAddonButtonMapping::OnButtonMotion(unsigned int buttonIndex, bool bPressed) +{ + if (m_buttonMapping) + return m_buttonMapping->OnButtonMotion(buttonIndex, bPressed); + + return false; +} + +bool CAddonButtonMapping::OnHatMotion(unsigned int hatIndex, HAT_STATE state) +{ + if (m_buttonMapping) + return m_buttonMapping->OnHatMotion(hatIndex, state); + + return false; +} + +bool CAddonButtonMapping::OnAxisMotion(unsigned int axisIndex, + float position, + int center, + unsigned int range) +{ + if (m_buttonMapping) + return m_buttonMapping->OnAxisMotion(axisIndex, position, center, range); + + return false; +} + +void CAddonButtonMapping::OnInputFrame(void) +{ + if (m_buttonMapping) + m_buttonMapping->OnInputFrame(); +} + +bool CAddonButtonMapping::OnKeyPress(const CKey& key) +{ + if (m_buttonMapping) + return m_buttonMapping->OnKeyPress(key); + + return false; +} + +void CAddonButtonMapping::OnKeyRelease(const CKey& key) +{ + if (m_buttonMapping) + m_buttonMapping->OnKeyRelease(key); +} + +bool CAddonButtonMapping::OnPosition(int x, int y) +{ + if (m_buttonMapping) + return m_buttonMapping->OnPosition(x, y); + + return false; +} + +bool CAddonButtonMapping::OnButtonPress(MOUSE::BUTTON_ID button) +{ + if (m_buttonMapping) + return m_buttonMapping->OnButtonPress(button); + + return false; +} + +void CAddonButtonMapping::OnButtonRelease(MOUSE::BUTTON_ID button) +{ + if (m_buttonMapping) + m_buttonMapping->OnButtonRelease(button); +} + +void CAddonButtonMapping::SaveButtonMap() +{ + if (m_buttonMapping) + m_buttonMapping->SaveButtonMap(); +} + +void CAddonButtonMapping::ResetIgnoredPrimitives() +{ + if (m_buttonMapping) + m_buttonMapping->ResetIgnoredPrimitives(); +} + +void CAddonButtonMapping::RevertButtonMap() +{ + if (m_buttonMapping) + m_buttonMapping->RevertButtonMap(); +} diff --git a/xbmc/peripherals/addons/AddonButtonMapping.h b/xbmc/peripherals/addons/AddonButtonMapping.h new file mode 100644 index 0000000..245a588 --- /dev/null +++ b/xbmc/peripherals/addons/AddonButtonMapping.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "input/joysticks/interfaces/IButtonMapCallback.h" +#include "input/joysticks/interfaces/IDriverHandler.h" +#include "input/keyboard/interfaces/IKeyboardDriverHandler.h" +#include "input/mouse/interfaces/IMouseDriverHandler.h" + +#include <memory> + +namespace KODI +{ +namespace JOYSTICK +{ +class CButtonMapping; +class IButtonMap; +class IButtonMapper; +} // namespace JOYSTICK +} // namespace KODI + +namespace PERIPHERALS +{ +class CPeripheral; +class CPeripherals; + +class CAddonButtonMapping : public KODI::JOYSTICK::IDriverHandler, + public KODI::KEYBOARD::IKeyboardDriverHandler, + public KODI::MOUSE::IMouseDriverHandler, + public KODI::JOYSTICK::IButtonMapCallback +{ +public: + CAddonButtonMapping(CPeripherals& manager, + CPeripheral* peripheral, + KODI::JOYSTICK::IButtonMapper* mapper); + + ~CAddonButtonMapping(void) override; + + // implementation of IDriverHandler + bool OnButtonMotion(unsigned int buttonIndex, bool bPressed) override; + bool OnHatMotion(unsigned int hatIndex, KODI::JOYSTICK::HAT_STATE state) override; + bool OnAxisMotion(unsigned int axisIndex, + float position, + int center, + unsigned int range) override; + void OnInputFrame(void) override; + + // implementation of IKeyboardDriverHandler + bool OnKeyPress(const CKey& key) override; + void OnKeyRelease(const CKey& key) override; + + // implementation of IMouseDriverHandler + bool OnPosition(int x, int y) override; + bool OnButtonPress(KODI::MOUSE::BUTTON_ID button) override; + void OnButtonRelease(KODI::MOUSE::BUTTON_ID button) override; + + // implementation of IButtonMapCallback + void SaveButtonMap() override; + void ResetIgnoredPrimitives() override; + void RevertButtonMap() override; + +private: + std::unique_ptr<KODI::JOYSTICK::CButtonMapping> m_buttonMapping; + std::unique_ptr<KODI::JOYSTICK::IButtonMap> m_buttonMap; +}; +} // namespace PERIPHERALS diff --git a/xbmc/peripherals/addons/AddonInputHandling.cpp b/xbmc/peripherals/addons/AddonInputHandling.cpp new file mode 100644 index 0000000..7490b96 --- /dev/null +++ b/xbmc/peripherals/addons/AddonInputHandling.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AddonInputHandling.h" + +#include "input/joysticks/generic/DriverReceiving.h" +#include "input/joysticks/generic/InputHandling.h" +#include "input/joysticks/interfaces/IDriverReceiver.h" +#include "input/joysticks/interfaces/IInputHandler.h" +#include "input/keyboard/generic/KeyboardInputHandling.h" +#include "input/keyboard/interfaces/IKeyboardInputHandler.h" +#include "input/mouse/generic/MouseInputHandling.h" +#include "input/mouse/interfaces/IMouseInputHandler.h" +#include "peripherals/addons/AddonButtonMap.h" +#include "peripherals/devices/Peripheral.h" +#include "utils/log.h" + +using namespace KODI; +using namespace JOYSTICK; +using namespace PERIPHERALS; + +CAddonInputHandling::CAddonInputHandling(CPeripherals& manager, + CPeripheral* peripheral, + std::shared_ptr<CPeripheralAddon> addon, + IInputHandler* handler, + IDriverReceiver* receiver) + : m_manager(manager), + m_peripheral(peripheral), + m_addon(std::move(addon)), + m_joystickInputHandler(handler), + m_joystickDriverReceiver(receiver) +{ +} + +CAddonInputHandling::CAddonInputHandling(CPeripherals& manager, + CPeripheral* peripheral, + std::shared_ptr<CPeripheralAddon> addon, + KEYBOARD::IKeyboardInputHandler* handler) + : m_manager(manager), + m_peripheral(peripheral), + m_addon(std::move(addon)), + m_keyboardInputHandler(handler) +{ +} + +CAddonInputHandling::CAddonInputHandling(CPeripherals& manager, + CPeripheral* peripheral, + std::shared_ptr<CPeripheralAddon> addon, + MOUSE::IMouseInputHandler* handler) + : m_manager(manager), + m_peripheral(peripheral), + m_addon(std::move(addon)), + m_mouseInputHandler(handler) +{ +} + +CAddonInputHandling::~CAddonInputHandling(void) +{ + m_joystickDriverHandler.reset(); + m_joystickInputReceiver.reset(); + m_keyboardDriverHandler.reset(); + m_mouseDriverHandler.reset(); + m_buttonMap.reset(); +} + +bool CAddonInputHandling::Load() +{ + std::string controllerId; + if (m_joystickInputHandler != nullptr) + controllerId = m_joystickInputHandler->ControllerID(); + else if (m_keyboardInputHandler != nullptr) + controllerId = m_keyboardInputHandler->ControllerID(); + else if (m_mouseInputHandler != nullptr) + controllerId = m_mouseInputHandler->ControllerID(); + + if (!controllerId.empty()) + m_buttonMap = std::make_unique<CAddonButtonMap>(m_peripheral, m_addon, controllerId, m_manager); + + if (m_buttonMap && m_buttonMap->Load()) + { + if (m_joystickInputHandler != nullptr) + { + m_joystickDriverHandler = + std::make_unique<CInputHandling>(m_joystickInputHandler, m_buttonMap.get()); + if (m_joystickDriverReceiver != nullptr) + { + m_joystickInputReceiver = + std::make_unique<CDriverReceiving>(m_joystickDriverReceiver, m_buttonMap.get()); + + // Interfaces are connected here because they share button map as a common resource + m_joystickInputHandler->SetInputReceiver(m_joystickInputReceiver.get()); + } + return true; + } + else if (m_keyboardInputHandler != nullptr) + { + m_keyboardDriverHandler = std::make_unique<KEYBOARD::CKeyboardInputHandling>( + m_keyboardInputHandler, m_buttonMap.get()); + return true; + } + else if (m_mouseInputHandler != nullptr) + { + m_mouseDriverHandler = + std::make_unique<MOUSE::CMouseInputHandling>(m_mouseInputHandler, m_buttonMap.get()); + return true; + } + } + + return false; +} + +bool CAddonInputHandling::OnButtonMotion(unsigned int buttonIndex, bool bPressed) +{ + if (m_joystickDriverHandler) + return m_joystickDriverHandler->OnButtonMotion(buttonIndex, bPressed); + + return false; +} + +bool CAddonInputHandling::OnHatMotion(unsigned int hatIndex, HAT_STATE state) +{ + if (m_joystickDriverHandler) + return m_joystickDriverHandler->OnHatMotion(hatIndex, state); + + return false; +} + +bool CAddonInputHandling::OnAxisMotion(unsigned int axisIndex, + float position, + int center, + unsigned int range) +{ + if (m_joystickDriverHandler) + return m_joystickDriverHandler->OnAxisMotion(axisIndex, position, center, range); + + return false; +} + +void CAddonInputHandling::OnInputFrame(void) +{ + if (m_joystickDriverHandler) + m_joystickDriverHandler->OnInputFrame(); +} + +bool CAddonInputHandling::OnKeyPress(const CKey& key) +{ + if (m_keyboardDriverHandler) + return m_keyboardDriverHandler->OnKeyPress(key); + + return false; +} + +void CAddonInputHandling::OnKeyRelease(const CKey& key) +{ + if (m_keyboardDriverHandler) + m_keyboardDriverHandler->OnKeyRelease(key); +} + +bool CAddonInputHandling::OnPosition(int x, int y) +{ + if (m_mouseDriverHandler) + return m_mouseDriverHandler->OnPosition(x, y); + + return false; +} + +bool CAddonInputHandling::OnButtonPress(MOUSE::BUTTON_ID button) +{ + if (m_mouseDriverHandler) + return m_mouseDriverHandler->OnButtonPress(button); + + return false; +} + +void CAddonInputHandling::OnButtonRelease(MOUSE::BUTTON_ID button) +{ + if (m_mouseDriverHandler) + m_mouseDriverHandler->OnButtonRelease(button); +} + +bool CAddonInputHandling::SetRumbleState(const JOYSTICK::FeatureName& feature, float magnitude) +{ + if (m_joystickInputReceiver) + return m_joystickInputReceiver->SetRumbleState(feature, magnitude); + + return false; +} diff --git a/xbmc/peripherals/addons/AddonInputHandling.h b/xbmc/peripherals/addons/AddonInputHandling.h new file mode 100644 index 0000000..295e6d4 --- /dev/null +++ b/xbmc/peripherals/addons/AddonInputHandling.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "input/joysticks/interfaces/IDriverHandler.h" +#include "input/joysticks/interfaces/IInputReceiver.h" +#include "input/keyboard/interfaces/IKeyboardDriverHandler.h" +#include "input/mouse/interfaces/IMouseDriverHandler.h" + +#include <memory> + +namespace KODI +{ +namespace JOYSTICK +{ +class IButtonMap; +class IDriverReceiver; +class IInputHandler; +} // namespace JOYSTICK + +namespace KEYBOARD +{ +class IKeyboardInputHandler; +} + +namespace MOUSE +{ +class IMouseInputHandler; +} +} // namespace KODI + +namespace PERIPHERALS +{ +class CPeripheral; +class CPeripherals; +class CPeripheralAddon; + +class CAddonInputHandling : public KODI::JOYSTICK::IDriverHandler, + public KODI::JOYSTICK::IInputReceiver, + public KODI::KEYBOARD::IKeyboardDriverHandler, + public KODI::MOUSE::IMouseDriverHandler +{ +public: + CAddonInputHandling(CPeripherals& manager, + CPeripheral* peripheral, + std::shared_ptr<CPeripheralAddon> addon, + KODI::JOYSTICK::IInputHandler* handler, + KODI::JOYSTICK::IDriverReceiver* receiver); + + CAddonInputHandling(CPeripherals& manager, + CPeripheral* peripheral, + std::shared_ptr<CPeripheralAddon> addon, + KODI::KEYBOARD::IKeyboardInputHandler* handler); + + CAddonInputHandling(CPeripherals& manager, + CPeripheral* peripheral, + std::shared_ptr<CPeripheralAddon> addon, + KODI::MOUSE::IMouseInputHandler* handler); + + ~CAddonInputHandling(void) override; + + bool Load(); + + // implementation of IDriverHandler + bool OnButtonMotion(unsigned int buttonIndex, bool bPressed) override; + bool OnHatMotion(unsigned int hatIndex, KODI::JOYSTICK::HAT_STATE state) override; + bool OnAxisMotion(unsigned int axisIndex, + float position, + int center, + unsigned int range) override; + void OnInputFrame(void) override; + + // implementation of IKeyboardDriverHandler + bool OnKeyPress(const CKey& key) override; + void OnKeyRelease(const CKey& key) override; + + // implementation of IMouseDriverHandler + bool OnPosition(int x, int y) override; + bool OnButtonPress(KODI::MOUSE::BUTTON_ID button) override; + void OnButtonRelease(KODI::MOUSE::BUTTON_ID button) override; + + // implementation of IInputReceiver + bool SetRumbleState(const KODI::JOYSTICK::FeatureName& feature, float magnitude) override; + +private: + // Construction parameters + CPeripherals& m_manager; + CPeripheral* const m_peripheral; + const std::shared_ptr<CPeripheralAddon> m_addon; + KODI::JOYSTICK::IInputHandler* const m_joystickInputHandler{nullptr}; + KODI::JOYSTICK::IDriverReceiver* const m_joystickDriverReceiver{nullptr}; + KODI::KEYBOARD::IKeyboardInputHandler* m_keyboardInputHandler{nullptr}; + KODI::MOUSE::IMouseInputHandler* const m_mouseInputHandler{nullptr}; + + // Input parameters + std::unique_ptr<KODI::JOYSTICK::IDriverHandler> m_joystickDriverHandler; + std::unique_ptr<KODI::JOYSTICK::IInputReceiver> m_joystickInputReceiver; + std::unique_ptr<KODI::KEYBOARD::IKeyboardDriverHandler> m_keyboardDriverHandler; + std::unique_ptr<KODI::MOUSE::IMouseDriverHandler> m_mouseDriverHandler; + std::unique_ptr<KODI::JOYSTICK::IButtonMap> m_buttonMap; +}; +} // namespace PERIPHERALS diff --git a/xbmc/peripherals/addons/CMakeLists.txt b/xbmc/peripherals/addons/CMakeLists.txt new file mode 100644 index 0000000..9f31978 --- /dev/null +++ b/xbmc/peripherals/addons/CMakeLists.txt @@ -0,0 +1,13 @@ +set(SOURCES AddonButtonMap.cpp + AddonButtonMapping.cpp + AddonInputHandling.cpp + PeripheralAddon.cpp + PeripheralAddonTranslator.cpp) + +set(HEADERS AddonButtonMap.h + AddonButtonMapping.h + AddonInputHandling.h + PeripheralAddon.h + PeripheralAddonTranslator.h) + +core_add_library(peripherals_addons) diff --git a/xbmc/peripherals/addons/PeripheralAddon.cpp b/xbmc/peripherals/addons/PeripheralAddon.cpp new file mode 100644 index 0000000..7a55bb3 --- /dev/null +++ b/xbmc/peripherals/addons/PeripheralAddon.cpp @@ -0,0 +1,990 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PeripheralAddon.h" + +#include "FileItem.h" +#include "PeripheralAddonTranslator.h" +#include "addons/addoninfo/AddonInfo.h" +#include "addons/addoninfo/AddonType.h" +#include "filesystem/Directory.h" +#include "filesystem/SpecialProtocol.h" +#include "games/controllers/Controller.h" +#include "games/controllers/ControllerManager.h" +#include "input/joysticks/DriverPrimitive.h" +#include "input/joysticks/interfaces/IButtonMap.h" +#include "peripherals/Peripherals.h" +#include "peripherals/bus/virtual/PeripheralBusAddon.h" +#include "peripherals/devices/PeripheralJoystick.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include <algorithm> +#include <mutex> +#include <shared_mutex> +#include <string.h> +#include <utility> + +using namespace KODI; +using namespace JOYSTICK; +using namespace PERIPHERALS; +using namespace XFILE; + +#define KEYBOARD_BUTTON_MAP_NAME "Keyboard" +#define KEYBOARD_PROVIDER "application" + +#define MOUSE_BUTTON_MAP_NAME "Mouse" +#define MOUSE_PROVIDER "application" + +#ifndef SAFE_DELETE +#define SAFE_DELETE(p) \ + do \ + { \ + delete (p); \ + (p) = NULL; \ + } while (0) +#endif + +CPeripheralAddon::CPeripheralAddon(const ADDON::AddonInfoPtr& addonInfo, CPeripherals& manager) + : IAddonInstanceHandler(ADDON_INSTANCE_PERIPHERAL, addonInfo), + m_manager(manager), + m_bSupportsJoystickRumble(false), + m_bSupportsJoystickPowerOff(false) +{ + m_bProvidesJoysticks = + addonInfo->Type(ADDON::AddonType::PERIPHERALDLL)->GetValue("@provides_joysticks").asBoolean(); + m_bProvidesButtonMaps = addonInfo->Type(ADDON::AddonType::PERIPHERALDLL) + ->GetValue("@provides_buttonmaps") + .asBoolean(); + + // Create "C" interface structures, used as own parts to prevent API problems on update + m_ifc.peripheral = new AddonInstance_Peripheral; + m_ifc.peripheral->props = new AddonProps_Peripheral(); + m_ifc.peripheral->toAddon = new KodiToAddonFuncTable_Peripheral(); + m_ifc.peripheral->toKodi = new AddonToKodiFuncTable_Peripheral(); + + ResetProperties(); +} + +CPeripheralAddon::~CPeripheralAddon(void) +{ + DestroyAddon(); + + if (m_ifc.peripheral) + { + delete m_ifc.peripheral->toAddon; + delete m_ifc.peripheral->toKodi; + delete m_ifc.peripheral->props; + } + delete m_ifc.peripheral; +} + +void CPeripheralAddon::ResetProperties(void) +{ + // Initialise members + m_strUserPath = CSpecialProtocol::TranslatePath(Profile()); + m_strClientPath = CSpecialProtocol::TranslatePath(Path()); + + m_ifc.peripheral->props->user_path = m_strUserPath.c_str(); + m_ifc.peripheral->props->addon_path = m_strClientPath.c_str(); + + m_ifc.peripheral->toKodi->kodiInstance = this; + m_ifc.peripheral->toKodi->feature_count = cb_feature_count; + m_ifc.peripheral->toKodi->feature_type = cb_feature_type; + m_ifc.peripheral->toKodi->refresh_button_maps = cb_refresh_button_maps; + m_ifc.peripheral->toKodi->trigger_scan = cb_trigger_scan; + + memset(m_ifc.peripheral->toAddon, 0, sizeof(KodiToAddonFuncTable_Peripheral)); +} + +bool CPeripheralAddon::CreateAddon(void) +{ + std::unique_lock<CSharedSection> lock(m_dllSection); + + // Reset all properties to defaults + ResetProperties(); + + // Create directory for user data + if (!CDirectory::Exists(m_strUserPath)) + CDirectory::Create(m_strUserPath); + + // Initialise the add-on + CLog::Log(LOGDEBUG, "PERIPHERAL - {} - creating peripheral add-on instance '{}'", __FUNCTION__, + Name()); + + if (CreateInstance() != ADDON_STATUS_OK) + return false; + + if (!GetAddonProperties()) + { + DestroyInstance(); + return false; + } + + return true; +} + +void CPeripheralAddon::DestroyAddon() +{ + { + std::unique_lock<CCriticalSection> lock(m_critSection); + m_peripherals.clear(); + } + + { + std::unique_lock<CCriticalSection> lock(m_buttonMapMutex); + // only clear buttonMaps but don't delete them as they are owned by a + // CAddonJoystickInputHandling instance + m_buttonMaps.clear(); + } + + { + std::unique_lock<CSharedSection> lock(m_dllSection); + DestroyInstance(); + } +} + +bool CPeripheralAddon::GetAddonProperties(void) +{ + PERIPHERAL_CAPABILITIES addonCapabilities = {}; + + // Get the capabilities + m_ifc.peripheral->toAddon->get_capabilities(m_ifc.peripheral, &addonCapabilities); + + // Verify capabilities against addon.xml + if (m_bProvidesJoysticks != addonCapabilities.provides_joysticks) + { + CLog::Log( + LOGERROR, + "PERIPHERAL - Add-on '{}': provides_joysticks'({}) in add-on DLL doesn't match " + "'provides_joysticks'({}) in addon.xml. Please contact the developer of this add-on: {}", + Name(), addonCapabilities.provides_joysticks ? "true" : "false", + m_bProvidesJoysticks ? "true" : "false", Author()); + return false; + } + if (m_bProvidesButtonMaps != addonCapabilities.provides_buttonmaps) + { + CLog::Log( + LOGERROR, + "PERIPHERAL - Add-on '{}': provides_buttonmaps' ({}) in add-on DLL doesn't match " + "'provides_buttonmaps' ({}) in addon.xml. Please contact the developer of this add-on: {}", + Name(), addonCapabilities.provides_buttonmaps ? "true" : "false", + m_bProvidesButtonMaps ? "true" : "false", Author()); + return false; + } + + // Read properties that depend on underlying driver + m_bSupportsJoystickRumble = addonCapabilities.provides_joystick_rumble; + m_bSupportsJoystickPowerOff = addonCapabilities.provides_joystick_power_off; + + return true; +} + +bool CPeripheralAddon::Register(unsigned int peripheralIndex, const PeripheralPtr& peripheral) +{ + if (!peripheral) + return false; + + std::unique_lock<CCriticalSection> lock(m_critSection); + if (m_peripherals.find(peripheralIndex) == m_peripherals.end()) + { + if (peripheral->Type() == PERIPHERAL_JOYSTICK) + { + m_peripherals[peripheralIndex] = std::static_pointer_cast<CPeripheralJoystick>(peripheral); + + CLog::Log(LOGINFO, "{} - new {} device registered on {}->{}: {}", __FUNCTION__, + PeripheralTypeTranslator::TypeToString(peripheral->Type()), + PeripheralTypeTranslator::BusTypeToString(PERIPHERAL_BUS_ADDON), + peripheral->Location(), peripheral->DeviceName()); + + return true; + } + } + return false; +} + +void CPeripheralAddon::UnregisterRemovedDevices(const PeripheralScanResults& results, + PeripheralVector& removedPeripherals) +{ + std::vector<unsigned int> removedIndexes; + + { + std::unique_lock<CCriticalSection> lock(m_critSection); + for (auto& it : m_peripherals) + { + const PeripheralPtr& peripheral = it.second; + PeripheralScanResult updatedDevice(PERIPHERAL_BUS_ADDON); + if (!results.GetDeviceOnLocation(peripheral->Location(), &updatedDevice) || + *peripheral != updatedDevice) + { + // Device removed + removedIndexes.push_back(it.first); + } + } + } + + for (auto index : removedIndexes) + { + auto it = m_peripherals.find(index); + const PeripheralPtr& peripheral = it->second; + CLog::Log(LOGINFO, "{} - device removed from {}/{}: {} ({}:{})", __FUNCTION__, + PeripheralTypeTranslator::TypeToString(peripheral->Type()), peripheral->Location(), + peripheral->DeviceName(), peripheral->VendorIdAsString(), + peripheral->ProductIdAsString()); + UnregisterButtonMap(peripheral.get()); + peripheral->OnDeviceRemoved(); + removedPeripherals.push_back(peripheral); + m_peripherals.erase(it); + } +} + +bool CPeripheralAddon::HasFeature(const PeripheralFeature feature) const +{ + if (feature == FEATURE_JOYSTICK) + return m_bProvidesJoysticks; + + return false; +} + +void CPeripheralAddon::GetFeatures(std::vector<PeripheralFeature>& features) const +{ + if (m_bProvidesJoysticks && + std::find(features.begin(), features.end(), FEATURE_JOYSTICK) == features.end()) + features.push_back(FEATURE_JOYSTICK); +} + +PeripheralPtr CPeripheralAddon::GetPeripheral(unsigned int index) const +{ + PeripheralPtr peripheral; + std::unique_lock<CCriticalSection> lock(m_critSection); + auto it = m_peripherals.find(index); + if (it != m_peripherals.end()) + peripheral = it->second; + return peripheral; +} + +PeripheralPtr CPeripheralAddon::GetByPath(const std::string& strPath) const +{ + PeripheralPtr result; + + std::unique_lock<CCriticalSection> lock(m_critSection); + for (const auto& it : m_peripherals) + { + if (StringUtils::EqualsNoCase(strPath, it.second->FileLocation())) + { + result = it.second; + break; + } + } + + return result; +} + +bool CPeripheralAddon::SupportsFeature(PeripheralFeature feature) const +{ + switch (feature) + { + case FEATURE_RUMBLE: + return m_bSupportsJoystickRumble; + case FEATURE_POWER_OFF: + return m_bSupportsJoystickPowerOff; + default: + break; + } + + return false; +} + +unsigned int CPeripheralAddon::GetPeripheralsWithFeature(PeripheralVector& results, + const PeripheralFeature feature) const +{ + unsigned int iReturn = 0; + std::unique_lock<CCriticalSection> lock(m_critSection); + for (const auto& it : m_peripherals) + { + if (it.second->HasFeature(feature)) + { + results.push_back(it.second); + ++iReturn; + } + } + return iReturn; +} + +unsigned int CPeripheralAddon::GetNumberOfPeripherals(void) const +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + return static_cast<unsigned int>(m_peripherals.size()); +} + +unsigned int CPeripheralAddon::GetNumberOfPeripheralsWithId(const int iVendorId, + const int iProductId) const +{ + unsigned int iReturn = 0; + std::unique_lock<CCriticalSection> lock(m_critSection); + for (const auto& it : m_peripherals) + { + if (it.second->VendorId() == iVendorId && it.second->ProductId() == iProductId) + iReturn++; + } + + return iReturn; +} + +void CPeripheralAddon::GetDirectory(const std::string& strPath, CFileItemList& items) const +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + for (const auto& it : m_peripherals) + { + const PeripheralPtr& peripheral = it.second; + if (peripheral->IsHidden()) + continue; + + CFileItemPtr peripheralFile(new CFileItem(peripheral->DeviceName())); + peripheralFile->SetPath(peripheral->FileLocation()); + peripheralFile->SetProperty("vendor", peripheral->VendorIdAsString()); + peripheralFile->SetProperty("product", peripheral->ProductIdAsString()); + peripheralFile->SetProperty( + "bus", PeripheralTypeTranslator::BusTypeToString(peripheral->GetBusType())); + peripheralFile->SetProperty("location", peripheral->Location()); + peripheralFile->SetProperty("class", + PeripheralTypeTranslator::TypeToString(peripheral->Type())); + peripheralFile->SetProperty("version", peripheral->GetVersionInfo()); + peripheralFile->SetArt("icon", peripheral->GetIcon()); + items.Add(peripheralFile); + } +} + +bool CPeripheralAddon::PerformDeviceScan(PeripheralScanResults& results) +{ + unsigned int peripheralCount; + PERIPHERAL_INFO* pScanResults; + PERIPHERAL_ERROR retVal; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->perform_device_scan) + return false; + + LogError(retVal = m_ifc.peripheral->toAddon->perform_device_scan(m_ifc.peripheral, + &peripheralCount, &pScanResults), + "PerformDeviceScan()"); + + if (retVal == PERIPHERAL_NO_ERROR) + { + for (unsigned int i = 0; i < peripheralCount; i++) + { + kodi::addon::Peripheral peripheral(pScanResults[i]); + PeripheralScanResult result(PERIPHERAL_BUS_ADDON); + switch (peripheral.Type()) + { + case PERIPHERAL_TYPE_JOYSTICK: + result.m_type = PERIPHERAL_JOYSTICK; + break; + default: + continue; + } + + result.m_strDeviceName = peripheral.Name(); + result.m_strLocation = StringUtils::Format("{}/{}", ID(), peripheral.Index()); + result.m_iVendorId = peripheral.VendorID(); + result.m_iProductId = peripheral.ProductID(); + result.m_mappedType = PERIPHERAL_JOYSTICK; + result.m_mappedBusType = PERIPHERAL_BUS_ADDON; + result.m_iSequence = 0; + + if (!results.ContainsResult(result)) + results.m_results.push_back(result); + } + + m_ifc.peripheral->toAddon->free_scan_results(m_ifc.peripheral, peripheralCount, pScanResults); + + return true; + } + + return false; +} + +bool CPeripheralAddon::ProcessEvents(void) +{ + if (!m_bProvidesJoysticks) + return false; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->get_events) + return false; + + PERIPHERAL_ERROR retVal; + + unsigned int eventCount = 0; + PERIPHERAL_EVENT* pEvents = nullptr; + + LogError(retVal = m_ifc.peripheral->toAddon->get_events(m_ifc.peripheral, &eventCount, &pEvents), + "GetEvents()"); + if (retVal == PERIPHERAL_NO_ERROR) + { + for (unsigned int i = 0; i < eventCount; i++) + { + kodi::addon::PeripheralEvent event(pEvents[i]); + PeripheralPtr device = GetPeripheral(event.PeripheralIndex()); + if (!device) + continue; + + switch (device->Type()) + { + case PERIPHERAL_JOYSTICK: + { + std::shared_ptr<CPeripheralJoystick> joystickDevice = + std::static_pointer_cast<CPeripheralJoystick>(device); + + switch (event.Type()) + { + case PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON: + { + const bool bPressed = (event.ButtonState() == JOYSTICK_STATE_BUTTON_PRESSED); + joystickDevice->OnButtonMotion(event.DriverIndex(), bPressed); + break; + } + case PERIPHERAL_EVENT_TYPE_DRIVER_HAT: + { + const HAT_STATE state = + CPeripheralAddonTranslator::TranslateHatState(event.HatState()); + joystickDevice->OnHatMotion(event.DriverIndex(), state); + break; + } + case PERIPHERAL_EVENT_TYPE_DRIVER_AXIS: + { + joystickDevice->OnAxisMotion(event.DriverIndex(), event.AxisState()); + break; + } + default: + break; + } + break; + } + default: + break; + } + } + + for (const auto& it : m_peripherals) + { + if (it.second->Type() == PERIPHERAL_JOYSTICK) + std::static_pointer_cast<CPeripheralJoystick>(it.second)->OnInputFrame(); + } + + m_ifc.peripheral->toAddon->free_events(m_ifc.peripheral, eventCount, pEvents); + + return true; + } + + return false; +} + +bool CPeripheralAddon::SendRumbleEvent(unsigned int peripheralIndex, + unsigned int driverIndex, + float magnitude) +{ + if (!m_bProvidesJoysticks) + return false; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->send_event) + return false; + + PERIPHERAL_EVENT eventStruct = {}; + + eventStruct.peripheral_index = peripheralIndex; + eventStruct.type = PERIPHERAL_EVENT_TYPE_SET_MOTOR; + eventStruct.driver_index = driverIndex; + eventStruct.motor_state = magnitude; + + return m_ifc.peripheral->toAddon->send_event(m_ifc.peripheral, &eventStruct); +} + +bool CPeripheralAddon::GetJoystickProperties(unsigned int index, CPeripheralJoystick& joystick) +{ + if (!m_bProvidesJoysticks) + return false; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->get_joystick_info) + return false; + + PERIPHERAL_ERROR retVal; + + JOYSTICK_INFO joystickStruct; + + LogError(retVal = m_ifc.peripheral->toAddon->get_joystick_info(m_ifc.peripheral, index, + &joystickStruct), + "GetJoystickInfo()"); + if (retVal == PERIPHERAL_NO_ERROR) + { + kodi::addon::Joystick addonJoystick(joystickStruct); + SetJoystickInfo(joystick, addonJoystick); + + m_ifc.peripheral->toAddon->free_joystick_info(m_ifc.peripheral, &joystickStruct); + + return true; + } + + return false; +} + +bool CPeripheralAddon::GetFeatures(const CPeripheral* device, + const std::string& strControllerId, + FeatureMap& features) +{ + if (!m_bProvidesButtonMaps) + return false; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->get_features) + return false; + + PERIPHERAL_ERROR retVal; + + kodi::addon::Joystick joystickInfo; + GetJoystickInfo(device, joystickInfo); + + JOYSTICK_INFO joystickStruct; + joystickInfo.ToStruct(joystickStruct); + + unsigned int featureCount = 0; + JOYSTICK_FEATURE* pFeatures = nullptr; + + LogError(retVal = m_ifc.peripheral->toAddon->get_features(m_ifc.peripheral, &joystickStruct, + strControllerId.c_str(), &featureCount, + &pFeatures), + "GetFeatures()"); + + kodi::addon::Joystick::FreeStruct(joystickStruct); + + if (retVal == PERIPHERAL_NO_ERROR) + { + for (unsigned int i = 0; i < featureCount; i++) + { + kodi::addon::JoystickFeature feature(pFeatures[i]); + if (feature.Type() != JOYSTICK_FEATURE_TYPE_UNKNOWN) + features[feature.Name()] = feature; + } + + m_ifc.peripheral->toAddon->free_features(m_ifc.peripheral, featureCount, pFeatures); + + return true; + } + + return false; +} + +bool CPeripheralAddon::MapFeature(const CPeripheral* device, + const std::string& strControllerId, + const kodi::addon::JoystickFeature& feature) +{ + if (!m_bProvidesButtonMaps) + return false; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->map_features) + return false; + + PERIPHERAL_ERROR retVal; + + kodi::addon::Joystick joystickInfo; + GetJoystickInfo(device, joystickInfo); + + JOYSTICK_INFO joystickStruct; + joystickInfo.ToStruct(joystickStruct); + + JOYSTICK_FEATURE addonFeature; + feature.ToStruct(addonFeature); + + LogError(retVal = m_ifc.peripheral->toAddon->map_features( + m_ifc.peripheral, &joystickStruct, strControllerId.c_str(), 1, &addonFeature), + "MapFeatures()"); + + kodi::addon::Joystick::FreeStruct(joystickStruct); + kodi::addon::JoystickFeature::FreeStruct(addonFeature); + + return retVal == PERIPHERAL_NO_ERROR; +} + +bool CPeripheralAddon::GetIgnoredPrimitives(const CPeripheral* device, PrimitiveVector& primitives) +{ + if (!m_bProvidesButtonMaps) + return false; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->get_ignored_primitives) + return false; + + PERIPHERAL_ERROR retVal; + + kodi::addon::Joystick joystickInfo; + GetJoystickInfo(device, joystickInfo); + + JOYSTICK_INFO joystickStruct; + joystickInfo.ToStruct(joystickStruct); + + unsigned int primitiveCount = 0; + JOYSTICK_DRIVER_PRIMITIVE* pPrimitives = nullptr; + + LogError(retVal = m_ifc.peripheral->toAddon->get_ignored_primitives( + m_ifc.peripheral, &joystickStruct, &primitiveCount, &pPrimitives), + "GetIgnoredPrimitives()"); + + kodi::addon::Joystick::FreeStruct(joystickStruct); + + if (retVal == PERIPHERAL_NO_ERROR) + { + for (unsigned int i = 0; i < primitiveCount; i++) + primitives.emplace_back(pPrimitives[i]); + + m_ifc.peripheral->toAddon->free_primitives(m_ifc.peripheral, primitiveCount, pPrimitives); + + return true; + } + + return false; +} + +bool CPeripheralAddon::SetIgnoredPrimitives(const CPeripheral* device, + const PrimitiveVector& primitives) +{ + if (!m_bProvidesButtonMaps) + return false; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->set_ignored_primitives) + return false; + + PERIPHERAL_ERROR retVal; + + kodi::addon::Joystick joystickInfo; + GetJoystickInfo(device, joystickInfo); + + JOYSTICK_INFO joystickStruct; + joystickInfo.ToStruct(joystickStruct); + + JOYSTICK_DRIVER_PRIMITIVE* addonPrimitives = nullptr; + kodi::addon::DriverPrimitives::ToStructs(primitives, &addonPrimitives); + const unsigned int primitiveCount = static_cast<unsigned int>(primitives.size()); + + LogError(retVal = m_ifc.peripheral->toAddon->set_ignored_primitives( + m_ifc.peripheral, &joystickStruct, primitiveCount, addonPrimitives), + "SetIgnoredPrimitives()"); + + kodi::addon::Joystick::FreeStruct(joystickStruct); + kodi::addon::DriverPrimitives::FreeStructs(primitiveCount, addonPrimitives); + + return retVal == PERIPHERAL_NO_ERROR; +} + +void CPeripheralAddon::SaveButtonMap(const CPeripheral* device) +{ + if (!m_bProvidesButtonMaps) + return; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->save_button_map) + return; + + kodi::addon::Joystick joystickInfo; + GetJoystickInfo(device, joystickInfo); + + JOYSTICK_INFO joystickStruct; + joystickInfo.ToStruct(joystickStruct); + + m_ifc.peripheral->toAddon->save_button_map(m_ifc.peripheral, &joystickStruct); + + kodi::addon::Joystick::FreeStruct(joystickStruct); + + // Notify observing button maps + RefreshButtonMaps(device->DeviceName()); +} + +void CPeripheralAddon::RevertButtonMap(const CPeripheral* device) +{ + if (!m_bProvidesButtonMaps) + return; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->revert_button_map) + return; + + kodi::addon::Joystick joystickInfo; + GetJoystickInfo(device, joystickInfo); + + JOYSTICK_INFO joystickStruct; + joystickInfo.ToStruct(joystickStruct); + + m_ifc.peripheral->toAddon->revert_button_map(m_ifc.peripheral, &joystickStruct); + + kodi::addon::Joystick::FreeStruct(joystickStruct); +} + +void CPeripheralAddon::ResetButtonMap(const CPeripheral* device, const std::string& strControllerId) +{ + if (!m_bProvidesButtonMaps) + return; + + kodi::addon::Joystick joystickInfo; + GetJoystickInfo(device, joystickInfo); + + JOYSTICK_INFO joystickStruct; + joystickInfo.ToStruct(joystickStruct); + + m_ifc.peripheral->toAddon->reset_button_map(m_ifc.peripheral, &joystickStruct, + strControllerId.c_str()); + + kodi::addon::Joystick::FreeStruct(joystickStruct); + + // Notify observing button maps + RefreshButtonMaps(device->DeviceName()); +} + +void CPeripheralAddon::PowerOffJoystick(unsigned int index) +{ + if (!HasFeature(FEATURE_JOYSTICK)) + return; + + if (!SupportsFeature(FEATURE_POWER_OFF)) + return; + + std::shared_lock<CSharedSection> lock(m_dllSection); + + if (!m_ifc.peripheral->toAddon->power_off_joystick) + return; + + m_ifc.peripheral->toAddon->power_off_joystick(m_ifc.peripheral, index); +} + +void CPeripheralAddon::RegisterButtonMap(CPeripheral* device, IButtonMap* buttonMap) +{ + std::unique_lock<CCriticalSection> lock(m_buttonMapMutex); + + UnregisterButtonMap(buttonMap); + m_buttonMaps.emplace_back(device, buttonMap); +} + +void CPeripheralAddon::UnregisterButtonMap(IButtonMap* buttonMap) +{ + std::unique_lock<CCriticalSection> lock(m_buttonMapMutex); + + for (auto it = m_buttonMaps.begin(); it != m_buttonMaps.end(); ++it) + { + if (it->second == buttonMap) + { + m_buttonMaps.erase(it); + break; + } + } +} + +void CPeripheralAddon::UnregisterButtonMap(CPeripheral* device) +{ + std::unique_lock<CCriticalSection> lock(m_buttonMapMutex); + + m_buttonMaps.erase( + std::remove_if(m_buttonMaps.begin(), m_buttonMaps.end(), + [device](const std::pair<CPeripheral*, JOYSTICK::IButtonMap*>& buttonMap) { + return buttonMap.first == device; + }), + m_buttonMaps.end()); +} + +void CPeripheralAddon::RefreshButtonMaps(const std::string& strDeviceName /* = "" */) +{ + std::unique_lock<CCriticalSection> lock(m_buttonMapMutex); + + for (auto it = m_buttonMaps.begin(); it != m_buttonMaps.end(); ++it) + { + if (strDeviceName.empty() || strDeviceName == it->first->DeviceName()) + it->second->Load(); + } +} + +bool CPeripheralAddon::ProvidesJoysticks(const ADDON::AddonInfoPtr& addonInfo) +{ + return addonInfo->Type(ADDON::AddonType::PERIPHERALDLL) + ->GetValue("@provides_joysticks") + .asBoolean(); +} + +bool CPeripheralAddon::ProvidesButtonMaps(const ADDON::AddonInfoPtr& addonInfo) +{ + return addonInfo->Type(ADDON::AddonType::PERIPHERALDLL) + ->GetValue("@provides_buttonmaps") + .asBoolean(); +} + +void CPeripheralAddon::TriggerDeviceScan() +{ + m_manager.TriggerDeviceScan(PERIPHERAL_BUS_ADDON); +} + +unsigned int CPeripheralAddon::FeatureCount(const std::string& controllerId, + JOYSTICK_FEATURE_TYPE type) const +{ + using namespace GAME; + + unsigned int count = 0; + + CControllerManager& controllerProfiles = m_manager.GetControllerProfiles(); + ControllerPtr controller = controllerProfiles.GetController(controllerId); + if (controller) + count = controller->FeatureCount(CPeripheralAddonTranslator::TranslateFeatureType(type)); + + return count; +} + +JOYSTICK_FEATURE_TYPE CPeripheralAddon::FeatureType(const std::string& controllerId, + const std::string& featureName) const +{ + using namespace GAME; + + JOYSTICK_FEATURE_TYPE type = JOYSTICK_FEATURE_TYPE_UNKNOWN; + + CControllerManager& controllerProfiles = m_manager.GetControllerProfiles(); + ControllerPtr controller = controllerProfiles.GetController(controllerId); + if (controller) + type = CPeripheralAddonTranslator::TranslateFeatureType(controller->FeatureType(featureName)); + + return type; +} + +void CPeripheralAddon::GetPeripheralInfo(const CPeripheral* device, + kodi::addon::Peripheral& peripheralInfo) +{ + peripheralInfo.SetType(CPeripheralAddonTranslator::TranslateType(device->Type())); + peripheralInfo.SetName(device->DeviceName()); + peripheralInfo.SetVendorID(device->VendorId()); + peripheralInfo.SetProductID(device->ProductId()); +} + +void CPeripheralAddon::GetJoystickInfo(const CPeripheral* device, + kodi::addon::Joystick& joystickInfo) +{ + GetPeripheralInfo(device, joystickInfo); + + if (device->Type() == PERIPHERAL_JOYSTICK) + { + const CPeripheralJoystick* joystick = static_cast<const CPeripheralJoystick*>(device); + joystickInfo.SetProvider(joystick->Provider()); + joystickInfo.SetButtonCount(joystick->ButtonCount()); + joystickInfo.SetHatCount(joystick->HatCount()); + joystickInfo.SetAxisCount(joystick->AxisCount()); + joystickInfo.SetMotorCount(joystick->MotorCount()); + joystickInfo.SetSupportsPowerOff(joystick->SupportsPowerOff()); + } + else if (device->Type() == PERIPHERAL_KEYBOARD || device->Type() == PERIPHERAL_MOUSE) + { + joystickInfo.SetName(GetDeviceName(device->Type())); // Override name with non-localized version + joystickInfo.SetProvider(GetProvider(device->Type())); + } +} + +void CPeripheralAddon::SetJoystickInfo(CPeripheralJoystick& joystick, + const kodi::addon::Joystick& joystickInfo) +{ + joystick.SetProvider(joystickInfo.Provider()); + joystick.SetRequestedPort(joystickInfo.RequestedPort()); + joystick.SetButtonCount(joystickInfo.ButtonCount()); + joystick.SetHatCount(joystickInfo.HatCount()); + joystick.SetAxisCount(joystickInfo.AxisCount()); + joystick.SetMotorCount(joystickInfo.MotorCount()); + joystick.SetSupportsPowerOff(joystickInfo.SupportsPowerOff()); +} + +bool CPeripheralAddon::LogError(const PERIPHERAL_ERROR error, const char* strMethod) const +{ + if (error != PERIPHERAL_NO_ERROR) + { + CLog::Log(LOGERROR, "PERIPHERAL - {} - addon '{}' returned an error: {}", strMethod, Name(), + CPeripheralAddonTranslator::TranslateError(error)); + return false; + } + return true; +} + +std::string CPeripheralAddon::GetDeviceName(PeripheralType type) +{ + switch (type) + { + case PERIPHERAL_KEYBOARD: + return KEYBOARD_BUTTON_MAP_NAME; + case PERIPHERAL_MOUSE: + return MOUSE_BUTTON_MAP_NAME; + default: + break; + } + + return ""; +} + +std::string CPeripheralAddon::GetProvider(PeripheralType type) +{ + switch (type) + { + case PERIPHERAL_KEYBOARD: + return KEYBOARD_PROVIDER; + case PERIPHERAL_MOUSE: + return MOUSE_PROVIDER; + default: + break; + } + + return ""; +} + +void CPeripheralAddon::cb_trigger_scan(void* kodiInstance) +{ + if (kodiInstance == nullptr) + return; + + static_cast<CPeripheralAddon*>(kodiInstance)->TriggerDeviceScan(); +} + +void CPeripheralAddon::cb_refresh_button_maps(void* kodiInstance, + const char* deviceName, + const char* controllerId) +{ + if (!kodiInstance) + return; + + static_cast<CPeripheralAddon*>(kodiInstance)->RefreshButtonMaps(deviceName ? deviceName : ""); +} + +unsigned int CPeripheralAddon::cb_feature_count(void* kodiInstance, + const char* controllerId, + JOYSTICK_FEATURE_TYPE type) +{ + if (kodiInstance == nullptr || controllerId == nullptr) + return 0; + + return static_cast<CPeripheralAddon*>(kodiInstance)->FeatureCount(controllerId, type); +} + +JOYSTICK_FEATURE_TYPE CPeripheralAddon::cb_feature_type(void* kodiInstance, + const char* controllerId, + const char* featureName) +{ + if (kodiInstance == nullptr || controllerId == nullptr || featureName == nullptr) + return JOYSTICK_FEATURE_TYPE_UNKNOWN; + + return static_cast<CPeripheralAddon*>(kodiInstance)->FeatureType(controllerId, featureName); +} diff --git a/xbmc/peripherals/addons/PeripheralAddon.h b/xbmc/peripherals/addons/PeripheralAddon.h new file mode 100644 index 0000000..2dd9d22 --- /dev/null +++ b/xbmc/peripherals/addons/PeripheralAddon.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "addons/binary-addons/AddonInstanceHandler.h" +#include "addons/kodi-dev-kit/include/kodi/addon-instance/Peripheral.h" +#include "input/joysticks/JoystickTypes.h" +#include "peripherals/PeripheralTypes.h" +#include "threads/CriticalSection.h" +#include "threads/SharedSection.h" + +#include <map> +#include <memory> +#include <vector> + +class CFileItemList; + +namespace KODI +{ +namespace JOYSTICK +{ +class IButtonMap; +class IDriverHandler; +} // namespace JOYSTICK +} // namespace KODI + +namespace PERIPHERALS +{ +class CPeripheral; +class CPeripheralJoystick; +class CPeripherals; + +typedef std::vector<kodi::addon::DriverPrimitive> PrimitiveVector; +typedef std::map<KODI::JOYSTICK::FeatureName, kodi::addon::JoystickFeature> FeatureMap; + +class CPeripheralAddon : public ADDON::IAddonInstanceHandler +{ +public: + explicit CPeripheralAddon(const ADDON::AddonInfoPtr& addonInfo, CPeripherals& manager); + ~CPeripheralAddon(void) override; + + /*! + * @brief Initialise the instance of this add-on + */ + bool CreateAddon(void); + + /*! + * \brief Deinitialize the instance of this add-on + */ + void DestroyAddon(); + + bool Register(unsigned int peripheralIndex, const PeripheralPtr& peripheral); + void UnregisterRemovedDevices(const PeripheralScanResults& results, + PeripheralVector& removedPeripherals); + void GetFeatures(std::vector<PeripheralFeature>& features) const; + bool HasFeature(const PeripheralFeature feature) const; + PeripheralPtr GetPeripheral(unsigned int index) const; + PeripheralPtr GetByPath(const std::string& strPath) const; + bool SupportsFeature(PeripheralFeature feature) const; + unsigned int GetPeripheralsWithFeature(PeripheralVector& results, + const PeripheralFeature feature) const; + unsigned int GetNumberOfPeripherals(void) const; + unsigned int GetNumberOfPeripheralsWithId(const int iVendorId, const int iProductId) const; + void GetDirectory(const std::string& strPath, CFileItemList& items) const; + + /** @name Peripheral add-on methods */ + //@{ + bool PerformDeviceScan(PeripheralScanResults& results); + bool ProcessEvents(void); + bool SendRumbleEvent(unsigned int index, unsigned int driverIndex, float magnitude); + //@} + + /** @name Joystick methods */ + //@{ + bool GetJoystickProperties(unsigned int index, CPeripheralJoystick& joystick); + bool HasButtonMaps(void) const { return m_bProvidesButtonMaps; } + bool GetFeatures(const CPeripheral* device, + const std::string& strControllerId, + FeatureMap& features); + bool MapFeature(const CPeripheral* device, + const std::string& strControllerId, + const kodi::addon::JoystickFeature& feature); + bool GetIgnoredPrimitives(const CPeripheral* device, PrimitiveVector& primitives); + bool SetIgnoredPrimitives(const CPeripheral* device, const PrimitiveVector& primitives); + void SaveButtonMap(const CPeripheral* device); + void RevertButtonMap(const CPeripheral* device); + void ResetButtonMap(const CPeripheral* device, const std::string& strControllerId); + void PowerOffJoystick(unsigned int index); + //@} + + void RegisterButtonMap(CPeripheral* device, KODI::JOYSTICK::IButtonMap* buttonMap); + void UnregisterButtonMap(KODI::JOYSTICK::IButtonMap* buttonMap); + + static bool ProvidesJoysticks(const ADDON::AddonInfoPtr& addonInfo); + static bool ProvidesButtonMaps(const ADDON::AddonInfoPtr& addonInfo); + +private: + void UnregisterButtonMap(CPeripheral* device); + + // Binary add-on callbacks + void TriggerDeviceScan(); + void RefreshButtonMaps(const std::string& strDeviceName = ""); + unsigned int FeatureCount(const std::string& controllerId, JOYSTICK_FEATURE_TYPE type) const; + JOYSTICK_FEATURE_TYPE FeatureType(const std::string& controllerId, + const std::string& featureName) const; + + /*! + * @brief Helper functions + */ + static void GetPeripheralInfo(const CPeripheral* device, kodi::addon::Peripheral& peripheralInfo); + + static void GetJoystickInfo(const CPeripheral* device, kodi::addon::Joystick& joystickInfo); + static void SetJoystickInfo(CPeripheralJoystick& joystick, + const kodi::addon::Joystick& joystickInfo); + + /*! + * @brief Reset all class members to their defaults. Called by the constructors + */ + void ResetProperties(void); + + /*! + * @brief Retrieve add-on properties from the add-on + */ + bool GetAddonProperties(void); + + bool LogError(const PERIPHERAL_ERROR error, const char* strMethod) const; + + static std::string GetDeviceName(PeripheralType type); + static std::string GetProvider(PeripheralType type); + + // Construction parameters + CPeripherals& m_manager; + + /* @brief Cache for const char* members in PERIPHERAL_PROPERTIES */ + std::string m_strUserPath; /*!< @brief translated path to the user profile */ + std::string m_strClientPath; /*!< @brief translated path to this add-on */ + + /*! + * @brief Callback functions from addon to kodi + */ + //@{ + static void cb_trigger_scan(void* kodiInstance); + static void cb_refresh_button_maps(void* kodiInstance, + const char* deviceName, + const char* controllerId); + static unsigned int cb_feature_count(void* kodiInstance, + const char* controllerId, + JOYSTICK_FEATURE_TYPE type); + static JOYSTICK_FEATURE_TYPE cb_feature_type(void* kodiInstance, + const char* controllerId, + const char* featureName); + //@} + + /* @brief Add-on properties */ + bool m_bProvidesJoysticks; + bool m_bSupportsJoystickRumble; + bool m_bSupportsJoystickPowerOff; + bool m_bProvidesButtonMaps; + + /* @brief Map of peripherals belonging to the add-on */ + std::map<unsigned int, PeripheralPtr> m_peripherals; + + /* @brief Button map observers */ + std::vector<std::pair<CPeripheral*, KODI::JOYSTICK::IButtonMap*>> m_buttonMaps; + CCriticalSection m_buttonMapMutex; + + /* @brief Thread synchronization */ + mutable CCriticalSection m_critSection; + + CSharedSection m_dllSection; +}; +} // namespace PERIPHERALS diff --git a/xbmc/peripherals/addons/PeripheralAddonTranslator.cpp b/xbmc/peripherals/addons/PeripheralAddonTranslator.cpp new file mode 100644 index 0000000..16dd529 --- /dev/null +++ b/xbmc/peripherals/addons/PeripheralAddonTranslator.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2015-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PeripheralAddonTranslator.h" + +#include "games/controllers/ControllerTranslator.h" +#include "input/joysticks/JoystickUtils.h" + +#include <algorithm> +#include <iterator> + +using namespace KODI; +using namespace JOYSTICK; +using namespace PERIPHERALS; + +// --- Helper function --------------------------------------------------------- + +JOYSTICK_DRIVER_SEMIAXIS_DIRECTION operator*(JOYSTICK_DRIVER_SEMIAXIS_DIRECTION dir, int i) +{ + return static_cast<JOYSTICK_DRIVER_SEMIAXIS_DIRECTION>(static_cast<int>(dir) * i); +} + +// --- CPeripheralAddonTranslator ---------------------------------------------- + +const char* CPeripheralAddonTranslator::TranslateError(const PERIPHERAL_ERROR error) +{ + switch (error) + { + case PERIPHERAL_NO_ERROR: + return "no error"; + case PERIPHERAL_ERROR_FAILED: + return "command failed"; + case PERIPHERAL_ERROR_INVALID_PARAMETERS: + return "invalid parameters"; + case PERIPHERAL_ERROR_NOT_IMPLEMENTED: + return "not implemented"; + case PERIPHERAL_ERROR_NOT_CONNECTED: + return "not connected"; + case PERIPHERAL_ERROR_CONNECTION_FAILED: + return "connection failed"; + case PERIPHERAL_ERROR_UNKNOWN: + default: + return "unknown error"; + } +} + +PeripheralType CPeripheralAddonTranslator::TranslateType(PERIPHERAL_TYPE type) +{ + switch (type) + { + case PERIPHERAL_TYPE_JOYSTICK: + return PERIPHERAL_JOYSTICK; + default: + break; + } + return PERIPHERAL_UNKNOWN; +} + +PERIPHERAL_TYPE CPeripheralAddonTranslator::TranslateType(PeripheralType type) +{ + switch (type) + { + case PERIPHERAL_JOYSTICK: + return PERIPHERAL_TYPE_JOYSTICK; + default: + break; + } + return PERIPHERAL_TYPE_UNKNOWN; +} + +CDriverPrimitive CPeripheralAddonTranslator::TranslatePrimitive( + const kodi::addon::DriverPrimitive& primitive) +{ + CDriverPrimitive retVal; + + switch (primitive.Type()) + { + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_BUTTON: + { + retVal = CDriverPrimitive(PRIMITIVE_TYPE::BUTTON, primitive.DriverIndex()); + break; + } + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_HAT_DIRECTION: + { + retVal = CDriverPrimitive(primitive.DriverIndex(), + TranslateHatDirection(primitive.HatDirection())); + break; + } + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_SEMIAXIS: + { + retVal = CDriverPrimitive(primitive.DriverIndex(), primitive.Center(), + TranslateSemiAxisDirection(primitive.SemiAxisDirection()), + primitive.Range()); + break; + } + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_MOTOR: + { + retVal = CDriverPrimitive(PRIMITIVE_TYPE::MOTOR, primitive.DriverIndex()); + break; + } + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_KEY: + { + KEYBOARD::KeySymbol keycode = + GAME::CControllerTranslator::TranslateKeysym(primitive.Keycode()); + retVal = CDriverPrimitive(keycode); + break; + } + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_MOUSE_BUTTON: + { + retVal = CDriverPrimitive(TranslateMouseButton(primitive.MouseIndex())); + break; + } + case JOYSTICK_DRIVER_PRIMITIVE_TYPE_RELPOINTER_DIRECTION: + { + retVal = CDriverPrimitive(TranslateRelPointerDirection(primitive.RelPointerDirection())); + break; + } + default: + break; + } + + return retVal; +} + +kodi::addon::DriverPrimitive CPeripheralAddonTranslator::TranslatePrimitive( + const CDriverPrimitive& primitive) +{ + kodi::addon::DriverPrimitive retVal; + + switch (primitive.Type()) + { + case PRIMITIVE_TYPE::BUTTON: + { + retVal = kodi::addon::DriverPrimitive::CreateButton(primitive.Index()); + break; + } + case PRIMITIVE_TYPE::HAT: + { + retVal = kodi::addon::DriverPrimitive(primitive.Index(), + TranslateHatDirection(primitive.HatDirection())); + break; + } + case PRIMITIVE_TYPE::SEMIAXIS: + { + retVal = kodi::addon::DriverPrimitive( + primitive.Index(), primitive.Center(), + TranslateSemiAxisDirection(primitive.SemiAxisDirection()), primitive.Range()); + break; + } + case PRIMITIVE_TYPE::MOTOR: + { + retVal = kodi::addon::DriverPrimitive::CreateMotor(primitive.Index()); + break; + } + case PRIMITIVE_TYPE::KEY: + { + std::string keysym = GAME::CControllerTranslator::TranslateKeycode(primitive.Keycode()); + retVal = kodi::addon::DriverPrimitive(keysym); + break; + } + case PRIMITIVE_TYPE::MOUSE_BUTTON: + { + retVal = kodi::addon::DriverPrimitive::CreateMouseButton( + TranslateMouseButton(primitive.MouseButton())); + break; + } + case PRIMITIVE_TYPE::RELATIVE_POINTER: + { + retVal = + kodi::addon::DriverPrimitive(TranslateRelPointerDirection(primitive.PointerDirection())); + break; + } + default: + break; + } + + return retVal; +} + +std::vector<JOYSTICK::CDriverPrimitive> CPeripheralAddonTranslator::TranslatePrimitives( + const std::vector<kodi::addon::DriverPrimitive>& primitives) +{ + std::vector<JOYSTICK::CDriverPrimitive> ret; + std::transform(primitives.begin(), primitives.end(), std::back_inserter(ret), + [](const kodi::addon::DriverPrimitive& primitive) { + return CPeripheralAddonTranslator::TranslatePrimitive(primitive); + }); + return ret; +} + +std::vector<kodi::addon::DriverPrimitive> CPeripheralAddonTranslator::TranslatePrimitives( + const std::vector<JOYSTICK::CDriverPrimitive>& primitives) +{ + std::vector<kodi::addon::DriverPrimitive> ret; + std::transform(primitives.begin(), primitives.end(), std::back_inserter(ret), + [](const JOYSTICK::CDriverPrimitive& primitive) { + return CPeripheralAddonTranslator::TranslatePrimitive(primitive); + }); + return ret; +} + +HAT_DIRECTION CPeripheralAddonTranslator::TranslateHatDirection(JOYSTICK_DRIVER_HAT_DIRECTION dir) +{ + switch (dir) + { + case JOYSTICK_DRIVER_HAT_LEFT: + return HAT_DIRECTION::LEFT; + case JOYSTICK_DRIVER_HAT_RIGHT: + return HAT_DIRECTION::RIGHT; + case JOYSTICK_DRIVER_HAT_UP: + return HAT_DIRECTION::UP; + case JOYSTICK_DRIVER_HAT_DOWN: + return HAT_DIRECTION::DOWN; + default: + break; + } + return HAT_DIRECTION::NONE; +} + +JOYSTICK_DRIVER_HAT_DIRECTION CPeripheralAddonTranslator::TranslateHatDirection(HAT_DIRECTION dir) +{ + switch (dir) + { + case HAT_DIRECTION::UP: + return JOYSTICK_DRIVER_HAT_UP; + case HAT_DIRECTION::DOWN: + return JOYSTICK_DRIVER_HAT_DOWN; + case HAT_DIRECTION::RIGHT: + return JOYSTICK_DRIVER_HAT_RIGHT; + case HAT_DIRECTION::LEFT: + return JOYSTICK_DRIVER_HAT_LEFT; + default: + break; + } + return JOYSTICK_DRIVER_HAT_UNKNOWN; +} + +HAT_STATE CPeripheralAddonTranslator::TranslateHatState(JOYSTICK_STATE_HAT state) +{ + HAT_STATE translatedState = HAT_STATE::NONE; + + if (state & JOYSTICK_STATE_HAT_UP) + translatedState |= HAT_STATE::UP; + if (state & JOYSTICK_STATE_HAT_DOWN) + translatedState |= HAT_STATE::DOWN; + if (state & JOYSTICK_STATE_HAT_RIGHT) + translatedState |= HAT_STATE::RIGHT; + if (state & JOYSTICK_STATE_HAT_LEFT) + translatedState |= HAT_STATE::LEFT; + + return translatedState; +} + +SEMIAXIS_DIRECTION CPeripheralAddonTranslator::TranslateSemiAxisDirection( + JOYSTICK_DRIVER_SEMIAXIS_DIRECTION dir) +{ + switch (dir) + { + case JOYSTICK_DRIVER_SEMIAXIS_POSITIVE: + return SEMIAXIS_DIRECTION::POSITIVE; + case JOYSTICK_DRIVER_SEMIAXIS_NEGATIVE: + return SEMIAXIS_DIRECTION::NEGATIVE; + default: + break; + } + return SEMIAXIS_DIRECTION::ZERO; +} + +JOYSTICK_DRIVER_SEMIAXIS_DIRECTION CPeripheralAddonTranslator::TranslateSemiAxisDirection( + SEMIAXIS_DIRECTION dir) +{ + switch (dir) + { + case SEMIAXIS_DIRECTION::POSITIVE: + return JOYSTICK_DRIVER_SEMIAXIS_POSITIVE; + case SEMIAXIS_DIRECTION::NEGATIVE: + return JOYSTICK_DRIVER_SEMIAXIS_NEGATIVE; + default: + break; + } + return JOYSTICK_DRIVER_SEMIAXIS_UNKNOWN; +} + +MOUSE::BUTTON_ID CPeripheralAddonTranslator::TranslateMouseButton( + JOYSTICK_DRIVER_MOUSE_INDEX button) +{ + switch (button) + { + case JOYSTICK_DRIVER_MOUSE_INDEX_LEFT: + return MOUSE::BUTTON_ID::LEFT; + case JOYSTICK_DRIVER_MOUSE_INDEX_RIGHT: + return MOUSE::BUTTON_ID::RIGHT; + case JOYSTICK_DRIVER_MOUSE_INDEX_MIDDLE: + return MOUSE::BUTTON_ID::MIDDLE; + case JOYSTICK_DRIVER_MOUSE_INDEX_BUTTON4: + return MOUSE::BUTTON_ID::BUTTON4; + case JOYSTICK_DRIVER_MOUSE_INDEX_BUTTON5: + return MOUSE::BUTTON_ID::BUTTON5; + case JOYSTICK_DRIVER_MOUSE_INDEX_WHEEL_UP: + return MOUSE::BUTTON_ID::WHEEL_UP; + case JOYSTICK_DRIVER_MOUSE_INDEX_WHEEL_DOWN: + return MOUSE::BUTTON_ID::WHEEL_DOWN; + case JOYSTICK_DRIVER_MOUSE_INDEX_HORIZ_WHEEL_LEFT: + return MOUSE::BUTTON_ID::HORIZ_WHEEL_LEFT; + case JOYSTICK_DRIVER_MOUSE_INDEX_HORIZ_WHEEL_RIGHT: + return MOUSE::BUTTON_ID::HORIZ_WHEEL_RIGHT; + default: + break; + } + + return MOUSE::BUTTON_ID::UNKNOWN; +} + +JOYSTICK_DRIVER_MOUSE_INDEX CPeripheralAddonTranslator::TranslateMouseButton( + MOUSE::BUTTON_ID button) +{ + switch (button) + { + case MOUSE::BUTTON_ID::LEFT: + return JOYSTICK_DRIVER_MOUSE_INDEX_LEFT; + case MOUSE::BUTTON_ID::RIGHT: + return JOYSTICK_DRIVER_MOUSE_INDEX_RIGHT; + case MOUSE::BUTTON_ID::MIDDLE: + return JOYSTICK_DRIVER_MOUSE_INDEX_MIDDLE; + case MOUSE::BUTTON_ID::BUTTON4: + return JOYSTICK_DRIVER_MOUSE_INDEX_BUTTON4; + case MOUSE::BUTTON_ID::BUTTON5: + return JOYSTICK_DRIVER_MOUSE_INDEX_BUTTON5; + case MOUSE::BUTTON_ID::WHEEL_UP: + return JOYSTICK_DRIVER_MOUSE_INDEX_WHEEL_UP; + case MOUSE::BUTTON_ID::WHEEL_DOWN: + return JOYSTICK_DRIVER_MOUSE_INDEX_WHEEL_DOWN; + case MOUSE::BUTTON_ID::HORIZ_WHEEL_LEFT: + return JOYSTICK_DRIVER_MOUSE_INDEX_HORIZ_WHEEL_LEFT; + case MOUSE::BUTTON_ID::HORIZ_WHEEL_RIGHT: + return JOYSTICK_DRIVER_MOUSE_INDEX_HORIZ_WHEEL_RIGHT; + default: + break; + } + + return JOYSTICK_DRIVER_MOUSE_INDEX_UNKNOWN; +} + +RELATIVE_POINTER_DIRECTION CPeripheralAddonTranslator::TranslateRelPointerDirection( + JOYSTICK_DRIVER_RELPOINTER_DIRECTION dir) +{ + switch (dir) + { + case JOYSTICK_DRIVER_RELPOINTER_LEFT: + return RELATIVE_POINTER_DIRECTION::LEFT; + case JOYSTICK_DRIVER_RELPOINTER_RIGHT: + return RELATIVE_POINTER_DIRECTION::RIGHT; + case JOYSTICK_DRIVER_RELPOINTER_UP: + return RELATIVE_POINTER_DIRECTION::UP; + case JOYSTICK_DRIVER_RELPOINTER_DOWN: + return RELATIVE_POINTER_DIRECTION::DOWN; + default: + break; + } + + return RELATIVE_POINTER_DIRECTION::NONE; +} + +JOYSTICK_DRIVER_RELPOINTER_DIRECTION CPeripheralAddonTranslator::TranslateRelPointerDirection( + RELATIVE_POINTER_DIRECTION dir) +{ + switch (dir) + { + case RELATIVE_POINTER_DIRECTION::UP: + return JOYSTICK_DRIVER_RELPOINTER_UP; + case RELATIVE_POINTER_DIRECTION::DOWN: + return JOYSTICK_DRIVER_RELPOINTER_DOWN; + case RELATIVE_POINTER_DIRECTION::RIGHT: + return JOYSTICK_DRIVER_RELPOINTER_RIGHT; + case RELATIVE_POINTER_DIRECTION::LEFT: + return JOYSTICK_DRIVER_RELPOINTER_LEFT; + default: + break; + } + return JOYSTICK_DRIVER_RELPOINTER_UNKNOWN; +} + +JOYSTICK::FEATURE_TYPE CPeripheralAddonTranslator::TranslateFeatureType(JOYSTICK_FEATURE_TYPE type) +{ + switch (type) + { + case JOYSTICK_FEATURE_TYPE_SCALAR: + return JOYSTICK::FEATURE_TYPE::SCALAR; + case JOYSTICK_FEATURE_TYPE_ANALOG_STICK: + return JOYSTICK::FEATURE_TYPE::ANALOG_STICK; + case JOYSTICK_FEATURE_TYPE_ACCELEROMETER: + return JOYSTICK::FEATURE_TYPE::ACCELEROMETER; + case JOYSTICK_FEATURE_TYPE_MOTOR: + return JOYSTICK::FEATURE_TYPE::MOTOR; + case JOYSTICK_FEATURE_TYPE_RELPOINTER: + return JOYSTICK::FEATURE_TYPE::RELPOINTER; + case JOYSTICK_FEATURE_TYPE_ABSPOINTER: + return JOYSTICK::FEATURE_TYPE::ABSPOINTER; + case JOYSTICK_FEATURE_TYPE_WHEEL: + return JOYSTICK::FEATURE_TYPE::WHEEL; + case JOYSTICK_FEATURE_TYPE_THROTTLE: + return JOYSTICK::FEATURE_TYPE::THROTTLE; + case JOYSTICK_FEATURE_TYPE_KEY: + return JOYSTICK::FEATURE_TYPE::KEY; + default: + break; + } + return JOYSTICK::FEATURE_TYPE::UNKNOWN; +} + +JOYSTICK_FEATURE_TYPE CPeripheralAddonTranslator::TranslateFeatureType(JOYSTICK::FEATURE_TYPE type) +{ + switch (type) + { + case JOYSTICK::FEATURE_TYPE::SCALAR: + return JOYSTICK_FEATURE_TYPE_SCALAR; + case JOYSTICK::FEATURE_TYPE::ANALOG_STICK: + return JOYSTICK_FEATURE_TYPE_ANALOG_STICK; + case JOYSTICK::FEATURE_TYPE::ACCELEROMETER: + return JOYSTICK_FEATURE_TYPE_ACCELEROMETER; + case JOYSTICK::FEATURE_TYPE::MOTOR: + return JOYSTICK_FEATURE_TYPE_MOTOR; + case JOYSTICK::FEATURE_TYPE::RELPOINTER: + return JOYSTICK_FEATURE_TYPE_RELPOINTER; + case JOYSTICK::FEATURE_TYPE::ABSPOINTER: + return JOYSTICK_FEATURE_TYPE_ABSPOINTER; + case JOYSTICK::FEATURE_TYPE::WHEEL: + return JOYSTICK_FEATURE_TYPE_WHEEL; + case JOYSTICK::FEATURE_TYPE::THROTTLE: + return JOYSTICK_FEATURE_TYPE_THROTTLE; + case JOYSTICK::FEATURE_TYPE::KEY: + return JOYSTICK_FEATURE_TYPE_KEY; + default: + break; + } + return JOYSTICK_FEATURE_TYPE_UNKNOWN; +} + +kodi::addon::DriverPrimitive CPeripheralAddonTranslator::Opposite( + const kodi::addon::DriverPrimitive& primitive) +{ + return kodi::addon::DriverPrimitive(primitive.DriverIndex(), primitive.Center() * -1, + primitive.SemiAxisDirection() * -1, primitive.Range()); +} diff --git a/xbmc/peripherals/addons/PeripheralAddonTranslator.h b/xbmc/peripherals/addons/PeripheralAddonTranslator.h new file mode 100644 index 0000000..308d62c --- /dev/null +++ b/xbmc/peripherals/addons/PeripheralAddonTranslator.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "addons/kodi-dev-kit/include/kodi/addon-instance/Peripheral.h" +#include "input/joysticks/DriverPrimitive.h" +#include "input/joysticks/JoystickTypes.h" +#include "input/mouse/MouseTypes.h" +#include "peripherals/PeripheralTypes.h" + +#include <vector> + +namespace PERIPHERALS +{ +class CPeripheralAddonTranslator +{ +public: + static const char* TranslateError(PERIPHERAL_ERROR error); + + static PeripheralType TranslateType(PERIPHERAL_TYPE type); + static PERIPHERAL_TYPE TranslateType(PeripheralType type); + + static KODI::JOYSTICK::CDriverPrimitive TranslatePrimitive( + const kodi::addon::DriverPrimitive& primitive); + static kodi::addon::DriverPrimitive TranslatePrimitive( + const KODI::JOYSTICK::CDriverPrimitive& primitive); + + static std::vector<KODI::JOYSTICK::CDriverPrimitive> TranslatePrimitives( + const std::vector<kodi::addon::DriverPrimitive>& primitives); + static std::vector<kodi::addon::DriverPrimitive> TranslatePrimitives( + const std::vector<KODI::JOYSTICK::CDriverPrimitive>& primitives); + + static KODI::JOYSTICK::HAT_DIRECTION TranslateHatDirection(JOYSTICK_DRIVER_HAT_DIRECTION dir); + static JOYSTICK_DRIVER_HAT_DIRECTION TranslateHatDirection(KODI::JOYSTICK::HAT_DIRECTION dir); + + static KODI::JOYSTICK::HAT_STATE TranslateHatState(JOYSTICK_STATE_HAT state); + + static KODI::JOYSTICK::SEMIAXIS_DIRECTION TranslateSemiAxisDirection( + JOYSTICK_DRIVER_SEMIAXIS_DIRECTION dir); + static JOYSTICK_DRIVER_SEMIAXIS_DIRECTION TranslateSemiAxisDirection( + KODI::JOYSTICK::SEMIAXIS_DIRECTION dir); + + static KODI::MOUSE::BUTTON_ID TranslateMouseButton(JOYSTICK_DRIVER_MOUSE_INDEX button); + static JOYSTICK_DRIVER_MOUSE_INDEX TranslateMouseButton(KODI::MOUSE::BUTTON_ID button); + + static KODI::JOYSTICK::RELATIVE_POINTER_DIRECTION TranslateRelPointerDirection( + JOYSTICK_DRIVER_RELPOINTER_DIRECTION dir); + static JOYSTICK_DRIVER_RELPOINTER_DIRECTION TranslateRelPointerDirection( + KODI::JOYSTICK::RELATIVE_POINTER_DIRECTION dir); + + static KODI::JOYSTICK::FEATURE_TYPE TranslateFeatureType(JOYSTICK_FEATURE_TYPE type); + static JOYSTICK_FEATURE_TYPE TranslateFeatureType(KODI::JOYSTICK::FEATURE_TYPE type); + + static kodi::addon::DriverPrimitive Opposite(const kodi::addon::DriverPrimitive& semiaxis); +}; +} // namespace PERIPHERALS |