diff options
Diffstat (limited to 'xbmc/input/mouse')
-rw-r--r-- | xbmc/input/mouse/CMakeLists.txt | 13 | ||||
-rw-r--r-- | xbmc/input/mouse/MouseStat.cpp | 378 | ||||
-rw-r--r-- | xbmc/input/mouse/MouseStat.h | 227 | ||||
-rw-r--r-- | xbmc/input/mouse/MouseTranslator.cpp | 135 | ||||
-rw-r--r-- | xbmc/input/mouse/MouseTranslator.h | 34 | ||||
-rw-r--r-- | xbmc/input/mouse/MouseTypes.h | 55 | ||||
-rw-r--r-- | xbmc/input/mouse/generic/CMakeLists.txt | 5 | ||||
-rw-r--r-- | xbmc/input/mouse/generic/MouseInputHandling.cpp | 352 | ||||
-rw-r--r-- | xbmc/input/mouse/generic/MouseInputHandling.h | 66 | ||||
-rw-r--r-- | xbmc/input/mouse/interfaces/IMouseDriverHandler.h | 56 | ||||
-rw-r--r-- | xbmc/input/mouse/interfaces/IMouseInputHandler.h | 66 | ||||
-rw-r--r-- | xbmc/input/mouse/interfaces/IMouseInputProvider.h | 43 |
12 files changed, 1430 insertions, 0 deletions
diff --git a/xbmc/input/mouse/CMakeLists.txt b/xbmc/input/mouse/CMakeLists.txt new file mode 100644 index 0000000..214adf0 --- /dev/null +++ b/xbmc/input/mouse/CMakeLists.txt @@ -0,0 +1,13 @@ +set(SOURCES MouseStat.cpp + MouseTranslator.cpp +) + +set(HEADERS interfaces/IMouseDriverHandler.h + interfaces/IMouseInputHandler.h + interfaces/IMouseInputProvider.h + MouseStat.h + MouseTranslator.h + MouseTypes.h +) + +core_add_library(input_mouse) diff --git a/xbmc/input/mouse/MouseStat.cpp b/xbmc/input/mouse/MouseStat.cpp new file mode 100644 index 0000000..8b6ce3b --- /dev/null +++ b/xbmc/input/mouse/MouseStat.cpp @@ -0,0 +1,378 @@ +/* + * 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 "MouseStat.h" + +#include "ServiceBroker.h" +#include "input/Key.h" +#include "utils/TimeUtils.h" +#include "windowing/WinSystem.h" + +#include <algorithm> +#include <cstring> + +CMouseStat::CMouseStat() +{ + SetEnabled(); + m_Key = KEY_MOUSE_NOOP; +} + +CMouseStat::~CMouseStat() = default; + +void CMouseStat::Initialize() +{ + // Set the default resolution (PAL) + SetResolution(720, 576, 1, 1); +} + +void CMouseStat::HandleEvent(const XBMC_Event& newEvent) +{ + // Save the mouse position and the size of the last move + int dx, dy; + if (newEvent.type == XBMC_MOUSEMOTION) + { + dx = newEvent.motion.x - m_mouseState.x; + dy = newEvent.motion.y - m_mouseState.y; + } + else if (newEvent.type == XBMC_MOUSEBUTTONDOWN || newEvent.type == XBMC_MOUSEBUTTONUP) + { + dx = newEvent.button.x - m_mouseState.x; + dy = newEvent.button.y - m_mouseState.y; + } + else + { + return; + } + m_mouseState.dx = dx; + m_mouseState.dy = dy; + m_mouseState.x = std::max(0, std::min(m_maxX, m_mouseState.x + dx)); + m_mouseState.y = std::max(0, std::min(m_maxY, m_mouseState.y + dy)); + + // Fill in the public members + if (newEvent.type == XBMC_MOUSEBUTTONDOWN) + { + if (newEvent.button.button == XBMC_BUTTON_LEFT) + m_mouseState.button[MOUSE_LEFT_BUTTON] = true; + if (newEvent.button.button == XBMC_BUTTON_RIGHT) + m_mouseState.button[MOUSE_RIGHT_BUTTON] = true; + if (newEvent.button.button == XBMC_BUTTON_MIDDLE) + m_mouseState.button[MOUSE_MIDDLE_BUTTON] = true; + if (newEvent.button.button == XBMC_BUTTON_X1) + m_mouseState.button[MOUSE_EXTRA_BUTTON1] = true; + if (newEvent.button.button == XBMC_BUTTON_X2) + m_mouseState.button[MOUSE_EXTRA_BUTTON2] = true; + if (newEvent.button.button == XBMC_BUTTON_X3) + m_mouseState.button[MOUSE_EXTRA_BUTTON3] = true; + if (newEvent.button.button == XBMC_BUTTON_X4) + m_mouseState.button[MOUSE_EXTRA_BUTTON4] = true; + if (newEvent.button.button == XBMC_BUTTON_WHEELUP) + m_mouseState.dz = 1; + if (newEvent.button.button == XBMC_BUTTON_WHEELDOWN) + m_mouseState.dz = -1; + } + else if (newEvent.type == XBMC_MOUSEBUTTONUP) + { + if (newEvent.button.button == XBMC_BUTTON_LEFT) + m_mouseState.button[MOUSE_LEFT_BUTTON] = false; + if (newEvent.button.button == XBMC_BUTTON_RIGHT) + m_mouseState.button[MOUSE_RIGHT_BUTTON] = false; + if (newEvent.button.button == XBMC_BUTTON_MIDDLE) + m_mouseState.button[MOUSE_MIDDLE_BUTTON] = false; + if (newEvent.button.button == XBMC_BUTTON_X1) + m_mouseState.button[MOUSE_EXTRA_BUTTON1] = false; + if (newEvent.button.button == XBMC_BUTTON_X2) + m_mouseState.button[MOUSE_EXTRA_BUTTON2] = false; + if (newEvent.button.button == XBMC_BUTTON_X3) + m_mouseState.button[MOUSE_EXTRA_BUTTON3] = false; + if (newEvent.button.button == XBMC_BUTTON_X4) + m_mouseState.button[MOUSE_EXTRA_BUTTON4] = false; + if (newEvent.button.button == XBMC_BUTTON_WHEELUP) + m_mouseState.dz = 0; + if (newEvent.button.button == XBMC_BUTTON_WHEELDOWN) + m_mouseState.dz = 0; + } + + // Now check the current message and the previous state to find out if + // this is a click, doubleclick, drag etc + uint32_t now = CTimeUtils::GetFrameTime(); + bool bNothingDown = true; + + for (int i = 0; i < MOUSE_MAX_BUTTON; i++) + { + bClick[i] = false; + bLongClick[i] = false; + bDoubleClick[i] = false; + m_hold[i] = HoldAction::NONE; + + // CButtonState::Update does the hard work of checking the button state + // and spotting drags, doubleclicks etc + CButtonState::BUTTON_ACTION action = + m_buttonState[i].Update(now, m_mouseState.x, m_mouseState.y, m_mouseState.button[i]); + switch (action) + { + case CButtonState::MB_SHORT_CLICK: + bClick[i] = true; + bNothingDown = false; + break; + case CButtonState::MB_LONG_CLICK: + bLongClick[i] = true; + bNothingDown = false; + break; + case CButtonState::MB_DOUBLE_CLICK: + bDoubleClick[i] = true; + bNothingDown = false; + break; + case CButtonState::MB_DRAG_START: + m_hold[i] = HoldAction::DRAG_START; + bNothingDown = false; + break; + case CButtonState::MB_DRAG: + m_hold[i] = HoldAction::DRAG; + bNothingDown = false; + break; + case CButtonState::MB_DRAG_END: + m_hold[i] = HoldAction::DRAG_END; + bNothingDown = false; + break; + default: + break; + } + } + + // Now work out what action ID to send to XBMC. + + // ignore any mouse messages by default + m_Key = KEY_MOUSE_NOOP; + + for (int button = 0; button < MOUSE_MAX_BUTTON; ++button) + { + // The bClick array is set true if CButtonState::Update spots a click + // i.e. a button down followed by a button up. + if (bClick[button]) + m_Key = KEY_MOUSE_CLICK + button; + // The bDoubleClick array is set true if CButtonState::Update spots a + // button down within double_click_time (500ms) of the last click + else if (bDoubleClick[button]) + m_Key = KEY_MOUSE_DOUBLE_CLICK + button; + else if (bLongClick[button]) + m_Key = KEY_MOUSE_LONG_CLICK + button; + + if (m_Key != KEY_MOUSE_NOOP) + break; + } + + if (m_Key == KEY_MOUSE_NOOP) + { + // The m_hold array is set to the drag action + if (m_hold[MOUSE_LEFT_BUTTON] != HoldAction::NONE) + { + switch (m_hold[MOUSE_LEFT_BUTTON]) + { + case HoldAction::DRAG: + m_Key = KEY_MOUSE_DRAG; + break; + case HoldAction::DRAG_START: + m_Key = KEY_MOUSE_DRAG_START; + break; + case HoldAction::DRAG_END: + m_Key = KEY_MOUSE_DRAG_END; + break; + default: + break; + } + } + else if (m_hold[MOUSE_RIGHT_BUTTON] != HoldAction::NONE) + { + switch (m_hold[MOUSE_RIGHT_BUTTON]) + { + case HoldAction::DRAG: + m_Key = KEY_MOUSE_RDRAG; + break; + case HoldAction::DRAG_START: + m_Key = KEY_MOUSE_RDRAG_START; + break; + case HoldAction::DRAG_END: + m_Key = KEY_MOUSE_RDRAG_END; + break; + default: + break; + } + } + + // dz is +1 on wheel up and -1 on wheel down + else if (m_mouseState.dz > 0) + m_Key = KEY_MOUSE_WHEEL_UP; + else if (m_mouseState.dz < 0) + m_Key = KEY_MOUSE_WHEEL_DOWN; + + // Check for a mouse move that isn't a drag, ignoring messages with no movement at all + else if (newEvent.type == XBMC_MOUSEMOTION && (m_mouseState.dx || m_mouseState.dy)) + m_Key = KEY_MOUSE_MOVE; + } + + // activate the mouse pointer if we have an action or the mouse has moved far enough + if ((MovedPastThreshold() && m_Key == KEY_MOUSE_MOVE) || + (m_Key != KEY_MOUSE_NOOP && m_Key != KEY_MOUSE_MOVE)) + SetActive(); + + // reset the mouse state if nothing is held down + if (bNothingDown) + SetState(MOUSE_STATE_NORMAL); +} + +void CMouseStat::SetResolution(int maxX, int maxY, float speedX, float speedY) +{ + m_maxX = maxX; + m_maxY = maxY; + + // speed is currently unused + m_speedX = speedX; + m_speedY = speedY; +} + +void CMouseStat::SetActive(bool active /*=true*/) +{ + m_lastActiveTime = CTimeUtils::GetFrameTime(); + m_mouseState.active = active; + // we show the OS mouse if: + // 1. The mouse is active (it has been moved) AND + // 2. The XBMC mouse is disabled in settings AND + // 3. XBMC is not in fullscreen. + CWinSystemBase* winSystem = CServiceBroker::GetWinSystem(); + if (winSystem) + winSystem->ShowOSMouse(m_mouseState.active && !IsEnabled() && + !CServiceBroker::GetWinSystem()->IsFullScreen()); +} + +// IsActive - returns true if we have been active in the last MOUSE_ACTIVE_LENGTH period +bool CMouseStat::IsActive() +{ + if (m_mouseState.active && (CTimeUtils::GetFrameTime() - m_lastActiveTime > MOUSE_ACTIVE_LENGTH)) + SetActive(false); + return (m_mouseState.active && IsEnabled()); +} + +void CMouseStat::SetEnabled(bool enabled) +{ + m_mouseEnabled = enabled; + SetActive(enabled); +} + +// IsEnabled - returns true if mouse is enabled +bool CMouseStat::IsEnabled() const +{ + return m_mouseEnabled; +} + +bool CMouseStat::MovedPastThreshold() const +{ + return (m_mouseState.dx * m_mouseState.dx + m_mouseState.dy * m_mouseState.dy >= + MOUSE_MINIMUM_MOVEMENT * MOUSE_MINIMUM_MOVEMENT); +} + +uint32_t CMouseStat::GetKey() const +{ + return m_Key; +} + +HoldAction CMouseStat::GetHold(int ButtonID) const +{ + switch (ButtonID) + { + case MOUSE_LEFT_BUTTON: + return m_hold[MOUSE_LEFT_BUTTON]; + } + return HoldAction::NONE; +} + +CMouseStat::CButtonState::CButtonState() +{ + m_state = STATE_RELEASED; + m_time = 0; + m_x = 0; + m_y = 0; +} + +bool CMouseStat::CButtonState::InClickRange(int x, int y) const +{ + int dx = x - m_x; + int dy = y - m_y; + return (unsigned int)(dx * dx + dy * dy) <= click_confines * click_confines; +} + +CMouseStat::CButtonState::BUTTON_ACTION CMouseStat::CButtonState::Update(unsigned int time, + int x, + int y, + bool down) +{ + if (m_state == STATE_IN_DRAG) + { + if (down) + return MB_DRAG; + m_state = STATE_RELEASED; + return MB_DRAG_END; + } + else if (m_state == STATE_RELEASED) + { + if (down) + { + m_state = STATE_IN_CLICK; + m_time = time; + m_x = x; + m_y = y; + } + } + else if (m_state == STATE_IN_CLICK) + { + if (down) + { + if (!InClickRange(x, y)) + { // beginning a drag + m_state = STATE_IN_DRAG; + return MB_DRAG_START; + } + } + else + { // button up + if (time - m_time < short_click_time) + { // single click + m_state = STATE_IN_DOUBLE_CLICK; + m_time = time; // double click time and positioning is measured from the + m_x = x; // end of a single click + m_y = y; + return MB_SHORT_CLICK; + } + else + { // long click + m_state = STATE_RELEASED; + return MB_LONG_CLICK; + } + } + } + else if (m_state == STATE_IN_DOUBLE_CLICK) + { + if (time - m_time > double_click_time || !InClickRange(x, y)) + { // too long, or moved to much - reset to released state and re-update, as we may be starting a + // new click + m_state = STATE_RELEASED; + return Update(time, x, y, down); + } + if (down) + { + m_state = STATE_IN_DOUBLE_IGNORE; + return MB_DOUBLE_CLICK; + } + } + else if (m_state == STATE_IN_DOUBLE_IGNORE) + { + if (!down) + m_state = STATE_RELEASED; + } + + return MB_NONE; +} diff --git a/xbmc/input/mouse/MouseStat.h b/xbmc/input/mouse/MouseStat.h new file mode 100644 index 0000000..354d1c3 --- /dev/null +++ b/xbmc/input/mouse/MouseStat.h @@ -0,0 +1,227 @@ +/* + * 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 "windowing/XBMC_events.h" + +#define XBMC_BUTTON(X) (1 << ((X)-1)) +#define XBMC_BUTTON_LEFT 1 +#define XBMC_BUTTON_MIDDLE 2 +#define XBMC_BUTTON_RIGHT 3 +#define XBMC_BUTTON_WHEELUP 4 +#define XBMC_BUTTON_WHEELDOWN 5 +#define XBMC_BUTTON_X1 6 +#define XBMC_BUTTON_X2 7 +#define XBMC_BUTTON_X3 8 +#define XBMC_BUTTON_X4 9 +#define XBMC_BUTTON_LMASK XBMC_BUTTON(XBMC_BUTTON_LEFT) +#define XBMC_BUTTON_MMASK XBMC_BUTTON(XBMC_BUTTON_MIDDLE) +#define XBMC_BUTTON_RMASK XBMC_BUTTON(XBMC_BUTTON_RIGHT) +#define XBMC_BUTTON_X1MASK XBMC_BUTTON(XBMC_BUTTON_X1) +#define XBMC_BUTTON_X2MASK XBMC_BUTTON(XBMC_BUTTON_X2) +#define XBMC_BUTTON_X3MASK XBMC_BUTTON(XBMC_BUTTON_X3) +#define XBMC_BUTTON_X4MASK XBMC_BUTTON(XBMC_BUTTON_X4) + +#define MOUSE_MINIMUM_MOVEMENT 2 +#define MOUSE_DOUBLE_CLICK_LENGTH 500L +#define MOUSE_ACTIVE_LENGTH 5000L + +#define MOUSE_MAX_BUTTON 7 + +enum MOUSE_STATE +{ + /*! Normal state */ + MOUSE_STATE_NORMAL = 1, + /*! Control below the mouse is currently in focus */ + MOUSE_STATE_FOCUS, + /*! A drag operation is being performed */ + MOUSE_STATE_DRAG, + /*! A mousebutton is being clicked */ + MOUSE_STATE_CLICK +}; + +enum MOUSE_BUTTON +{ + MOUSE_LEFT_BUTTON = 0, + MOUSE_RIGHT_BUTTON, + MOUSE_MIDDLE_BUTTON, + MOUSE_EXTRA_BUTTON1, + MOUSE_EXTRA_BUTTON2, + MOUSE_EXTRA_BUTTON3, + MOUSE_EXTRA_BUTTON4 +}; + +enum class HoldAction +{ + /*! No action should occur */ + NONE, + /*! A drag action has started */ + DRAG_START, + /*! A drag action is in progress */ + DRAG, + /*! A drag action has finished */ + DRAG_END +}; + +//! Holds everything we know about the current state of the mouse +struct MouseState +{ + /*! X location */ + int x; + /*! Y location */ + int y; + /*! Change in x */ + int16_t dx; + /*! Change in y */ + int16_t dy; + /*! Change in z (wheel) */ + int8_t dz; + /*! Current state of the buttons */ + bool button[MOUSE_MAX_BUTTON]; + /*! True if the mouse is active */ + bool active; +}; + +struct MousePosition +{ + int x; + int y; +}; + +class CAction; + +class CMouseStat +{ +public: + CMouseStat(); + virtual ~CMouseStat(); + + void Initialize(); + void HandleEvent(const XBMC_Event& newEvent); + void SetResolution(int maxX, int maxY, float speedX, float speedY); + bool IsActive(); + bool IsEnabled() const; + + void SetActive(bool active = true); + void SetState(MOUSE_STATE state) { m_pointerState = state; } + void SetEnabled(bool enabled = true); + MOUSE_STATE GetState() const { return m_pointerState; } + uint32_t GetKey() const; + + HoldAction GetHold(int ButtonID) const; + inline int GetX(void) const { return m_mouseState.x; } + inline int GetY(void) const { return m_mouseState.y; } + inline int GetDX(void) const { return m_mouseState.dx; } + inline int GetDY(void) const { return m_mouseState.dy; } + MousePosition GetPosition() { return MousePosition{m_mouseState.x, m_mouseState.y}; } + +private: + /*! \brief Holds information regarding a particular mouse button state + + The CButtonState class is used to track where in a button event the mouse currently is. + There is effectively 5 BUTTON_STATE's available, and transitioning between those states + is handled by the Update() function. + + The actions we detect are: + * short clicks - down/up press of the mouse within short_click_time ms, where the pointer stays + within click_confines pixels + * long clicks - down/up press of the mouse greater than short_click_time ms, where the pointers + stays within click_confines pixels + * double clicks - a further down press of the mouse within double_click_time of the up press of + a short click, where the pointer stays within click_confines pixels + * drag - the mouse is down and has been moved more than click_confines pixels + + \sa CMouseStat + */ + class CButtonState + { + public: + /*! \brief enum for the actions to perform as a result of an Update function + */ + enum BUTTON_ACTION + { + MB_NONE = 0, ///< no action should occur + MB_SHORT_CLICK, ///< a short click has occurred (a double click may be in process) + MB_LONG_CLICK, ///< a long click has occurred + MB_DOUBLE_CLICK, ///< a double click has occurred + MB_DRAG_START, ///< a drag action has started + MB_DRAG, ///< a drag action is in progress + MB_DRAG_END + }; ///< a drag action has finished + + CButtonState(); + + /*! \brief Update the button state, with where the mouse is, and whether the button is down or + not + + \param time frame time in ms + \param x horizontal coordinate of the mouse + \param y vertical coordinate of the mouse + \param down true if the button is down + \return action that should be performed + */ + BUTTON_ACTION Update(unsigned int time, int x, int y, bool down); + + private: + //! number of pixels that the pointer may move while the button is down to + //! trigger a click + static const unsigned int click_confines = 5; + + //! Time for mouse down/up to trigger a short click rather than a long click + static const unsigned int short_click_time = 1000; + + //! Time for mouse down following a short click to trigger a double click + static const unsigned int double_click_time = 500; + + bool InClickRange(int x, int y) const; + + enum BUTTON_STATE + { + STATE_RELEASED = 0, ///< mouse button is released, no events pending + STATE_IN_CLICK, ///< mouse button is down, a click is pending + STATE_IN_DOUBLE_CLICK, ///< mouse button is released, pending double click + STATE_IN_DOUBLE_IGNORE, ///< mouse button is down following double click + STATE_IN_DRAG + }; ///< mouse button is down during a drag + + BUTTON_STATE m_state; + unsigned int m_time; + int m_x; + int m_y; + }; + + /*! \brief detect whether the mouse has moved + + Uses a trigger threshold of 2 pixels to detect mouse movement + + \return whether the mouse has moved past the trigger threshold. + */ + bool MovedPastThreshold() const; + + // state of the mouse + MOUSE_STATE m_pointerState{MOUSE_STATE_NORMAL}; + MouseState m_mouseState{}; + bool m_mouseEnabled; + CButtonState m_buttonState[MOUSE_MAX_BUTTON]; + + int m_maxX{0}; + int m_maxY{0}; + float m_speedX{0.0f}; + float m_speedY{0.0f}; + + // active/click timers + unsigned int m_lastActiveTime; + + bool bClick[MOUSE_MAX_BUTTON]{}; + bool bDoubleClick[MOUSE_MAX_BUTTON]{}; + HoldAction m_hold[MOUSE_MAX_BUTTON]{}; + bool bLongClick[MOUSE_MAX_BUTTON]{}; + + uint32_t m_Key; +}; diff --git a/xbmc/input/mouse/MouseTranslator.cpp b/xbmc/input/mouse/MouseTranslator.cpp new file mode 100644 index 0000000..b5cac7d --- /dev/null +++ b/xbmc/input/mouse/MouseTranslator.cpp @@ -0,0 +1,135 @@ +/* + * 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 "MouseTranslator.h" + +#include "MouseStat.h" +#include "input/Key.h" +#include "utils/StringUtils.h" +#include "utils/XBMCTinyXML.h" +#include "utils/log.h" + +#include <map> +#include <string> + +using namespace KODI; +using namespace MOUSE; + +namespace +{ + +using ActionName = std::string; +using KeyID = uint32_t; + +static const std::map<ActionName, KeyID> MouseKeys = {{"click", KEY_MOUSE_CLICK}, + {"leftclick", KEY_MOUSE_CLICK}, + {"rightclick", KEY_MOUSE_RIGHTCLICK}, + {"middleclick", KEY_MOUSE_MIDDLECLICK}, + {"doubleclick", KEY_MOUSE_DOUBLE_CLICK}, + {"longclick", KEY_MOUSE_LONG_CLICK}, + {"wheelup", KEY_MOUSE_WHEEL_UP}, + {"wheeldown", KEY_MOUSE_WHEEL_DOWN}, + {"mousemove", KEY_MOUSE_MOVE}, + {"mousedrag", KEY_MOUSE_DRAG}, + {"mousedragstart", KEY_MOUSE_DRAG_START}, + {"mousedragend", KEY_MOUSE_DRAG_END}, + {"mouserdrag", KEY_MOUSE_RDRAG}, + {"mouserdragstart", KEY_MOUSE_RDRAG_START}, + {"mouserdragend", KEY_MOUSE_RDRAG_END}}; + +} // anonymous namespace + +uint32_t CMouseTranslator::TranslateCommand(const TiXmlElement* pButton) +{ + uint32_t buttonId = 0; + + if (pButton != nullptr) + { + std::string szKey = pButton->ValueStr(); + if (!szKey.empty()) + { + StringUtils::ToLower(szKey); + + auto it = MouseKeys.find(szKey); + if (it != MouseKeys.end()) + buttonId = it->second; + + if (buttonId == 0) + { + CLog::Log(LOGERROR, "Unknown mouse action ({}), skipping", pButton->Value()); + } + else + { + int id = 0; + if ((pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS)) + { + if (0 <= id && id < MOUSE_MAX_BUTTON) + buttonId += id; + } + } + } + } + + return buttonId; +} + +bool CMouseTranslator::TranslateEventID(unsigned int eventId, BUTTON_ID& buttonId) +{ + switch (eventId) + { + case XBMC_BUTTON_LEFT: + { + buttonId = BUTTON_ID::LEFT; + return true; + } + case XBMC_BUTTON_MIDDLE: + { + buttonId = BUTTON_ID::MIDDLE; + return true; + } + case XBMC_BUTTON_RIGHT: + { + buttonId = BUTTON_ID::RIGHT; + return true; + } + case XBMC_BUTTON_WHEELUP: + { + buttonId = BUTTON_ID::WHEEL_UP; + return true; + } + case XBMC_BUTTON_WHEELDOWN: + { + buttonId = BUTTON_ID::WHEEL_DOWN; + return true; + } + case XBMC_BUTTON_X1: + { + buttonId = BUTTON_ID::BUTTON4; + return true; + } + case XBMC_BUTTON_X2: + { + buttonId = BUTTON_ID::BUTTON5; + return true; + } + case XBMC_BUTTON_X3: + { + buttonId = BUTTON_ID::HORIZ_WHEEL_LEFT; + return true; + } + case XBMC_BUTTON_X4: + { + buttonId = BUTTON_ID::HORIZ_WHEEL_RIGHT; + return true; + } + default: + break; + } + + return false; +} diff --git a/xbmc/input/mouse/MouseTranslator.h b/xbmc/input/mouse/MouseTranslator.h new file mode 100644 index 0000000..e3d4d4b --- /dev/null +++ b/xbmc/input/mouse/MouseTranslator.h @@ -0,0 +1,34 @@ +/* + * 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 "MouseTypes.h" + +#include <stdint.h> + +class TiXmlElement; + +class CMouseTranslator +{ +public: + /*! + * \brief Translate a keymap element to a key ID + */ + static uint32_t TranslateCommand(const TiXmlElement* pButton); + + /*! + * \brief Translate a mouse event ID to a mouse button index + * + * \param eventId The event ID from MouseStat.h + * \param[out] buttonId The button ID from MouseTypes.h, or unmodified if unsuccessful + * + * \return True if successful, false otherwise + */ + static bool TranslateEventID(unsigned int eventId, KODI::MOUSE::BUTTON_ID& buttonId); +}; diff --git a/xbmc/input/mouse/MouseTypes.h b/xbmc/input/mouse/MouseTypes.h new file mode 100644 index 0000000..f11f7ea --- /dev/null +++ b/xbmc/input/mouse/MouseTypes.h @@ -0,0 +1,55 @@ +/* + * 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 "input/InputTypes.h" + +#include <string> + +namespace KODI +{ +namespace MOUSE +{ +/*! + * \brief Buttons on a mouse + */ +enum class BUTTON_ID +{ + UNKNOWN, + LEFT, + RIGHT, + MIDDLE, + BUTTON4, + BUTTON5, + WHEEL_UP, + WHEEL_DOWN, + HORIZ_WHEEL_LEFT, + HORIZ_WHEEL_RIGHT, +}; + +/*! + * \brief Name of a mouse button + * + * Names are defined in the mouse's controller profile. + */ +using ButtonName = std::string; + +/*! + * \brief Directions of motion for a mouse pointer + */ +using POINTER_DIRECTION = INPUT::CARDINAL_DIRECTION; + +/*! + * \brief Name of the mouse pointer + * + * Names are defined in the mouse's controller profile. + */ +using PointerName = std::string; +} // namespace MOUSE +} // namespace KODI diff --git a/xbmc/input/mouse/generic/CMakeLists.txt b/xbmc/input/mouse/generic/CMakeLists.txt new file mode 100644 index 0000000..b5c2858 --- /dev/null +++ b/xbmc/input/mouse/generic/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SOURCES MouseInputHandling.cpp) + +set(HEADERS MouseInputHandling.h) + +core_add_library(input_mouse_generic) diff --git a/xbmc/input/mouse/generic/MouseInputHandling.cpp b/xbmc/input/mouse/generic/MouseInputHandling.cpp new file mode 100644 index 0000000..0f51ede --- /dev/null +++ b/xbmc/input/mouse/generic/MouseInputHandling.cpp @@ -0,0 +1,352 @@ +/* + * 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 "MouseInputHandling.h" + +#include "input/InputTranslator.h" +#include "input/joysticks/interfaces/IButtonMap.h" +#include "input/mouse/interfaces/IMouseInputHandler.h" + +using namespace KODI; +using namespace MOUSE; + +CMouseInputHandling::CMouseInputHandling(IMouseInputHandler* handler, + JOYSTICK::IButtonMap* buttonMap) + : m_handler(handler), m_buttonMap(buttonMap) +{ +} + +bool CMouseInputHandling::OnPosition(int x, int y) +{ + using namespace JOYSTICK; + + if (!m_bHasPosition) + { + m_bHasPosition = true; + m_x = x; + m_y = y; + return true; + } + + int dx = x - m_x; + int dy = y - m_y; + + bool bHandled = false; + + // Get direction of motion + POINTER_DIRECTION dir = GetPointerDirection(dx, dy); + + CDriverPrimitive source(dir); + if (source.IsValid()) + { + // Get pointer in direction of motion + PointerName pointerName; + if (m_buttonMap->GetFeature(source, pointerName)) + { + // Get orthogonal direction of motion + POINTER_DIRECTION dirCCW = GetOrthogonalDirectionCCW(dir); + + // Get mapped directions of motion for rotation and reflection + CDriverPrimitive target; + CDriverPrimitive targetCCW; + + if (m_buttonMap->GetRelativePointer(pointerName, dir, target)) + m_buttonMap->GetRelativePointer(pointerName, dirCCW, targetCCW); + + if (target.IsValid()) + { + // Invert y to right-handed cartesian system + dy *= -1; + + // Perform rotation + int rotation[2][2] = {{1, 0}, {0, 1}}; + + GetRotation(dir, target.PointerDirection(), rotation); + + dx = rotation[0][0] * dx + rotation[0][1] * dy; + dy = rotation[1][0] * dx + rotation[1][1] * dy; + + if (targetCCW.IsValid()) + { + // Perform reflection + int reflection[2][2] = {{1, 0}, {0, 1}}; + + GetReflectionCCW(target.PointerDirection(), targetCCW.PointerDirection(), reflection); + + dx = reflection[0][0] * dx + reflection[0][1] * dy; + dy = reflection[1][0] * dx + reflection[1][1] * dy; + } + + // Invert y back to left-handed coordinate system + dy *= -1; + } + + bHandled = m_handler->OnMotion(pointerName, dx, dy); + } + } + else + { + // Don't fall through - might disrupt the game + bHandled = true; + } + + m_x = x; + m_y = y; + + return bHandled; +} + +bool CMouseInputHandling::OnButtonPress(BUTTON_ID button) +{ + bool bHandled = false; + + JOYSTICK::CDriverPrimitive source(button); + + ButtonName buttonName; + if (m_buttonMap->GetFeature(source, buttonName)) + bHandled = m_handler->OnButtonPress(buttonName); + + return bHandled; +} + +void CMouseInputHandling::OnButtonRelease(BUTTON_ID button) +{ + JOYSTICK::CDriverPrimitive source(button); + + ButtonName buttonName; + if (m_buttonMap->GetFeature(source, buttonName)) + m_handler->OnButtonRelease(buttonName); +} + +POINTER_DIRECTION CMouseInputHandling::GetPointerDirection(int x, int y) +{ + using namespace INPUT; + + return CInputTranslator::VectorToCardinalDirection(static_cast<float>(x), static_cast<float>(-y)); +} + +POINTER_DIRECTION CMouseInputHandling::GetOrthogonalDirectionCCW(POINTER_DIRECTION direction) +{ + switch (direction) + { + case POINTER_DIRECTION::RIGHT: + return POINTER_DIRECTION::UP; + case POINTER_DIRECTION::UP: + return POINTER_DIRECTION::LEFT; + case POINTER_DIRECTION::LEFT: + return POINTER_DIRECTION::DOWN; + case POINTER_DIRECTION::DOWN: + return POINTER_DIRECTION::RIGHT; + default: + break; + } + + return POINTER_DIRECTION::NONE; +} + +void CMouseInputHandling::GetRotation(POINTER_DIRECTION source, + POINTER_DIRECTION target, + int (&rotation)[2][2]) +{ + switch (source) + { + case POINTER_DIRECTION::RIGHT: + { + switch (target) + { + case POINTER_DIRECTION::UP: + GetRotation(90, rotation); + break; + case POINTER_DIRECTION::LEFT: + GetRotation(180, rotation); + break; + case POINTER_DIRECTION::DOWN: + GetRotation(270, rotation); + break; + default: + break; + } + break; + } + case POINTER_DIRECTION::UP: + { + switch (target) + { + case POINTER_DIRECTION::LEFT: + GetRotation(90, rotation); + break; + case POINTER_DIRECTION::DOWN: + GetRotation(180, rotation); + break; + case POINTER_DIRECTION::RIGHT: + GetRotation(270, rotation); + break; + default: + break; + } + break; + } + case POINTER_DIRECTION::LEFT: + { + switch (target) + { + case POINTER_DIRECTION::DOWN: + GetRotation(90, rotation); + break; + case POINTER_DIRECTION::RIGHT: + GetRotation(180, rotation); + break; + case POINTER_DIRECTION::UP: + GetRotation(270, rotation); + break; + default: + break; + } + break; + } + case POINTER_DIRECTION::DOWN: + { + switch (target) + { + case POINTER_DIRECTION::RIGHT: + GetRotation(90, rotation); + break; + case POINTER_DIRECTION::UP: + GetRotation(180, rotation); + break; + case POINTER_DIRECTION::LEFT: + GetRotation(270, rotation); + break; + default: + break; + } + break; + } + default: + break; + } +} + +void CMouseInputHandling::GetRotation(int deg, int (&rotation)[2][2]) +{ + switch (deg) + { + case 90: + { + rotation[0][0] = 0; + rotation[0][1] = -1; + rotation[1][0] = 1; + rotation[1][1] = 0; + break; + } + case 180: + { + rotation[0][0] = -1; + rotation[0][1] = 0; + rotation[1][0] = 0; + rotation[1][1] = -1; + break; + } + case 270: + { + rotation[0][0] = 0; + rotation[0][1] = 1; + rotation[1][0] = -1; + rotation[1][1] = 0; + break; + } + default: + break; + } +} + +void CMouseInputHandling::GetReflectionCCW(POINTER_DIRECTION source, + POINTER_DIRECTION target, + int (&rotation)[2][2]) +{ + switch (source) + { + case POINTER_DIRECTION::RIGHT: + { + switch (target) + { + case POINTER_DIRECTION::DOWN: + GetReflection(0, rotation); + break; + default: + break; + } + break; + } + case POINTER_DIRECTION::UP: + { + switch (target) + { + case POINTER_DIRECTION::RIGHT: + GetReflection(90, rotation); + break; + default: + break; + } + break; + } + case POINTER_DIRECTION::LEFT: + { + switch (target) + { + case POINTER_DIRECTION::UP: + GetReflection(180, rotation); + break; + default: + break; + } + break; + } + case POINTER_DIRECTION::DOWN: + { + switch (target) + { + case POINTER_DIRECTION::LEFT: + GetReflection(270, rotation); + break; + default: + break; + } + break; + } + default: + break; + } +} + +void CMouseInputHandling::GetReflection(int deg, int (&reflection)[2][2]) +{ + switch (deg) + { + case 0: + case 180: + { + reflection[0][0] = 1; + reflection[0][1] = 0; + reflection[1][0] = 0; + reflection[1][1] = -1; + break; + } + case 90: + case 270: + { + reflection[0][0] = -1; + reflection[0][1] = 0; + reflection[1][0] = 0; + reflection[1][1] = 1; + break; + } + default: + break; + } +} diff --git a/xbmc/input/mouse/generic/MouseInputHandling.h b/xbmc/input/mouse/generic/MouseInputHandling.h new file mode 100644 index 0000000..6d19da9 --- /dev/null +++ b/xbmc/input/mouse/generic/MouseInputHandling.h @@ -0,0 +1,66 @@ +/* + * 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/mouse/MouseTypes.h" +#include "input/mouse/interfaces/IMouseDriverHandler.h" + +namespace KODI +{ +namespace JOYSTICK +{ +class IButtonMap; +} + +namespace MOUSE +{ +class IMouseInputHandler; + +/*! + * \ingroup mouse + * \brief Class to translate input from driver info to higher-level features + */ +class CMouseInputHandling : public IMouseDriverHandler +{ +public: + CMouseInputHandling(IMouseInputHandler* handler, JOYSTICK::IButtonMap* buttonMap); + + ~CMouseInputHandling(void) override = default; + + // implementation of IMouseDriverHandler + bool OnPosition(int x, int y) override; + bool OnButtonPress(BUTTON_ID button) override; + void OnButtonRelease(BUTTON_ID button) override; + +private: + // Utility functions + static POINTER_DIRECTION GetPointerDirection(int x, int y); + static POINTER_DIRECTION GetOrthogonalDirectionCCW(POINTER_DIRECTION direction); + + static void GetRotation(POINTER_DIRECTION source, + POINTER_DIRECTION target, + int (&rotation)[2][2]); + static void GetRotation(int deg, int (&rotation)[2][2]); + + static void GetReflectionCCW(POINTER_DIRECTION source, + POINTER_DIRECTION target, + int (&reflection)[2][2]); + static void GetReflection(int deg, int (&reflection)[2][2]); + + // Construction parameters + IMouseInputHandler* const m_handler; + JOYSTICK::IButtonMap* const m_buttonMap; + + // Mouse parameters + bool m_bHasPosition = false; + int m_x = 0; + int m_y = 0; +}; +} // namespace MOUSE +} // namespace KODI diff --git a/xbmc/input/mouse/interfaces/IMouseDriverHandler.h b/xbmc/input/mouse/interfaces/IMouseDriverHandler.h new file mode 100644 index 0000000..7989e39 --- /dev/null +++ b/xbmc/input/mouse/interfaces/IMouseDriverHandler.h @@ -0,0 +1,56 @@ +/* + * 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/mouse/MouseTypes.h" + +namespace KODI +{ +namespace MOUSE +{ +/*! + * \ingroup mouse + * \brief Interface for handling mouse driver events + */ +class IMouseDriverHandler +{ +public: + virtual ~IMouseDriverHandler(void) = default; + + /*! + * \brief Handle mouse position updates + * + * \param x The new x coordinate of the pointer + * \param y The new y coordinate of the pointer + * + * The mouse uses a left-handed (graphics) cartesian coordinate system. + * Positive X is right, positive Y is down. + * + * \return True if the event was handled, false otherwise + */ + virtual bool OnPosition(int x, int y) = 0; + + /*! + * \brief A mouse button has been pressed + * + * \param button The index of the pressed button + * + * \return True if the event was handled, otherwise false + */ + virtual bool OnButtonPress(BUTTON_ID button) = 0; + + /*! + * \brief A mouse button has been released + * + * \param button The index of the released button + */ + virtual void OnButtonRelease(BUTTON_ID button) = 0; +}; +} // namespace MOUSE +} // namespace KODI diff --git a/xbmc/input/mouse/interfaces/IMouseInputHandler.h b/xbmc/input/mouse/interfaces/IMouseInputHandler.h new file mode 100644 index 0000000..910ddd4 --- /dev/null +++ b/xbmc/input/mouse/interfaces/IMouseInputHandler.h @@ -0,0 +1,66 @@ +/* + * 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/mouse/MouseTypes.h" + +#include <string> + +namespace KODI +{ +namespace MOUSE +{ +/*! + * \ingroup mouse + * \brief Interface for handling mouse events + */ +class IMouseInputHandler +{ +public: + virtual ~IMouseInputHandler(void) = default; + + /*! + * \brief The controller profile for this mouse input handler + * + * \return The ID of the add-on extending kodi.game.controller + */ + virtual std::string ControllerID(void) const = 0; + + /*! + * \brief A relative pointer has moved + * + * \param relpointer The name of the relative pointer being moved + * \param dx The relative x coordinate of motion + * \param dy The relative y coordinate of motion + * + * The mouse uses a left-handed (graphics) cartesian coordinate system. + * Positive X is right, positive Y is down. + * + * \return True if the event was handled, otherwise false + */ + virtual bool OnMotion(const PointerName& relpointer, int dx, int dy) = 0; + + /*! + * \brief A mouse button has been pressed + * + * \param button The name of the feature being pressed + * + * \return True if the event was handled, otherwise false + */ + virtual bool OnButtonPress(const ButtonName& button) = 0; + + /*! + * \brief A mouse button has been released + * + * \param button The name of the feature being released + */ + virtual void OnButtonRelease(const ButtonName& button) = 0; +}; +} // namespace MOUSE +} // namespace KODI diff --git a/xbmc/input/mouse/interfaces/IMouseInputProvider.h b/xbmc/input/mouse/interfaces/IMouseInputProvider.h new file mode 100644 index 0000000..f289070 --- /dev/null +++ b/xbmc/input/mouse/interfaces/IMouseInputProvider.h @@ -0,0 +1,43 @@ +/* + * 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 + +namespace KODI +{ +namespace MOUSE +{ +class IMouseInputHandler; + +/*! + * \ingroup mouse + * \brief Interface for classes that can provide mouse input + */ +class IMouseInputProvider +{ +public: + virtual ~IMouseInputProvider() = default; + + /*! + * \brief Registers a handler to be called on mouse input + * + * \param handler The handler to receive mouse input provided by this class + * \param bPromiscuous True to observe all events without affecting + * subsequent handlers + */ + virtual void RegisterMouseHandler(IMouseInputHandler* handler, bool bPromiscuous) = 0; + + /*! + * \brief Unregisters handler from mouse input + * + * \param handler The handler that was receiving mouse input + */ + virtual void UnregisterMouseHandler(IMouseInputHandler* handler) = 0; +}; +} // namespace MOUSE +} // namespace KODI |