summaryrefslogtreecommitdiffstats
path: root/xbmc/games/controllers/dialogs
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/games/controllers/dialogs')
-rw-r--r--xbmc/games/controllers/dialogs/CMakeLists.txt15
-rw-r--r--xbmc/games/controllers/dialogs/ControllerInstaller.cpp138
-rw-r--r--xbmc/games/controllers/dialogs/ControllerInstaller.h28
-rw-r--r--xbmc/games/controllers/dialogs/ControllerSelect.cpp133
-rw-r--r--xbmc/games/controllers/dialogs/ControllerSelect.h44
-rw-r--r--xbmc/games/controllers/dialogs/GUIDialogAxisDetection.cpp84
-rw-r--r--xbmc/games/controllers/dialogs/GUIDialogAxisDetection.h54
-rw-r--r--xbmc/games/controllers/dialogs/GUIDialogButtonCapture.cpp130
-rw-r--r--xbmc/games/controllers/dialogs/GUIDialogButtonCapture.h65
-rw-r--r--xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.cpp132
-rw-r--r--xbmc/games/controllers/dialogs/GUIDialogIgnoreInput.h47
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