diff options
Diffstat (limited to 'xbmc/games/controllers/dialogs')
-rw-r--r-- | xbmc/games/controllers/dialogs/CMakeLists.txt | 15 | ||||
-rw-r--r-- | xbmc/games/controllers/dialogs/ControllerInstaller.cpp | 138 | ||||
-rw-r--r-- | xbmc/games/controllers/dialogs/ControllerInstaller.h | 28 | ||||
-rw-r--r-- | xbmc/games/controllers/dialogs/ControllerSelect.cpp | 133 | ||||
-rw-r--r-- | xbmc/games/controllers/dialogs/ControllerSelect.h | 44 | ||||
-rw-r--r-- | xbmc/games/controllers/dialogs/GUIDialogAxisDetection.cpp | 84 | ||||
-rw-r--r-- | xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h | 54 | ||||
-rw-r--r-- | xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp | 130 | ||||
-rw-r--r-- | xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h | 65 | ||||
-rw-r--r-- | xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.cpp | 132 | ||||
-rw-r--r-- | xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h | 47 |
11 files changed, 870 insertions, 0 deletions
diff --git a/xbmc/games/controllers/dialogs/CMakeLists.txt b/xbmc/games/controllers/dialogs/CMakeLists.txt new file mode 100644 index 0000000..e40e60e --- /dev/null +++ b/xbmc/games/controllers/dialogs/CMakeLists.txt @@ -0,0 +1,15 @@ +set(SOURCES ControllerInstaller.cpp + ControllerSelect.cpp + GUIDialogAxisDetection.cpp + GUIDialogButtonCapture.cpp + GUIDialogIgnoreInput.cpp +) + +set(HEADERS ControllerInstaller.h + ControllerSelect.h + GUIDialogAxisDetection.h + GUIDialogButtonCapture.h + GUIDialogIgnoreInput.h +) + +core_add_library(games_controller_dialogs) diff --git a/xbmc/games/controllers/dialogs/ControllerInstaller.cpp b/xbmc/games/controllers/dialogs/ControllerInstaller.cpp new file mode 100644 index 0000000..9b0fe6f --- /dev/null +++ b/xbmc/games/controllers/dialogs/ControllerInstaller.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 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 "ControllerInstaller.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "addons/Addon.h" +#include "addons/AddonInstaller.h" +#include "addons/AddonManager.h" +#include "addons/addoninfo/AddonType.h" +#include "dialogs/GUIDialogProgress.h" +#include "dialogs/GUIDialogSelect.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "guilib/WindowIDs.h" +#include "messaging/helpers/DialogOKHelper.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +using namespace KODI; +using namespace GAME; + +CControllerInstaller::CControllerInstaller() : CThread("ControllerInstaller") +{ +} + +void CControllerInstaller::Process() +{ + CGUIComponent* gui = CServiceBroker::GetGUI(); + if (gui == nullptr) + return; + + CGUIWindowManager& windowManager = gui->GetWindowManager(); + + auto pSelectDialog = windowManager.GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + if (pSelectDialog == nullptr) + return; + + auto pProgressDialog = windowManager.GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS); + if (pProgressDialog == nullptr) + return; + + ADDON::VECADDONS installableAddons; + CServiceBroker::GetAddonMgr().GetInstallableAddons(installableAddons, + ADDON::AddonType::GAME_CONTROLLER); + if (installableAddons.empty()) + { + // "Controller profiles" + // "All available controller profiles are installed." + MESSAGING::HELPERS::ShowOKDialogText(CVariant{35050}, CVariant{35062}); + return; + } + + CLog::Log(LOGDEBUG, "Controller installer: Found {} controller add-ons", + installableAddons.size()); + + CFileItemList items; + for (const auto& addon : installableAddons) + { + CFileItemPtr item(new CFileItem(addon->Name())); + item->SetArt("icon", addon->Icon()); + items.Add(std::move(item)); + } + + pSelectDialog->Reset(); + pSelectDialog->SetHeading(39020); // "The following additional add-ons will be installed" + pSelectDialog->SetUseDetails(true); + pSelectDialog->EnableButton(true, 186); // "OK"" + for (const auto& it : items) + pSelectDialog->Add(*it); + pSelectDialog->Open(); + + if (!pSelectDialog->IsButtonPressed()) + { + CLog::Log(LOGDEBUG, "Controller installer: User cancelled installation dialog"); + return; + } + + CLog::Log(LOGDEBUG, "Controller installer: Installing {} controller add-ons", + installableAddons.size()); + + pProgressDialog->SetHeading(CVariant{24086}); // "Installing add-on..." + pProgressDialog->SetLine(0, CVariant{""}); + pProgressDialog->SetLine(1, CVariant{""}); + pProgressDialog->SetLine(2, CVariant{""}); + + pProgressDialog->Open(); + + unsigned int installedCount = 0; + while (installedCount < installableAddons.size()) + { + const auto& addon = installableAddons[installedCount]; + + // Set dialog text + const std::string& progressTemplate = g_localizeStrings.Get(24057); // "Installing {0:s}..." + const std::string progressText = StringUtils::Format(progressTemplate, addon->Name()); + pProgressDialog->SetLine(0, CVariant{progressText}); + + // Set dialog percentage + const unsigned int percentage = + 100 * (installedCount + 1) / static_cast<unsigned int>(installableAddons.size()); + pProgressDialog->SetPercentage(percentage); + + if (!ADDON::CAddonInstaller::GetInstance().InstallOrUpdate( + addon->ID(), ADDON::BackgroundJob::CHOICE_NO, ADDON::ModalJob::CHOICE_NO)) + { + CLog::Log(LOGERROR, "Controller installer: Failed to install {}", addon->ID()); + // "Error" + // "Failed to install add-on." + MESSAGING::HELPERS::ShowOKDialogText(257, 35256); + break; + } + + if (pProgressDialog->IsCanceled()) + { + CLog::Log(LOGDEBUG, "Controller installer: User cancelled add-on installation"); + break; + } + + if (windowManager.GetActiveWindowOrDialog() != WINDOW_DIALOG_PROGRESS) + { + CLog::Log(LOGDEBUG, "Controller installer: Progress dialog is hidden, cancelling"); + break; + } + + installedCount++; + } + + CLog::Log(LOGDEBUG, "Controller window: Installed {} controller add-ons", installedCount); + pProgressDialog->Close(); +} diff --git a/xbmc/games/controllers/dialogs/ControllerInstaller.h b/xbmc/games/controllers/dialogs/ControllerInstaller.h new file mode 100644 index 0000000..9863e27 --- /dev/null +++ b/xbmc/games/controllers/dialogs/ControllerInstaller.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 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 "threads/Thread.h" + +namespace KODI +{ +namespace GAME +{ +class CControllerInstaller : public CThread +{ +public: + CControllerInstaller(); + ~CControllerInstaller() override = default; + +protected: + // implementation of CThread + void Process() override; +}; +} // namespace GAME +} // namespace KODI diff --git a/xbmc/games/controllers/dialogs/ControllerSelect.cpp b/xbmc/games/controllers/dialogs/ControllerSelect.cpp new file mode 100644 index 0000000..2ef9941 --- /dev/null +++ b/xbmc/games/controllers/dialogs/ControllerSelect.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2021 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 "ControllerSelect.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "dialogs/GUIDialogSelect.h" +#include "games/controllers/Controller.h" +#include "games/controllers/ControllerLayout.h" +#include "games/controllers/ControllerTypes.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "guilib/WindowIDs.h" +#include "utils/Variant.h" +#include "utils/log.h" + +using namespace KODI; +using namespace GAME; + +CControllerSelect::CControllerSelect() : CThread("ControllerSelect") +{ +} + +CControllerSelect::~CControllerSelect() = default; + +void CControllerSelect::Initialize(ControllerVector controllers, + ControllerPtr defaultController, + bool showDisconnect, + const std::function<void(ControllerPtr)>& callback) +{ + // Validate parameters + if (!callback) + return; + + // Stop thread and reset state + Deinitialize(); + + // Initialize state + m_controllers = std::move(controllers); + m_defaultController = std::move(defaultController); + m_showDisconnect = showDisconnect; + m_callback = callback; + + // Create thread + Create(false); +} + +void CControllerSelect::Deinitialize() +{ + // Stop thread + StopThread(true); + + // Reset state + m_controllers.clear(); + m_defaultController.reset(); + m_showDisconnect = true; + m_callback = nullptr; +} + +void CControllerSelect::Process() +{ + // Select first controller by default + unsigned int initialSelected = 0; + + CGUIComponent* gui = CServiceBroker::GetGUI(); + if (gui == nullptr) + return; + + CGUIWindowManager& windowManager = gui->GetWindowManager(); + + auto pSelectDialog = windowManager.GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + if (pSelectDialog == nullptr) + return; + + CLog::Log(LOGDEBUG, "Controller select: Showing dialog for {} controllers", m_controllers.size()); + + CFileItemList items; + for (const ControllerPtr& controller : m_controllers) + { + CFileItemPtr item(new CFileItem(controller->Layout().Label())); + item->SetArt("icon", controller->Layout().ImagePath()); + items.Add(std::move(item)); + + // Check if a specified controller should be selected by default + if (m_defaultController && m_defaultController->ID() == controller->ID()) + initialSelected = items.Size() - 1; + } + + if (m_showDisconnect) + { + // Add a button to disconnect the port + CFileItemPtr item(new CFileItem(g_localizeStrings.Get(13298))); // "Disconnected" + item->SetArt("icon", "DefaultAddonNone.png"); + items.Add(std::move(item)); + + // Check if the disconnect button should be selected by default + if (!m_defaultController) + initialSelected = items.Size() - 1; + } + + pSelectDialog->Reset(); + pSelectDialog->SetHeading(CVariant{35113}); // "Select a Controller" + pSelectDialog->SetUseDetails(true); + pSelectDialog->EnableButton(false, 186); // "OK"" + pSelectDialog->SetButtonFocus(false); + for (const auto& it : items) + pSelectDialog->Add(*it); + pSelectDialog->SetSelected(static_cast<int>(initialSelected)); + pSelectDialog->Open(); + + // If the thread was stopped, exit early + if (m_bStop) + return; + + if (pSelectDialog->IsConfirmed()) + { + ControllerPtr resultController; + + const int selected = pSelectDialog->GetSelectedItem(); + if (0 <= selected && selected < static_cast<int>(m_controllers.size())) + resultController = m_controllers.at(selected); + + // Fire a callback with the result + m_callback(resultController); + } +} diff --git a/xbmc/games/controllers/dialogs/ControllerSelect.h b/xbmc/games/controllers/dialogs/ControllerSelect.h new file mode 100644 index 0000000..83a1b41 --- /dev/null +++ b/xbmc/games/controllers/dialogs/ControllerSelect.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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 "games/controllers/ControllerTypes.h" +#include "threads/Thread.h" + +#include <functional> + +namespace KODI +{ +namespace GAME +{ +class CControllerSelect : public CThread +{ +public: + CControllerSelect(); + ~CControllerSelect() override; + + void Initialize(ControllerVector controllers, + ControllerPtr defaultController, + bool showDisconnect, + const std::function<void(ControllerPtr)>& callback); + void Deinitialize(); + +protected: + // Implementation of CThread + void Process() override; + +private: + // State parameters + ControllerVector m_controllers; + ControllerPtr m_defaultController; + bool m_showDisconnect = true; + std::function<void(ControllerPtr)> m_callback; +}; +} // namespace GAME +} // namespace KODI diff --git a/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.cpp b/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.cpp new file mode 100644 index 0000000..2052a4c --- /dev/null +++ b/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017-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 "GUIDialogAxisDetection.h" + +#include "guilib/LocalizeStrings.h" +#include "input/joysticks/DriverPrimitive.h" +#include "input/joysticks/JoystickTranslator.h" +#include "input/joysticks/interfaces/IButtonMap.h" +#include "utils/StringUtils.h" + +#include <algorithm> + +using namespace KODI; +using namespace GAME; + +std::string CGUIDialogAxisDetection::GetDialogText() +{ + // "Press all analog buttons now to detect them:[CR][CR]%s" + const std::string& dialogText = g_localizeStrings.Get(35020); + + std::vector<std::string> primitives; + + for (const auto& axisEntry : m_detectedAxes) + { + JOYSTICK::CDriverPrimitive axis(axisEntry.second, 0, JOYSTICK::SEMIAXIS_DIRECTION::POSITIVE, 1); + primitives.emplace_back(JOYSTICK::CJoystickTranslator::GetPrimitiveName(axis)); + } + + return StringUtils::Format(dialogText, StringUtils::Join(primitives, " | ")); +} + +std::string CGUIDialogAxisDetection::GetDialogHeader() +{ + return g_localizeStrings.Get(35058); // "Controller Configuration" +} + +bool CGUIDialogAxisDetection::MapPrimitiveInternal(JOYSTICK::IButtonMap* buttonMap, + IKeymap* keymap, + const JOYSTICK::CDriverPrimitive& primitive) +{ + if (primitive.Type() == JOYSTICK::PRIMITIVE_TYPE::SEMIAXIS) + AddAxis(buttonMap->Location(), primitive.Index()); + + return true; +} + +bool CGUIDialogAxisDetection::AcceptsPrimitive(JOYSTICK::PRIMITIVE_TYPE type) const +{ + switch (type) + { + case JOYSTICK::PRIMITIVE_TYPE::SEMIAXIS: + return true; + default: + break; + } + + return false; +} + +void CGUIDialogAxisDetection::OnLateAxis(const JOYSTICK::IButtonMap* buttonMap, + unsigned int axisIndex) +{ + AddAxis(buttonMap->Location(), axisIndex); +} + +void CGUIDialogAxisDetection::AddAxis(const std::string& deviceLocation, unsigned int axisIndex) +{ + auto it = std::find_if(m_detectedAxes.begin(), m_detectedAxes.end(), + [&deviceLocation, axisIndex](const AxisEntry& axis) { + return axis.first == deviceLocation && axis.second == axisIndex; + }); + + if (it == m_detectedAxes.end()) + { + m_detectedAxes.emplace_back(std::make_pair(deviceLocation, axisIndex)); + m_captureEvent.Set(); + } +} diff --git a/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h b/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h new file mode 100644 index 0000000..4ca0d27 --- /dev/null +++ b/xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017-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 "GUIDialogButtonCapture.h" + +#include <string> +#include <utility> +#include <vector> + +namespace KODI +{ +namespace GAME +{ +class CGUIDialogAxisDetection : public CGUIDialogButtonCapture +{ +public: + CGUIDialogAxisDetection() = default; + + ~CGUIDialogAxisDetection() override = default; + + // specialization of IButtonMapper via CGUIDialogButtonCapture + bool AcceptsPrimitive(JOYSTICK::PRIMITIVE_TYPE type) const override; + void OnLateAxis(const JOYSTICK::IButtonMap* buttonMap, unsigned int axisIndex) override; + +protected: + // implementation of CGUIDialogButtonCapture + std::string GetDialogText() override; + std::string GetDialogHeader() override; + bool MapPrimitiveInternal(JOYSTICK::IButtonMap* buttonMap, + IKeymap* keymap, + const JOYSTICK::CDriverPrimitive& primitive) override; + void OnClose(bool bAccepted) override {} + +private: + void AddAxis(const std::string& deviceLocation, unsigned int axisIndex); + + // Axis types + using DeviceName = std::string; + using AxisIndex = unsigned int; + using AxisEntry = std::pair<DeviceName, AxisIndex>; + using AxisVector = std::vector<AxisEntry>; + + // Axis detection + AxisVector m_detectedAxes; +}; +} // namespace GAME +} // namespace KODI diff --git a/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp new file mode 100644 index 0000000..9bf8dbc --- /dev/null +++ b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2016-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 "GUIDialogButtonCapture.h" + +#include "ServiceBroker.h" +#include "games/controllers/ControllerIDs.h" +#include "input/IKeymap.h" +#include "input/actions/ActionIDs.h" +#include "input/joysticks/JoystickUtils.h" +#include "input/joysticks/interfaces/IButtonMap.h" +#include "messaging/helpers/DialogOKHelper.h" +#include "peripherals/Peripherals.h" +#include "utils/Variant.h" + +#include <algorithm> +#include <iterator> + +using namespace KODI; +using namespace GAME; +using namespace KODI::MESSAGING; + +CGUIDialogButtonCapture::CGUIDialogButtonCapture() : CThread("ButtonCaptureDlg") +{ +} + +std::string CGUIDialogButtonCapture::ControllerID(void) const +{ + return DEFAULT_CONTROLLER_ID; +} + +void CGUIDialogButtonCapture::Show() +{ + if (!IsRunning()) + { + InstallHooks(); + + Create(); + + bool bAccepted = + HELPERS::ShowOKDialogText(CVariant{GetDialogHeader()}, CVariant{GetDialogText()}); + + StopThread(false); + + m_captureEvent.Set(); + + OnClose(bAccepted); + + RemoveHooks(); + } +} + +void CGUIDialogButtonCapture::Process() +{ + while (!m_bStop) + { + m_captureEvent.Wait(); + + if (m_bStop) + break; + + //! @todo Move to rendering thread when there is a rendering thread + HELPERS::UpdateOKDialogText(CVariant{35013}, CVariant{GetDialogText()}); + } +} + +bool CGUIDialogButtonCapture::MapPrimitive(JOYSTICK::IButtonMap* buttonMap, + IKeymap* keymap, + const JOYSTICK::CDriverPrimitive& primitive) +{ + if (m_bStop) + return false; + + // First check to see if driver primitive closes the dialog + if (keymap && keymap->ControllerID() == buttonMap->ControllerID()) + { + std::string feature; + if (buttonMap->GetFeature(primitive, feature)) + { + const auto& actions = + keymap->GetActions(JOYSTICK::CJoystickUtils::MakeKeyName(feature)).actions; + if (!actions.empty()) + { + switch (actions.begin()->actionId) + { + case ACTION_SELECT_ITEM: + case ACTION_NAV_BACK: + case ACTION_PREVIOUS_MENU: + return false; + default: + break; + } + } + } + } + + return MapPrimitiveInternal(buttonMap, keymap, primitive); +} + +void CGUIDialogButtonCapture::InstallHooks(void) +{ + CServiceBroker::GetPeripherals().RegisterJoystickButtonMapper(this); + CServiceBroker::GetPeripherals().RegisterObserver(this); +} + +void CGUIDialogButtonCapture::RemoveHooks(void) +{ + CServiceBroker::GetPeripherals().UnregisterObserver(this); + CServiceBroker::GetPeripherals().UnregisterJoystickButtonMapper(this); +} + +void CGUIDialogButtonCapture::Notify(const Observable& obs, const ObservableMessage msg) +{ + switch (msg) + { + case ObservableMessagePeripheralsChanged: + { + CServiceBroker::GetPeripherals().UnregisterJoystickButtonMapper(this); + CServiceBroker::GetPeripherals().RegisterJoystickButtonMapper(this); + break; + } + default: + break; + } +} diff --git a/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h new file mode 100644 index 0000000..97795c6 --- /dev/null +++ b/xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016-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 "input/joysticks/interfaces/IButtonMapper.h" +#include "threads/Event.h" +#include "threads/Thread.h" +#include "utils/Observer.h" + +#include <string> +#include <vector> + +namespace KODI +{ +namespace GAME +{ +class CGUIDialogButtonCapture : public JOYSTICK::IButtonMapper, public Observer, protected CThread +{ +public: + CGUIDialogButtonCapture(); + + ~CGUIDialogButtonCapture() override = default; + + // implementation of IButtonMapper + std::string ControllerID() const override; + bool NeedsCooldown() const override { return false; } + bool MapPrimitive(JOYSTICK::IButtonMap* buttonMap, + IKeymap* keymap, + const JOYSTICK::CDriverPrimitive& primitive) override; + void OnEventFrame(const JOYSTICK::IButtonMap* buttonMap, bool bMotion) override {} + void OnLateAxis(const JOYSTICK::IButtonMap* buttonMap, unsigned int axisIndex) override {} + + // implementation of Observer + void Notify(const Observable& obs, const ObservableMessage msg) override; + + /*! + * \brief Show the dialog + */ + void Show(); + +protected: + // implementation of CThread + void Process() override; + + virtual std::string GetDialogText() = 0; + virtual std::string GetDialogHeader() = 0; + virtual bool MapPrimitiveInternal(JOYSTICK::IButtonMap* buttonMap, + IKeymap* keymap, + const JOYSTICK::CDriverPrimitive& primitive) = 0; + virtual void OnClose(bool bAccepted) = 0; + + CEvent m_captureEvent; + +private: + void InstallHooks(); + void RemoveHooks(); +}; +} // namespace GAME +} // namespace KODI diff --git a/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.cpp b/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.cpp new file mode 100644 index 0000000..b5f28d6 --- /dev/null +++ b/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2017-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 "GUIDialogIgnoreInput.h" + +#include "guilib/LocalizeStrings.h" +#include "input/joysticks/JoystickTranslator.h" +#include "input/joysticks/interfaces/IButtonMap.h" +#include "input/joysticks/interfaces/IButtonMapCallback.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include <algorithm> +#include <iterator> + +using namespace KODI; +using namespace GAME; + +bool CGUIDialogIgnoreInput::AcceptsPrimitive(JOYSTICK::PRIMITIVE_TYPE type) const +{ + switch (type) + { + case JOYSTICK::PRIMITIVE_TYPE::BUTTON: + case JOYSTICK::PRIMITIVE_TYPE::SEMIAXIS: + return true; + default: + break; + } + + return false; +} + +std::string CGUIDialogIgnoreInput::GetDialogText() +{ + // "Some controllers have buttons and axes that interfere with mapping. Press + // these now to disable them:[CR]%s" + const std::string& dialogText = g_localizeStrings.Get(35014); + + std::vector<std::string> primitives; + + std::transform(m_capturedPrimitives.begin(), m_capturedPrimitives.end(), + std::back_inserter(primitives), [](const JOYSTICK::CDriverPrimitive& primitive) { + return JOYSTICK::CJoystickTranslator::GetPrimitiveName(primitive); + }); + + return StringUtils::Format(dialogText, StringUtils::Join(primitives, " | ")); +} + +std::string CGUIDialogIgnoreInput::GetDialogHeader() +{ + + return g_localizeStrings.Get(35019); // "Ignore input" +} + +bool CGUIDialogIgnoreInput::MapPrimitiveInternal(JOYSTICK::IButtonMap* buttonMap, + IKeymap* keymap, + const JOYSTICK::CDriverPrimitive& primitive) +{ + // Check if we have already started capturing primitives for a device + const bool bHasDevice = !m_location.empty(); + + // If a primitive comes from a different device, ignore it + if (bHasDevice && m_location != buttonMap->Location()) + { + CLog::Log(LOGDEBUG, "{}: ignoring input from device {}", buttonMap->ControllerID(), + buttonMap->Location()); + return false; + } + + if (!bHasDevice) + { + CLog::Log(LOGDEBUG, "{}: capturing input for device {}", buttonMap->ControllerID(), + buttonMap->Location()); + m_location = buttonMap->Location(); + } + + if (AddPrimitive(primitive)) + { + buttonMap->SetIgnoredPrimitives(m_capturedPrimitives); + m_captureEvent.Set(); + } + + return true; +} + +void CGUIDialogIgnoreInput::OnClose(bool bAccepted) +{ + for (auto& callback : ButtonMapCallbacks()) + { + if (bAccepted) + { + // See documentation of IButtonMapCallback::ResetIgnoredPrimitives() + // for why this call is needed + if (m_location.empty()) + callback.second->ResetIgnoredPrimitives(); + + if (m_location.empty() || m_location == callback.first) + callback.second->SaveButtonMap(); + } + else + callback.second->RevertButtonMap(); + } +} + +bool CGUIDialogIgnoreInput::AddPrimitive(const JOYSTICK::CDriverPrimitive& primitive) +{ + bool bValid = false; + + if (primitive.Type() == JOYSTICK::PRIMITIVE_TYPE::BUTTON || + primitive.Type() == JOYSTICK::PRIMITIVE_TYPE::SEMIAXIS) + { + auto PrimitiveMatch = [&primitive](const JOYSTICK::CDriverPrimitive& other) { + return primitive.Type() == other.Type() && primitive.Index() == other.Index(); + }; + + bValid = std::find_if(m_capturedPrimitives.begin(), m_capturedPrimitives.end(), + PrimitiveMatch) == m_capturedPrimitives.end(); + } + + if (bValid) + { + m_capturedPrimitives.emplace_back(primitive); + return true; + } + + return false; +} diff --git a/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h b/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h new file mode 100644 index 0000000..e1d3922 --- /dev/null +++ b/xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017-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 "GUIDialogButtonCapture.h" +#include "input/joysticks/DriverPrimitive.h" + +#include <string> +#include <vector> + +namespace KODI +{ +namespace GAME +{ +class CGUIDialogIgnoreInput : public CGUIDialogButtonCapture +{ +public: + CGUIDialogIgnoreInput() = default; + + ~CGUIDialogIgnoreInput() override = default; + + // specialization of IButtonMapper via CGUIDialogButtonCapture + bool AcceptsPrimitive(JOYSTICK::PRIMITIVE_TYPE type) const override; + +protected: + // implementation of CGUIDialogButtonCapture + std::string GetDialogText() override; + std::string GetDialogHeader() override; + bool MapPrimitiveInternal(JOYSTICK::IButtonMap* buttonMap, + IKeymap* keymap, + const JOYSTICK::CDriverPrimitive& primitive) override; + void OnClose(bool bAccepted) override; + +private: + bool AddPrimitive(const JOYSTICK::CDriverPrimitive& primitive); + + std::string m_location; + std::vector<JOYSTICK::CDriverPrimitive> m_capturedPrimitives; +}; +} // namespace GAME +} // namespace KODI |