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 "input/Key.h"
23 #include "utils/TimeUtils.h"
24 #include "windowing/WindowingFactory.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_Key
= KEY_MOUSE_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_X3
) m_mouseState
.button
[MOUSE_EXTRA_BUTTON3
] = true;
78 if (newEvent
.button
.button
== XBMC_BUTTON_X4
) m_mouseState
.button
[MOUSE_EXTRA_BUTTON4
] = true;
79 if (newEvent
.button
.button
== XBMC_BUTTON_WHEELUP
) m_mouseState
.dz
= 1;
80 if (newEvent
.button
.button
== XBMC_BUTTON_WHEELDOWN
) m_mouseState
.dz
= -1;
82 else if (newEvent
.button
.type
== XBMC_MOUSEBUTTONUP
)
84 if (newEvent
.button
.button
== XBMC_BUTTON_LEFT
) m_mouseState
.button
[MOUSE_LEFT_BUTTON
] = false;
85 if (newEvent
.button
.button
== XBMC_BUTTON_RIGHT
) m_mouseState
.button
[MOUSE_RIGHT_BUTTON
] = false;
86 if (newEvent
.button
.button
== XBMC_BUTTON_MIDDLE
) m_mouseState
.button
[MOUSE_MIDDLE_BUTTON
] = false;
87 if (newEvent
.button
.button
== XBMC_BUTTON_X1
) m_mouseState
.button
[MOUSE_EXTRA_BUTTON1
] = false;
88 if (newEvent
.button
.button
== XBMC_BUTTON_X2
) m_mouseState
.button
[MOUSE_EXTRA_BUTTON2
] = false;
89 if (newEvent
.button
.button
== XBMC_BUTTON_X3
) m_mouseState
.button
[MOUSE_EXTRA_BUTTON3
] = false;
90 if (newEvent
.button
.button
== XBMC_BUTTON_X4
) m_mouseState
.button
[MOUSE_EXTRA_BUTTON4
] = false;
91 if (newEvent
.button
.button
== XBMC_BUTTON_WHEELUP
) m_mouseState
.dz
= 0;
92 if (newEvent
.button
.button
== XBMC_BUTTON_WHEELDOWN
) m_mouseState
.dz
= 0;
95 // Now check the current message and the previous state to find out if
96 // this is a click, doubleclick, drag etc
97 uint32_t now
= CTimeUtils::GetFrameTime();
98 bool bNothingDown
= true;
100 for (int i
= 0; i
< MOUSE_MAX_BUTTON
; i
++)
103 bLongClick
[i
] = false;
104 bDoubleClick
[i
] = false;
107 // CButtonState::Update does the hard work of checking the button state
108 // and spotting drags, doubleclicks etc
109 CButtonState::BUTTON_ACTION action
= m_buttonState
[i
].Update(now
, m_mouseState
.x
, m_mouseState
.y
, m_mouseState
.button
[i
]);
112 case CButtonState::MB_SHORT_CLICK
:
114 bNothingDown
= false;
116 case CButtonState::MB_LONG_CLICK
:
117 bLongClick
[i
] = true;
118 bNothingDown
= false;
120 case CButtonState::MB_DOUBLE_CLICK
:
121 bDoubleClick
[i
] = true;
122 bNothingDown
= false;
124 case CButtonState::MB_DRAG_START
:
125 bHold
[i
] = CButtonState::MB_DRAG_START
;
126 bNothingDown
= false;
128 case CButtonState::MB_DRAG
:
129 bHold
[i
] = CButtonState::MB_DRAG
;
130 bNothingDown
= false;
132 case CButtonState::MB_DRAG_END
:
133 bHold
[i
] = CButtonState::MB_DRAG_END
;
134 bNothingDown
= false;
141 // Now work out what action ID to send to XBMC.
143 // ignore any mouse messages by default
144 m_Key
= KEY_MOUSE_NOOP
;
146 for (int button
=0; button
<MOUSE_MAX_BUTTON
; ++button
)
148 // The bClick array is set true if CButtonState::Update spots a click
149 // i.e. a button down followed by a button up.
151 m_Key
= KEY_MOUSE_CLICK
+ button
;
152 // The bDoubleClick array is set true if CButtonState::Update spots a
153 // button down within double_click_time (500ms) of the last click
154 else if (bDoubleClick
[button
])
155 m_Key
= KEY_MOUSE_DOUBLE_CLICK
+ button
;
156 else if (bLongClick
[button
])
157 m_Key
= KEY_MOUSE_LONG_CLICK
+ button
;
159 if (m_Key
!= KEY_MOUSE_NOOP
)
163 if (m_Key
== KEY_MOUSE_NOOP
)
165 // The bHold array is set to the drag action
166 if (bHold
[MOUSE_LEFT_BUTTON
] != 0)
168 switch (bHold
[MOUSE_LEFT_BUTTON
])
170 case CButtonState::MB_DRAG
:
171 m_Key
= KEY_MOUSE_DRAG
;
173 case CButtonState::MB_DRAG_START
:
174 m_Key
= KEY_MOUSE_DRAG_START
;
176 case CButtonState::MB_DRAG_END
:
177 m_Key
= KEY_MOUSE_DRAG_END
;
181 else if (bHold
[MOUSE_RIGHT_BUTTON
] != 0)
183 switch (bHold
[MOUSE_RIGHT_BUTTON
])
185 case CButtonState::MB_DRAG
:
186 m_Key
= KEY_MOUSE_RDRAG
;
188 case CButtonState::MB_DRAG_START
:
189 m_Key
= KEY_MOUSE_RDRAG_START
;
191 case CButtonState::MB_DRAG_END
:
192 m_Key
= KEY_MOUSE_RDRAG_END
;
197 // dz is +1 on wheel up and -1 on wheel down
198 else if (m_mouseState
.dz
> 0)
199 m_Key
= KEY_MOUSE_WHEEL_UP
;
200 else if (m_mouseState
.dz
< 0)
201 m_Key
= KEY_MOUSE_WHEEL_DOWN
;
203 // Check for a mouse move that isn't a drag, ignoring messages with no movement at all
204 else if (newEvent
.type
== XBMC_MOUSEMOTION
&& (m_mouseState
.dx
|| m_mouseState
.dy
))
205 m_Key
= KEY_MOUSE_MOVE
;
208 // activate the mouse pointer if we have an action or the mouse has moved far enough
209 if ((MovedPastThreshold() && m_Key
== KEY_MOUSE_MOVE
) ||
210 (m_Key
!= KEY_MOUSE_NOOP
&& m_Key
!= KEY_MOUSE_MOVE
))
213 // reset the mouse state if nothing is held down
215 SetState(MOUSE_STATE_NORMAL
);
218 void CMouseStat::SetResolution(int maxX
, int maxY
, float speedX
, float speedY
)
223 // speed is currently unused
228 void CMouseStat::SetActive(bool active
/*=true*/)
230 m_lastActiveTime
= CTimeUtils::GetFrameTime();
231 m_mouseState
.active
= active
;
232 // we show the OS mouse if:
233 // 1. The mouse is active (it has been moved) AND
234 // 2. The XBMC mouse is disabled in settings AND
235 // 3. XBMC is not in fullscreen.
236 g_Windowing
.ShowOSMouse(m_mouseState
.active
&& !IsEnabled() && !g_Windowing
.IsFullScreen());
239 // IsActive - returns true if we have been active in the last MOUSE_ACTIVE_LENGTH period
240 bool CMouseStat::IsActive()
242 if (m_mouseState
.active
&& (CTimeUtils::GetFrameTime() - m_lastActiveTime
> MOUSE_ACTIVE_LENGTH
))
244 return (m_mouseState
.active
&& IsEnabled());
247 void CMouseStat::SetEnabled(bool enabled
)
249 m_mouseEnabled
= enabled
;
253 // IsEnabled - returns true if mouse is enabled
254 bool CMouseStat::IsEnabled() const
256 return m_mouseEnabled
;
259 bool CMouseStat::MovedPastThreshold() const
261 return (m_mouseState
.dx
* m_mouseState
.dx
+ m_mouseState
.dy
* m_mouseState
.dy
>= MOUSE_MINIMUM_MOVEMENT
* MOUSE_MINIMUM_MOVEMENT
);
264 uint32_t CMouseStat::GetKey() const
269 int CMouseStat::GetHold(int ButtonID
) const
272 { case MOUSE_LEFT_BUTTON
:
273 return bHold
[MOUSE_LEFT_BUTTON
];
278 CMouseStat::CButtonState::CButtonState()
280 m_state
= STATE_RELEASED
;
286 bool CMouseStat::CButtonState::InClickRange(int x
, int y
) const
290 return (unsigned int)(dx
*dx
+ dy
*dy
) <= click_confines
*click_confines
;
293 CMouseStat::CButtonState::BUTTON_ACTION
CMouseStat::CButtonState::Update(unsigned int time
, int x
, int y
, bool down
)
295 if (m_state
== STATE_IN_DRAG
)
299 m_state
= STATE_RELEASED
;
302 else if (m_state
== STATE_RELEASED
)
306 m_state
= STATE_IN_CLICK
;
312 else if (m_state
== STATE_IN_CLICK
)
316 if (!InClickRange(x
,y
))
317 { // beginning a drag
318 m_state
= STATE_IN_DRAG
;
319 return MB_DRAG_START
;
324 if (time
- m_time
< short_click_time
)
326 m_state
= STATE_IN_DOUBLE_CLICK
;
327 m_time
= time
; // double click time and positioning is measured from the
328 m_x
= x
; // end of a single click
330 return MB_SHORT_CLICK
;
334 m_state
= STATE_RELEASED
;
335 return MB_LONG_CLICK
;
339 else if (m_state
== STATE_IN_DOUBLE_CLICK
)
341 if (time
- m_time
> double_click_time
|| !InClickRange(x
,y
))
342 { // too long, or moved to much - reset to released state and re-update, as we may be starting a new click
343 m_state
= STATE_RELEASED
;
344 return Update(time
, x
, y
, down
);
348 m_state
= STATE_IN_DOUBLE_IGNORE
;
349 return MB_DOUBLE_CLICK
;
352 else if (m_state
== STATE_IN_DOUBLE_IGNORE
)
355 m_state
= STATE_RELEASED
;