only call IPlayerCallback::OnPlayBackSpeedChanged if the speed has actually changed
[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 #ifdef 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 = GetPeripheralAtLocation(strLocation, bus.Type());
208
209   /* only create a new device instances if there's no device at the given location */
210   if (!peripheral)
211   {
212     /* check whether there's something mapped in peripherals.xml */
213     PeripheralType mappedType = type;
214     CStdString strDeviceName;
215     int iMappingPtr = GetMappingForDevice(bus, type, iVendorId, iProductId);
216     bool bHasMapping(iMappingPtr >= 0);
217     if (bHasMapping)
218     {
219       mappedType    = m_mappings[iMappingPtr].m_mappedTo;
220       strDeviceName = m_mappings[iMappingPtr].m_strDeviceName;
221     }
222     else
223     {
224       /* don't create instances for devices that aren't mapped in peripherals.xml */
225       return NULL;
226     }
227
228     switch(mappedType)
229     {
230     case PERIPHERAL_HID:
231       peripheral = new CPeripheralHID(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
232       break;
233
234     case PERIPHERAL_NIC:
235       peripheral = new CPeripheralNIC(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
236       break;
237
238     case PERIPHERAL_DISK:
239       peripheral = new CPeripheralDisk(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
240       break;
241
242     case PERIPHERAL_NYXBOARD:
243       peripheral = new CPeripheralNyxboard(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
244       break;
245
246     case PERIPHERAL_TUNER:
247       peripheral = new CPeripheralTuner(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
248       break;
249
250     case PERIPHERAL_BLUETOOTH:
251       peripheral = new CPeripheralBluetooth(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
252       break;
253
254 #ifdef HAVE_LIBCEC
255     case PERIPHERAL_CEC:
256       peripheral = new CPeripheralCecAdapter(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
257       break;
258 #endif
259
260     default:
261       peripheral = new CPeripheral(type, bus.Type(), strLocation, strDeviceName, iVendorId, iProductId);
262       break;
263     }
264
265     if (peripheral)
266     {
267       /* try to initialise the new peripheral
268        * Initialise() will make sure that each device is only initialised once */
269       if (peripheral->Initialise())
270       {
271         if (!bHasMapping)
272           peripheral->SetHidden(true);
273         bus.Register(peripheral);
274       }
275       else
276       {
277         CLog::Log(LOGDEBUG, "%s - failed to initialise peripheral on '%s'", __FUNCTION__, strLocation.c_str());
278         delete peripheral;
279         peripheral = NULL;
280       }
281     }
282   }
283
284   return peripheral;
285 }
286
287 void CPeripherals::OnDeviceAdded(const CPeripheralBus &bus, const CStdString &strLocation)
288 {
289   CGUIDialogPeripheralManager *dialog = (CGUIDialogPeripheralManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PERIPHERAL_MANAGER);
290   if (dialog && dialog->IsActive())
291     dialog->Update();
292
293   CPeripheral *peripheral = GetByPath(strLocation);
294   if (peripheral)
295     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35005), peripheral->DeviceName());
296 }
297
298 void CPeripherals::OnDeviceDeleted(const CPeripheralBus &bus, const CStdString &strLocation)
299 {
300   CGUIDialogPeripheralManager *dialog = (CGUIDialogPeripheralManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PERIPHERAL_MANAGER);
301   if (dialog && dialog->IsActive())
302     dialog->Update();
303
304   CPeripheral *peripheral = GetByPath(strLocation);
305   if (peripheral)
306     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35006), peripheral->DeviceName());
307 }
308
309 int CPeripherals::GetMappingForDevice(const CPeripheralBus &bus, const PeripheralType classType, int iVendorId, int iProductId) const
310 {
311   /* check all mappings in the order in which they are defined in peripherals.xml */
312   for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
313   {
314     PeripheralDeviceMapping mapping = m_mappings.at(iMappingPtr);
315     bool bBusMatch = (mapping.m_busType == PERIPHERAL_BUS_UNKNOWN || mapping.m_busType == bus.Type());
316     bool bVendorMatch = (mapping.m_iVendorId == 0 || mapping.m_iVendorId == iVendorId);
317     bool bProductMatch = (mapping.m_iProductId == 0 || mapping.m_iProductId == iProductId);
318     bool bClassMatch = (mapping.m_class == PERIPHERAL_UNKNOWN || mapping.m_class == classType);
319
320     if (bBusMatch && bVendorMatch && bProductMatch && bClassMatch)
321     {
322       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));
323       return iMappingPtr;
324     }
325   }
326
327   return -1;
328 }
329
330 void CPeripherals::GetSettingsFromMapping(CPeripheral &peripheral) const
331 {
332   /* check all mappings in the order in which they are defined in peripherals.xml */
333   for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
334   {
335     const PeripheralDeviceMapping *mapping = &m_mappings.at(iMappingPtr);
336     bool bBusMatch = (mapping->m_busType == PERIPHERAL_BUS_UNKNOWN || mapping->m_busType == peripheral.GetBusType());
337     bool bVendorMatch = (mapping->m_iVendorId == 0 || mapping->m_iVendorId == peripheral.VendorId());
338     bool bProductMatch = (mapping->m_iProductId == 0 || mapping->m_iProductId == peripheral.ProductId());
339     bool bClassMatch = (mapping->m_class == PERIPHERAL_UNKNOWN || mapping->m_class == peripheral.Type());
340
341     if (bBusMatch && bVendorMatch && bProductMatch && bClassMatch)
342     {
343       for (map<CStdString, CSetting *>::const_iterator itr = mapping->m_settings.begin(); itr != mapping->m_settings.end(); itr++)
344         peripheral.AddSetting((*itr).first, (*itr).second);
345     }
346   }
347 }
348
349 bool CPeripherals::LoadMappings(void)
350 {
351   TiXmlDocument xmlDoc;
352   if (!xmlDoc.LoadFile("special://xbmc/system/peripherals.xml"))
353   {
354     CLog::Log(LOGWARNING, "%s - peripherals.xml does not exist", __FUNCTION__);
355     return true;
356   }
357
358   TiXmlElement *pRootElement = xmlDoc.RootElement();
359   if (strcmpi(pRootElement->Value(), "peripherals") != 0)
360   {
361     CLog::Log(LOGERROR, "%s - peripherals.xml does not contain <peripherals>", __FUNCTION__);
362     return false;
363   }
364
365   TiXmlElement *currentNode = pRootElement->FirstChildElement("peripheral");
366   while (currentNode)
367   {
368     PeripheralDeviceMapping mapping;
369
370     mapping.m_iVendorId     = currentNode->Attribute("vendor") ? PeripheralTypeTranslator::HexStringToInt(currentNode->Attribute("vendor")) : 0;
371     mapping.m_iProductId    = currentNode->Attribute("product") ? PeripheralTypeTranslator::HexStringToInt(currentNode->Attribute("product")) : 0;
372     mapping.m_busType       = PeripheralTypeTranslator::GetBusTypeFromString(currentNode->Attribute("bus"));
373     mapping.m_class         = PeripheralTypeTranslator::GetTypeFromString(currentNode->Attribute("class"));
374     mapping.m_strDeviceName = currentNode->Attribute("name") ? CStdString(currentNode->Attribute("name")) : StringUtils::EmptyString;
375     mapping.m_mappedTo      = PeripheralTypeTranslator::GetTypeFromString(currentNode->Attribute("mapTo"));
376     GetSettingsFromMappingsFile(currentNode, mapping.m_settings);
377
378     m_mappings.push_back(mapping);
379     currentNode = currentNode->NextSiblingElement("peripheral");
380   }
381
382   return true;
383 }
384
385 void CPeripherals::GetSettingsFromMappingsFile(TiXmlElement *xmlNode, map<CStdString, CSetting *> &m_settings)
386 {
387   TiXmlElement *currentNode = xmlNode->FirstChildElement("setting");
388   while (currentNode)
389   {
390     CSetting *setting = NULL;
391     CStdString strKey(currentNode->Attribute("key"));
392     if (strKey.IsEmpty())
393       continue;
394
395     CStdString strSettingsType(currentNode->Attribute("type"));
396     int iLabelId = currentNode->Attribute("label") ? atoi(currentNode->Attribute("label")) : -1;
397     bool bConfigurable = (!currentNode->Attribute("configurable") ||
398                           strcmp(currentNode->Attribute("configurable"), "") == 0 ||
399                            (strcmp(currentNode->Attribute("configurable"), "no") != 0 &&
400                             strcmp(currentNode->Attribute("configurable"), "false") != 0 &&
401                             strcmp(currentNode->Attribute("configurable"), "0") != 0));
402     if (strSettingsType.Equals("bool"))
403     {
404       bool bValue = (strcmp(currentNode->Attribute("value"), "no") != 0 &&
405                      strcmp(currentNode->Attribute("value"), "false") != 0 &&
406                      strcmp(currentNode->Attribute("value"), "0") != 0);
407       setting = new CSettingBool(0, strKey, iLabelId, bValue, CHECKMARK_CONTROL);
408     }
409     else if (strSettingsType.Equals("int"))
410     {
411       int iValue = currentNode->Attribute("value") ? atoi(currentNode->Attribute("value")) : 0;
412       int iMin   = currentNode->Attribute("min") ? atoi(currentNode->Attribute("min")) : 0;
413       int iStep  = currentNode->Attribute("step") ? atoi(currentNode->Attribute("step")) : 0;
414       int iMax   = currentNode->Attribute("max") ? atoi(currentNode->Attribute("max")) : 0;
415       CStdString strFormat(currentNode->Attribute("format"));
416       setting = new CSettingInt(0, strKey, iLabelId, iValue, iMin, iStep, iMax, SPIN_CONTROL_INT, strFormat);
417     }
418     else if (strSettingsType.Equals("float"))
419     {
420       float fValue = currentNode->Attribute("value") ? atof(currentNode->Attribute("value")) : 0;
421       float fMin   = currentNode->Attribute("min") ? atof(currentNode->Attribute("min")) : 0;
422       float fStep  = currentNode->Attribute("step") ? atof(currentNode->Attribute("step")) : 0;
423       float fMax   = currentNode->Attribute("max") ? atof(currentNode->Attribute("max")) : 0;
424       setting = new CSettingFloat(0, strKey, iLabelId, fValue, fMin, fStep, fMax, SPIN_CONTROL_FLOAT);
425     }
426     else
427     {
428       CStdString strValue(currentNode->Attribute("value"));
429       setting = new CSettingString(0, strKey, iLabelId, strValue, EDIT_CONTROL_INPUT, !bConfigurable, -1);
430     }
431
432     //TODO add more types if needed
433     setting->SetVisible(bConfigurable);
434     m_settings[strKey] = setting;
435     currentNode = currentNode->NextSiblingElement("setting");
436   }
437 }
438
439 void CPeripherals::GetDirectory(const CStdString &strPath, CFileItemList &items) const
440 {
441   if (!strPath.Left(14).Equals("peripherals://"))
442     return;
443
444   CStdString strPathCut = strPath.Right(strPath.length() - 14);
445   CStdString strBus = strPathCut.Left(strPathCut.Find('/'));
446
447   CSingleLock lock(m_critSection);
448   for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
449   {
450     if (strBus.Equals("all") || strBus.Equals(PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type())))
451       m_busses.at(iBusPtr)->GetDirectory(strPath, items);
452   }
453 }
454
455 CPeripheral *CPeripherals::GetByPath(const CStdString &strPath) const
456 {
457   if (!strPath.Left(14).Equals("peripherals://"))
458     return NULL;
459
460   CStdString strPathCut = strPath.Right(strPath.length() - 14);
461   CStdString strBus = strPathCut.Left(strPathCut.Find('/'));
462
463   CSingleLock lock(m_critSection);
464   for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
465   {
466     if (strBus.Equals(PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type())))
467       return m_busses.at(iBusPtr)->GetByPath(strPath);
468   }
469
470   return NULL;
471 }