Merge pull request #2533 from petriposio/master
[vuplus_xbmc] / xbmc / input / MouseStat.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "MouseStat.h"
22 #include "guilib/Key.h"
23 #include "windowing/WindowingFactory.h"
24 #include "utils/TimeUtils.h"
25
26 CMouseStat::CMouseStat()
27 {
28   m_pointerState = MOUSE_STATE_NORMAL;
29   SetEnabled();
30   m_speedX = m_speedY = 0;
31   m_maxX = m_maxY = 0;
32   memset(&m_mouseState, 0, sizeof(m_mouseState));
33   m_Action = ACTION_NOOP;
34 }
35
36 CMouseStat::~CMouseStat()
37 {
38 }
39
40 void CMouseStat::Initialize()
41 {
42   // Set the default resolution (PAL)
43   SetResolution(720, 576, 1, 1);
44 }
45
46 void CMouseStat::HandleEvent(XBMC_Event& newEvent)
47 {
48   // Save the mouse position and the size of the last move
49   int dx, dy;
50   if (newEvent.type == XBMC_MOUSEMOTION)
51   {
52     dx = newEvent.motion.x - m_mouseState.x;
53     dy = newEvent.motion.y - m_mouseState.y;
54   }
55   else if (newEvent.type == XBMC_MOUSEBUTTONDOWN || newEvent.type == XBMC_MOUSEBUTTONUP)
56   {
57     dx = newEvent.button.x - m_mouseState.x;
58     dy = newEvent.button.y - m_mouseState.y;
59   }
60   else
61   {
62     return;
63   }
64   m_mouseState.dx = dx;
65   m_mouseState.dy = dy;
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));
68
69   // Fill in the public members
70   if (newEvent.button.type == XBMC_MOUSEBUTTONDOWN)
71   {
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;
79   }
80   else if (newEvent.button.type == XBMC_MOUSEBUTTONUP)
81   {
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;
89   }
90
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;
95   
96   for (int i = 0; i < 5; i++)
97   {
98     bClick[i] = false;
99     bDoubleClick[i] = false;
100     bHold[i] = 0;
101
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]);
105     switch (action)
106     {
107     case CButtonState::MB_SHORT_CLICK:
108     case CButtonState::MB_LONG_CLICK:
109       bClick[i] = true;
110       bNothingDown = false;
111       break;
112     case CButtonState::MB_DOUBLE_CLICK:
113       bDoubleClick[i] = true;
114       bNothingDown = false;
115       break;
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;
121       break;
122     default:
123       break;
124     }
125   }
126
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;
136
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;
141
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;
145
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;
151
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;
155
156   // ignore any other mouse messages
157   else
158     m_Action = ACTION_NOOP;
159
160   // Activate the mouse pointer
161   if (MovedPastThreshold() || m_mouseState.dz)
162     SetActive();
163   else if (bNothingDown)
164     SetState(MOUSE_STATE_NORMAL);
165   else
166     SetActive();
167 }
168
169 void CMouseStat::SetResolution(int maxX, int maxY, float speedX, float speedY)
170 {
171   m_maxX = maxX;
172   m_maxY = maxY;
173
174   // speed is currently unused
175   m_speedX = speedX;
176   m_speedY = speedY;
177 }
178
179 void CMouseStat::SetActive(bool active /*=true*/)
180 {
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());
188 }
189
190 // IsActive - returns true if we have been active in the last MOUSE_ACTIVE_LENGTH period
191 bool CMouseStat::IsActive()
192 {
193   if (m_mouseState.active && (CTimeUtils::GetFrameTime() - m_lastActiveTime > MOUSE_ACTIVE_LENGTH))
194     SetActive(false);
195   return (m_mouseState.active && IsEnabled());
196 }
197
198 void CMouseStat::SetEnabled(bool enabled)
199 {
200   m_mouseEnabled = enabled;
201   SetActive(enabled);
202 }
203
204 // IsEnabled - returns true if mouse is enabled
205 bool CMouseStat::IsEnabled() const
206 {
207   return m_mouseEnabled;
208 }
209
210 bool CMouseStat::MovedPastThreshold() const
211 {
212   return (m_mouseState.dx * m_mouseState.dx + m_mouseState.dy * m_mouseState.dy >= MOUSE_MINIMUM_MOVEMENT * MOUSE_MINIMUM_MOVEMENT);
213 }
214
215 uint32_t CMouseStat::GetAction() const
216 {
217   return m_Action;
218 }
219
220 int CMouseStat::GetHold(int ButtonID) const
221 {
222   switch (ButtonID)
223   { case MOUSE_LEFT_BUTTON:
224       return bHold[MOUSE_LEFT_BUTTON];
225   }
226   return false;
227 }
228
229 CMouseStat::CButtonState::CButtonState()
230 {
231   m_state = STATE_RELEASED;
232   m_time = 0;
233   m_x = 0;
234   m_y = 0;
235 }
236
237 bool CMouseStat::CButtonState::InClickRange(int x, int y) const
238 {
239   int dx = x - m_x;
240   int dy = y - m_y;
241   return (unsigned int)(dx*dx + dy*dy) <= click_confines*click_confines;
242 }
243
244 CMouseStat::CButtonState::BUTTON_ACTION CMouseStat::CButtonState::Update(unsigned int time, int x, int y, bool down)
245 {
246   if (m_state == STATE_IN_DRAG)
247   {
248     if (down)
249       return MB_DRAG;
250     m_state = STATE_RELEASED;
251     return MB_DRAG_END;
252   }
253   else if (m_state == STATE_RELEASED)
254   {
255     if (down)
256     {
257       m_state = STATE_IN_CLICK;
258       m_time = time;
259       m_x = x;
260       m_y = y;
261     }
262   }
263   else if (m_state == STATE_IN_CLICK)
264   {
265     if (down)
266     {
267       if (!InClickRange(x,y))
268       { // beginning a drag
269         m_state = STATE_IN_DRAG;
270         return MB_DRAG_START;
271       }
272     }
273     else
274     { // button up
275       if (time - m_time < short_click_time)
276       { // single click
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
280         m_y = y;
281         return MB_SHORT_CLICK;
282       }
283       else
284       { // long click
285         m_state = STATE_RELEASED;
286         return MB_LONG_CLICK;
287       }
288     }
289   }
290   else if (m_state == STATE_IN_DOUBLE_CLICK)
291   {
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);
296     }
297     if (down)
298     {
299       m_state = STATE_IN_DOUBLE_IGNORE;
300       return MB_DOUBLE_CLICK;
301     }
302   }
303   else if (m_state == STATE_IN_DOUBLE_IGNORE)
304   {
305     if (!down)
306       m_state = STATE_RELEASED;
307   }
308
309   return MB_NONE;
310 }
311