[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / peripherals / addons / PeripheralAddon.cpp
blob2b7aca75843bd6ede0de7216c5c9e60a41fb84d1
1 /*
2 * Copyright (C) 2014-2024 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "PeripheralAddon.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "PeripheralAddonTranslator.h"
14 #include "addons/addoninfo/AddonInfo.h"
15 #include "addons/addoninfo/AddonType.h"
16 #include "filesystem/Directory.h"
17 #include "filesystem/SpecialProtocol.h"
18 #include "games/controllers/Controller.h"
19 #include "games/controllers/ControllerManager.h"
20 #include "input/joysticks/DriverPrimitive.h"
21 #include "input/joysticks/interfaces/IButtonMap.h"
22 #include "peripherals/Peripherals.h"
23 #include "peripherals/bus/virtual/PeripheralBusAddon.h"
24 #include "peripherals/devices/PeripheralJoystick.h"
25 #include "utils/StringUtils.h"
26 #include "utils/log.h"
28 #include <algorithm>
29 #include <mutex>
30 #include <shared_mutex>
31 #include <string.h>
32 #include <utility>
34 using namespace KODI;
35 using namespace JOYSTICK;
36 using namespace PERIPHERALS;
37 using namespace XFILE;
39 #define KEYBOARD_BUTTON_MAP_NAME "Keyboard"
40 #define KEYBOARD_PROVIDER "application"
42 #define MOUSE_BUTTON_MAP_NAME "Mouse"
43 #define MOUSE_PROVIDER "application"
45 #ifndef SAFE_DELETE
46 #define SAFE_DELETE(p) \
47 do \
48 { \
49 delete (p); \
50 (p) = NULL; \
51 } while (0)
52 #endif
54 CPeripheralAddon::CPeripheralAddon(const ADDON::AddonInfoPtr& addonInfo, CPeripherals& manager)
55 : IAddonInstanceHandler(ADDON_INSTANCE_PERIPHERAL, addonInfo), m_manager(manager)
57 m_bProvidesJoysticks =
58 addonInfo->Type(ADDON::AddonType::PERIPHERALDLL)->GetValue("@provides_joysticks").asBoolean();
59 m_bProvidesButtonMaps = addonInfo->Type(ADDON::AddonType::PERIPHERALDLL)
60 ->GetValue("@provides_buttonmaps")
61 .asBoolean();
63 // Create "C" interface structures, used as own parts to prevent API problems on update
64 m_ifc.peripheral = new AddonInstance_Peripheral;
65 m_ifc.peripheral->props = new AddonProps_Peripheral();
66 m_ifc.peripheral->toAddon = new KodiToAddonFuncTable_Peripheral();
67 m_ifc.peripheral->toKodi = new AddonToKodiFuncTable_Peripheral();
69 ResetProperties();
72 CPeripheralAddon::~CPeripheralAddon(void)
74 DestroyAddon();
76 if (m_ifc.peripheral)
78 delete m_ifc.peripheral->toAddon;
79 delete m_ifc.peripheral->toKodi;
80 delete m_ifc.peripheral->props;
82 delete m_ifc.peripheral;
85 void CPeripheralAddon::ResetProperties(void)
87 // Initialise members
88 m_strUserPath = CSpecialProtocol::TranslatePath(Profile());
89 m_strClientPath = CSpecialProtocol::TranslatePath(Path());
91 m_ifc.peripheral->props->user_path = m_strUserPath.c_str();
92 m_ifc.peripheral->props->addon_path = m_strClientPath.c_str();
94 m_ifc.peripheral->toKodi->kodiInstance = this;
95 m_ifc.peripheral->toKodi->feature_count = cb_feature_count;
96 m_ifc.peripheral->toKodi->feature_type = cb_feature_type;
97 m_ifc.peripheral->toKodi->refresh_button_maps = cb_refresh_button_maps;
98 m_ifc.peripheral->toKodi->trigger_scan = cb_trigger_scan;
100 memset(m_ifc.peripheral->toAddon, 0, sizeof(KodiToAddonFuncTable_Peripheral));
103 bool CPeripheralAddon::CreateAddon(void)
105 std::unique_lock<CSharedSection> lock(m_dllSection);
107 // Reset all properties to defaults
108 ResetProperties();
110 // Create directory for user data
111 if (!CDirectory::Exists(m_strUserPath))
112 CDirectory::Create(m_strUserPath);
114 // Initialise the add-on
115 CLog::Log(LOGDEBUG, "PERIPHERAL - {} - creating peripheral add-on instance '{}'", __FUNCTION__,
116 Name());
118 if (CreateInstance() != ADDON_STATUS_OK)
119 return false;
121 if (!GetAddonProperties())
123 DestroyInstance();
124 return false;
127 return true;
130 void CPeripheralAddon::DestroyAddon()
133 std::unique_lock<CCriticalSection> lock(m_critSection);
134 m_peripherals.clear();
138 std::unique_lock<CCriticalSection> lock(m_buttonMapMutex);
139 // only clear buttonMaps but don't delete them as they are owned by a
140 // CAddonJoystickInputHandling instance
141 m_buttonMaps.clear();
145 std::unique_lock<CSharedSection> lock(m_dllSection);
146 DestroyInstance();
150 bool CPeripheralAddon::GetAddonProperties(void)
152 PERIPHERAL_CAPABILITIES addonCapabilities = {};
154 // Get the capabilities
155 m_ifc.peripheral->toAddon->get_capabilities(m_ifc.peripheral, &addonCapabilities);
157 // Verify capabilities against addon.xml
158 if (m_bProvidesJoysticks != addonCapabilities.provides_joysticks)
160 CLog::Log(
161 LOGERROR,
162 "PERIPHERAL - Add-on '{}': provides_joysticks'({}) in add-on DLL doesn't match "
163 "'provides_joysticks'({}) in addon.xml. Please contact the developer of this add-on: {}",
164 Name(), addonCapabilities.provides_joysticks ? "true" : "false",
165 m_bProvidesJoysticks ? "true" : "false", Author());
166 return false;
168 if (m_bProvidesButtonMaps != addonCapabilities.provides_buttonmaps)
170 CLog::Log(
171 LOGERROR,
172 "PERIPHERAL - Add-on '{}': provides_buttonmaps' ({}) in add-on DLL doesn't match "
173 "'provides_buttonmaps' ({}) in addon.xml. Please contact the developer of this add-on: {}",
174 Name(), addonCapabilities.provides_buttonmaps ? "true" : "false",
175 m_bProvidesButtonMaps ? "true" : "false", Author());
176 return false;
179 // Read properties that depend on underlying driver
180 m_bSupportsJoystickRumble = addonCapabilities.provides_joystick_rumble;
181 m_bSupportsJoystickPowerOff = addonCapabilities.provides_joystick_power_off;
183 return true;
186 bool CPeripheralAddon::Register(unsigned int peripheralIndex, const PeripheralPtr& peripheral)
188 if (!peripheral)
189 return false;
191 std::unique_lock<CCriticalSection> lock(m_critSection);
192 if (m_peripherals.find(peripheralIndex) == m_peripherals.end())
194 if (peripheral->Type() == PERIPHERAL_JOYSTICK)
196 m_peripherals[peripheralIndex] = std::static_pointer_cast<CPeripheralJoystick>(peripheral);
198 CLog::Log(LOGINFO, "{} - new {} device registered on {}->{}: {}", __FUNCTION__,
199 PeripheralTypeTranslator::TypeToString(peripheral->Type()),
200 PeripheralTypeTranslator::BusTypeToString(PERIPHERAL_BUS_ADDON),
201 peripheral->Location(), peripheral->DeviceName());
203 return true;
206 return false;
209 void CPeripheralAddon::UnregisterRemovedDevices(const PeripheralScanResults& results,
210 PeripheralVector& removedPeripherals)
212 std::vector<unsigned int> removedIndexes;
215 std::unique_lock<CCriticalSection> lock(m_critSection);
216 for (auto& it : m_peripherals)
218 const PeripheralPtr& peripheral = it.second;
219 PeripheralScanResult updatedDevice(PERIPHERAL_BUS_ADDON);
220 if (!results.GetDeviceOnLocation(peripheral->Location(), &updatedDevice) ||
221 *peripheral != updatedDevice)
223 // Device removed
224 removedIndexes.push_back(it.first);
229 for (auto index : removedIndexes)
231 auto it = m_peripherals.find(index);
232 const PeripheralPtr& peripheral = it->second;
233 CLog::Log(LOGINFO, "{} - device removed from {}/{}: {} ({}:{})", __FUNCTION__,
234 PeripheralTypeTranslator::TypeToString(peripheral->Type()), peripheral->Location(),
235 peripheral->DeviceName(), peripheral->VendorIdAsString(),
236 peripheral->ProductIdAsString());
237 UnregisterButtonMap(peripheral.get());
238 peripheral->OnDeviceRemoved();
239 removedPeripherals.push_back(peripheral);
240 m_peripherals.erase(it);
244 bool CPeripheralAddon::HasFeature(const PeripheralFeature feature) const
246 if (feature == FEATURE_JOYSTICK)
247 return m_bProvidesJoysticks;
249 return false;
252 void CPeripheralAddon::GetFeatures(std::vector<PeripheralFeature>& features) const
254 if (m_bProvidesJoysticks &&
255 std::find(features.begin(), features.end(), FEATURE_JOYSTICK) == features.end())
256 features.push_back(FEATURE_JOYSTICK);
259 PeripheralPtr CPeripheralAddon::GetPeripheral(unsigned int index) const
261 PeripheralPtr peripheral;
262 std::unique_lock<CCriticalSection> lock(m_critSection);
263 auto it = m_peripherals.find(index);
264 if (it != m_peripherals.end())
265 peripheral = it->second;
266 return peripheral;
269 PeripheralPtr CPeripheralAddon::GetByPath(const std::string& strPath) const
271 PeripheralPtr result;
273 std::unique_lock<CCriticalSection> lock(m_critSection);
274 for (const auto& it : m_peripherals)
276 if (StringUtils::EqualsNoCase(strPath, it.second->FileLocation()))
278 result = it.second;
279 break;
283 return result;
286 bool CPeripheralAddon::SupportsFeature(PeripheralFeature feature) const
288 switch (feature)
290 case FEATURE_RUMBLE:
291 return m_bSupportsJoystickRumble;
292 case FEATURE_POWER_OFF:
293 return m_bSupportsJoystickPowerOff;
294 default:
295 break;
298 return false;
301 unsigned int CPeripheralAddon::GetPeripheralsWithFeature(PeripheralVector& results,
302 const PeripheralFeature feature) const
304 unsigned int iReturn = 0;
305 std::unique_lock<CCriticalSection> lock(m_critSection);
306 for (const auto& it : m_peripherals)
308 if (it.second->HasFeature(feature))
310 results.push_back(it.second);
311 ++iReturn;
314 return iReturn;
317 unsigned int CPeripheralAddon::GetNumberOfPeripherals(void) const
319 std::unique_lock<CCriticalSection> lock(m_critSection);
320 return static_cast<unsigned int>(m_peripherals.size());
323 unsigned int CPeripheralAddon::GetNumberOfPeripheralsWithId(const int iVendorId,
324 const int iProductId) const
326 unsigned int iReturn = 0;
327 std::unique_lock<CCriticalSection> lock(m_critSection);
328 for (const auto& it : m_peripherals)
330 if (it.second->VendorId() == iVendorId && it.second->ProductId() == iProductId)
331 iReturn++;
334 return iReturn;
337 void CPeripheralAddon::GetDirectory(const std::string& strPath, CFileItemList& items) const
339 std::unique_lock<CCriticalSection> lock(m_critSection);
340 for (const auto& it : m_peripherals)
342 const PeripheralPtr& peripheral = it.second;
343 if (peripheral->IsHidden())
344 continue;
346 CFileItemPtr peripheralFile(new CFileItem(peripheral->DeviceName()));
347 peripheralFile->SetPath(peripheral->FileLocation());
348 peripheralFile->SetProperty("vendor", peripheral->VendorIdAsString());
349 peripheralFile->SetProperty("product", peripheral->ProductIdAsString());
350 peripheralFile->SetProperty(
351 "bus", PeripheralTypeTranslator::BusTypeToString(peripheral->GetBusType()));
352 peripheralFile->SetProperty("location", peripheral->Location());
353 peripheralFile->SetProperty("class",
354 PeripheralTypeTranslator::TypeToString(peripheral->Type()));
355 peripheralFile->SetProperty("version", peripheral->GetVersionInfo());
356 peripheralFile->SetArt("icon", peripheral->GetIcon());
357 items.Add(peripheralFile);
361 bool CPeripheralAddon::PerformDeviceScan(PeripheralScanResults& results)
363 unsigned int peripheralCount;
364 PERIPHERAL_INFO* pScanResults;
365 PERIPHERAL_ERROR retVal;
367 std::shared_lock<CSharedSection> lock(m_dllSection);
369 if (!m_ifc.peripheral->toAddon->perform_device_scan)
370 return false;
372 LogError(retVal = m_ifc.peripheral->toAddon->perform_device_scan(m_ifc.peripheral,
373 &peripheralCount, &pScanResults),
374 "PerformDeviceScan()");
376 if (retVal == PERIPHERAL_NO_ERROR)
378 for (unsigned int i = 0; i < peripheralCount; i++)
380 kodi::addon::Peripheral peripheral(pScanResults[i]);
381 PeripheralScanResult result(PERIPHERAL_BUS_ADDON);
382 switch (peripheral.Type())
384 case PERIPHERAL_TYPE_JOYSTICK:
385 result.m_type = PERIPHERAL_JOYSTICK;
386 break;
387 default:
388 continue;
391 result.m_strDeviceName = peripheral.Name();
392 result.m_strLocation = StringUtils::Format("{}/{}", ID(), peripheral.Index());
393 result.m_iVendorId = peripheral.VendorID();
394 result.m_iProductId = peripheral.ProductID();
395 result.m_mappedType = PERIPHERAL_JOYSTICK;
396 result.m_mappedBusType = PERIPHERAL_BUS_ADDON;
397 result.m_iSequence = 0;
399 if (!results.ContainsResult(result))
400 results.m_results.push_back(result);
403 m_ifc.peripheral->toAddon->free_scan_results(m_ifc.peripheral, peripheralCount, pScanResults);
405 return true;
408 return false;
411 bool CPeripheralAddon::ProcessEvents(void)
413 if (!m_bProvidesJoysticks)
414 return false;
416 std::shared_lock<CSharedSection> lock(m_dllSection);
418 if (!m_ifc.peripheral->toAddon->get_events)
419 return false;
421 PERIPHERAL_ERROR retVal;
423 unsigned int eventCount = 0;
424 PERIPHERAL_EVENT* pEvents = nullptr;
426 LogError(retVal = m_ifc.peripheral->toAddon->get_events(m_ifc.peripheral, &eventCount, &pEvents),
427 "GetEvents()");
428 if (retVal == PERIPHERAL_NO_ERROR)
430 for (unsigned int i = 0; i < eventCount; i++)
432 kodi::addon::PeripheralEvent event(pEvents[i]);
433 PeripheralPtr device = GetPeripheral(event.PeripheralIndex());
434 if (!device)
435 continue;
437 switch (device->Type())
439 case PERIPHERAL_JOYSTICK:
441 std::shared_ptr<CPeripheralJoystick> joystickDevice =
442 std::static_pointer_cast<CPeripheralJoystick>(device);
444 switch (event.Type())
446 case PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON:
448 const bool bPressed = (event.ButtonState() == JOYSTICK_STATE_BUTTON_PRESSED);
449 joystickDevice->OnButtonMotion(event.DriverIndex(), bPressed);
450 break;
452 case PERIPHERAL_EVENT_TYPE_DRIVER_HAT:
454 const HAT_STATE state =
455 CPeripheralAddonTranslator::TranslateHatState(event.HatState());
456 joystickDevice->OnHatMotion(event.DriverIndex(), state);
457 break;
459 case PERIPHERAL_EVENT_TYPE_DRIVER_AXIS:
461 joystickDevice->OnAxisMotion(event.DriverIndex(), event.AxisState());
462 break;
464 default:
465 break;
467 break;
469 default:
470 break;
474 for (const auto& it : m_peripherals)
476 if (it.second->Type() == PERIPHERAL_JOYSTICK)
477 std::static_pointer_cast<CPeripheralJoystick>(it.second)->OnInputFrame();
480 m_ifc.peripheral->toAddon->free_events(m_ifc.peripheral, eventCount, pEvents);
482 return true;
485 return false;
488 bool CPeripheralAddon::SendRumbleEvent(unsigned int peripheralIndex,
489 unsigned int driverIndex,
490 float magnitude)
492 if (!m_bProvidesJoysticks)
493 return false;
495 std::shared_lock<CSharedSection> lock(m_dllSection);
497 if (!m_ifc.peripheral->toAddon->send_event)
498 return false;
500 PERIPHERAL_EVENT eventStruct = {};
502 eventStruct.peripheral_index = peripheralIndex;
503 eventStruct.type = PERIPHERAL_EVENT_TYPE_SET_MOTOR;
504 eventStruct.driver_index = driverIndex;
505 eventStruct.motor_state = magnitude;
507 return m_ifc.peripheral->toAddon->send_event(m_ifc.peripheral, &eventStruct);
510 bool CPeripheralAddon::GetJoystickProperties(unsigned int index, CPeripheralJoystick& joystick)
512 if (!m_bProvidesJoysticks)
513 return false;
515 std::shared_lock<CSharedSection> lock(m_dllSection);
517 if (!m_ifc.peripheral->toAddon->get_joystick_info)
518 return false;
520 PERIPHERAL_ERROR retVal;
522 JOYSTICK_INFO joystickStruct;
524 LogError(retVal = m_ifc.peripheral->toAddon->get_joystick_info(m_ifc.peripheral, index,
525 &joystickStruct),
526 "GetJoystickInfo()");
527 if (retVal == PERIPHERAL_NO_ERROR)
529 kodi::addon::Joystick addonJoystick(joystickStruct);
530 SetJoystickInfo(joystick, addonJoystick);
532 m_ifc.peripheral->toAddon->free_joystick_info(m_ifc.peripheral, &joystickStruct);
534 return true;
537 return false;
540 bool CPeripheralAddon::GetAppearance(const CPeripheral* device, std::string& controllerId)
542 if (!m_bProvidesButtonMaps)
543 return false;
545 std::shared_lock<CSharedSection> lock(m_dllSection);
547 if (!m_ifc.peripheral->toAddon->get_appearance)
548 return false;
550 PERIPHERAL_ERROR retVal;
552 kodi::addon::Joystick joystickInfo;
553 GetJoystickInfo(device, joystickInfo);
555 JOYSTICK_INFO joystickStruct;
556 joystickInfo.ToStruct(joystickStruct);
558 char strControllerId[1024]{};
560 LogError(retVal = m_ifc.peripheral->toAddon->get_appearance(
561 m_ifc.peripheral, &joystickStruct, strControllerId, sizeof(strControllerId)),
562 "GetAppearance()");
564 kodi::addon::Joystick::FreeStruct(joystickStruct);
566 if (retVal == PERIPHERAL_NO_ERROR)
568 controllerId = strControllerId;
569 return true;
572 return false;
575 bool CPeripheralAddon::SetAppearance(const CPeripheral* device, const std::string& controllerId)
577 if (!m_bProvidesButtonMaps)
578 return false;
580 std::shared_lock<CSharedSection> lock(m_dllSection);
582 if (!m_ifc.peripheral->toAddon->set_appearance)
583 return false;
585 PERIPHERAL_ERROR retVal;
587 kodi::addon::Joystick joystickInfo;
588 GetJoystickInfo(device, joystickInfo);
590 JOYSTICK_INFO joystickStruct;
591 joystickInfo.ToStruct(joystickStruct);
593 LogError(retVal = m_ifc.peripheral->toAddon->set_appearance(m_ifc.peripheral, &joystickStruct,
594 controllerId.c_str()),
595 "SetAppearance()");
597 kodi::addon::Joystick::FreeStruct(joystickStruct);
599 return retVal == PERIPHERAL_NO_ERROR;
602 bool CPeripheralAddon::GetFeatures(const CPeripheral* device,
603 const std::string& strControllerId,
604 FeatureMap& features)
606 if (!m_bProvidesButtonMaps)
607 return false;
609 std::shared_lock<CSharedSection> lock(m_dllSection);
611 if (!m_ifc.peripheral->toAddon->get_features)
612 return false;
614 PERIPHERAL_ERROR retVal;
616 kodi::addon::Joystick joystickInfo;
617 GetJoystickInfo(device, joystickInfo);
619 JOYSTICK_INFO joystickStruct;
620 joystickInfo.ToStruct(joystickStruct);
622 unsigned int featureCount = 0;
623 JOYSTICK_FEATURE* pFeatures = nullptr;
625 LogError(retVal = m_ifc.peripheral->toAddon->get_features(m_ifc.peripheral, &joystickStruct,
626 strControllerId.c_str(), &featureCount,
627 &pFeatures),
628 "GetFeatures()");
630 kodi::addon::Joystick::FreeStruct(joystickStruct);
632 if (retVal == PERIPHERAL_NO_ERROR)
634 for (unsigned int i = 0; i < featureCount; i++)
636 kodi::addon::JoystickFeature feature(pFeatures[i]);
637 if (feature.Type() != JOYSTICK_FEATURE_TYPE_UNKNOWN)
638 features[feature.Name()] = feature;
641 m_ifc.peripheral->toAddon->free_features(m_ifc.peripheral, featureCount, pFeatures);
643 return true;
646 return false;
649 bool CPeripheralAddon::MapFeature(const CPeripheral* device,
650 const std::string& strControllerId,
651 const kodi::addon::JoystickFeature& feature)
653 if (!m_bProvidesButtonMaps)
654 return false;
656 std::shared_lock<CSharedSection> lock(m_dllSection);
658 if (!m_ifc.peripheral->toAddon->map_features)
659 return false;
661 PERIPHERAL_ERROR retVal;
663 kodi::addon::Joystick joystickInfo;
664 GetJoystickInfo(device, joystickInfo);
666 JOYSTICK_INFO joystickStruct;
667 joystickInfo.ToStruct(joystickStruct);
669 JOYSTICK_FEATURE addonFeature;
670 feature.ToStruct(addonFeature);
672 LogError(retVal = m_ifc.peripheral->toAddon->map_features(
673 m_ifc.peripheral, &joystickStruct, strControllerId.c_str(), 1, &addonFeature),
674 "MapFeatures()");
676 kodi::addon::Joystick::FreeStruct(joystickStruct);
677 kodi::addon::JoystickFeature::FreeStruct(addonFeature);
679 return retVal == PERIPHERAL_NO_ERROR;
682 bool CPeripheralAddon::GetIgnoredPrimitives(const CPeripheral* device, PrimitiveVector& primitives)
684 if (!m_bProvidesButtonMaps)
685 return false;
687 std::shared_lock<CSharedSection> lock(m_dllSection);
689 if (!m_ifc.peripheral->toAddon->get_ignored_primitives)
690 return false;
692 PERIPHERAL_ERROR retVal;
694 kodi::addon::Joystick joystickInfo;
695 GetJoystickInfo(device, joystickInfo);
697 JOYSTICK_INFO joystickStruct;
698 joystickInfo.ToStruct(joystickStruct);
700 unsigned int primitiveCount = 0;
701 JOYSTICK_DRIVER_PRIMITIVE* pPrimitives = nullptr;
703 LogError(retVal = m_ifc.peripheral->toAddon->get_ignored_primitives(
704 m_ifc.peripheral, &joystickStruct, &primitiveCount, &pPrimitives),
705 "GetIgnoredPrimitives()");
707 kodi::addon::Joystick::FreeStruct(joystickStruct);
709 if (retVal == PERIPHERAL_NO_ERROR)
711 for (unsigned int i = 0; i < primitiveCount; i++)
712 primitives.emplace_back(pPrimitives[i]);
714 m_ifc.peripheral->toAddon->free_primitives(m_ifc.peripheral, primitiveCount, pPrimitives);
716 return true;
719 return false;
722 bool CPeripheralAddon::SetIgnoredPrimitives(const CPeripheral* device,
723 const PrimitiveVector& primitives)
725 if (!m_bProvidesButtonMaps)
726 return false;
728 std::shared_lock<CSharedSection> lock(m_dllSection);
730 if (!m_ifc.peripheral->toAddon->set_ignored_primitives)
731 return false;
733 PERIPHERAL_ERROR retVal;
735 kodi::addon::Joystick joystickInfo;
736 GetJoystickInfo(device, joystickInfo);
738 JOYSTICK_INFO joystickStruct;
739 joystickInfo.ToStruct(joystickStruct);
741 JOYSTICK_DRIVER_PRIMITIVE* addonPrimitives = nullptr;
742 kodi::addon::DriverPrimitives::ToStructs(primitives, &addonPrimitives);
743 const unsigned int primitiveCount = static_cast<unsigned int>(primitives.size());
745 LogError(retVal = m_ifc.peripheral->toAddon->set_ignored_primitives(
746 m_ifc.peripheral, &joystickStruct, primitiveCount, addonPrimitives),
747 "SetIgnoredPrimitives()");
749 kodi::addon::Joystick::FreeStruct(joystickStruct);
750 kodi::addon::DriverPrimitives::FreeStructs(primitiveCount, addonPrimitives);
752 return retVal == PERIPHERAL_NO_ERROR;
755 void CPeripheralAddon::SaveButtonMap(const CPeripheral* device)
757 if (!m_bProvidesButtonMaps)
758 return;
760 std::shared_lock<CSharedSection> lock(m_dllSection);
762 if (!m_ifc.peripheral->toAddon->save_button_map)
763 return;
765 kodi::addon::Joystick joystickInfo;
766 GetJoystickInfo(device, joystickInfo);
768 JOYSTICK_INFO joystickStruct;
769 joystickInfo.ToStruct(joystickStruct);
771 m_ifc.peripheral->toAddon->save_button_map(m_ifc.peripheral, &joystickStruct);
773 kodi::addon::Joystick::FreeStruct(joystickStruct);
775 // Notify observing button maps
776 RefreshButtonMaps(device->DeviceName());
779 void CPeripheralAddon::RevertButtonMap(const CPeripheral* device)
781 if (!m_bProvidesButtonMaps)
782 return;
784 std::shared_lock<CSharedSection> lock(m_dllSection);
786 if (!m_ifc.peripheral->toAddon->revert_button_map)
787 return;
789 kodi::addon::Joystick joystickInfo;
790 GetJoystickInfo(device, joystickInfo);
792 JOYSTICK_INFO joystickStruct;
793 joystickInfo.ToStruct(joystickStruct);
795 m_ifc.peripheral->toAddon->revert_button_map(m_ifc.peripheral, &joystickStruct);
797 kodi::addon::Joystick::FreeStruct(joystickStruct);
800 void CPeripheralAddon::ResetButtonMap(const CPeripheral* device, const std::string& strControllerId)
802 if (!m_bProvidesButtonMaps)
803 return;
805 kodi::addon::Joystick joystickInfo;
806 GetJoystickInfo(device, joystickInfo);
808 JOYSTICK_INFO joystickStruct;
809 joystickInfo.ToStruct(joystickStruct);
811 m_ifc.peripheral->toAddon->reset_button_map(m_ifc.peripheral, &joystickStruct,
812 strControllerId.c_str());
814 kodi::addon::Joystick::FreeStruct(joystickStruct);
816 // Notify observing button maps
817 RefreshButtonMaps(device->DeviceName());
820 void CPeripheralAddon::PowerOffJoystick(unsigned int index)
822 if (!HasFeature(FEATURE_JOYSTICK))
823 return;
825 if (!SupportsFeature(FEATURE_POWER_OFF))
826 return;
828 std::shared_lock<CSharedSection> lock(m_dllSection);
830 if (!m_ifc.peripheral->toAddon->power_off_joystick)
831 return;
833 m_ifc.peripheral->toAddon->power_off_joystick(m_ifc.peripheral, index);
836 void CPeripheralAddon::RegisterButtonMap(CPeripheral* device, IButtonMap* buttonMap)
838 std::unique_lock<CCriticalSection> lock(m_buttonMapMutex);
840 UnregisterButtonMap(buttonMap);
841 m_buttonMaps.emplace_back(device, buttonMap);
844 void CPeripheralAddon::UnregisterButtonMap(IButtonMap* buttonMap)
846 std::unique_lock<CCriticalSection> lock(m_buttonMapMutex);
848 for (auto it = m_buttonMaps.begin(); it != m_buttonMaps.end(); ++it)
850 if (it->second == buttonMap)
852 m_buttonMaps.erase(it);
853 break;
858 void CPeripheralAddon::UnregisterButtonMap(CPeripheral* device)
860 std::unique_lock<CCriticalSection> lock(m_buttonMapMutex);
862 m_buttonMaps.erase(
863 std::remove_if(m_buttonMaps.begin(), m_buttonMaps.end(),
864 [device](const std::pair<CPeripheral*, JOYSTICK::IButtonMap*>& buttonMap)
865 { return buttonMap.first == device; }),
866 m_buttonMaps.end());
869 void CPeripheralAddon::RefreshButtonMaps(const std::string& strDeviceName /* = "" */)
871 std::unique_lock<CCriticalSection> lock(m_buttonMapMutex);
873 for (auto it = m_buttonMaps.begin(); it != m_buttonMaps.end(); ++it)
875 if (strDeviceName.empty() || strDeviceName == it->first->DeviceName())
876 it->second->Load();
880 bool CPeripheralAddon::ProvidesJoysticks(const ADDON::AddonInfoPtr& addonInfo)
882 return addonInfo->Type(ADDON::AddonType::PERIPHERALDLL)
883 ->GetValue("@provides_joysticks")
884 .asBoolean();
887 bool CPeripheralAddon::ProvidesButtonMaps(const ADDON::AddonInfoPtr& addonInfo)
889 return addonInfo->Type(ADDON::AddonType::PERIPHERALDLL)
890 ->GetValue("@provides_buttonmaps")
891 .asBoolean();
894 void CPeripheralAddon::TriggerDeviceScan()
896 m_manager.TriggerDeviceScan(PERIPHERAL_BUS_ADDON);
899 unsigned int CPeripheralAddon::FeatureCount(const std::string& controllerId,
900 JOYSTICK_FEATURE_TYPE type) const
902 using namespace GAME;
904 unsigned int count = 0;
906 CControllerManager& controllerProfiles = m_manager.GetControllerProfiles();
907 ControllerPtr controller = controllerProfiles.GetController(controllerId);
908 if (controller)
909 count = controller->FeatureCount(CPeripheralAddonTranslator::TranslateFeatureType(type));
911 return count;
914 JOYSTICK_FEATURE_TYPE CPeripheralAddon::FeatureType(const std::string& controllerId,
915 const std::string& featureName) const
917 using namespace GAME;
919 JOYSTICK_FEATURE_TYPE type = JOYSTICK_FEATURE_TYPE_UNKNOWN;
921 CControllerManager& controllerProfiles = m_manager.GetControllerProfiles();
922 ControllerPtr controller = controllerProfiles.GetController(controllerId);
923 if (controller)
924 type = CPeripheralAddonTranslator::TranslateFeatureType(controller->FeatureType(featureName));
926 return type;
929 void CPeripheralAddon::GetPeripheralInfo(const CPeripheral* device,
930 kodi::addon::Peripheral& peripheralInfo)
932 peripheralInfo.SetType(CPeripheralAddonTranslator::TranslateType(device->Type()));
933 peripheralInfo.SetName(device->DeviceName());
934 peripheralInfo.SetVendorID(device->VendorId());
935 peripheralInfo.SetProductID(device->ProductId());
938 void CPeripheralAddon::GetJoystickInfo(const CPeripheral* device,
939 kodi::addon::Joystick& joystickInfo)
941 GetPeripheralInfo(device, joystickInfo);
943 if (device->Type() == PERIPHERAL_JOYSTICK)
945 const CPeripheralJoystick* joystick = static_cast<const CPeripheralJoystick*>(device);
946 joystickInfo.SetProvider(joystick->Provider());
947 joystickInfo.SetButtonCount(joystick->ButtonCount());
948 joystickInfo.SetHatCount(joystick->HatCount());
949 joystickInfo.SetAxisCount(joystick->AxisCount());
950 joystickInfo.SetMotorCount(joystick->MotorCount());
951 joystickInfo.SetSupportsPowerOff(joystick->SupportsPowerOff());
953 else if (device->Type() == PERIPHERAL_KEYBOARD || device->Type() == PERIPHERAL_MOUSE)
955 joystickInfo.SetName(GetDeviceName(device->Type())); // Override name with non-localized version
956 joystickInfo.SetProvider(GetProvider(device->Type()));
960 void CPeripheralAddon::SetJoystickInfo(CPeripheralJoystick& joystick,
961 const kodi::addon::Joystick& joystickInfo)
963 joystick.SetProvider(joystickInfo.Provider());
964 joystick.SetRequestedPort(joystickInfo.RequestedPort());
965 joystick.SetButtonCount(joystickInfo.ButtonCount());
966 joystick.SetHatCount(joystickInfo.HatCount());
967 joystick.SetAxisCount(joystickInfo.AxisCount());
968 joystick.SetMotorCount(joystickInfo.MotorCount());
969 joystick.SetSupportsPowerOff(joystickInfo.SupportsPowerOff());
972 bool CPeripheralAddon::LogError(const PERIPHERAL_ERROR error, const char* strMethod) const
974 if (error != PERIPHERAL_NO_ERROR)
976 CLog::Log(LOGERROR, "PERIPHERAL - {} - addon '{}' returned an error: {}", strMethod, Name(),
977 CPeripheralAddonTranslator::TranslateError(error));
978 return false;
980 return true;
983 std::string CPeripheralAddon::GetDeviceName(PeripheralType type)
985 switch (type)
987 case PERIPHERAL_KEYBOARD:
988 return KEYBOARD_BUTTON_MAP_NAME;
989 case PERIPHERAL_MOUSE:
990 return MOUSE_BUTTON_MAP_NAME;
991 default:
992 break;
995 return "";
998 std::string CPeripheralAddon::GetProvider(PeripheralType type)
1000 switch (type)
1002 case PERIPHERAL_KEYBOARD:
1003 return KEYBOARD_PROVIDER;
1004 case PERIPHERAL_MOUSE:
1005 return MOUSE_PROVIDER;
1006 default:
1007 break;
1010 return "";
1013 void CPeripheralAddon::cb_trigger_scan(void* kodiInstance)
1015 if (kodiInstance == nullptr)
1016 return;
1018 static_cast<CPeripheralAddon*>(kodiInstance)->TriggerDeviceScan();
1021 void CPeripheralAddon::cb_refresh_button_maps(void* kodiInstance,
1022 const char* deviceName,
1023 const char* controllerId)
1025 if (!kodiInstance)
1026 return;
1028 static_cast<CPeripheralAddon*>(kodiInstance)->RefreshButtonMaps(deviceName ? deviceName : "");
1031 unsigned int CPeripheralAddon::cb_feature_count(void* kodiInstance,
1032 const char* controllerId,
1033 JOYSTICK_FEATURE_TYPE type)
1035 if (kodiInstance == nullptr || controllerId == nullptr)
1036 return 0;
1038 return static_cast<CPeripheralAddon*>(kodiInstance)->FeatureCount(controllerId, type);
1041 JOYSTICK_FEATURE_TYPE CPeripheralAddon::cb_feature_type(void* kodiInstance,
1042 const char* controllerId,
1043 const char* featureName)
1045 if (kodiInstance == nullptr || controllerId == nullptr || featureName == nullptr)
1046 return JOYSTICK_FEATURE_TYPE_UNKNOWN;
1048 return static_cast<CPeripheralAddon*>(kodiInstance)->FeatureType(controllerId, featureName);