summaryrefslogtreecommitdiffstats
path: root/xbmc/input/mouse/MouseStat.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/input/mouse/MouseStat.cpp')
-rw-r--r--xbmc/input/mouse/MouseStat.cpp378
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;
+}