1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "Peripherals.h"
10 
11 #include "CompileInfo.h"
12 #include "EventScanner.h"
13 #include "addons/AddonButtonMap.h"
14 #include "addons/AddonManager.h"
15 #include "addons/gui/GUIDialogAddonSettings.h"
16 #include "addons/gui/GUIWindowAddonBrowser.h"
17 #include "bus/PeripheralBus.h"
18 #include "bus/PeripheralBusUSB.h"
19 
20 #include <utility>
21 #if defined(TARGET_ANDROID)
22 #include "platform/android/peripherals/PeripheralBusAndroid.h"
23 #elif defined(TARGET_DARWIN_EMBEDDED)
24 #include "platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.h"
25 #endif
26 #include "FileItem.h"
27 #include "GUIUserMessages.h"
28 #include "ServiceBroker.h"
29 #include "Util.h"
30 #include "bus/virtual/PeripheralBusAddon.h"
31 #include "bus/virtual/PeripheralBusApplication.h"
32 #include "devices/PeripheralBluetooth.h"
33 #include "devices/PeripheralCecAdapter.h"
34 #include "devices/PeripheralDisk.h"
35 #include "devices/PeripheralHID.h"
36 #include "devices/PeripheralImon.h"
37 #include "devices/PeripheralJoystick.h"
38 #include "devices/PeripheralKeyboard.h"
39 #include "devices/PeripheralMouse.h"
40 #include "devices/PeripheralNIC.h"
41 #include "devices/PeripheralNyxboard.h"
42 #include "devices/PeripheralTuner.h"
43 #include "filesystem/Directory.h"
44 #include "guilib/GUIComponent.h"
45 #include "guilib/GUIWindowManager.h"
46 #include "guilib/WindowIDs.h"
47 #include "input/Key.h"
48 #include "input/joysticks/interfaces/IButtonMapper.h"
49 #include "interfaces/AnnouncementManager.h"
50 #include "messaging/ApplicationMessenger.h"
51 #include "messaging/ThreadMessage.h"
52 #include "peripherals/dialogs/GUIDialogPeripherals.h"
53 #include "settings/Settings.h"
54 #include "settings/SettingsComponent.h"
55 #include "settings/lib/Setting.h"
56 #include "threads/SingleLock.h"
57 #include "utils/StringUtils.h"
58 #include "utils/XBMCTinyXML.h"
59 #include "utils/XMLUtils.h"
60 #include "utils/log.h"
61 
62 #if defined(HAVE_LIBCEC)
63 #include "bus/virtual/PeripheralBusCEC.h"
64 #else
65 #include "dialogs/GUIDialogKaiToast.h"
66 #include "guilib/LocalizeStrings.h"
67 #endif
68 
69 using namespace KODI;
70 using namespace JOYSTICK;
71 using namespace PERIPHERALS;
72 using namespace XFILE;
73 using namespace KODI::MESSAGING;
74 
CPeripherals(CInputManager & inputManager,GAME::CControllerManager & controllerProfiles)75 CPeripherals::CPeripherals(CInputManager& inputManager,
76                            GAME::CControllerManager& controllerProfiles)
77   : m_inputManager(inputManager),
78     m_controllerProfiles(controllerProfiles),
79     m_eventScanner(new CEventScanner(*this))
80 {
81   // Register settings
82   std::set<std::string> settingSet;
83   settingSet.insert(CSettings::SETTING_INPUT_PERIPHERALS);
84   settingSet.insert(CSettings::SETTING_INPUT_PERIPHERALLIBRARIES);
85   settingSet.insert(CSettings::SETTING_INPUT_CONTROLLERCONFIG);
86   settingSet.insert(CSettings::SETTING_INPUT_TESTRUMBLE);
87   settingSet.insert(CSettings::SETTING_LOCALE_LANGUAGE);
88   CServiceBroker::GetSettingsComponent()->GetSettings()->RegisterCallback(this, settingSet);
89 }
90 
~CPeripherals()91 CPeripherals::~CPeripherals()
92 {
93   // Unregister settings
94   CServiceBroker::GetSettingsComponent()->GetSettings()->UnregisterCallback(this);
95 
96   Clear();
97 }
98 
Initialise()99 void CPeripherals::Initialise()
100 {
101   Clear();
102 
103 #if !defined(TARGET_DARWIN_TVOS)
104   CDirectory::Create("special://profile/peripheral_data");
105 
106   /* load mappings from peripherals.xml */
107   LoadMappings();
108 
109   std::vector<PeripheralBusPtr> busses;
110 
111 #if defined(HAVE_PERIPHERAL_BUS_USB)
112   busses.push_back(std::make_shared<CPeripheralBusUSB>(*this));
113 #endif
114 #if defined(HAVE_LIBCEC)
115   busses.push_back(std::make_shared<CPeripheralBusCEC>(*this));
116 #endif
117   busses.push_back(std::make_shared<CPeripheralBusAddon>(*this));
118 #if defined(TARGET_ANDROID)
119   busses.push_back(std::make_shared<CPeripheralBusAndroid>(*this));
120 #elif defined(TARGET_DARWIN_EMBEDDED)
121   busses.push_back(std::make_shared<CPeripheralBusDarwinEmbedded>(*this));
122 #endif
123   busses.push_back(std::make_shared<CPeripheralBusApplication>(*this));
124 
125   {
126     CSingleLock bussesLock(m_critSectionBusses);
127     m_busses = busses;
128   }
129 
130   /* initialise all known busses and run an initial scan for devices */
131   for (auto& bus : busses)
132     bus->Initialise();
133 
134   m_eventScanner->Start();
135 
136   MESSAGING::CApplicationMessenger::GetInstance().RegisterReceiver(this);
137   CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this);
138 #endif
139 }
140 
Clear()141 void CPeripherals::Clear()
142 {
143   CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this);
144 
145   m_eventScanner->Stop();
146 
147   // avoid deadlocks by copying all busses into a temporary variable and destroying them from there
148   std::vector<PeripheralBusPtr> busses;
149   {
150     CSingleLock bussesLock(m_critSectionBusses);
151     /* delete busses and devices */
152     busses = m_busses;
153     m_busses.clear();
154   }
155 
156   for (const auto& bus : busses)
157     bus->Clear();
158   busses.clear();
159 
160   {
161     CSingleLock mappingsLock(m_critSectionMappings);
162     /* delete mappings */
163     for (auto& mapping : m_mappings)
164       mapping.m_settings.clear();
165     m_mappings.clear();
166   }
167 
168 #if !defined(HAVE_LIBCEC)
169   m_bMissingLibCecWarningDisplayed = false;
170 #endif
171 }
172 
TriggerDeviceScan(const PeripheralBusType type)173 void CPeripherals::TriggerDeviceScan(const PeripheralBusType type /* = PERIPHERAL_BUS_UNKNOWN */)
174 {
175   std::vector<PeripheralBusPtr> busses;
176   {
177     CSingleLock lock(m_critSectionBusses);
178     busses = m_busses;
179   }
180 
181   for (auto& bus : busses)
182   {
183     bool bScan = false;
184 
185     if (type == PERIPHERAL_BUS_UNKNOWN)
186       bScan = true;
187     else if (bus->Type() == PERIPHERAL_BUS_ADDON)
188       bScan = true;
189     else if (bus->Type() == type)
190       bScan = true;
191 
192     if (bScan)
193       bus->TriggerDeviceScan();
194   }
195 }
196 
GetBusByType(const PeripheralBusType type) const197 PeripheralBusPtr CPeripherals::GetBusByType(const PeripheralBusType type) const
198 {
199   CSingleLock lock(m_critSectionBusses);
200 
201   const auto& bus =
202       std::find_if(m_busses.cbegin(), m_busses.cend(),
203                    [type](const PeripheralBusPtr& bus) { return bus->Type() == type; });
204   if (bus != m_busses.cend())
205     return *bus;
206 
207   return nullptr;
208 }
209 
GetPeripheralAtLocation(const std::string & strLocation,PeripheralBusType busType) const210 PeripheralPtr CPeripherals::GetPeripheralAtLocation(
211     const std::string& strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
212 {
213   PeripheralPtr result;
214 
215   CSingleLock lock(m_critSectionBusses);
216   for (const auto& bus : m_busses)
217   {
218     /* check whether the bus matches if a bus type other than unknown was passed */
219     if (busType != PERIPHERAL_BUS_UNKNOWN && bus->Type() != busType)
220       continue;
221 
222     /* return the first device that matches */
223     PeripheralPtr peripheral = bus->GetPeripheral(strLocation);
224     if (peripheral)
225     {
226       result = peripheral;
227       break;
228     }
229   }
230 
231   return result;
232 }
233 
HasPeripheralAtLocation(const std::string & strLocation,PeripheralBusType busType) const234 bool CPeripherals::HasPeripheralAtLocation(
235     const std::string& strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
236 {
237   return (GetPeripheralAtLocation(strLocation, busType) != nullptr);
238 }
239 
GetBusWithDevice(const std::string & strLocation) const240 PeripheralBusPtr CPeripherals::GetBusWithDevice(const std::string& strLocation) const
241 {
242   CSingleLock lock(m_critSectionBusses);
243 
244   const auto& bus =
245       std::find_if(m_busses.cbegin(), m_busses.cend(), [&strLocation](const PeripheralBusPtr& bus) {
246         return bus->HasPeripheral(strLocation);
247       });
248   if (bus != m_busses.cend())
249     return *bus;
250 
251   return nullptr;
252 }
253 
SupportsFeature(PeripheralFeature feature) const254 bool CPeripherals::SupportsFeature(PeripheralFeature feature) const
255 {
256   bool bSupportsFeature = false;
257 
258   CSingleLock lock(m_critSectionBusses);
259   for (const auto& bus : m_busses)
260     bSupportsFeature |= bus->SupportsFeature(feature);
261 
262   return bSupportsFeature;
263 }
264 
GetPeripheralsWithFeature(PeripheralVector & results,const PeripheralFeature feature,PeripheralBusType busType) const265 int CPeripherals::GetPeripheralsWithFeature(
266     PeripheralVector& results,
267     const PeripheralFeature feature,
268     PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
269 {
270   CSingleLock lock(m_critSectionBusses);
271   int iReturn(0);
272   for (const auto& bus : m_busses)
273   {
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;
277 
278     iReturn += bus->GetPeripheralsWithFeature(results, feature);
279   }
280 
281   return iReturn;
282 }
283 
GetNumberOfPeripherals() const284 size_t CPeripherals::GetNumberOfPeripherals() const
285 {
286   size_t iReturn(0);
287   CSingleLock lock(m_critSectionBusses);
288   for (const auto& bus : m_busses)
289     iReturn += bus->GetNumberOfPeripherals();
290 
291   return iReturn;
292 }
293 
HasPeripheralWithFeature(const PeripheralFeature feature,PeripheralBusType busType) const294 bool CPeripherals::HasPeripheralWithFeature(
295     const PeripheralFeature feature, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
296 {
297   PeripheralVector dummy;
298   return (GetPeripheralsWithFeature(dummy, feature, busType) > 0);
299 }
300 
CreatePeripheral(CPeripheralBus & bus,const PeripheralScanResult & result)301 void CPeripherals::CreatePeripheral(CPeripheralBus& bus, const PeripheralScanResult& result)
302 {
303   PeripheralPtr peripheral;
304   PeripheralScanResult mappedResult = result;
305   if (mappedResult.m_busType == PERIPHERAL_BUS_UNKNOWN)
306     mappedResult.m_busType = bus.Type();
307 
308   /* check whether there's something mapped in peripherals.xml */
309   GetMappingForDevice(bus, mappedResult);
310 
311   switch (mappedResult.m_mappedType)
312   {
313     case PERIPHERAL_HID:
314       peripheral = PeripheralPtr(new CPeripheralHID(*this, mappedResult, &bus));
315       break;
316 
317     case PERIPHERAL_NIC:
318       peripheral = PeripheralPtr(new CPeripheralNIC(*this, mappedResult, &bus));
319       break;
320 
321     case PERIPHERAL_DISK:
322       peripheral = PeripheralPtr(new CPeripheralDisk(*this, mappedResult, &bus));
323       break;
324 
325     case PERIPHERAL_NYXBOARD:
326       peripheral = PeripheralPtr(new CPeripheralNyxboard(*this, mappedResult, &bus));
327       break;
328 
329     case PERIPHERAL_TUNER:
330       peripheral = PeripheralPtr(new CPeripheralTuner(*this, mappedResult, &bus));
331       break;
332 
333     case PERIPHERAL_BLUETOOTH:
334       peripheral = PeripheralPtr(new CPeripheralBluetooth(*this, mappedResult, &bus));
335       break;
336 
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)
343       {
344         m_bMissingLibCecWarningDisplayed = true;
345         CLog::Log(
346             LOGWARNING,
347             "%s - 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));
352       }
353 #endif
354       break;
355 
356     case PERIPHERAL_IMON:
357       peripheral = PeripheralPtr(new CPeripheralImon(*this, mappedResult, &bus));
358       break;
359 
360     case PERIPHERAL_JOYSTICK:
361       peripheral = PeripheralPtr(new CPeripheralJoystick(*this, mappedResult, &bus));
362       break;
363 
364     case PERIPHERAL_KEYBOARD:
365       peripheral = PeripheralPtr(new CPeripheralKeyboard(*this, mappedResult, &bus));
366       break;
367 
368     case PERIPHERAL_MOUSE:
369       peripheral = PeripheralPtr(new CPeripheralMouse(*this, mappedResult, &bus));
370       break;
371 
372     default:
373       break;
374   }
375 
376   if (peripheral)
377   {
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
383     {
384       CLog::Log(LOGDEBUG, "%s - failed to initialise peripheral on '%s'", __FUNCTION__,
385                 mappedResult.m_strLocation.c_str());
386     }
387   }
388 }
389 
OnDeviceAdded(const CPeripheralBus & bus,const CPeripheral & peripheral)390 void CPeripherals::OnDeviceAdded(const CPeripheralBus& bus, const CPeripheral& peripheral)
391 {
392   OnDeviceChanged();
393 
394   //! @todo Improve device notifications in v18
395 #if 0
396   bool bNotify = true;
397 
398   // don't show a notification for devices detected during the initial scan
399   if (!bus.IsInitialised())
400     bNotify = false;
401 
402   if (bNotify)
403     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35005), peripheral.DeviceName());
404 #endif
405 }
406 
OnDeviceDeleted(const CPeripheralBus & bus,const CPeripheral & peripheral)407 void CPeripherals::OnDeviceDeleted(const CPeripheralBus& bus, const CPeripheral& peripheral)
408 {
409   OnDeviceChanged();
410 
411   //! @todo Improve device notifications in v18
412 #if 0
413   bool bNotify = true;
414 
415   if (bNotify)
416     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35006), peripheral.DeviceName());
417 #endif
418 }
419 
OnDeviceChanged()420 void CPeripherals::OnDeviceChanged()
421 {
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);
426 
427   SetChanged();
428 }
429 
GetMappingForDevice(const CPeripheralBus & bus,PeripheralScanResult & result) const430 bool CPeripherals::GetMappingForDevice(const CPeripheralBus& bus,
431                                        PeripheralScanResult& result) const
432 {
433   CSingleLock lock(m_critSectionMappings);
434 
435   /* check all mappings in the order in which they are defined in peripherals.xml */
436   for (const auto& mapping : m_mappings)
437   {
438     bool bProductMatch = false;
439     if (mapping.m_PeripheralID.empty())
440       bProductMatch = true;
441     else
442     {
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;
447     }
448 
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);
452 
453     if (bProductMatch && bBusMatch && bClassMatch)
454     {
455       std::string strVendorId, strProductId;
456       PeripheralTypeTranslator::FormatHexString(result.m_iVendorId, strVendorId);
457       PeripheralTypeTranslator::FormatHexString(result.m_iProductId, strProductId);
458       CLog::Log(LOGDEBUG, "%s - device (%s:%s) mapped to %s (type = %s)", __FUNCTION__,
459                 strVendorId.c_str(), strProductId.c_str(), mapping.m_strDeviceName.c_str(),
460                 PeripheralTypeTranslator::TypeToString(mapping.m_mappedTo));
461       result.m_mappedType = mapping.m_mappedTo;
462       if (!mapping.m_strDeviceName.empty())
463         result.m_strDeviceName = mapping.m_strDeviceName;
464       return true;
465     }
466   }
467 
468   return false;
469 }
470 
GetSettingsFromMapping(CPeripheral & peripheral) const471 void CPeripherals::GetSettingsFromMapping(CPeripheral& peripheral) const
472 {
473   CSingleLock lock(m_critSectionMappings);
474 
475   /* check all mappings in the order in which they are defined in peripherals.xml */
476   for (const auto& mapping : m_mappings)
477   {
478     bool bProductMatch = false;
479     if (mapping.m_PeripheralID.empty())
480       bProductMatch = true;
481     else
482     {
483       for (const auto& peripheralID : mapping.m_PeripheralID)
484         if (peripheralID.m_iVendorId == peripheral.VendorId() &&
485             peripheralID.m_iProductId == peripheral.ProductId())
486           bProductMatch = true;
487     }
488 
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());
493 
494     if (bBusMatch && bProductMatch && bClassMatch)
495     {
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);
498     }
499   }
500 }
501 
502 #define SS(x) ((x) ? x : "")
LoadMappings()503 bool CPeripherals::LoadMappings()
504 {
505   CSingleLock lock(m_critSectionMappings);
506 
507   CXBMCTinyXML xmlDoc;
508   if (!xmlDoc.LoadFile("special://xbmc/system/peripherals.xml"))
509   {
510     CLog::Log(LOGWARNING, "%s - peripherals.xml does not exist", __FUNCTION__);
511     return true;
512   }
513 
514   TiXmlElement* pRootElement = xmlDoc.RootElement();
515   if (!pRootElement || StringUtils::CompareNoCase(pRootElement->Value(), "peripherals") != 0)
516   {
517     CLog::Log(LOGERROR, "%s - peripherals.xml does not contain <peripherals>", __FUNCTION__);
518     return false;
519   }
520 
521   for (TiXmlElement* currentNode = pRootElement->FirstChildElement("peripheral"); currentNode;
522        currentNode = currentNode->NextSiblingElement("peripheral"))
523   {
524     PeripheralID id;
525     PeripheralDeviceMapping mapping;
526 
527     mapping.m_strDeviceName = XMLUtils::GetAttribute(currentNode, "name");
528 
529     // If there is no vendor_product attribute ignore this entry
530     if (currentNode->Attribute("vendor_product"))
531     {
532       // The vendor_product attribute is a list of comma separated vendor:product pairs
533       std::vector<std::string> vpArray =
534           StringUtils::Split(currentNode->Attribute("vendor_product"), ",");
535       for (const auto& i : vpArray)
536       {
537         std::vector<std::string> idArray = StringUtils::Split(i, ":");
538         if (idArray.size() != 2)
539         {
540           CLog::Log(LOGERROR, "%s - ignoring node \"%s\" with invalid vendor_product attribute",
541                     __FUNCTION__, mapping.m_strDeviceName.c_str());
542           continue;
543         }
544 
545         id.m_iVendorId = PeripheralTypeTranslator::HexStringToInt(idArray[0].c_str());
546         id.m_iProductId = PeripheralTypeTranslator::HexStringToInt(idArray[1].c_str());
547         mapping.m_PeripheralID.push_back(id);
548       }
549     }
550 
551     mapping.m_busType =
552         PeripheralTypeTranslator::GetBusTypeFromString(XMLUtils::GetAttribute(currentNode, "bus"));
553     mapping.m_class =
554         PeripheralTypeTranslator::GetTypeFromString(XMLUtils::GetAttribute(currentNode, "class"));
555     mapping.m_mappedTo =
556         PeripheralTypeTranslator::GetTypeFromString(XMLUtils::GetAttribute(currentNode, "mapTo"));
557     GetSettingsFromMappingsFile(currentNode, mapping.m_settings);
558 
559     m_mappings.push_back(mapping);
560     CLog::Log(LOGDEBUG, "%s - loaded node \"%s\"", __FUNCTION__, mapping.m_strDeviceName.c_str());
561   }
562 
563   return true;
564 }
565 
GetSettingsFromMappingsFile(TiXmlElement * xmlNode,std::map<std::string,PeripheralDeviceSetting> & settings)566 void CPeripherals::GetSettingsFromMappingsFile(
567     TiXmlElement* xmlNode, std::map<std::string, PeripheralDeviceSetting>& settings)
568 {
569   TiXmlElement* currentNode = xmlNode->FirstChildElement("setting");
570   int iMaxOrder = 0;
571 
572   while (currentNode)
573   {
574     SettingPtr setting;
575     std::string strKey = XMLUtils::GetAttribute(currentNode, "key");
576     if (strKey.empty())
577       continue;
578 
579     std::string strSettingsType = XMLUtils::GetAttribute(currentNode, "type");
580     int iLabelId = currentNode->Attribute("label") ? atoi(currentNode->Attribute("label")) : -1;
581     const std::string config = XMLUtils::GetAttribute(currentNode, "configurable");
582     bool bConfigurable = (config.empty() || (config != "no" && config != "false" && config != "0"));
583     if (strSettingsType == "bool")
584     {
585       const std::string value = XMLUtils::GetAttribute(currentNode, "value");
586       bool bValue = (value != "no" && value != "false" && value != "0");
587       setting = std::make_shared<CSettingBool>(strKey, iLabelId, bValue);
588     }
589     else if (strSettingsType == "int")
590     {
591       int iValue = currentNode->Attribute("value") ? atoi(currentNode->Attribute("value")) : 0;
592       int iMin = currentNode->Attribute("min") ? atoi(currentNode->Attribute("min")) : 0;
593       int iStep = currentNode->Attribute("step") ? atoi(currentNode->Attribute("step")) : 1;
594       int iMax = currentNode->Attribute("max") ? atoi(currentNode->Attribute("max")) : 255;
595       setting = std::make_shared<CSettingInt>(strKey, iLabelId, iValue, iMin, iStep, iMax);
596     }
597     else if (strSettingsType == "float")
598     {
599       float fValue =
600           currentNode->Attribute("value") ? (float)atof(currentNode->Attribute("value")) : 0;
601       float fMin = currentNode->Attribute("min") ? (float)atof(currentNode->Attribute("min")) : 0;
602       float fStep =
603           currentNode->Attribute("step") ? (float)atof(currentNode->Attribute("step")) : 0;
604       float fMax = currentNode->Attribute("max") ? (float)atof(currentNode->Attribute("max")) : 0;
605       setting = std::make_shared<CSettingNumber>(strKey, iLabelId, fValue, fMin, fStep, fMax);
606     }
607     else if (StringUtils::EqualsNoCase(strSettingsType, "enum"))
608     {
609       std::string strEnums = XMLUtils::GetAttribute(currentNode, "lvalues");
610       if (!strEnums.empty())
611       {
612         TranslatableIntegerSettingOptions enums;
613         std::vector<std::string> valuesVec;
614         StringUtils::Tokenize(strEnums, valuesVec, "|");
615         for (unsigned int i = 0; i < valuesVec.size(); i++)
616           enums.emplace_back(atoi(valuesVec[i].c_str()), atoi(valuesVec[i].c_str()));
617         int iValue = currentNode->Attribute("value") ? atoi(currentNode->Attribute("value")) : 0;
618         setting = std::make_shared<CSettingInt>(strKey, iLabelId, iValue, enums);
619       }
620     }
621     else
622     {
623       std::string strValue = XMLUtils::GetAttribute(currentNode, "value");
624       setting = std::make_shared<CSettingString>(strKey, iLabelId, strValue);
625     }
626 
627     if (setting)
628     {
629       //! @todo add more types if needed
630 
631       /* set the visibility */
632       setting->SetVisible(bConfigurable);
633 
634       /* set the order */
635       int iOrder = 0;
636       currentNode->Attribute("order", &iOrder);
637       /* if the order attribute is invalid or 0, then the setting will be added at the end */
638       if (iOrder < 0)
639         iOrder = 0;
640       if (iOrder > iMaxOrder)
641         iMaxOrder = iOrder;
642 
643       /* and add this new setting */
644       PeripheralDeviceSetting deviceSetting = {setting, iOrder};
645       settings[strKey] = deviceSetting;
646     }
647 
648     currentNode = currentNode->NextSiblingElement("setting");
649   }
650 
651   /* add the settings without an order attribute or an invalid order attribute set at the end */
652   for (auto& it : settings)
653   {
654     if (it.second.m_order == 0)
655       it.second.m_order = ++iMaxOrder;
656   }
657 }
658 
GetDirectory(const std::string & strPath,CFileItemList & items) const659 void CPeripherals::GetDirectory(const std::string& strPath, CFileItemList& items) const
660 {
661   if (!StringUtils::StartsWithNoCase(strPath, "peripherals://"))
662     return;
663 
664   std::string strPathCut = strPath.substr(14);
665   std::string strBus = strPathCut.substr(0, strPathCut.find('/'));
666 
667   CSingleLock lock(m_critSectionBusses);
668   for (const auto& bus : m_busses)
669   {
670     if (StringUtils::EqualsNoCase(strBus, "all") ||
671         StringUtils::EqualsNoCase(strBus, PeripheralTypeTranslator::BusTypeToString(bus->Type())))
672       bus->GetDirectory(strPath, items);
673   }
674 }
675 
GetByPath(const std::string & strPath) const676 PeripheralPtr CPeripherals::GetByPath(const std::string& strPath) const
677 {
678   PeripheralPtr result;
679 
680   if (!StringUtils::StartsWithNoCase(strPath, "peripherals://"))
681     return result;
682 
683   std::string strPathCut = strPath.substr(14);
684   std::string strBus = strPathCut.substr(0, strPathCut.find('/'));
685 
686   CSingleLock lock(m_critSectionBusses);
687   for (const auto& bus : m_busses)
688   {
689     if (StringUtils::EqualsNoCase(strBus, PeripheralTypeTranslator::BusTypeToString(bus->Type())))
690     {
691       result = bus->GetByPath(strPath);
692       break;
693     }
694   }
695 
696   return result;
697 }
698 
OnAction(const CAction & action)699 bool CPeripherals::OnAction(const CAction& action)
700 {
701   if (action.GetID() == ACTION_MUTE)
702   {
703     return ToggleMute();
704   }
705 
706   if (SupportsCEC() && action.GetAmount() &&
707       (action.GetID() == ACTION_VOLUME_UP || action.GetID() == ACTION_VOLUME_DOWN))
708   {
709     PeripheralVector peripherals;
710     if (GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
711     {
712       for (auto& peripheral : peripherals)
713       {
714         std::shared_ptr<CPeripheralCecAdapter> cecDevice =
715             std::static_pointer_cast<CPeripheralCecAdapter>(peripheral);
716         if (cecDevice->HasAudioControl())
717         {
718           if (action.GetID() == ACTION_VOLUME_UP)
719             cecDevice->VolumeUp();
720           else
721             cecDevice->VolumeDown();
722           return true;
723         }
724       }
725     }
726   }
727 
728   return false;
729 }
730 
IsMuted()731 bool CPeripherals::IsMuted()
732 {
733   PeripheralVector peripherals;
734   if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
735   {
736     for (const auto& peripheral : peripherals)
737     {
738       std::shared_ptr<CPeripheralCecAdapter> cecDevice =
739           std::static_pointer_cast<CPeripheralCecAdapter>(peripheral);
740       if (cecDevice->IsMuted())
741         return true;
742     }
743   }
744 
745   return false;
746 }
747 
ToggleMute()748 bool CPeripherals::ToggleMute()
749 {
750   PeripheralVector peripherals;
751   if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
752   {
753     for (auto& peripheral : peripherals)
754     {
755       std::shared_ptr<CPeripheralCecAdapter> cecDevice =
756           std::static_pointer_cast<CPeripheralCecAdapter>(peripheral);
757       if (cecDevice->HasAudioControl())
758       {
759         cecDevice->ToggleMute();
760         return true;
761       }
762     }
763   }
764 
765   return false;
766 }
767 
ToggleDeviceState(CecStateChange mode)768 bool CPeripherals::ToggleDeviceState(CecStateChange mode /*= STATE_SWITCH_TOGGLE */)
769 {
770   bool ret(false);
771   PeripheralVector peripherals;
772 
773   if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
774   {
775     for (auto& peripheral : peripherals)
776     {
777       std::shared_ptr<CPeripheralCecAdapter> cecDevice =
778           std::static_pointer_cast<CPeripheralCecAdapter>(peripheral);
779       ret |= cecDevice->ToggleDeviceState(mode);
780     }
781   }
782 
783   return ret;
784 }
785 
GetNextKeypress(float frameTime,CKey & key)786 bool CPeripherals::GetNextKeypress(float frameTime, CKey& key)
787 {
788   PeripheralVector peripherals;
789   if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
790   {
791     for (auto& peripheral : peripherals)
792     {
793       std::shared_ptr<CPeripheralCecAdapter> cecDevice =
794           std::static_pointer_cast<CPeripheralCecAdapter>(peripheral);
795       if (cecDevice->GetButton())
796       {
797         CKey newKey(cecDevice->GetButton(), cecDevice->GetHoldTime());
798         cecDevice->ResetButton();
799         key = newKey;
800         return true;
801       }
802     }
803   }
804 
805   return false;
806 }
807 
RegisterEventPoller()808 EventPollHandlePtr CPeripherals::RegisterEventPoller()
809 {
810   return m_eventScanner->RegisterPollHandle();
811 }
812 
RegisterEventLock()813 EventLockHandlePtr CPeripherals::RegisterEventLock()
814 {
815   return m_eventScanner->RegisterLock();
816 }
817 
OnUserNotification()818 void CPeripherals::OnUserNotification()
819 {
820   if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
821           CSettings::SETTING_INPUT_RUMBLENOTIFY))
822     return;
823 
824   PeripheralVector peripherals;
825   GetPeripheralsWithFeature(peripherals, FEATURE_RUMBLE);
826 
827   for (auto& peripheral : peripherals)
828     peripheral->OnUserNotification();
829 }
830 
TestFeature(PeripheralFeature feature)831 void CPeripherals::TestFeature(PeripheralFeature feature)
832 {
833   PeripheralVector peripherals;
834   GetPeripheralsWithFeature(peripherals, feature);
835 
836   for (auto& peripheral : peripherals)
837   {
838     if (peripheral->TestFeature(feature))
839     {
840       CLog::Log(LOGDEBUG, "PERIPHERALS: Device \"%s\" tested %s feature",
841                 peripheral->DeviceName().c_str(),
842                 PeripheralTypeTranslator::FeatureToString(feature));
843     }
844     else
845     {
846       if (peripheral->HasFeature(feature))
847         CLog::Log(LOGDEBUG, "PERIPHERALS: Device \"%s\" failed to test %s feature",
848                   peripheral->DeviceName().c_str(),
849                   PeripheralTypeTranslator::FeatureToString(feature));
850       else
851         CLog::Log(LOGDEBUG, "PERIPHERALS: Device \"%s\" doesn't support %s feature",
852                   peripheral->DeviceName().c_str(),
853                   PeripheralTypeTranslator::FeatureToString(feature));
854     }
855   }
856 }
857 
PowerOffDevices()858 void CPeripherals::PowerOffDevices()
859 {
860   TestFeature(FEATURE_POWER_OFF);
861 }
862 
ProcessEvents(void)863 void CPeripherals::ProcessEvents(void)
864 {
865   std::vector<PeripheralBusPtr> busses;
866   {
867     CSingleLock lock(m_critSectionBusses);
868     busses = m_busses;
869   }
870 
871   for (PeripheralBusPtr& bus : busses)
872     bus->ProcessEvents();
873 }
874 
EnableButtonMapping()875 void CPeripherals::EnableButtonMapping()
876 {
877   std::vector<PeripheralBusPtr> busses;
878   {
879     CSingleLock lock(m_critSectionBusses);
880     busses = m_busses;
881   }
882 
883   for (PeripheralBusPtr& bus : busses)
884     bus->EnableButtonMapping();
885 }
886 
GetAddonWithButtonMap(const CPeripheral * device)887 PeripheralAddonPtr CPeripherals::GetAddonWithButtonMap(const CPeripheral* device)
888 {
889   PeripheralBusAddonPtr addonBus =
890       std::static_pointer_cast<CPeripheralBusAddon>(GetBusByType(PERIPHERAL_BUS_ADDON));
891 
892   PeripheralAddonPtr addon;
893 
894   PeripheralAddonPtr addonWithButtonMap;
895   if (addonBus && addonBus->GetAddonWithButtonMap(device, addonWithButtonMap))
896     addon = std::move(addonWithButtonMap);
897 
898   return addon;
899 }
900 
ResetButtonMaps(const std::string & controllerId)901 void CPeripherals::ResetButtonMaps(const std::string& controllerId)
902 {
903   PeripheralBusAddonPtr addonBus =
904       std::static_pointer_cast<CPeripheralBusAddon>(GetBusByType(PERIPHERAL_BUS_ADDON));
905 
906   PeripheralVector peripherals;
907   GetPeripheralsWithFeature(peripherals, FEATURE_JOYSTICK);
908 
909   for (auto& peripheral : peripherals)
910   {
911     PeripheralAddonPtr addon;
912     if (addonBus->GetAddonWithButtonMap(peripheral.get(), addon))
913     {
914       CAddonButtonMap buttonMap(peripheral.get(), addon, controllerId);
915       buttonMap.Reset();
916     }
917   }
918 }
919 
RegisterJoystickButtonMapper(IButtonMapper * mapper)920 void CPeripherals::RegisterJoystickButtonMapper(IButtonMapper* mapper)
921 {
922   PeripheralVector peripherals;
923   GetPeripheralsWithFeature(peripherals, FEATURE_JOYSTICK);
924   GetPeripheralsWithFeature(peripherals, FEATURE_KEYBOARD);
925   GetPeripheralsWithFeature(peripherals, FEATURE_MOUSE);
926 
927   for (auto& peripheral : peripherals)
928     peripheral->RegisterJoystickButtonMapper(mapper);
929 }
930 
UnregisterJoystickButtonMapper(IButtonMapper * mapper)931 void CPeripherals::UnregisterJoystickButtonMapper(IButtonMapper* mapper)
932 {
933   mapper->ResetButtonMapCallbacks();
934 
935   PeripheralVector peripherals;
936   GetPeripheralsWithFeature(peripherals, FEATURE_JOYSTICK);
937   GetPeripheralsWithFeature(peripherals, FEATURE_KEYBOARD);
938   GetPeripheralsWithFeature(peripherals, FEATURE_MOUSE);
939 
940   for (auto& peripheral : peripherals)
941     peripheral->UnregisterJoystickButtonMapper(mapper);
942 }
943 
OnSettingChanged(const std::shared_ptr<const CSetting> & setting)944 void CPeripherals::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
945 {
946   if (setting == nullptr)
947     return;
948 
949   const std::string& settingId = setting->GetId();
950   if (settingId == CSettings::SETTING_LOCALE_LANGUAGE)
951   {
952     // user set language, no longer use the TV's language
953     PeripheralVector cecDevices;
954     if (GetPeripheralsWithFeature(cecDevices, FEATURE_CEC) > 0)
955     {
956       for (auto& cecDevice : cecDevices)
957         cecDevice->SetSetting("use_tv_menu_language", false);
958     }
959   }
960 }
961 
OnSettingAction(const std::shared_ptr<const CSetting> & setting)962 void CPeripherals::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
963 {
964   if (setting == nullptr)
965     return;
966 
967   const std::string& settingId = setting->GetId();
968   if (settingId == CSettings::SETTING_INPUT_PERIPHERALS)
969     CGUIDialogPeripherals::Show(*this);
970   else if (settingId == CSettings::SETTING_INPUT_CONTROLLERCONFIG)
971     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_DIALOG_GAME_CONTROLLERS);
972   else if (settingId == CSettings::SETTING_INPUT_TESTRUMBLE)
973     TestFeature(FEATURE_RUMBLE);
974   else if (settingId == CSettings::SETTING_INPUT_PERIPHERALLIBRARIES)
975   {
976     std::string strAddonId;
977     if (CGUIWindowAddonBrowser::SelectAddonID(ADDON::ADDON_PERIPHERALDLL, strAddonId, false, true,
978                                               true, false, true) == 1 &&
979         !strAddonId.empty())
980     {
981       ADDON::AddonPtr addon;
982       if (CServiceBroker::GetAddonMgr().GetAddon(strAddonId, addon, ADDON::ADDON_UNKNOWN,
983                                                  ADDON::OnlyEnabled::YES))
984         CGUIDialogAddonSettings::ShowForAddon(addon);
985     }
986   }
987 }
988 
OnApplicationMessage(MESSAGING::ThreadMessage * pMsg)989 void CPeripherals::OnApplicationMessage(MESSAGING::ThreadMessage* pMsg)
990 {
991   switch (pMsg->dwMessage)
992   {
993     case TMSG_CECTOGGLESTATE:
994       *static_cast<bool*>(pMsg->lpVoid) = ToggleDeviceState(STATE_SWITCH_TOGGLE);
995       break;
996 
997     case TMSG_CECACTIVATESOURCE:
998       ToggleDeviceState(STATE_ACTIVATE_SOURCE);
999       break;
1000 
1001     case TMSG_CECSTANDBY:
1002       ToggleDeviceState(STATE_STANDBY);
1003       break;
1004   }
1005 }
1006 
GetMessageMask()1007 int CPeripherals::GetMessageMask()
1008 {
1009   return TMSG_MASK_PERIPHERALS;
1010 }
1011 
Announce(ANNOUNCEMENT::AnnouncementFlag flag,const std::string & sender,const std::string & message,const CVariant & data)1012 void CPeripherals::Announce(ANNOUNCEMENT::AnnouncementFlag flag,
1013                             const std::string& sender,
1014                             const std::string& message,
1015                             const CVariant& data)
1016 {
1017   if (flag == ANNOUNCEMENT::Player &&
1018       sender == ANNOUNCEMENT::CAnnouncementManager::ANNOUNCEMENT_SENDER)
1019   {
1020     if (message == "OnQuit")
1021     {
1022       if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
1023               CSettings::SETTING_INPUT_CONTROLLERPOWEROFF))
1024         PowerOffDevices();
1025     }
1026   }
1027 }
1028