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