summaryrefslogtreecommitdiffstats
path: root/xbmc/peripherals/addons
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/peripherals/addons')
-rw-r--r--xbmc/peripherals/addons/AddonButtonMap.cpp734
-rw-r--r--xbmc/peripherals/addons/AddonButtonMap.h146
-rw-r--r--xbmc/peripherals/addons/AddonButtonMapping.cpp139
-rw-r--r--xbmc/peripherals/addons/AddonButtonMapping.h72
-rw-r--r--xbmc/peripherals/addons/AddonInputHandling.cpp192
-rw-r--r--xbmc/peripherals/addons/AddonInputHandling.h108
-rw-r--r--xbmc/peripherals/addons/CMakeLists.txt13
-rw-r--r--xbmc/peripherals/addons/PeripheralAddon.cpp990
-rw-r--r--xbmc/peripherals/addons/PeripheralAddon.h178
-rw-r--r--xbmc/peripherals/addons/PeripheralAddonTranslator.cpp449
-rw-r--r--xbmc/peripherals/addons/PeripheralAddonTranslator.h62
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