aboutsummaryrefslogtreecommitdiff
path: root/src/peripherals
diff options
context:
space:
mode:
Diffstat (limited to 'src/peripherals')
-rw-r--r--src/peripherals/Makefile6
-rw-r--r--src/peripherals/PeripheralTypes.h264
-rw-r--r--src/peripherals/Peripherals.cpp727
-rw-r--r--src/peripherals/Peripherals.h229
-rw-r--r--src/peripherals/bus/Makefile.in22
-rw-r--r--src/peripherals/bus/PeripheralBus.cpp316
-rw-r--r--src/peripherals/bus/PeripheralBus.h176
-rw-r--r--src/peripherals/bus/PeripheralBusUSB.h40
-rw-r--r--src/peripherals/bus/linux/PeripheralBusUSBLibUSB.cpp88
-rw-r--r--src/peripherals/bus/linux/PeripheralBusUSBLibUSB.h45
-rw-r--r--src/peripherals/bus/linux/PeripheralBusUSBLibUdev.cpp257
-rw-r--r--src/peripherals/bus/linux/PeripheralBusUSBLibUdev.h54
-rw-r--r--src/peripherals/bus/osx/PeripheralBusUSB.cpp309
-rw-r--r--src/peripherals/bus/osx/PeripheralBusUSB.h57
-rw-r--r--src/peripherals/bus/virtual/PeripheralBusCEC.cpp118
-rw-r--r--src/peripherals/bus/virtual/PeripheralBusCEC.h57
-rw-r--r--src/peripherals/bus/win32/PeripheralBusUSB.cpp179
-rw-r--r--src/peripherals/bus/win32/PeripheralBusUSB.h44
-rw-r--r--src/peripherals/devices/Makefile.in17
-rw-r--r--src/peripherals/devices/Peripheral.cpp538
-rw-r--r--src/peripherals/devices/Peripheral.h189
-rw-r--r--src/peripherals/devices/PeripheralBluetooth.cpp30
-rw-r--r--src/peripherals/devices/PeripheralBluetooth.h32
-rw-r--r--src/peripherals/devices/PeripheralCecAdapter.cpp1746
-rw-r--r--src/peripherals/devices/PeripheralCecAdapter.h209
-rw-r--r--src/peripherals/devices/PeripheralDisk.cpp31
-rw-r--r--src/peripherals/devices/PeripheralDisk.h32
-rw-r--r--src/peripherals/devices/PeripheralHID.cpp92
-rw-r--r--src/peripherals/devices/PeripheralHID.h39
-rw-r--r--src/peripherals/devices/PeripheralImon.cpp117
-rw-r--r--src/peripherals/devices/PeripheralImon.h47
-rw-r--r--src/peripherals/devices/PeripheralNIC.cpp33
-rw-r--r--src/peripherals/devices/PeripheralNIC.h32
-rw-r--r--src/peripherals/devices/PeripheralNyxboard.cpp64
-rw-r--r--src/peripherals/devices/PeripheralNyxboard.h33
-rw-r--r--src/peripherals/devices/PeripheralTuner.cpp30
-rw-r--r--src/peripherals/devices/PeripheralTuner.h32
-rw-r--r--src/peripherals/dialogs/GUIDialogPeripheralManager.cpp197
-rw-r--r--src/peripherals/dialogs/GUIDialogPeripheralManager.h58
-rw-r--r--src/peripherals/dialogs/GUIDialogPeripheralSettings.cpp231
-rw-r--r--src/peripherals/dialogs/GUIDialogPeripheralSettings.h55
-rw-r--r--src/peripherals/dialogs/Makefile7
42 files changed, 6879 insertions, 0 deletions
diff --git a/src/peripherals/Makefile b/src/peripherals/Makefile
new file mode 100644
index 0000000000..754a2c5de3
--- /dev/null
+++ b/src/peripherals/Makefile
@@ -0,0 +1,6 @@
+SRCS=Peripherals.cpp
+
+LIB=peripherals.a
+
+include ../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/src/peripherals/PeripheralTypes.h b/src/peripherals/PeripheralTypes.h
new file mode 100644
index 0000000000..e92fd41140
--- /dev/null
+++ b/src/peripherals/PeripheralTypes.h
@@ -0,0 +1,264 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+#include <stdio.h>
+#ifdef TARGET_WINDOWS
+#include "PlatformDefs.h"
+#endif
+#include "utils/StdString.h"
+#include "utils/StringUtils.h"
+
+class CSetting;
+
+namespace PERIPHERALS
+{
+ enum PeripheralBusType
+ {
+ PERIPHERAL_BUS_UNKNOWN = 0,
+ PERIPHERAL_BUS_USB,
+ PERIPHERAL_BUS_PCI,
+ PERIPHERAL_BUS_RPI,
+ PERIPHERAL_BUS_CEC
+ };
+
+ enum PeripheralFeature
+ {
+ FEATURE_UNKNOWN = 0,
+ FEATURE_HID,
+ FEATURE_NIC,
+ FEATURE_DISK,
+ FEATURE_NYXBOARD,
+ FEATURE_CEC,
+ FEATURE_BLUETOOTH,
+ FEATURE_TUNER,
+ FEATURE_IMON
+ };
+
+ enum PeripheralType
+ {
+ PERIPHERAL_UNKNOWN = 0,
+ PERIPHERAL_HID,
+ PERIPHERAL_NIC,
+ PERIPHERAL_DISK,
+ PERIPHERAL_NYXBOARD,
+ PERIPHERAL_CEC,
+ PERIPHERAL_BLUETOOTH,
+ PERIPHERAL_TUNER,
+ PERIPHERAL_IMON
+ };
+
+ struct PeripheralID
+ {
+ int m_iVendorId;
+ int m_iProductId;
+ };
+
+ struct PeripheralDeviceSetting
+ {
+ CSetting* m_setting;
+ int m_order;
+ };
+
+ struct PeripheralDeviceMapping
+ {
+ std::vector<PeripheralID> m_PeripheralID;
+ PeripheralBusType m_busType;
+ PeripheralType m_class;
+ CStdString m_strDeviceName;
+ PeripheralType m_mappedTo;
+ std::map<CStdString, PeripheralDeviceSetting> m_settings;
+ };
+
+ class PeripheralTypeTranslator
+ {
+ public:
+ static const char *TypeToString(const PeripheralType type)
+ {
+ switch (type)
+ {
+ case PERIPHERAL_BLUETOOTH:
+ return "bluetooth";
+ case PERIPHERAL_CEC:
+ return "cec";
+ case PERIPHERAL_DISK:
+ return "disk";
+ case PERIPHERAL_HID:
+ return "hid";
+ case PERIPHERAL_NIC:
+ return "nic";
+ case PERIPHERAL_NYXBOARD:
+ return "nyxboard";
+ case PERIPHERAL_TUNER:
+ return "tuner";
+ case PERIPHERAL_IMON:
+ return "imon";
+ default:
+ return "unknown";
+ }
+ };
+
+ static PeripheralType GetTypeFromString(const CStdString &strType)
+ {
+ CStdString strTypeLowerCase(strType);
+ StringUtils::ToLower(strTypeLowerCase);
+
+ if (strTypeLowerCase.Equals("bluetooth"))
+ return PERIPHERAL_BLUETOOTH;
+ else if (strTypeLowerCase.Equals("cec"))
+ return PERIPHERAL_CEC;
+ else if (strTypeLowerCase.Equals("disk"))
+ return PERIPHERAL_DISK;
+ else if (strTypeLowerCase.Equals("hid"))
+ return PERIPHERAL_HID;
+ else if (strTypeLowerCase.Equals("nic"))
+ return PERIPHERAL_NIC;
+ else if (strTypeLowerCase.Equals("nyxboard"))
+ return PERIPHERAL_NYXBOARD;
+ else if (strTypeLowerCase.Equals("tuner"))
+ return PERIPHERAL_TUNER;
+ else if (strTypeLowerCase.Equals("imon"))
+ return PERIPHERAL_IMON;
+
+ return PERIPHERAL_UNKNOWN;
+ };
+
+ static const char *BusTypeToString(const PeripheralBusType type)
+ {
+ switch(type)
+ {
+ case PERIPHERAL_BUS_USB:
+ return "usb";
+ case PERIPHERAL_BUS_PCI:
+ return "pci";
+ case PERIPHERAL_BUS_RPI:
+ return "rpi";
+ case PERIPHERAL_BUS_CEC:
+ return "cec";
+ default:
+ return "unknown";
+ }
+ };
+
+ static PeripheralBusType GetBusTypeFromString(const CStdString &strType)
+ {
+ CStdString strTypeLowerCase(strType);
+ StringUtils::ToLower(strTypeLowerCase);
+
+ if (strTypeLowerCase.Equals("usb"))
+ return PERIPHERAL_BUS_USB;
+ else if (strTypeLowerCase.Equals("pci"))
+ return PERIPHERAL_BUS_PCI;
+ else if (strTypeLowerCase.Equals("rpi"))
+ return PERIPHERAL_BUS_RPI;
+ else if (strTypeLowerCase.Equals("cec"))
+ return PERIPHERAL_BUS_CEC;
+
+ return PERIPHERAL_BUS_UNKNOWN;
+ };
+
+ static int HexStringToInt(const char *strHex)
+ {
+ int iVal;
+ sscanf(strHex, "%x", &iVal);
+ return iVal;
+ };
+
+ static void FormatHexString(int iVal, CStdString &strHexString)
+ {
+ if (iVal < 0)
+ iVal = 0;
+ if (iVal > 65536)
+ iVal = 65536;
+
+ strHexString = StringUtils::Format("%04X", iVal);
+ };
+ };
+
+ class PeripheralScanResult
+ {
+ public:
+ PeripheralScanResult(const PeripheralBusType busType) :
+ m_type(PERIPHERAL_UNKNOWN),
+ m_iVendorId(0),
+ m_iProductId(0),
+ m_mappedType(PERIPHERAL_UNKNOWN),
+ m_busType(busType),
+ m_mappedBusType(busType),
+ m_iSequence(0) {}
+
+ PeripheralScanResult(void) :
+ m_type(PERIPHERAL_UNKNOWN),
+ m_iVendorId(0),
+ m_iProductId(0),
+ m_mappedType(PERIPHERAL_UNKNOWN),
+ m_busType(PERIPHERAL_BUS_UNKNOWN),
+ m_mappedBusType(PERIPHERAL_BUS_UNKNOWN),
+ m_iSequence(0) {}
+
+ bool operator ==(const PeripheralScanResult& right) const
+ {
+ return m_iVendorId == right.m_iVendorId &&
+ m_iProductId == right.m_iProductId &&
+ m_type == right.m_type &&
+ m_busType == right.m_busType &&
+ m_strLocation.Equals(right.m_strLocation);
+ }
+
+ bool operator !=(const PeripheralScanResult& right) const
+ {
+ return !(*this == right);
+ }
+
+ PeripheralType m_type;
+ CStdString m_strLocation;
+ int m_iVendorId;
+ int m_iProductId;
+ PeripheralType m_mappedType;
+ CStdString m_strDeviceName;
+ PeripheralBusType m_busType;
+ PeripheralBusType m_mappedBusType;
+ unsigned int m_iSequence; // when more than one adapter of the same type is found
+ };
+
+ struct PeripheralScanResults
+ {
+ bool GetDeviceOnLocation(const CStdString& strLocation, PeripheralScanResult* result) const
+ {
+ for (std::vector<PeripheralScanResult>::const_iterator it = m_results.begin(); it != m_results.end(); it++)
+ {
+ if ((*it).m_strLocation == strLocation)
+ {
+ *result = *it;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool ContainsResult(const PeripheralScanResult& result) const
+ {
+ return std::find(m_results.begin(), m_results.end(), result) != m_results.end();
+ }
+
+ std::vector<PeripheralScanResult> m_results;
+ };
+}
diff --git a/src/peripherals/Peripherals.cpp b/src/peripherals/Peripherals.cpp
new file mode 100644
index 0000000000..557ee351f4
--- /dev/null
+++ b/src/peripherals/Peripherals.cpp
@@ -0,0 +1,727 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Peripherals.h"
+#include "bus/PeripheralBus.h"
+#include "devices/PeripheralBluetooth.h"
+#include "devices/PeripheralDisk.h"
+#include "devices/PeripheralHID.h"
+#include "devices/PeripheralNIC.h"
+#include "devices/PeripheralNyxboard.h"
+#include "devices/PeripheralTuner.h"
+#include "devices/PeripheralCecAdapter.h"
+#include "devices/PeripheralImon.h"
+#include "bus/PeripheralBusUSB.h"
+#include "dialogs/GUIDialogPeripheralManager.h"
+
+#if defined(HAVE_LIBCEC)
+#include "bus/virtual/PeripheralBusCEC.h"
+#endif
+
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+#include "utils/XMLUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "filesystem/Directory.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "GUIUserMessages.h"
+#include "utils/StringUtils.h"
+#include "Util.h"
+#include "guilib/Key.h"
+#include "settings/lib/Setting.h"
+
+using namespace PERIPHERALS;
+using namespace XFILE;
+using namespace std;
+
+CPeripherals::CPeripherals(void)
+{
+ Clear();
+}
+
+CPeripherals::~CPeripherals(void)
+{
+ Clear();
+}
+
+CPeripherals &CPeripherals::Get(void)
+{
+ static CPeripherals peripheralsInstance;
+ return peripheralsInstance;
+}
+
+void CPeripherals::Initialise(void)
+{
+ CSingleLock lock(m_critSection);
+ if (!m_bIsStarted)
+ {
+ m_bIsStarted = true;
+
+ CDirectory::Create("special://profile/peripheral_data");
+
+ /* load mappings from peripherals.xml */
+ LoadMappings();
+
+#if defined(HAVE_PERIPHERAL_BUS_USB)
+ m_busses.push_back(new CPeripheralBusUSB(this));
+#endif
+#if defined(HAVE_LIBCEC)
+ m_busses.push_back(new CPeripheralBusCEC(this));
+#endif
+
+ /* initialise all known busses */
+ for (int iBusPtr = (int)m_busses.size() - 1; iBusPtr >= 0; iBusPtr--)
+ {
+ if (!m_busses.at(iBusPtr)->Initialise())
+ {
+ CLog::Log(LOGERROR, "%s - failed to initialise bus %s", __FUNCTION__, PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type()));
+ delete m_busses.at(iBusPtr);
+ m_busses.erase(m_busses.begin() + iBusPtr);
+ }
+ }
+
+ m_bInitialised = true;
+ }
+}
+
+void CPeripherals::Clear(void)
+{
+ CSingleLock lock(m_critSection);
+ /* delete busses and devices */
+ for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
+ delete m_busses.at(iBusPtr);
+ m_busses.clear();
+
+ /* delete mappings */
+ for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
+ {
+ map<CStdString, PeripheralDeviceSetting> settings = m_mappings.at(iMappingPtr).m_settings;
+ for (map<CStdString, PeripheralDeviceSetting>::iterator itr = settings.begin(); itr != settings.end(); ++itr)
+ delete itr->second.m_setting;
+ m_mappings.at(iMappingPtr).m_settings.clear();
+ }
+ m_mappings.clear();
+
+ /* reset class state */
+ m_bIsStarted = false;
+ m_bInitialised = false;
+#if !defined(HAVE_LIBCEC)
+ m_bMissingLibCecWarningDisplayed = false;
+#endif
+}
+
+void CPeripherals::TriggerDeviceScan(const PeripheralBusType type /* = PERIPHERAL_BUS_UNKNOWN */)
+{
+ CSingleLock lock(m_critSection);
+ for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
+ {
+ if (type == PERIPHERAL_BUS_UNKNOWN || m_busses.at(iBusPtr)->Type() == type)
+ {
+ m_busses.at(iBusPtr)->TriggerDeviceScan();
+ if (type != PERIPHERAL_BUS_UNKNOWN)
+ break;
+ }
+ }
+}
+
+CPeripheralBus *CPeripherals::GetBusByType(const PeripheralBusType type) const
+{
+ CSingleLock lock(m_critSection);
+ CPeripheralBus *bus(NULL);
+ for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
+ {
+ if (m_busses.at(iBusPtr)->Type() == type)
+ {
+ bus = m_busses.at(iBusPtr);
+ break;
+ }
+ }
+
+ return bus;
+}
+
+CPeripheral *CPeripherals::GetPeripheralAtLocation(const CStdString &strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
+{
+ CSingleLock lock(m_critSection);
+ CPeripheral *peripheral(NULL);
+ for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
+ {
+ /* check whether the bus matches if a bus type other than unknown was passed */
+ if (busType != PERIPHERAL_BUS_UNKNOWN && m_busses.at(iBusPtr)->Type() != busType)
+ continue;
+
+ /* return the first device that matches */
+ if ((peripheral = m_busses.at(iBusPtr)->GetPeripheral(strLocation)) != NULL)
+ break;
+ }
+
+ return peripheral;
+}
+
+bool CPeripherals::HasPeripheralAtLocation(const CStdString &strLocation, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
+{
+ return (GetPeripheralAtLocation(strLocation, busType) != NULL);
+}
+
+CPeripheralBus *CPeripherals::GetBusWithDevice(const CStdString &strLocation) const
+{
+ CSingleLock lock(m_critSection);
+ for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
+ {
+ /* return the first bus that matches */
+ if (m_busses.at(iBusPtr)->HasPeripheral(strLocation))
+ return m_busses.at(iBusPtr);
+ }
+
+ return NULL;
+}
+
+int CPeripherals::GetPeripheralsWithFeature(vector<CPeripheral *> &results, const PeripheralFeature feature, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
+{
+ CSingleLock lock(m_critSection);
+ int iReturn(0);
+ for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
+ {
+ /* check whether the bus matches if a bus type other than unknown was passed */
+ if (busType != PERIPHERAL_BUS_UNKNOWN && m_busses.at(iBusPtr)->Type() != busType)
+ continue;
+
+ iReturn += m_busses.at(iBusPtr)->GetPeripheralsWithFeature(results, feature);
+ }
+
+ return iReturn;
+}
+
+size_t CPeripherals::GetNumberOfPeripherals() const
+{
+ size_t iReturn(0);
+ CSingleLock lock(m_critSection);
+ for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
+ {
+ iReturn += m_busses.at(iBusPtr)->GetNumberOfPeripherals();
+ }
+
+ return iReturn;
+}
+
+bool CPeripherals::HasPeripheralWithFeature(const PeripheralFeature feature, PeripheralBusType busType /* = PERIPHERAL_BUS_UNKNOWN */) const
+{
+ vector<CPeripheral *> dummy;
+ return (GetPeripheralsWithFeature(dummy, feature, busType) > 0);
+}
+
+CPeripheral *CPeripherals::CreatePeripheral(CPeripheralBus &bus, const PeripheralScanResult& result)
+{
+ CPeripheral *peripheral = NULL;
+ PeripheralScanResult mappedResult = result;
+ if (mappedResult.m_busType == PERIPHERAL_BUS_UNKNOWN)
+ mappedResult.m_busType = bus.Type();
+
+ /* check whether there's something mapped in peripherals.xml */
+ if (!GetMappingForDevice(bus, mappedResult))
+ {
+ /* don't create instances for devices that aren't mapped in peripherals.xml */
+ return NULL;
+ }
+
+ switch(mappedResult.m_mappedType)
+ {
+ case PERIPHERAL_HID:
+ peripheral = new CPeripheralHID(mappedResult);
+ break;
+
+ case PERIPHERAL_NIC:
+ peripheral = new CPeripheralNIC(mappedResult);
+ break;
+
+ case PERIPHERAL_DISK:
+ peripheral = new CPeripheralDisk(mappedResult);
+ break;
+
+ case PERIPHERAL_NYXBOARD:
+ peripheral = new CPeripheralNyxboard(mappedResult);
+ break;
+
+ case PERIPHERAL_TUNER:
+ peripheral = new CPeripheralTuner(mappedResult);
+ break;
+
+ case PERIPHERAL_BLUETOOTH:
+ peripheral = new CPeripheralBluetooth(mappedResult);
+ break;
+
+ case PERIPHERAL_CEC:
+#if defined(HAVE_LIBCEC)
+ if (bus.Type() == PERIPHERAL_BUS_CEC)
+ peripheral = new CPeripheralCecAdapter(mappedResult);
+#else
+ if (!m_bMissingLibCecWarningDisplayed)
+ {
+ m_bMissingLibCecWarningDisplayed = true;
+ CLog::Log(LOGWARNING, "%s - libCEC support has not been compiled in, so the CEC adapter cannot be used.", __FUNCTION__);
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(36000), g_localizeStrings.Get(36017));
+ }
+#endif
+ break;
+
+ case PERIPHERAL_IMON:
+ peripheral = new CPeripheralImon(mappedResult);
+ break;
+
+ default:
+ break;
+ }
+
+ if (peripheral)
+ {
+ /* try to initialise the new peripheral
+ * Initialise() will make sure that each device is only initialised once */
+ if (peripheral->Initialise())
+ {
+ bus.Register(peripheral);
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "%s - failed to initialise peripheral on '%s'", __FUNCTION__, mappedResult.m_strLocation.c_str());
+ delete peripheral;
+ peripheral = NULL;
+ }
+ }
+
+ return peripheral;
+}
+
+void CPeripherals::OnDeviceAdded(const CPeripheralBus &bus, const CPeripheral &peripheral)
+{
+ CGUIDialogPeripheralManager *dialog = (CGUIDialogPeripheralManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PERIPHERAL_MANAGER);
+ if (dialog && dialog->IsActive())
+ dialog->Update();
+
+ // refresh settings (peripherals manager could be enabled now)
+ CGUIMessage msg(GUI_MSG_UPDATE, WINDOW_SETTINGS_SYSTEM, 0);
+ g_windowManager.SendThreadMessage(msg, WINDOW_SETTINGS_SYSTEM);
+
+ SetChanged();
+
+ // don't show a notification for devices detected during the initial scan
+ if (bus.IsInitialised())
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35005), peripheral.DeviceName());
+}
+
+void CPeripherals::OnDeviceDeleted(const CPeripheralBus &bus, const CPeripheral &peripheral)
+{
+ CGUIDialogPeripheralManager *dialog = (CGUIDialogPeripheralManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PERIPHERAL_MANAGER);
+ if (dialog && dialog->IsActive())
+ dialog->Update();
+
+ // refresh settings (peripherals manager could be disabled now)
+ CGUIMessage msg(GUI_MSG_UPDATE, WINDOW_SETTINGS_SYSTEM, 0);
+ g_windowManager.SendThreadMessage(msg, WINDOW_SETTINGS_SYSTEM);
+
+ SetChanged();
+
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(35006), peripheral.DeviceName());
+}
+
+bool CPeripherals::GetMappingForDevice(const CPeripheralBus &bus, PeripheralScanResult& result) const
+{
+ /* check all mappings in the order in which they are defined in peripherals.xml */
+ for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
+ {
+ PeripheralDeviceMapping mapping = m_mappings.at(iMappingPtr);
+
+ bool bProductMatch = false;
+ if (mapping.m_PeripheralID.size() == 0)
+ {
+ bProductMatch = true;
+ }
+ else
+ {
+ for (unsigned int i = 0; i < mapping.m_PeripheralID.size(); i++)
+ if (mapping.m_PeripheralID[i].m_iVendorId == result.m_iVendorId && mapping.m_PeripheralID[i].m_iProductId == result.m_iProductId)
+ bProductMatch = true;
+ }
+
+ bool bBusMatch = (mapping.m_busType == PERIPHERAL_BUS_UNKNOWN || mapping.m_busType == bus.Type());
+ bool bClassMatch = (mapping.m_class == PERIPHERAL_UNKNOWN || mapping.m_class == result.m_type);
+
+ if (bProductMatch && bBusMatch && bClassMatch)
+ {
+ CStdString strVendorId, strProductId;
+ PeripheralTypeTranslator::FormatHexString(result.m_iVendorId, strVendorId);
+ PeripheralTypeTranslator::FormatHexString(result.m_iProductId, strProductId);
+ CLog::Log(LOGDEBUG, "%s - device (%s:%s) mapped to %s (type = %s)", __FUNCTION__, strVendorId.c_str(), strProductId.c_str(), mapping.m_strDeviceName.c_str(), PeripheralTypeTranslator::TypeToString(mapping.m_mappedTo));
+ result.m_mappedType = m_mappings[iMappingPtr].m_mappedTo;
+ result.m_strDeviceName = m_mappings[iMappingPtr].m_strDeviceName;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void CPeripherals::GetSettingsFromMapping(CPeripheral &peripheral) const
+{
+ /* check all mappings in the order in which they are defined in peripherals.xml */
+ for (unsigned int iMappingPtr = 0; iMappingPtr < m_mappings.size(); iMappingPtr++)
+ {
+ const PeripheralDeviceMapping *mapping = &m_mappings.at(iMappingPtr);
+
+ bool bProductMatch = false;
+ if (mapping->m_PeripheralID.size() == 0)
+ {
+ bProductMatch = true;
+ }
+ else
+ {
+ for (unsigned int i = 0; i < mapping->m_PeripheralID.size(); i++)
+ if (mapping->m_PeripheralID[i].m_iVendorId == peripheral.VendorId() && mapping->m_PeripheralID[i].m_iProductId == peripheral.ProductId())
+ bProductMatch = true;
+ }
+
+ bool bBusMatch = (mapping->m_busType == PERIPHERAL_BUS_UNKNOWN || mapping->m_busType == peripheral.GetBusType());
+ bool bClassMatch = (mapping->m_class == PERIPHERAL_UNKNOWN || mapping->m_class == peripheral.Type());
+
+ if (bBusMatch && bProductMatch && bClassMatch)
+ {
+ for (map<CStdString, PeripheralDeviceSetting>::const_iterator itr = mapping->m_settings.begin(); itr != mapping->m_settings.end(); ++itr)
+ peripheral.AddSetting((*itr).first, (*itr).second.m_setting, (*itr).second.m_order);
+ }
+ }
+}
+
+bool CPeripherals::LoadMappings(void)
+{
+ CXBMCTinyXML xmlDoc;
+ if (!xmlDoc.LoadFile("special://xbmc/system/peripherals.xml"))
+ {
+ CLog::Log(LOGWARNING, "%s - peripherals.xml does not exist", __FUNCTION__);
+ return true;
+ }
+
+ TiXmlElement *pRootElement = xmlDoc.RootElement();
+ if (strcmpi(pRootElement->Value(), "peripherals") != 0)
+ {
+ CLog::Log(LOGERROR, "%s - peripherals.xml does not contain <peripherals>", __FUNCTION__);
+ return false;
+ }
+
+ for (TiXmlElement *currentNode = pRootElement->FirstChildElement("peripheral"); currentNode; currentNode = currentNode->NextSiblingElement("peripheral"))
+ {
+ PeripheralID id;
+ PeripheralDeviceMapping mapping;
+
+ mapping.m_strDeviceName = XMLUtils::GetAttribute(currentNode, "name");
+
+ // If there is no vendor_product attribute ignore this entry
+ if (currentNode->Attribute("vendor_product"))
+ {
+ // The vendor_product attribute is a list of comma separated vendor:product pairs
+ vector<string> vpArray = StringUtils::Split(currentNode->Attribute("vendor_product"), ",");
+ for (vector<string>::const_iterator i = vpArray.begin(); i != vpArray.end(); ++i)
+ {
+ vector<string> idArray = StringUtils::Split(*i, ":");
+ if (idArray.size() != 2)
+ {
+ CLog::Log(LOGERROR, "%s - ignoring node \"%s\" with invalid vendor_product attribute", __FUNCTION__, mapping.m_strDeviceName.c_str());
+ continue;
+ }
+
+ id.m_iVendorId = PeripheralTypeTranslator::HexStringToInt(idArray[0].c_str());
+ id.m_iProductId = PeripheralTypeTranslator::HexStringToInt(idArray[1].c_str());
+ mapping.m_PeripheralID.push_back(id);
+ }
+ }
+
+ mapping.m_busType = PeripheralTypeTranslator::GetBusTypeFromString(XMLUtils::GetAttribute(currentNode, "bus"));
+ mapping.m_class = PeripheralTypeTranslator::GetTypeFromString(XMLUtils::GetAttribute(currentNode, "class"));
+ mapping.m_mappedTo = PeripheralTypeTranslator::GetTypeFromString(XMLUtils::GetAttribute(currentNode, "mapTo"));
+ GetSettingsFromMappingsFile(currentNode, mapping.m_settings);
+
+ m_mappings.push_back(mapping);
+ CLog::Log(LOGDEBUG, "%s - loaded node \"%s\"", __FUNCTION__, mapping.m_strDeviceName.c_str());
+ }
+
+ return true;
+}
+
+void CPeripherals::GetSettingsFromMappingsFile(TiXmlElement *xmlNode, map<CStdString, PeripheralDeviceSetting> &settings)
+{
+ TiXmlElement *currentNode = xmlNode->FirstChildElement("setting");
+ int iMaxOrder = 0;
+
+ while (currentNode)
+ {
+ CSetting *setting = NULL;
+ CStdString strKey = XMLUtils::GetAttribute(currentNode, "key");
+ if (strKey.empty())
+ continue;
+
+ CStdString strSettingsType = XMLUtils::GetAttribute(currentNode, "type");
+ int iLabelId = currentNode->Attribute("label") ? atoi(currentNode->Attribute("label")) : -1;
+ const std::string config = XMLUtils::GetAttribute(currentNode, "configurable");
+ bool bConfigurable = (config.empty() || (config != "no" && config != "false" && config != "0"));
+ if (strSettingsType.Equals("bool"))
+ {
+ const std::string value = XMLUtils::GetAttribute(currentNode, "value");
+ bool bValue = (value != "no" && value != "false" && value != "0");
+ setting = new CSettingBool(strKey, iLabelId, bValue);
+ }
+ else if (strSettingsType.Equals("int"))
+ {
+ int iValue = currentNode->Attribute("value") ? atoi(currentNode->Attribute("value")) : 0;
+ int iMin = currentNode->Attribute("min") ? atoi(currentNode->Attribute("min")) : 0;
+ int iStep = currentNode->Attribute("step") ? atoi(currentNode->Attribute("step")) : 1;
+ int iMax = currentNode->Attribute("max") ? atoi(currentNode->Attribute("max")) : 255;
+ setting = new CSettingInt(strKey, iLabelId, iValue, iMin, iStep, iMax);
+ }
+ else if (strSettingsType.Equals("float"))
+ {
+ float fValue = currentNode->Attribute("value") ? (float) atof(currentNode->Attribute("value")) : 0;
+ float fMin = currentNode->Attribute("min") ? (float) atof(currentNode->Attribute("min")) : 0;
+ float fStep = currentNode->Attribute("step") ? (float) atof(currentNode->Attribute("step")) : 0;
+ float fMax = currentNode->Attribute("max") ? (float) atof(currentNode->Attribute("max")) : 0;
+ setting = new CSettingNumber(strKey, iLabelId, fValue, fMin, fStep, fMax);
+ }
+ else if (strSettingsType.Equals("enum"))
+ {
+ CStdString strEnums = XMLUtils::GetAttribute(currentNode, "lvalues");
+ if (!strEnums.empty())
+ {
+ vector< pair<int,int> > enums;
+ vector<std::string> valuesVec;
+ StringUtils::Tokenize(strEnums, valuesVec, "|");
+ for (unsigned int i = 0; i < valuesVec.size(); i++)
+ enums.push_back(make_pair(atoi(valuesVec[i].c_str()), atoi(valuesVec[i].c_str())));
+ int iValue = currentNode->Attribute("value") ? atoi(currentNode->Attribute("value")) : 0;
+ setting = new CSettingInt(strKey, iLabelId, iValue, enums);
+ }
+ }
+ else
+ {
+ CStdString strValue = XMLUtils::GetAttribute(currentNode, "value");
+ setting = new CSettingString(strKey, iLabelId, strValue);
+ }
+
+ if (setting)
+ {
+ //TODO add more types if needed
+
+ /* set the visibility */
+ setting->SetVisible(bConfigurable);
+
+ /* set the order */
+ int iOrder = 0;
+ currentNode->Attribute("order", &iOrder);
+ /* if the order attribute is invalid or 0, then the setting will be added at the end */
+ if (iOrder < 0)
+ iOrder = 0;
+ if (iOrder > iMaxOrder)
+ iMaxOrder = iOrder;
+
+ /* and add this new setting */
+ PeripheralDeviceSetting deviceSetting = { setting, iOrder };
+ settings[strKey] = deviceSetting;
+ }
+
+ currentNode = currentNode->NextSiblingElement("setting");
+ }
+
+ /* add the settings without an order attribute or an invalid order attribute set at the end */
+ for (map<CStdString, PeripheralDeviceSetting>::iterator it = settings.begin(); it != settings.end(); ++it)
+ {
+ if (it->second.m_order == 0)
+ it->second.m_order = ++iMaxOrder;
+ }
+}
+
+void CPeripherals::GetDirectory(const CStdString &strPath, CFileItemList &items) const
+{
+ if (!StringUtils::StartsWithNoCase(strPath, "peripherals://"))
+ return;
+
+ CStdString strPathCut = strPath.substr(14);
+ CStdString strBus = strPathCut.substr(0, strPathCut.find('/'));
+
+ CSingleLock lock(m_critSection);
+ for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
+ {
+ if (strBus.Equals("all") || strBus.Equals(PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type())))
+ m_busses.at(iBusPtr)->GetDirectory(strPath, items);
+ }
+}
+
+CPeripheral *CPeripherals::GetByPath(const CStdString &strPath) const
+{
+ if (!StringUtils::StartsWithNoCase(strPath, "peripherals://"))
+ return NULL;
+
+ CStdString strPathCut = strPath.substr(14);
+ CStdString strBus = strPathCut.substr(0, strPathCut.find('/'));
+
+ CSingleLock lock(m_critSection);
+ for (unsigned int iBusPtr = 0; iBusPtr < m_busses.size(); iBusPtr++)
+ {
+ if (strBus.Equals(PeripheralTypeTranslator::BusTypeToString(m_busses.at(iBusPtr)->Type())))
+ return m_busses.at(iBusPtr)->GetByPath(strPath);
+ }
+
+ return NULL;
+}
+
+bool CPeripherals::OnAction(const CAction &action)
+{
+ if (action.GetID() == ACTION_MUTE)
+ {
+ return ToggleMute();
+ }
+
+ if (SupportsCEC() && action.GetAmount() && (action.GetID() == ACTION_VOLUME_UP || action.GetID() == ACTION_VOLUME_DOWN))
+ {
+ vector<CPeripheral *> peripherals;
+ if (GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
+ {
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < peripherals.size(); iPeripheralPtr++)
+ {
+ CPeripheralCecAdapter *cecDevice = (CPeripheralCecAdapter *) peripherals.at(iPeripheralPtr);
+ if (cecDevice && cecDevice->HasAudioControl())
+ {
+ if (action.GetID() == ACTION_VOLUME_UP)
+ cecDevice->VolumeUp();
+ else
+ cecDevice->VolumeDown();
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool CPeripherals::IsMuted(void)
+{
+ vector<CPeripheral *> peripherals;
+ if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
+ {
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < peripherals.size(); iPeripheralPtr++)
+ {
+ CPeripheralCecAdapter *cecDevice = (CPeripheralCecAdapter *) peripherals.at(iPeripheralPtr);
+ if (cecDevice && cecDevice->IsMuted())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool CPeripherals::ToggleMute(void)
+{
+ vector<CPeripheral *> peripherals;
+ if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
+ {
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < peripherals.size(); iPeripheralPtr++)
+ {
+ CPeripheralCecAdapter *cecDevice = (CPeripheralCecAdapter *) peripherals.at(iPeripheralPtr);
+ if (cecDevice && cecDevice->HasAudioControl())
+ {
+ cecDevice->ToggleMute();
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool CPeripherals::ToggleDeviceState(CecStateChange mode /*= STATE_SWITCH_TOGGLE */, unsigned int iPeripheral /*= 0 */)
+{
+ bool ret(false);
+ vector<CPeripheral *> peripherals;
+
+ if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
+ {
+ for (unsigned int iPeripheralPtr = iPeripheral; iPeripheralPtr < peripherals.size(); iPeripheralPtr++)
+ {
+ CPeripheralCecAdapter *cecDevice = (CPeripheralCecAdapter *) peripherals.at(iPeripheralPtr);
+ if (cecDevice)
+ ret = cecDevice->ToggleDeviceState(mode);
+ if (iPeripheral)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+bool CPeripherals::GetNextKeypress(float frameTime, CKey &key)
+{
+ vector<CPeripheral *> peripherals;
+ if (SupportsCEC() && GetPeripheralsWithFeature(peripherals, FEATURE_CEC))
+ {
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < peripherals.size(); iPeripheralPtr++)
+ {
+ CPeripheralCecAdapter *cecDevice = (CPeripheralCecAdapter *) peripherals.at(iPeripheralPtr);
+ if (cecDevice && cecDevice->GetButton())
+ {
+ CKey newKey(cecDevice->GetButton(), cecDevice->GetHoldTime());
+ cecDevice->ResetButton();
+ key = newKey;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void CPeripherals::OnSettingChanged(const CSetting *setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == "locale.language")
+ {
+ // user set language, no longer use the TV's language
+ vector<CPeripheral *> cecDevices;
+ if (g_peripherals.GetPeripheralsWithFeature(cecDevices, FEATURE_CEC) > 0)
+ {
+ for (vector<CPeripheral *>::iterator it = cecDevices.begin(); it != cecDevices.end(); ++it)
+ (*it)->SetSetting("use_tv_menu_language", false);
+ }
+ }
+}
+
+void CPeripherals::OnSettingAction(const CSetting *setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == "input.peripherals")
+ {
+ CGUIDialogPeripheralManager *dialog = (CGUIDialogPeripheralManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PERIPHERAL_MANAGER);
+ if (dialog != NULL)
+ dialog->DoModal();
+ }
+}
diff --git a/src/peripherals/Peripherals.h b/src/peripherals/Peripherals.h
new file mode 100644
index 0000000000..fcef6cad11
--- /dev/null
+++ b/src/peripherals/Peripherals.h
@@ -0,0 +1,229 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#include "bus/PeripheralBus.h"
+#include "devices/Peripheral.h"
+#include "settings/lib/ISettingCallback.h"
+#include "threads/CriticalSection.h"
+#include "threads/Thread.h"
+#include "utils/Observer.h"
+
+class CFileItemList;
+class CSetting;
+class CSettingsCategory;
+class TiXmlElement;
+class CAction;
+class CKey;
+
+namespace PERIPHERALS
+{
+ #define g_peripherals CPeripherals::Get()
+
+ class CPeripherals : public ISettingCallback,
+ public Observable
+ {
+ public:
+ static CPeripherals &Get(void);
+ virtual ~CPeripherals(void);
+
+ /*!
+ * @brief Initialise the peripherals manager.
+ */
+ virtual void Initialise(void);
+
+ /*!
+ * @brief Clear all data known by the peripherals manager.
+ */
+ virtual void Clear(void);
+
+ /*!
+ * @brief Get the instance of the peripheral at the given location.
+ * @param strLocation The location.
+ * @param busType The bus to query. Default (PERIPHERAL_BUS_UNKNOWN) searches all busses.
+ * @return The peripheral or NULL if it wasn't found.
+ */
+ virtual CPeripheral *GetPeripheralAtLocation(const CStdString &strLocation, PeripheralBusType busType = PERIPHERAL_BUS_UNKNOWN) const;
+
+ /*!
+ * @brief Check whether a peripheral is present at the given location.
+ * @param strLocation The location.
+ * @param busType The bus to query. Default (PERIPHERAL_BUS_UNKNOWN) searches all busses.
+ * @return True when a peripheral was found, false otherwise.
+ */
+ virtual bool HasPeripheralAtLocation(const CStdString &strLocation, PeripheralBusType busType = PERIPHERAL_BUS_UNKNOWN) const;
+
+ /*!
+ * @brief Get the bus that holds the device with the given location.
+ * @param strLocation The location.
+ * @return The bus or NULL if no device was found.
+ */
+ virtual CPeripheralBus *GetBusWithDevice(const CStdString &strLocation) const;
+
+ /*!
+ * @brief Get all peripheral instances that have the given feature.
+ * @param results The list of results.
+ * @param feature The feature to search for.
+ * @param busType The bus to query. Default (PERIPHERAL_BUS_UNKNOWN) searches all busses.
+ * @return The number of devices that have been found.
+ */
+ virtual int GetPeripheralsWithFeature(std::vector<CPeripheral *> &results, const PeripheralFeature feature, PeripheralBusType busType = PERIPHERAL_BUS_UNKNOWN) const;
+
+ size_t GetNumberOfPeripherals() const;
+
+ /*!
+ * @brief Check whether there is at least one device present with the given feature.
+ * @param feature The feature to check for.
+ * @param busType The bus to query. Default (PERIPHERAL_BUS_UNKNOWN) searches all busses.
+ * @return True when at least one device was found with this feature, false otherwise.
+ */
+ virtual bool HasPeripheralWithFeature(const PeripheralFeature feature, PeripheralBusType busType = PERIPHERAL_BUS_UNKNOWN) const;
+
+ /*!
+ * @brief Called when a device has been added to a bus.
+ * @param bus The bus the device was added to.
+ * @param peripheral The peripheral that has been added.
+ */
+ virtual void OnDeviceAdded(const CPeripheralBus &bus, const CPeripheral &peripheral);
+
+ /*!
+ * @brief Called when a device has been deleted from a bus.
+ * @param bus The bus from which the device removed.
+ * @param peripheral The peripheral that has been removed.
+ */
+ virtual void OnDeviceDeleted(const CPeripheralBus &bus, const CPeripheral &peripheral);
+
+ /*!
+ * @brief Creates a new instance of a peripheral.
+ * @param bus The bus on which this peripheral is present.
+ * @param result The scan result from the device scanning code.
+ * @return The new peripheral or NULL if it could not be created.
+ */
+ CPeripheral *CreatePeripheral(CPeripheralBus &bus, const PeripheralScanResult& result);
+
+ /*!
+ * @brief Add the settings that are defined in the mappings file to the peripheral (if there is anything defined).
+ * @param peripheral The peripheral to get the settings for.
+ */
+ void GetSettingsFromMapping(CPeripheral &peripheral) const;
+
+ /*!
+ * @brief Trigger a device scan on all known busses
+ */
+ virtual void TriggerDeviceScan(const PeripheralBusType type = PERIPHERAL_BUS_UNKNOWN);
+
+ /*!
+ * @brief Get the instance of a bus given it's type.
+ * @param type The bus type.
+ * @return The bus or NULL if it wasn't found.
+ */
+ virtual CPeripheralBus *GetBusByType(const PeripheralBusType type) const;
+
+ /*!
+ * @brief Get all fileitems for a path.
+ * @param strPath The path to the directory to get the items from.
+ * @param items The item list.
+ */
+ virtual void GetDirectory(const CStdString &strPath, CFileItemList &items) const;
+
+ /*!
+ * @brief Get the instance of a peripheral given it's path.
+ * @param strPath The path to the peripheral.
+ * @return The peripheral or NULL if it wasn't found.
+ */
+ virtual CPeripheral *GetByPath(const CStdString &strPath) const;
+
+ /*!
+ * @brief Try to let one of the peripherals handle an action.
+ * @param action The change to handle.
+ * @return True when this change was handled by a peripheral (and should not be handled by anything else), false otherwise.
+ */
+ virtual bool OnAction(const CAction &action);
+
+ /*!
+ * @brief Check whether there's a peripheral that reports to be muted.
+ * @return True when at least one peripheral reports to be muted, false otherwise.
+ */
+ virtual bool IsMuted(void);
+
+ /*!
+ * @brief Try to toggle the mute status via a peripheral.
+ * @return True when this change was handled by a peripheral (and should not be handled by anything else), false otherwise.
+ */
+ virtual bool ToggleMute(void);
+
+ /*!
+ * @brief Try to toggle the playing device state via a peripheral.
+ * @param mode Whether to activate, put on standby or toggle the source.
+ * @param iPeripheral Optional CPeripheralCecAdapter pointer to a specific device, instead of iterating through all of them.
+ * @return True when the playing device has been switched on, false otherwise.
+ */
+ virtual bool ToggleDeviceState(const CecStateChange mode = STATE_SWITCH_TOGGLE, const unsigned int iPeripheral = 0);
+
+ /*!
+ * @brief Try to mute the audio via a peripheral.
+ * @return True when this change was handled by a peripheral (and should not be handled by anything else), false otherwise.
+ */
+ virtual bool Mute(void) { return ToggleMute(); } // TODO CEC only supports toggling the mute status at this time
+
+ /*!
+ * @brief Try to unmute the audio via a peripheral.
+ * @return True when this change was handled by a peripheral (and should not be handled by anything else), false otherwise.
+ */
+ virtual bool UnMute(void) { return ToggleMute(); } // TODO CEC only supports toggling the mute status at this time
+
+ /*!
+ * @brief Try to get a keypress from a peripheral.
+ * @param frameTime The current frametime.
+ * @param key The fetched key.
+ * @return True when a keypress was fetched, false otherwise.
+ */
+ virtual bool GetNextKeypress(float frameTime, CKey &key);
+
+ bool SupportsCEC(void) const
+ {
+#if defined(HAVE_LIBCEC)
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ virtual void OnSettingChanged(const CSetting *setting);
+ virtual void OnSettingAction(const CSetting *setting);
+
+ private:
+ CPeripherals(void);
+ bool LoadMappings(void);
+ bool GetMappingForDevice(const CPeripheralBus &bus, PeripheralScanResult& result) const;
+ static void GetSettingsFromMappingsFile(TiXmlElement *xmlNode, std::map<CStdString, PeripheralDeviceSetting> &m_settings);
+
+ bool m_bInitialised;
+ bool m_bIsStarted;
+#if !defined(HAVE_LIBCEC)
+ bool m_bMissingLibCecWarningDisplayed;
+#endif
+ std::vector<CPeripheralBus *> m_busses;
+ std::vector<PeripheralDeviceMapping> m_mappings;
+ CSettingsCategory * m_settings;
+ CCriticalSection m_critSection;
+ };
+}
diff --git a/src/peripherals/bus/Makefile.in b/src/peripherals/bus/Makefile.in
new file mode 100644
index 0000000000..2262e4a40a
--- /dev/null
+++ b/src/peripherals/bus/Makefile.in
@@ -0,0 +1,22 @@
+SRCS = PeripheralBus.cpp
+
+ifeq (@USE_LIBUDEV@,1)
+SRCS += linux/PeripheralBusUSBLibUdev.cpp
+endif
+
+ifeq (@USE_LIBUSB@,1)
+SRCS += linux/PeripheralBusUSBLibUSB.cpp
+endif
+
+ifeq ($(findstring osx,@ARCH@),osx)
+SRCS += osx/PeripheralBusUSB.cpp
+endif
+
+ifeq (@USE_LIBCEC@,1)
+SRCS += virtual/PeripheralBusCEC.cpp
+endif
+
+LIB = peripheral-bus.a
+
+include @abs_top_srcdir@/Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/src/peripherals/bus/PeripheralBus.cpp b/src/peripherals/bus/PeripheralBus.cpp
new file mode 100644
index 0000000000..7f1191b845
--- /dev/null
+++ b/src/peripherals/bus/PeripheralBus.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralBus.h"
+#include "peripherals/Peripherals.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "FileItem.h"
+
+using namespace std;
+using namespace PERIPHERALS;
+
+#define PERIPHERAL_DEFAULT_RESCAN_INTERVAL 5000
+
+CPeripheralBus::CPeripheralBus(const CStdString &threadname, CPeripherals *manager, PeripheralBusType type) :
+ CThread(threadname),
+ m_iRescanTime(PERIPHERAL_DEFAULT_RESCAN_INTERVAL),
+ m_bInitialised(false),
+ m_bIsStarted(false),
+ m_bNeedsPolling(true),
+ m_manager(manager),
+ m_type(type),
+ m_triggerEvent(true)
+{
+}
+
+void CPeripheralBus::OnDeviceAdded(const CStdString &strLocation)
+{
+ ScanForDevices();
+}
+
+void CPeripheralBus::OnDeviceChanged(const CStdString &strLocation)
+{
+ ScanForDevices();
+}
+
+void CPeripheralBus::OnDeviceRemoved(const CStdString &strLocation)
+{
+ ScanForDevices();
+}
+
+void CPeripheralBus::Clear(void)
+{
+ if (m_bNeedsPolling)
+ {
+ m_bStop = true;
+ m_triggerEvent.Set();
+ StopThread(true);
+ }
+
+ CSingleLock lock(m_critSection);
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < m_peripherals.size(); iPeripheralPtr++)
+ delete m_peripherals.at(iPeripheralPtr);
+ m_peripherals.clear();
+}
+
+void CPeripheralBus::UnregisterRemovedDevices(const PeripheralScanResults &results)
+{
+ CSingleLock lock(m_critSection);
+ vector<CPeripheral *> removedPeripherals;
+ for (int iDevicePtr = (int) m_peripherals.size() - 1; iDevicePtr >= 0; iDevicePtr--)
+ {
+ CPeripheral *peripheral = m_peripherals.at(iDevicePtr);
+ PeripheralScanResult updatedDevice(m_type);
+ if (!results.GetDeviceOnLocation(peripheral->Location(), &updatedDevice) ||
+ *peripheral != updatedDevice)
+ {
+ /* device removed */
+ removedPeripherals.push_back(peripheral);
+ m_peripherals.erase(m_peripherals.begin() + iDevicePtr);
+ }
+ }
+ lock.Leave();
+
+ for (unsigned int iDevicePtr = 0; iDevicePtr < removedPeripherals.size(); iDevicePtr++)
+ {
+ CPeripheral *peripheral = removedPeripherals.at(iDevicePtr);
+ vector<PeripheralFeature> features;
+ peripheral->GetFeatures(features);
+ bool peripheralHasFeatures = features.size() > 1 || (features.size() == 1 && features.at(0) != FEATURE_UNKNOWN);
+ if (peripheral->Type() != PERIPHERAL_UNKNOWN || peripheralHasFeatures)
+ {
+ CLog::Log(LOGNOTICE, "%s - device removed from %s/%s: %s (%s:%s)", __FUNCTION__, PeripheralTypeTranslator::TypeToString(peripheral->Type()), peripheral->Location().c_str(), peripheral->DeviceName().c_str(), peripheral->VendorIdAsString(), peripheral->ProductIdAsString());
+ peripheral->OnDeviceRemoved();
+ }
+
+ m_manager->OnDeviceDeleted(*this, *peripheral);
+ delete peripheral;
+ }
+}
+
+void CPeripheralBus::RegisterNewDevices(const PeripheralScanResults &results)
+{
+ CSingleLock lock(m_critSection);
+ for (unsigned int iResultPtr = 0; iResultPtr < results.m_results.size(); iResultPtr++)
+ {
+ const PeripheralScanResult& result = results.m_results.at(iResultPtr);
+ if (!HasPeripheral(result.m_strLocation))
+ g_peripherals.CreatePeripheral(*this, result);
+ }
+}
+
+bool CPeripheralBus::ScanForDevices(void)
+{
+ bool bReturn(false);
+
+ PeripheralScanResults results;
+ if (PerformDeviceScan(results))
+ {
+ UnregisterRemovedDevices(results);
+ RegisterNewDevices(results);
+
+ CPeripherals::Get().NotifyObservers(ObservableMessagePeripheralsChanged);
+
+ bReturn = true;
+ }
+
+ m_bInitialised = true;
+ return bReturn;
+}
+
+bool CPeripheralBus::HasFeature(const PeripheralFeature feature) const
+{
+ bool bReturn(false);
+ CSingleLock lock(m_critSection);
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < m_peripherals.size(); iPeripheralPtr++)
+ {
+ if (m_peripherals.at(iPeripheralPtr)->HasFeature(feature))
+ {
+ bReturn = true;
+ break;
+ }
+ }
+ return bReturn;
+}
+
+void CPeripheralBus::GetFeatures(std::vector<PeripheralFeature> &features) const
+{
+ CSingleLock lock(m_critSection);
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < m_peripherals.size(); iPeripheralPtr++)
+ m_peripherals.at(iPeripheralPtr)->GetFeatures(features);
+}
+
+CPeripheral *CPeripheralBus::GetPeripheral(const CStdString &strLocation) const
+{
+ CPeripheral *peripheral(NULL);
+ CSingleLock lock(m_critSection);
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < m_peripherals.size(); iPeripheralPtr++)
+ {
+ if (m_peripherals.at(iPeripheralPtr)->Location() == strLocation)
+ {
+ peripheral = m_peripherals.at(iPeripheralPtr);
+ break;
+ }
+ }
+ return peripheral;
+}
+
+int CPeripheralBus::GetPeripheralsWithFeature(vector<CPeripheral *> &results, const PeripheralFeature feature) const
+{
+ int iReturn(0);
+ CSingleLock lock(m_critSection);
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < m_peripherals.size(); iPeripheralPtr++)
+ {
+ if (m_peripherals.at(iPeripheralPtr)->HasFeature(feature))
+ {
+ results.push_back(m_peripherals.at(iPeripheralPtr));
+ ++iReturn;
+ }
+ }
+
+ return iReturn;
+}
+
+size_t CPeripheralBus::GetNumberOfPeripheralsWithId(const int iVendorId, const int iProductId) const
+{
+ int iReturn(0);
+ CSingleLock lock(m_critSection);
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < m_peripherals.size(); iPeripheralPtr++)
+ {
+ if (m_peripherals.at(iPeripheralPtr)->VendorId() == iVendorId &&
+ m_peripherals.at(iPeripheralPtr)->ProductId() == iProductId)
+ iReturn++;
+ }
+
+ return iReturn;
+}
+
+void CPeripheralBus::Process(void)
+{
+ while (!m_bStop)
+ {
+ m_triggerEvent.Reset();
+
+ if (!ScanForDevices())
+ break;
+
+ if (!m_bStop)
+ m_triggerEvent.WaitMSec(m_iRescanTime);
+ }
+
+ m_bIsStarted = false;
+}
+
+bool CPeripheralBus::Initialise(void)
+{
+ CSingleLock lock(m_critSection);
+ if (!m_bIsStarted)
+ {
+ /* do an initial scan of the bus */
+ m_bIsStarted = ScanForDevices();
+
+ if (m_bIsStarted && m_bNeedsPolling)
+ {
+ lock.Leave();
+ m_triggerEvent.Reset();
+ Create();
+ SetPriority(-1);
+ }
+ }
+
+ return m_bIsStarted;
+}
+
+void CPeripheralBus::Register(CPeripheral *peripheral)
+{
+ if (!peripheral)
+ return;
+
+ CSingleLock lock(m_critSection);
+ if (!HasPeripheral(peripheral->Location()))
+ {
+ m_peripherals.push_back(peripheral);
+ CLog::Log(LOGNOTICE, "%s - new %s device registered on %s->%s: %s (%s:%s)", __FUNCTION__, PeripheralTypeTranslator::TypeToString(peripheral->Type()), PeripheralTypeTranslator::BusTypeToString(m_type), peripheral->Location().c_str(), peripheral->DeviceName().c_str(), peripheral->VendorIdAsString(), peripheral->ProductIdAsString());
+ lock.Leave();
+
+ m_manager->OnDeviceAdded(*this, *peripheral);
+ }
+}
+
+void CPeripheralBus::TriggerDeviceScan(void)
+{
+ CSingleLock lock(m_critSection);
+ if (m_bNeedsPolling)
+ {
+ lock.Leave();
+ m_triggerEvent.Set();
+ }
+ else
+ {
+ lock.Leave();
+ ScanForDevices();
+ }
+}
+
+bool CPeripheralBus::HasPeripheral(const CStdString &strLocation) const
+{
+ return (GetPeripheral(strLocation) != NULL);
+}
+
+void CPeripheralBus::GetDirectory(const CStdString &strPath, CFileItemList &items) const
+{
+ CStdString strDevPath;
+ CSingleLock lock(m_critSection);
+ for (unsigned int iDevicePtr = 0; iDevicePtr < m_peripherals.size(); iDevicePtr++)
+ {
+ const CPeripheral *peripheral = m_peripherals.at(iDevicePtr);
+ if (peripheral->IsHidden())
+ continue;
+
+ CFileItemPtr peripheralFile(new CFileItem(peripheral->DeviceName()));
+ peripheralFile->SetPath(peripheral->FileLocation());
+ peripheralFile->SetProperty("vendor", peripheral->VendorIdAsString());
+ peripheralFile->SetProperty("product", peripheral->ProductIdAsString());
+ peripheralFile->SetProperty("bus", PeripheralTypeTranslator::BusTypeToString(peripheral->GetBusType()));
+ peripheralFile->SetProperty("location", peripheral->Location());
+ peripheralFile->SetProperty("class", PeripheralTypeTranslator::TypeToString(peripheral->Type()));
+ peripheralFile->SetProperty("version", peripheral->GetVersionInfo());
+ items.Add(peripheralFile);
+ }
+}
+
+CPeripheral *CPeripheralBus::GetByPath(const CStdString &strPath) const
+{
+ CStdString strDevPath;
+ CSingleLock lock(m_critSection);
+ for (unsigned int iDevicePtr = 0; iDevicePtr < m_peripherals.size(); iDevicePtr++)
+ {
+ if (strPath.Equals(m_peripherals.at(iDevicePtr)->FileLocation()))
+ return m_peripherals.at(iDevicePtr);
+ }
+
+ return NULL;
+}
+
+size_t CPeripheralBus::GetNumberOfPeripherals() const
+{
+ return m_peripherals.size();
+}
diff --git a/src/peripherals/bus/PeripheralBus.h b/src/peripherals/bus/PeripheralBus.h
new file mode 100644
index 0000000000..795f88b6a4
--- /dev/null
+++ b/src/peripherals/bus/PeripheralBus.h
@@ -0,0 +1,176 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <vector>
+#include "utils/StdString.h"
+#include "threads/Thread.h"
+#include "peripherals/PeripheralTypes.h"
+#include "peripherals/devices/Peripheral.h"
+
+class CFileItemList;
+
+namespace PERIPHERALS
+{
+ class CPeripherals;
+
+ /*!
+ * @class CPeripheralBus
+ * This represents a bus on the system. By default, this bus instance will scan for changes every 5 seconds.
+ * If this bus only has to be updated after a notification sent by the system, set m_bNeedsPolling to false
+ * in the constructor, and implement the OnDeviceAdded(), OnDeviceChanged() and OnDeviceRemoved() methods.
+ *
+ * The PerformDeviceScan() method has to be implemented by each specific bus implementation.
+ */
+ class CPeripheralBus : protected CThread
+ {
+ public:
+ CPeripheralBus(const CStdString &threadname, CPeripherals *manager, PeripheralBusType type);
+ virtual ~CPeripheralBus(void) { Clear(); }
+
+ /*!
+ * @return The bus type
+ */
+ const PeripheralBusType Type(void) const { return m_type; }
+
+ /*!
+ * @return True if this bus needs to be polled for changes, false if this bus performs updates via callbacks
+ */
+ bool NeedsPolling(void) const { return m_bNeedsPolling; }
+
+ /*!
+ * @brief Get the instance of the peripheral at the given location.
+ * @param strLocation The location.
+ * @return The peripheral or NULL if it wasn't found.
+ */
+ virtual CPeripheral *GetPeripheral(const CStdString &strLocation) const;
+
+ /*!
+ * @brief Check whether a peripheral is present at the given location.
+ * @param strLocation The location.
+ * @return True when a peripheral was found, false otherwise.
+ */
+ virtual bool HasPeripheral(const CStdString &strLocation) const;
+
+ /*!
+ * @brief Get all peripheral instances that have the given feature.
+ * @param results The list of results.
+ * @param feature The feature to search for.
+ * @return The number of devices that have been found.
+ */
+ virtual int GetPeripheralsWithFeature(std::vector<CPeripheral *> &results, const PeripheralFeature feature) const;
+
+ virtual size_t GetNumberOfPeripherals() const;
+ virtual size_t GetNumberOfPeripheralsWithId(const int iVendorId, const int iProductId) const;
+
+ /*!
+ * @brief Get all features that are supported by devices on this bus.
+ * @param features All features.
+ */
+ virtual void GetFeatures(std::vector<PeripheralFeature> &features) const;
+
+ /*!
+ * @brief Check whether there is at least one device present with the given feature.
+ * @param feature The feature to check for.
+ * @return True when at least one device was found with this feature, false otherwise.
+ */
+ virtual bool HasFeature(const PeripheralFeature feature) const;
+
+ /*!
+ * @brief Callback method for when a device has been added. Will perform a device scan.
+ * @param strLocation The location of the device that has been added.
+ */
+ virtual void OnDeviceAdded(const CStdString &strLocation);
+
+ /*!
+ * @brief Callback method for when a device has been changed. Will perform a device scan.
+ * @param strLocation The location of the device that has been changed.
+ */
+ virtual void OnDeviceChanged(const CStdString &strLocation);
+
+ /*!
+ * @brief Callback method for when a device has been removed. Will perform a device scan.
+ * @param strLocation The location of the device that has been removed.
+ */
+ virtual void OnDeviceRemoved(const CStdString &strLocation);
+
+ /*!
+ * @brief Initialise this bus and start a polling thread if this bus needs polling.
+ */
+ virtual bool Initialise(void);
+
+ /*!
+ * @brief Stop the polling thread and clear all known devices on this bus.
+ */
+ virtual void Clear(void);
+
+ /*!
+ * @brief Scan for devices.
+ */
+ virtual void TriggerDeviceScan(void);
+
+ /*!
+ * @brief Get all fileitems for a path.
+ * @param strPath The path to the directory to get the items from.
+ * @param items The item list.
+ */
+ virtual void GetDirectory(const CStdString &strPath, CFileItemList &items) const;
+
+ /*!
+ * @brief Get the instance of a peripheral given it's path.
+ * @param strPath The path to the peripheral.
+ * @return The peripheral or NULL if it wasn't found.
+ */
+ virtual CPeripheral *GetByPath(const CStdString &strPath) const;
+
+ /*!
+ * @brief Register a new peripheral on this bus.
+ * @param peripheral The peripheral to register.
+ */
+ virtual void Register(CPeripheral *peripheral);
+
+ virtual bool FindComPort(CStdString &strLocation) { return false; }
+
+ virtual bool IsInitialised(void) const { return m_bInitialised; }
+
+ protected:
+ virtual void Process(void);
+ virtual bool ScanForDevices(void);
+ virtual void UnregisterRemovedDevices(const PeripheralScanResults &results);
+ virtual void RegisterNewDevices(const PeripheralScanResults &results);
+
+ /*!
+ * @brief Scan for devices on this bus and add them to the results list. This will have to be implemented for each bus.
+ * @param results The result list.
+ * @return True when the scan was successful, false otherwise.
+ */
+ virtual bool PerformDeviceScan(PeripheralScanResults &results) = 0;
+
+ std::vector<CPeripheral *> m_peripherals;
+ int m_iRescanTime;
+ bool m_bInitialised;
+ bool m_bIsStarted;
+ bool m_bNeedsPolling; /*!< true when this bus needs to be polled for new devices, false when it uses callbacks to notify this bus of changed */
+ CPeripherals * m_manager;
+ PeripheralBusType m_type;
+ CCriticalSection m_critSection;
+ CEvent m_triggerEvent;
+ };
+}
diff --git a/src/peripherals/bus/PeripheralBusUSB.h b/src/peripherals/bus/PeripheralBusUSB.h
new file mode 100644
index 0000000000..a39f8791b3
--- /dev/null
+++ b/src/peripherals/bus/PeripheralBusUSB.h
@@ -0,0 +1,40 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if defined(TARGET_WINDOWS)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "win32/PeripheralBusUSB.h"
+#elif defined(TARGET_LINUX) && defined(HAVE_LIBUDEV)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "linux/PeripheralBusUSBLibUdev.h"
+#elif defined(TARGET_LINUX) && defined(HAVE_LIBUSB)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "linux/PeripheralBusUSBLibUSB.h"
+#elif defined(TARGET_FREEBSD) && defined(HAVE_LIBUSB)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "linux/PeripheralBusUSBLibUSB.h"
+#elif defined(TARGET_DARWIN)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "osx/PeripheralBusUSB.h"
+#elif defined(TARGET_ANDROID)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "linux/PeripheralBusUSBLibUSB.h"
+#endif
diff --git a/src/peripherals/bus/linux/PeripheralBusUSBLibUSB.cpp b/src/peripherals/bus/linux/PeripheralBusUSBLibUSB.cpp
new file mode 100644
index 0000000000..2658218b63
--- /dev/null
+++ b/src/peripherals/bus/linux/PeripheralBusUSBLibUSB.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralBusUSBLibUSB.h"
+#include "peripherals/Peripherals.h"
+#include <usb.h>
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+
+using namespace PERIPHERALS;
+
+CPeripheralBusUSB::CPeripheralBusUSB(CPeripherals *manager) :
+ CPeripheralBus("PeripBusUSB", manager, PERIPHERAL_BUS_USB)
+{
+ usb_init();
+ usb_find_busses();
+ m_busses = usb_get_busses();
+ CLog::Log(LOGDEBUG, "%s - using libusb peripheral scanning", __FUNCTION__);
+}
+
+bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results)
+{
+ struct usb_bus *bus;
+ usb_find_devices();
+ for (bus = m_busses; bus; bus = bus->next)
+ {
+ struct usb_device *dev;
+ for (dev = bus->devices; dev; dev = dev->next)
+ {
+ PeripheralScanResult result(m_type);
+ result.m_iVendorId = dev->descriptor.idVendor;
+ result.m_iProductId = dev->descriptor.idProduct;
+ result.m_type = (dev->descriptor.bDeviceClass == USB_CLASS_PER_INTERFACE && dev->descriptor.bNumConfigurations > 0 &&
+ dev->config[0].bNumInterfaces > 0 && dev->config[0].interface[0].num_altsetting > 0) ?
+ GetType(dev->config[0].interface[0].altsetting[0].bInterfaceClass) :
+ GetType(dev->descriptor.bDeviceClass);
+#ifdef TARGET_FREEBSD
+ result.m_strLocation = StringUtils::Format("%s", dev->filename);
+#else
+ result.m_strLocation = StringUtils::Format("/bus%s/dev%s", bus->dirname, dev->filename);
+#endif
+ result.m_iSequence = GetNumberOfPeripheralsWithId(result.m_iVendorId, result.m_iProductId);
+ if (!results.ContainsResult(result))
+ results.m_results.push_back(result);
+ }
+ }
+
+ return true;
+}
+
+const PeripheralType CPeripheralBusUSB::GetType(int iDeviceClass)
+{
+ switch (iDeviceClass)
+ {
+ case USB_CLASS_HID:
+ return PERIPHERAL_HID;
+ case USB_CLASS_COMM:
+ return PERIPHERAL_NIC;
+ case USB_CLASS_MASS_STORAGE:
+ return PERIPHERAL_DISK;
+ case USB_CLASS_PER_INTERFACE:
+ case USB_CLASS_AUDIO:
+ case USB_CLASS_PRINTER:
+ case USB_CLASS_PTP:
+ case USB_CLASS_HUB:
+ case USB_CLASS_DATA:
+ case USB_CLASS_VENDOR_SPEC:
+ default:
+ return PERIPHERAL_UNKNOWN;
+ }
+}
diff --git a/src/peripherals/bus/linux/PeripheralBusUSBLibUSB.h b/src/peripherals/bus/linux/PeripheralBusUSBLibUSB.h
new file mode 100644
index 0000000000..461d29673d
--- /dev/null
+++ b/src/peripherals/bus/linux/PeripheralBusUSBLibUSB.h
@@ -0,0 +1,45 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "peripherals/bus/PeripheralBus.h"
+#include "peripherals/devices/Peripheral.h"
+
+struct usb_bus;
+
+namespace PERIPHERALS
+{
+ class CPeripherals;
+
+ class CPeripheralBusUSB : public CPeripheralBus
+ {
+ public:
+ CPeripheralBusUSB(CPeripherals *manager);
+
+ /*!
+ * @see PeripheralBus::PerformDeviceScan()
+ */
+ bool PerformDeviceScan(PeripheralScanResults &results);
+
+ protected:
+ static const PeripheralType GetType(int iDeviceClass);
+ struct usb_bus *m_busses;
+ };
+}
diff --git a/src/peripherals/bus/linux/PeripheralBusUSBLibUdev.cpp b/src/peripherals/bus/linux/PeripheralBusUSBLibUdev.cpp
new file mode 100644
index 0000000000..461a4fc7f6
--- /dev/null
+++ b/src/peripherals/bus/linux/PeripheralBusUSBLibUdev.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralBusUSBLibUdev.h"
+#include "peripherals/Peripherals.h"
+extern "C" {
+#include <libudev.h>
+}
+#include <poll.h>
+#include "utils/log.h"
+
+#ifndef USB_CLASS_PER_INTERFACE
+#define USB_CLASS_PER_INTERFACE 0
+#endif
+
+#ifndef USB_CLASS_AUDIO
+#define USB_CLASS_AUDIO 1
+#endif
+
+#ifndef USB_CLASS_COMM
+#define USB_CLASS_COMM 2
+#endif
+
+#ifndef USB_CLASS_HID
+#define USB_CLASS_HID 3
+#endif
+
+#ifndef USB_CLASS_PHYSICAL
+#define USB_CLASS_PHYSICAL 5
+#endif
+
+#ifndef USB_CLASS_PTP
+#define USB_CLASS_PTP 6
+#endif
+
+#ifndef USB_CLASS_PRINTER
+#define USB_CLASS_PRINTER 7
+#endif
+
+#ifndef USB_CLASS_MASS_STORAGE
+#define USB_CLASS_MASS_STORAGE 8
+#endif
+
+#ifndef USB_CLASS_HUB
+#define USB_CLASS_HUB 9
+#endif
+
+#ifndef USB_CLASS_DATA
+#define USB_CLASS_DATA 10
+#endif
+
+#ifndef USB_CLASS_APP_SPEC
+#define USB_CLASS_APP_SPEC 0xfe
+#endif
+
+#ifndef USB_CLASS_VENDOR_SPEC
+#define USB_CLASS_VENDOR_SPEC 0xff
+#endif
+
+using namespace PERIPHERALS;
+
+CPeripheralBusUSB::CPeripheralBusUSB(CPeripherals *manager) :
+ CPeripheralBus("PeripBusUSBUdev", manager, PERIPHERAL_BUS_USB)
+{
+ /* the Process() method in this class overrides the one in CPeripheralBus, so leave this set to true */
+ m_bNeedsPolling = true;
+
+ m_udev = NULL;
+ m_udevMon = NULL;
+
+ if (!(m_udev = udev_new()))
+ {
+ CLog::Log(LOGERROR, "%s - failed to allocate udev context", __FUNCTION__);
+ return;
+ }
+
+ /* set up a devices monitor that listen for any device change */
+ m_udevMon = udev_monitor_new_from_netlink(m_udev, "udev");
+ udev_monitor_enable_receiving(m_udevMon);
+
+ CLog::Log(LOGDEBUG, "%s - initialised udev monitor", __FUNCTION__);
+}
+
+CPeripheralBusUSB::~CPeripheralBusUSB(void)
+{
+ StopThread(true);
+ udev_monitor_unref(m_udevMon);
+ udev_unref(m_udev);
+}
+
+bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results)
+{
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ struct udev_device *dev(NULL), *parent(NULL);
+ enumerate = udev_enumerate_new(m_udev);
+ udev_enumerate_scan_devices(enumerate);
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ bool bContinue(true);
+ CStdString strPath, strClass;
+ udev_list_entry_foreach(dev_list_entry, devices)
+ {
+ strPath = udev_list_entry_get_name(dev_list_entry);
+ if (strPath.empty())
+ bContinue = false;
+
+ if (bContinue)
+ {
+ if (!(parent = udev_device_new_from_syspath(m_udev, strPath)))
+ bContinue = false;
+ }
+
+ if (bContinue)
+ {
+ dev = udev_device_get_parent(udev_device_get_parent(parent));
+ if (!dev || !udev_device_get_sysattr_value(dev,"idVendor") || !udev_device_get_sysattr_value(dev, "idProduct"))
+ bContinue = false;
+ }
+
+ if (bContinue)
+ {
+ strClass = udev_device_get_sysattr_value(dev, "bDeviceClass");
+ if (strClass.empty())
+ bContinue = false;
+ }
+
+ if (bContinue)
+ {
+ int iClass = PeripheralTypeTranslator::HexStringToInt(strClass.c_str());
+ if (iClass == USB_CLASS_PER_INTERFACE)
+ {
+ //TODO just assume this is a HID device for now, since the only devices that we're currently
+ // interested in are HID devices
+ iClass = USB_CLASS_HID;
+ }
+
+ PeripheralScanResult result(m_type);
+ result.m_iVendorId = PeripheralTypeTranslator::HexStringToInt(udev_device_get_sysattr_value(dev, "idVendor"));
+ result.m_iProductId = PeripheralTypeTranslator::HexStringToInt(udev_device_get_sysattr_value(dev, "idProduct"));
+ result.m_type = GetType(iClass);
+ result.m_strLocation = udev_device_get_syspath(dev);
+ result.m_iSequence = GetNumberOfPeripheralsWithId(result.m_iVendorId, result.m_iProductId);
+ if (!results.ContainsResult(result))
+ results.m_results.push_back(result);
+ }
+
+ bContinue = true;
+ if (parent)
+ {
+ /* unref the _parent_ device */
+ udev_device_unref(parent);
+ parent = NULL;
+ }
+ }
+ /* Free the enumerator object */
+ udev_enumerate_unref(enumerate);
+
+ return true;
+}
+
+const PeripheralType CPeripheralBusUSB::GetType(int iDeviceClass)
+{
+ switch (iDeviceClass)
+ {
+ case USB_CLASS_HID:
+ return PERIPHERAL_HID;
+ case USB_CLASS_COMM:
+ return PERIPHERAL_NIC;
+ case USB_CLASS_MASS_STORAGE:
+ return PERIPHERAL_DISK;
+ case USB_CLASS_PER_INTERFACE:
+ case USB_CLASS_AUDIO:
+ case USB_CLASS_PRINTER:
+ case USB_CLASS_PTP:
+ case USB_CLASS_HUB:
+ case USB_CLASS_DATA:
+ case USB_CLASS_VENDOR_SPEC:
+ default:
+ return PERIPHERAL_UNKNOWN;
+ }
+}
+
+void CPeripheralBusUSB::Process(void)
+{
+ bool bUpdated(false);
+ ScanForDevices();
+ while (!m_bStop)
+ {
+ bUpdated = WaitForUpdate();
+ if (bUpdated && !m_bStop)
+ ScanForDevices();
+ }
+
+ m_bIsStarted = false;
+}
+
+void CPeripheralBusUSB::Clear(void)
+{
+ StopThread(false);
+
+ CPeripheralBus::Clear();
+}
+
+bool CPeripheralBusUSB::WaitForUpdate()
+{
+ int udevFd = udev_monitor_get_fd(m_udevMon);
+
+ if (udevFd < 0)
+ {
+ CLog::Log(LOGERROR, "%s - get udev monitor", __FUNCTION__);
+ return false;
+ }
+
+ /* poll for udev changes */
+ struct pollfd pollFd;
+ pollFd.fd = udevFd;
+ pollFd.events = POLLIN;
+ int iPollResult;
+ while (!m_bStop && ((iPollResult = poll(&pollFd, 1, 100)) <= 0))
+ if (errno != EINTR && iPollResult != 0)
+ break;
+
+ /* the thread is being stopped, so just return false */
+ if (m_bStop)
+ return false;
+
+ /* we have to read the message from the queue, even though we're not actually using it */
+ struct udev_device *dev = udev_monitor_receive_device(m_udevMon);
+ if (dev)
+ udev_device_unref(dev);
+ else
+ {
+ CLog::Log(LOGERROR, "%s - failed to get device from udev_monitor_receive_device()", __FUNCTION__);
+ Clear();
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/peripherals/bus/linux/PeripheralBusUSBLibUdev.h b/src/peripherals/bus/linux/PeripheralBusUSBLibUdev.h
new file mode 100644
index 0000000000..b7715ce2e0
--- /dev/null
+++ b/src/peripherals/bus/linux/PeripheralBusUSBLibUdev.h
@@ -0,0 +1,54 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "peripherals/bus/PeripheralBus.h"
+#include "peripherals/devices/Peripheral.h"
+
+struct udev;
+struct udev_monitor;
+
+namespace PERIPHERALS
+{
+ class CPeripherals;
+
+ class CPeripheralBusUSB : public CPeripheralBus
+ {
+ public:
+ CPeripheralBusUSB(CPeripherals *manager);
+ virtual ~CPeripheralBusUSB(void);
+
+ virtual void Clear(void);
+
+ /*!
+ * @see PeripheralBus::PerformDeviceScan()
+ */
+ bool PerformDeviceScan(PeripheralScanResults &results);
+
+ protected:
+ static const PeripheralType GetType(int iDeviceClass);
+
+ virtual void Process(void);
+ bool WaitForUpdate(void);
+
+ struct udev * m_udev;
+ struct udev_monitor *m_udevMon;
+ };
+}
diff --git a/src/peripherals/bus/osx/PeripheralBusUSB.cpp b/src/peripherals/bus/osx/PeripheralBusUSB.cpp
new file mode 100644
index 0000000000..641ed65ef7
--- /dev/null
+++ b/src/peripherals/bus/osx/PeripheralBusUSB.cpp
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralBusUSB.h"
+#include "peripherals/Peripherals.h"
+#include "utils/log.h"
+#include "osx/DarwinUtils.h"
+
+#include <sys/param.h>
+
+using namespace PERIPHERALS;
+
+#ifdef TARGET_DARWIN_OSX
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/hid/IOHIDLib.h>
+#include <IOKit/hid/IOHIDKeys.h>
+#include <IOKit/serial/IOSerialKeys.h>
+
+typedef struct USBDevicePrivateData {
+ CPeripheralBusUSB *refCon;
+ CStdString deviceName;
+ io_object_t notification;
+ PeripheralScanResult result;
+} USBDevicePrivateData;
+#endif
+
+CPeripheralBusUSB::CPeripheralBusUSB(CPeripherals *manager) :
+ CPeripheralBus("PeripBusUSB", manager, PERIPHERAL_BUS_USB)
+{
+ m_bNeedsPolling = false;
+
+#ifdef TARGET_DARWIN_OSX
+ //set up the matching criteria for the devices we're interested in
+ //interested in instances of class IOUSBDevice and its subclasses
+ // match any usb device by not creating matching values in the dict
+ CFMutableDictionaryRef matching_dict = IOServiceMatching(kIOUSBDeviceClassName);
+
+ m_notify_port = IONotificationPortCreate(kIOMasterPortDefault);
+ CFRunLoopSourceRef run_loop_source = IONotificationPortGetRunLoopSource(m_notify_port);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source, kCFRunLoopCommonModes);
+
+ //add a notification callback for attach event
+ IOReturn result = IOServiceAddMatchingNotification(m_notify_port,
+ kIOFirstMatchNotification, matching_dict,
+ (IOServiceMatchingCallback)DeviceAttachCallback, this, &m_attach_iterator);
+ if (result == kIOReturnSuccess)
+ {
+ //call the callback to 'arm' the notification
+ DeviceAttachCallback(this, m_attach_iterator);
+ }
+#endif
+}
+
+CPeripheralBusUSB::~CPeripheralBusUSB()
+{
+#ifdef TARGET_DARWIN_OSX
+ if (m_notify_port)
+ {
+ // remove the sleep notification port from the application runloop
+ CFRunLoopRemoveSource( CFRunLoopGetCurrent(),
+ IONotificationPortGetRunLoopSource(m_notify_port), kCFRunLoopDefaultMode );
+ IONotificationPortDestroy(m_notify_port);
+ m_notify_port = 0;
+ }
+ if (m_attach_iterator)
+ {
+ IOObjectRelease(m_attach_iterator);
+ m_attach_iterator = 0;
+ }
+#endif
+}
+
+bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results)
+{
+ results = m_scan_results;
+ return true;
+}
+
+#ifdef TARGET_DARWIN_OSX
+const PeripheralType CPeripheralBusUSB::GetType(int iDeviceClass)
+{
+ switch (iDeviceClass)
+ {
+ case kUSBHIDClass:
+ return PERIPHERAL_HID;
+ case kUSBCommClass:
+ return PERIPHERAL_NIC;
+ case kUSBMassStorageClass:
+ return PERIPHERAL_DISK;
+ //case USB_CLASS_PER_INTERFACE:
+ case kUSBAudioClass:
+ case kUSBPrintingClass:
+ //case USB_CLASS_PTP:
+ case kUSBHubClass:
+ case kUSBDataClass:
+ case kUSBVendorSpecificClass:
+ default:
+ return PERIPHERAL_UNKNOWN;
+ }
+}
+
+void CPeripheralBusUSB::DeviceDetachCallback(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
+{
+ if (messageType == kIOMessageServiceIsTerminated)
+ {
+ IOReturn result;
+
+ USBDevicePrivateData *privateDataRef = (USBDevicePrivateData*)refCon;
+
+ std::vector<PeripheralScanResult>::iterator it = privateDataRef->refCon->m_scan_results.m_results.begin();
+ while(it != privateDataRef->refCon->m_scan_results.m_results.end())
+ {
+ if (privateDataRef->result.m_strLocation == it->m_strLocation)
+ it = privateDataRef->refCon->m_scan_results.m_results.erase(it);
+ else
+ ++it;
+ }
+ privateDataRef->refCon->ScanForDevices();
+
+ CLog::Log(LOGDEBUG, "USB Device Detach:%s, %s\n",
+ privateDataRef->deviceName.c_str(), privateDataRef->result.m_strLocation.c_str());
+ result = IOObjectRelease(privateDataRef->notification);
+ delete privateDataRef;
+ //release the service
+ result = IOObjectRelease(service);
+ }
+}
+
+void CPeripheralBusUSB::DeviceAttachCallback(CPeripheralBusUSB* refCon, io_iterator_t iterator)
+{
+ io_service_t usbDevice;
+ while ((usbDevice = IOIteratorNext(iterator)))
+ {
+ IOReturn result;
+
+ io_name_t deviceName;
+ result = IORegistryEntryGetName(usbDevice, deviceName);
+ if (result != KERN_SUCCESS)
+ deviceName[0] = '\0';
+
+ SInt32 deviceScore;
+ IOCFPlugInInterface **devicePlugin;
+ result = IOCreatePlugInInterfaceForService(usbDevice,
+ kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devicePlugin, &deviceScore);
+ if (result != kIOReturnSuccess)
+ {
+ IOObjectRelease(usbDevice);
+ continue;
+ }
+
+ IOUSBDeviceInterface **deviceInterface;
+ // Use the plugin interface to retrieve the device interface.
+ result = (*devicePlugin)->QueryInterface(devicePlugin,
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&deviceInterface);
+ if (result != kIOReturnSuccess)
+ {
+ IODestroyPlugInInterface(devicePlugin);
+ IOObjectRelease(usbDevice);
+ continue;
+ }
+
+ // get vendor/product ids
+ UInt16 vendorId;
+ UInt16 productId;
+ UInt32 locationId;
+ UInt8 bDeviceClass;
+
+ result = (*deviceInterface)->GetDeviceVendor( deviceInterface, &vendorId);
+ result = (*deviceInterface)->GetDeviceProduct(deviceInterface, &productId);
+ result = (*deviceInterface)->GetLocationID( deviceInterface, &locationId);
+ result = (*deviceInterface)->GetDeviceClass( deviceInterface, &bDeviceClass);
+
+ io_service_t usbInterface;
+ io_iterator_t interface_iterator;
+ IOUSBFindInterfaceRequest request;
+ request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+ request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+ result = (*deviceInterface)->CreateInterfaceIterator(deviceInterface, &request, &interface_iterator);
+ while ((usbInterface = IOIteratorNext(interface_iterator)))
+ {
+ SInt32 interfaceScore;
+ IOCFPlugInInterface **interfacePlugin;
+ //create intermediate plugin for interface access
+ result = IOCreatePlugInInterfaceForService(usbInterface,
+ kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &interfacePlugin, &interfaceScore);
+ if (result != kIOReturnSuccess)
+ {
+ IOObjectRelease(usbInterface);
+ continue;
+ }
+ IOUSBInterfaceInterface** interfaceInterface;
+ result = (*interfacePlugin)->QueryInterface(interfacePlugin,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (void**)&interfaceInterface);
+ if (result != kIOReturnSuccess)
+ {
+ IODestroyPlugInInterface(interfacePlugin);
+ IOObjectRelease(usbInterface);
+ continue;
+ }
+
+ // finally we can get to the bInterfaceClass
+ // we should also check for kHIDKeyboardInterfaceProtocol but
+ // some IR remotes that emulate an HID keyboard do not report this.
+ UInt8 bInterfaceClass;
+ result = (*interfaceInterface)->GetInterfaceClass(interfaceInterface, &bInterfaceClass);
+ if (bInterfaceClass == kUSBHIDInterfaceClass || bInterfaceClass == kUSBCommunicationDataInterfaceClass)
+ {
+ std::string ttlDeviceFilePath;
+ CFStringRef deviceFilePathAsCFString;
+ USBDevicePrivateData *privateDataRef;
+ privateDataRef = new USBDevicePrivateData;
+ // save the device info to our private data.
+ privateDataRef->refCon = refCon;
+ privateDataRef->deviceName = deviceName;
+ privateDataRef->result.m_iVendorId = vendorId;
+ privateDataRef->result.m_iProductId = productId;
+
+ if (bInterfaceClass == kUSBCommunicationDataInterfaceClass)
+ {
+ // fetch the bds device path if this is USB serial device.
+ // to do this we have to switch from the kIOUSBPlane to
+ // kIOServicePlane, then we can search down for the path.
+ io_registry_entry_t parent;
+ kern_return_t kresult;
+ kresult = IORegistryEntryGetParentEntry(usbInterface, kIOServicePlane, &parent);
+ if (kresult == KERN_SUCCESS)
+ {
+ deviceFilePathAsCFString = (CFStringRef)IORegistryEntrySearchCFProperty(parent,
+ kIOServicePlane, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, kIORegistryIterateRecursively);
+ if (deviceFilePathAsCFString)
+ {
+ // Convert the path from a CFString to a std::string
+ if (!CDarwinUtils::CFStringRefToUTF8String(deviceFilePathAsCFString, ttlDeviceFilePath))
+ CLog::Log(LOGWARNING, "CPeripheralBusUSB::DeviceAttachCallback failed to convert CFStringRef");
+ CFRelease(deviceFilePathAsCFString);
+ }
+ IOObjectRelease(parent);
+ }
+ }
+ if (!ttlDeviceFilePath.empty())
+ privateDataRef->result.m_strLocation = StringUtils::Format("%s", ttlDeviceFilePath.c_str());
+ else
+ privateDataRef->result.m_strLocation = StringUtils::Format("%d", locationId);
+
+ if (bDeviceClass == kUSBCompositeClass)
+ privateDataRef->result.m_type = refCon->GetType(bInterfaceClass);
+ else
+ privateDataRef->result.m_type = refCon->GetType(bDeviceClass);
+
+ privateDataRef->result.m_iSequence = refCon->GetNumberOfPeripheralsWithId(privateDataRef->result.m_iVendorId, privateDataRef->result.m_iProductId);
+ if (!refCon->m_scan_results.ContainsResult(privateDataRef->result))
+ {
+ // register this usb device for an interest notification callback.
+ result = IOServiceAddInterestNotification(refCon->m_notify_port,
+ usbDevice, // service
+ kIOGeneralInterest, // interestType
+ (IOServiceInterestCallback)DeviceDetachCallback, // callback
+ privateDataRef, // refCon
+ &privateDataRef->notification); // notification
+
+ if (result == kIOReturnSuccess)
+ {
+ refCon->m_scan_results.m_results.push_back(privateDataRef->result);
+ CLog::Log(LOGDEBUG, "USB Device Attach:%s, %s\n",
+ deviceName, privateDataRef->result.m_strLocation.c_str());
+ }
+ else
+ {
+ delete privateDataRef;
+ }
+ }
+ else
+ {
+ delete privateDataRef;
+ }
+ // done with this device, only need one notification per device.
+ IODestroyPlugInInterface(interfacePlugin);
+ IOObjectRelease(usbInterface);
+ break;
+ }
+ IODestroyPlugInInterface(interfacePlugin);
+ IOObjectRelease(usbInterface);
+ }
+ IODestroyPlugInInterface(devicePlugin);
+ IOObjectRelease(usbDevice);
+ }
+ refCon->ScanForDevices();
+}
+#endif
diff --git a/src/peripherals/bus/osx/PeripheralBusUSB.h b/src/peripherals/bus/osx/PeripheralBusUSB.h
new file mode 100644
index 0000000000..46ca534899
--- /dev/null
+++ b/src/peripherals/bus/osx/PeripheralBusUSB.h
@@ -0,0 +1,57 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "peripherals/bus/PeripheralBus.h"
+#include "peripherals/devices/Peripheral.h"
+
+#ifdef TARGET_DARWIN_OSX
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOMessage.h>
+#endif
+
+namespace PERIPHERALS
+{
+ class CPeripherals;
+
+ class CPeripheralBusUSB : public CPeripheralBus
+ {
+ public:
+ CPeripheralBusUSB(CPeripherals *manager);
+ virtual ~CPeripheralBusUSB();
+
+ /*!
+ * @see PeripheralBus::PerformDeviceScan()
+ */
+ bool PerformDeviceScan(PeripheralScanResults &results);
+
+ protected:
+ PeripheralScanResults m_scan_results;
+ #ifdef TARGET_DARWIN_OSX
+ static const PeripheralType GetType(int iDeviceClass);
+ static void DeviceDetachCallback(void *refCon, io_service_t service, natural_t messageType, void *messageArgument);
+ static void DeviceAttachCallback(CPeripheralBusUSB* refCon, io_iterator_t iterator);
+
+ IONotificationPortRef m_notify_port;
+ io_iterator_t m_attach_iterator;
+ #endif
+ };
+
+}
diff --git a/src/peripherals/bus/virtual/PeripheralBusCEC.cpp b/src/peripherals/bus/virtual/PeripheralBusCEC.cpp
new file mode 100644
index 0000000000..4941ae644a
--- /dev/null
+++ b/src/peripherals/bus/virtual/PeripheralBusCEC.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#if defined(HAVE_LIBCEC)
+#include "PeripheralBusCEC.h"
+#include "peripherals/Peripherals.h"
+#include "utils/log.h"
+#include "DynamicDll.h"
+
+#include <libcec/cec.h>
+
+using namespace PERIPHERALS;
+using namespace CEC;
+using namespace std;
+
+class DllLibCECInterface
+{
+public:
+ virtual ~DllLibCECInterface() {}
+ virtual ICECAdapter* CECInitialise(libcec_configuration *configuration)=0;
+ virtual void* CECDestroy(ICECAdapter *adapter)=0;
+};
+
+class PERIPHERALS::DllLibCEC : public DllDynamic, DllLibCECInterface
+{
+ DECLARE_DLL_WRAPPER(DllLibCEC, DLL_PATH_LIBCEC)
+
+ DEFINE_METHOD1(ICECAdapter*, CECInitialise, (libcec_configuration *p1))
+ DEFINE_METHOD1(void* , CECDestroy, (ICECAdapter *p1))
+
+ BEGIN_METHOD_RESOLVE()
+ RESOLVE_METHOD_RENAME(CECInitialise, CECInitialise)
+ RESOLVE_METHOD_RENAME(CECDestroy, CECDestroy)
+ END_METHOD_RESOLVE()
+};
+
+CPeripheralBusCEC::CPeripheralBusCEC(CPeripherals *manager) :
+ CPeripheralBus("PeripBusCEC", manager, PERIPHERAL_BUS_CEC),
+ m_dll(new DllLibCEC),
+ m_cecAdapter(NULL)
+{
+ m_iRescanTime = 5000;
+ if (!m_dll->Load() || !m_dll->IsLoaded())
+ {
+ delete m_dll;
+ m_dll = NULL;
+ }
+ else
+ {
+ m_cecAdapter = m_dll->CECInitialise(&m_configuration);
+ }
+}
+
+CPeripheralBusCEC::~CPeripheralBusCEC(void)
+{
+ if (m_dll && m_cecAdapter)
+ m_dll->CECDestroy(m_cecAdapter);
+ delete m_dll;
+}
+
+bool CPeripheralBusCEC::PerformDeviceScan(PeripheralScanResults &results)
+{
+ if (!m_dll || !m_cecAdapter)
+ return false;
+
+ cec_adapter_descriptor deviceList[10];
+ int8_t iFound = m_cecAdapter->DetectAdapters(deviceList, 10, NULL, true);
+
+ for (uint8_t iDevicePtr = 0; iDevicePtr < iFound; iDevicePtr++)
+ {
+ PeripheralScanResult result(m_type);
+ result.m_iVendorId = deviceList[iDevicePtr].iVendorId;
+ result.m_iProductId = deviceList[iDevicePtr].iProductId;
+ result.m_strLocation = deviceList[iDevicePtr].strComName;
+ result.m_type = PERIPHERAL_CEC;
+
+ // override the bus type, so users don't have to reconfigure their adapters
+ switch(deviceList[iDevicePtr].adapterType)
+ {
+ case ADAPTERTYPE_P8_EXTERNAL:
+ case ADAPTERTYPE_P8_DAUGHTERBOARD:
+ result.m_mappedBusType = PERIPHERAL_BUS_USB;
+ break;
+ case ADAPTERTYPE_RPI:
+ result.m_mappedBusType = PERIPHERAL_BUS_RPI;
+ /** the Pi's adapter cannot be removed, no need to rescan */
+ m_bNeedsPolling = false;
+ break;
+ default:
+ break;
+ }
+
+ result.m_iSequence = GetNumberOfPeripheralsWithId(result.m_iVendorId, result.m_iProductId);
+ if (!results.ContainsResult(result))
+ results.m_results.push_back(result);
+ }
+ return true;
+}
+
+#endif
diff --git a/src/peripherals/bus/virtual/PeripheralBusCEC.h b/src/peripherals/bus/virtual/PeripheralBusCEC.h
new file mode 100644
index 0000000000..acba01a856
--- /dev/null
+++ b/src/peripherals/bus/virtual/PeripheralBusCEC.h
@@ -0,0 +1,57 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "peripherals/bus/PeripheralBus.h"
+#include "peripherals/devices/Peripheral.h"
+
+// undefine macro isset, it collides with function in cectypes.h
+#ifdef isset
+#undef isset
+#endif
+#include <libcec/cectypes.h>
+
+namespace CEC
+{
+ class ICECAdapter;
+}
+
+namespace PERIPHERALS
+{
+ class CPeripherals;
+ class DllLibCEC;
+
+ class CPeripheralBusCEC : public CPeripheralBus
+ {
+ public:
+ CPeripheralBusCEC(CPeripherals *manager);
+ virtual ~CPeripheralBusCEC(void);
+
+ /*!
+ * @see PeripheralBus::PerformDeviceScan()
+ */
+ bool PerformDeviceScan(PeripheralScanResults &results);
+
+ private:
+ DllLibCEC* m_dll;
+ CEC::ICECAdapter* m_cecAdapter;
+ CEC::libcec_configuration m_configuration;
+ };
+}
diff --git a/src/peripherals/bus/win32/PeripheralBusUSB.cpp b/src/peripherals/bus/win32/PeripheralBusUSB.cpp
new file mode 100644
index 0000000000..e53feede0a
--- /dev/null
+++ b/src/peripherals/bus/win32/PeripheralBusUSB.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralBusUSB.h"
+#include "peripherals/Peripherals.h"
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+
+const static GUID USB_RAW_GUID = { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
+const static GUID USB_HID_GUID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
+const static GUID USB_DISK_GUID = { 0x53F56307, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B } };
+const static GUID USB_NIC_GUID = { 0xAD498944, 0x762F, 0x11D0, { 0x8D, 0xCB, 0x00, 0xC0, 0x4F, 0xC3, 0x35, 0x8C } };
+
+using namespace PERIPHERALS;
+
+// Only to avoid endless loops while scanning for devices
+#define MAX_BUS_DEVICES 2000
+
+CPeripheralBusUSB::CPeripheralBusUSB(CPeripherals *manager) :
+ CPeripheralBus("PeripBusUSB", manager, PERIPHERAL_BUS_USB)
+{
+ /* device removals aren't always triggering OnDeviceRemoved events, so poll for changes every 5 seconds to be sure we don't miss anything */
+ m_iRescanTime = 5000;
+}
+
+bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results)
+{
+ /* XXX we'll just scan the RAW guid and find all devices. they'll show up as type 'unknown' in the UI,
+ but the other option is that they're detected more than once, because RAW will return all devices.
+ we have to scan the RAW guid here, because not every device is found by it's GUID correctly, e.g. CDC adapters. */
+ return PerformDeviceScan(&USB_RAW_GUID, PERIPHERAL_UNKNOWN, results);
+}
+
+bool CPeripheralBusUSB::PerformDeviceScan(const GUID *guid, const PeripheralType defaultType, PeripheralScanResults &results)
+{
+ bool bReturn(false);
+ DWORD required = 0, iMemberIndex = 0;
+ int nBufferSize = 200; // Just initial guess, will be increased if required
+
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
+ deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+
+ SP_DEVINFO_DATA devInfoData;
+ devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+
+ HDEVINFO const hDevHandle = SetupDiGetClassDevs(guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if (hDevHandle == INVALID_HANDLE_VALUE)
+ {
+ CLog::Log(LOGWARNING, "%s - cannot query USB devices: invalid handle", __FUNCTION__);
+ return bReturn;
+ }
+
+ PSP_DEVICE_INTERFACE_DETAIL_DATA devicedetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(nBufferSize);
+ int nPropertyBufferSize = 100; // Just initial guess, will be increased if required
+ char* deviceProperty = (char*)malloc(nPropertyBufferSize);
+ if (!devicedetailData || !deviceProperty)
+ {
+ free(devicedetailData);
+ free(deviceProperty);
+ CLog::Log(LOGSEVERE, "%s: memory allocation failed", __FUNCTION__);
+ return false;
+ }
+
+ SP_DEVINFO_DATA deviceInfo;
+ bReturn = true;
+ for (iMemberIndex = 0; bReturn && iMemberIndex < MAX_BUS_DEVICES; iMemberIndex++)
+ {
+ bReturn = SetupDiEnumDeviceInfo(hDevHandle, iMemberIndex, &devInfoData) == TRUE;
+
+ if (bReturn)
+ bReturn = SetupDiEnumDeviceInterfaces(hDevHandle, 0, guid, iMemberIndex, &deviceInterfaceData) == TRUE;
+ else
+ {
+ bReturn = true;
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break; // no more USB devices, nothing more to scan
+ else
+ continue; // try to get other USB devices
+ }
+
+ if (bReturn)
+ {
+ devicedetailData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
+ deviceInfo.cbSize = sizeof(SP_DEVINFO_DATA);
+
+ BOOL bDetailResult = SetupDiGetDeviceInterfaceDetail(hDevHandle, &deviceInterfaceData, devicedetailData, nBufferSize , &required, &deviceInfo);
+ if (!bDetailResult && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ free(devicedetailData);
+ devicedetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(required * sizeof(TCHAR));
+ if (!devicedetailData)
+ {
+ free(deviceProperty);
+ CLog::Log(LOGSEVERE, "%s: memory allocation failed", __FUNCTION__);
+ return false;
+ }
+ devicedetailData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
+ nBufferSize = required;
+ bDetailResult = SetupDiGetDeviceInterfaceDetail(hDevHandle, &deviceInterfaceData, devicedetailData, nBufferSize , &required, &deviceInfo);
+ }
+
+ if (bDetailResult)
+ {
+ bDetailResult = SetupDiGetDeviceRegistryProperty(hDevHandle, &deviceInfo, SPDRP_HARDWAREID, NULL, (PBYTE)deviceProperty, nPropertyBufferSize, &required);
+ if (!bDetailResult && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ free(deviceProperty);
+ deviceProperty = (char*)malloc(required);
+ if (!deviceProperty)
+ {
+ free(devicedetailData);
+ CLog::Log(LOGSEVERE, "%s: memory allocation failed", __FUNCTION__);
+ return false;
+ }
+ nPropertyBufferSize = required;
+ bDetailResult = SetupDiGetDeviceRegistryProperty(hDevHandle, &deviceInfo, SPDRP_HARDWAREID, NULL, (PBYTE)deviceProperty, nPropertyBufferSize, &required);
+ }
+ }
+
+ if (bDetailResult)
+ {
+ std::string strTmp(deviceProperty);
+
+ StringUtils::ToLower(strTmp);
+ size_t posVid, posPid;
+ if (((posVid=strTmp.find("\\vid_")) != std::string::npos || (posVid=strTmp.find("&vid_")) != std::string::npos) &&
+ ((posPid=strTmp.find("\\pid_")) != std::string::npos || (posPid=strTmp.find("&pid_")) != std::string::npos))
+ {
+ std::string strVendorId(strTmp, posVid + 5, 4);
+ std::string strProductId(strTmp, posPid + 5, 4);
+ PeripheralScanResult prevDevice(m_type);
+ if (!results.GetDeviceOnLocation(devicedetailData->DevicePath, &prevDevice))
+ {
+ PeripheralScanResult result(m_type);
+ result.m_strLocation = devicedetailData->DevicePath;
+ result.m_iVendorId = PeripheralTypeTranslator::HexStringToInt(strVendorId.c_str());
+ result.m_iProductId = PeripheralTypeTranslator::HexStringToInt(strProductId.c_str());
+ result.m_iSequence = GetNumberOfPeripheralsWithId(result.m_iVendorId, result.m_iProductId);
+
+ // Assume that buffer is more then enough (we need only 8 chars, initial allocation is 100 chars). If not - just skip type detection.
+ if (SetupDiGetDeviceRegistryProperty(hDevHandle, &devInfoData, SPDRP_CLASS, NULL, (PBYTE)deviceProperty, nPropertyBufferSize, &required) &&
+ strcmp("HIDClass", deviceProperty) == 0)
+ result.m_type = PERIPHERAL_HID;
+ else
+ result.m_type = defaultType;
+
+ if (!results.ContainsResult(result))
+ results.m_results.push_back(result);
+ }
+ }
+ }
+ }
+ }
+
+ SetupDiDestroyDeviceInfoList(hDevHandle);
+ if (devicedetailData)
+ free(devicedetailData);
+ if (deviceProperty)
+ free(deviceProperty);
+
+ return bReturn;
+}
diff --git a/src/peripherals/bus/win32/PeripheralBusUSB.h b/src/peripherals/bus/win32/PeripheralBusUSB.h
new file mode 100644
index 0000000000..92c51d3a08
--- /dev/null
+++ b/src/peripherals/bus/win32/PeripheralBusUSB.h
@@ -0,0 +1,44 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "peripherals/bus/PeripheralBus.h"
+#include "peripherals/devices/Peripheral.h"
+#include <setupapi.h> //needed for GUID
+
+namespace PERIPHERALS
+{
+ class CPeripherals;
+
+ class CPeripheralBusUSB : public CPeripheralBus
+ {
+ public:
+ CPeripheralBusUSB(CPeripherals *manager);
+
+ /*!
+ * @see PeripheralBus::PerformDeviceScan()
+ */
+ bool PerformDeviceScan(PeripheralScanResults &results);
+
+ private:
+ bool PerformDeviceScan(const GUID *guid, const PeripheralType defaultType, PeripheralScanResults &results);
+ bool GetProductAndVendorId(const PeripheralType type, const CStdString &strDeviceLocation, int *iVendorId, int *iProductId);
+ };
+}
diff --git a/src/peripherals/devices/Makefile.in b/src/peripherals/devices/Makefile.in
new file mode 100644
index 0000000000..23989f28b2
--- /dev/null
+++ b/src/peripherals/devices/Makefile.in
@@ -0,0 +1,17 @@
+SRCS = Peripheral.cpp
+SRCS += PeripheralBluetooth.cpp
+SRCS += PeripheralDisk.cpp
+SRCS += PeripheralHID.cpp
+SRCS += PeripheralImon.cpp
+SRCS += PeripheralNIC.cpp
+SRCS += PeripheralNyxboard.cpp
+SRCS += PeripheralTuner.cpp
+
+ifeq (@USE_LIBCEC@,1)
+SRCS += PeripheralCecAdapter.cpp
+endif
+
+LIB = peripheral-devices.a
+
+include @abs_top_srcdir@/Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/src/peripherals/devices/Peripheral.cpp b/src/peripherals/devices/Peripheral.cpp
new file mode 100644
index 0000000000..b1eaa1fa27
--- /dev/null
+++ b/src/peripherals/devices/Peripheral.cpp
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Peripheral.h"
+#include "peripherals/Peripherals.h"
+#include "utils/log.h"
+#include "utils/StringUtils.h"
+#include "settings/lib/Setting.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/URIUtils.h"
+#include "guilib/LocalizeStrings.h"
+
+using namespace PERIPHERALS;
+using namespace std;
+
+struct SortBySettingsOrder
+{
+ bool operator()(const PeripheralDeviceSetting &left, const PeripheralDeviceSetting& right)
+ {
+ return left.m_order < right.m_order;
+ }
+};
+
+CPeripheral::CPeripheral(const PeripheralScanResult& scanResult) :
+ m_type(scanResult.m_mappedType),
+ m_busType(scanResult.m_busType),
+ m_mappedBusType(scanResult.m_mappedBusType),
+ m_strLocation(scanResult.m_strLocation),
+ m_strDeviceName(scanResult.m_strDeviceName),
+ m_strFileLocation(StringUtils::EmptyString),
+ m_iVendorId(scanResult.m_iVendorId),
+ m_iProductId(scanResult.m_iProductId),
+ m_strVersionInfo(g_localizeStrings.Get(13205)), // "unknown"
+ m_bInitialised(false),
+ m_bHidden(false),
+ m_bError(false)
+{
+ PeripheralTypeTranslator::FormatHexString(scanResult.m_iVendorId, m_strVendorId);
+ PeripheralTypeTranslator::FormatHexString(scanResult.m_iProductId, m_strProductId);
+ m_strFileLocation = StringUtils::Format(scanResult.m_iSequence > 0 ? "peripherals://%s/%s_%d.dev" : "peripherals://%s/%s.dev",
+ PeripheralTypeTranslator::BusTypeToString(scanResult.m_busType),
+ scanResult.m_strLocation.c_str(),
+ scanResult.m_iSequence);
+}
+
+CPeripheral::~CPeripheral(void)
+{
+ PersistSettings(true);
+
+ for (unsigned int iSubdevicePtr = 0; iSubdevicePtr < m_subDevices.size(); iSubdevicePtr++)
+ delete m_subDevices.at(iSubdevicePtr);
+ m_subDevices.clear();
+
+ ClearSettings();
+}
+
+bool CPeripheral::operator ==(const CPeripheral &right) const
+{
+ return m_type == right.m_type &&
+ m_strLocation == right.m_strLocation &&
+ m_iVendorId == right.m_iVendorId &&
+ m_iProductId == right.m_iProductId;
+}
+
+bool CPeripheral::operator !=(const CPeripheral &right) const
+{
+ return !(*this == right);
+}
+
+bool CPeripheral::HasFeature(const PeripheralFeature feature) const
+{
+ bool bReturn(false);
+
+ for (unsigned int iFeaturePtr = 0; iFeaturePtr < m_features.size(); iFeaturePtr++)
+ {
+ if (m_features.at(iFeaturePtr) == feature)
+ {
+ bReturn = true;
+ break;
+ }
+ }
+
+ if (!bReturn)
+ {
+ for (unsigned int iSubdevicePtr = 0; iSubdevicePtr < m_subDevices.size(); iSubdevicePtr++)
+ {
+ if (m_subDevices.at(iSubdevicePtr)->HasFeature(feature))
+ {
+ bReturn = true;
+ break;
+ }
+ }
+ }
+
+ return bReturn;
+}
+
+void CPeripheral::GetFeatures(std::vector<PeripheralFeature> &features) const
+{
+ for (unsigned int iFeaturePtr = 0; iFeaturePtr < m_features.size(); iFeaturePtr++)
+ features.push_back(m_features.at(iFeaturePtr));
+
+ for (unsigned int iSubdevicePtr = 0; iSubdevicePtr < m_subDevices.size(); iSubdevicePtr++)
+ m_subDevices.at(iSubdevicePtr)->GetFeatures(features);
+}
+
+bool CPeripheral::Initialise(void)
+{
+ bool bReturn(false);
+
+ if (m_bError)
+ return bReturn;
+
+ bReturn = true;
+ if (m_bInitialised)
+ return bReturn;
+
+ g_peripherals.GetSettingsFromMapping(*this);
+ m_strSettingsFile = StringUtils::Format("special://profile/peripheral_data/%s_%s_%s.xml",
+ PeripheralTypeTranslator::BusTypeToString(m_mappedBusType),
+ m_strVendorId.c_str(),
+ m_strProductId.c_str());
+ LoadPersistedSettings();
+
+ for (unsigned int iFeaturePtr = 0; iFeaturePtr < m_features.size(); iFeaturePtr++)
+ {
+ PeripheralFeature feature = m_features.at(iFeaturePtr);
+ bReturn &= InitialiseFeature(feature);
+ }
+
+ for (unsigned int iSubdevicePtr = 0; iSubdevicePtr < m_subDevices.size(); iSubdevicePtr++)
+ bReturn &= m_subDevices.at(iSubdevicePtr)->Initialise();
+
+ if (bReturn)
+ {
+ CLog::Log(LOGDEBUG, "%s - initialised peripheral on '%s' with %d features and %d sub devices",
+ __FUNCTION__, m_strLocation.c_str(), (int)m_features.size(), (int)m_subDevices.size());
+ m_bInitialised = true;
+ }
+
+ return bReturn;
+}
+
+void CPeripheral::GetSubdevices(vector<CPeripheral *> &subDevices) const
+{
+ for (unsigned int iSubdevicePtr = 0; iSubdevicePtr < m_subDevices.size(); iSubdevicePtr++)
+ subDevices.push_back(m_subDevices.at(iSubdevicePtr));
+}
+
+bool CPeripheral::IsMultiFunctional(void) const
+{
+ return m_subDevices.size() > 0;
+}
+
+vector<CSetting *> CPeripheral::GetSettings(void) const
+{
+ vector<PeripheralDeviceSetting> tmpSettings;
+ for (map<CStdString, PeripheralDeviceSetting>::const_iterator it = m_settings.begin(); it != m_settings.end(); ++it)
+ tmpSettings.push_back(it->second);
+ sort(tmpSettings.begin(), tmpSettings.end(), SortBySettingsOrder());
+
+ vector<CSetting *> settings;
+ for (vector<PeripheralDeviceSetting>::const_iterator it = tmpSettings.begin(); it != tmpSettings.end(); ++it)
+ settings.push_back(it->m_setting);
+ return settings;
+}
+
+void CPeripheral::AddSetting(const CStdString &strKey, const CSetting *setting, int order)
+{
+ if (!setting)
+ {
+ CLog::Log(LOGERROR, "%s - invalid setting", __FUNCTION__);
+ return;
+ }
+
+ if (!HasSetting(strKey))
+ {
+ PeripheralDeviceSetting deviceSetting = { NULL, order };
+ switch(setting->GetType())
+ {
+ case SettingTypeBool:
+ {
+ const CSettingBool *mappedSetting = (const CSettingBool *) setting;
+ CSettingBool *boolSetting = new CSettingBool(strKey, *mappedSetting);
+ if (boolSetting)
+ {
+ boolSetting->SetVisible(mappedSetting->IsVisible());
+ deviceSetting.m_setting = boolSetting;
+ }
+ }
+ break;
+ case SettingTypeInteger:
+ {
+ const CSettingInt *mappedSetting = (const CSettingInt *) setting;
+ CSettingInt *intSetting = new CSettingInt(strKey, *mappedSetting);
+ if (intSetting)
+ {
+ intSetting->SetVisible(mappedSetting->IsVisible());
+ deviceSetting.m_setting = intSetting;
+ }
+ }
+ break;
+ case SettingTypeNumber:
+ {
+ const CSettingNumber *mappedSetting = (const CSettingNumber *) setting;
+ CSettingNumber *floatSetting = new CSettingNumber(strKey, *mappedSetting);
+ if (floatSetting)
+ {
+ floatSetting->SetVisible(mappedSetting->IsVisible());
+ deviceSetting.m_setting = floatSetting;
+ }
+ }
+ break;
+ case SettingTypeString:
+ {
+ const CSettingString *mappedSetting = (const CSettingString *) setting;
+ CSettingString *stringSetting = new CSettingString(strKey, *mappedSetting);
+ if (stringSetting)
+ {
+ stringSetting->SetVisible(mappedSetting->IsVisible());
+ deviceSetting.m_setting = stringSetting;
+ }
+ }
+ break;
+ default:
+ //TODO add more types if needed
+ break;
+ }
+
+ if (deviceSetting.m_setting != NULL)
+ m_settings.insert(make_pair(strKey, deviceSetting));
+ }
+}
+
+bool CPeripheral::HasSetting(const CStdString &strKey) const
+{
+ map<CStdString, PeripheralDeviceSetting>:: const_iterator it = m_settings.find(strKey);
+ return it != m_settings.end();
+}
+
+bool CPeripheral::HasSettings(void) const
+{
+ return !m_settings.empty();
+}
+
+bool CPeripheral::HasConfigurableSettings(void) const
+{
+ bool bReturn(false);
+ map<CStdString, PeripheralDeviceSetting>::const_iterator it = m_settings.begin();
+ while (it != m_settings.end() && !bReturn)
+ {
+ if ((*it).second.m_setting->IsVisible())
+ {
+ bReturn = true;
+ break;
+ }
+
+ ++it;
+ }
+
+ return bReturn;
+}
+
+bool CPeripheral::GetSettingBool(const CStdString &strKey) const
+{
+ map<CStdString, PeripheralDeviceSetting>::const_iterator it = m_settings.find(strKey);
+ if (it != m_settings.end() && (*it).second.m_setting->GetType() == SettingTypeBool)
+ {
+ CSettingBool *boolSetting = (CSettingBool *) (*it).second.m_setting;
+ if (boolSetting)
+ return boolSetting->GetValue();
+ }
+
+ return false;
+}
+
+int CPeripheral::GetSettingInt(const CStdString &strKey) const
+{
+ map<CStdString, PeripheralDeviceSetting>::const_iterator it = m_settings.find(strKey);
+ if (it != m_settings.end() && (*it).second.m_setting->GetType() == SettingTypeInteger)
+ {
+ CSettingInt *intSetting = (CSettingInt *) (*it).second.m_setting;
+ if (intSetting)
+ return intSetting->GetValue();
+ }
+
+ return 0;
+}
+
+float CPeripheral::GetSettingFloat(const CStdString &strKey) const
+{
+ map<CStdString, PeripheralDeviceSetting>::const_iterator it = m_settings.find(strKey);
+ if (it != m_settings.end() && (*it).second.m_setting->GetType() == SettingTypeNumber)
+ {
+ CSettingNumber *floatSetting = (CSettingNumber *) (*it).second.m_setting;
+ if (floatSetting)
+ return (float)floatSetting->GetValue();
+ }
+
+ return 0;
+}
+
+const CStdString CPeripheral::GetSettingString(const CStdString &strKey) const
+{
+ map<CStdString, PeripheralDeviceSetting>::const_iterator it = m_settings.find(strKey);
+ if (it != m_settings.end() && (*it).second.m_setting->GetType() == SettingTypeString)
+ {
+ CSettingString *stringSetting = (CSettingString *) (*it).second.m_setting;
+ if (stringSetting)
+ return stringSetting->GetValue();
+ }
+
+ return StringUtils::EmptyString;
+}
+
+bool CPeripheral::SetSetting(const CStdString &strKey, bool bValue)
+{
+ bool bChanged(false);
+ map<CStdString, PeripheralDeviceSetting>::iterator it = m_settings.find(strKey);
+ if (it != m_settings.end() && (*it).second.m_setting->GetType() == SettingTypeBool)
+ {
+ CSettingBool *boolSetting = (CSettingBool *) (*it).second.m_setting;
+ if (boolSetting)
+ {
+ bChanged = boolSetting->GetValue() != bValue;
+ boolSetting->SetValue(bValue);
+ if (bChanged && m_bInitialised)
+ m_changedSettings.insert(strKey);
+ }
+ }
+ return bChanged;
+}
+
+bool CPeripheral::SetSetting(const CStdString &strKey, int iValue)
+{
+ bool bChanged(false);
+ map<CStdString, PeripheralDeviceSetting>::iterator it = m_settings.find(strKey);
+ if (it != m_settings.end() && (*it).second.m_setting->GetType() == SettingTypeInteger)
+ {
+ CSettingInt *intSetting = (CSettingInt *) (*it).second.m_setting;
+ if (intSetting)
+ {
+ bChanged = intSetting->GetValue() != iValue;
+ intSetting->SetValue(iValue);
+ if (bChanged && m_bInitialised)
+ m_changedSettings.insert(strKey);
+ }
+ }
+ return bChanged;
+}
+
+bool CPeripheral::SetSetting(const CStdString &strKey, float fValue)
+{
+ bool bChanged(false);
+ map<CStdString, PeripheralDeviceSetting>::iterator it = m_settings.find(strKey);
+ if (it != m_settings.end() && (*it).second.m_setting->GetType() == SettingTypeNumber)
+ {
+ CSettingNumber *floatSetting = (CSettingNumber *) (*it).second.m_setting;
+ if (floatSetting)
+ {
+ bChanged = floatSetting->GetValue() != fValue;
+ floatSetting->SetValue(fValue);
+ if (bChanged && m_bInitialised)
+ m_changedSettings.insert(strKey);
+ }
+ }
+ return bChanged;
+}
+
+void CPeripheral::SetSettingVisible(const CStdString &strKey, bool bSetTo)
+{
+ map<CStdString, PeripheralDeviceSetting>::iterator it = m_settings.find(strKey);
+ if (it != m_settings.end())
+ (*it).second.m_setting->SetVisible(bSetTo);
+}
+
+bool CPeripheral::IsSettingVisible(const CStdString &strKey) const
+{
+ map<CStdString, PeripheralDeviceSetting>::const_iterator it = m_settings.find(strKey);
+ if (it != m_settings.end())
+ return (*it).second.m_setting->IsVisible();
+ return false;
+}
+
+bool CPeripheral::SetSetting(const CStdString &strKey, const CStdString &strValue)
+{
+ bool bChanged(false);
+ map<CStdString, PeripheralDeviceSetting>::iterator it = m_settings.find(strKey);
+ if (it != m_settings.end())
+ {
+ if ((*it).second.m_setting->GetType() == SettingTypeString)
+ {
+ CSettingString *stringSetting = (CSettingString *) (*it).second.m_setting;
+ if (stringSetting)
+ {
+ bChanged = !StringUtils::EqualsNoCase(stringSetting->GetValue(), strValue);
+ stringSetting->SetValue(strValue);
+ if (bChanged && m_bInitialised)
+ m_changedSettings.insert(strKey);
+ }
+ }
+ else if ((*it).second.m_setting->GetType() == SettingTypeInteger)
+ bChanged = SetSetting(strKey, (int) (strValue.empty() ? 0 : atoi(strValue.c_str())));
+ else if ((*it).second.m_setting->GetType() == SettingTypeNumber)
+ bChanged = SetSetting(strKey, (float) (strValue.empty() ? 0 : atof(strValue.c_str())));
+ else if ((*it).second.m_setting->GetType() == SettingTypeBool)
+ bChanged = SetSetting(strKey, strValue.Equals("1"));
+ }
+ return bChanged;
+}
+
+void CPeripheral::PersistSettings(bool bExiting /* = false */)
+{
+ CXBMCTinyXML doc;
+ TiXmlElement node("settings");
+ doc.InsertEndChild(node);
+ for (map<CStdString, PeripheralDeviceSetting>::const_iterator itr = m_settings.begin(); itr != m_settings.end(); ++itr)
+ {
+ TiXmlElement nodeSetting("setting");
+ nodeSetting.SetAttribute("id", itr->first.c_str());
+ CStdString strValue;
+ switch ((*itr).second.m_setting->GetType())
+ {
+ case SettingTypeString:
+ {
+ CSettingString *stringSetting = (CSettingString *) (*itr).second.m_setting;
+ if (stringSetting)
+ strValue = stringSetting->GetValue();
+ }
+ break;
+ case SettingTypeInteger:
+ {
+ CSettingInt *intSetting = (CSettingInt *) (*itr).second.m_setting;
+ if (intSetting)
+ strValue = StringUtils::Format("%d", intSetting->GetValue());
+ }
+ break;
+ case SettingTypeNumber:
+ {
+ CSettingNumber *floatSetting = (CSettingNumber *) (*itr).second.m_setting;
+ if (floatSetting)
+ strValue = StringUtils::Format("%.2f", floatSetting->GetValue());
+ }
+ break;
+ case SettingTypeBool:
+ {
+ CSettingBool *boolSetting = (CSettingBool *) (*itr).second.m_setting;
+ if (boolSetting)
+ strValue = StringUtils::Format("%d", boolSetting->GetValue() ? 1:0);
+ }
+ break;
+ default:
+ break;
+ }
+ nodeSetting.SetAttribute("value", strValue.c_str());
+ doc.RootElement()->InsertEndChild(nodeSetting);
+ }
+
+ doc.SaveFile(m_strSettingsFile);
+
+ if (!bExiting)
+ {
+ for (set<CStdString>::const_iterator it = m_changedSettings.begin(); it != m_changedSettings.end(); ++it)
+ OnSettingChanged(*it);
+ }
+ m_changedSettings.clear();
+}
+
+void CPeripheral::LoadPersistedSettings(void)
+{
+ CXBMCTinyXML doc;
+ if (doc.LoadFile(m_strSettingsFile))
+ {
+ const TiXmlElement *setting = doc.RootElement()->FirstChildElement("setting");
+ while (setting)
+ {
+ CStdString strId = XMLUtils::GetAttribute(setting, "id");
+ CStdString strValue = XMLUtils::GetAttribute(setting, "value");
+ SetSetting(strId, strValue);
+
+ setting = setting->NextSiblingElement("setting");
+ }
+ }
+}
+
+void CPeripheral::ResetDefaultSettings(void)
+{
+ ClearSettings();
+ g_peripherals.GetSettingsFromMapping(*this);
+
+ map<CStdString, PeripheralDeviceSetting>::iterator it = m_settings.begin();
+ while (it != m_settings.end())
+ {
+ m_changedSettings.insert((*it).first);
+ ++it;
+ }
+
+ PersistSettings();
+}
+
+void CPeripheral::ClearSettings(void)
+{
+ map<CStdString, PeripheralDeviceSetting>::iterator it = m_settings.begin();
+ while (it != m_settings.end())
+ {
+ delete (*it).second.m_setting;
+ ++it;
+ }
+ m_settings.clear();
+}
+
+bool CPeripheral::operator ==(const PeripheralScanResult& right) const
+{
+ return m_strLocation.Equals(right.m_strLocation);
+}
+
+bool CPeripheral::operator !=(const PeripheralScanResult& right) const
+{
+ return !(*this == right);
+}
diff --git a/src/peripherals/devices/Peripheral.h b/src/peripherals/devices/Peripheral.h
new file mode 100644
index 0000000000..10a7fb33cb
--- /dev/null
+++ b/src/peripherals/devices/Peripheral.h
@@ -0,0 +1,189 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <set>
+#include "utils/StdString.h"
+#include "peripherals/PeripheralTypes.h"
+
+class TiXmlDocument;
+
+class CSetting;
+
+namespace PERIPHERALS
+{
+ class CGUIDialogPeripheralSettings;
+
+ typedef enum
+ {
+ STATE_SWITCH_TOGGLE,
+ STATE_ACTIVATE_SOURCE,
+ STATE_STANDBY
+ } CecStateChange;
+
+ class CPeripheral
+ {
+ friend class CGUIDialogPeripheralSettings;
+
+ public:
+ CPeripheral(const PeripheralScanResult& scanResult);
+ virtual ~CPeripheral(void);
+
+ bool operator ==(const CPeripheral &right) const;
+ bool operator !=(const CPeripheral &right) const;
+ bool operator ==(const PeripheralScanResult& right) const;
+ bool operator !=(const PeripheralScanResult& right) const;
+
+ const CStdString &FileLocation(void) const { return m_strFileLocation; }
+ const CStdString &Location(void) const { return m_strLocation; }
+ int VendorId(void) const { return m_iVendorId; }
+ const char *VendorIdAsString(void) const { return m_strVendorId.c_str(); }
+ int ProductId(void) const { return m_iProductId; }
+ const char *ProductIdAsString(void) const { return m_strProductId.c_str(); }
+ const PeripheralType Type(void) const { return m_type; }
+ const PeripheralBusType GetBusType(void) const { return m_busType; };
+ const CStdString &DeviceName(void) const { return m_strDeviceName; }
+ bool IsHidden(void) const { return m_bHidden; }
+ void SetHidden(bool bSetTo = true) { m_bHidden = bSetTo; }
+ const CStdString &GetVersionInfo(void) const { return m_strVersionInfo; }
+
+ /*!
+ * @brief Check whether this device has the given feature.
+ * @param feature The feature to check for.
+ * @return True when the device has the feature, false otherwise.
+ */
+ bool HasFeature(const PeripheralFeature feature) const;
+
+ /*!
+ * @brief Get all features that are supported by this device.
+ * @param features The features.
+ */
+ void GetFeatures(std::vector<PeripheralFeature> &features) const;
+
+ /*!
+ * @brief Initialises the peripheral.
+ * @return True when the peripheral has been initialised succesfully, false otherwise.
+ */
+ bool Initialise(void);
+
+ /*!
+ * @brief Initialise one of the features of this peripheral.
+ * @param feature The feature to initialise.
+ * @return True when the feature has been initialised succesfully, false otherwise.
+ */
+ virtual bool InitialiseFeature(const PeripheralFeature feature) { return true; }
+
+ /*!
+ * @brief Called when a setting changed.
+ * @param strChangedSetting The changed setting.
+ */
+ virtual void OnSettingChanged(const CStdString &strChangedSetting) {};
+
+ /*!
+ * @brief Called when this device is removed, before calling the destructor.
+ */
+ virtual void OnDeviceRemoved(void) {}
+
+ /*!
+ * @brief Get all subdevices if this device is multifunctional.
+ * @param subDevices The subdevices.
+ */
+ virtual void GetSubdevices(std::vector<CPeripheral *> &subDevices) const;
+
+ /*!
+ * @return True when this device is multifunctional, false otherwise.
+ */
+ virtual bool IsMultiFunctional(void) const;
+
+ /*!
+ * @brief Add a setting to this peripheral. This will overwrite a previous setting with the same key.
+ * @param strKey The key of the setting.
+ * @param setting The setting.
+ */
+ virtual void AddSetting(const CStdString &strKey, const CSetting *setting, int order);
+
+ /*!
+ * @brief Check whether a setting is known with the given key.
+ * @param strKey The key to search.
+ * @return True when found, false otherwise.
+ */
+ virtual bool HasSetting(const CStdString &strKey) const;
+
+ /*!
+ * @return True when this device has any settings, false otherwise.
+ */
+ virtual bool HasSettings(void) const;
+
+ /*!
+ * @return True when this device has any configurable settings, false otherwise.
+ */
+ virtual bool HasConfigurableSettings(void) const;
+
+ /*!
+ * @brief Get the value of a setting.
+ * @param strKey The key to search.
+ * @return The value or an empty string if it wasn't found.
+ */
+ virtual const CStdString GetSettingString(const CStdString &strKey) const;
+ virtual bool SetSetting(const CStdString &strKey, const CStdString &strValue);
+ virtual void SetSettingVisible(const CStdString &strKey, bool bSetTo);
+ virtual bool IsSettingVisible(const CStdString &strKey) const;
+
+ virtual int GetSettingInt(const CStdString &strKey) const;
+ virtual bool SetSetting(const CStdString &strKey, int iValue);
+
+ virtual bool GetSettingBool(const CStdString &strKey) const;
+ virtual bool SetSetting(const CStdString &strKey, bool bValue);
+
+ virtual float GetSettingFloat(const CStdString &strKey) const;
+ virtual bool SetSetting(const CStdString &strKey, float fValue);
+
+ virtual void PersistSettings(bool bExiting = false);
+ virtual void LoadPersistedSettings(void);
+ virtual void ResetDefaultSettings(void);
+
+ virtual std::vector<CSetting *> GetSettings(void) const;
+
+ virtual bool ErrorOccured(void) const { return m_bError; }
+
+ protected:
+ virtual void ClearSettings(void);
+
+ PeripheralType m_type;
+ PeripheralBusType m_busType;
+ PeripheralBusType m_mappedBusType;
+ CStdString m_strLocation;
+ CStdString m_strDeviceName;
+ CStdString m_strSettingsFile;
+ CStdString m_strFileLocation;
+ int m_iVendorId;
+ CStdString m_strVendorId;
+ int m_iProductId;
+ CStdString m_strProductId;
+ CStdString m_strVersionInfo;
+ bool m_bInitialised;
+ bool m_bHidden;
+ bool m_bError;
+ std::vector<PeripheralFeature> m_features;
+ std::vector<CPeripheral *> m_subDevices;
+ std::map<CStdString, PeripheralDeviceSetting> m_settings;
+ std::set<CStdString> m_changedSettings;
+ };
+}
diff --git a/src/peripherals/devices/PeripheralBluetooth.cpp b/src/peripherals/devices/PeripheralBluetooth.cpp
new file mode 100644
index 0000000000..c48aa91ffc
--- /dev/null
+++ b/src/peripherals/devices/PeripheralBluetooth.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralBluetooth.h"
+#include "utils/log.h"
+
+using namespace PERIPHERALS;
+
+CPeripheralBluetooth::CPeripheralBluetooth(const PeripheralScanResult& scanResult) :
+ CPeripheral(scanResult)
+{
+ m_features.push_back(FEATURE_BLUETOOTH);
+}
diff --git a/src/peripherals/devices/PeripheralBluetooth.h b/src/peripherals/devices/PeripheralBluetooth.h
new file mode 100644
index 0000000000..292df34eab
--- /dev/null
+++ b/src/peripherals/devices/PeripheralBluetooth.h
@@ -0,0 +1,32 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Peripheral.h"
+
+namespace PERIPHERALS
+{
+ class CPeripheralBluetooth : public CPeripheral
+ {
+ public:
+ CPeripheralBluetooth(const PeripheralScanResult& scanResult);
+ virtual ~CPeripheralBluetooth(void) {};
+ };
+}
diff --git a/src/peripherals/devices/PeripheralCecAdapter.cpp b/src/peripherals/devices/PeripheralCecAdapter.cpp
new file mode 100644
index 0000000000..e80feb16bb
--- /dev/null
+++ b/src/peripherals/devices/PeripheralCecAdapter.cpp
@@ -0,0 +1,1746 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+#if defined(HAVE_LIBCEC)
+#include "PeripheralCecAdapter.h"
+#include "input/XBIRRemote.h"
+#include "Application.h"
+#include "ApplicationMessenger.h"
+#include "DynamicDll.h"
+#include "threads/SingleLock.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/Key.h"
+#include "guilib/LocalizeStrings.h"
+#include "peripherals/Peripherals.h"
+#include "peripherals/bus/PeripheralBus.h"
+#include "pictures/GUIWindowSlideShow.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "utils/log.h"
+#include "utils/Variant.h"
+
+#include <libcec/cec.h>
+
+using namespace PERIPHERALS;
+using namespace ANNOUNCEMENT;
+using namespace CEC;
+using namespace std;
+
+#define CEC_LIB_SUPPORTED_VERSION 0x2100
+
+/* time in seconds to ignore standby commands from devices after the screensaver has been activated */
+#define SCREENSAVER_TIMEOUT 20
+#define VOLUME_CHANGE_TIMEOUT 250
+#define VOLUME_REFRESH_TIMEOUT 100
+
+#define LOCALISED_ID_TV 36037
+#define LOCALISED_ID_AVR 36038
+#define LOCALISED_ID_TV_AVR 36039
+#define LOCALISED_ID_NONE 231
+
+/* time in seconds to suppress source activation after receiving OnStop */
+#define CEC_SUPPRESS_ACTIVATE_SOURCE_AFTER_ON_STOP 2
+
+class DllLibCECInterface
+{
+public:
+ virtual ~DllLibCECInterface() {}
+ virtual ICECAdapter* CECInitialise(libcec_configuration *configuration)=0;
+ virtual void* CECDestroy(ICECAdapter *adapter)=0;
+};
+
+class DllLibCEC : public DllDynamic, DllLibCECInterface
+{
+ DECLARE_DLL_WRAPPER(DllLibCEC, DLL_PATH_LIBCEC)
+
+ DEFINE_METHOD1(ICECAdapter*, CECInitialise, (libcec_configuration *p1))
+ DEFINE_METHOD1(void* , CECDestroy, (ICECAdapter *p1))
+
+ BEGIN_METHOD_RESOLVE()
+ RESOLVE_METHOD_RENAME(CECInitialise, CECInitialise)
+ RESOLVE_METHOD_RENAME(CECDestroy, CECDestroy)
+ END_METHOD_RESOLVE()
+};
+
+CPeripheralCecAdapter::CPeripheralCecAdapter(const PeripheralScanResult& scanResult) :
+ CPeripheralHID(scanResult),
+ CThread("CECAdapter"),
+ m_dll(NULL),
+ m_cecAdapter(NULL)
+{
+ ResetMembers();
+ m_features.push_back(FEATURE_CEC);
+ m_strComPort = scanResult.m_strLocation;
+}
+
+CPeripheralCecAdapter::~CPeripheralCecAdapter(void)
+{
+ {
+ CSingleLock lock(m_critSection);
+ CAnnouncementManager::Get().RemoveAnnouncer(this);
+ m_bStop = true;
+ }
+
+ StopThread(true);
+ delete m_queryThread;
+
+ if (m_dll && m_cecAdapter)
+ {
+ m_dll->CECDestroy(m_cecAdapter);
+ m_cecAdapter = NULL;
+ delete m_dll;
+ m_dll = NULL;
+ }
+}
+
+void CPeripheralCecAdapter::ResetMembers(void)
+{
+ if (m_cecAdapter && m_dll)
+ m_dll->CECDestroy(m_cecAdapter);
+ m_cecAdapter = NULL;
+ delete m_dll;
+ m_dll = NULL;
+ m_bStarted = false;
+ m_bHasButton = false;
+ m_bIsReady = false;
+ m_bHasConnectedAudioSystem = false;
+ m_strMenuLanguage = "???";
+ m_lastKeypress = 0;
+ m_lastChange = VOLUME_CHANGE_NONE;
+ m_iExitCode = EXITCODE_QUIT;
+ m_bIsMuted = false; // TODO fetch the correct initial value when system audiostatus is implemented in libCEC
+ m_bGoingToStandby = false;
+ m_bIsRunning = false;
+ m_bDeviceRemoved = false;
+ m_bActiveSourcePending = false;
+ m_bStandbyPending = false;
+ m_bActiveSourceBeforeStandby = false;
+ m_bOnPlayReceived = false;
+ m_bPlaybackPaused = false;
+ m_queryThread = NULL;
+
+ m_currentButton.iButton = 0;
+ m_currentButton.iDuration = 0;
+ m_standbySent.SetValid(false);
+ m_configuration.Clear();
+}
+
+void CPeripheralCecAdapter::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
+{
+ if (flag == System && !strcmp(sender, "xbmc") && !strcmp(message, "OnQuit") && m_bIsReady)
+ {
+ CSingleLock lock(m_critSection);
+ m_iExitCode = static_cast<int>(data["exitcode"].asInteger(EXITCODE_QUIT));
+ CAnnouncementManager::Get().RemoveAnnouncer(this);
+ StopThread(false);
+ }
+ else if (flag == GUI && !strcmp(sender, "xbmc") && !strcmp(message, "OnScreensaverDeactivated") && m_bIsReady)
+ {
+ bool bIgnoreDeactivate(false);
+ if (data["shuttingdown"].isBoolean())
+ {
+ // don't respond to the deactivation if we are just going to suspend/shutdown anyway
+ // the tv will not have time to switch on before being told to standby and
+ // may not action the standby command.
+ bIgnoreDeactivate = data["shuttingdown"].asBoolean();
+ if (bIgnoreDeactivate)
+ CLog::Log(LOGDEBUG, "%s - ignoring OnScreensaverDeactivated for power action", __FUNCTION__);
+ }
+ if (m_configuration.bPowerOnScreensaver == 1 && !bIgnoreDeactivate &&
+ m_configuration.bActivateSource == 1)
+ {
+ ActivateSource();
+ }
+ }
+ else if (flag == GUI && !strcmp(sender, "xbmc") && !strcmp(message, "OnScreensaverActivated") && m_bIsReady)
+ {
+ // Don't put devices to standby if application is currently playing
+ if ((!g_application.m_pPlayer->IsPlaying() && !g_application.m_pPlayer->IsPaused()) && m_configuration.bPowerOffScreensaver == 1)
+ {
+ // only power off when we're the active source
+ if (m_cecAdapter->IsLibCECActiveSource())
+ StandbyDevices();
+ }
+ }
+ else if (flag == System && !strcmp(sender, "xbmc") && !strcmp(message, "OnSleep"))
+ {
+ // this will also power off devices when we're the active source
+ {
+ CSingleLock lock(m_critSection);
+ m_bGoingToStandby = true;
+ }
+ StopThread();
+ }
+ else if (flag == System && !strcmp(sender, "xbmc") && !strcmp(message, "OnWake"))
+ {
+ CLog::Log(LOGDEBUG, "%s - reconnecting to the CEC adapter after standby mode", __FUNCTION__);
+ if (ReopenConnection())
+ {
+ bool bActivate(false);
+ {
+ CSingleLock lock(m_critSection);
+ bActivate = m_bActiveSourceBeforeStandby;
+ m_bActiveSourceBeforeStandby = false;
+ }
+ if (bActivate)
+ ActivateSource();
+ }
+ }
+ else if (flag == Player && !strcmp(sender, "xbmc") && !strcmp(message, "OnStop"))
+ {
+ CSingleLock lock(m_critSection);
+ m_preventActivateSourceOnPlay = CDateTime::GetCurrentDateTime();
+ m_bOnPlayReceived = false;
+ }
+ else if (flag == Player && !strcmp(sender, "xbmc") && !strcmp(message, "OnPlay"))
+ {
+ // activate the source when playback started, and the option is enabled
+ bool bActivateSource(false);
+ {
+ CSingleLock lock(m_critSection);
+ bActivateSource = (m_configuration.bActivateSource &&
+ !m_bOnPlayReceived &&
+ !m_cecAdapter->IsLibCECActiveSource() &&
+ (!m_preventActivateSourceOnPlay.IsValid() || CDateTime::GetCurrentDateTime() - m_preventActivateSourceOnPlay > CDateTimeSpan(0, 0, 0, CEC_SUPPRESS_ACTIVATE_SOURCE_AFTER_ON_STOP)));
+ m_bOnPlayReceived = true;
+ }
+ if (bActivateSource)
+ ActivateSource();
+ }
+}
+
+bool CPeripheralCecAdapter::InitialiseFeature(const PeripheralFeature feature)
+{
+ if (feature == FEATURE_CEC && !m_bStarted && GetSettingBool("enabled"))
+ {
+ // hide settings that have an override set
+ if (!GetSettingString("wake_devices_advanced").empty())
+ SetSettingVisible("wake_devices", false);
+ if (!GetSettingString("standby_devices_advanced").empty())
+ SetSettingVisible("standby_devices", false);
+
+ SetConfigurationFromSettings();
+ m_callbacks.Clear();
+ m_callbacks.CBCecLogMessage = &CecLogMessage;
+ m_callbacks.CBCecKeyPress = &CecKeyPress;
+ m_callbacks.CBCecCommand = &CecCommand;
+ m_callbacks.CBCecConfigurationChanged = &CecConfiguration;
+ m_callbacks.CBCecAlert = &CecAlert;
+ m_callbacks.CBCecSourceActivated = &CecSourceActivated;
+ m_configuration.callbackParam = this;
+ m_configuration.callbacks = &m_callbacks;
+
+ m_dll = new DllLibCEC;
+ if (m_dll->Load() && m_dll->IsLoaded())
+ m_cecAdapter = m_dll->CECInitialise(&m_configuration);
+ else
+ {
+ // display warning: libCEC could not be loaded
+ CLog::Log(LOGERROR, "%s", g_localizeStrings.Get(36017).c_str());
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(36000), g_localizeStrings.Get(36017));
+ delete m_dll;
+ m_dll = NULL;
+ m_features.clear();
+ return false;
+ }
+
+ if (m_configuration.serverVersion < CEC_LIB_SUPPORTED_VERSION)
+ {
+ /* unsupported libcec version */
+ CLog::Log(LOGERROR, g_localizeStrings.Get(36040).c_str(), m_cecAdapter ? m_configuration.serverVersion : -1, CEC_LIB_SUPPORTED_VERSION);
+
+ // display warning: incompatible libCEC
+ CStdString strMessage = StringUtils::Format(g_localizeStrings.Get(36040).c_str(), m_cecAdapter ? m_configuration.serverVersion : -1, CEC_LIB_SUPPORTED_VERSION);
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(36000), strMessage);
+ m_bError = true;
+ if (m_cecAdapter)
+ m_dll->CECDestroy(m_cecAdapter);
+ m_cecAdapter = NULL;
+
+ m_features.clear();
+ return false;
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "%s - using libCEC v%s", __FUNCTION__, m_cecAdapter->ToString((cec_server_version)m_configuration.serverVersion));
+ SetVersionInfo(m_configuration);
+ }
+
+ m_bStarted = true;
+ Create();
+ }
+
+ return CPeripheral::InitialiseFeature(feature);
+}
+
+void CPeripheralCecAdapter::SetVersionInfo(const libcec_configuration &configuration)
+{
+ m_strVersionInfo = StringUtils::Format("libCEC %s - firmware v%d", m_cecAdapter->ToString((cec_server_version)configuration.serverVersion), configuration.iFirmwareVersion);
+
+ // append firmware build date
+ if (configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
+ {
+ CDateTime dt((time_t)configuration.iFirmwareBuildDate);
+ m_strVersionInfo += StringUtils::Format(" (%s)", dt.GetAsDBDate().c_str());
+ }
+}
+
+bool CPeripheralCecAdapter::OpenConnection(void)
+{
+ bool bIsOpen(false);
+
+ if (!GetSettingBool("enabled"))
+ {
+ CLog::Log(LOGDEBUG, "%s - CEC adapter is disabled in peripheral settings", __FUNCTION__);
+ m_bStarted = false;
+ return bIsOpen;
+ }
+
+ // open the CEC adapter
+ CLog::Log(LOGDEBUG, "%s - opening a connection to the CEC adapter: %s", __FUNCTION__, m_strComPort.c_str());
+
+ // scanning the CEC bus takes about 5 seconds, so display a notification to inform users that we're busy
+ CStdString strMessage = StringUtils::Format(g_localizeStrings.Get(21336).c_str(), g_localizeStrings.Get(36000).c_str());
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(36000), strMessage);
+
+ bool bConnectionFailedDisplayed(false);
+
+ while (!m_bStop && !bIsOpen)
+ {
+ if ((bIsOpen = m_cecAdapter->Open(m_strComPort.c_str(), 10000)) == false)
+ {
+ // display warning: couldn't initialise libCEC
+ CLog::Log(LOGERROR, "%s - could not opening a connection to the CEC adapter", __FUNCTION__);
+ if (!bConnectionFailedDisplayed)
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(36000), g_localizeStrings.Get(36012));
+ bConnectionFailedDisplayed = true;
+
+ Sleep(10000);
+ }
+ }
+
+ if (bIsOpen)
+ {
+ CLog::Log(LOGDEBUG, "%s - connection to the CEC adapter opened", __FUNCTION__);
+
+ // read the configuration
+ libcec_configuration config;
+ if (m_cecAdapter->GetCurrentConfiguration(&config))
+ {
+ // update the local configuration
+ CSingleLock lock(m_critSection);
+ SetConfigurationFromLibCEC(config);
+ }
+ }
+
+ return bIsOpen;
+}
+
+void CPeripheralCecAdapter::Process(void)
+{
+ if (!OpenConnection())
+ return;
+
+ {
+ CSingleLock lock(m_critSection);
+ m_iExitCode = EXITCODE_QUIT;
+ m_bGoingToStandby = false;
+ m_bIsRunning = true;
+ m_bActiveSourceBeforeStandby = false;
+ }
+
+ CAnnouncementManager::Get().AddAnnouncer(this);
+
+ m_queryThread = new CPeripheralCecAdapterUpdateThread(this, &m_configuration);
+ m_queryThread->Create(false);
+
+ while (!m_bStop)
+ {
+ if (!m_bStop)
+ ProcessVolumeChange();
+
+ if (!m_bStop)
+ ProcessActivateSource();
+
+ if (!m_bStop)
+ ProcessStandbyDevices();
+
+ if (!m_bStop)
+ Sleep(5);
+ }
+
+ m_queryThread->StopThread(true);
+
+ bool bSendStandbyCommands(false);
+ {
+ CSingleLock lock(m_critSection);
+ bSendStandbyCommands = m_iExitCode != EXITCODE_REBOOT &&
+ m_iExitCode != EXITCODE_RESTARTAPP &&
+ !m_bDeviceRemoved &&
+ (!m_bGoingToStandby || GetSettingBool("standby_tv_on_pc_standby")) &&
+ GetSettingBool("enabled");
+
+ if (m_bGoingToStandby)
+ m_bActiveSourceBeforeStandby = m_cecAdapter->IsLibCECActiveSource();
+ }
+
+ if (bSendStandbyCommands)
+ {
+ if (m_cecAdapter->IsLibCECActiveSource())
+ {
+ if (!m_configuration.powerOffDevices.IsEmpty())
+ {
+ CLog::Log(LOGDEBUG, "%s - sending standby commands", __FUNCTION__);
+ m_standbySent = CDateTime::GetCurrentDateTime();
+ m_cecAdapter->StandbyDevices();
+ }
+ else if (m_configuration.bSendInactiveSource == 1)
+ {
+ CLog::Log(LOGDEBUG, "%s - sending inactive source commands", __FUNCTION__);
+ m_cecAdapter->SetInactiveView();
+ }
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "%s - XBMC is not the active source, not sending any standby commands", __FUNCTION__);
+ }
+ }
+
+ m_cecAdapter->Close();
+
+ CLog::Log(LOGDEBUG, "%s - CEC adapter processor thread ended", __FUNCTION__);
+
+ {
+ CSingleLock lock(m_critSection);
+ m_bStarted = false;
+ m_bIsRunning = false;
+ }
+}
+
+bool CPeripheralCecAdapter::HasAudioControl(void)
+{
+ CSingleLock lock(m_critSection);
+ return m_bHasConnectedAudioSystem;
+}
+
+void CPeripheralCecAdapter::SetAudioSystemConnected(bool bSetTo)
+{
+ CSingleLock lock(m_critSection);
+ m_bHasConnectedAudioSystem = bSetTo;
+}
+
+void CPeripheralCecAdapter::ProcessVolumeChange(void)
+{
+ bool bSendRelease(false);
+ CecVolumeChange pendingVolumeChange = VOLUME_CHANGE_NONE;
+ {
+ CSingleLock lock(m_critSection);
+ if (!m_volumeChangeQueue.empty())
+ {
+ /* get the first change from the queue */
+ pendingVolumeChange = m_volumeChangeQueue.front();
+ m_volumeChangeQueue.pop();
+
+ /* remove all dupe entries */
+ while (!m_volumeChangeQueue.empty() && m_volumeChangeQueue.front() == pendingVolumeChange)
+ m_volumeChangeQueue.pop();
+
+ /* send another keypress after VOLUME_REFRESH_TIMEOUT ms */
+ bool bRefresh(XbmcThreads::SystemClockMillis() - m_lastKeypress > VOLUME_REFRESH_TIMEOUT);
+
+ /* only send the keypress when it hasn't been sent yet */
+ if (pendingVolumeChange != m_lastChange)
+ {
+ m_lastKeypress = XbmcThreads::SystemClockMillis();
+ m_lastChange = pendingVolumeChange;
+ }
+ else if (bRefresh)
+ {
+ m_lastKeypress = XbmcThreads::SystemClockMillis();
+ pendingVolumeChange = m_lastChange;
+ }
+ else
+ pendingVolumeChange = VOLUME_CHANGE_NONE;
+ }
+ else if (m_lastKeypress > 0 && XbmcThreads::SystemClockMillis() - m_lastKeypress > VOLUME_CHANGE_TIMEOUT)
+ {
+ /* send a key release */
+ m_lastKeypress = 0;
+ bSendRelease = true;
+ m_lastChange = VOLUME_CHANGE_NONE;
+ }
+ }
+
+ switch (pendingVolumeChange)
+ {
+ case VOLUME_CHANGE_UP:
+ m_cecAdapter->SendKeypress(CECDEVICE_AUDIOSYSTEM, CEC_USER_CONTROL_CODE_VOLUME_UP, false);
+ break;
+ case VOLUME_CHANGE_DOWN:
+ m_cecAdapter->SendKeypress(CECDEVICE_AUDIOSYSTEM, CEC_USER_CONTROL_CODE_VOLUME_DOWN, false);
+ break;
+ case VOLUME_CHANGE_MUTE:
+ m_cecAdapter->SendKeypress(CECDEVICE_AUDIOSYSTEM, CEC_USER_CONTROL_CODE_MUTE, false);
+ {
+ CSingleLock lock(m_critSection);
+ m_bIsMuted = !m_bIsMuted;
+ }
+ break;
+ case VOLUME_CHANGE_NONE:
+ if (bSendRelease)
+ m_cecAdapter->SendKeyRelease(CECDEVICE_AUDIOSYSTEM, false);
+ break;
+ }
+}
+
+void CPeripheralCecAdapter::VolumeUp(void)
+{
+ if (HasAudioControl())
+ {
+ CSingleLock lock(m_critSection);
+ m_volumeChangeQueue.push(VOLUME_CHANGE_UP);
+ }
+}
+
+void CPeripheralCecAdapter::VolumeDown(void)
+{
+ if (HasAudioControl())
+ {
+ CSingleLock lock(m_critSection);
+ m_volumeChangeQueue.push(VOLUME_CHANGE_DOWN);
+ }
+}
+
+void CPeripheralCecAdapter::ToggleMute(void)
+{
+ if (HasAudioControl())
+ {
+ CSingleLock lock(m_critSection);
+ m_volumeChangeQueue.push(VOLUME_CHANGE_MUTE);
+ }
+}
+
+bool CPeripheralCecAdapter::IsMuted(void)
+{
+ if (HasAudioControl())
+ {
+ CSingleLock lock(m_critSection);
+ return m_bIsMuted;
+ }
+ return false;
+}
+
+void CPeripheralCecAdapter::SetMenuLanguage(const char *strLanguage)
+{
+ if (m_strMenuLanguage.Equals(strLanguage))
+ return;
+
+ CStdString strGuiLanguage;
+
+ if (!strcmp(strLanguage, "bul"))
+ strGuiLanguage = "Bulgarian";
+ else if (!strcmp(strLanguage, "hrv"))
+ strGuiLanguage = "Croatian";
+ else if (!strcmp(strLanguage, "cze"))
+ strGuiLanguage = "Czech";
+ else if (!strcmp(strLanguage, "dan"))
+ strGuiLanguage = "Danish";
+ else if (!strcmp(strLanguage, "dut"))
+ strGuiLanguage = "Dutch";
+ else if (!strcmp(strLanguage, "eng"))
+ strGuiLanguage = "English";
+ else if (!strcmp(strLanguage, "fin"))
+ strGuiLanguage = "Finnish";
+ else if (!strcmp(strLanguage, "fre"))
+ strGuiLanguage = "French";
+ else if (!strcmp(strLanguage, "ger"))
+ strGuiLanguage = "German";
+ else if (!strcmp(strLanguage, "gre"))
+ strGuiLanguage = "Greek";
+ else if (!strcmp(strLanguage, "hun"))
+ strGuiLanguage = "Hungarian";
+ else if (!strcmp(strLanguage, "ita"))
+ strGuiLanguage = "Italian";
+ else if (!strcmp(strLanguage, "nor"))
+ strGuiLanguage = "Norwegian";
+ else if (!strcmp(strLanguage, "pol"))
+ strGuiLanguage = "Polish";
+ else if (!strcmp(strLanguage, "por"))
+ strGuiLanguage = "Portuguese";
+ else if (!strcmp(strLanguage, "rum"))
+ strGuiLanguage = "Romanian";
+ else if (!strcmp(strLanguage, "rus"))
+ strGuiLanguage = "Russian";
+ else if (!strcmp(strLanguage, "srp"))
+ strGuiLanguage = "Serbian";
+ else if (!strcmp(strLanguage, "slo"))
+ strGuiLanguage = "Slovenian";
+ else if (!strcmp(strLanguage, "spa"))
+ strGuiLanguage = "Spanish";
+ else if (!strcmp(strLanguage, "swe"))
+ strGuiLanguage = "Swedish";
+ else if (!strcmp(strLanguage, "tur"))
+ strGuiLanguage = "Turkish";
+
+ if (!strGuiLanguage.empty())
+ {
+ CApplicationMessenger::Get().SetGUILanguage(strGuiLanguage);
+ CLog::Log(LOGDEBUG, "%s - language set to '%s'", __FUNCTION__, strGuiLanguage.c_str());
+ }
+ else
+ CLog::Log(LOGWARNING, "%s - TV menu language set to unknown value '%s'", __FUNCTION__, strLanguage);
+}
+
+int CPeripheralCecAdapter::CecCommand(void *cbParam, const cec_command command)
+{
+ CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
+ if (!adapter)
+ return 0;
+
+ if (adapter->m_bIsReady)
+ {
+ switch (command.opcode)
+ {
+ case CEC_OPCODE_STANDBY:
+ /* a device was put in standby mode */
+ if (command.initiator == CECDEVICE_TV &&
+ (adapter->m_configuration.bPowerOffOnStandby == 1 || adapter->m_configuration.bShutdownOnStandby == 1) &&
+ (!adapter->m_standbySent.IsValid() || CDateTime::GetCurrentDateTime() - adapter->m_standbySent > CDateTimeSpan(0, 0, 0, SCREENSAVER_TIMEOUT)))
+ {
+ adapter->m_bStarted = false;
+ if (adapter->m_configuration.bPowerOffOnStandby == 1)
+ CApplicationMessenger::Get().Suspend();
+ else if (adapter->m_configuration.bShutdownOnStandby == 1)
+ CApplicationMessenger::Get().Shutdown();
+ }
+ break;
+ case CEC_OPCODE_SET_MENU_LANGUAGE:
+ if (adapter->m_configuration.bUseTVMenuLanguage == 1 && command.initiator == CECDEVICE_TV && command.parameters.size == 3)
+ {
+ char strNewLanguage[4];
+ for (int iPtr = 0; iPtr < 3; iPtr++)
+ strNewLanguage[iPtr] = command.parameters[iPtr];
+ strNewLanguage[3] = 0;
+ adapter->SetMenuLanguage(strNewLanguage);
+ }
+ break;
+ case CEC_OPCODE_DECK_CONTROL:
+ if (command.initiator == CECDEVICE_TV &&
+ command.parameters.size == 1 &&
+ command.parameters[0] == CEC_DECK_CONTROL_MODE_STOP)
+ {
+ cec_keypress key;
+ key.duration = 500;
+ key.keycode = CEC_USER_CONTROL_CODE_STOP;
+ adapter->PushCecKeypress(key);
+ }
+ break;
+ case CEC_OPCODE_PLAY:
+ if (command.initiator == CECDEVICE_TV &&
+ command.parameters.size == 1)
+ {
+ if (command.parameters[0] == CEC_PLAY_MODE_PLAY_FORWARD)
+ {
+ cec_keypress key;
+ key.duration = 500;
+ key.keycode = CEC_USER_CONTROL_CODE_PLAY;
+ adapter->PushCecKeypress(key);
+ }
+ else if (command.parameters[0] == CEC_PLAY_MODE_PLAY_STILL)
+ {
+ cec_keypress key;
+ key.duration = 500;
+ key.keycode = CEC_USER_CONTROL_CODE_PAUSE;
+ adapter->PushCecKeypress(key);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 1;
+}
+
+int CPeripheralCecAdapter::CecConfiguration(void *cbParam, const libcec_configuration config)
+{
+ CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
+ if (!adapter)
+ return 0;
+
+ CSingleLock lock(adapter->m_critSection);
+ adapter->SetConfigurationFromLibCEC(config);
+ return 1;
+}
+
+int CPeripheralCecAdapter::CecAlert(void *cbParam, const libcec_alert alert, const libcec_parameter data)
+{
+ CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
+ if (!adapter)
+ return 0;
+
+ bool bReopenConnection(false);
+ int iAlertString(0);
+ switch (alert)
+ {
+ case CEC_ALERT_SERVICE_DEVICE:
+ iAlertString = 36027;
+ break;
+ case CEC_ALERT_CONNECTION_LOST:
+ bReopenConnection = true;
+ iAlertString = 36030;
+ break;
+#if defined(CEC_ALERT_PERMISSION_ERROR)
+ case CEC_ALERT_PERMISSION_ERROR:
+ bReopenConnection = true;
+ iAlertString = 36031;
+ break;
+ case CEC_ALERT_PORT_BUSY:
+ bReopenConnection = true;
+ iAlertString = 36032;
+ break;
+#endif
+ default:
+ break;
+ }
+
+ // display the alert
+ if (iAlertString)
+ {
+ CStdString strLog(g_localizeStrings.Get(iAlertString));
+ if (data.paramType == CEC_PARAMETER_TYPE_STRING && data.paramData)
+ strLog += StringUtils::Format(" - %s", (const char *)data.paramData);
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(36000), strLog);
+ }
+
+ if (bReopenConnection)
+ adapter->ReopenConnection();
+
+ return 1;
+}
+
+int CPeripheralCecAdapter::CecKeyPress(void *cbParam, const cec_keypress key)
+{
+ CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
+ if (!adapter)
+ return 0;
+
+ adapter->PushCecKeypress(key);
+ return 1;
+}
+
+void CPeripheralCecAdapter::GetNextKey(void)
+{
+ CSingleLock lock(m_critSection);
+ m_bHasButton = false;
+ if (m_bIsReady)
+ {
+ vector<CecButtonPress>::iterator it = m_buttonQueue.begin();
+ if (it != m_buttonQueue.end())
+ {
+ m_currentButton = (*it);
+ m_buttonQueue.erase(it);
+ m_bHasButton = true;
+ }
+ }
+}
+
+void CPeripheralCecAdapter::PushCecKeypress(const CecButtonPress &key)
+{
+ CLog::Log(LOGDEBUG, "%s - received key %2x duration %d", __FUNCTION__, key.iButton, key.iDuration);
+
+ CSingleLock lock(m_critSection);
+ if (key.iDuration > 0)
+ {
+ if (m_currentButton.iButton == key.iButton && m_currentButton.iDuration == 0)
+ {
+ // update the duration
+ if (m_bHasButton)
+ m_currentButton.iDuration = key.iDuration;
+ // ignore this one, since it's already been handled by xbmc
+ return;
+ }
+ // if we received a keypress with a duration set, try to find the same one without a duration set, and replace it
+ for (vector<CecButtonPress>::reverse_iterator it = m_buttonQueue.rbegin(); it != m_buttonQueue.rend(); ++it)
+ {
+ if ((*it).iButton == key.iButton)
+ {
+ if ((*it).iDuration == 0)
+ {
+ // replace this entry
+ (*it).iDuration = key.iDuration;
+ return;
+ }
+ // add a new entry
+ break;
+ }
+ }
+ }
+
+ m_buttonQueue.push_back(key);
+}
+
+void CPeripheralCecAdapter::PushCecKeypress(const cec_keypress &key)
+{
+ CecButtonPress xbmcKey;
+ xbmcKey.iDuration = key.duration;
+
+ switch (key.keycode)
+ {
+ case CEC_USER_CONTROL_CODE_SELECT:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_SELECT;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_UP:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_UP;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_DOWN:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_DOWN;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_LEFT:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_LEFT;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_LEFT_UP:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_LEFT;
+ PushCecKeypress(xbmcKey);
+ xbmcKey.iButton = XINPUT_IR_REMOTE_UP;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_LEFT_DOWN:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_LEFT;
+ PushCecKeypress(xbmcKey);
+ xbmcKey.iButton = XINPUT_IR_REMOTE_DOWN;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_RIGHT:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_RIGHT;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_RIGHT_UP:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_RIGHT;
+ PushCecKeypress(xbmcKey);
+ xbmcKey.iButton = XINPUT_IR_REMOTE_UP;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_RIGHT_DOWN:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_RIGHT;
+ PushCecKeypress(xbmcKey);
+ xbmcKey.iButton = XINPUT_IR_REMOTE_DOWN;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_SETUP_MENU:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_TITLE;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_CONTENTS_MENU;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_ROOT_MENU:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_ROOT_MENU;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_TOP_MENU:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_TOP_MENU;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_DVD_MENU:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_DVD_MENU;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_FAVORITE_MENU:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_MENU;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_EXIT:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_BACK;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_ENTER:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_ENTER;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_CHANNEL_DOWN:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_CHANNEL_MINUS;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_CHANNEL_UP:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_CHANNEL_PLUS;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_TELETEXT;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_SOUND_SELECT:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_LANGUAGE;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_POWER:
+ case CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION:
+ case CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_POWER;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_VOLUME_UP:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_VOLUME_PLUS;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_VOLUME_DOWN:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_VOLUME_MINUS;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_MUTE:
+ case CEC_USER_CONTROL_CODE_MUTE_FUNCTION:
+ case CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_MUTE;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_PLAY:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_PLAY;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_STOP:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_STOP;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_PAUSE:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_PAUSE;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_REWIND:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_REVERSE;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_FAST_FORWARD:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_FORWARD;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NUMBER0:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_0;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NUMBER1:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_1;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NUMBER2:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_2;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NUMBER3:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_3;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NUMBER4:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_4;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NUMBER5:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_5;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NUMBER6:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_6;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NUMBER7:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_7;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NUMBER8:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_8;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NUMBER9:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_9;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_RECORD:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_RECORD;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_CLEAR:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_CLEAR;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_INFO;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_PAGE_UP:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_CHANNEL_PLUS;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_PAGE_DOWN:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_CHANNEL_MINUS;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_FORWARD:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_SKIP_PLUS;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_BACKWARD:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_SKIP_MINUS;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_F1_BLUE:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_BLUE;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_F2_RED:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_RED;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_F3_GREEN:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_GREEN;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_F4_YELLOW:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_YELLOW;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_GUIDE;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_LIVE_TV;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_NEXT_FAVORITE:
+ case CEC_USER_CONTROL_CODE_DOT:
+ case CEC_USER_CONTROL_CODE_AN_RETURN:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_TITLE; // context menu
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_DATA:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_TELETEXT;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_SUB_PICTURE:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_SUBTITLE;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_EJECT:
+ xbmcKey.iButton = XINPUT_IR_REMOTE_EJECT;
+ PushCecKeypress(xbmcKey);
+ break;
+ case CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION:
+ case CEC_USER_CONTROL_CODE_INPUT_SELECT:
+ case CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION:
+ case CEC_USER_CONTROL_CODE_HELP:
+ case CEC_USER_CONTROL_CODE_STOP_RECORD:
+ case CEC_USER_CONTROL_CODE_PAUSE_RECORD:
+ case CEC_USER_CONTROL_CODE_ANGLE:
+ case CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND:
+ case CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:
+ case CEC_USER_CONTROL_CODE_PLAY_FUNCTION:
+ case CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:
+ case CEC_USER_CONTROL_CODE_RECORD_FUNCTION:
+ case CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION:
+ case CEC_USER_CONTROL_CODE_STOP_FUNCTION:
+ case CEC_USER_CONTROL_CODE_TUNE_FUNCTION:
+ case CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION:
+ case CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION:
+ case CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION:
+ case CEC_USER_CONTROL_CODE_F5:
+ case CEC_USER_CONTROL_CODE_NUMBER_ENTRY_MODE:
+ case CEC_USER_CONTROL_CODE_NUMBER11:
+ case CEC_USER_CONTROL_CODE_NUMBER12:
+ case CEC_USER_CONTROL_CODE_SELECT_BROADCAST_TYPE:
+ case CEC_USER_CONTROL_CODE_SELECT_SOUND_PRESENTATION:
+ case CEC_USER_CONTROL_CODE_UNKNOWN:
+ default:
+ break;
+ }
+}
+
+int CPeripheralCecAdapter::GetButton(void)
+{
+ CSingleLock lock(m_critSection);
+ if (!m_bHasButton)
+ GetNextKey();
+
+ return m_bHasButton ? m_currentButton.iButton : 0;
+}
+
+unsigned int CPeripheralCecAdapter::GetHoldTime(void)
+{
+ CSingleLock lock(m_critSection);
+ if (!m_bHasButton)
+ GetNextKey();
+
+ return m_bHasButton ? m_currentButton.iDuration : 0;
+}
+
+void CPeripheralCecAdapter::ResetButton(void)
+{
+ CSingleLock lock(m_critSection);
+ m_bHasButton = false;
+
+ // wait for the key release if the duration isn't 0
+ if (m_currentButton.iDuration > 0)
+ {
+ m_currentButton.iButton = 0;
+ m_currentButton.iDuration = 0;
+ }
+}
+
+void CPeripheralCecAdapter::OnSettingChanged(const CStdString &strChangedSetting)
+{
+ if (strChangedSetting.Equals("enabled"))
+ {
+ bool bEnabled(GetSettingBool("enabled"));
+ if (!bEnabled && IsRunning())
+ {
+ CLog::Log(LOGDEBUG, "%s - closing the CEC connection", __FUNCTION__);
+ StopThread(true);
+ }
+ else if (bEnabled && !IsRunning())
+ {
+ CLog::Log(LOGDEBUG, "%s - starting the CEC connection", __FUNCTION__);
+ SetConfigurationFromSettings();
+ InitialiseFeature(FEATURE_CEC);
+ }
+ }
+ else if (IsRunning())
+ {
+ if (m_queryThread->IsRunning())
+ {
+ CLog::Log(LOGDEBUG, "%s - sending the updated configuration to libCEC", __FUNCTION__);
+ SetConfigurationFromSettings();
+ m_queryThread->UpdateConfiguration(&m_configuration);
+ }
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "%s - restarting the CEC connection", __FUNCTION__);
+ SetConfigurationFromSettings();
+ InitialiseFeature(FEATURE_CEC);
+ }
+}
+
+void CPeripheralCecAdapter::CecSourceActivated(void *cbParam, const CEC::cec_logical_address address, const uint8_t activated)
+{
+ CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
+ if (!adapter)
+ return;
+
+ // wake up the screensaver, so the user doesn't switch to a black screen
+ if (activated == 1)
+ g_application.WakeUpScreenSaverAndDPMS();
+
+ if (adapter->GetSettingBool("pause_playback_on_deactivate"))
+ {
+ bool bShowingSlideshow = (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW);
+ CGUIWindowSlideShow *pSlideShow = bShowingSlideshow ? (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW) : NULL;
+ bool bPlayingAndDeactivated = activated == 0 && (
+ (pSlideShow && pSlideShow->IsPlaying()) || g_application.m_pPlayer->IsPlaying());
+ bool bPausedAndActivated = activated == 1 && adapter->m_bPlaybackPaused && (
+ (pSlideShow && pSlideShow->IsPaused()) || g_application.m_pPlayer->IsPausedPlayback());
+ if (bPlayingAndDeactivated)
+ adapter->m_bPlaybackPaused = true;
+ else if (bPausedAndActivated)
+ adapter->m_bPlaybackPaused = false;
+
+ if (bPlayingAndDeactivated || bPausedAndActivated)
+ {
+ if (pSlideShow)
+ // pause/resume slideshow
+ pSlideShow->OnAction(CAction(ACTION_PAUSE));
+ else
+ // pause/resume player
+ CApplicationMessenger::Get().MediaPause();
+ }
+ }
+}
+
+int CPeripheralCecAdapter::CecLogMessage(void *cbParam, const cec_log_message message)
+{
+ CPeripheralCecAdapter *adapter = (CPeripheralCecAdapter *)cbParam;
+ if (!adapter)
+ return 0;
+
+ int iLevel = -1;
+ switch (message.level)
+ {
+ case CEC_LOG_ERROR:
+ iLevel = LOGERROR;
+ break;
+ case CEC_LOG_WARNING:
+ iLevel = LOGWARNING;
+ break;
+ case CEC_LOG_NOTICE:
+ iLevel = LOGDEBUG;
+ break;
+ case CEC_LOG_TRAFFIC:
+ case CEC_LOG_DEBUG:
+ iLevel = LOGDEBUG;
+ break;
+ default:
+ break;
+ }
+
+ if (iLevel >= CEC_LOG_NOTICE || (iLevel >= 0 && CLog::IsLogLevelLogged(LOGDEBUG) && g_advancedSettings.CanLogComponent(LOGCEC)))
+ CLog::Log(iLevel, "%s - %s", __FUNCTION__, message.message);
+
+ return 1;
+}
+
+void CPeripheralCecAdapter::SetConfigurationFromLibCEC(const CEC::libcec_configuration &config)
+{
+ bool bChanged(false);
+
+ // set the primary device type
+ m_configuration.deviceTypes.Clear();
+ m_configuration.deviceTypes.Add(config.deviceTypes[0]);
+
+ // hide the "connected device" and "hdmi port number" settings when the PA was autodetected
+ bool bPAAutoDetected(config.bAutodetectAddress == 1);
+
+ SetSettingVisible("connected_device", !bPAAutoDetected);
+ SetSettingVisible("cec_hdmi_port", !bPAAutoDetected);
+
+ // set the connected device
+ m_configuration.baseDevice = config.baseDevice;
+ bChanged |= SetSetting("connected_device", config.baseDevice == CECDEVICE_AUDIOSYSTEM ? LOCALISED_ID_AVR : LOCALISED_ID_TV);
+
+ // set the HDMI port number
+ m_configuration.iHDMIPort = config.iHDMIPort;
+ bChanged |= SetSetting("cec_hdmi_port", config.iHDMIPort);
+
+ // set the physical address, when baseDevice or iHDMIPort are not set
+ CStdString strPhysicalAddress("0");
+ if (!bPAAutoDetected && (m_configuration.baseDevice == CECDEVICE_UNKNOWN ||
+ m_configuration.iHDMIPort < CEC_MIN_HDMI_PORTNUMBER ||
+ m_configuration.iHDMIPort > CEC_MAX_HDMI_PORTNUMBER))
+ {
+ m_configuration.iPhysicalAddress = config.iPhysicalAddress;
+ strPhysicalAddress = StringUtils::Format("%x", config.iPhysicalAddress);
+ }
+ bChanged |= SetSetting("physical_address", strPhysicalAddress);
+
+ // set the devices to wake when starting
+ m_configuration.wakeDevices = config.wakeDevices;
+ bChanged |= WriteLogicalAddresses(config.wakeDevices, "wake_devices", "wake_devices_advanced");
+
+ // set the devices to power off when stopping
+ m_configuration.powerOffDevices = config.powerOffDevices;
+ bChanged |= WriteLogicalAddresses(config.powerOffDevices, "standby_devices", "standby_devices_advanced");
+
+ // set the boolean settings
+ m_configuration.bUseTVMenuLanguage = config.bUseTVMenuLanguage;
+ bChanged |= SetSetting("use_tv_menu_language", m_configuration.bUseTVMenuLanguage == 1);
+
+ m_configuration.bActivateSource = config.bActivateSource;
+ bChanged |= SetSetting("activate_source", m_configuration.bActivateSource == 1);
+
+ m_configuration.bPowerOffScreensaver = config.bPowerOffScreensaver;
+ bChanged |= SetSetting("cec_standby_screensaver", m_configuration.bPowerOffScreensaver == 1);
+
+ m_configuration.bPowerOnScreensaver = config.bPowerOnScreensaver;
+ bChanged |= SetSetting("cec_wake_screensaver", m_configuration.bPowerOnScreensaver == 1);
+
+ m_configuration.bPowerOffOnStandby = config.bPowerOffOnStandby;
+
+ m_configuration.bSendInactiveSource = config.bSendInactiveSource;
+ bChanged |= SetSetting("send_inactive_source", m_configuration.bSendInactiveSource == 1);
+
+ m_configuration.iFirmwareVersion = config.iFirmwareVersion;
+ m_configuration.bShutdownOnStandby = config.bShutdownOnStandby;
+
+ memcpy(m_configuration.strDeviceLanguage, config.strDeviceLanguage, 3);
+ m_configuration.iFirmwareBuildDate = config.iFirmwareBuildDate;
+
+ SetVersionInfo(m_configuration);
+
+ bChanged |= SetSetting("standby_pc_on_tv_standby",
+ m_configuration.bPowerOffOnStandby == 1 ? 13011 :
+ m_configuration.bShutdownOnStandby == 1 ? 13005 : 36028);
+
+ if (bChanged)
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(36000), g_localizeStrings.Get(36023));
+}
+
+void CPeripheralCecAdapter::SetConfigurationFromSettings(void)
+{
+ // client version matches the version of libCEC that we originally used the API from
+ m_configuration.clientVersion = CEC_CLIENT_VERSION_2_2_0;
+
+ // device name 'XBMC'
+ snprintf(m_configuration.strDeviceName, 13, "%s", GetSettingString("device_name").c_str());
+
+ // set the primary device type
+ m_configuration.deviceTypes.Clear();
+ int iDeviceType = GetSettingInt("device_type");
+ if (iDeviceType != (int)CEC_DEVICE_TYPE_RECORDING_DEVICE &&
+ iDeviceType != (int)CEC_DEVICE_TYPE_PLAYBACK_DEVICE &&
+ iDeviceType != (int)CEC_DEVICE_TYPE_TUNER)
+ iDeviceType = (int)CEC_DEVICE_TYPE_RECORDING_DEVICE;
+ m_configuration.deviceTypes.Add((cec_device_type)iDeviceType);
+
+ // always try to autodetect the address.
+ // when the firmware supports this, it will override the physical address, connected device and hdmi port settings
+ m_configuration.bAutodetectAddress = CEC_DEFAULT_SETTING_AUTODETECT_ADDRESS;
+
+ // set the physical address
+ // when set, it will override the connected device and hdmi port settings
+ CStdString strPhysicalAddress = GetSettingString("physical_address");
+ int iPhysicalAddress;
+ if (sscanf(strPhysicalAddress.c_str(), "%x", &iPhysicalAddress) &&
+ iPhysicalAddress >= CEC_PHYSICAL_ADDRESS_TV &&
+ iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS)
+ m_configuration.iPhysicalAddress = iPhysicalAddress;
+ else
+ m_configuration.iPhysicalAddress = CEC_PHYSICAL_ADDRESS_TV;
+
+ // set the connected device
+ int iConnectedDevice = GetSettingInt("connected_device");
+ if (iConnectedDevice == LOCALISED_ID_AVR)
+ m_configuration.baseDevice = CECDEVICE_AUDIOSYSTEM;
+ else if (iConnectedDevice == LOCALISED_ID_TV)
+ m_configuration.baseDevice = CECDEVICE_TV;
+
+ // set the HDMI port number
+ int iHDMIPort = GetSettingInt("cec_hdmi_port");
+ if (iHDMIPort >= CEC_MIN_HDMI_PORTNUMBER &&
+ iHDMIPort <= CEC_MAX_HDMI_PORTNUMBER)
+ m_configuration.iHDMIPort = iHDMIPort;
+
+ // set the tv vendor override
+ int iVendor = GetSettingInt("tv_vendor");
+ if (iVendor >= CEC_MAX_VENDORID &&
+ iVendor <= CEC_MAX_VENDORID)
+ m_configuration.tvVendor = iVendor;
+
+ // read the devices to wake when starting
+ CStdString strWakeDevices = GetSettingString("wake_devices_advanced");
+ StringUtils::Trim(strWakeDevices);
+ m_configuration.wakeDevices.Clear();
+ if (!strWakeDevices.empty())
+ ReadLogicalAddresses(strWakeDevices, m_configuration.wakeDevices);
+ else
+ ReadLogicalAddresses(GetSettingInt("wake_devices"), m_configuration.wakeDevices);
+
+ // read the devices to power off when stopping
+ CStdString strStandbyDevices = GetSettingString("standby_devices_advanced");
+ StringUtils::Trim(strStandbyDevices);
+ m_configuration.powerOffDevices.Clear();
+ if (!strStandbyDevices.empty())
+ ReadLogicalAddresses(strStandbyDevices, m_configuration.powerOffDevices);
+ else
+ ReadLogicalAddresses(GetSettingInt("standby_devices"), m_configuration.powerOffDevices);
+
+ // read the boolean settings
+ m_configuration.bUseTVMenuLanguage = GetSettingBool("use_tv_menu_language") ? 1 : 0;
+ m_configuration.bActivateSource = GetSettingBool("activate_source") ? 1 : 0;
+ m_configuration.bPowerOffScreensaver = GetSettingBool("cec_standby_screensaver") ? 1 : 0;
+ m_configuration.bPowerOnScreensaver = GetSettingBool("cec_wake_screensaver") ? 1 : 0;
+ m_configuration.bSendInactiveSource = GetSettingBool("send_inactive_source") ? 1 : 0;
+
+ // read the mutually exclusive boolean settings
+ int iStandbyAction(GetSettingInt("standby_pc_on_tv_standby"));
+ m_configuration.bPowerOffOnStandby = iStandbyAction == 13011 ? 1 : 0;
+ m_configuration.bShutdownOnStandby = iStandbyAction == 13005 ? 1 : 0;
+
+#if defined(CEC_DOUBLE_TAP_TIMEOUT_MS_OLD)
+ // double tap prevention timeout in ms. libCEC uses 50ms units for this in 2.2.0, so divide by 50
+ m_configuration.iDoubleTapTimeout50Ms = GetSettingInt("double_tap_timeout_ms") / 50;
+#else
+ // backwards compatibility. will be removed once the next major release of libCEC is out
+ m_configuration.iDoubleTapTimeoutMs = GetSettingInt("double_tap_timeout_ms");
+#endif
+}
+
+void CPeripheralCecAdapter::ReadLogicalAddresses(const CStdString &strString, cec_logical_addresses &addresses)
+{
+ for (size_t iPtr = 0; iPtr < strString.size(); iPtr++)
+ {
+ CStdString strDevice = strString.substr(iPtr, 1);
+ StringUtils::Trim(strDevice);
+ if (!strDevice.empty())
+ {
+ int iDevice(0);
+ if (sscanf(strDevice.c_str(), "%x", &iDevice) == 1 && iDevice >= 0 && iDevice <= 0xF)
+ addresses.Set((cec_logical_address)iDevice);
+ }
+ }
+}
+
+void CPeripheralCecAdapter::ReadLogicalAddresses(int iLocalisedId, cec_logical_addresses &addresses)
+{
+ addresses.Clear();
+ switch (iLocalisedId)
+ {
+ case LOCALISED_ID_TV:
+ addresses.Set(CECDEVICE_TV);
+ break;
+ case LOCALISED_ID_AVR:
+ addresses.Set(CECDEVICE_AUDIOSYSTEM);
+ break;
+ case LOCALISED_ID_TV_AVR:
+ addresses.Set(CECDEVICE_TV);
+ addresses.Set(CECDEVICE_AUDIOSYSTEM);
+ break;
+ case LOCALISED_ID_NONE:
+ default:
+ break;
+ }
+}
+
+bool CPeripheralCecAdapter::WriteLogicalAddresses(const cec_logical_addresses& addresses, const string& strSettingName, const string& strAdvancedSettingName)
+{
+ bool bChanged(false);
+
+ // only update the advanced setting if it was set by the user
+ if (!GetSettingString(strAdvancedSettingName).empty())
+ {
+ CStdString strPowerOffDevices;
+ for (unsigned int iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
+ if (addresses[iPtr])
+ strPowerOffDevices += StringUtils::Format(" %X", iPtr);
+ StringUtils::Trim(strPowerOffDevices);
+ bChanged = SetSetting(strAdvancedSettingName, strPowerOffDevices);
+ }
+
+ int iSettingPowerOffDevices = LOCALISED_ID_NONE;
+ if (addresses[CECDEVICE_TV] && addresses[CECDEVICE_AUDIOSYSTEM])
+ iSettingPowerOffDevices = LOCALISED_ID_TV_AVR;
+ else if (addresses[CECDEVICE_TV])
+ iSettingPowerOffDevices = LOCALISED_ID_TV;
+ else if (addresses[CECDEVICE_AUDIOSYSTEM])
+ iSettingPowerOffDevices = LOCALISED_ID_AVR;
+ return SetSetting(strSettingName, iSettingPowerOffDevices) || bChanged;
+}
+
+CPeripheralCecAdapterUpdateThread::CPeripheralCecAdapterUpdateThread(CPeripheralCecAdapter *adapter, libcec_configuration *configuration) :
+ CThread("CECAdapterUpdate"),
+ m_adapter(adapter),
+ m_configuration(*configuration),
+ m_bNextConfigurationScheduled(false),
+ m_bIsUpdating(true)
+{
+ m_nextConfiguration.Clear();
+ m_event.Reset();
+}
+
+CPeripheralCecAdapterUpdateThread::~CPeripheralCecAdapterUpdateThread(void)
+{
+ StopThread(false);
+ m_event.Set();
+ StopThread(true);
+}
+
+void CPeripheralCecAdapterUpdateThread::Signal(void)
+{
+ m_event.Set();
+}
+
+bool CPeripheralCecAdapterUpdateThread::UpdateConfiguration(libcec_configuration *configuration)
+{
+ CSingleLock lock(m_critSection);
+ if (!configuration)
+ return false;
+
+ if (m_bIsUpdating)
+ {
+ m_bNextConfigurationScheduled = true;
+ m_nextConfiguration = *configuration;
+ }
+ else
+ {
+ m_configuration = *configuration;
+ m_event.Set();
+ }
+ return true;
+}
+
+bool CPeripheralCecAdapterUpdateThread::WaitReady(void)
+{
+ // don't wait if we're not powering up anything
+ if (m_configuration.wakeDevices.IsEmpty() && m_configuration.bActivateSource == 0)
+ return true;
+
+ // wait for the TV if we're configured to become the active source.
+ // wait for the first device in the wake list otherwise.
+ cec_logical_address waitFor = (m_configuration.bActivateSource == 1) ?
+ CECDEVICE_TV :
+ m_configuration.wakeDevices.primary;
+
+ cec_power_status powerStatus(CEC_POWER_STATUS_UNKNOWN);
+ bool bContinue(true);
+ while (bContinue && !m_adapter->m_bStop && !m_bStop && powerStatus != CEC_POWER_STATUS_ON)
+ {
+ powerStatus = m_adapter->m_cecAdapter->GetDevicePowerStatus(waitFor);
+ if (powerStatus != CEC_POWER_STATUS_ON)
+ bContinue = !m_event.WaitMSec(1000);
+ }
+
+ return powerStatus == CEC_POWER_STATUS_ON;
+}
+
+void CPeripheralCecAdapterUpdateThread::UpdateMenuLanguage(void)
+{
+ // request the menu language of the TV
+ if (m_configuration.bUseTVMenuLanguage == 1)
+ {
+ CLog::Log(LOGDEBUG, "%s - requesting the menu language of the TV", __FUNCTION__);
+ cec_menu_language language;
+ if (m_adapter->m_cecAdapter->GetDeviceMenuLanguage(CECDEVICE_TV, &language))
+ m_adapter->SetMenuLanguage(language.language);
+ else
+ CLog::Log(LOGDEBUG, "%s - unknown menu language", __FUNCTION__);
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG, "%s - using TV menu language is disabled", __FUNCTION__);
+ }
+}
+
+CStdString CPeripheralCecAdapterUpdateThread::UpdateAudioSystemStatus(void)
+{
+ CStdString strAmpName;
+
+ /* disable the mute setting when an amp is found, because the amp handles the mute setting and
+ set PCM output to 100% */
+ if (m_adapter->m_cecAdapter->IsActiveDeviceType(CEC_DEVICE_TYPE_AUDIO_SYSTEM))
+ {
+ // request the OSD name of the amp
+ cec_osd_name ampName = m_adapter->m_cecAdapter->GetDeviceOSDName(CECDEVICE_AUDIOSYSTEM);
+ CLog::Log(LOGDEBUG, "%s - CEC capable amplifier found (%s). volume will be controlled on the amp", __FUNCTION__, ampName.name);
+ strAmpName += StringUtils::Format("%s", ampName.name);
+
+ // set amp present
+ m_adapter->SetAudioSystemConnected(true);
+ g_application.SetMute(false);
+ g_application.SetVolume(VOLUME_MAXIMUM, false);
+ }
+ else
+ {
+ // set amp present
+ CLog::Log(LOGDEBUG, "%s - no CEC capable amplifier found", __FUNCTION__);
+ m_adapter->SetAudioSystemConnected(false);
+ }
+
+ return strAmpName;
+}
+
+bool CPeripheralCecAdapterUpdateThread::SetInitialConfiguration(void)
+{
+ // the option to make XBMC the active source is set
+ if (m_configuration.bActivateSource == 1)
+ m_adapter->m_cecAdapter->SetActiveSource();
+
+ // devices to wake are set
+ cec_logical_addresses tvOnly;
+ tvOnly.Clear(); tvOnly.Set(CECDEVICE_TV);
+ if (!m_configuration.wakeDevices.IsEmpty() && (m_configuration.wakeDevices != tvOnly || m_configuration.bActivateSource == 0))
+ m_adapter->m_cecAdapter->PowerOnDevices(CECDEVICE_BROADCAST);
+
+ // wait until devices are powered up
+ if (!WaitReady())
+ return false;
+
+ UpdateMenuLanguage();
+
+ // request the OSD name of the TV
+ CStdString strNotification;
+ cec_osd_name tvName = m_adapter->m_cecAdapter->GetDeviceOSDName(CECDEVICE_TV);
+ strNotification = StringUtils::Format("%s: %s", g_localizeStrings.Get(36016).c_str(), tvName.name);
+
+ CStdString strAmpName = UpdateAudioSystemStatus();
+ if (!strAmpName.empty())
+ strNotification += StringUtils::Format("- %s", strAmpName.c_str());
+
+ m_adapter->m_bIsReady = true;
+
+ // and let the gui know that we're done
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(36000), strNotification);
+
+ CSingleLock lock(m_critSection);
+ m_bIsUpdating = false;
+ return true;
+}
+
+bool CPeripheralCecAdapter::IsRunning(void) const
+{
+ CSingleLock lock(m_critSection);
+ return m_bIsRunning;
+}
+
+void CPeripheralCecAdapterUpdateThread::Process(void)
+{
+ // set the initial configuration
+ if (!SetInitialConfiguration())
+ return;
+
+ // and wait for updates
+ bool bUpdate(false);
+ while (!m_bStop)
+ {
+ // update received
+ if (bUpdate || m_event.WaitMSec(500))
+ {
+ if (m_bStop)
+ return;
+ // set the new configuration
+ libcec_configuration configuration;
+ {
+ CSingleLock lock(m_critSection);
+ configuration = m_configuration;
+ m_bIsUpdating = false;
+ }
+
+ CLog::Log(LOGDEBUG, "%s - updating the configuration", __FUNCTION__);
+ bool bConfigSet(m_adapter->m_cecAdapter->SetConfiguration(&configuration));
+ // display message: config updated / failed to update
+ if (!bConfigSet)
+ CLog::Log(LOGERROR, "%s - libCEC couldn't set the new configuration", __FUNCTION__);
+ else
+ {
+ UpdateMenuLanguage();
+ UpdateAudioSystemStatus();
+ }
+
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(36000), g_localizeStrings.Get(bConfigSet ? 36023 : 36024));
+
+ {
+ CSingleLock lock(m_critSection);
+ if ((bUpdate = m_bNextConfigurationScheduled) == true)
+ {
+ // another update is scheduled
+ m_bNextConfigurationScheduled = false;
+ m_configuration = m_nextConfiguration;
+ }
+ else
+ {
+ // nothing left to do, wait for updates
+ m_bIsUpdating = false;
+ m_event.Reset();
+ }
+ }
+ }
+ }
+}
+
+void CPeripheralCecAdapter::OnDeviceRemoved(void)
+{
+ CSingleLock lock(m_critSection);
+ m_bDeviceRemoved = true;
+}
+
+bool CPeripheralCecAdapter::ReopenConnection(void)
+{
+ // stop running thread
+ {
+ CSingleLock lock(m_critSection);
+ m_iExitCode = EXITCODE_RESTARTAPP;
+ CAnnouncementManager::Get().RemoveAnnouncer(this);
+ StopThread(false);
+ }
+ StopThread();
+
+ // reset all members to their defaults
+ ResetMembers();
+
+ // reopen the connection
+ return InitialiseFeature(FEATURE_CEC);
+}
+
+void CPeripheralCecAdapter::ActivateSource(void)
+{
+ CSingleLock lock(m_critSection);
+ m_bActiveSourcePending = true;
+}
+
+void CPeripheralCecAdapter::ProcessActivateSource(void)
+{
+ bool bActivate(false);
+
+ {
+ CSingleLock lock(m_critSection);
+ bActivate = m_bActiveSourcePending;
+ m_bActiveSourcePending = false;
+ }
+
+ if (bActivate)
+ m_cecAdapter->SetActiveSource();
+}
+
+void CPeripheralCecAdapter::StandbyDevices(void)
+{
+ CSingleLock lock(m_critSection);
+ m_bStandbyPending = true;
+}
+
+void CPeripheralCecAdapter::ProcessStandbyDevices(void)
+{
+ bool bStandby(false);
+
+ {
+ CSingleLock lock(m_critSection);
+ bStandby = m_bStandbyPending;
+ m_bStandbyPending = false;
+ if (bStandby)
+ m_bGoingToStandby = true;
+ }
+
+ if (bStandby)
+ {
+ if (!m_configuration.powerOffDevices.IsEmpty())
+ {
+ m_standbySent = CDateTime::GetCurrentDateTime();
+ m_cecAdapter->StandbyDevices(CECDEVICE_BROADCAST);
+ }
+ else if (m_configuration.bSendInactiveSource == 1)
+ {
+ CLog::Log(LOGDEBUG, "%s - sending inactive source commands", __FUNCTION__);
+ m_cecAdapter->SetInactiveView();
+ }
+ }
+}
+
+bool CPeripheralCecAdapter::ToggleDeviceState(CecStateChange mode /*= STATE_SWITCH_TOGGLE */, bool forceType /*= false */)
+{
+ if (!IsRunning())
+ return false;
+ if (m_cecAdapter->IsLibCECActiveSource() && (mode == STATE_SWITCH_TOGGLE || mode == STATE_STANDBY))
+ {
+ CLog::Log(LOGDEBUG, "%s - putting CEC device on standby...", __FUNCTION__);
+ StandbyDevices();
+ return false;
+ }
+ else if (mode == STATE_SWITCH_TOGGLE || mode == STATE_ACTIVATE_SOURCE)
+ {
+ CLog::Log(LOGDEBUG, "%s - waking up CEC device...", __FUNCTION__);
+ ActivateSource();
+ return true;
+ }
+
+ return false;
+}
+
+#endif
diff --git a/src/peripherals/devices/PeripheralCecAdapter.h b/src/peripherals/devices/PeripheralCecAdapter.h
new file mode 100644
index 0000000000..0809b038fd
--- /dev/null
+++ b/src/peripherals/devices/PeripheralCecAdapter.h
@@ -0,0 +1,209 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "system.h"
+
+#if !defined(HAVE_LIBCEC)
+#include "Peripheral.h"
+
+// an empty implementation, so CPeripherals can be compiled without a bunch of #ifdef's when libCEC is not available
+namespace PERIPHERALS
+{
+ class CPeripheralCecAdapter : public CPeripheral
+ {
+ public:
+ bool HasAudioControl(void) { return false; }
+ void VolumeUp(void) {}
+ void VolumeDown(void) {}
+ bool IsMuted(void) { return false; }
+ void ToggleMute(void) {}
+ bool ToggleDeviceState(CecStateChange mode = STATE_SWITCH_TOGGLE, bool forceType = false) { return false; }
+
+ int GetButton(void) { return 0; }
+ unsigned int GetHoldTime(void) { return 0; }
+ void ResetButton(void) {}
+ };
+}
+
+#else
+
+#include "PeripheralHID.h"
+#include "interfaces/AnnouncementManager.h"
+#include "threads/Thread.h"
+#include "threads/CriticalSection.h"
+#include <queue>
+
+// undefine macro isset, it collides with function in cectypes.h
+#ifdef isset
+#undef isset
+#endif
+#include <libcec/cectypes.h>
+
+class DllLibCEC;
+
+namespace CEC
+{
+ class ICECAdapter;
+};
+
+namespace PERIPHERALS
+{
+ class CPeripheralCecAdapterUpdateThread;
+
+ typedef struct
+ {
+ int iButton;
+ unsigned int iDuration;
+ } CecButtonPress;
+
+ typedef enum
+ {
+ VOLUME_CHANGE_NONE,
+ VOLUME_CHANGE_UP,
+ VOLUME_CHANGE_DOWN,
+ VOLUME_CHANGE_MUTE
+ } CecVolumeChange;
+
+ class CPeripheralCecAdapter : public CPeripheralHID, public ANNOUNCEMENT::IAnnouncer, private CThread
+ {
+ friend class CPeripheralCecAdapterUpdateThread;
+
+ public:
+ CPeripheralCecAdapter(const PeripheralScanResult& scanResult);
+ virtual ~CPeripheralCecAdapter(void);
+
+ void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data);
+
+ // audio control
+ bool HasAudioControl(void);
+ void VolumeUp(void);
+ void VolumeDown(void);
+ void ToggleMute(void);
+ bool IsMuted(void);
+
+ // CPeripheral callbacks
+ void OnSettingChanged(const CStdString &strChangedSetting);
+ void OnDeviceRemoved(void);
+
+ // input
+ int GetButton(void);
+ unsigned int GetHoldTime(void);
+ void ResetButton(void);
+
+ // public CEC methods
+ void ActivateSource(void);
+ void StandbyDevices(void);
+ bool ToggleDeviceState(CecStateChange mode = STATE_SWITCH_TOGGLE, bool forceType = false);
+
+ private:
+ bool InitialiseFeature(const PeripheralFeature feature);
+ void ResetMembers(void);
+ void Process(void);
+ bool IsRunning(void) const;
+
+ bool OpenConnection(void);
+ bool ReopenConnection(void);
+
+ void SetConfigurationFromSettings(void);
+ void SetConfigurationFromLibCEC(const CEC::libcec_configuration &config);
+ void SetVersionInfo(const CEC::libcec_configuration &configuration);
+
+ static void ReadLogicalAddresses(const CStdString &strString, CEC::cec_logical_addresses &addresses);
+ static void ReadLogicalAddresses(int iLocalisedId, CEC::cec_logical_addresses &addresses);
+ bool WriteLogicalAddresses(const CEC::cec_logical_addresses& addresses, const std::string& strSettingName, const std::string& strAdvancedSettingName);
+
+ void ProcessActivateSource(void);
+ void ProcessStandbyDevices(void);
+ void ProcessVolumeChange(void);
+
+ void PushCecKeypress(const CEC::cec_keypress &key);
+ void PushCecKeypress(const CecButtonPress &key);
+ void GetNextKey(void);
+
+ void SetAudioSystemConnected(bool bSetTo);
+ void SetMenuLanguage(const char *strLanguage);
+
+ // callbacks from libCEC
+ static int CecLogMessage(void *cbParam, const CEC::cec_log_message message);
+ static int CecCommand(void *cbParam, const CEC::cec_command command);
+ static int CecConfiguration(void *cbParam, const CEC::libcec_configuration config);
+ static int CecAlert(void *cbParam, const CEC::libcec_alert alert, const CEC::libcec_parameter data);
+ static void CecSourceActivated(void *param, const CEC::cec_logical_address address, const uint8_t activated);
+ static int CecKeyPress(void *cbParam, const CEC::cec_keypress key);
+
+ DllLibCEC* m_dll;
+ CEC::ICECAdapter* m_cecAdapter;
+ bool m_bStarted;
+ bool m_bHasButton;
+ bool m_bIsReady;
+ bool m_bHasConnectedAudioSystem;
+ CStdString m_strMenuLanguage;
+ CDateTime m_standbySent;
+ std::vector<CecButtonPress> m_buttonQueue;
+ CecButtonPress m_currentButton;
+ std::queue<CecVolumeChange> m_volumeChangeQueue;
+ unsigned int m_lastKeypress;
+ CecVolumeChange m_lastChange;
+ int m_iExitCode;
+ bool m_bIsMuted;
+ bool m_bGoingToStandby;
+ bool m_bIsRunning;
+ bool m_bDeviceRemoved;
+ CPeripheralCecAdapterUpdateThread*m_queryThread;
+ CEC::ICECCallbacks m_callbacks;
+ CCriticalSection m_critSection;
+ CEC::libcec_configuration m_configuration;
+ bool m_bActiveSourcePending;
+ bool m_bStandbyPending;
+ CDateTime m_preventActivateSourceOnPlay;
+ bool m_bActiveSourceBeforeStandby;
+ bool m_bOnPlayReceived;
+ bool m_bPlaybackPaused;
+ CStdString m_strComPort;
+ };
+
+ class CPeripheralCecAdapterUpdateThread : public CThread
+ {
+ public:
+ CPeripheralCecAdapterUpdateThread(CPeripheralCecAdapter *adapter, CEC::libcec_configuration *configuration);
+ virtual ~CPeripheralCecAdapterUpdateThread(void);
+
+ void Signal(void);
+ bool UpdateConfiguration(CEC::libcec_configuration *configuration);
+
+ protected:
+ void UpdateMenuLanguage(void);
+ CStdString UpdateAudioSystemStatus(void);
+ bool WaitReady(void);
+ bool SetInitialConfiguration(void);
+ void Process(void);
+
+ CPeripheralCecAdapter * m_adapter;
+ CEvent m_event;
+ CCriticalSection m_critSection;
+ CEC::libcec_configuration m_configuration;
+ CEC::libcec_configuration m_nextConfiguration;
+ bool m_bNextConfigurationScheduled;
+ bool m_bIsUpdating;
+ };
+}
+
+#endif
diff --git a/src/peripherals/devices/PeripheralDisk.cpp b/src/peripherals/devices/PeripheralDisk.cpp
new file mode 100644
index 0000000000..f64668a366
--- /dev/null
+++ b/src/peripherals/devices/PeripheralDisk.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralDisk.h"
+#include "guilib/LocalizeStrings.h"
+
+using namespace PERIPHERALS;
+
+CPeripheralDisk::CPeripheralDisk(const PeripheralScanResult& scanResult) :
+ CPeripheral(scanResult)
+{
+ m_strDeviceName = scanResult.m_strDeviceName.empty() ? g_localizeStrings.Get(35003) : scanResult.m_strDeviceName;
+ m_features.push_back(FEATURE_DISK);
+}
diff --git a/src/peripherals/devices/PeripheralDisk.h b/src/peripherals/devices/PeripheralDisk.h
new file mode 100644
index 0000000000..432b7f7d96
--- /dev/null
+++ b/src/peripherals/devices/PeripheralDisk.h
@@ -0,0 +1,32 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Peripheral.h"
+
+namespace PERIPHERALS
+{
+ class CPeripheralDisk : public CPeripheral
+ {
+ public:
+ CPeripheralDisk(const PeripheralScanResult& scanResult);
+ virtual ~CPeripheralDisk(void) {};
+ };
+}
diff --git a/src/peripherals/devices/PeripheralHID.cpp b/src/peripherals/devices/PeripheralHID.cpp
new file mode 100644
index 0000000000..ed1976b48d
--- /dev/null
+++ b/src/peripherals/devices/PeripheralHID.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralHID.h"
+#include "utils/log.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/ButtonTranslator.h"
+
+using namespace PERIPHERALS;
+using namespace std;
+
+CPeripheralHID::CPeripheralHID(const PeripheralScanResult& scanResult) :
+ CPeripheral(scanResult)
+{
+ m_strDeviceName = scanResult.m_strDeviceName.empty() ? g_localizeStrings.Get(35001) : scanResult.m_strDeviceName;
+ m_features.push_back(FEATURE_HID);
+}
+
+CPeripheralHID::~CPeripheralHID(void)
+{
+ if (!m_strKeymap.empty() && !GetSettingBool("do_not_use_custom_keymap"))
+ {
+ CLog::Log(LOGDEBUG, "%s - switching active keymapping to: default", __FUNCTION__);
+ CButtonTranslator::GetInstance().RemoveDevice(m_strKeymap);
+ }
+}
+
+bool CPeripheralHID::InitialiseFeature(const PeripheralFeature feature)
+{
+ if (feature == FEATURE_HID && !m_bInitialised)
+ {
+ m_bInitialised = true;
+
+ if (HasSetting("keymap"))
+ m_strKeymap = GetSettingString("keymap");
+
+ if (m_strKeymap.empty())
+ {
+ m_strKeymap = StringUtils::Format("v%sp%s", VendorIdAsString(), ProductIdAsString());
+ SetSetting("keymap", m_strKeymap);
+ }
+
+ if (!IsSettingVisible("keymap"))
+ SetSettingVisible("do_not_use_custom_keymap", false);
+
+ if (!m_strKeymap.empty())
+ {
+ bool bKeymapEnabled(!GetSettingBool("do_not_use_custom_keymap"));
+ if (bKeymapEnabled)
+ {
+ CLog::Log(LOGDEBUG, "%s - adding keymapping for: %s", __FUNCTION__, m_strKeymap.c_str());
+ CButtonTranslator::GetInstance().AddDevice(m_strKeymap);
+ }
+ else if (!bKeymapEnabled)
+ {
+ CLog::Log(LOGDEBUG, "%s - removing keymapping for: %s", __FUNCTION__, m_strKeymap.c_str());
+ CButtonTranslator::GetInstance().RemoveDevice(m_strKeymap);
+ }
+ }
+
+ CLog::Log(LOGDEBUG, "%s - initialised HID device (%s:%s)", __FUNCTION__, m_strVendorId.c_str(), m_strProductId.c_str());
+ }
+
+ return CPeripheral::InitialiseFeature(feature);
+}
+
+void CPeripheralHID::OnSettingChanged(const CStdString &strChangedSetting)
+{
+ if (m_bInitialised && ((strChangedSetting.Equals("keymap") && !GetSettingBool("do_not_use_custom_keymap")) || strChangedSetting.Equals("keymap_enabled")))
+ {
+ m_bInitialised = false;
+ InitialiseFeature(FEATURE_HID);
+ }
+}
+
diff --git a/src/peripherals/devices/PeripheralHID.h b/src/peripherals/devices/PeripheralHID.h
new file mode 100644
index 0000000000..bc9ea69f56
--- /dev/null
+++ b/src/peripherals/devices/PeripheralHID.h
@@ -0,0 +1,39 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Peripheral.h"
+#include "input/XBMC_keyboard.h"
+
+namespace PERIPHERALS
+{
+ class CPeripheralHID : public CPeripheral
+ {
+ public:
+ CPeripheralHID(const PeripheralScanResult& scanResult);
+ virtual ~CPeripheralHID(void);
+ virtual bool InitialiseFeature(const PeripheralFeature feature);
+ virtual bool LookupSymAndUnicode(XBMC_keysym &keysym, uint8_t *key, char *unicode) { return false; }
+ virtual void OnSettingChanged(const CStdString &strChangedSetting);
+
+ protected:
+ CStdString m_strKeymap;
+ };
+}
diff --git a/src/peripherals/devices/PeripheralImon.cpp b/src/peripherals/devices/PeripheralImon.cpp
new file mode 100644
index 0000000000..d42d647589
--- /dev/null
+++ b/src/peripherals/devices/PeripheralImon.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralImon.h"
+#include "utils/log.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/Settings.h"
+#include "threads/Atomics.h"
+#if defined (TARGET_WINDOWS)
+#include "system.h" // For HAS_SDL_JOYSTICK
+#if defined (HAS_SDL_JOYSTICK)
+#include "input/windows/WINJoystick.h"
+#endif // HAS_SDL_JOYSTICK
+#endif // TARGET_WINDOWS
+
+
+using namespace PERIPHERALS;
+using namespace std;
+
+volatile long CPeripheralImon::m_lCountOfImonsConflictWithDInput = 0;
+
+
+CPeripheralImon::CPeripheralImon(const PeripheralScanResult& scanResult) :
+ CPeripheralHID(scanResult)
+{
+ m_features.push_back(FEATURE_IMON);
+ m_bImonConflictsWithDInput = false;
+}
+
+void CPeripheralImon::OnDeviceRemoved()
+{
+ if (m_bImonConflictsWithDInput)
+ {
+ if (AtomicDecrement(&m_lCountOfImonsConflictWithDInput) == 0)
+ ActionOnImonConflict(false);
+ }
+}
+
+bool CPeripheralImon::InitialiseFeature(const PeripheralFeature feature)
+{
+ if (feature == FEATURE_IMON)
+ {
+#if defined(TARGET_WINDOWS)
+ if (HasSetting("disable_winjoystick") && GetSettingBool("disable_winjoystick"))
+ m_bImonConflictsWithDInput = true;
+ else
+#endif // TARGET_WINDOWS
+ m_bImonConflictsWithDInput = false;
+
+ if (m_bImonConflictsWithDInput)
+ {
+ AtomicIncrement(&m_lCountOfImonsConflictWithDInput);
+ ActionOnImonConflict(true);
+ }
+ return CPeripheral::InitialiseFeature(feature);
+ }
+
+ return CPeripheralHID::InitialiseFeature(feature);
+}
+
+void CPeripheralImon::AddSetting(const CStdString &strKey, const CSetting *setting, int order)
+{
+#if !defined(TARGET_WINDOWS)
+ if (strKey.compare("disable_winjoystick")!=0)
+#endif // !TARGET_WINDOWS
+ CPeripheralHID::AddSetting(strKey, setting, order);
+}
+
+void CPeripheralImon::OnSettingChanged(const CStdString &strChangedSetting)
+{
+ if (strChangedSetting.compare("disable_winjoystick") == 0)
+ {
+ if (m_bImonConflictsWithDInput && !GetSettingBool("disable_winjoystick"))
+ {
+ m_bImonConflictsWithDInput = false;
+ if (AtomicDecrement(&m_lCountOfImonsConflictWithDInput) == 0)
+ ActionOnImonConflict(false);
+ }
+ else if(!m_bImonConflictsWithDInput && GetSettingBool("disable_winjoystick"))
+ {
+ m_bImonConflictsWithDInput = true;
+ AtomicIncrement(&m_lCountOfImonsConflictWithDInput);
+ ActionOnImonConflict(true);
+ }
+ }
+}
+
+void CPeripheralImon::ActionOnImonConflict(bool deviceInserted /*= true*/)
+{
+ if (deviceInserted || m_lCountOfImonsConflictWithDInput == 0)
+ {
+#if defined(TARGET_WINDOWS) && defined (HAS_SDL_JOYSTICK)
+ bool enableJoystickNow = !deviceInserted && CSettings::Get().GetBool("input.enablejoystick");
+ CLog::Log(LOGNOTICE, "Problematic iMON hardware %s. Joystick usage: %s", (deviceInserted ? "detected" : "was removed"),
+ (enableJoystickNow) ? "enabled." : "disabled." );
+ g_Joystick.SetEnabled(enableJoystickNow);
+#endif
+ }
+}
+
diff --git a/src/peripherals/devices/PeripheralImon.h b/src/peripherals/devices/PeripheralImon.h
new file mode 100644
index 0000000000..d73ac8f4f5
--- /dev/null
+++ b/src/peripherals/devices/PeripheralImon.h
@@ -0,0 +1,47 @@
+#pragma once
+/*
+ * Copyright (C) 2012-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralHID.h"
+
+class CSetting;
+
+namespace PERIPHERALS
+{
+ class CPeripheralImon : public CPeripheralHID
+ {
+ public:
+ CPeripheralImon(const PeripheralScanResult& scanResult);
+ virtual ~CPeripheralImon(void) {}
+ virtual bool InitialiseFeature(const PeripheralFeature feature);
+ virtual void OnSettingChanged(const CStdString &strChangedSetting);
+ virtual void OnDeviceRemoved();
+ virtual void AddSetting(const CStdString &strKey, const CSetting *setting, int order);
+ inline bool IsImonConflictsWithDInput()
+ { return m_bImonConflictsWithDInput;}
+ static inline long GetCountOfImonsConflictWithDInput()
+ { return m_lCountOfImonsConflictWithDInput; }
+ static void ActionOnImonConflict(bool deviceInserted = true);
+
+ private:
+ bool m_bImonConflictsWithDInput;
+ static volatile long m_lCountOfImonsConflictWithDInput;
+ };
+}
diff --git a/src/peripherals/devices/PeripheralNIC.cpp b/src/peripherals/devices/PeripheralNIC.cpp
new file mode 100644
index 0000000000..0e413aa8d5
--- /dev/null
+++ b/src/peripherals/devices/PeripheralNIC.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralNIC.h"
+#include "utils/log.h"
+#include "guilib/LocalizeStrings.h"
+
+using namespace PERIPHERALS;
+using namespace std;
+
+CPeripheralNIC::CPeripheralNIC(const PeripheralScanResult& scanResult) :
+ CPeripheral(scanResult)
+{
+ m_strDeviceName = scanResult.m_strDeviceName.empty() ? g_localizeStrings.Get(35002) : scanResult.m_strDeviceName;
+ m_features.push_back(FEATURE_NIC);
+}
diff --git a/src/peripherals/devices/PeripheralNIC.h b/src/peripherals/devices/PeripheralNIC.h
new file mode 100644
index 0000000000..e31e7f1021
--- /dev/null
+++ b/src/peripherals/devices/PeripheralNIC.h
@@ -0,0 +1,32 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Peripheral.h"
+
+namespace PERIPHERALS
+{
+ class CPeripheralNIC : public CPeripheral
+ {
+ public:
+ CPeripheralNIC(const PeripheralScanResult& scanResult);
+ virtual ~CPeripheralNIC(void) {};
+ };
+}
diff --git a/src/peripherals/devices/PeripheralNyxboard.cpp b/src/peripherals/devices/PeripheralNyxboard.cpp
new file mode 100644
index 0000000000..a66bd01241
--- /dev/null
+++ b/src/peripherals/devices/PeripheralNyxboard.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralNyxboard.h"
+#include "PeripheralHID.h"
+#include "guilib/Key.h"
+#include "utils/log.h"
+#include "Application.h"
+
+using namespace PERIPHERALS;
+using namespace std;
+
+CPeripheralNyxboard::CPeripheralNyxboard(const PeripheralScanResult& scanResult) :
+ CPeripheralHID(scanResult)
+{
+ m_features.push_back(FEATURE_NYXBOARD);
+}
+
+bool CPeripheralNyxboard::LookupSymAndUnicode(XBMC_keysym &keysym, uint8_t *key, char *unicode)
+{
+ CStdString strCommand;
+ if (keysym.sym == XBMCK_F7 && keysym.mod == XBMCKMOD_NONE && GetSettingBool("enable_flip_commands"))
+ {
+ /* switched to keyboard side */
+ CLog::Log(LOGDEBUG, "%s - switched to keyboard side", __FUNCTION__);
+ strCommand = GetSettingString("flip_keyboard");
+ }
+ else if (keysym.sym == XBMCK_F7 && keysym.mod == XBMCKMOD_LCTRL && GetSettingBool("enable_flip_commands"))
+ {
+ /* switched to remote side */
+ CLog::Log(LOGDEBUG, "%s - switched to remote side", __FUNCTION__);
+ strCommand = GetSettingString("flip_remote");
+ }
+
+ if (!strCommand.empty())
+ {
+ CLog::Log(LOGDEBUG, "%s - executing command '%s'", __FUNCTION__, strCommand.c_str());
+ if (g_application.ExecuteXBMCAction(strCommand))
+ {
+ *key = 0;
+ *unicode = (char) 0;
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/peripherals/devices/PeripheralNyxboard.h b/src/peripherals/devices/PeripheralNyxboard.h
new file mode 100644
index 0000000000..6648e42929
--- /dev/null
+++ b/src/peripherals/devices/PeripheralNyxboard.h
@@ -0,0 +1,33 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralHID.h"
+
+namespace PERIPHERALS
+{
+ class CPeripheralNyxboard : public CPeripheralHID
+ {
+ public:
+ CPeripheralNyxboard(const PeripheralScanResult& scanResult);
+ virtual ~CPeripheralNyxboard(void) {};
+ virtual bool LookupSymAndUnicode(XBMC_keysym &keysym, uint8_t *key, char *unicode);
+ };
+}
diff --git a/src/peripherals/devices/PeripheralTuner.cpp b/src/peripherals/devices/PeripheralTuner.cpp
new file mode 100644
index 0000000000..65504b0646
--- /dev/null
+++ b/src/peripherals/devices/PeripheralTuner.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "PeripheralTuner.h"
+#include "utils/log.h"
+
+using namespace PERIPHERALS;
+
+CPeripheralTuner::CPeripheralTuner(const PeripheralScanResult& scanResult) :
+ CPeripheral(scanResult)
+{
+ m_features.push_back(FEATURE_TUNER);
+}
diff --git a/src/peripherals/devices/PeripheralTuner.h b/src/peripherals/devices/PeripheralTuner.h
new file mode 100644
index 0000000000..8749378de0
--- /dev/null
+++ b/src/peripherals/devices/PeripheralTuner.h
@@ -0,0 +1,32 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "Peripheral.h"
+
+namespace PERIPHERALS
+{
+ class CPeripheralTuner : public CPeripheral
+ {
+ public:
+ CPeripheralTuner(const PeripheralScanResult& scanResult);
+ virtual ~CPeripheralTuner(void) {};
+ };
+}
diff --git a/src/peripherals/dialogs/GUIDialogPeripheralManager.cpp b/src/peripherals/dialogs/GUIDialogPeripheralManager.cpp
new file mode 100644
index 0000000000..16b4aed84a
--- /dev/null
+++ b/src/peripherals/dialogs/GUIDialogPeripheralManager.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GUIDialogPeripheralManager.h"
+#include "GUIDialogPeripheralSettings.h"
+#include "guilib/GUIWindowManager.h"
+#include "peripherals/Peripherals.h"
+#include "FileItem.h"
+#include "guilib/Key.h"
+#include "utils/log.h"
+
+#define BUTTON_CLOSE 10
+#define BUTTON_SETTINGS 11
+#define CONTROL_LIST 20
+
+using namespace std;
+using namespace PERIPHERALS;
+
+CGUIDialogPeripheralManager::CGUIDialogPeripheralManager(void) :
+ CGUIDialog(WINDOW_DIALOG_PERIPHERAL_MANAGER, "DialogPeripheralManager.xml"),
+ m_iSelected(0),
+ m_peripheralItems(new CFileItemList)
+{
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogPeripheralManager::~CGUIDialogPeripheralManager(void)
+{
+ delete m_peripheralItems;
+}
+
+bool CGUIDialogPeripheralManager::OnAction(const CAction &action)
+{
+ int iActionId = action.GetID();
+ if (GetFocusedControlID() == CONTROL_LIST &&
+ (iActionId == ACTION_MOVE_DOWN || iActionId == ACTION_MOVE_UP ||
+ iActionId == ACTION_PAGE_DOWN || iActionId == ACTION_PAGE_UP))
+ {
+ CGUIDialog::OnAction(action);
+ int iSelected = m_viewControl.GetSelectedItem();
+ if (iSelected != m_iSelected)
+ m_iSelected = iSelected;
+ UpdateButtons();
+ return true;
+ }
+
+ return CGUIDialog::OnAction(action);
+}
+
+void CGUIDialogPeripheralManager::OnInitWindow()
+{
+ m_iSelected = 0;
+ Update();
+ CGUIDialog::OnInitWindow();
+}
+
+bool CGUIDialogPeripheralManager::OnClickList(CGUIMessage &message)
+{
+ if (CurrentItemHasSettings())
+ return OpenSettingsDialog();
+
+ return true;
+}
+
+bool CGUIDialogPeripheralManager::OnClickButtonClose(CGUIMessage &message)
+{
+ Close();
+ return true;
+}
+
+bool CGUIDialogPeripheralManager::OnClickButtonSettings(CGUIMessage &message)
+{
+ return OpenSettingsDialog();
+}
+
+bool CGUIDialogPeripheralManager::OpenSettingsDialog(void)
+{
+ CGUIDialogPeripheralSettings *dialog = (CGUIDialogPeripheralSettings *)g_windowManager.GetWindow(WINDOW_DIALOG_PERIPHERAL_SETTINGS);
+ if (dialog)
+ {
+ dialog->SetFileItem(GetCurrentListItem().get());
+ dialog->DoModal();
+ return true;
+ }
+
+ return false;
+}
+
+bool CGUIDialogPeripheralManager::OnMessageClick(CGUIMessage &message)
+{
+ int iControl = message.GetSenderId();
+ switch(iControl)
+ {
+ case CONTROL_LIST:
+ return OnClickList(message);
+ case BUTTON_CLOSE:
+ return OnClickButtonClose(message);
+ case BUTTON_SETTINGS:
+ return OnClickButtonSettings(message);
+ default:
+ return false;
+ }
+}
+
+bool CGUIDialogPeripheralManager::OnMessage(CGUIMessage& message)
+{
+ unsigned int iMessage = message.GetMessage();
+
+ switch (iMessage)
+ {
+ case GUI_MSG_WINDOW_DEINIT:
+ Clear();
+ break;
+ case GUI_MSG_ITEM_SELECT:
+ return true;
+ case GUI_MSG_CLICKED:
+ return OnMessageClick(message);
+ }
+
+ return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogPeripheralManager::OnWindowLoaded(void)
+{
+ CGUIDialog::OnWindowLoaded();
+
+ m_viewControl.Reset();
+ m_viewControl.SetParentWindow(GetID());
+ const CGUIControl *list = GetControl(CONTROL_LIST);
+ m_viewControl.AddView(list);
+}
+
+void CGUIDialogPeripheralManager::OnWindowUnload(void)
+{
+ CGUIDialog::OnWindowUnload();
+ m_viewControl.Reset();
+}
+
+CFileItemPtr CGUIDialogPeripheralManager::GetCurrentListItem(void) const
+{
+ return m_peripheralItems->Get(m_iSelected);
+}
+
+void CGUIDialogPeripheralManager::Update()
+{
+ CSingleLock lock(g_graphicsContext);
+
+ m_viewControl.SetCurrentView(CONTROL_LIST);
+ Clear();
+ g_peripherals.GetDirectory("peripherals://all/", *m_peripheralItems);
+ m_viewControl.SetItems(*m_peripheralItems);
+ m_viewControl.SetSelectedItem(m_iSelected);
+
+ UpdateButtons();
+ CGUIControl *list = (CGUIControl *) GetControl(CONTROL_LIST);
+ if (list)
+ list->SetInvalid();
+}
+
+void CGUIDialogPeripheralManager::Clear(void)
+{
+ m_viewControl.Clear();
+ m_peripheralItems->Clear();
+}
+
+bool CGUIDialogPeripheralManager::CurrentItemHasSettings(void) const
+{
+ CSingleLock lock(g_graphicsContext);
+ CFileItemPtr currentItem = GetCurrentListItem();
+ if (!currentItem)
+ return false;
+
+ CPeripheral *peripheral = g_peripherals.GetByPath(currentItem.get()->GetPath());
+ return peripheral && peripheral->HasConfigurableSettings();
+}
+
+void CGUIDialogPeripheralManager::UpdateButtons(void)
+{
+ CONTROL_ENABLE_ON_CONDITION(BUTTON_SETTINGS, CurrentItemHasSettings());
+}
diff --git a/src/peripherals/dialogs/GUIDialogPeripheralManager.h b/src/peripherals/dialogs/GUIDialogPeripheralManager.h
new file mode 100644
index 0000000000..1ee107c099
--- /dev/null
+++ b/src/peripherals/dialogs/GUIDialogPeripheralManager.h
@@ -0,0 +1,58 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "guilib/GUIDialog.h"
+#include "view/GUIViewControl.h"
+
+namespace PERIPHERALS
+{
+ class CGUIDialogPeripheralManager : public CGUIDialog
+ {
+ public:
+ CGUIDialogPeripheralManager(void);
+ virtual ~CGUIDialogPeripheralManager(void);
+ virtual bool OnMessage(CGUIMessage& message);
+ virtual bool OnAction(const CAction& action);
+ virtual void OnInitWindow();
+ virtual void OnWindowLoaded(void);
+ virtual void OnWindowUnload(void);
+ virtual bool HasListItems() const { return true; };
+ virtual CFileItemPtr GetCurrentListItem(void) const;
+ virtual void Update(void);
+
+ protected:
+ virtual bool OnMessageClick(CGUIMessage &message);
+
+ virtual bool OnClickList(CGUIMessage &message);
+ virtual bool OnClickButtonClose(CGUIMessage &message);
+ virtual bool OnClickButtonSettings(CGUIMessage &message);
+ virtual bool OpenSettingsDialog(void);
+ virtual bool CurrentItemHasSettings(void) const;
+
+ private:
+ void Clear(void);
+ void UpdateButtons(void);
+
+ int m_iSelected;
+ CFileItemList* m_peripheralItems;
+ CGUIViewControl m_viewControl;
+ };
+}
diff --git a/src/peripherals/dialogs/GUIDialogPeripheralSettings.cpp b/src/peripherals/dialogs/GUIDialogPeripheralSettings.cpp
new file mode 100644
index 0000000000..4e86cdd056
--- /dev/null
+++ b/src/peripherals/dialogs/GUIDialogPeripheralSettings.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2005-2013 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GUIDialogPeripheralSettings.h"
+#include "FileItem.h"
+#include "addons/Skin.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "peripherals/Peripherals.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingSection.h"
+#include "utils/log.h"
+
+#define CONTROL_BUTTON_DEFAULTS 50
+
+using namespace std;
+using namespace PERIPHERALS;
+
+CGUIDialogPeripheralSettings::CGUIDialogPeripheralSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_PERIPHERAL_SETTINGS, "DialogPeripheralSettings.xml"),
+ m_item(NULL),
+ m_initialising(false)
+{ }
+
+CGUIDialogPeripheralSettings::~CGUIDialogPeripheralSettings()
+{
+ if (m_item != NULL)
+ delete m_item;
+
+ m_settingsMap.clear();
+}
+
+bool CGUIDialogPeripheralSettings::OnMessage(CGUIMessage &message)
+{
+ if (message.GetMessage() == GUI_MSG_CLICKED &&
+ message.GetSenderId() == CONTROL_BUTTON_DEFAULTS)
+ {
+ OnResetSettings();
+ return true;
+ }
+
+ return CGUIDialogSettingsManualBase::OnMessage(message);
+}
+
+void CGUIDialogPeripheralSettings::SetFileItem(const CFileItem *item)
+{
+ if (item == NULL)
+ return;
+
+ if (m_item != NULL)
+ delete m_item;
+
+ m_item = new CFileItem(*item);
+}
+
+void CGUIDialogPeripheralSettings::OnSettingChanged(const CSetting *setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ // we need to copy the new value of the setting from the copy to the
+ // original setting
+ std::map<std::string, CSetting*>::iterator itSetting = m_settingsMap.find(setting->GetId());
+ if (itSetting == m_settingsMap.end())
+ return;
+
+ itSetting->second->FromString(setting->ToString());
+}
+
+void CGUIDialogPeripheralSettings::Save()
+{
+ if (m_item == NULL || m_initialising)
+ return;
+
+ CPeripheral *peripheral = g_peripherals.GetByPath(m_item->GetPath());
+ if (peripheral == NULL)
+ return;
+
+ peripheral->PersistSettings();
+}
+
+void CGUIDialogPeripheralSettings::OnResetSettings()
+{
+ if (m_item == NULL)
+ return;
+
+ CPeripheral *peripheral = g_peripherals.GetByPath(m_item->GetPath());
+ if (peripheral == NULL)
+ return;
+
+ if (!CGUIDialogYesNo::ShowAndGetInput(10041, 0, 10042, 0))
+ return;
+
+ // reset the settings in the peripheral
+ peripheral->ResetDefaultSettings();
+
+ // re-create all settings and their controls
+ SetupView();
+}
+
+void CGUIDialogPeripheralSettings::InitializeSettings()
+{
+ if (m_item == NULL)
+ {
+ m_initialising = false;
+ return;
+ }
+
+ m_initialising = true;
+ bool usePopup = g_SkinInfo->HasSkinFile("DialogSlider.xml");
+
+ CPeripheral *peripheral = g_peripherals.GetByPath(m_item->GetPath());
+ if (peripheral == NULL)
+ {
+ CLog::Log(LOGDEBUG, "%s - no peripheral", __FUNCTION__);
+ m_initialising = false;
+ return;
+ }
+
+ m_settingsMap.clear();
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ CSettingCategory *category = AddCategory("peripheralsettings", -1);
+ if (category == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogPeripheralSettings: unable to setup settings");
+ return;
+ }
+
+ CSettingGroup *group = AddGroup(category);
+ if (group == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogPeripheralSettings: unable to setup settings");
+ return;
+ }
+
+ vector<CSetting*> settings = peripheral->GetSettings();
+ for (vector<CSetting*>::iterator itSetting = settings.begin(); itSetting != settings.end(); ++itSetting)
+ {
+ CSetting *setting = *itSetting;
+ if (setting == NULL)
+ continue;
+
+ if (!setting->IsVisible())
+ {
+ CLog::Log(LOGDEBUG, "%s - invisible", __FUNCTION__);
+ continue;
+ }
+
+ // we need to create a copy of the setting because the CSetting instances
+ // are destroyed when leaving the dialog
+ CSetting *settingCopy = NULL;
+ switch(setting->GetType())
+ {
+ case SettingTypeBool:
+ {
+ CSettingBool *settingBool = new CSettingBool(setting->GetId(), *static_cast<CSettingBool*>(setting));
+ settingBool->SetControl(GetCheckmarkControl());
+
+ settingCopy = static_cast<CSetting*>(settingBool);
+ break;
+ }
+
+ case SettingTypeInteger:
+ {
+ CSettingInt *intSetting = static_cast<CSettingInt*>(setting);
+ if (intSetting == NULL)
+ break;
+
+ CSettingInt *settingInt = new CSettingInt(setting->GetId(), *intSetting);
+ if (settingInt->GetOptions().empty())
+ settingInt->SetControl(GetSliderControl("integer", false, -1, usePopup, -1, "%i"));
+ else
+ settingInt->SetControl(GetSpinnerControl("string"));
+
+ settingCopy = static_cast<CSetting*>(settingInt);
+ break;
+ }
+
+ case SettingTypeNumber:
+ {
+ CSettingNumber *settingNumber = new CSettingNumber(setting->GetId(), *static_cast<CSettingNumber*>(setting));
+ settingNumber->SetControl(GetSliderControl("number", false, -1, usePopup, -1, "%2.2f"));
+
+ settingCopy = static_cast<CSetting*>(settingNumber);
+ break;
+ }
+
+ case SettingTypeString:
+ {
+ CSettingString *settingString = new CSettingString(setting->GetId(), *static_cast<CSettingString*>(setting));
+ settingString->SetControl(GetEditControl("string"));
+
+ settingCopy = static_cast<CSetting*>(settingString);
+ break;
+ }
+
+ default:
+ // TODO: add more types if needed
+ CLog::Log(LOGDEBUG, "%s - unknown type", __FUNCTION__);
+ break;
+ }
+
+ if (settingCopy != NULL && settingCopy->GetControl() != NULL)
+ {
+ settingCopy->SetLevel(SettingLevelBasic);
+ group->AddSetting(settingCopy);
+ m_settingsMap.insert(std::make_pair(setting->GetId(), setting));
+ }
+ }
+
+ m_initialising = false;
+}
diff --git a/src/peripherals/dialogs/GUIDialogPeripheralSettings.h b/src/peripherals/dialogs/GUIDialogPeripheralSettings.h
new file mode 100644
index 0000000000..373bd5bfbc
--- /dev/null
+++ b/src/peripherals/dialogs/GUIDialogPeripheralSettings.h
@@ -0,0 +1,55 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2014 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+
+class CFileItem;
+
+namespace PERIPHERALS
+{
+class CGUIDialogPeripheralSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogPeripheralSettings();
+ virtual ~CGUIDialogPeripheralSettings();
+
+ // specializations of CGUIControl
+ virtual bool OnMessage(CGUIMessage &message);
+
+ virtual void SetFileItem(const CFileItem *item);
+
+protected:
+ // implementations of ISettingCallback
+ virtual void OnSettingChanged(const CSetting *setting);
+
+ // specialization of CGUIDialogSettingsBase
+ virtual bool AllowResettingSettings() const { return false; }
+ virtual void Save();
+ virtual void OnResetSettings();
+
+ // specialization of CGUIDialogSettingsManualBase
+ virtual void InitializeSettings();
+
+ CFileItem *m_item;
+ bool m_initialising;
+ std::map<std::string, CSetting*> m_settingsMap;
+};
+}
diff --git a/src/peripherals/dialogs/Makefile b/src/peripherals/dialogs/Makefile
new file mode 100644
index 0000000000..4aa6331efd
--- /dev/null
+++ b/src/peripherals/dialogs/Makefile
@@ -0,0 +1,7 @@
+SRCS=GUIDialogPeripheralSettings.cpp \
+ GUIDialogPeripheralManager.cpp
+
+LIB=peripheral-dialogs.a
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))