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
50 if (newEvent.type == XBMC_MOUSEMOTION)
52 dx = newEvent.motion.x - m_mouseState.x;
53 dy = newEvent.motion.y - m_mouseState.y;
55 else if (newEvent.type == XBMC_MOUSEBUTTONDOWN || newEvent.type == XBMC_MOUSEBUTTONUP)
57 dx = newEvent.button.x - m_mouseState.x;
58 dy = newEvent.button.y - m_mouseState.y;
66 m_mouseState.x = std::max(0, std::min(m_maxX, m_mouseState.x + dx));
67 m_mouseState.y = std::max(0, std::min(m_maxY, m_mouseState.y + dy));
69 // Fill in the public members
70 if (newEvent.button.type == XBMC_MOUSEBUTTONDOWN)
72 if (newEvent.button.button == XBMC_BUTTON_LEFT) m_mouseState.button[MOUSE_LEFT_BUTTON] = true;
73 if (newEvent.button.button == XBMC_BUTTON_RIGHT) m_mouseState.button[MOUSE_RIGHT_BUTTON] = true;
74 if (newEvent.button.button == XBMC_BUTTON_MIDDLE) m_mouseState.button[MOUSE_MIDDLE_BUTTON] = true;
75 if (newEvent.button.button == XBMC_BUTTON_X1) m_mouseState.button[MOUSE_EXTRA_BUTTON1] = true;
76 if (newEvent.button.button == XBMC_BUTTON_X2) m_mouseState.button[MOUSE_EXTRA_BUTTON2] = true;
77 if (newEvent.button.button == XBMC_BUTTON_WHEELUP) m_mouseState.dz = 1;
78 if (newEvent.button.button == XBMC_BUTTON_WHEELDOWN) m_mouseState.dz = -1;
80 else if (newEvent.button.type == XBMC_MOUSEBUTTONUP)
82 if (newEvent.button.button == XBMC_BUTTON_LEFT) m_mouseState.button[MOUSE_LEFT_BUTTON] = false;
83 if (newEvent.button.button == XBMC_BUTTON_RIGHT) m_mouseState.button[MOUSE_RIGHT_BUTTON] = false;
84 if (newEvent.button.button == XBMC_BUTTON_MIDDLE) m_mouseState.button[MOUSE_MIDDLE_BUTTON] = false;
85 if (newEvent.button.button == XBMC_BUTTON_X1) m_mouseState.button[MOUSE_EXTRA_BUTTON1] = false;
86 if (newEvent.button.button == XBMC_BUTTON_X2) m_mouseState.button[MOUSE_EXTRA_BUTTON2] = false;
87 if (newEvent.button.button == XBMC_BUTTON_WHEELUP) m_mouseState.dz = 0;
88 if (newEvent.button.button == XBMC_BUTTON_WHEELDOWN) m_mouseState.dz = 0;
91 // Now check the current message and the previous state to find out if
92 // this is a click, doubleclick, drag etc
93 uint32_t now = CTimeUtils::GetFrameTime();
94 bool bNothingDown = true;
96 for (int i = 0; i < 5; i++)
99 bDoubleClick[i] = false;
102 // CButtonState::Update does the hard work of checking the button state
103 // and spotting drags, doubleclicks etc
104 CButtonState::BUTTON_ACTION action = m_buttonState[i].Update(now, m_mouseState.x, m_mouseState.y, m_mouseState.button[i]);
107 case CButtonState::MB_SHORT_CLICK:
108 case CButtonState::MB_LONG_CLICK:
110 bNothingDown = false;
112 case CButtonState::MB_DOUBLE_CLICK:
113 bDoubleClick[i] = true;
114 bNothingDown = false;
116 case CButtonState::MB_DRAG_START:
117 case CButtonState::MB_DRAG:
118 case CButtonState::MB_DRAG_END:
119 bHold[i] = action - CButtonState::MB_DRAG_START + 1;
120 bNothingDown = false;
127 // Now work out what action ID to send to XBMC.
128 // The bClick array is set true if CButtonState::Update spots a click
129 // i.e. a button down followed by a button up.
130 if (bClick[MOUSE_LEFT_BUTTON])
131 m_Action = ACTION_MOUSE_LEFT_CLICK;
132 else if (bClick[MOUSE_RIGHT_BUTTON])
133 m_Action = ACTION_MOUSE_RIGHT_CLICK;
134 else if (bClick[MOUSE_MIDDLE_BUTTON])
135 m_Action = ACTION_MOUSE_MIDDLE_CLICK;
137 // The bDoubleClick array is set true if CButtonState::Update spots a
138 // button down within double_click_time (500ms) of the last click
139 else if (bDoubleClick[MOUSE_LEFT_BUTTON])
140 m_Action = ACTION_MOUSE_DOUBLE_CLICK;
142 // The bHold array is set true if CButtonState::Update spots a mouse drag
143 else if (bHold[MOUSE_LEFT_BUTTON])
144 m_Action = ACTION_MOUSE_DRAG;
146 // dz is +1 on wheel up and -1 on wheel down
147 else if (m_mouseState.dz > 0)
148 m_Action = ACTION_MOUSE_WHEEL_UP;
149 else if (m_mouseState.dz < 0)
150 m_Action = ACTION_MOUSE_WHEEL_DOWN;
152 // Finally check for a mouse move (that isn't a drag)
153 else if (newEvent.type == XBMC_MOUSEMOTION)
154 m_Action = ACTION_MOUSE_MOVE;
156 // ignore any other mouse messages
158 m_Action = ACTION_NOOP;
160 // Activate the mouse pointer
161 if (MovedPastThreshold() || m_mouseState.dz)
163 else if (bNothingDown)
164 SetState(MOUSE_STATE_NORMAL);
169 void CMouseStat::SetResolution(int maxX, int maxY, float speedX, float speedY)
174 // speed is currently unused
179 void CMouseStat::SetActive(bool active /*=true*/)
181 m_lastActiveTime = CTimeUtils::GetFrameTime();
182 m_mouseState.active = active;
183 // we show the OS mouse if:
184 // 1. The mouse is active (it has been moved) AND
185 // 2. The XBMC mouse is disabled in settings AND
186 // 3. XBMC is not in fullscreen.
187 g_Windowing.ShowOSMouse(m_mouseState.active && !IsEnabled() && !g_Windowing.IsFullScreen());
190 // IsActive - returns true if we have been active in the last MOUSE_ACTIVE_LENGTH period
191 bool CMouseStat::IsActive()
193 if (m_mouseState.active && (CTimeUtils::GetFrameTime() - m_lastActiveTime > MOUSE_ACTIVE_LENGTH))
195 return (m_mouseState.active && IsEnabled());
198 void CMouseStat::SetEnabled(bool enabled)
200 m_mouseEnabled = enabled;
204 // IsEnabled - returns true if mouse is enabled
205 bool CMouseStat::IsEnabled() const
207 return m_mouseEnabled;
210 bool CMouseStat::MovedPastThreshold() const
212 return (m_mouseState.dx * m_mouseState.dx + m_mouseState.dy * m_mouseState.dy >= MOUSE_MINIMUM_MOVEMENT * MOUSE_MINIMUM_MOVEMENT);
215 uint32_t CMouseStat::GetAction() const
220 int CMouseStat::GetHold(int ButtonID) const
223 { case MOUSE_LEFT_BUTTON:
224 return bHold[MOUSE_LEFT_BUTTON];
229 CMouseStat::CButtonState::CButtonState()
231 m_state = STATE_RELEASED;
237 bool CMouseStat::CButtonState::InClickRange(int x, int y) const
241 return (unsigned int)(dx*dx + dy*dy) <= click_confines*click_confines;
244 CMouseStat::CButtonState::BUTTON_ACTION CMouseStat::CButtonState::Update(unsigned int time, int x, int y, bool down)
246 if (m_state == STATE_IN_DRAG)
250 m_state = STATE_RELEASED;
253 else if (m_state == STATE_RELEASED)
257 m_state = STATE_IN_CLICK;
263 else if (m_state == STATE_IN_CLICK)
267 if (!InClickRange(x,y))
268 { // beginning a drag
269 m_state = STATE_IN_DRAG;
270 return MB_DRAG_START;
275 if (time - m_time < short_click_time)
277 m_state = STATE_IN_DOUBLE_CLICK;
278 m_time = time; // double click time and positioning is measured from the
279 m_x = x; // end of a single click
281 return MB_SHORT_CLICK;
285 m_state = STATE_RELEASED;
286 return MB_LONG_CLICK;
290 else if (m_state == STATE_IN_DOUBLE_CLICK)
292 if (time - m_time > double_click_time || !InClickRange(x,y))
293 { // too long, or moved to much - reset to released state and re-update, as we may be starting a new click
294 m_state = STATE_RELEASED;
295 return Update(time, x, y, down);
299 m_state = STATE_IN_DOUBLE_IGNORE;
300 return MB_DOUBLE_CLICK;
303 else if (m_state == STATE_IN_DOUBLE_IGNORE)
306 m_state = STATE_RELEASED;