xref: /reactos/win32ss/user/ntuser/mouse.c (revision c2c66aff)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS kernel
4  * PURPOSE:          Mouse functions
5  * FILE:             win32ss/user/ntuser/mouse.c
6  * PROGRAMERS:       Casper S. Hornstrup (chorns@users.sourceforge.net)
7  *                   Rafal Harabien (rafalh@reactos.org)
8  */
9 
10 #include <win32k.h>
11 DBG_DEFAULT_CHANNEL(UserInput);
12 
13 MOUSEMOVEPOINT gMouseHistoryOfMoves[64];
14 INT gcMouseHistoryOfMoves = 0;
15 
16 /*
17  * UserGetMouseButtonsState
18  *
19  * Returns bitfield of MK_* flags used in mouse messages
20  */
21 WORD FASTCALL
22 UserGetMouseButtonsState(VOID)
23 {
24     WORD wRet = 0;
25 
26     wRet = IntGetSysCursorInfo()->ButtonsDown;
27 
28     if (IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT)) wRet |= MK_SHIFT;
29     if (IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL)) wRet |= MK_CONTROL;
30 
31     return wRet;
32 }
33 
34 /*
35  * UserProcessMouseInput
36  *
37  * Process raw mouse input data
38  */
39 VOID NTAPI
40 UserProcessMouseInput(PMOUSE_INPUT_DATA mid)
41 {
42     MOUSEINPUT mi;
43 
44     /* Convert MOUSE_INPUT_DATA to MOUSEINPUT. First init all fields. */
45     mi.dx = mid->LastX;
46     mi.dy = mid->LastY;
47     mi.mouseData = 0;
48     mi.dwFlags = 0;
49     mi.time = 0;
50     mi.dwExtraInfo = mid->ExtraInformation;
51 
52     /* Mouse position */
53     if (mi.dx != 0 || mi.dy != 0)
54         mi.dwFlags |= MOUSEEVENTF_MOVE;
55 
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;
61 
62     /* Left button */
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;
67 
68     /* Middle button */
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;
73 
74     /* Right button */
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;
79 
80     /* Note: Next buttons use mouseData field so they cannot be sent in one call */
81 
82     /* Button 4 */
83     if (mid->ButtonFlags & MOUSE_BUTTON_4_DOWN)
84     {
85         mi.dwFlags |= MOUSEEVENTF_XDOWN;
86         mi.mouseData |= XBUTTON1;
87     }
88     if (mid->ButtonFlags & MOUSE_BUTTON_4_UP)
89     {
90         mi.dwFlags |= MOUSEEVENTF_XUP;
91         mi.mouseData |= XBUTTON1;
92     }
93 
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))
96     {
97         UserSendMouseInput(&mi, FALSE);
98         RtlZeroMemory(&mi, sizeof(mi));
99     }
100 
101     /* Button 5 */
102     if (mid->ButtonFlags & MOUSE_BUTTON_5_DOWN)
103     {
104         mi.mouseData |= XBUTTON2;
105         mi.dwFlags |= MOUSEEVENTF_XDOWN;
106     }
107     if (mid->ButtonFlags & MOUSE_BUTTON_5_UP)
108     {
109         mi.mouseData |= XBUTTON2;
110         mi.dwFlags |= MOUSEEVENTF_XUP;
111     }
112 
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))
115     {
116         UserSendMouseInput(&mi, FALSE);
117         RtlZeroMemory(&mi, sizeof(mi));
118     }
119 
120     /* Mouse wheel */
121     if (mid->ButtonFlags & MOUSE_WHEEL)
122     {
123         mi.mouseData = mid->ButtonData;
124         mi.dwFlags |= MOUSEEVENTF_WHEEL;
125     }
126 
127     /* If something has changed, send input to user */
128     if (mi.dwFlags)
129         UserSendMouseInput(&mi, FALSE);
130 }
131 
132 /*
133  * IntFixMouseInputButtons
134  *
135  * Helper function for supporting mouse button swap function
136  */
137 DWORD
138 IntFixMouseInputButtons(DWORD dwFlags)
139 {
140     DWORD dwNewFlags;
141 
142     if (!gspv.bMouseBtnSwap)
143         return dwFlags;
144 
145     /* Buttons other than left and right are not affected */
146     dwNewFlags = dwFlags & ~(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP |
147                              MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP);
148 
149     /* Swap buttons */
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;
158 
159     return dwNewFlags;
160 }
161 
162 /*
163  * UserSendMouseInput
164  *
165  * Process mouse input from input devices and SendInput API
166  */
167 BOOL NTAPI
168 UserSendMouseInput(MOUSEINPUT *pmi, BOOL bInjected)
169 {
170     POINT ptCursor;
171     PSYSTEM_CURSORINFO pCurInfo;
172     MSG Msg;
173     DWORD dwFlags;
174 
175     ASSERT(pmi);
176 
177     pCurInfo = IntGetSysCursorInfo();
178     ptCursor = gpsi->ptCursor;
179     dwFlags = IntFixMouseInputButtons(pmi->dwFlags);
180 
181     gppiInputProvider = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->ppi;
182 
183     if (pmi->dwFlags & MOUSEEVENTF_MOVE)
184     {
185         /* Mouse has changes position */
186         if (!(pmi->dwFlags & MOUSEEVENTF_ABSOLUTE))
187         {
188             /* Relative move */
189             ptCursor.x += pmi->dx;
190             ptCursor.y += pmi->dy;
191         }
192         else if (pmi->dwFlags & MOUSEEVENTF_VIRTUALDESK)
193         {
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;
197         }
198         else
199         {
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;
203         }
204     }
205 
206     /* Init message fields */
207     Msg.wParam = UserGetMouseButtonsState();
208     Msg.lParam = MAKELPARAM(ptCursor.x, ptCursor.y);
209     Msg.pt = ptCursor;
210     Msg.time = pmi->time;
211     if (!Msg.time)
212     {
213         LARGE_INTEGER LargeTickCount;
214         KeQueryTickCount(&LargeTickCount);
215         Msg.time = MsqCalculateMessageTime(&LargeTickCount);
216     }
217 
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.
225 
226     /* Update cursor position */
227     if (dwFlags & MOUSEEVENTF_MOVE)
228     {
229         UserSetCursorPos(ptCursor.x, ptCursor.y, bInjected, pmi->dwExtraInfo, TRUE);
230     }
231 
232     /* Left button */
233     if (dwFlags & MOUSEEVENTF_LEFTDOWN)
234     {
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);
240     }
241     else if (dwFlags & MOUSEEVENTF_LEFTUP)
242     {
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);
248     }
249 
250     /* Middle button */
251     if (dwFlags & MOUSEEVENTF_MIDDLEDOWN)
252     {
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);
258     }
259     else if (dwFlags & MOUSEEVENTF_MIDDLEUP)
260     {
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);
266     }
267 
268     /* Right button */
269     if (dwFlags & MOUSEEVENTF_RIGHTDOWN)
270     {
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);
276     }
277     else if (dwFlags & MOUSEEVENTF_RIGHTUP)
278     {
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);
284     }
285 
286     if((dwFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)) &&
287        (dwFlags & MOUSEEVENTF_WHEEL))
288     {
289         /* Fail because both types of events use the mouseData field */
290         WARN("Invalid flags!\n");
291         return FALSE;
292     }
293 
294     /* X-Button (4 or 5) */
295     if (dwFlags & MOUSEEVENTF_XDOWN)
296     {
297         Msg.message = WM_XBUTTONDOWN;
298         if (pmi->mouseData & XBUTTON1)
299         {
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);
304         }
305         if (pmi->mouseData & XBUTTON2)
306         {
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);
311         }
312     }
313     else if (dwFlags & MOUSEEVENTF_XUP)
314     {
315         Msg.message = WM_XBUTTONUP;
316         if(pmi->mouseData & XBUTTON1)
317         {
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);
323         }
324         if (pmi->mouseData & XBUTTON2)
325         {
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);
331         }
332     }
333 
334     /* Mouse wheel */
335     if (dwFlags & MOUSEEVENTF_WHEEL)
336     {
337         Msg.message = WM_MOUSEWHEEL;
338         Msg.wParam = MAKEWPARAM(pCurInfo->ButtonsDown, pmi->mouseData);
339         co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
340     }
341 
342     return TRUE;
343 }
344 
345 BOOL
346 FASTCALL
347 IntQueryTrackMouseEvent(
348     LPTRACKMOUSEEVENT lpEventTrack)
349 {
350     PDESKTOP pDesk;
351     PTHREADINFO pti;
352 
353     pti = PsGetCurrentThreadWin32Thread();
354     pDesk = pti->rpdesk;
355 
356     /* Always cleared with size set and return true. */
357     RtlZeroMemory(lpEventTrack , sizeof(TRACKMOUSEEVENT));
358     lpEventTrack->cbSize = sizeof(TRACKMOUSEEVENT);
359 
360     if (pDesk->dwDTFlags & (DF_TME_LEAVE | DF_TME_HOVER) &&
361         pDesk->spwndTrack &&
362         pti->MessageQueue == pDesk->spwndTrack->head.pti->MessageQueue)
363     {
364         if (pDesk->htEx != HTCLIENT)
365             lpEventTrack->dwFlags |= TME_NONCLIENT;
366 
367         if (pDesk->dwDTFlags & DF_TME_LEAVE)
368             lpEventTrack->dwFlags |= TME_LEAVE;
369 
370         if (pDesk->dwDTFlags & DF_TME_HOVER)
371         {
372             lpEventTrack->dwFlags |= TME_HOVER;
373             lpEventTrack->dwHoverTime = pDesk->dwMouseHoverTime;
374         }
375         lpEventTrack->hwndTrack = UserHMGetHandle(pDesk->spwndTrack);
376     }
377     return TRUE;
378 }
379 
380 BOOL
381 FASTCALL
382 IntTrackMouseEvent(
383     LPTRACKMOUSEEVENT lpEventTrack)
384 {
385     PDESKTOP pDesk;
386     PTHREADINFO pti;
387     PWND pWnd;
388     POINT point;
389 
390     pti = PsGetCurrentThreadWin32Thread();
391     pDesk = pti->rpdesk;
392 
393     if (!(pWnd = UserGetWindowObject(lpEventTrack->hwndTrack)))
394         return FALSE;
395 
396     if ( pDesk->spwndTrack != pWnd ||
397             (pDesk->htEx != HTCLIENT) ^ !!(lpEventTrack->dwFlags & TME_NONCLIENT) )
398     {
399         if ( lpEventTrack->dwFlags & TME_LEAVE && !(lpEventTrack->dwFlags & TME_CANCEL) )
400         {
401             UserPostMessage( lpEventTrack->hwndTrack,
402                              lpEventTrack->dwFlags & TME_NONCLIENT ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
403                              0, 0);
404         }
405         TRACE("IntTrackMouseEvent spwndTrack %p pwnd %p\n", pDesk->spwndTrack, pWnd);
406         return TRUE;
407     }
408 
409     /* Tracking spwndTrack same as pWnd */
410     if (lpEventTrack->dwFlags & TME_CANCEL) // Canceled mode.
411     {
412         if (lpEventTrack->dwFlags & TME_LEAVE)
413             pDesk->dwDTFlags &= ~DF_TME_LEAVE;
414 
415         if (lpEventTrack->dwFlags & TME_HOVER)
416         {
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;
421             }
422         }
423     }
424     else // Not Canceled.
425     {
426         if (lpEventTrack->dwFlags & TME_LEAVE)
427             pDesk->dwDTFlags |= DF_TME_LEAVE;
428 
429         if (lpEventTrack->dwFlags & TME_HOVER)
430         {
431             pDesk->dwDTFlags |= DF_TME_HOVER;
432 
433             if (!lpEventTrack->dwHoverTime || lpEventTrack->dwHoverTime == HOVER_DEFAULT)
434                 pDesk->dwMouseHoverTime = gspv.iMouseHoverTime; // use the system default hover time-out.
435             else
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);
447         }
448     }
449     return TRUE;
450 }
451 
452 BOOL
453 APIENTRY
454 NtUserTrackMouseEvent(
455     LPTRACKMOUSEEVENT lpEventTrack)
456 {
457     TRACKMOUSEEVENT SafeTME;
458     BOOL bRet = FALSE;
459 
460     TRACE("Enter NtUserTrackMouseEvent\n");
461 
462     _SEH2_TRY
463     {
464         ProbeForRead(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
465         RtlCopyMemory(&SafeTME, lpEventTrack, sizeof(TRACKMOUSEEVENT));
466     }
467     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
468     {
469         SetLastNtError(_SEH2_GetExceptionCode());
470         _SEH2_YIELD(return FALSE);
471     }
472     _SEH2_END;
473 
474     if (SafeTME.cbSize != sizeof(TRACKMOUSEEVENT))
475     {
476         EngSetLastError(ERROR_INVALID_PARAMETER);
477         return FALSE;
478     }
479 
480     if (SafeTME.dwFlags & ~(TME_CANCEL | TME_QUERY | TME_NONCLIENT | TME_LEAVE | TME_HOVER) )
481     {
482         EngSetLastError(ERROR_INVALID_FLAGS);
483         return FALSE;
484     }
485 
486     UserEnterExclusive();
487 
488     if (SafeTME.dwFlags & TME_QUERY)
489     {
490         bRet = IntQueryTrackMouseEvent(&SafeTME);
491         _SEH2_TRY
492         {
493             ProbeForWrite(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
494             RtlCopyMemory(lpEventTrack, &SafeTME, sizeof(TRACKMOUSEEVENT));
495         }
496         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
497         {
498             SetLastNtError(_SEH2_GetExceptionCode());
499             bRet = FALSE;
500         }
501         _SEH2_END;
502     }
503     else
504     {
505         bRet = IntTrackMouseEvent(&SafeTME);
506     }
507 
508     TRACE("Leave NtUserTrackMouseEvent, ret=%i\n", bRet);
509     UserLeave();
510     return bRet;
511 }
512 
513 DWORD
514 APIENTRY
515 NtUserGetMouseMovePointsEx(
516     UINT cbSize,
517     LPMOUSEMOVEPOINT lpptIn,
518     LPMOUSEMOVEPOINT lpptOut,
519     int nBufPoints,
520     DWORD resolution)
521 {
522     MOUSEMOVEPOINT Safeppt;
523     //BOOL Hit;
524     INT iRet = -1;
525 
526     TRACE("Enter NtUserGetMouseMovePointsEx\n");
527 
528     if ((cbSize != sizeof(MOUSEMOVEPOINT)) || (nBufPoints < 0) || (nBufPoints > 64))
529     {
530         EngSetLastError(ERROR_INVALID_PARAMETER);
531         return (DWORD)-1;
532     }
533 
534     if (!lpptIn || (!lpptOut && nBufPoints))
535     {
536         EngSetLastError(ERROR_NOACCESS);
537         return (DWORD)-1;
538     }
539 
540     _SEH2_TRY
541     {
542         ProbeForRead(lpptIn, cbSize, 1);
543         RtlCopyMemory(&Safeppt, lpptIn, cbSize);
544     }
545     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
546     {
547         SetLastNtError(_SEH2_GetExceptionCode());
548         _SEH2_YIELD(return (DWORD)-1);
549     }
550     _SEH2_END;
551 
552     UserEnterShared();
553 
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.
557     //Hit = FALSE;
558     do
559     {
560         if (Safeppt.x == 0 && Safeppt.y == 0)
561             break; // No test.
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)
564         {
565             if (Safeppt.time) // Now test time and it seems to be absolute.
566             {
567                 if (Safeppt.time == gMouseHistoryOfMoves[iRet].time)
568                 {
569                     //Hit = TRUE;
570                     break;
571                 }
572                 else
573                 {
574                     if (--iRet < 0) iRet = 63;
575                     continue;
576                 }
577             }
578             //Hit = TRUE;
579             break;
580         }
581         if (--iRet < 0) iRet = 63;
582     }
583     while (iRet != gcMouseHistoryOfMoves);
584 
585     switch(resolution)
586     {
587         case GMMP_USE_DISPLAY_POINTS:
588             if (nBufPoints)
589             {
590                 _SEH2_TRY
591                 {
592                     ProbeForWrite(lpptOut, cbSize, 1);
593                 }
594                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
595                 {
596                     SetLastNtError(_SEH2_GetExceptionCode());
597                     iRet = -1;
598                     _SEH2_YIELD(goto cleanup);
599                 }
600                 _SEH2_END;
601             }
602             iRet = nBufPoints;
603             break;
604         case GMMP_USE_HIGH_RESOLUTION_POINTS:
605             break;
606         default:
607             EngSetLastError(ERROR_POINT_NOT_FOUND);
608             iRet = -1;
609     }
610 
611 cleanup:
612     TRACE("Leave NtUserGetMouseMovePointsEx, ret=%i\n", iRet);
613     UserLeave();
614     return (DWORD)iRet;
615 }
616