[cosmetics] update date in GPL header
[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 = newEvent.motion.x - m_mouseState.x;
50   int dy = newEvent.motion.y - m_mouseState.y;
51   
52   m_mouseState.dx = dx;
53   m_mouseState.dy = dy;
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));
56
57   // Fill in the public members
58   if (newEvent.button.type == XBMC_MOUSEBUTTONDOWN)
59   {
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;
67   }
68   else if (newEvent.button.type == XBMC_MOUSEBUTTONUP)
69   {
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;
77   }
78
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;
83   
84   for (int i = 0; i < 5; i++)
85   {
86     bClick[i] = false;
87     bDoubleClick[i] = false;
88     bHold[i] = 0;
89
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]);
93     switch (action)
94     {
95     case CButtonState::MB_SHORT_CLICK:
96     case CButtonState::MB_LONG_CLICK:
97       bClick[i] = true;
98       bNothingDown = false;
99       break;
100     case CButtonState::MB_DOUBLE_CLICK:
101       bDoubleClick[i] = true;
102       bNothingDown = false;
103       break;
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;
109       break;
110     default:
111       break;
112     }
113   }
114
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;
124
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;
129
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;
133
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;
139
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;
143
144   // ignore any other mouse messages
145   else
146     m_Action = ACTION_NOOP;
147
148   // Activate the mouse pointer
149   if (MovedPastThreshold() || m_mouseState.dz)
150     SetActive();
151   else if (bNothingDown)
152     SetState(MOUSE_STATE_NORMAL);
153   else
154     SetActive();
155 }
156
157 void CMouseStat::SetResolution(int maxX, int maxY, float speedX, float speedY)
158 {
159   m_maxX = maxX;
160   m_maxY = maxY;
161
162   // speed is currently unused
163   m_speedX = speedX;
164   m_speedY = speedY;
165 }
166
167 void CMouseStat::SetActive(bool active /*=true*/)
168 {
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());
176 }
177
178 // IsActive - returns true if we have been active in the last MOUSE_ACTIVE_LENGTH period
179 bool CMouseStat::IsActive()
180 {
181   if (m_mouseState.active && (CTimeUtils::GetFrameTime() - m_lastActiveTime > MOUSE_ACTIVE_LENGTH))
182     SetActive(false);
183   return (m_mouseState.active && IsEnabled());
184 }
185
186 void CMouseStat::SetEnabled(bool enabled)
187 {
188   m_mouseEnabled = enabled;
189   SetActive(enabled);
190 }
191
192 // IsEnabled - returns true if mouse is enabled
193 bool CMouseStat::IsEnabled() const
194 {
195   return m_mouseEnabled;
196 }
197
198 bool CMouseStat::MovedPastThreshold() const
199 {
200   return (m_mouseState.dx * m_mouseState.dx + m_mouseState.dy * m_mouseState.dy >= MOUSE_MINIMUM_MOVEMENT * MOUSE_MINIMUM_MOVEMENT);
201 }
202
203 uint32_t CMouseStat::GetAction() const
204 {
205   return m_Action;
206 }
207
208 int CMouseStat::GetHold(int ButtonID) const
209 {
210   switch (ButtonID)
211   { case MOUSE_LEFT_BUTTON:
212       return bHold[MOUSE_LEFT_BUTTON];
213   }
214   return false;
215 }
216
217 CMouseStat::CButtonState::CButtonState()
218 {
219   m_state = STATE_RELEASED;
220   m_time = 0;
221   m_x = 0;
222   m_y = 0;
223 }
224
225 bool CMouseStat::CButtonState::InClickRange(int x, int y) const
226 {
227   int dx = x - m_x;
228   int dy = y - m_y;
229   return (unsigned int)(dx*dx + dy*dy) <= click_confines*click_confines;
230 }
231
232 CMouseStat::CButtonState::BUTTON_ACTION CMouseStat::CButtonState::Update(unsigned int time, int x, int y, bool down)
233 {
234   if (m_state == STATE_IN_DRAG)
235   {
236     if (down)
237       return MB_DRAG;
238     m_state = STATE_RELEASED;
239     return MB_DRAG_END;
240   }
241   else if (m_state == STATE_RELEASED)
242   {
243     if (down)
244     {
245       m_state = STATE_IN_CLICK;
246       m_time = time;
247       m_x = x;
248       m_y = y;
249     }
250   }
251   else if (m_state == STATE_IN_CLICK)
252   {
253     if (down)
254     {
255       if (!InClickRange(x,y))
256       { // beginning a drag
257         m_state = STATE_IN_DRAG;
258         return MB_DRAG_START;
259       }
260     }
261     else
262     { // button up
263       if (time - m_time < short_click_time)
264       { // single click
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
268         m_y = y;
269         return MB_SHORT_CLICK;
270       }
271       else
272       { // long click
273         m_state = STATE_RELEASED;
274         return MB_LONG_CLICK;
275       }
276     }
277   }
278   else if (m_state == STATE_IN_DOUBLE_CLICK)
279   {
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);
284     }
285     if (down)
286     {
287       m_state = STATE_IN_DOUBLE_IGNORE;
288       return MB_DOUBLE_CLICK;
289     }
290   }
291   else if (m_state == STATE_IN_DOUBLE_IGNORE)
292   {
293     if (!down)
294       m_state = STATE_RELEASED;
295   }
296
297   return MB_NONE;
298 }
299