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.
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"
23 #if defined(TARGET_ANDROID)
24 #include "platform/android/peripherals/PeripheralBusAndroid.h"
25 #elif defined(TARGET_DARWIN)
26 #include "platform/darwin/peripherals/PeripheralBusGCController.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"
69 #include "dialogs/GUIDialogKaiToast.h"
70 #include "guilib/LocalizeStrings.h"
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))
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);
102 void CPeripherals::Initialise()
106 CDirectory::Create("special://profile/peripheral_data");
108 /* load mappings from peripherals.xml */
111 std::vector
<PeripheralBusPtr
> busses
;
113 #if defined(HAVE_PERIPHERAL_BUS_USB)
114 busses
.push_back(std::make_shared
<CPeripheralBusUSB
>(*this));
116 #if defined(HAVE_LIBCEC)
117 busses
.push_back(std::make_shared
<CPeripheralBusCEC
>(*this));
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));
125 busses
.push_back(std::make_shared
<CPeripheralBusApplication
>(*this));
128 std::unique_lock
<CCriticalSection
> bussesLock(m_critSectionBusses
);
132 /* initialise all known busses and run an initial scan for devices */
133 for (auto& bus
: busses
)
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 */
157 for (const auto& bus
: busses
)
162 std::unique_lock
<CCriticalSection
> mappingsLock(m_critSectionMappings
);
163 /* delete mappings */
164 for (auto& mapping
: m_mappings
)
165 mapping
.m_settings
.clear();
169 #if !defined(HAVE_LIBCEC)
170 m_bMissingLibCecWarningDisplayed
= false;
174 void CPeripherals::TriggerDeviceScan(const PeripheralBusType type
/* = PERIPHERAL_BUS_UNKNOWN */)
176 std::vector
<PeripheralBusPtr
> busses
;
178 std::unique_lock
<CCriticalSection
> lock(m_critSectionBusses
);
182 for (auto& bus
: busses
)
186 if (type
== PERIPHERAL_BUS_UNKNOWN
)
188 else if (bus
->Type() == PERIPHERAL_BUS_ADDON
)
190 else if (bus
->Type() == type
)
194 bus
->TriggerDeviceScan();
198 PeripheralBusPtr
CPeripherals::GetBusByType(const PeripheralBusType type
) const
200 std::unique_lock
<CCriticalSection
> lock(m_critSectionBusses
);
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())
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
)
223 /* return the first device that matches */
224 PeripheralPtr peripheral
= bus
->GetPeripheral(strLocation
);
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())
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
);
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
)
278 iReturn
+= bus
->GetPeripheralsWithFeature(results
, feature
);
284 size_t CPeripherals::GetNumberOfPeripherals() const
287 std::unique_lock
<CCriticalSection
> lock(m_critSectionBusses
);
288 for (const auto& bus
: m_busses
)
289 iReturn
+= bus
->GetNumberOfPeripherals();
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
)
314 peripheral
= PeripheralPtr(new CPeripheralHID(*this, mappedResult
, &bus
));
318 peripheral
= PeripheralPtr(new CPeripheralNIC(*this, mappedResult
, &bus
));
321 case PERIPHERAL_DISK
:
322 peripheral
= PeripheralPtr(new CPeripheralDisk(*this, mappedResult
, &bus
));
325 case PERIPHERAL_NYXBOARD
:
326 peripheral
= PeripheralPtr(new CPeripheralNyxboard(*this, mappedResult
, &bus
));
329 case PERIPHERAL_TUNER
:
330 peripheral
= PeripheralPtr(new CPeripheralTuner(*this, mappedResult
, &bus
));
333 case PERIPHERAL_BLUETOOTH
:
334 peripheral
= PeripheralPtr(new CPeripheralBluetooth(*this, mappedResult
, &bus
));
338 #if defined(HAVE_LIBCEC)
339 if (bus
.Type() == PERIPHERAL_BUS_CEC
)
340 peripheral
= PeripheralPtr(new CPeripheralCecAdapter(*this, mappedResult
, &bus
));
342 if (!m_bMissingLibCecWarningDisplayed
)
344 m_bMissingLibCecWarningDisplayed
= true;
347 "{} - libCEC support has not been compiled in, so the CEC adapter cannot be used.",
349 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning
,
350 g_localizeStrings
.Get(36000),
351 g_localizeStrings
.Get(36017));
356 case PERIPHERAL_IMON
:
357 peripheral
= PeripheralPtr(new CPeripheralImon(*this, mappedResult
, &bus
));
360 case PERIPHERAL_JOYSTICK
:
361 peripheral
= PeripheralPtr(new CPeripheralJoystick(*this, mappedResult
, &bus
));
364 case PERIPHERAL_KEYBOARD
:
365 peripheral
= PeripheralPtr(new CPeripheralKeyboard(*this, mappedResult
, &bus
));
368 case PERIPHERAL_MOUSE
:
369 peripheral
= PeripheralPtr(new CPeripheralMouse(*this, mappedResult
, &bus
));
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
);
384 CLog::Log(LOGDEBUG
, "{} - failed to initialise peripheral on '{}'", __FUNCTION__
,
385 mappedResult
.m_strLocation
);
390 void CPeripherals::OnDeviceAdded(const CPeripheralBus
& bus
, const CPeripheral
& peripheral
)
394 //! @todo Improve device notifications in v18
398 // don't show a notification for devices detected during the initial scan
399 if (!bus
.IsInitialised())
403 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info
, g_localizeStrings
.Get(35005), peripheral
.DeviceName());
407 void CPeripherals::OnDeviceDeleted(const CPeripheralBus
& bus
, const CPeripheral
& peripheral
)
411 //! @todo Improve device notifications in v18
416 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info
, g_localizeStrings
.Get(35006), peripheral
.DeviceName());
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
);
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;
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;
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
;
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;
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());
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");
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>");
522 for (auto* currentNode
= pRootElement
->FirstChildElement("peripheral"); currentNode
!= nullptr;
523 currentNode
= currentNode
->NextSiblingElement("peripheral"))
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
);
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
);
553 PeripheralTypeTranslator::GetBusTypeFromString(XMLUtils::GetAttribute(currentNode
, "bus"));
555 PeripheralTypeTranslator::GetTypeFromString(XMLUtils::GetAttribute(currentNode
, "class"));
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
);
567 void CPeripherals::GetSettingsFromMappingsFile(
568 tinyxml2::XMLElement
* xmlNode
, std::map
<std::string
, PeripheralDeviceSetting
>& settings
)
570 auto* currentNode
= xmlNode
->FirstChildElement("setting");
573 while (currentNode
!= nullptr)
576 std::string strKey
= XMLUtils::GetAttribute(currentNode
, "key");
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")
601 currentNode
->Attribute("value") ? (float)atof(currentNode
->Attribute("value")) : 0;
602 float fMin
= currentNode
->Attribute("min") ? (float)atof(currentNode
->Attribute("min")) : 0;
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
);
632 std::string strValue
= XMLUtils::GetAttribute(currentNode
, "value");
633 setting
= std::make_shared
<CSettingString
>(strKey
, iLabelId
, strValue
);
638 //! @todo add more types if needed
640 /* set the visibility */
641 setting
->SetVisible(bConfigurable
);
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 */
649 if (iOrder
> iMaxOrder
)
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://"))
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://"))
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
);
708 bool CPeripherals::OnAction(const CAction
& action
)
710 if (action
.GetID() == ACTION_MUTE
)
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();
730 cecDevice
->VolumeDown();
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())
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();
777 bool CPeripherals::ToggleDeviceState(CecStateChange mode
/*= STATE_SWITCH_TOGGLE */)
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
);
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();
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
))
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
));
854 if (peripheral
->HasFeature(feature
))
855 CLog::Log(LOGDEBUG
, "PERIPHERALS: Device \"{}\" failed to test {} feature",
856 peripheral
->DeviceName(), PeripheralTypeTranslator::FeatureToString(feature
));
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
);
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
);
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
);
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);
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)
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)
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 &&
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
);
1002 case TMSG_CECACTIVATESOURCE
:
1003 ToggleDeviceState(STATE_ACTIVATE_SOURCE
);
1006 case TMSG_CECSTANDBY
:
1007 ToggleDeviceState(STATE_STANDBY
);
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
))