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 "settings/lib/Setting.h"
24 #include "utils/TimeUtils.h"
25 #include "windowing/WindowingFactory.h"
27 CMouseStat::CMouseStat()
29 m_pointerState
= MOUSE_STATE_NORMAL
;
31 m_speedX
= m_speedY
= 0;
33 memset(&m_mouseState
, 0, sizeof(m_mouseState
));
34 m_Action
= ACTION_NOOP
;
37 CMouseStat::~CMouseStat()
41 void CMouseStat::OnSettingChanged(const CSetting
*setting
)
46 const std::string
&settingId
= setting
->GetId();
47 if (settingId
== "input.enablemouse")
48 SetEnabled(((CSettingBool
*)setting
)->GetValue());
51 void CMouseStat::Initialize()
53 // Set the default resolution (PAL)
54 SetResolution(720, 576, 1, 1);
57 void CMouseStat::HandleEvent(XBMC_Event
& newEvent
)
59 // Save the mouse position and the size of the last move
61 if (newEvent
.type
== XBMC_MOUSEMOTION
)
63 dx
= newEvent
.motion
.x
- m_mouseState
.x
;
64 dy
= newEvent
.motion
.y
- m_mouseState
.y
;
66 else if (newEvent
.type
== XBMC_MOUSEBUTTONDOWN
|| newEvent
.type
== XBMC_MOUSEBUTTONUP
)
68 dx
= newEvent
.button
.x
- m_mouseState
.x
;
69 dy
= newEvent
.button
.y
- m_mouseState
.y
;
77 m_mouseState
.x
= std::max(0, std::min(m_maxX
, m_mouseState
.x
+ dx
));
78 m_mouseState
.y
= std::max(0, std::min(m_maxY
, m_mouseState
.y
+ dy
));
80 // Fill in the public members
81 if (newEvent
.button
.type
== XBMC_MOUSEBUTTONDOWN
)
83 if (newEvent
.button
.button
== XBMC_BUTTON_LEFT
) m_mouseState
.button
[MOUSE_LEFT_BUTTON
] = true;
84 if (newEvent
.button
.button
== XBMC_BUTTON_RIGHT
) m_mouseState
.button
[MOUSE_RIGHT_BUTTON
] = true;
85 if (newEvent
.button
.button
== XBMC_BUTTON_MIDDLE
) m_mouseState
.button
[MOUSE_MIDDLE_BUTTON
] = true;
86 if (newEvent
.button
.button
== XBMC_BUTTON_X1
) m_mouseState
.button
[MOUSE_EXTRA_BUTTON1
] = true;
87 if (newEvent
.button
.button
== XBMC_BUTTON_X2
) m_mouseState
.button
[MOUSE_EXTRA_BUTTON2
] = true;
88 if (newEvent
.button
.button
== XBMC_BUTTON_WHEELUP
) m_mouseState
.dz
= 1;
89 if (newEvent
.button
.button
== XBMC_BUTTON_WHEELDOWN
) m_mouseState
.dz
= -1;
91 else if (newEvent
.button
.type
== XBMC_MOUSEBUTTONUP
)
93 if (newEvent
.button
.button
== XBMC_BUTTON_LEFT
) m_mouseState
.button
[MOUSE_LEFT_BUTTON
] = false;
94 if (newEvent
.button
.button
== XBMC_BUTTON_RIGHT
) m_mouseState
.button
[MOUSE_RIGHT_BUTTON
] = false;
95 if (newEvent
.button
.button
== XBMC_BUTTON_MIDDLE
) m_mouseState
.button
[MOUSE_MIDDLE_BUTTON
] = false;
96 if (newEvent
.button
.button
== XBMC_BUTTON_X1
) m_mouseState
.button
[MOUSE_EXTRA_BUTTON1
] = false;
97 if (newEvent
.button
.button
== XBMC_BUTTON_X2
) m_mouseState
.button
[MOUSE_EXTRA_BUTTON2
] = false;
98 if (newEvent
.button
.button
== XBMC_BUTTON_WHEELUP
) m_mouseState
.dz
= 0;
99 if (newEvent
.button
.button
== XBMC_BUTTON_WHEELDOWN
) m_mouseState
.dz
= 0;
102 // Now check the current message and the previous state to find out if
103 // this is a click, doubleclick, drag etc
104 uint32_t now
= CTimeUtils::GetFrameTime();
105 bool bNothingDown
= true;
107 for (int i
= 0; i
< 5; i
++)
110 bDoubleClick
[i
] = false;
113 // CButtonState::Update does the hard work of checking the button state
114 // and spotting drags, doubleclicks etc
115 CButtonState::BUTTON_ACTION action
= m_buttonState
[i
].Update(now
, m_mouseState
.x
, m_mouseState
.y
, m_mouseState
.button
[i
]);
118 case CButtonState::MB_SHORT_CLICK
:
119 case CButtonState::MB_LONG_CLICK
:
121 bNothingDown
= false;
123 case CButtonState::MB_DOUBLE_CLICK
:
124 bDoubleClick
[i
] = true;
125 bNothingDown
= false;
127 case CButtonState::MB_DRAG_START
:
128 case CButtonState::MB_DRAG
:
129 case CButtonState::MB_DRAG_END
:
130 bHold
[i
] = action
- CButtonState::MB_DRAG_START
+ 1;
131 bNothingDown
= false;
138 // Now work out what action ID to send to XBMC.
139 // The bClick array is set true if CButtonState::Update spots a click
140 // i.e. a button down followed by a button up.
141 if (bClick
[MOUSE_LEFT_BUTTON
])
142 m_Action
= ACTION_MOUSE_LEFT_CLICK
;
143 else if (bClick
[MOUSE_RIGHT_BUTTON
])
144 m_Action
= ACTION_MOUSE_RIGHT_CLICK
;
145 else if (bClick
[MOUSE_MIDDLE_BUTTON
])
146 m_Action
= ACTION_MOUSE_MIDDLE_CLICK
;
148 // The bDoubleClick array is set true if CButtonState::Update spots a
149 // button down within double_click_time (500ms) of the last click
150 else if (bDoubleClick
[MOUSE_LEFT_BUTTON
])
151 m_Action
= ACTION_MOUSE_DOUBLE_CLICK
;
153 // The bHold array is set true if CButtonState::Update spots a mouse drag
154 else if (bHold
[MOUSE_LEFT_BUTTON
])
155 m_Action
= ACTION_MOUSE_DRAG
;
157 // dz is +1 on wheel up and -1 on wheel down
158 else if (m_mouseState
.dz
> 0)
159 m_Action
= ACTION_MOUSE_WHEEL_UP
;
160 else if (m_mouseState
.dz
< 0)
161 m_Action
= ACTION_MOUSE_WHEEL_DOWN
;
163 // Finally check for a mouse move (that isn't a drag)
164 else if (newEvent
.type
== XBMC_MOUSEMOTION
)
165 m_Action
= ACTION_MOUSE_MOVE
;
167 // ignore any other mouse messages
169 m_Action
= ACTION_NOOP
;
171 // activate the mouse pointer if we have an action or the mouse has moved far enough
172 if ((MovedPastThreshold() && m_Action
== ACTION_MOUSE_MOVE
) ||
173 (m_Action
!= ACTION_NOOP
&& m_Action
!= ACTION_MOUSE_MOVE
))
176 // reset the mouse state if nothing is held down
178 SetState(MOUSE_STATE_NORMAL
);
181 void CMouseStat::SetResolution(int maxX
, int maxY
, float speedX
, float speedY
)
186 // speed is currently unused
191 void CMouseStat::SetActive(bool active
/*=true*/)
193 m_lastActiveTime
= CTimeUtils::GetFrameTime();
194 m_mouseState
.active
= active
;
195 // we show the OS mouse if:
196 // 1. The mouse is active (it has been moved) AND
197 // 2. The XBMC mouse is disabled in settings AND
198 // 3. XBMC is not in fullscreen.
199 g_Windowing
.ShowOSMouse(m_mouseState
.active
&& !IsEnabled() && !g_Windowing
.IsFullScreen());
202 // IsActive - returns true if we have been active in the last MOUSE_ACTIVE_LENGTH period
203 bool CMouseStat::IsActive()
205 if (m_mouseState
.active
&& (CTimeUtils::GetFrameTime() - m_lastActiveTime
> MOUSE_ACTIVE_LENGTH
))
207 return (m_mouseState
.active
&& IsEnabled());
210 void CMouseStat::SetEnabled(bool enabled
)
212 m_mouseEnabled
= enabled
;
216 // IsEnabled - returns true if mouse is enabled
217 bool CMouseStat::IsEnabled() const
219 return m_mouseEnabled
;
222 bool CMouseStat::MovedPastThreshold() const
224 return (m_mouseState
.dx
* m_mouseState
.dx
+ m_mouseState
.dy
* m_mouseState
.dy
>= MOUSE_MINIMUM_MOVEMENT
* MOUSE_MINIMUM_MOVEMENT
);
227 uint32_t CMouseStat::GetAction() const
232 int CMouseStat::GetHold(int ButtonID
) const
235 { case MOUSE_LEFT_BUTTON
:
236 return bHold
[MOUSE_LEFT_BUTTON
];
241 CMouseStat::CButtonState::CButtonState()
243 m_state
= STATE_RELEASED
;
249 bool CMouseStat::CButtonState::InClickRange(int x
, int y
) const
253 return (unsigned int)(dx
*dx
+ dy
*dy
) <= click_confines
*click_confines
;
256 CMouseStat::CButtonState::BUTTON_ACTION
CMouseStat::CButtonState::Update(unsigned int time
, int x
, int y
, bool down
)
258 if (m_state
== STATE_IN_DRAG
)
262 m_state
= STATE_RELEASED
;
265 else if (m_state
== STATE_RELEASED
)
269 m_state
= STATE_IN_CLICK
;
275 else if (m_state
== STATE_IN_CLICK
)
279 if (!InClickRange(x
,y
))
280 { // beginning a drag
281 m_state
= STATE_IN_DRAG
;
282 return MB_DRAG_START
;
287 if (time
- m_time
< short_click_time
)
289 m_state
= STATE_IN_DOUBLE_CLICK
;
290 m_time
= time
; // double click time and positioning is measured from the
291 m_x
= x
; // end of a single click
293 return MB_SHORT_CLICK
;
297 m_state
= STATE_RELEASED
;
298 return MB_LONG_CLICK
;
302 else if (m_state
== STATE_IN_DOUBLE_CLICK
)
304 if (time
- m_time
> double_click_time
|| !InClickRange(x
,y
))
305 { // too long, or moved to much - reset to released state and re-update, as we may be starting a new click
306 m_state
= STATE_RELEASED
;
307 return Update(time
, x
, y
, down
);
311 m_state
= STATE_IN_DOUBLE_IGNORE
;
312 return MB_DOUBLE_CLICK
;
315 else if (m_state
== STATE_IN_DOUBLE_IGNORE
)
318 m_state
= STATE_RELEASED
;