[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / peripherals / Peripherals.cpp
blob1276cf52d676322e459a4252a0e9220b32f2c7db
1 /*
2 * Copyright (C) 2005-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 "Peripherals.h"
11 #include "CompileInfo.h"
12 #include "addons/AddonButtonMap.h"
13 #include "addons/AddonManager.h"
14 #include "addons/addoninfo/AddonInfo.h"
15 #include "addons/addoninfo/AddonType.h"
16 #include "addons/gui/GUIDialogAddonSettings.h"
17 #include "addons/gui/GUIWindowAddonBrowser.h"
18 #include "bus/PeripheralBus.h"
19 #include "bus/PeripheralBusUSB.h"
21 #include <mutex>
22 #include <utility>
23 #if defined(TARGET_ANDROID)
24 #include "platform/android/peripherals/PeripheralBusAndroid.h"
25 #elif defined(TARGET_DARWIN)
26 #include "platform/darwin/peripherals/PeripheralBusGCController.h"
27 #endif
28 #include "FileItem.h"
29 #include "GUIUserMessages.h"
30 #include "ServiceBroker.h"
31 #include "bus/virtual/PeripheralBusAddon.h"
32 #include "bus/virtual/PeripheralBusApplication.h"
33 #include "devices/PeripheralBluetooth.h"
34 #include "devices/PeripheralCecAdapter.h"
35 #include "devices/PeripheralDisk.h"
36 #include "devices/PeripheralHID.h"
37 #include "devices/PeripheralImon.h"
38 #include "devices/PeripheralJoystick.h"
39 #include "devices/PeripheralKeyboard.h"
40 #include "devices/PeripheralMouse.h"
41 #include "devices/PeripheralNIC.h"
42 #include "devices/PeripheralNyxboard.h"
43 #include "devices/PeripheralTuner.h"
44 #include "filesystem/Directory.h"
45 #include "guilib/GUIComponent.h"
46 #include "guilib/GUIWindowManager.h"
47 #include "guilib/WindowIDs.h"
48 #include "input/actions/Action.h"
49 #include "input/actions/ActionIDs.h"
50 #include "input/joysticks/interfaces/IButtonMapper.h"
51 #include "input/keyboard/Key.h"
52 #include "interfaces/AnnouncementManager.h"
53 #include "messaging/ApplicationMessenger.h"
54 #include "messaging/ThreadMessage.h"
55 #include "peripherals/dialogs/GUIDialogPeripherals.h"
56 #include "peripherals/events/EventScanner.h"
57 #include "settings/SettingAddon.h"
58 #include "settings/Settings.h"
59 #include "settings/SettingsComponent.h"
60 #include "settings/lib/Setting.h"
61 #include "utils/StringUtils.h"
62 #include "utils/XBMCTinyXML2.h"
63 #include "utils/XMLUtils.h"
64 #include "utils/log.h"
66 #if defined(HAVE_LIBCEC)
67 #include "bus/virtual/PeripheralBusCEC.h"
68 #else
69 #include "dialogs/GUIDialogKaiToast.h"
70 #include "guilib/LocalizeStrings.h"
71 #endif
73 using namespace KODI;
74 using namespace JOYSTICK;
75 using namespace PERIPHERALS;
76 using namespace XFILE;
78 CPeripherals::CPeripherals(CInputManager& inputManager,
79 GAME::CControllerManager& controllerProfiles)
80 : m_inputManager(inputManager),
81 m_controllerProfiles(controllerProfiles),
82 m_eventScanner(new CEventScanner(*this))
84 // Register settings
85 std::set<std::string> settingSet;
86 settingSet.insert(CSettings::SETTING_INPUT_PERIPHERALS);
87 settingSet.insert(CSettings::SETTING_INPUT_PERIPHERALLIBRARIES);
88 settingSet.insert(CSettings::SETTING_INPUT_CONTROLLERCONFIG);
89 settingSet.insert(CSettings::SETTING_INPUT_TESTRUMBLE);
90 settingSet.insert(CSettings::SETTING_LOCALE_LANGUAGE);
91 CServiceBroker::GetSettingsComponent()->GetSettings()->RegisterCallback(this, settingSet);
94 CPeripherals::~CPeripherals()
96 // Unregister settings
97 CServiceBroker::GetSettingsComponent()->GetSettings()->UnregisterCallback(this);
99 Clear();
102 void CPeripherals::Initialise()
104 Clear();
106 CDirectory::Create("special://profile/peripheral_data");
108 /* load mappings from peripherals.xml */
109 LoadMappings();
111 std::vector<PeripheralBusPtr> busses;
113 #if defined(HAVE_PERIPHERAL_BUS_USB)
114 busses.push_back(std::make_shared<CPeripheralBusUSB>(*this));
115 #endif
116 #if defined(HAVE_LIBCEC)
117 busses.push_back(std::make_shared<CPeripheralBusCEC>(*this));
118 #endif
119 busses.push_back(std::make_shared<CPeripheralBusAddon>(*this));
120 #if defined(TARGET_ANDROID)
121 busses.push_back(std::make_shared<CPeripheralBusAndroid>(*this));
122 #elif defined(TARGET_DARWIN)
123 busses.push_back(std::make_shared<CPeripheralBusGCController>(*this));
124 #endif
125 busses.push_back(std::make_shared<CPeripheralBusApplication>(*this));
128 std::unique_lock<CCriticalSection> bussesLock(m_critSectionBusses);
129 m_busses = busses;
132 /* initialise all known busses and run an initial scan for devices */
133 for (auto& bus : busses)
134 bus->Initialise();
136 m_eventScanner->Start();
138 CServiceBroker::GetAppMessenger()->RegisterReceiver(this);
139 CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this);
142 void CPeripherals::Clear()
144 CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this);
146 m_eventScanner->Stop();
148 // avoid deadlocks by copying all busses into a temporary variable and destroying them from there
149 std::vector<PeripheralBusPtr> busses;
151 std::unique_lock<CCriticalSection> bussesLock(m_critSectionBusses);
152 /* delete busses and devices */
153 busses = m_busses;
154 m_busses.clear();
157 for (const auto& bus : busses)
158 bus->Clear();
159 busses.clear();
162 std::unique_lock<CCriticalSection> mappingsLock(m_critSectionMappings);
163 /* delete mappings */
164 for (auto& mapping : m_mappings)
165 mapping.m_settings.clear();
166 m_mappings.clear();
169 #if !defined(HAVE_LIBCEC)
170 m_bMissingLibCecWarningDisplayed = false;
171 #endif
174 void CPeripherals::TriggerDeviceScan(const PeripheralBusType type /* = PERIPHERAL_BUS_UNKNOWN */)
176 std::vector<PeripheralBusPtr> busses;
178 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
179 busses = m_busses;
182 for (auto& bus : busses)
184 bool bScan = false;
186 if (type == PERIPHERAL_BUS_UNKNOWN)
187 bScan = true;
188 else if (bus->Type() == PERIPHERAL_BUS_ADDON)
189 bScan = true;
190 else if (bus->Type() == type)
191 bScan = true;
193 if (bScan)
194 bus->TriggerDeviceScan();
198 PeripheralBusPtr CPeripherals::GetBusByType(const PeripheralBusType type) const
200 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
202 const auto& bus =
203 std::find_if(m_busses.cbegin(), m_busses.cend(),
204 [type](const PeripheralBusPtr& bus) { return bus->Type() == type; });
205 if (bus != m_busses.cend())
206 return *bus;
208 return nullptr;
211 PeripheralPtr CPeripherals::GetPeripheralAtLocation(
212 const std::string& strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
214 PeripheralPtr result;
216 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
217 for (const auto& bus : m_busses)
219 /* check whether the bus matches if a bus type other than unknown was passed */
220 if (busType != PERIPHERAL_BUS_UNKNOWN && bus->Type() != busType)
221 continue;
223 /* return the first device that matches */
224 PeripheralPtr peripheral = bus->GetPeripheral(strLocation);
225 if (peripheral)
227 result = peripheral;
228 break;
232 return result;
235 bool CPeripherals::HasPeripheralAtLocation(
236 const std::string& strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
238 return (GetPeripheralAtLocation(strLocation, busType) != nullptr);
241 PeripheralBusPtr CPeripherals::GetBusWithDevice(const std::string& strLocation) const
243 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
245 const auto& bus = std::find_if(m_busses.cbegin(), m_busses.cend(),
246 [&strLocation](const PeripheralBusPtr& bus)
247 { return bus->HasPeripheral(strLocation); });
248 if (bus != m_busses.cend())
249 return *bus;
251 return nullptr;
254 bool CPeripherals::SupportsFeature(PeripheralFeature feature) const
256 bool bSupportsFeature = false;
258 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
259 for (const auto& bus : m_busses)
260 bSupportsFeature |= bus->SupportsFeature(feature);
262 return bSupportsFeature;
265 int CPeripherals::GetPeripheralsWithFeature(
266 PeripheralVector& results,
267 const PeripheralFeature feature,
268 PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
270 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
271 int iReturn(0);
272 for (const auto& bus : m_busses)
274 /* check whether the bus matches if a bus type other than unknown was passed */
275 if (busType != PERIPHERAL_BUS_UNKNOWN && bus->Type() != busType)
276 continue;
278 iReturn += bus->GetPeripheralsWithFeature(results, feature);
281 return iReturn;
284 size_t CPeripherals::GetNumberOfPeripherals() const
286 size_t iReturn(0);
287 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
288 for (const auto& bus : m_busses)
289 iReturn += bus->GetNumberOfPeripherals();
291 return iReturn;
294 bool CPeripherals::HasPeripheralWithFeature(
295 const PeripheralFeature feature, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
297 PeripheralVector dummy;
298 return (GetPeripheralsWithFeature(dummy, feature, busType) > 0);
301 void CPeripherals::CreatePeripheral(CPeripheralBus& bus, const PeripheralScanResult& result)
303 PeripheralPtr peripheral;
304 PeripheralScanResult mappedResult = result;
305 if (mappedResult.m_busType == PERIPHERAL_BUS_UNKNOWN)
306 mappedResult.m_busType = bus.Type();
308 /* check whether there's something mapped in peripherals.xml */
309 GetMappingForDevice(bus, mappedResult);
311 switch (mappedResult.m_mappedType)
313 case PERIPHERAL_HID:
314 peripheral = PeripheralPtr(new CPeripheralHID(*this, mappedResult, &bus));
315 break;
317 case PERIPHERAL_NIC:
318 peripheral = PeripheralPtr(new CPeripheralNIC(*this, mappedResult, &bus));
319 break;
321 case PERIPHERAL_DISK:
322 peripheral = PeripheralPtr(new CPeripheralDisk(*this, mappedResult, &bus));
323 break;
325 case PERIPHERAL_NYXBOARD:
326 peripheral = PeripheralPtr(new CPeripheralNyxboard(*this, mappedResult, &bus));
327 break;
329 case PERIPHERAL_TUNER:
330 peripheral = PeripheralPtr(new CPeripheralTuner(*this, mappedResult, &bus));
331 break;
333 case PERIPHERAL_BLUETOOTH:
334 peripheral = PeripheralPtr(new CPeripheralBluetooth(*this, mappedResult, &bus));
335 break;
337 case PERIPHERAL_CEC:
338 #if defined(HAVE_LIBCEC)
339 if (bus.Type() == PERIPHERAL_BUS_CEC)
340 peripheral = PeripheralPtr(new CPeripheralCecAdapter(*this, mappedResult, &bus));
341 #else
342 if (!m_bMissingLibCecWarningDisplayed)
344 m_bMissingLibCecWarningDisplayed = true;
345 CLog::Log(
346 LOGWARNING,
347 "{} - libCEC support has not been compiled in, so the CEC adapter cannot be used.",
348 __FUNCTION__);
349 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning,
350 g_localizeStrings.Get(36000),
351 g_localizeStrings.Get(36017));
353 #endif
354 break;
356 case PERIPHERAL_IMON:
357 peripheral = PeripheralPtr(new CPeripheralImon(*this, mappedResult, &bus));
358 break;
360 case PERIPHERAL_JOYSTICK:
361 peripheral = PeripheralPtr(new CPeripheralJoystick(*this, mappedResult, &bus));
362 break;
364 case PERIPHERAL_KEYBOARD:
365 peripheral = PeripheralPtr(new CPeripheralKeyboard(*this, mappedResult, &bus));
366 break;
368 case PERIPHERAL_MOUSE:
369 peripheral = PeripheralPtr(new CPeripheralMouse(*this, mappedResult, &bus));
370 break;
372 default:
373 break;
376 if (peripheral)
378 /* try to initialise the new peripheral
379 * Initialise() will make sure that each device is only initialised once */
380 if (peripheral->Initialise())
381 bus.Register(peripheral);
382 else
384 CLog::Log(LOGDEBUG, "{} - failed to initialise peripheral on '{}'", __FUNCTION__,
385 mappedResult.m_strLocation);
390 void CPeripherals::OnDeviceAdded(const CPeripheralBus& bus, const CPeripheral& peripheral)
392 OnDeviceChanged();
394 //! @todo Improve device notifications in v18
395 #if 0
396 bool bNotify = true;
398 // don't show a notification for devices detected during the initial scan
399 if (!bus.IsInitialised())
400 bNotify = false;
402 if (bNotify)
403 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35005), peripheral.DeviceName());
404 #endif
407 void CPeripherals::OnDeviceDeleted(const CPeripheralBus& bus, const CPeripheral& peripheral)
409 OnDeviceChanged();
411 //! @todo Improve device notifications in v18
412 #if 0
413 bool bNotify = true;
415 if (bNotify)
416 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35006), peripheral.DeviceName());
417 #endif
420 void CPeripherals::OnDeviceChanged()
422 // refresh settings (peripherals manager could be enabled/disabled now)
423 CGUIMessage msgSettings(GUI_MSG_UPDATE, WINDOW_SETTINGS_SYSTEM, 0);
424 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msgSettings,
425 WINDOW_SETTINGS_SYSTEM);
427 SetChanged();
430 bool CPeripherals::GetMappingForDevice(const CPeripheralBus& bus,
431 PeripheralScanResult& result) const
433 std::unique_lock<CCriticalSection> lock(m_critSectionMappings);
435 /* check all mappings in the order in which they are defined in peripherals.xml */
436 for (const auto& mapping : m_mappings)
438 bool bProductMatch = false;
439 if (mapping.m_PeripheralID.empty())
440 bProductMatch = true;
441 else
443 for (const auto& peripheralID : mapping.m_PeripheralID)
444 if (peripheralID.m_iVendorId == result.m_iVendorId &&
445 peripheralID.m_iProductId == result.m_iProductId)
446 bProductMatch = true;
449 bool bBusMatch =
450 (mapping.m_busType == PERIPHERAL_BUS_UNKNOWN || mapping.m_busType == bus.Type());
451 bool bClassMatch = (mapping.m_class == PERIPHERAL_UNKNOWN || mapping.m_class == result.m_type);
453 if (bProductMatch && bBusMatch && bClassMatch)
455 std::string strVendorId, strProductId;
456 PeripheralTypeTranslator::FormatHexString(result.m_iVendorId, strVendorId);
457 PeripheralTypeTranslator::FormatHexString(result.m_iProductId, strProductId);
458 CLog::Log(LOGDEBUG, "{} - device ({}:{}) mapped to {} (type = {})", __FUNCTION__, strVendorId,
459 strProductId, mapping.m_strDeviceName,
460 PeripheralTypeTranslator::TypeToString(mapping.m_mappedTo));
461 result.m_mappedType = mapping.m_mappedTo;
462 if (result.m_strDeviceName.empty() && !mapping.m_strDeviceName.empty())
463 result.m_strDeviceName = mapping.m_strDeviceName;
464 return true;
468 return false;
471 void CPeripherals::GetSettingsFromMapping(CPeripheral& peripheral) const
473 std::unique_lock<CCriticalSection> lock(m_critSectionMappings);
475 /* check all mappings in the order in which they are defined in peripherals.xml */
476 for (const auto& mapping : m_mappings)
478 bool bProductMatch = false;
479 if (mapping.m_PeripheralID.empty())
480 bProductMatch = true;
481 else
483 for (const auto& peripheralID : mapping.m_PeripheralID)
484 if (peripheralID.m_iVendorId == peripheral.VendorId() &&
485 peripheralID.m_iProductId == peripheral.ProductId())
486 bProductMatch = true;
489 bool bBusMatch = (mapping.m_busType == PERIPHERAL_BUS_UNKNOWN ||
490 mapping.m_busType == peripheral.GetBusType());
491 bool bClassMatch =
492 (mapping.m_class == PERIPHERAL_UNKNOWN || mapping.m_class == peripheral.Type());
494 if (bBusMatch && bProductMatch && bClassMatch)
496 for (auto itr = mapping.m_settings.begin(); itr != mapping.m_settings.end(); ++itr)
497 peripheral.AddSetting((*itr).first, (*itr).second.m_setting, (*itr).second.m_order);
502 #define SS(x) ((x) ? x : "")
503 bool CPeripherals::LoadMappings()
505 std::unique_lock<CCriticalSection> lock(m_critSectionMappings);
507 CXBMCTinyXML2 xmlDoc;
508 if (!xmlDoc.LoadFile("special://xbmc/system/peripherals.xml"))
510 CLog::LogF(LOGWARNING, "peripherals.xml does not exist");
511 return true;
514 auto* pRootElement = xmlDoc.RootElement();
515 if (pRootElement == nullptr ||
516 StringUtils::CompareNoCase(pRootElement->Value(), "peripherals") != 0)
518 CLog::LogF(LOGERROR, "peripherals.xml does not contain <peripherals>");
519 return false;
522 for (auto* currentNode = pRootElement->FirstChildElement("peripheral"); currentNode != nullptr;
523 currentNode = currentNode->NextSiblingElement("peripheral"))
525 PeripheralID id;
526 PeripheralDeviceMapping mapping;
528 mapping.m_strDeviceName = XMLUtils::GetAttribute(currentNode, "name");
530 // If there is no vendor_product attribute ignore this entry
531 if (currentNode->Attribute("vendor_product"))
533 // The vendor_product attribute is a list of comma separated vendor:product pairs
534 std::vector<std::string> vpArray =
535 StringUtils::Split(currentNode->Attribute("vendor_product"), ",");
536 for (const auto& i : vpArray)
538 std::vector<std::string> idArray = StringUtils::Split(i, ":");
539 if (idArray.size() != 2)
541 CLog::LogF(LOGERROR, "ignoring node \"{}\" with invalid vendor_product attribute",
542 mapping.m_strDeviceName);
543 continue;
546 id.m_iVendorId = PeripheralTypeTranslator::HexStringToInt(idArray[0].c_str());
547 id.m_iProductId = PeripheralTypeTranslator::HexStringToInt(idArray[1].c_str());
548 mapping.m_PeripheralID.push_back(id);
552 mapping.m_busType =
553 PeripheralTypeTranslator::GetBusTypeFromString(XMLUtils::GetAttribute(currentNode, "bus"));
554 mapping.m_class =
555 PeripheralTypeTranslator::GetTypeFromString(XMLUtils::GetAttribute(currentNode, "class"));
556 mapping.m_mappedTo =
557 PeripheralTypeTranslator::GetTypeFromString(XMLUtils::GetAttribute(currentNode, "mapTo"));
558 GetSettingsFromMappingsFile(currentNode, mapping.m_settings);
560 m_mappings.push_back(mapping);
561 CLog::LogF(LOGDEBUG, "loaded node \"{}\"", mapping.m_strDeviceName);
564 return true;
567 void CPeripherals::GetSettingsFromMappingsFile(
568 tinyxml2::XMLElement* xmlNode, std::map<std::string, PeripheralDeviceSetting>& settings)
570 auto* currentNode = xmlNode->FirstChildElement("setting");
571 int iMaxOrder = 0;
573 while (currentNode != nullptr)
575 SettingPtr setting;
576 std::string strKey = XMLUtils::GetAttribute(currentNode, "key");
577 if (strKey.empty())
578 continue;
580 std::string strSettingsType = XMLUtils::GetAttribute(currentNode, "type");
581 int iLabelId = currentNode->Attribute("label") ? atoi(currentNode->Attribute("label")) : -1;
582 const std::string config = XMLUtils::GetAttribute(currentNode, "configurable");
583 bool bConfigurable = (config.empty() || (config != "no" && config != "false" && config != "0"));
584 if (strSettingsType == "bool")
586 const std::string value = XMLUtils::GetAttribute(currentNode, "value");
587 bool bValue = (value != "no" && value != "false" && value != "0");
588 setting = std::make_shared<CSettingBool>(strKey, iLabelId, bValue);
590 else if (strSettingsType == "int")
592 int iValue = currentNode->Attribute("value") ? atoi(currentNode->Attribute("value")) : 0;
593 int iMin = currentNode->Attribute("min") ? atoi(currentNode->Attribute("min")) : 0;
594 int iStep = currentNode->Attribute("step") ? atoi(currentNode->Attribute("step")) : 1;
595 int iMax = currentNode->Attribute("max") ? atoi(currentNode->Attribute("max")) : 255;
596 setting = std::make_shared<CSettingInt>(strKey, iLabelId, iValue, iMin, iStep, iMax);
598 else if (strSettingsType == "float")
600 float fValue =
601 currentNode->Attribute("value") ? (float)atof(currentNode->Attribute("value")) : 0;
602 float fMin = currentNode->Attribute("min") ? (float)atof(currentNode->Attribute("min")) : 0;
603 float fStep =
604 currentNode->Attribute("step") ? (float)atof(currentNode->Attribute("step")) : 0;
605 float fMax = currentNode->Attribute("max") ? (float)atof(currentNode->Attribute("max")) : 0;
606 setting = std::make_shared<CSettingNumber>(strKey, iLabelId, fValue, fMin, fStep, fMax);
608 else if (StringUtils::EqualsNoCase(strSettingsType, "enum"))
610 std::string strEnums = XMLUtils::GetAttribute(currentNode, "lvalues");
611 if (!strEnums.empty())
613 TranslatableIntegerSettingOptions enums;
614 std::vector<std::string> valuesVec;
615 StringUtils::Tokenize(strEnums, valuesVec, "|");
616 for (unsigned int i = 0; i < valuesVec.size(); i++)
617 enums.emplace_back(atoi(valuesVec[i].c_str()), atoi(valuesVec[i].c_str()));
618 int iValue = currentNode->Attribute("value") ? atoi(currentNode->Attribute("value")) : 0;
619 setting = std::make_shared<CSettingInt>(strKey, iLabelId, iValue, enums);
622 else if (StringUtils::EqualsNoCase(strSettingsType, "addon"))
624 std::string addonFilter = XMLUtils::GetAttribute(currentNode, "addontype");
625 ADDON::AddonType addonType = ADDON::CAddonInfo::TranslateType(addonFilter);
626 std::string strValue = XMLUtils::GetAttribute(currentNode, "value");
627 setting = std::make_shared<CSettingAddon>(strKey, iLabelId, strValue);
628 static_cast<CSettingAddon&>(*setting).SetAddonType(addonType);
630 else
632 std::string strValue = XMLUtils::GetAttribute(currentNode, "value");
633 setting = std::make_shared<CSettingString>(strKey, iLabelId, strValue);
636 if (setting)
638 //! @todo add more types if needed
640 /* set the visibility */
641 setting->SetVisible(bConfigurable);
643 /* set the order */
644 int iOrder = 0;
645 currentNode->Attribute("order", std::to_string(iOrder).c_str());
646 /* if the order attribute is invalid or 0, then the setting will be added at the end */
647 if (iOrder < 0)
648 iOrder = 0;
649 if (iOrder > iMaxOrder)
650 iMaxOrder = iOrder;
652 /* and add this new setting */
653 PeripheralDeviceSetting deviceSetting = {setting, iOrder};
654 settings[strKey] = deviceSetting;
657 currentNode = currentNode->NextSiblingElement("setting");
660 /* add the settings without an order attribute or an invalid order attribute set at the end */
661 for (auto& it : settings)
663 if (it.second.m_order == 0)
664 it.second.m_order = ++iMaxOrder;
668 void CPeripherals::GetDirectory(const std::string& strPath, CFileItemList& items) const
670 if (!StringUtils::StartsWithNoCase(strPath, "peripherals://"))
671 return;
673 std::string strPathCut = strPath.substr(14);
674 std::string strBus = strPathCut.substr(0, strPathCut.find('/'));
676 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
677 for (const auto& bus : m_busses)
679 if (StringUtils::EqualsNoCase(strBus, "all") ||
680 StringUtils::EqualsNoCase(strBus, PeripheralTypeTranslator::BusTypeToString(bus->Type())))
681 bus->GetDirectory(strPath, items);
685 PeripheralPtr CPeripherals::GetByPath(const std::string& strPath) const
687 PeripheralPtr result;
689 if (!StringUtils::StartsWithNoCase(strPath, "peripherals://"))
690 return result;
692 std::string strPathCut = strPath.substr(14);
693 std::string strBus = strPathCut.substr(0, strPathCut.find('/'));
695 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
696 for (const auto& bus : m_busses)
698 if (StringUtils::EqualsNoCase(strBus, PeripheralTypeTranslator::BusTypeToString(bus->Type())))
700 result = bus->GetByPath(strPath);
701 break;
705 return result;
708 bool CPeripherals::OnAction(const CAction& action)
710 if (action.GetID() == ACTION_MUTE)
712 return ToggleMute();
715 if (SupportsCEC() && action.GetAmount() &&
716 (action.GetID() == ACTION_VOLUME_UP || action.GetID() == ACTION_VOLUME_DOWN))
718 PeripheralVector peripherals;
719 if (GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
721 for (auto& peripheral : peripherals)
723 std::shared_ptr<CPeripheralCecAdapter> cecDevice =
724 std::static_pointer_cast<CPeripheralCecAdapter>(peripheral);
725 if (cecDevice->HasAudioControl())
727 if (action.GetID() == ACTION_VOLUME_UP)
728 cecDevice->VolumeUp();
729 else
730 cecDevice->VolumeDown();
731 return true;
737 return false;
740 bool CPeripherals::IsMuted()
742 PeripheralVector peripherals;
743 if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
745 for (const auto& peripheral : peripherals)
747 std::shared_ptr<CPeripheralCecAdapter> cecDevice =
748 std::static_pointer_cast<CPeripheralCecAdapter>(peripheral);
749 if (cecDevice->IsMuted())
750 return true;
754 return false;
757 bool CPeripherals::ToggleMute()
759 PeripheralVector peripherals;
760 if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
762 for (auto& peripheral : peripherals)
764 std::shared_ptr<CPeripheralCecAdapter> cecDevice =
765 std::static_pointer_cast<CPeripheralCecAdapter>(peripheral);
766 if (cecDevice->HasAudioControl())
768 cecDevice->ToggleMute();
769 return true;
774 return false;
777 bool CPeripherals::ToggleDeviceState(CecStateChange mode /*= STATE_SWITCH_TOGGLE */)
779 bool ret(false);
780 PeripheralVector peripherals;
782 if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
784 for (auto& peripheral : peripherals)
786 std::shared_ptr<CPeripheralCecAdapter> cecDevice =
787 std::static_pointer_cast<CPeripheralCecAdapter>(peripheral);
788 ret |= cecDevice->ToggleDeviceState(mode);
792 return ret;
795 bool CPeripherals::GetNextKeypress(float frameTime, CKey& key)
797 PeripheralVector peripherals;
798 if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
800 for (auto& peripheral : peripherals)
802 std::shared_ptr<CPeripheralCecAdapter> cecDevice =
803 std::static_pointer_cast<CPeripheralCecAdapter>(peripheral);
804 if (cecDevice->GetButton())
806 CKey newKey(cecDevice->GetButton(), cecDevice->GetHoldTime());
807 cecDevice->ResetButton();
808 key = newKey;
809 return true;
814 return false;
817 EventPollHandlePtr CPeripherals::RegisterEventPoller()
819 return m_eventScanner->RegisterPollHandle();
822 EventLockHandlePtr CPeripherals::RegisterEventLock()
824 return m_eventScanner->RegisterLock();
827 void CPeripherals::OnUserNotification()
829 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
830 CSettings::SETTING_INPUT_RUMBLENOTIFY))
831 return;
833 PeripheralVector peripherals;
834 GetPeripheralsWithFeature(peripherals, FEATURE_RUMBLE);
836 for (auto& peripheral : peripherals)
837 peripheral->OnUserNotification();
840 void CPeripherals::TestFeature(PeripheralFeature feature)
842 PeripheralVector peripherals;
843 GetPeripheralsWithFeature(peripherals, feature);
845 for (auto& peripheral : peripherals)
847 if (peripheral->TestFeature(feature))
849 CLog::Log(LOGDEBUG, "PERIPHERALS: Device \"{}\" tested {} feature", peripheral->DeviceName(),
850 PeripheralTypeTranslator::FeatureToString(feature));
852 else
854 if (peripheral->HasFeature(feature))
855 CLog::Log(LOGDEBUG, "PERIPHERALS: Device \"{}\" failed to test {} feature",
856 peripheral->DeviceName(), PeripheralTypeTranslator::FeatureToString(feature));
857 else
858 CLog::Log(LOGDEBUG, "PERIPHERALS: Device \"{}\" doesn't support {} feature",
859 peripheral->DeviceName(), PeripheralTypeTranslator::FeatureToString(feature));
864 void CPeripherals::PowerOffDevices()
866 TestFeature(FEATURE_POWER_OFF);
869 void CPeripherals::ProcessEvents(void)
871 std::vector<PeripheralBusPtr> busses;
873 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
874 busses = m_busses;
877 for (PeripheralBusPtr& bus : busses)
878 bus->ProcessEvents();
881 void CPeripherals::EnableButtonMapping()
883 std::vector<PeripheralBusPtr> busses;
885 std::unique_lock<CCriticalSection> lock(m_critSectionBusses);
886 busses = m_busses;
889 for (PeripheralBusPtr& bus : busses)
890 bus->EnableButtonMapping();
893 PeripheralAddonPtr CPeripherals::GetAddonWithButtonMap(const CPeripheral* device)
895 PeripheralBusAddonPtr addonBus =
896 std::static_pointer_cast<CPeripheralBusAddon>(GetBusByType(PERIPHERAL_BUS_ADDON));
898 PeripheralAddonPtr addon;
900 PeripheralAddonPtr addonWithButtonMap;
901 if (addonBus && addonBus->GetAddonWithButtonMap(device, addonWithButtonMap))
902 addon = std::move(addonWithButtonMap);
904 return addon;
907 void CPeripherals::ResetButtonMaps(const std::string& controllerId)
909 PeripheralBusAddonPtr addonBus =
910 std::static_pointer_cast<CPeripheralBusAddon>(GetBusByType(PERIPHERAL_BUS_ADDON));
912 PeripheralVector peripherals;
913 GetPeripheralsWithFeature(peripherals, FEATURE_JOYSTICK);
915 for (auto& peripheral : peripherals)
917 PeripheralAddonPtr addon;
918 if (addonBus->GetAddonWithButtonMap(peripheral.get(), addon))
920 CAddonButtonMap buttonMap(peripheral.get(), addon, controllerId, *this);
921 buttonMap.Reset();
926 void CPeripherals::RegisterJoystickButtonMapper(IButtonMapper* mapper)
928 PeripheralVector peripherals;
929 GetPeripheralsWithFeature(peripherals, FEATURE_JOYSTICK);
930 GetPeripheralsWithFeature(peripherals, FEATURE_KEYBOARD);
931 GetPeripheralsWithFeature(peripherals, FEATURE_MOUSE);
933 for (auto& peripheral : peripherals)
934 peripheral->RegisterJoystickButtonMapper(mapper);
937 void CPeripherals::UnregisterJoystickButtonMapper(IButtonMapper* mapper)
939 mapper->ResetButtonMapCallbacks();
941 PeripheralVector peripherals;
942 GetPeripheralsWithFeature(peripherals, FEATURE_JOYSTICK);
943 GetPeripheralsWithFeature(peripherals, FEATURE_KEYBOARD);
944 GetPeripheralsWithFeature(peripherals, FEATURE_MOUSE);
946 for (auto& peripheral : peripherals)
947 peripheral->UnregisterJoystickButtonMapper(mapper);
950 void CPeripherals::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
952 if (setting == nullptr)
953 return;
955 const std::string& settingId = setting->GetId();
956 if (settingId == CSettings::SETTING_LOCALE_LANGUAGE)
958 // user set language, no longer use the TV's language
959 PeripheralVector cecDevices;
960 if (GetPeripheralsWithFeature(cecDevices, FEATURE_CEC) > 0)
962 for (auto& cecDevice : cecDevices)
963 cecDevice->SetSetting("use_tv_menu_language", false);
968 void CPeripherals::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
970 if (setting == nullptr)
971 return;
973 const std::string& settingId = setting->GetId();
974 if (settingId == CSettings::SETTING_INPUT_PERIPHERALS)
975 CGUIDialogPeripherals::Show(*this);
976 else if (settingId == CSettings::SETTING_INPUT_CONTROLLERCONFIG)
977 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_DIALOG_GAME_CONTROLLERS);
978 else if (settingId == CSettings::SETTING_INPUT_TESTRUMBLE)
979 TestFeature(FEATURE_RUMBLE);
980 else if (settingId == CSettings::SETTING_INPUT_PERIPHERALLIBRARIES)
982 std::string strAddonId;
983 if (CGUIWindowAddonBrowser::SelectAddonID(ADDON::AddonType::PERIPHERALDLL, strAddonId, false,
984 true, true, false, true) == 1 &&
985 !strAddonId.empty())
987 ADDON::AddonPtr addon;
988 if (CServiceBroker::GetAddonMgr().GetAddon(strAddonId, addon, ADDON::OnlyEnabled::CHOICE_YES))
989 CGUIDialogAddonSettings::ShowForAddon(addon);
994 void CPeripherals::OnApplicationMessage(MESSAGING::ThreadMessage* pMsg)
996 switch (pMsg->dwMessage)
998 case TMSG_CECTOGGLESTATE:
999 *static_cast<bool*>(pMsg->lpVoid) = ToggleDeviceState(STATE_SWITCH_TOGGLE);
1000 break;
1002 case TMSG_CECACTIVATESOURCE:
1003 ToggleDeviceState(STATE_ACTIVATE_SOURCE);
1004 break;
1006 case TMSG_CECSTANDBY:
1007 ToggleDeviceState(STATE_STANDBY);
1008 break;
1012 int CPeripherals::GetMessageMask()
1014 return TMSG_MASK_PERIPHERALS;
1017 void CPeripherals::Announce(ANNOUNCEMENT::AnnouncementFlag flag,
1018 const std::string& sender,
1019 const std::string& message,
1020 const CVariant& data)
1022 if (flag == ANNOUNCEMENT::Player &&
1023 sender == ANNOUNCEMENT::CAnnouncementManager::ANNOUNCEMENT_SENDER)
1025 if (message == "OnQuit")
1027 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
1028 CSettings::SETTING_INPUT_CONTROLLERPOWEROFF))
1029 PowerOffDevices();