2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "MouseStat.h"
22 #include "guilib/Key.h"
23 #include "windowing/WindowingFactory.h"
24 #include "utils/TimeUtils.h"
26 CMouseStat::CMouseStat()
28 m_pointerState = MOUSE_STATE_NORMAL;
30 m_speedX = m_speedY = 0;
32 memset(&m_mouseState, 0, sizeof(m_mouseState));
33 m_Action = ACTION_NOOP;
36 CMouseStat::~CMouseStat()
40 void CMouseStat::Initialize()
42 // Set the default resolution (PAL)
43 SetResolution(720, 576, 1, 1);
46 void CMouseStat::HandleEvent(XBMC_Event& newEvent)
48 // Save the mouse position and the size of the last move
49 int dx = newEvent.motion.x - m_mouseState.x;
50 int dy = newEvent.motion.y - m_mouseState.y;
54 m_mouseState.x = std::max(0, std::min(m_maxX, m_mouseState.x + dx));
55 m_mouseState.y = std::max(0, std::min(m_maxY, m_mouseState.y + dy));
57 // Fill in the public members
58 if (newEvent.button.type == XBMC_MOUSEBUTTONDOWN)
60 if (newEvent.button.button == XBMC_BUTTON_LEFT) m_mouseState.button[MOUSE_LEFT_BUTTON] = true;
61 if (newEvent.button.button == XBMC_BUTTON_RIGHT) m_mouseState.button[MOUSE_RIGHT_BUTTON] = true;
62 if (newEvent.button.button == XBMC_BUTTON_MIDDLE) m_mouseState.button[MOUSE_MIDDLE_BUTTON] = true;
63 if (newEvent.button.button == XBMC_BUTTON_X1) m_mouseState.button[MOUSE_EXTRA_BUTTON1] = true;
64 if (newEvent.button.button == XBMC_BUTTON_X2) m_mouseState.button[MOUSE_EXTRA_BUTTON2] = true;
65 if (newEvent.button.button == XBMC_BUTTON_WHEELUP) m_mouseState.dz = 1;
66 if (newEvent.button.button == XBMC_BUTTON_WHEELDOWN) m_mouseState.dz = -1;
68 else if (newEvent.button.type == XBMC_MOUSEBUTTONUP)
70 if (newEvent.button.button == XBMC_BUTTON_LEFT) m_mouseState.button[MOUSE_LEFT_BUTTON] = false;
71 if (newEvent.button.button == XBMC_BUTTON_RIGHT) m_mouseState.button[MOUSE_RIGHT_BUTTON] = false;
72 if (newEvent.button.button == XBMC_BUTTON_MIDDLE) m_mouseState.button[MOUSE_MIDDLE_BUTTON] = false;
73 if (newEvent.button.button == XBMC_BUTTON_X1) m_mouseState.button[MOUSE_EXTRA_BUTTON1] = false;
74 if (newEvent.button.button == XBMC_BUTTON_X2) m_mouseState.button[MOUSE_EXTRA_BUTTON2] = false;
75 if (newEvent.button.button == XBMC_BUTTON_WHEELUP) m_mouseState.dz = 0;
76 if (newEvent.button.button == XBMC_BUTTON_WHEELDOWN) m_mouseState.dz = 0;
79 // Now check the current message and the previous state to find out if
80 // this is a click, doubleclick, drag etc
81 uint32_t now = CTimeUtils::GetFrameTime();
82 bool bNothingDown = true;
84 for (int i = 0; i < 5; i++)
87 bDoubleClick[i] = false;
90 // CButtonState::Update does the hard work of checking the button state
91 // and spotting drags, doubleclicks etc
92 CButtonState::BUTTON_ACTION action = m_buttonState[i].Update(now, m_mouseState.x, m_mouseState.y, m_mouseState.button[i]);
95 case CButtonState::MB_SHORT_CLICK:
96 case CButtonState::MB_LONG_CLICK:
100 case CButtonState::MB_DOUBLE_CLICK:
101 bDoubleClick[i] = true;
102 bNothingDown = false;
104 case CButtonState::MB_DRAG_START:
105 case CButtonState::MB_DRAG:
106 case CButtonState::MB_DRAG_END:
107 bHold[i] = action - CButtonState::MB_DRAG_START + 1;
108 bNothingDown = false;
115 // Now work out what action ID to send to XBMC.
116 // The bClick array is set true if CButtonState::Update spots a click
117 // i.e. a button down followed by a button up.
118 if (bClick[MOUSE_LEFT_BUTTON])
119 m_Action = ACTION_MOUSE_LEFT_CLICK;
120 else if (bClick[MOUSE_RIGHT_BUTTON])
121 m_Action = ACTION_MOUSE_RIGHT_CLICK;
122 else if (bClick[MOUSE_MIDDLE_BUTTON])
123 m_Action = ACTION_MOUSE_MIDDLE_CLICK;
125 // The bDoubleClick array is set true if CButtonState::Update spots a
126 // button down within double_click_time (500ms) of the last click
127 else if (bDoubleClick[MOUSE_LEFT_BUTTON])
128 m_Action = ACTION_MOUSE_DOUBLE_CLICK;
130 // The bHold array is set true if CButtonState::Update spots a mouse drag
131 else if (bHold[MOUSE_LEFT_BUTTON])
132 m_Action = ACTION_MOUSE_DRAG;
134 // dz is +1 on wheel up and -1 on wheel down
135 else if (m_mouseState.dz > 0)
136 m_Action = ACTION_MOUSE_WHEEL_UP;
137 else if (m_mouseState.dz < 0)
138 m_Action = ACTION_MOUSE_WHEEL_DOWN;
140 // Finally check for a mouse move (that isn't a drag)
141 else if (newEvent.type == XBMC_MOUSEMOTION)
142 m_Action = ACTION_MOUSE_MOVE;
144 // ignore any other mouse messages
146 m_Action = ACTION_NOOP;
148 // Activate the mouse pointer
149 if (MovedPastThreshold() || m_mouseState.dz)
151 else if (bNothingDown)
152 SetState(MOUSE_STATE_NORMAL);
157 void CMouseStat::SetResolution(int maxX, int maxY, float speedX, float speedY)
162 // speed is currently unused
167 void CMouseStat::SetActive(bool active /*=true*/)
169 m_lastActiveTime = CTimeUtils::GetFrameTime();
170 m_mouseState.active = active;
171 // we show the OS mouse if:
172 // 1. The mouse is active (it has been moved) AND
173 // 2. The XBMC mouse is disabled in settings AND
174 // 3. XBMC is not in fullscreen.
175 g_Windowing.ShowOSMouse(m_mouseState.active && !IsEnabled() && !g_Windowing.IsFullScreen());
178 // IsActive - returns true if we have been active in the last MOUSE_ACTIVE_LENGTH period
179 bool CMouseStat::IsActive()
181 if (m_mouseState.active && (CTimeUtils::GetFrameTime() - m_lastActiveTime > MOUSE_ACTIVE_LENGTH))
183 return (m_mouseState.active && IsEnabled());
186 void CMouseStat::SetEnabled(bool enabled)
188 m_mouseEnabled = enabled;
192 // IsEnabled - returns true if mouse is enabled
193 bool CMouseStat::IsEnabled() const
195 return m_mouseEnabled;
198 bool CMouseStat::MovedPastThreshold() const
200 return (m_mouseState.dx * m_mouseState.dx + m_mouseState.dy * m_mouseState.dy >= MOUSE_MINIMUM_MOVEMENT * MOUSE_MINIMUM_MOVEMENT);
203 uint32_t CMouseStat::GetAction() const
208 int CMouseStat::GetHold(int ButtonID) const
211 { case MOUSE_LEFT_BUTTON:
212 return bHold[MOUSE_LEFT_BUTTON];
217 CMouseStat::CButtonState::CButtonState()
219 m_state = STATE_RELEASED;
225 bool CMouseStat::CButtonState::InClickRange(int x, int y) const
229 return (unsigned int)(dx*dx + dy*dy) <= click_confines*click_confines;
232 CMouseStat::CButtonState::BUTTON_ACTION CMouseStat::CButtonState::Update(unsigned int time, int x, int y, bool down)
234 if (m_state == STATE_IN_DRAG)
238 m_state = STATE_RELEASED;
241 else if (m_state == STATE_RELEASED)
245 m_state = STATE_IN_CLICK;
251 else if (m_state == STATE_IN_CLICK)
255 if (!InClickRange(x,y))
256 { // beginning a drag
257 m_state = STATE_IN_DRAG;
258 return MB_DRAG_START;
263 if (time - m_time < short_click_time)
265 m_state = STATE_IN_DOUBLE_CLICK;
266 m_time = time; // double click time and positioning is measured from the
267 m_x = x; // end of a single click
269 return MB_SHORT_CLICK;
273 m_state = STATE_RELEASED;
274 return MB_LONG_CLICK;
278 else if (m_state == STATE_IN_DOUBLE_CLICK)
280 if (time - m_time > double_click_time || !InClickRange(x,y))
281 { // too long, or moved to much - reset to released state and re-update, as we may be starting a new click
282 m_state = STATE_RELEASED;
283 return Update(time, x, y, down);
287 m_state = STATE_IN_DOUBLE_IGNORE;
288 return MB_DOUBLE_CLICK;
291 else if (m_state == STATE_IN_DOUBLE_IGNORE)
294 m_state = STATE_RELEASED;