2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Mouse functions
5 * FILE: subsystems/win32/win32k/ntuser/input.c
6 * PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Rafal Harabien (rafalh@reactos.org)
11 DBG_DEFAULT_CHANNEL(UserInput
);
13 MOUSEMOVEPOINT gMouseHistoryOfMoves
[64];
14 INT gcMouseHistoryOfMoves
= 0;
17 * UserGetMouseButtonsState
19 * Returns bitfield of MK_* flags used in mouse messages
22 UserGetMouseButtonsState(VOID
)
26 wRet
= IntGetSysCursorInfo()->ButtonsDown
;
28 if (IS_KEY_DOWN(gafAsyncKeyState
, VK_SHIFT
)) wRet
|= MK_SHIFT
;
29 if (IS_KEY_DOWN(gafAsyncKeyState
, VK_CONTROL
)) wRet
|= MK_CONTROL
;
35 * UserProcessMouseInput
37 * Process raw mouse input data
40 UserProcessMouseInput(PMOUSE_INPUT_DATA mid
)
44 /* Convert MOUSE_INPUT_DATA to MOUSEINPUT. First init all fields. */
50 mi
.dwExtraInfo
= mid
->ExtraInformation
;
53 if (mi
.dx
!= 0 || mi
.dy
!= 0)
54 mi
.dwFlags
|= MOUSEEVENTF_MOVE
;
56 /* Flags for absolute move */
57 if (mid
->Flags
& MOUSE_MOVE_ABSOLUTE
)
58 mi
.dwFlags
|= MOUSEEVENTF_ABSOLUTE
;
59 if (mid
->Flags
& MOUSE_VIRTUAL_DESKTOP
)
60 mi
.dwFlags
|= MOUSEEVENTF_VIRTUALDESK
;
63 if (mid
->ButtonFlags
& MOUSE_LEFT_BUTTON_DOWN
)
64 mi
.dwFlags
|= MOUSEEVENTF_LEFTDOWN
;
65 if (mid
->ButtonFlags
& MOUSE_LEFT_BUTTON_UP
)
66 mi
.dwFlags
|= MOUSEEVENTF_LEFTUP
;
69 if (mid
->ButtonFlags
& MOUSE_MIDDLE_BUTTON_DOWN
)
70 mi
.dwFlags
|= MOUSEEVENTF_MIDDLEDOWN
;
71 if (mid
->ButtonFlags
& MOUSE_MIDDLE_BUTTON_UP
)
72 mi
.dwFlags
|= MOUSEEVENTF_MIDDLEUP
;
75 if (mid
->ButtonFlags
& MOUSE_RIGHT_BUTTON_DOWN
)
76 mi
.dwFlags
|= MOUSEEVENTF_RIGHTDOWN
;
77 if (mid
->ButtonFlags
& MOUSE_RIGHT_BUTTON_UP
)
78 mi
.dwFlags
|= MOUSEEVENTF_RIGHTUP
;
80 /* Note: Next buttons use mouseData field so they cannot be sent in one call */
83 if (mid
->ButtonFlags
& MOUSE_BUTTON_4_DOWN
)
85 mi
.dwFlags
|= MOUSEEVENTF_XDOWN
;
86 mi
.mouseData
|= XBUTTON1
;
88 if (mid
->ButtonFlags
& MOUSE_BUTTON_4_UP
)
90 mi
.dwFlags
|= MOUSEEVENTF_XUP
;
91 mi
.mouseData
|= XBUTTON1
;
94 /* If mouseData is used by button 4, send input and clear mi */
95 if (mi
.dwFlags
& (MOUSE_BUTTON_4_DOWN
| MOUSE_BUTTON_4_UP
))
97 UserSendMouseInput(&mi
, FALSE
);
98 RtlZeroMemory(&mi
, sizeof(mi
));
102 if (mid
->ButtonFlags
& MOUSE_BUTTON_5_DOWN
)
104 mi
.mouseData
|= XBUTTON2
;
105 mi
.dwFlags
|= MOUSEEVENTF_XDOWN
;
107 if (mid
->ButtonFlags
& MOUSE_BUTTON_5_UP
)
109 mi
.mouseData
|= XBUTTON2
;
110 mi
.dwFlags
|= MOUSEEVENTF_XUP
;
113 /* If mouseData is used by button 5, send input and clear mi */
114 if (mi
.dwFlags
& (MOUSE_BUTTON_5_DOWN
| MOUSE_BUTTON_5_UP
))
116 UserSendMouseInput(&mi
, FALSE
);
117 RtlZeroMemory(&mi
, sizeof(mi
));
121 if (mid
->ButtonFlags
& MOUSE_WHEEL
)
123 mi
.mouseData
= mid
->ButtonData
;
124 mi
.dwFlags
|= MOUSEEVENTF_WHEEL
;
127 /* If something has changed, send input to user */
129 UserSendMouseInput(&mi
, FALSE
);
133 * IntFixMouseInputButtons
135 * Helper function for supporting mouse button swap function
138 IntFixMouseInputButtons(DWORD dwFlags
)
142 if (!gspv
.bMouseBtnSwap
)
145 /* Buttons other than left and right are not affected */
146 dwNewFlags
= dwFlags
& ~(MOUSEEVENTF_LEFTDOWN
| MOUSEEVENTF_LEFTUP
|
147 MOUSEEVENTF_RIGHTDOWN
| MOUSEEVENTF_RIGHTUP
);
150 if (dwFlags
& MOUSEEVENTF_LEFTDOWN
)
151 dwNewFlags
|= MOUSEEVENTF_RIGHTDOWN
;
152 if (dwFlags
& MOUSEEVENTF_LEFTUP
)
153 dwNewFlags
|= MOUSEEVENTF_RIGHTUP
;
154 if (dwFlags
& MOUSEEVENTF_RIGHTDOWN
)
155 dwNewFlags
|= MOUSEEVENTF_LEFTDOWN
;
156 if (dwFlags
& MOUSEEVENTF_RIGHTUP
)
157 dwNewFlags
|= MOUSEEVENTF_LEFTUP
;
165 * Process mouse input from input devices and SendInput API
168 UserSendMouseInput(MOUSEINPUT
*pmi
, BOOL bInjected
)
171 PSYSTEM_CURSORINFO pCurInfo
;
177 pCurInfo
= IntGetSysCursorInfo();
178 ptCursor
= gpsi
->ptCursor
;
179 dwFlags
= IntFixMouseInputButtons(pmi
->dwFlags
);
181 gppiInputProvider
= ((PTHREADINFO
)PsGetCurrentThreadWin32Thread())->ppi
;
183 if (pmi
->dwFlags
& MOUSEEVENTF_MOVE
)
185 /* Mouse has changes position */
186 if (!(pmi
->dwFlags
& MOUSEEVENTF_ABSOLUTE
))
189 ptCursor
.x
+= pmi
->dx
;
190 ptCursor
.y
+= pmi
->dy
;
192 else if (pmi
->dwFlags
& MOUSEEVENTF_VIRTUALDESK
)
194 /* Absolute move in virtual screen units */
195 ptCursor
.x
= pmi
->dx
* UserGetSystemMetrics(SM_CXVIRTUALSCREEN
) >> 16;
196 ptCursor
.y
= pmi
->dy
* UserGetSystemMetrics(SM_CYVIRTUALSCREEN
) >> 16;
200 /* Absolute move in primary monitor units */
201 ptCursor
.x
= pmi
->dx
* UserGetSystemMetrics(SM_CXSCREEN
) >> 16;
202 ptCursor
.y
= pmi
->dy
* UserGetSystemMetrics(SM_CYSCREEN
) >> 16;
206 /* Init message fields */
207 Msg
.wParam
= UserGetMouseButtonsState();
208 Msg
.lParam
= MAKELPARAM(ptCursor
.x
, ptCursor
.y
);
210 Msg
.time
= pmi
->time
;
213 LARGE_INTEGER LargeTickCount
;
214 KeQueryTickCount(&LargeTickCount
);
215 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
218 /* Do GetMouseMovePointsEx FIFO. */
219 gMouseHistoryOfMoves
[gcMouseHistoryOfMoves
].x
= ptCursor
.x
;
220 gMouseHistoryOfMoves
[gcMouseHistoryOfMoves
].y
= ptCursor
.y
;
221 gMouseHistoryOfMoves
[gcMouseHistoryOfMoves
].time
= Msg
.time
;
222 gMouseHistoryOfMoves
[gcMouseHistoryOfMoves
].dwExtraInfo
= pmi
->dwExtraInfo
;
223 if (++gcMouseHistoryOfMoves
== ARRAYSIZE(gMouseHistoryOfMoves
))
224 gcMouseHistoryOfMoves
= 0; // 0 - 63 is 64, FIFO forwards.
226 /* Update cursor position */
227 if (dwFlags
& MOUSEEVENTF_MOVE
)
229 UserSetCursorPos(ptCursor
.x
, ptCursor
.y
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
233 if (dwFlags
& MOUSEEVENTF_LEFTDOWN
)
235 SET_KEY_DOWN(gafAsyncKeyState
, VK_LBUTTON
, TRUE
);
236 Msg
.message
= WM_LBUTTONDOWN
;
237 pCurInfo
->ButtonsDown
|= MK_LBUTTON
;
238 Msg
.wParam
|= MK_LBUTTON
;
239 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
241 else if (dwFlags
& MOUSEEVENTF_LEFTUP
)
243 SET_KEY_DOWN(gafAsyncKeyState
, VK_LBUTTON
, FALSE
);
244 Msg
.message
= WM_LBUTTONUP
;
245 pCurInfo
->ButtonsDown
&= ~MK_LBUTTON
;
246 Msg
.wParam
&= ~MK_LBUTTON
;
247 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
251 if (dwFlags
& MOUSEEVENTF_MIDDLEDOWN
)
253 SET_KEY_DOWN(gafAsyncKeyState
, VK_MBUTTON
, TRUE
);
254 Msg
.message
= WM_MBUTTONDOWN
;
255 pCurInfo
->ButtonsDown
|= MK_MBUTTON
;
256 Msg
.wParam
|= MK_MBUTTON
;
257 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
259 else if (dwFlags
& MOUSEEVENTF_MIDDLEUP
)
261 SET_KEY_DOWN(gafAsyncKeyState
, VK_MBUTTON
, FALSE
);
262 Msg
.message
= WM_MBUTTONUP
;
263 pCurInfo
->ButtonsDown
&= ~MK_MBUTTON
;
264 Msg
.wParam
&= ~MK_MBUTTON
;
265 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
269 if (dwFlags
& MOUSEEVENTF_RIGHTDOWN
)
271 SET_KEY_DOWN(gafAsyncKeyState
, VK_RBUTTON
, TRUE
);
272 Msg
.message
= WM_RBUTTONDOWN
;
273 pCurInfo
->ButtonsDown
|= MK_RBUTTON
;
274 Msg
.wParam
|= MK_RBUTTON
;
275 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
277 else if (dwFlags
& MOUSEEVENTF_RIGHTUP
)
279 SET_KEY_DOWN(gafAsyncKeyState
, VK_RBUTTON
, FALSE
);
280 Msg
.message
= WM_RBUTTONUP
;
281 pCurInfo
->ButtonsDown
&= ~MK_RBUTTON
;
282 Msg
.wParam
&= ~MK_RBUTTON
;
283 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
286 if((dwFlags
& (MOUSEEVENTF_XDOWN
| MOUSEEVENTF_XUP
)) &&
287 (dwFlags
& MOUSEEVENTF_WHEEL
))
289 /* Fail because both types of events use the mouseData field */
290 WARN("Invalid flags!\n");
294 /* X-Button (4 or 5) */
295 if (dwFlags
& MOUSEEVENTF_XDOWN
)
297 Msg
.message
= WM_XBUTTONDOWN
;
298 if (pmi
->mouseData
& XBUTTON1
)
300 SET_KEY_DOWN(gafAsyncKeyState
, VK_XBUTTON1
, TRUE
);
301 pCurInfo
->ButtonsDown
|= MK_XBUTTON1
;
302 Msg
.wParam
|= MAKEWPARAM(MK_XBUTTON1
, XBUTTON1
);
303 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
305 if (pmi
->mouseData
& XBUTTON2
)
307 SET_KEY_DOWN(gafAsyncKeyState
, VK_XBUTTON2
, TRUE
);
308 pCurInfo
->ButtonsDown
|= MK_XBUTTON2
;
309 Msg
.wParam
|= MAKEWPARAM(MK_XBUTTON2
, XBUTTON2
);
310 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
313 else if (dwFlags
& MOUSEEVENTF_XUP
)
315 Msg
.message
= WM_XBUTTONUP
;
316 if(pmi
->mouseData
& XBUTTON1
)
318 SET_KEY_DOWN(gafAsyncKeyState
, VK_XBUTTON1
, FALSE
);
319 pCurInfo
->ButtonsDown
&= ~MK_XBUTTON1
;
320 Msg
.wParam
&= ~MK_XBUTTON1
;
321 Msg
.wParam
|= MAKEWPARAM(0, XBUTTON2
);
322 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
324 if (pmi
->mouseData
& XBUTTON2
)
326 SET_KEY_DOWN(gafAsyncKeyState
, VK_XBUTTON2
, FALSE
);
327 pCurInfo
->ButtonsDown
&= ~MK_XBUTTON2
;
328 Msg
.wParam
&= ~MK_XBUTTON2
;
329 Msg
.wParam
|= MAKEWPARAM(0, XBUTTON2
);
330 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
335 if (dwFlags
& MOUSEEVENTF_WHEEL
)
337 Msg
.message
= WM_MOUSEWHEEL
;
338 Msg
.wParam
= MAKEWPARAM(pCurInfo
->ButtonsDown
, pmi
->mouseData
);
339 co_MsqInsertMouseMessage(&Msg
, bInjected
, pmi
->dwExtraInfo
, TRUE
);
347 IntQueryTrackMouseEvent(
348 LPTRACKMOUSEEVENT lpEventTrack
)
353 pti
= PsGetCurrentThreadWin32Thread();
356 /* Always cleared with size set and return true. */
357 RtlZeroMemory(lpEventTrack
, sizeof(TRACKMOUSEEVENT
));
358 lpEventTrack
->cbSize
= sizeof(TRACKMOUSEEVENT
);
360 if (pDesk
->dwDTFlags
& (DF_TME_LEAVE
| DF_TME_HOVER
) &&
362 pti
->MessageQueue
== pDesk
->spwndTrack
->head
.pti
->MessageQueue
)
364 if (pDesk
->htEx
!= HTCLIENT
)
365 lpEventTrack
->dwFlags
|= TME_NONCLIENT
;
367 if (pDesk
->dwDTFlags
& DF_TME_LEAVE
)
368 lpEventTrack
->dwFlags
|= TME_LEAVE
;
370 if (pDesk
->dwDTFlags
& DF_TME_HOVER
)
372 lpEventTrack
->dwFlags
|= TME_HOVER
;
373 lpEventTrack
->dwHoverTime
= pDesk
->dwMouseHoverTime
;
375 lpEventTrack
->hwndTrack
= UserHMGetHandle(pDesk
->spwndTrack
);
383 LPTRACKMOUSEEVENT lpEventTrack
)
390 pti
= PsGetCurrentThreadWin32Thread();
393 if (!(pWnd
= UserGetWindowObject(lpEventTrack
->hwndTrack
)))
396 if ( pDesk
->spwndTrack
!= pWnd
||
397 (pDesk
->htEx
!= HTCLIENT
) ^ !!(lpEventTrack
->dwFlags
& TME_NONCLIENT
) )
399 if ( lpEventTrack
->dwFlags
& TME_LEAVE
&& !(lpEventTrack
->dwFlags
& TME_CANCEL
) )
401 UserPostMessage( lpEventTrack
->hwndTrack
,
402 lpEventTrack
->dwFlags
& TME_NONCLIENT
? WM_NCMOUSELEAVE
: WM_MOUSELEAVE
,
405 TRACE("IntTrackMouseEvent spwndTrack %p pwnd %p\n", pDesk
->spwndTrack
, pWnd
);
409 /* Tracking spwndTrack same as pWnd */
410 if (lpEventTrack
->dwFlags
& TME_CANCEL
) // Canceled mode.
412 if (lpEventTrack
->dwFlags
& TME_LEAVE
)
413 pDesk
->dwDTFlags
&= ~DF_TME_LEAVE
;
415 if (lpEventTrack
->dwFlags
& TME_HOVER
)
417 if (pDesk
->dwDTFlags
& DF_TME_HOVER
)
418 { // Kill hover timer.
419 IntKillTimer(pWnd
, ID_EVENT_SYSTIMER_MOUSEHOVER
, TRUE
);
420 pDesk
->dwDTFlags
&= ~DF_TME_HOVER
;
424 else // Not Canceled.
426 if (lpEventTrack
->dwFlags
& TME_LEAVE
)
427 pDesk
->dwDTFlags
|= DF_TME_LEAVE
;
429 if (lpEventTrack
->dwFlags
& TME_HOVER
)
431 pDesk
->dwDTFlags
|= DF_TME_HOVER
;
433 if (!lpEventTrack
->dwHoverTime
|| lpEventTrack
->dwHoverTime
== HOVER_DEFAULT
)
434 pDesk
->dwMouseHoverTime
= gspv
.iMouseHoverTime
; // use the system default hover time-out.
436 pDesk
->dwMouseHoverTime
= lpEventTrack
->dwHoverTime
;
437 // Start timer for the hover period.
438 IntSetTimer(pWnd
, ID_EVENT_SYSTIMER_MOUSEHOVER
, pDesk
->dwMouseHoverTime
, SystemTimerProc
, TMRF_SYSTEM
);
439 // Get windows thread message points.
440 point
= pWnd
->head
.pti
->ptLast
;
441 // Set desktop mouse hover from the system default hover rectangle.
442 RECTL_vSetRect(&pDesk
->rcMouseHover
,
443 point
.x
- gspv
.iMouseHoverWidth
/ 2,
444 point
.y
- gspv
.iMouseHoverHeight
/ 2,
445 point
.x
+ gspv
.iMouseHoverWidth
/ 2,
446 point
.y
+ gspv
.iMouseHoverHeight
/ 2);
454 NtUserTrackMouseEvent(
455 LPTRACKMOUSEEVENT lpEventTrack
)
457 TRACKMOUSEEVENT SafeTME
;
460 TRACE("Enter NtUserTrackMouseEvent\n");
464 ProbeForRead(lpEventTrack
, sizeof(TRACKMOUSEEVENT
), 1);
465 RtlCopyMemory(&SafeTME
, lpEventTrack
, sizeof(TRACKMOUSEEVENT
));
467 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
469 SetLastNtError(_SEH2_GetExceptionCode());
470 _SEH2_YIELD(return FALSE
);
474 if (SafeTME
.cbSize
!= sizeof(TRACKMOUSEEVENT
))
476 EngSetLastError(ERROR_INVALID_PARAMETER
);
480 if (SafeTME
.dwFlags
& ~(TME_CANCEL
| TME_QUERY
| TME_NONCLIENT
| TME_LEAVE
| TME_HOVER
) )
482 EngSetLastError(ERROR_INVALID_FLAGS
);
486 UserEnterExclusive();
488 if (SafeTME
.dwFlags
& TME_QUERY
)
490 bRet
= IntQueryTrackMouseEvent(&SafeTME
);
493 ProbeForWrite(lpEventTrack
, sizeof(TRACKMOUSEEVENT
), 1);
494 RtlCopyMemory(lpEventTrack
, &SafeTME
, sizeof(TRACKMOUSEEVENT
));
496 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
498 SetLastNtError(_SEH2_GetExceptionCode());
505 bRet
= IntTrackMouseEvent(&SafeTME
);
508 TRACE("Leave NtUserTrackMouseEvent, ret=%i\n", bRet
);
515 NtUserGetMouseMovePointsEx(
517 LPMOUSEMOVEPOINT lpptIn
,
518 LPMOUSEMOVEPOINT lpptOut
,
522 MOUSEMOVEPOINT Safeppt
;
526 TRACE("Enter NtUserGetMouseMovePointsEx\n");
528 if ((cbSize
!= sizeof(MOUSEMOVEPOINT
)) || (nBufPoints
< 0) || (nBufPoints
> 64))
530 EngSetLastError(ERROR_INVALID_PARAMETER
);
534 if (!lpptIn
|| (!lpptOut
&& nBufPoints
))
536 EngSetLastError(ERROR_NOACCESS
);
542 ProbeForRead(lpptIn
, cbSize
, 1);
543 RtlCopyMemory(&Safeppt
, lpptIn
, cbSize
);
545 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
547 SetLastNtError(_SEH2_GetExceptionCode());
548 _SEH2_YIELD(return (DWORD
)-1);
554 // http://msdn.microsoft.com/en-us/library/ms646259(v=vs.85).aspx
555 // This explains the math issues in transforming points.
556 iRet
= gcMouseHistoryOfMoves
; // FIFO is forward so retrieve backward.
560 if (Safeppt
.x
== 0 && Safeppt
.y
== 0)
562 // Finds the point, it returns the last nBufPoints prior to and including the supplied point.
563 if (gMouseHistoryOfMoves
[iRet
].x
== Safeppt
.x
&& gMouseHistoryOfMoves
[iRet
].y
== Safeppt
.y
)
565 if (Safeppt
.time
) // Now test time and it seems to be absolute.
567 if (Safeppt
.time
== gMouseHistoryOfMoves
[iRet
].time
)
574 if (--iRet
< 0) iRet
= 63;
581 if (--iRet
< 0) iRet
= 63;
583 while (iRet
!= gcMouseHistoryOfMoves
);
587 case GMMP_USE_DISPLAY_POINTS
:
592 ProbeForWrite(lpptOut
, cbSize
, 1);
594 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
596 SetLastNtError(_SEH2_GetExceptionCode());
598 _SEH2_YIELD(goto cleanup
);
604 case GMMP_USE_HIGH_RESOLUTION_POINTS
:
607 EngSetLastError(ERROR_POINT_NOT_FOUND
);
612 TRACE("Leave NtUserGetMouseMovePointsEx, ret=%i\n", iRet
);