Merge pull request #473 from Montellese/onplaybackspeedchanged
[vuplus_xbmc] / xbmc / peripherals / Peripherals.cpp
1 /*
2  *      Copyright (C) 2005-2011 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "Peripherals.h"
23 #include "bus/PeripheralBus.h"
24 #include "devices/PeripheralBluetooth.h"
25 #include "devices/PeripheralDisk.h"
26 #include "devices/PeripheralHID.h"
27 #include "devices/PeripheralNIC.h"
28 #include "devices/PeripheralNyxboard.h"
29 #include "devices/PeripheralTuner.h"
30 #if defined(HAVE_LIBCEC)
31 #include "devices/PeripheralCecAdapter.h"
32 #endif
33 #include "bus/PeripheralBusUSB.h"
34 #include "dialogs/GUIDialogPeripheralManager.h"
35
36 #include "threads/SingleLock.h"
37 #include "utils/log.h"
38 #include "utils/XMLUtils.h"
39 #include "settings/GUISettings.h"
40 #include "tinyXML/tinyxml.h"
41 #include "filesystem/Directory.h"
42 #include "guilib/GUIWindowManager.h"
43 #include "guilib/LocalizeStrings.h"
44 #include "dialogs/GUIDialogKaiToast.h"
45 #include "utils/StringUtils.h"
46
47 using namespace PERIPHERALS;
48 using namespace XFILE;
49 using namespace std;
50
51 CPeripherals::CPeripherals(void)
52 {
53   CDirectory::Create("special://profile/peripheral_data");
54
55   Clear();
56 }
57
58 CPeripherals::~CPeripherals(void)
59 {
60   Clear();
61 }
62
63 CPeripherals &CPeripherals::Get(void)
64 {
65   static CPeripherals peripheralsInstance;
66   return peripheralsInstance;
67 }
68
69 void CPeripherals::Initialise(void)
70 {
71   CSingleLock lock(m_critSection);
72   if (!m_bIsStarted)
73   {
74     m_bIsStarted = true;
75
76     /* load mappings from peripherals.xml */
77     LoadMappings();
78
79 #if defined(HAVE_PERIPHERAL_BUS_USB)
80     m_busses.push_back(new CPeripheralBusUSB(this));
81 #endif
82
83     /* initialise all known busses */
84     for (int iBusPtr = (int)m_busses.size() - 1; iBusPtr >= 0; iBusPtr--)
85     {
86       if (!m_busses.at(iBusPtr)->Initialise())
87       {
88         CLog::Log(LOGERROR, "%s - failed to initialise bus %s", __FUNCTION__, PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type()));
89         delete m_busses.at(iBusPtr);
90         m_busses.erase(m_busses.begin() + iBusPtr);
91       }
92     }
93
94     m_bInitialised = true;
95   }
96 }
97
98 void CPeripherals::Clear(void)
99 {
100   CSingleLock lock(m_critSection);
101   /* delete busses and devices */
102   for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
103     delete m_busses.at(iBusPtr);
104   m_busses.clear();
105
106   /* delete mappings */
107   for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
108     m_mappings.at(iMappingPtr).m_settings.clear();
109   m_mappings.clear();
110
111   /* reset class state */
112   m_bIsStarted   = false;
113   m_bInitialised = false;
114 }
115
116 void CPeripherals::TriggerDeviceScan(const PeripheralBusType type /* = PERIPHERAL_BUS_UNKNOWN */)
117 {
118   CSingleLock lock(m_critSection);
119   for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
120   {
121     if (type == PERIPHERAL_BUS_UNKNOWN || m_busses.at(iBusPtr)->Type() == type)
122     {
123       m_busses.at(iBusPtr)->TriggerDeviceScan();
124       if (type != PERIPHERAL_BUS_UNKNOWN)
125         break;
126     }
127   }
128 }
129
130 CPeripheralBus *CPeripherals::GetBusByType(const PeripheralBusType type) const
131 {
132   CPeripheralBus *bus(NULL);
133
134   CSingleLock lock(m_critSection);
135   for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
136   {
137     if (m_busses.at(iBusPtr)->Type() == type)
138     {
139       bus = m_busses.at(iBusPtr);
140       break;
141     }
142   }
143
144   return bus;
145 }
146
147 CPeripheral *CPeripherals::GetPeripheralAtLocation(const CStdString &strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
148 {
149   CPeripheral *peripheral(NULL);
150   CSingleLock lock(m_critSection);
151   for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
152   {
153     /* check whether the bus matches if a bus type other than unknown was passed */
154     if (busType != PERIPHERAL_BUS_UNKNOWN && m_busses.at(iBusPtr)->Type() != busType)
155       continue;
156
157     /* return the first device that matches */
158     if ((peripheral = m_busses.at(iBusPtr)->GetPeripheral(strLocation)) != NULL)
159       break;
160   }
161
162   return peripheral;
163 }
164
165 bool CPeripherals::HasPeripheralAtLocation(const CStdString &strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
166 {
167   return (GetPeripheralAtLocation(strLocation, busType) != NULL);
168 }
169
170 CPeripheralBus *CPeripherals::GetBusWithDevice(const CStdString &strLocation) const
171 {
172   CSingleLock lock(m_critSection);
173   for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
174   {
175     /* return the first bus that matches */
176     if (m_busses.at(iBusPtr)->HasPeripheral(strLocation))
177       return m_busses.at(iBusPtr);
178   }
179
180   return NULL;
181 }
182
183 int CPeripherals::GetPeripheralsWithFeature(vector<CPeripheral *> &results, const PeripheralFeature feature, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
184 {
185   int iReturn(0);
186   CSingleLock lock(m_critSection);
187   for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
188   {
189     /* check whether the bus matches if a bus type other than unknown was passed */
190     if (busType != PERIPHERAL_BUS_UNKNOWN && m_busses.at(iBusPtr)->Type() != busType)
191       continue;
192
193     iReturn += m_busses.at(iBusPtr)->GetPeripheralsWithFeature(results, feature);
194   }
195
196   return iReturn;
197 }
198
199 bool CPeripherals::HasPeripheralWithFeature(const PeripheralFeature feature, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
200 {
201   vector<CPeripheral *> dummy;
202   return (GetPeripheralsWithFeature(dummy, feature, busType) > 0);
203 }
204
205 CPeripheral *CPeripherals::CreatePeripheral(CPeripheralBus &bus, const PeripheralType type, const CStdString &strLocation, int iVendorId /* = 0 */, int iProductId /* = 0 */)
206 {
207   CPeripheral *peripheral = NULL;
208   /* check whether there's something mapped in peripherals.xml */
209   PeripheralType mappedType = type;
210   CStdString strDeviceName;
211   int iMappingPtr = GetMappingForDevice(bus, type, iVendorId, iProductId);
212   bool bHasMapping(iMappingPtr >= 0);
213   if (bHasMapping)
214   {
215     mappedType    = m_mappings[iMappingPtr].m_mappedTo;
216     strDeviceName = m_mappings[iMappingPtr].m_strDeviceName;
217   }
218   else
219   {
220     /* don't create instances for devices that aren't mapped in peripherals.xml */
221     return NULL;
222   }
223
224   switch(mappedType)
225   {
226   case PERIPHERAL_HID:
227     peripheral = new CPeripheralHID(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
228     break;
229
230   case PERIPHERAL_NIC:
231     peripheral = new CPeripheralNIC(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
232     break;
233
234   case PERIPHERAL_DISK:
235     peripheral = new CPeripheralDisk(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
236     break;
237
238   case PERIPHERAL_NYXBOARD:
239     peripheral = new CPeripheralNyxboard(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
240     break;
241
242   case PERIPHERAL_TUNER:
243     peripheral = new CPeripheralTuner(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
244     break;
245
246   case PERIPHERAL_BLUETOOTH:
247     peripheral = new CPeripheralBluetooth(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
248     break;
249
250 #if defined(HAVE_LIBCEC)
251   case PERIPHERAL_CEC:
252     peripheral = new CPeripheralCecAdapter(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
253     break;
254 #endif
255
256   default:
257     break;
258   }
259
260   if (peripheral)
261   {
262     /* try to initialise the new peripheral
263      * Initialise() will make sure that each device is only initialised once */
264     if (peripheral->Initialise())
265     {
266       if (!bHasMapping)
267         peripheral->SetHidden(true);
268       bus.Register(peripheral);
269     }
270     else
271     {
272       CLog::Log(LOGDEBUG, "%s - failed to initialise peripheral on '%s'", __FUNCTION__, strLocation.c_str());
273       delete peripheral;
274       peripheral = NULL;
275     }
276   }
277
278   return peripheral;
279 }
280
281 void CPeripherals::OnDeviceAdded(const CPeripheralBus &bus, const CStdString &strLocation)
282 {
283   CGUIDialogPeripheralManager *dialog = (CGUIDialogPeripheralManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PERIPHERAL_MANAGER);
284   if (dialog && dialog->IsActive())
285     dialog->Update();
286
287   CPeripheral *peripheral = GetByPath(strLocation);
288   if (peripheral)
289     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35005), peripheral->DeviceName());
290 }
291
292 void CPeripherals::OnDeviceDeleted(const CPeripheralBus &bus, const CStdString &strLocation)
293 {
294   CGUIDialogPeripheralManager *dialog = (CGUIDialogPeripheralManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PERIPHERAL_MANAGER);
295   if (dialog && dialog->IsActive())
296     dialog->Update();
297
298   CPeripheral *peripheral = GetByPath(strLocation);
299   if (peripheral)
300     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35006), peripheral->DeviceName());
301 }
302
303 int CPeripherals::GetMappingForDevice(const CPeripheralBus &bus, const PeripheralType classType, int iVendorId, int iProductId) const
304 {
305   /* check all mappings in the order in which they are defined in peripherals.xml */
306   for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
307   {
308     PeripheralDeviceMapping mapping = m_mappings.at(iMappingPtr);
309     bool bBusMatch = (mapping.m_busType == PERIPHERAL_BUS_UNKNOWN || mapping.m_busType == bus.Type());
310     bool bVendorMatch = (mapping.m_iVendorId == 0 || mapping.m_iVendorId == iVendorId);
311     bool bProductMatch = (mapping.m_iProductId == 0 || mapping.m_iProductId == iProductId);
312     bool bClassMatch = (mapping.m_class == PERIPHERAL_UNKNOWN || mapping.m_class == classType);
313
314     if (bBusMatch && bVendorMatch && bProductMatch && bClassMatch)
315     {
316       CLog::Log(LOGDEBUG, "%s - device (%s:%s) mapped to %s (type = %s)", __FUNCTION__, PeripheralTypeTranslator::IntToHexString(iVendorId), PeripheralTypeTranslator::IntToHexString(iProductId), mapping.m_strDeviceName.c_str(), PeripheralTypeTranslator::TypeToString(mapping.m_mappedTo));
317       return iMappingPtr;
318     }
319   }
320
321   return -1;
322 }
323
324 void CPeripherals::GetSettingsFromMapping(CPeripheral &peripheral) const
325 {
326   /* check all mappings in the order in which they are defined in peripherals.xml */
327   for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
328   {
329     const PeripheralDeviceMapping *mapping = &m_mappings.at(iMappingPtr);
330     bool bBusMatch = (mapping->m_busType == PERIPHERAL_BUS_UNKNOWN || mapping->m_busType == peripheral.GetBusType());
331     bool bVendorMatch = (mapping->m_iVendorId == 0 || mapping->m_iVendorId == peripheral.VendorId());
332     bool bProductMatch = (mapping->m_iProductId == 0 || mapping->m_iProductId == peripheral.ProductId());
333     bool bClassMatch = (mapping->m_class == PERIPHERAL_UNKNOWN || mapping->m_class == peripheral.Type());
334
335     if (bBusMatch && bVendorMatch && bProductMatch && bClassMatch)
336     {
337       for (map<CStdString, CSetting *>::const_iterator itr = mapping->m_settings.begin(); itr != mapping->m_settings.end(); itr++)
338         peripheral.AddSetting((*itr).first, (*itr).second);
339     }
340   }
341 }
342
343 bool CPeripherals::LoadMappings(void)
344 {
345   TiXmlDocument xmlDoc;
346   if (!xmlDoc.LoadFile("special://xbmc/system/peripherals.xml"))
347   {
348     CLog::Log(LOGWARNING, "%s - peripherals.xml does not exist", __FUNCTION__);
349     return true;
350   }
351
352   TiXmlElement *pRootElement = xmlDoc.RootElement();
353   if (strcmpi(pRootElement->Value(), "peripherals") != 0)
354   {
355     CLog::Log(LOGERROR, "%s - peripherals.xml does not contain <peripherals>", __FUNCTION__);
356     return false;
357   }
358
359   TiXmlElement *currentNode = pRootElement->FirstChildElement("peripheral");
360   while (currentNode)
361   {
362     PeripheralDeviceMapping mapping;
363
364     mapping.m_iVendorId     = currentNode->Attribute("vendor") ? PeripheralTypeTranslator::HexStringToInt(currentNode->Attribute("vendor")) : 0;
365     mapping.m_iProductId    = currentNode->Attribute("product") ? PeripheralTypeTranslator::HexStringToInt(currentNode->Attribute("product")) : 0;
366     mapping.m_busType       = PeripheralTypeTranslator::GetBusTypeFromString(currentNode->Attribute("bus"));
367     mapping.m_class         = PeripheralTypeTranslator::GetTypeFromString(currentNode->Attribute("class"));
368     mapping.m_strDeviceName = currentNode->Attribute("name") ? CStdString(currentNode->Attribute("name")) : StringUtils::EmptyString;
369     mapping.m_mappedTo      = PeripheralTypeTranslator::GetTypeFromString(currentNode->Attribute("mapTo"));
370     GetSettingsFromMappingsFile(currentNode, mapping.m_settings);
371
372     m_mappings.push_back(mapping);
373     currentNode = currentNode->NextSiblingElement("peripheral");
374   }
375
376   return true;
377 }
378
379 void CPeripherals::GetSettingsFromMappingsFile(TiXmlElement *xmlNode, map<CStdString, CSetting *> &m_settings)
380 {
381   TiXmlElement *currentNode = xmlNode->FirstChildElement("setting");
382   while (currentNode)
383   {
384     CSetting *setting = NULL;
385     CStdString strKey(currentNode->Attribute("key"));
386     if (strKey.IsEmpty())
387       continue;
388
389     CStdString strSettingsType(currentNode->Attribute("type"));
390     int iLabelId = currentNode->Attribute("label") ? atoi(currentNode->Attribute("label")) : -1;
391     bool bConfigurable = (!currentNode->Attribute("configurable") ||
392                           strcmp(currentNode->Attribute("configurable"), "") == 0 ||
393                            (strcmp(currentNode->Attribute("configurable"), "no") != 0 &&
394                             strcmp(currentNode->Attribute("configurable"), "false") != 0 &&
395                             strcmp(currentNode->Attribute("configurable"), "0") != 0));
396     if (strSettingsType.Equals("bool"))
397     {
398       bool bValue = (strcmp(currentNode->Attribute("value"), "no") != 0 &&
399                      strcmp(currentNode->Attribute("value"), "false") != 0 &&
400                      strcmp(currentNode->Attribute("value"), "0") != 0);
401       setting = new CSettingBool(0, strKey, iLabelId, bValue, CHECKMARK_CONTROL);
402     }
403     else if (strSettingsType.Equals("int"))
404     {
405       int iValue = currentNode->Attribute("value") ? atoi(currentNode->Attribute("value")) : 0;
406       int iMin   = currentNode->Attribute("min") ? atoi(currentNode->Attribute("min")) : 0;
407       int iStep  = currentNode->Attribute("step") ? atoi(currentNode->Attribute("step")) : 0;
408       int iMax   = currentNode->Attribute("max") ? atoi(currentNode->Attribute("max")) : 0;
409       CStdString strFormat(currentNode->Attribute("format"));
410       setting = new CSettingInt(0, strKey, iLabelId, iValue, iMin, iStep, iMax, SPIN_CONTROL_INT, strFormat);
411     }
412     else if (strSettingsType.Equals("float"))
413     {
414       float fValue = currentNode->Attribute("value") ? atof(currentNode->Attribute("value")) : 0;
415       float fMin   = currentNode->Attribute("min") ? atof(currentNode->Attribute("min")) : 0;
416       float fStep  = currentNode->Attribute("step") ? atof(currentNode->Attribute("step")) : 0;
417       float fMax   = currentNode->Attribute("max") ? atof(currentNode->Attribute("max")) : 0;
418       setting = new CSettingFloat(0, strKey, iLabelId, fValue, fMin, fStep, fMax, SPIN_CONTROL_FLOAT);
419     }
420     else
421     {
422       CStdString strValue(currentNode->Attribute("value"));
423       setting = new CSettingString(0, strKey, iLabelId, strValue, EDIT_CONTROL_INPUT, !bConfigurable, -1);
424     }
425
426     //TODO add more types if needed
427     setting->SetVisible(bConfigurable);
428     m_settings[strKey] = setting;
429     currentNode = currentNode->NextSiblingElement("setting");
430   }
431 }
432
433 void CPeripherals::GetDirectory(const CStdString &strPath, CFileItemList &items) const
434 {
435   if (!strPath.Left(14).Equals("peripherals://"))
436     return;
437
438   CStdString strPathCut = strPath.Right(strPath.length() - 14);
439   CStdString strBus = strPathCut.Left(strPathCut.Find('/'));
440
441   CSingleLock lock(m_critSection);
442   for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
443   {
444     if (strBus.Equals("all") || strBus.Equals(PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type())))
445       m_busses.at(iBusPtr)->GetDirectory(strPath, items);
446   }
447 }
448
449 CPeripheral *CPeripherals::GetByPath(const CStdString &strPath) const
450 {
451   if (!strPath.Left(14).Equals("peripherals://"))
452     return NULL;
453
454   CStdString strPathCut = strPath.Right(strPath.length() - 14);
455   CStdString strBus = strPathCut.Left(strPathCut.Find('/'));
456
457   CSingleLock lock(m_critSection);
458   for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
459   {
460     if (strBus.Equals(PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type())))
461       return m_busses.at(iBusPtr)->GetByPath(strPath);
462   }
463
464   return NULL;
465 }