/* * 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 #include #include #include #include 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 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 lock(m_critSection); m_peripherals.clear(); } { std::unique_lock 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 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 lock(m_critSection); if (m_peripherals.find(peripheralIndex) == m_peripherals.end()) { if (peripheral->Type() == PERIPHERAL_JOYSTICK) { m_peripherals[peripheralIndex] = std::static_pointer_cast(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 removedIndexes; { std::unique_lock 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& 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 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 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 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 lock(m_critSection); return static_cast(m_peripherals.size()); } unsigned int CPeripheralAddon::GetNumberOfPeripheralsWithId(const int iVendorId, const int iProductId) const { unsigned int iReturn = 0; std::unique_lock 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 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 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 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 joystickDevice = std::static_pointer_cast(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(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 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 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 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 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 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 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(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 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 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 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 lock(m_buttonMapMutex); UnregisterButtonMap(buttonMap); m_buttonMaps.emplace_back(device, buttonMap); } void CPeripheralAddon::UnregisterButtonMap(IButtonMap* buttonMap) { std::unique_lock 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 lock(m_buttonMapMutex); m_buttonMaps.erase( std::remove_if(m_buttonMaps.begin(), m_buttonMaps.end(), [device](const std::pair& buttonMap) { return buttonMap.first == device; }), m_buttonMaps.end()); } void CPeripheralAddon::RefreshButtonMaps(const std::string& strDeviceName /* = "" */) { std::unique_lock 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(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(kodiInstance)->TriggerDeviceScan(); } void CPeripheralAddon::cb_refresh_button_maps(void* kodiInstance, const char* deviceName, const char* controllerId) { if (!kodiInstance) return; static_cast(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(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(kodiInstance)->FeatureType(controllerId, featureName); }