summaryrefslogtreecommitdiffstats
path: root/xbmc/peripherals/bus
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/peripherals/bus')
-rw-r--r--xbmc/peripherals/bus/CMakeLists.txt6
-rw-r--r--xbmc/peripherals/bus/PeripheralBus.cpp352
-rw-r--r--xbmc/peripherals/bus/PeripheralBus.h221
-rw-r--r--xbmc/peripherals/bus/PeripheralBusUSB.h29
-rw-r--r--xbmc/peripherals/bus/virtual/CMakeLists.txt12
-rw-r--r--xbmc/peripherals/bus/virtual/PeripheralBusAddon.cpp502
-rw-r--r--xbmc/peripherals/bus/virtual/PeripheralBusAddon.h94
-rw-r--r--xbmc/peripherals/bus/virtual/PeripheralBusApplication.cpp83
-rw-r--r--xbmc/peripherals/bus/virtual/PeripheralBusApplication.h40
-rw-r--r--xbmc/peripherals/bus/virtual/PeripheralBusCEC.cpp57
-rw-r--r--xbmc/peripherals/bus/virtual/PeripheralBusCEC.h43
11 files changed, 1439 insertions, 0 deletions
diff --git a/xbmc/peripherals/bus/CMakeLists.txt b/xbmc/peripherals/bus/CMakeLists.txt
new file mode 100644
index 0000000..81c80c5
--- /dev/null
+++ b/xbmc/peripherals/bus/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(SOURCES PeripheralBus.cpp)
+
+set(HEADERS PeripheralBus.h
+ PeripheralBusUSB.h)
+
+core_add_library(peripherals_bus)
diff --git a/xbmc/peripherals/bus/PeripheralBus.cpp b/xbmc/peripherals/bus/PeripheralBus.cpp
new file mode 100644
index 0000000..59a4c61
--- /dev/null
+++ b/xbmc/peripherals/bus/PeripheralBus.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "PeripheralBus.h"
+
+#include "FileItem.h"
+#include "guilib/LocalizeStrings.h"
+#include "peripherals/Peripherals.h"
+#include "peripherals/devices/Peripheral.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <mutex>
+
+using namespace PERIPHERALS;
+using namespace std::chrono_literals;
+
+namespace
+{
+constexpr auto PERIPHERAL_DEFAULT_RESCAN_INTERVAL = 5000ms;
+}
+
+CPeripheralBus::CPeripheralBus(const std::string& threadname,
+ CPeripherals& manager,
+ PeripheralBusType type)
+ : CThread(threadname.c_str()),
+ m_iRescanTime(PERIPHERAL_DEFAULT_RESCAN_INTERVAL),
+ m_bNeedsPolling(true),
+ m_manager(manager),
+ m_type(type),
+ m_triggerEvent(true)
+{
+}
+
+bool CPeripheralBus::InitializeProperties(CPeripheral& peripheral)
+{
+ return true;
+}
+
+void CPeripheralBus::OnDeviceAdded(const std::string& strLocation)
+{
+ ScanForDevices();
+}
+
+void CPeripheralBus::OnDeviceChanged(const std::string& strLocation)
+{
+ ScanForDevices();
+}
+
+void CPeripheralBus::OnDeviceRemoved(const std::string& strLocation)
+{
+ ScanForDevices();
+}
+
+void CPeripheralBus::Clear(void)
+{
+ if (m_bNeedsPolling)
+ {
+ StopThread(false);
+ m_triggerEvent.Set();
+ StopThread(true);
+ }
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ m_peripherals.clear();
+}
+
+void CPeripheralBus::UnregisterRemovedDevices(const PeripheralScanResults& results)
+{
+ PeripheralVector removedPeripherals;
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (int iDevicePtr = (int)m_peripherals.size() - 1; iDevicePtr >= 0; iDevicePtr--)
+ {
+ const PeripheralPtr& 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);
+ }
+ }
+ }
+
+ for (auto& peripheral : removedPeripherals)
+ {
+ std::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(LOGINFO, "{} - device removed from {}/{}: {} ({}:{})", __FUNCTION__,
+ PeripheralTypeTranslator::TypeToString(peripheral->Type()), peripheral->Location(),
+ peripheral->DeviceName(), peripheral->VendorIdAsString(),
+ peripheral->ProductIdAsString());
+ peripheral->OnDeviceRemoved();
+ }
+
+ m_manager.OnDeviceDeleted(*this, *peripheral);
+ }
+}
+
+void CPeripheralBus::RegisterNewDevices(const PeripheralScanResults& results)
+{
+ for (unsigned int iResultPtr = 0; iResultPtr < results.m_results.size(); iResultPtr++)
+ {
+ const PeripheralScanResult& result = results.m_results.at(iResultPtr);
+ if (!HasPeripheral(result.m_strLocation))
+ m_manager.CreatePeripheral(*this, result);
+ }
+}
+
+bool CPeripheralBus::ScanForDevices(void)
+{
+ bool bReturn(false);
+
+ PeripheralScanResults results;
+ if (PerformDeviceScan(results))
+ {
+ UnregisterRemovedDevices(results);
+ RegisterNewDevices(results);
+
+ m_manager.NotifyObservers(ObservableMessagePeripheralsChanged);
+
+ bReturn = true;
+ }
+
+ return bReturn;
+}
+
+bool CPeripheralBus::HasFeature(const PeripheralFeature feature) const
+{
+ bool bReturn(false);
+ std::unique_lock<CCriticalSection> 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
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (unsigned int iPeripheralPtr = 0; iPeripheralPtr < m_peripherals.size(); iPeripheralPtr++)
+ m_peripherals.at(iPeripheralPtr)->GetFeatures(features);
+}
+
+PeripheralPtr CPeripheralBus::GetPeripheral(const std::string& strLocation) const
+{
+ PeripheralPtr result;
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (auto& peripheral : m_peripherals)
+ {
+ if (peripheral->Location() == strLocation)
+ {
+ result = peripheral;
+ break;
+ }
+ }
+ return result;
+}
+
+unsigned int CPeripheralBus::GetPeripheralsWithFeature(PeripheralVector& results,
+ const PeripheralFeature feature) const
+{
+ unsigned int iReturn = 0;
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (auto& peripheral : m_peripherals)
+ {
+ if (peripheral->HasFeature(feature))
+ {
+ results.push_back(peripheral);
+ ++iReturn;
+ }
+ }
+
+ return iReturn;
+}
+
+unsigned int CPeripheralBus::GetNumberOfPeripheralsWithId(const int iVendorId,
+ const int iProductId) const
+{
+ unsigned int iReturn = 0;
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (const auto& peripheral : m_peripherals)
+ {
+ if (peripheral->VendorId() == iVendorId && peripheral->ProductId() == iProductId)
+ iReturn++;
+ }
+
+ return iReturn;
+}
+
+void CPeripheralBus::Process(void)
+{
+ while (!m_bStop)
+ {
+ m_triggerEvent.Reset();
+
+ if (!ScanForDevices())
+ break;
+
+ // depending on bus implementation
+ // needsPolling can be set properly
+ // only after initial scan.
+ // if this is the case, bail out.
+ if (!m_bNeedsPolling)
+ break;
+
+ if (!m_bStop)
+ m_triggerEvent.Wait(m_iRescanTime);
+ }
+}
+
+void CPeripheralBus::Initialise(void)
+{
+ bool bNeedsPolling = false;
+
+ if (!IsRunning())
+ {
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ bNeedsPolling = m_bNeedsPolling;
+ }
+
+ if (bNeedsPolling)
+ {
+ m_triggerEvent.Reset();
+ Create();
+ SetPriority(ThreadPriority::BELOW_NORMAL);
+ }
+}
+
+void CPeripheralBus::Register(const PeripheralPtr& peripheral)
+{
+ if (!peripheral)
+ return;
+
+ bool bPeripheralAdded = false;
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ if (!HasPeripheral(peripheral->Location()))
+ {
+ m_peripherals.push_back(peripheral);
+ bPeripheralAdded = true;
+ }
+ }
+
+ if (bPeripheralAdded)
+ {
+ CLog::Log(LOGINFO, "{} - new {} device registered on {}->{}: {} ({}:{})", __FUNCTION__,
+ PeripheralTypeTranslator::TypeToString(peripheral->Type()),
+ PeripheralTypeTranslator::BusTypeToString(m_type), peripheral->Location(),
+ peripheral->DeviceName(), peripheral->VendorIdAsString(),
+ peripheral->ProductIdAsString());
+ m_manager.OnDeviceAdded(*this, *peripheral);
+ }
+}
+
+void CPeripheralBus::TriggerDeviceScan(void)
+{
+ bool bNeedsPolling;
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ bNeedsPolling = m_bNeedsPolling;
+ }
+
+ if (bNeedsPolling)
+ m_triggerEvent.Set();
+ else
+ ScanForDevices();
+}
+
+bool CPeripheralBus::HasPeripheral(const std::string& strLocation) const
+{
+ return (GetPeripheral(strLocation) != NULL);
+}
+
+void CPeripheralBus::GetDirectory(const std::string& strPath, CFileItemList& items) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (const auto& peripheral : m_peripherals)
+ {
+ 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()));
+
+ std::string strVersion(peripheral->GetVersionInfo());
+ if (strVersion.empty())
+ strVersion = g_localizeStrings.Get(13205);
+
+ std::string strDetails = StringUtils::Format("{} {}", g_localizeStrings.Get(24051), strVersion);
+ if (peripheral->GetBusType() == PERIPHERAL_BUS_CEC && !peripheral->GetSettingBool("enabled"))
+ strDetails =
+ StringUtils::Format("{}: {}", g_localizeStrings.Get(126), g_localizeStrings.Get(13106));
+
+ peripheralFile->SetProperty("version", strVersion);
+ peripheralFile->SetLabel2(strDetails);
+ peripheralFile->SetArt("icon", peripheral->GetIcon());
+
+ items.Add(peripheralFile);
+ }
+}
+
+PeripheralPtr CPeripheralBus::GetByPath(const std::string& strPath) const
+{
+ PeripheralPtr result;
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (auto& peripheral : m_peripherals)
+ {
+ if (StringUtils::EqualsNoCase(strPath, peripheral->FileLocation()))
+ {
+ result = peripheral;
+ break;
+ }
+ }
+
+ return result;
+}
+
+unsigned int CPeripheralBus::GetNumberOfPeripherals() const
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ return static_cast<unsigned int>(m_peripherals.size());
+}
diff --git a/xbmc/peripherals/bus/PeripheralBus.h b/xbmc/peripherals/bus/PeripheralBus.h
new file mode 100644
index 0000000..6b67a7f
--- /dev/null
+++ b/xbmc/peripherals/bus/PeripheralBus.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "peripherals/PeripheralTypes.h"
+#include "threads/Thread.h"
+
+#include <memory>
+#include <mutex>
+#include <vector>
+
+class CFileItemList;
+
+namespace KODI
+{
+namespace JOYSTICK
+{
+class IButtonMap;
+} // namespace JOYSTICK
+} // namespace KODI
+
+namespace PERIPHERALS
+{
+class CPeripheral;
+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 std::string& threadname, CPeripherals& manager, PeripheralBusType type);
+ ~CPeripheralBus(void) override { Clear(); }
+
+ /*!
+ * @return The bus type
+ */
+ 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
+ {
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ return m_bNeedsPolling;
+ }
+
+ /*!
+ * \brief Initialize the properties of a peripheral with a known location
+ */
+ virtual bool InitializeProperties(CPeripheral& peripheral);
+
+ /*!
+ * \brief Initialize a joystick buttonmap, if possible
+ */
+ virtual bool InitializeButtonMap(const CPeripheral& peripheral,
+ KODI::JOYSTICK::IButtonMap& buttonMap) const
+ {
+ return false;
+ }
+
+ /*!
+ * @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 PeripheralPtr GetPeripheral(const std::string& 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 std::string& strLocation) const;
+
+ /*!
+ * @brief Check if the bus supports the given feature
+ * @param feature The feature to check for
+ * @return True if the bus supports the feature, false otherwise
+ */
+ virtual bool SupportsFeature(PeripheralFeature feature) const { return false; }
+
+ /*!
+ * @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 unsigned int GetPeripheralsWithFeature(PeripheralVector& results,
+ const PeripheralFeature feature) const;
+
+ virtual unsigned int GetNumberOfPeripherals() const;
+ virtual unsigned int 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 std::string& 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 std::string& 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 std::string& strLocation);
+
+ /*!
+ * @brief Initialise this bus and start a polling thread if this bus needs polling.
+ */
+ virtual void 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 std::string& 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 PeripheralPtr GetByPath(const std::string& strPath) const;
+
+ /*!
+ * @brief Register a new peripheral on this bus.
+ * @param peripheral The peripheral to register.
+ */
+ virtual void Register(const PeripheralPtr& peripheral);
+
+ virtual bool FindComPort(std::string& strLocation) { return false; }
+
+ /*!
+ * \brief Poll for events
+ */
+ virtual void ProcessEvents(void) {}
+
+ /*!
+ * \brief Initialize button mapping
+ * \return True if button mapping is enabled for this bus
+ */
+ virtual void EnableButtonMapping() {}
+
+ /*!
+ * \brief Power off the specified device
+ * \param strLocation The device's location
+ */
+ virtual void PowerOff(const std::string& strLocation) {}
+
+protected:
+ void Process(void) override;
+ 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;
+
+ PeripheralVector m_peripherals;
+ std::chrono::milliseconds m_iRescanTime;
+ 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;
+ const PeripheralBusType m_type;
+ mutable CCriticalSection m_critSection;
+ CEvent m_triggerEvent;
+};
+using PeripheralBusPtr = std::shared_ptr<CPeripheralBus>;
+using PeripheralBusVector = std::vector<PeripheralBusPtr>;
+} // namespace PERIPHERALS
diff --git a/xbmc/peripherals/bus/PeripheralBusUSB.h b/xbmc/peripherals/bus/PeripheralBusUSB.h
new file mode 100644
index 0000000..dd32ff6
--- /dev/null
+++ b/xbmc/peripherals/bus/PeripheralBusUSB.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#if defined(TARGET_WINDOWS_DESKTOP)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "platform/win32/peripherals/PeripheralBusUSB.h"
+#elif defined(TARGET_WINDOWS_STORE)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "platform/win10/peripherals/PeripheralBusUSB.h"
+#elif defined(TARGET_LINUX) && defined(HAVE_LIBUDEV)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "platform/linux/peripherals/PeripheralBusUSBLibUdev.h"
+#elif defined(TARGET_LINUX) && defined(HAVE_LIBUSB)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "platform/linux/peripherals/PeripheralBusUSBLibUSB.h"
+#elif defined(TARGET_FREEBSD) && defined(HAVE_LIBUSB)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "platform/linux/peripherals/PeripheralBusUSBLibUSB.h"
+#elif defined(TARGET_DARWIN_OSX)
+#define HAVE_PERIPHERAL_BUS_USB 1
+#include "platform/darwin/osx/peripherals/PeripheralBusUSB.h"
+#endif
diff --git a/xbmc/peripherals/bus/virtual/CMakeLists.txt b/xbmc/peripherals/bus/virtual/CMakeLists.txt
new file mode 100644
index 0000000..bf59695
--- /dev/null
+++ b/xbmc/peripherals/bus/virtual/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(SOURCES PeripheralBusAddon.cpp
+ PeripheralBusApplication.cpp)
+
+set(HEADERS PeripheralBusAddon.h
+ PeripheralBusApplication.h)
+
+if(CEC_FOUND)
+ list(APPEND SOURCES PeripheralBusCEC.cpp)
+ list(APPEND HEADERS PeripheralBusCEC.h)
+endif()
+
+core_add_library(peripheral_bus_virtual)
diff --git a/xbmc/peripherals/bus/virtual/PeripheralBusAddon.cpp b/xbmc/peripherals/bus/virtual/PeripheralBusAddon.cpp
new file mode 100644
index 0000000..2162c45
--- /dev/null
+++ b/xbmc/peripherals/bus/virtual/PeripheralBusAddon.cpp
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2014-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "PeripheralBusAddon.h"
+
+#include "ServiceBroker.h"
+#include "addons/AddonEvents.h"
+#include "addons/AddonManager.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/addoninfo/AddonType.h"
+#include "messaging/helpers/DialogHelper.h"
+#include "peripherals/Peripherals.h"
+#include "peripherals/addons/PeripheralAddon.h"
+#include "peripherals/devices/PeripheralJoystick.h"
+#include "threads/SingleLock.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+
+using namespace KODI;
+using namespace PERIPHERALS;
+
+CPeripheralBusAddon::CPeripheralBusAddon(CPeripherals& manager)
+ : CPeripheralBus("PeripBusAddon", manager, PERIPHERAL_BUS_ADDON)
+{
+ using namespace ADDON;
+
+ CServiceBroker::GetAddonMgr().Events().Subscribe(this, &CPeripheralBusAddon::OnEvent);
+
+ UpdateAddons();
+}
+
+CPeripheralBusAddon::~CPeripheralBusAddon()
+{
+ using namespace ADDON;
+
+ CServiceBroker::GetAddonMgr().Events().Unsubscribe(this);
+
+ // stop everything before destroying any (loaded) addons
+ Clear();
+
+ // destroy any (loaded) addons
+ for (const auto& addon : m_addons)
+ addon->DestroyAddon();
+
+ m_failedAddons.clear();
+ m_addons.clear();
+}
+
+bool CPeripheralBusAddon::GetAddonWithButtonMap(PeripheralAddonPtr& addon) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ auto it = std::find_if(m_addons.begin(), m_addons.end(),
+ [](const PeripheralAddonPtr& addon) { return addon->HasButtonMaps(); });
+
+ if (it != m_addons.end())
+ {
+ addon = *it;
+ return true;
+ }
+
+ return false;
+}
+
+bool CPeripheralBusAddon::GetAddonWithButtonMap(const CPeripheral* device,
+ PeripheralAddonPtr& addon) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ // If device is from an add-on, try to use that add-on
+ if (device && device->GetBusType() == PERIPHERAL_BUS_ADDON)
+ {
+ PeripheralAddonPtr addonWithButtonMap;
+ unsigned int index;
+ if (SplitLocation(device->Location(), addonWithButtonMap, index))
+ {
+ if (addonWithButtonMap->HasButtonMaps())
+ addon = std::move(addonWithButtonMap);
+ else
+ CLog::Log(LOGDEBUG, "Add-on {} doesn't provide button maps for its controllers",
+ addonWithButtonMap->ID());
+ }
+ }
+
+ if (!addon)
+ {
+ auto it = std::find_if(m_addons.begin(), m_addons.end(),
+ [](const PeripheralAddonPtr& addon) { return addon->HasButtonMaps(); });
+
+ if (it != m_addons.end())
+ addon = *it;
+ }
+
+ return addon.get() != nullptr;
+}
+
+bool CPeripheralBusAddon::PerformDeviceScan(PeripheralScanResults& results)
+{
+ PeripheralAddonVector addons;
+ {
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ addons = m_addons;
+ }
+
+ for (const auto& addon : addons)
+ addon->PerformDeviceScan(results);
+
+ // Scan during bus initialization must return true or bus gets deleted
+ return true;
+}
+
+bool CPeripheralBusAddon::InitializeProperties(CPeripheral& peripheral)
+{
+ if (!CPeripheralBus::InitializeProperties(peripheral))
+ return false;
+
+ bool bSuccess = false;
+
+ PeripheralAddonPtr addon;
+ unsigned int index;
+
+ if (SplitLocation(peripheral.Location(), addon, index))
+ {
+ switch (peripheral.Type())
+ {
+ case PERIPHERAL_JOYSTICK:
+ bSuccess =
+ addon->GetJoystickProperties(index, static_cast<CPeripheralJoystick&>(peripheral));
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return bSuccess;
+}
+
+bool CPeripheralBusAddon::SendRumbleEvent(const std::string& strLocation,
+ unsigned int motorIndex,
+ float magnitude)
+{
+ bool bHandled = false;
+
+ PeripheralAddonPtr addon;
+ unsigned int peripheralIndex;
+ if (SplitLocation(strLocation, addon, peripheralIndex))
+ bHandled = addon->SendRumbleEvent(peripheralIndex, motorIndex, magnitude);
+
+ return bHandled;
+}
+
+void CPeripheralBusAddon::ProcessEvents(void)
+{
+ PeripheralAddonVector addons;
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ addons = m_addons;
+ }
+
+ for (const auto& addon : addons)
+ addon->ProcessEvents();
+}
+
+void CPeripheralBusAddon::EnableButtonMapping()
+{
+ using namespace ADDON;
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ PeripheralAddonPtr dummy;
+
+ if (!GetAddonWithButtonMap(dummy))
+ {
+ std::vector<AddonInfoPtr> disabledAddons;
+ CServiceBroker::GetAddonMgr().GetDisabledAddonInfos(disabledAddons, AddonType::PERIPHERALDLL);
+ if (!disabledAddons.empty())
+ PromptEnableAddons(disabledAddons);
+ }
+}
+
+void CPeripheralBusAddon::PowerOff(const std::string& strLocation)
+{
+ PeripheralAddonPtr addon;
+ unsigned int peripheralIndex;
+ if (SplitLocation(strLocation, addon, peripheralIndex))
+ addon->PowerOffJoystick(peripheralIndex);
+}
+
+void CPeripheralBusAddon::UnregisterRemovedDevices(const PeripheralScanResults& results)
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ PeripheralVector removedPeripherals;
+
+ for (const auto& addon : m_addons)
+ addon->UnregisterRemovedDevices(results, removedPeripherals);
+
+ for (const auto& peripheral : removedPeripherals)
+ m_manager.OnDeviceDeleted(*this, *peripheral);
+}
+
+void CPeripheralBusAddon::Register(const PeripheralPtr& peripheral)
+{
+ if (!peripheral)
+ return;
+
+ PeripheralAddonPtr addon;
+ unsigned int peripheralIndex;
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ if (SplitLocation(peripheral->Location(), addon, peripheralIndex))
+ {
+ if (addon->Register(peripheralIndex, peripheral))
+ m_manager.OnDeviceAdded(*this, *peripheral);
+ }
+}
+
+void CPeripheralBusAddon::GetFeatures(std::vector<PeripheralFeature>& features) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (const auto& addon : m_addons)
+ addon->GetFeatures(features);
+}
+
+bool CPeripheralBusAddon::HasFeature(const PeripheralFeature feature) const
+{
+ bool bReturn(false);
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (const auto& addon : m_addons)
+ bReturn = bReturn || addon->HasFeature(feature);
+ return bReturn;
+}
+
+PeripheralPtr CPeripheralBusAddon::GetPeripheral(const std::string& strLocation) const
+{
+ PeripheralPtr peripheral;
+ PeripheralAddonPtr addon;
+ unsigned int peripheralIndex;
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ if (SplitLocation(strLocation, addon, peripheralIndex))
+ peripheral = addon->GetPeripheral(peripheralIndex);
+
+ return peripheral;
+}
+
+PeripheralPtr CPeripheralBusAddon::GetByPath(const std::string& strPath) const
+{
+ PeripheralPtr result;
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ for (const auto& addon : m_addons)
+ {
+ PeripheralPtr peripheral = addon->GetByPath(strPath);
+ if (peripheral)
+ {
+ result = peripheral;
+ break;
+ }
+ }
+
+ return result;
+}
+
+bool CPeripheralBusAddon::SupportsFeature(PeripheralFeature feature) const
+{
+ bool bSupportsFeature = false;
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (const auto& addon : m_addons)
+ bSupportsFeature |= addon->SupportsFeature(feature);
+
+ return bSupportsFeature;
+}
+
+unsigned int CPeripheralBusAddon::GetPeripheralsWithFeature(PeripheralVector& results,
+ const PeripheralFeature feature) const
+{
+ unsigned int iReturn = 0;
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (const auto& addon : m_addons)
+ iReturn += addon->GetPeripheralsWithFeature(results, feature);
+ return iReturn;
+}
+
+unsigned int CPeripheralBusAddon::GetNumberOfPeripherals(void) const
+{
+ unsigned int iReturn = 0;
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (const auto& addon : m_addons)
+ iReturn += addon->GetNumberOfPeripherals();
+ return iReturn;
+}
+
+unsigned int CPeripheralBusAddon::GetNumberOfPeripheralsWithId(const int iVendorId,
+ const int iProductId) const
+{
+ unsigned int iReturn = 0;
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (const auto& addon : m_addons)
+ iReturn += addon->GetNumberOfPeripheralsWithId(iVendorId, iProductId);
+ return iReturn;
+}
+
+void CPeripheralBusAddon::GetDirectory(const std::string& strPath, CFileItemList& items) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (const auto& addon : m_addons)
+ addon->GetDirectory(strPath, items);
+}
+
+void CPeripheralBusAddon::OnEvent(const ADDON::AddonEvent& event)
+{
+ if (typeid(event) == typeid(ADDON::AddonEvents::Enabled) ||
+ typeid(event) == typeid(ADDON::AddonEvents::ReInstalled))
+ {
+ if (CServiceBroker::GetAddonMgr().HasType(event.addonId, ADDON::AddonType::PERIPHERALDLL))
+ UpdateAddons();
+ }
+ else if (typeid(event) == typeid(ADDON::AddonEvents::Disabled))
+ {
+ if (CServiceBroker::GetAddonMgr().HasType(event.addonId, ADDON::AddonType::PERIPHERALDLL))
+ UnRegisterAddon(event.addonId);
+ }
+ else if (typeid(event) == typeid(ADDON::AddonEvents::UnInstalled))
+ {
+ UnRegisterAddon(event.addonId);
+ }
+}
+
+bool CPeripheralBusAddon::SplitLocation(const std::string& strLocation,
+ PeripheralAddonPtr& addon,
+ unsigned int& peripheralIndex) const
+{
+ std::vector<std::string> parts = StringUtils::Split(strLocation, "/");
+ if (parts.size() == 2)
+ {
+ addon.reset();
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ const std::string& strAddonId = parts[0];
+ for (const auto& addonIt : m_addons)
+ {
+ if (addonIt->ID() == strAddonId)
+ {
+ addon = addonIt;
+ break;
+ }
+ }
+
+ if (addon)
+ {
+ const char* strJoystickIndex = parts[1].c_str();
+ char* p = NULL;
+ peripheralIndex = strtol(strJoystickIndex, &p, 10);
+ if (strJoystickIndex != p)
+ return true;
+ }
+ }
+ return false;
+}
+
+void CPeripheralBusAddon::UpdateAddons(void)
+{
+ using namespace ADDON;
+
+ auto GetPeripheralAddonID = [](const PeripheralAddonPtr& addon) { return addon->ID(); };
+ auto GetAddonID = [](const AddonInfoPtr& addon) { return addon->ID(); };
+
+ std::set<std::string> currentIds;
+ std::set<std::string> newIds;
+
+ std::set<std::string> added;
+ std::set<std::string> removed;
+
+ // Get new add-ons
+ std::vector<AddonInfoPtr> newAddons;
+ CServiceBroker::GetAddonMgr().GetAddonInfos(newAddons, true, AddonType::PERIPHERALDLL);
+ std::transform(newAddons.begin(), newAddons.end(), std::inserter(newIds, newIds.end()),
+ GetAddonID);
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ // Get current add-ons
+ std::transform(m_addons.begin(), m_addons.end(), std::inserter(currentIds, currentIds.end()),
+ GetPeripheralAddonID);
+ std::transform(m_failedAddons.begin(), m_failedAddons.end(),
+ std::inserter(currentIds, currentIds.end()), GetPeripheralAddonID);
+
+ // Differences
+ std::set_difference(newIds.begin(), newIds.end(), currentIds.begin(), currentIds.end(),
+ std::inserter(added, added.end()));
+ std::set_difference(currentIds.begin(), currentIds.end(), newIds.begin(), newIds.end(),
+ std::inserter(removed, removed.end()));
+
+ // Register new add-ons
+ for (const std::string& addonId : added)
+ {
+ CLog::Log(LOGDEBUG, "Add-on bus: Registering add-on {}", addonId);
+
+ auto GetAddon = [&addonId](const AddonInfoPtr& addon) { return addon->ID() == addonId; };
+
+ auto it = std::find_if(newAddons.begin(), newAddons.end(), GetAddon);
+ if (it != newAddons.end())
+ {
+ PeripheralAddonPtr newAddon = std::make_shared<CPeripheralAddon>(*it, m_manager);
+ if (newAddon)
+ {
+ bool bCreated;
+
+ {
+ CSingleExit exit(m_critSection);
+ bCreated = newAddon->CreateAddon();
+ }
+
+ if (bCreated)
+ m_addons.emplace_back(std::move(newAddon));
+ else
+ m_failedAddons.emplace_back(std::move(newAddon));
+ }
+ }
+ }
+
+ // Destroy removed add-ons
+ for (const std::string& addonId : removed)
+ {
+ UnRegisterAddon(addonId);
+ }
+}
+
+void CPeripheralBusAddon::UnRegisterAddon(const std::string& addonId)
+{
+ PeripheralAddonPtr erased;
+ auto ErasePeripheralAddon = [&addonId, &erased](const PeripheralAddonPtr& addon) {
+ if (addon->ID() == addonId)
+ {
+ erased = addon;
+ return true;
+ }
+ return false;
+ };
+
+ m_addons.erase(std::remove_if(m_addons.begin(), m_addons.end(), ErasePeripheralAddon),
+ m_addons.end());
+ if (!erased)
+ m_failedAddons.erase(
+ std::remove_if(m_failedAddons.begin(), m_failedAddons.end(), ErasePeripheralAddon),
+ m_failedAddons.end());
+
+ if (erased)
+ {
+ CLog::Log(LOGDEBUG, "Add-on bus: Unregistered add-on {}", addonId);
+ CSingleExit exit(m_critSection);
+ erased->DestroyAddon();
+ }
+}
+
+void CPeripheralBusAddon::PromptEnableAddons(
+ const std::vector<std::shared_ptr<ADDON::CAddonInfo>>& disabledAddons)
+{
+ using namespace ADDON;
+ using namespace MESSAGING::HELPERS;
+
+ // True if the user confirms enabling the disabled peripheral add-on
+ bool bAccepted = false;
+
+ auto itAddon =
+ std::find_if(disabledAddons.begin(), disabledAddons.end(), [](const AddonInfoPtr& addonInfo) {
+ return CPeripheralAddon::ProvidesJoysticks(addonInfo);
+ });
+
+ if (itAddon != disabledAddons.end())
+ {
+ // "Unable to configure controllers"
+ // "Controller configuration depends on a disabled add-on. Would you like to enable it?"
+ bAccepted =
+ (ShowYesNoDialogLines(CVariant{35017}, CVariant{35018}) == DialogResponse::CHOICE_YES);
+ }
+
+ if (bAccepted)
+ {
+ for (const auto& addonInfo : disabledAddons)
+ {
+ if (CPeripheralAddon::ProvidesJoysticks(addonInfo))
+ CServiceBroker::GetAddonMgr().EnableAddon(addonInfo->ID());
+ }
+ }
+}
diff --git a/xbmc/peripherals/bus/virtual/PeripheralBusAddon.h b/xbmc/peripherals/bus/virtual/PeripheralBusAddon.h
new file mode 100644
index 0000000..696b189
--- /dev/null
+++ b/xbmc/peripherals/bus/virtual/PeripheralBusAddon.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "peripherals/PeripheralTypes.h"
+#include "peripherals/bus/PeripheralBus.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace ADDON
+{
+struct AddonEvent;
+class CAddonInfo;
+} // namespace ADDON
+
+namespace PERIPHERALS
+{
+class CPeripheralBusAddon : public CPeripheralBus
+{
+public:
+ explicit CPeripheralBusAddon(CPeripherals& manager);
+ ~CPeripheralBusAddon(void) override;
+
+ void UpdateAddons(void);
+
+ /*!
+ * \brief Get peripheral add-on that can provide button maps
+ */
+ bool GetAddonWithButtonMap(PeripheralAddonPtr& addon) const;
+
+ /*!
+ * \brief Get peripheral add-on that can provide button maps for the given device
+ */
+ bool GetAddonWithButtonMap(const CPeripheral* device, PeripheralAddonPtr& addon) const;
+
+ /*!
+ * \brief Set the rumble state of a rumble motor
+ *
+ * \param strLocation The location of the peripheral with the motor
+ * \param motorIndex The index of the motor being rumbled
+ * \param magnitude The amount of vibration in the closed interval [0.0, 1.0]
+ *
+ * \return true if the rumble motor's state is set, false otherwise
+ *
+ * TODO: Move declaration to parent class
+ */
+ bool SendRumbleEvent(const std::string& strLocation, unsigned int motorIndex, float magnitude);
+
+ // Inherited from CPeripheralBus
+ bool InitializeProperties(CPeripheral& peripheral) override;
+ void Register(const PeripheralPtr& peripheral) override;
+ void GetFeatures(std::vector<PeripheralFeature>& features) const override;
+ bool HasFeature(const PeripheralFeature feature) const override;
+ PeripheralPtr GetPeripheral(const std::string& strLocation) const override;
+ PeripheralPtr GetByPath(const std::string& strPath) const override;
+ bool SupportsFeature(PeripheralFeature feature) const override;
+ unsigned int GetPeripheralsWithFeature(PeripheralVector& results,
+ const PeripheralFeature feature) const override;
+ unsigned int GetNumberOfPeripherals(void) const override;
+ unsigned int GetNumberOfPeripheralsWithId(const int iVendorId,
+ const int iProductId) const override;
+ void GetDirectory(const std::string& strPath, CFileItemList& items) const override;
+ void ProcessEvents(void) override;
+ void EnableButtonMapping() override;
+ void PowerOff(const std::string& strLocation) override;
+
+ bool SplitLocation(const std::string& strLocation,
+ PeripheralAddonPtr& addon,
+ unsigned int& peripheralIndex) const;
+
+protected:
+ // Inherited from CPeripheralBus
+ bool PerformDeviceScan(PeripheralScanResults& results) override;
+ void UnregisterRemovedDevices(const PeripheralScanResults& results) override;
+
+private:
+ void OnEvent(const ADDON::AddonEvent& event);
+ void UnRegisterAddon(const std::string& addonId);
+
+ void PromptEnableAddons(const std::vector<std::shared_ptr<ADDON::CAddonInfo>>& disabledAddons);
+
+ PeripheralAddonVector m_addons;
+ PeripheralAddonVector m_failedAddons;
+};
+using PeripheralBusAddonPtr = std::shared_ptr<CPeripheralBusAddon>;
+} // namespace PERIPHERALS
diff --git a/xbmc/peripherals/bus/virtual/PeripheralBusApplication.cpp b/xbmc/peripherals/bus/virtual/PeripheralBusApplication.cpp
new file mode 100644
index 0000000..4dad03c
--- /dev/null
+++ b/xbmc/peripherals/bus/virtual/PeripheralBusApplication.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "PeripheralBusApplication.h"
+
+#include "ServiceBroker.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/StringUtils.h"
+
+using namespace PERIPHERALS;
+
+CPeripheralBusApplication::CPeripheralBusApplication(CPeripherals& manager)
+ : CPeripheralBus("PeripBusApplication", manager, PERIPHERAL_BUS_APPLICATION)
+{
+ // Initialize CPeripheralBus
+ m_bNeedsPolling = false;
+}
+
+void CPeripheralBusApplication::Initialise(void)
+{
+ CPeripheralBus::Initialise();
+ TriggerDeviceScan();
+}
+
+bool CPeripheralBusApplication::PerformDeviceScan(PeripheralScanResults& results)
+{
+ {
+ PeripheralScanResult result(Type());
+ result.m_type = PERIPHERAL_KEYBOARD;
+ result.m_strDeviceName = g_localizeStrings.Get(35150); // "Keyboard"
+ result.m_strLocation = PeripheralTypeTranslator::TypeToString(PERIPHERAL_KEYBOARD);
+ result.m_iVendorId = 0;
+ result.m_iProductId = 0;
+ result.m_mappedType = PERIPHERAL_KEYBOARD;
+ result.m_mappedBusType = Type();
+ result.m_iSequence = 0;
+
+ if (!results.ContainsResult(result))
+ results.m_results.push_back(result);
+ }
+
+ bool bHasMouse = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
+ CSettings::SETTING_INPUT_ENABLEMOUSE);
+
+ //! @todo Fix game clients to handle mouse disconnecting
+ //! For now mouse is always connected
+ bHasMouse = true;
+
+ if (bHasMouse)
+ {
+ PeripheralScanResult result(Type());
+ result.m_type = PERIPHERAL_MOUSE;
+ result.m_strDeviceName = g_localizeStrings.Get(35171); // "Mouse"
+ result.m_strLocation = PeripheralTypeTranslator::TypeToString(PERIPHERAL_MOUSE);
+ result.m_iVendorId = 0;
+ result.m_iProductId = 0;
+ result.m_mappedType = PERIPHERAL_MOUSE;
+ result.m_mappedBusType = Type();
+ result.m_iSequence = 0;
+
+ if (!results.ContainsResult(result))
+ results.m_results.push_back(result);
+ }
+
+ return true;
+}
+
+void CPeripheralBusApplication::GetDirectory(const std::string& strPath, CFileItemList& items) const
+{
+ // Don't list virtual devices in the GUI
+}
+
+std::string CPeripheralBusApplication::MakeLocation(unsigned int controllerIndex) const
+{
+ return std::to_string(controllerIndex);
+}
diff --git a/xbmc/peripherals/bus/virtual/PeripheralBusApplication.h b/xbmc/peripherals/bus/virtual/PeripheralBusApplication.h
new file mode 100644
index 0000000..4fcc40d
--- /dev/null
+++ b/xbmc/peripherals/bus/virtual/PeripheralBusApplication.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "peripherals/bus/PeripheralBus.h"
+
+namespace PERIPHERALS
+{
+/*!
+ * @class CPeripheralBusApplication
+ *
+ * This exposes peripherals that exist logically at the application level,
+ * such as emulated joysticks.
+ */
+class CPeripheralBusApplication : public CPeripheralBus
+{
+public:
+ explicit CPeripheralBusApplication(CPeripherals& manager);
+ ~CPeripheralBusApplication(void) override = default;
+
+ // implementation of CPeripheralBus
+ void Initialise(void) override;
+ void GetDirectory(const std::string& strPath, CFileItemList& items) const override;
+
+ /*!
+ * \brief Get the location for the specified controller index
+ */
+ std::string MakeLocation(unsigned int controllerIndex) const;
+
+protected:
+ // implementation of CPeripheralBus
+ bool PerformDeviceScan(PeripheralScanResults& results) override;
+};
+} // namespace PERIPHERALS
diff --git a/xbmc/peripherals/bus/virtual/PeripheralBusCEC.cpp b/xbmc/peripherals/bus/virtual/PeripheralBusCEC.cpp
new file mode 100644
index 0000000..e119638
--- /dev/null
+++ b/xbmc/peripherals/bus/virtual/PeripheralBusCEC.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "PeripheralBusCEC.h"
+
+#include <libcec/cec.h>
+
+using namespace PERIPHERALS;
+using namespace CEC;
+
+CPeripheralBusCEC::CPeripheralBusCEC(CPeripherals& manager)
+ : CPeripheralBus("PeripBusCEC", manager, PERIPHERAL_BUS_CEC)
+{
+ m_cecAdapter = CECInitialise(&m_configuration);
+}
+
+CPeripheralBusCEC::~CPeripheralBusCEC(void)
+{
+ if (m_cecAdapter)
+ CECDestroy(m_cecAdapter);
+}
+
+bool CPeripheralBusCEC::PerformDeviceScan(PeripheralScanResults& results)
+{
+ 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;
+ default:
+ break;
+ }
+
+ result.m_iSequence = GetNumberOfPeripheralsWithId(result.m_iVendorId, result.m_iProductId);
+ if (!results.ContainsResult(result))
+ results.m_results.push_back(result);
+ }
+ return true;
+}
diff --git a/xbmc/peripherals/bus/virtual/PeripheralBusCEC.h b/xbmc/peripherals/bus/virtual/PeripheralBusCEC.h
new file mode 100644
index 0000000..9aa2643
--- /dev/null
+++ b/xbmc/peripherals/bus/virtual/PeripheralBusCEC.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "peripherals/bus/PeripheralBus.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 CPeripheralBusCEC : public CPeripheralBus
+{
+public:
+ explicit CPeripheralBusCEC(CPeripherals& manager);
+ ~CPeripheralBusCEC(void) override;
+
+ /*!
+ * @see PeripheralBus::PerformDeviceScan()
+ */
+ bool PerformDeviceScan(PeripheralScanResults& results) override;
+
+private:
+ CEC::ICECAdapter* m_cecAdapter;
+ CEC::libcec_configuration m_configuration;
+};
+} // namespace PERIPHERALS