diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/peripherals/addons/PeripheralAddon.cpp | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/peripherals/addons/PeripheralAddon.cpp')
-rw-r--r-- | xbmc/peripherals/addons/PeripheralAddon.cpp | 990 |
1 files changed, 990 insertions, 0 deletions
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); +} |