summaryrefslogtreecommitdiffstats
path: root/xbmc/peripherals/bus/virtual
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/peripherals/bus/virtual')
-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
7 files changed, 831 insertions, 0 deletions
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