diff options
Diffstat (limited to 'xbmc/input/mouse/MouseStat.cpp')
-rw-r--r-- | xbmc/input/mouse/MouseStat.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
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; +} |