diff options
-rw-r--r-- | addons/skin.estuary/xml/Includes_Games.xml | 8 | ||||
-rw-r--r-- | xbmc/games/addons/input/GameClientInput.cpp | 11 | ||||
-rw-r--r-- | xbmc/games/addons/input/GameClientInput.h | 1 | ||||
-rw-r--r-- | xbmc/games/addons/input/GameClientJoystick.cpp | 5 | ||||
-rw-r--r-- | xbmc/games/addons/input/GameClientJoystick.h | 5 | ||||
-rw-r--r-- | xbmc/games/agents/GameAgentManager.cpp | 10 | ||||
-rw-r--r-- | xbmc/games/agents/GameAgentManager.h | 3 | ||||
-rw-r--r-- | xbmc/games/controllers/guicontrols/GUIGameController.cpp | 74 | ||||
-rw-r--r-- | xbmc/games/controllers/guicontrols/GUIGameController.h | 9 | ||||
-rw-r--r-- | xbmc/games/controllers/input/CMakeLists.txt | 6 | ||||
-rw-r--r-- | xbmc/games/controllers/input/ControllerActivity.cpp | 48 | ||||
-rw-r--r-- | xbmc/games/controllers/input/ControllerActivity.h | 44 | ||||
-rw-r--r-- | xbmc/games/ports/input/PortInput.cpp | 27 | ||||
-rw-r--r-- | xbmc/games/ports/input/PortInput.h | 10 | ||||
-rw-r--r-- | xbmc/guilib/GUIControlFactory.cpp | 10 |
15 files changed, 255 insertions, 16 deletions
diff --git a/addons/skin.estuary/xml/Includes_Games.xml b/addons/skin.estuary/xml/Includes_Games.xml index 8c110a7785..e3d553afa0 100644 --- a/addons/skin.estuary/xml/Includes_Games.xml +++ b/addons/skin.estuary/xml/Includes_Games.xml @@ -254,10 +254,12 @@ <shadowcolor>text_shadow</shadowcolor> <align>right</align> </control> - <control type="image"> + <control type="gamecontroller"> <right>12</right> <width>100</width> <texture>$INFO[ListItem.Icon]</texture> + <portaddress>$INFO[ListItem.FilenameAndPath]</portaddress> + <controllerdiffuse>button_focus</controllerdiffuse> </control> </control> </itemlayout> @@ -287,10 +289,12 @@ <shadowcolor>text_shadow</shadowcolor> <align>right</align> </control> - <control type="image"> + <control type="gamecontroller"> <right>12</right> <width>100</width> <texture>$INFO[ListItem.Icon]</texture> + <portaddress>$INFO[ListItem.FilenameAndPath]</portaddress> + <controllerdiffuse>button_focus</controllerdiffuse> </control> </control> </focusedlayout> diff --git a/xbmc/games/addons/input/GameClientInput.cpp b/xbmc/games/addons/input/GameClientInput.cpp index 5805cbf1bc..13300db461 100644 --- a/xbmc/games/addons/input/GameClientInput.cpp +++ b/xbmc/games/addons/input/GameClientInput.cpp @@ -166,6 +166,17 @@ bool CGameClientInput::InputEvent(const game_input_event& event) return bHandled; } +float CGameClientInput::GetPortActivation(const std::string& portAddress) +{ + float activation = 0.0f; + + auto it = m_joysticks.find(portAddress); + if (it != m_joysticks.end()) + activation = it->second->GetActivation(); + + return activation; +} + void CGameClientInput::LoadTopology() { game_input_topology* topologyStruct = nullptr; diff --git a/xbmc/games/addons/input/GameClientInput.h b/xbmc/games/addons/input/GameClientInput.h index be7e71c9b9..5dce0fb53b 100644 --- a/xbmc/games/addons/input/GameClientInput.h +++ b/xbmc/games/addons/input/GameClientInput.h @@ -62,6 +62,7 @@ public: bool HasFeature(const std::string& controllerId, const std::string& featureName) const; bool AcceptsInput() const; bool InputEvent(const game_input_event& event); + float GetPortActivation(const std::string& portAddress); // Topology functions const CControllerTree& GetDefaultControllerTree() const; diff --git a/xbmc/games/addons/input/GameClientJoystick.cpp b/xbmc/games/addons/input/GameClientJoystick.cpp index d4f083ba08..367fb10d63 100644 --- a/xbmc/games/addons/input/GameClientJoystick.cpp +++ b/xbmc/games/addons/input/GameClientJoystick.cpp @@ -184,6 +184,11 @@ std::string CGameClientJoystick::GetSourceLocation() const return ""; } +float CGameClientJoystick::GetActivation() const +{ + return m_portInput->GetActivation(); +} + void CGameClientJoystick::SetSource(PERIPHERALS::PeripheralPtr sourcePeripheral) { m_sourcePeripheral = std::move(sourcePeripheral); diff --git a/xbmc/games/addons/input/GameClientJoystick.h b/xbmc/games/addons/input/GameClientJoystick.h index cfb1709a12..d5144426b9 100644 --- a/xbmc/games/addons/input/GameClientJoystick.h +++ b/xbmc/games/addons/input/GameClientJoystick.h @@ -75,10 +75,11 @@ public: // Input accessors const std::string& GetPortAddress() const { return m_portAddress; } - const ControllerPtr& GetController() const { return m_controller; } + ControllerPtr GetController() const { return m_controller; } std::string GetControllerAddress() const; - const PERIPHERALS::PeripheralPtr& GetSource() const { return m_sourcePeripheral; } + PERIPHERALS::PeripheralPtr GetSource() const { return m_sourcePeripheral; } std::string GetSourceLocation() const; + float GetActivation() const; // Input mutators void SetSource(PERIPHERALS::PeripheralPtr sourcePeripheral); diff --git a/xbmc/games/agents/GameAgentManager.cpp b/xbmc/games/agents/GameAgentManager.cpp index 76994aaa28..7ddb0f6140 100644 --- a/xbmc/games/agents/GameAgentManager.cpp +++ b/xbmc/games/agents/GameAgentManager.cpp @@ -133,6 +133,16 @@ bool CGameAgentManager::OnButtonPress(MOUSE::BUTTON_ID button) return false; } +float CGameAgentManager::GetPortActivation(const std::string& portAddress) const +{ + float activation = 0.0f; + + if (m_gameClient) + activation = m_gameClient->Input().GetPortActivation(portAddress); + + return activation; +} + void CGameAgentManager::ProcessJoysticks(PERIPHERALS::EventLockHandlePtr& inputHandlingLock) { // Get system joysticks. diff --git a/xbmc/games/agents/GameAgentManager.h b/xbmc/games/agents/GameAgentManager.h index 901a492e23..621cbe2962 100644 --- a/xbmc/games/agents/GameAgentManager.h +++ b/xbmc/games/agents/GameAgentManager.h @@ -78,6 +78,9 @@ public: bool OnButtonPress(MOUSE::BUTTON_ID button) override; void OnButtonRelease(MOUSE::BUTTON_ID button) override {} + // Public interface + float GetPortActivation(const std::string& address) const; + private: //! @todo De-duplicate these types using PortAddress = std::string; diff --git a/xbmc/games/controllers/guicontrols/GUIGameController.cpp b/xbmc/games/controllers/guicontrols/GUIGameController.cpp index 02d9429094..39c49fcdaa 100644 --- a/xbmc/games/controllers/guicontrols/GUIGameController.cpp +++ b/xbmc/games/controllers/guicontrols/GUIGameController.cpp @@ -12,6 +12,7 @@ #include "ServiceBroker.h" #include "games/GameServices.h" #include "games/addons/input/GameClientTopology.h" +#include "games/agents/GameAgentManager.h" #include "games/controllers/Controller.h" #include "games/controllers/ControllerLayout.h" #include "guilib/GUIListItem.h" @@ -40,6 +41,8 @@ CGUIGameController::CGUIGameController(const CGUIGameController& from) : CGUIImage(from), m_controllerIdInfo(from.m_controllerIdInfo), m_controllerAddressInfo(from.m_controllerAddressInfo), + m_controllerDiffuse(from.m_controllerDiffuse), + m_portAddressInfo(from.m_portAddressInfo), m_currentController(from.m_currentController), m_portAddress(from.m_portAddress) { @@ -52,16 +55,27 @@ CGUIGameController* CGUIGameController::Clone(void) const return new CGUIGameController(*this); } -void CGUIGameController::Render(void) +void CGUIGameController::DoProcess(unsigned int currentTime, CDirtyRegionList& dirtyregions) { - CGUIImage::Render(); + std::string portAddress; - std::lock_guard<std::mutex> lock(m_mutex); - - if (m_currentController) { - //! @todo Render pressed buttons + std::lock_guard<std::mutex> lock(m_mutex); + portAddress = m_portAddress; } + + const GAME::CGameAgentManager& agentManager = + CServiceBroker::GetGameServices().GameAgentManager(); + + // Highlight the controller if it is active + float activation = 0.0f; + + if (!portAddress.empty()) + activation = agentManager.GetPortActivation(portAddress); + + SetActivation(activation); + + CGUIImage::DoProcess(currentTime, dirtyregions); } void CGUIGameController::UpdateInfo(const CGUIListItem* item /* = nullptr */) @@ -79,6 +93,8 @@ void CGUIGameController::UpdateInfo(const CGUIListItem* item /* = nullptr */) if (controllerId.empty()) controllerId = m_controllerIdInfo.GetItemLabel(item); + portAddress = m_portAddressInfo.GetItemLabel(item); + std::string controllerAddress = m_controllerAddressInfo.GetItemLabel(item); if (!controllerAddress.empty()) std::tie(portAddress, controllerId) = CGameClientTopology::SplitAddress(controllerAddress); @@ -126,6 +142,25 @@ void CGUIGameController::SetControllerAddress( } } +void CGUIGameController::SetControllerDiffuse(const GUILIB::GUIINFO::CGUIInfoColor& color) +{ + m_controllerDiffuse = color; +} + +void CGUIGameController::SetPortAddress(const GUILIB::GUIINFO::CGUIInfoLabel& portAddress) +{ + m_portAddressInfo = portAddress; + + // Check if a port address is available without a listitem + static const CFileItem empty; + const std::string strPortAddress = m_portAddressInfo.GetItemLabel(&empty); + if (!strPortAddress.empty()) + { + std::lock_guard<std::mutex> lock(m_mutex); + m_portAddress = strPortAddress; + } +} + void CGUIGameController::ActivateController(const std::string& controllerId) { CGameServices& gameServices = CServiceBroker::GetGameServices(); @@ -157,3 +192,30 @@ std::string CGUIGameController::GetPortAddress() std::lock_guard<std::mutex> lock(m_mutex); return m_portAddress; } + +void CGUIGameController::SetActivation(float activation) +{ + // Validate parameters + if (activation < 0.0f) + activation = 0.0f; + if (activation > 1.0f) + activation = 1.0f; + + // Get diffuse color parts + const uint8_t alpha = (m_controllerDiffuse >> 24) & 0xff; + const uint8_t red = (m_controllerDiffuse >> 16) & 0xff; + const uint8_t green = (m_controllerDiffuse >> 8) & 0xff; + const uint8_t blue = m_controllerDiffuse & 0xff; + + // Merge the diffuse color with white as a portion of the activation + const uint8_t newAlpha = static_cast<uint8_t>(0xff - (0xff - alpha) * activation); + const uint8_t newRed = static_cast<uint8_t>(0xff - (0xff - red) * activation); + const uint8_t newGreen = static_cast<uint8_t>(0xff - (0xff - green) * activation); + const uint8_t newBlue = static_cast<uint8_t>(0xff - (0xff - blue) * activation); + + const UTILS::COLOR::Color activationColor = + (newAlpha << 24) | (newRed << 16) | (newGreen << 8) | newBlue; + + if (CGUIImage::SetColorDiffuse(activationColor)) + CGUIImage::UpdateDiffuseColor(nullptr); +} diff --git a/xbmc/games/controllers/guicontrols/GUIGameController.h b/xbmc/games/controllers/guicontrols/GUIGameController.h index e667d77258..351d97f308 100644 --- a/xbmc/games/controllers/guicontrols/GUIGameController.h +++ b/xbmc/games/controllers/guicontrols/GUIGameController.h @@ -33,12 +33,14 @@ public: // implementation of CGUIControl via CGUIImage CGUIGameController* Clone() const override; - void Render() override; + void DoProcess(unsigned int currentTime, CDirtyRegionList& dirtyregions) override; void UpdateInfo(const CGUIListItem* item = nullptr) override; // GUI functions void SetControllerID(const GUILIB::GUIINFO::CGUIInfoLabel& controllerId); void SetControllerAddress(const GUILIB::GUIINFO::CGUIInfoLabel& controllerAddress); + void SetControllerDiffuse(const GUILIB::GUIINFO::CGUIInfoColor& color); + void SetPortAddress(const GUILIB::GUIINFO::CGUIInfoLabel& portAddress); // Game functions void ActivateController(const std::string& controllerId); @@ -46,9 +48,14 @@ public: std::string GetPortAddress(); private: + // GUI functions + void SetActivation(float activation); + // GUI parameters GUILIB::GUIINFO::CGUIInfoLabel m_controllerIdInfo; GUILIB::GUIINFO::CGUIInfoLabel m_controllerAddressInfo; + GUILIB::GUIINFO::CGUIInfoColor m_controllerDiffuse; + GUILIB::GUIINFO::CGUIInfoLabel m_portAddressInfo; // Game parameters ControllerPtr m_currentController; diff --git a/xbmc/games/controllers/input/CMakeLists.txt b/xbmc/games/controllers/input/CMakeLists.txt index 511fa3751e..1519b11a9e 100644 --- a/xbmc/games/controllers/input/CMakeLists.txt +++ b/xbmc/games/controllers/input/CMakeLists.txt @@ -1,9 +1,11 @@ -set(SOURCES InputSink.cpp +set(SOURCES ControllerActivity.cpp + InputSink.cpp PhysicalFeature.cpp PhysicalTopology.cpp ) -set(HEADERS InputSink.h +set(HEADERS ControllerActivity.h + InputSink.h PhysicalFeature.h PhysicalTopology.h ) diff --git a/xbmc/games/controllers/input/ControllerActivity.cpp b/xbmc/games/controllers/input/ControllerActivity.cpp new file mode 100644 index 0000000000..1ac2b40d20 --- /dev/null +++ b/xbmc/games/controllers/input/ControllerActivity.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 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 "ControllerActivity.h" + +using namespace KODI; +using namespace GAME; + +#include <algorithm> +#include <cstdlib> + +void CControllerActivity::OnButtonPress(bool pressed) +{ + if (pressed) + m_currentActivation = 1.0f; +} + +void CControllerActivity::OnButtonMotion(float magnitude) +{ + m_currentActivation = std::max(magnitude, m_currentActivation); +} + +void CControllerActivity::OnAnalogStickMotion(float x, float y) +{ + m_currentActivation = std::max(std::abs(x), m_currentActivation); + m_currentActivation = std::max(std::abs(y), m_currentActivation); +} + +void CControllerActivity::OnWheelMotion(float position) +{ + m_currentActivation = std::max(std::abs(position), m_currentActivation); +} + +void CControllerActivity::OnThrottleMotion(float position) +{ + m_currentActivation = std::max(std::abs(position), m_currentActivation); +} + +void CControllerActivity::OnInputFrame() +{ + m_lastActivation = m_currentActivation; + m_currentActivation = 0.0f; +} diff --git a/xbmc/games/controllers/input/ControllerActivity.h b/xbmc/games/controllers/input/ControllerActivity.h new file mode 100644 index 0000000000..10627b5cbf --- /dev/null +++ b/xbmc/games/controllers/input/ControllerActivity.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 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 + +namespace KODI +{ +namespace GAME +{ +/*! + * \ingroup games + * + * \brief Class to hold state about the current activity of a controller + * + * The state is held as a single float value, which is updated by the various + * On*() methods. The activity is the maximum value on a single input frame. + * The value is saved to m_lastActivity on each call to OnInputFrame(). + */ +class CControllerActivity +{ +public: + CControllerActivity() = default; + ~CControllerActivity() = default; + + float GetActivation() const { return m_lastActivation; } + + void OnButtonPress(bool pressed); + void OnButtonMotion(float magnitude); + void OnAnalogStickMotion(float x, float y); + void OnWheelMotion(float position); + void OnThrottleMotion(float position); + void OnInputFrame(); + +private: + float m_lastActivation{0.0f}; + float m_currentActivation{0.0f}; +}; +} // namespace GAME +} // namespace KODI diff --git a/xbmc/games/ports/input/PortInput.cpp b/xbmc/games/ports/input/PortInput.cpp index af1652b048..ccfe3093ee 100644 --- a/xbmc/games/ports/input/PortInput.cpp +++ b/xbmc/games/ports/input/PortInput.cpp @@ -9,6 +9,7 @@ #include "PortInput.h" #include "games/addons/GameClient.h" +#include "games/controllers/input/ControllerActivity.h" #include "games/controllers/input/InputSink.h" #include "guilib/WindowIDs.h" #include "input/joysticks/keymaps/KeymapHandling.h" @@ -18,7 +19,9 @@ using namespace KODI; using namespace GAME; CPortInput::CPortInput(JOYSTICK::IInputHandler* gameInput) - : m_gameInput(gameInput), m_inputSink(new CInputSink(gameInput)) + : m_gameInput(gameInput), + m_inputSink(new CInputSink(gameInput)), + m_controllerActivity(std::make_unique<CControllerActivity>()) { } @@ -51,6 +54,11 @@ void CPortInput::UnregisterInput(JOYSTICK::IInputProvider* provider) } } +float CPortInput::GetActivation() const +{ + return m_controllerActivity->GetActivation(); +} + std::string CPortInput::ControllerID() const { return m_gameInput->ControllerID(); @@ -63,6 +71,8 @@ bool CPortInput::AcceptsInput(const std::string& feature) const bool CPortInput::OnButtonPress(const std::string& feature, bool bPressed) { + m_controllerActivity->OnButtonPress(bPressed); + if (bPressed && !m_gameInput->AcceptsInput(feature)) return false; @@ -71,6 +81,8 @@ bool CPortInput::OnButtonPress(const std::string& feature, bool bPressed) void CPortInput::OnButtonHold(const std::string& feature, unsigned int holdTimeMs) { + m_controllerActivity->OnButtonPress(true); + m_gameInput->OnButtonHold(feature, holdTimeMs); } @@ -78,6 +90,8 @@ bool CPortInput::OnButtonMotion(const std::string& feature, float magnitude, unsigned int motionTimeMs) { + m_controllerActivity->OnButtonMotion(magnitude); + if (magnitude > 0.0f && !m_gameInput->AcceptsInput(feature)) return false; @@ -89,6 +103,8 @@ bool CPortInput::OnAnalogStickMotion(const std::string& feature, float y, unsigned int motionTimeMs) { + m_controllerActivity->OnAnalogStickMotion(x, y); + if ((x != 0.0f || y != 0.0f) && !m_gameInput->AcceptsInput(feature)) return false; @@ -107,6 +123,8 @@ bool CPortInput::OnWheelMotion(const std::string& feature, float position, unsigned int motionTimeMs) { + m_controllerActivity->OnWheelMotion(position); + if ((position != 0.0f) && !m_gameInput->AcceptsInput(feature)) return false; @@ -117,12 +135,19 @@ bool CPortInput::OnThrottleMotion(const std::string& feature, float position, unsigned int motionTimeMs) { + m_controllerActivity->OnThrottleMotion(position); + if ((position != 0.0f) && !m_gameInput->AcceptsInput(feature)) return false; return m_gameInput->OnThrottleMotion(feature, position, motionTimeMs); } +void CPortInput::OnInputFrame() +{ + m_controllerActivity->OnInputFrame(); +} + int CPortInput::GetWindowID() const { return WINDOW_FULLSCREEN_GAME; diff --git a/xbmc/games/ports/input/PortInput.h b/xbmc/games/ports/input/PortInput.h index d3c6524cce..56a293449e 100644 --- a/xbmc/games/ports/input/PortInput.h +++ b/xbmc/games/ports/input/PortInput.h @@ -23,6 +23,8 @@ class IInputProvider; namespace GAME { +class CControllerActivity; + class CPortInput : public JOYSTICK::IInputHandler, public IKeymapEnvironment { public: @@ -32,7 +34,8 @@ public: void RegisterInput(JOYSTICK::IInputProvider* provider); void UnregisterInput(JOYSTICK::IInputProvider* provider); - JOYSTICK::IInputHandler* InputHandler() { return m_gameInput; } + // Input parameters + float GetActivation() const; // Implementation of IInputHandler std::string ControllerID() const override; @@ -54,7 +57,7 @@ public: bool OnThrottleMotion(const std::string& feature, float position, unsigned int motionTimeMs) override; - void OnInputFrame() override {} + void OnInputFrame() override; // Implementation of IKeymapEnvironment int GetWindowID() const override; @@ -72,6 +75,9 @@ private: // Prevents input falling through to Kodi when not handled by the game std::unique_ptr<JOYSTICK::IInputHandler> m_inputSink; + + // Records controller activity + std::unique_ptr<CControllerActivity> m_controllerActivity; }; } // namespace GAME } // namespace KODI diff --git a/xbmc/guilib/GUIControlFactory.cpp b/xbmc/guilib/GUIControlFactory.cpp index 3e6b769d73..5d84a45ba4 100644 --- a/xbmc/guilib/GUIControlFactory.cpp +++ b/xbmc/guilib/GUIControlFactory.cpp @@ -1576,6 +1576,16 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl GetInfoLabel(pControlNode, "controlleraddress", controllerAddress, parentID); gcontrol->SetControllerAddress(controllerAddress); + // Set controller diffuse color + GUIINFO::CGUIInfoColor controllerDiffuse(0xFFFFFFFF); + GetInfoColor(pControlNode, "controllerdiffuse", controllerDiffuse, parentID); + gcontrol->SetControllerDiffuse(controllerDiffuse); + + // Set port address + GUIINFO::CGUIInfoLabel portAddress; + GetInfoLabel(pControlNode, "portaddress", portAddress, parentID); + gcontrol->SetPortAddress(portAddress); + break; } case CGUIControl::GUICONTROL_COLORBUTTON: |