xref: /reactos/win32ss/user/ntuser/mouse.c (revision 5100859e)
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 VOID
346 FASTCALL
347 IntRemoveTrackMouseEvent(
348     PDESKTOP pDesk)
349 {
350     /* Generate a leave message */
351     if (pDesk->dwDTFlags & DF_TME_LEAVE)
352     {
353         UINT uMsg = (pDesk->htEx != HTCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE;
354         UserPostMessage(UserHMGetHandle(pDesk->spwndTrack), uMsg, 0, 0);
355     }
356     /* Kill the timer */
357     if (pDesk->dwDTFlags & DF_TME_HOVER)
358         IntKillTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
359 
360     /* Reset state */
361     pDesk->dwDTFlags &= ~(DF_TME_LEAVE|DF_TME_HOVER);
362     pDesk->spwndTrack = NULL;
363 }
364 
365 BOOL
366 FASTCALL
367 IntQueryTrackMouseEvent(
368     LPTRACKMOUSEEVENT lpEventTrack)
369 {
370     PDESKTOP pDesk;
371     PTHREADINFO pti;
372 
373     pti = PsGetCurrentThreadWin32Thread();
374     pDesk = pti->rpdesk;
375 
376     /* Always cleared with size set and return true. */
377     RtlZeroMemory(lpEventTrack , sizeof(TRACKMOUSEEVENT));
378     lpEventTrack->cbSize = sizeof(TRACKMOUSEEVENT);
379 
380     if (pDesk->dwDTFlags & (DF_TME_LEAVE | DF_TME_HOVER) &&
381         pDesk->spwndTrack &&
382         pti->MessageQueue == pDesk->spwndTrack->head.pti->MessageQueue)
383     {
384         if (pDesk->htEx != HTCLIENT)
385             lpEventTrack->dwFlags |= TME_NONCLIENT;
386 
387         if (pDesk->dwDTFlags & DF_TME_LEAVE)
388             lpEventTrack->dwFlags |= TME_LEAVE;
389 
390         if (pDesk->dwDTFlags & DF_TME_HOVER)
391         {
392             lpEventTrack->dwFlags |= TME_HOVER;
393             lpEventTrack->dwHoverTime = pDesk->dwMouseHoverTime;
394         }
395         lpEventTrack->hwndTrack = UserHMGetHandle(pDesk->spwndTrack);
396     }
397     return TRUE;
398 }
399 
400 BOOL
401 FASTCALL
402 IntTrackMouseEvent(
403     LPTRACKMOUSEEVENT lpEventTrack)
404 {
405     PDESKTOP pDesk;
406     PTHREADINFO pti;
407     PWND pWnd;
408     POINT point;
409 
410     pti = PsGetCurrentThreadWin32Thread();
411     pDesk = pti->rpdesk;
412 
413     if (!(pWnd = UserGetWindowObject(lpEventTrack->hwndTrack)))
414         return FALSE;
415 
416     if ( pDesk->spwndTrack != pWnd ||
417             (pDesk->htEx != HTCLIENT) ^ !!(lpEventTrack->dwFlags & TME_NONCLIENT) )
418     {
419         if ( lpEventTrack->dwFlags & TME_LEAVE && !(lpEventTrack->dwFlags & TME_CANCEL) )
420         {
421             UserPostMessage( lpEventTrack->hwndTrack,
422                              lpEventTrack->dwFlags & TME_NONCLIENT ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
423                              0, 0);
424         }
425         TRACE("IntTrackMouseEvent spwndTrack %p pwnd %p\n", pDesk->spwndTrack, pWnd);
426         return TRUE;
427     }
428 
429     /* Tracking spwndTrack same as pWnd */
430     if (lpEventTrack->dwFlags & TME_CANCEL) // Canceled mode.
431     {
432         if (lpEventTrack->dwFlags & TME_LEAVE)
433             pDesk->dwDTFlags &= ~DF_TME_LEAVE;
434 
435         if (lpEventTrack->dwFlags & TME_HOVER)
436         {
437             if (pDesk->dwDTFlags & DF_TME_HOVER)
438             {   // Kill hover timer.
439                 IntKillTimer(pWnd, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
440                 pDesk->dwDTFlags &= ~DF_TME_HOVER;
441             }
442         }
443     }
444     else // Not Canceled.
445     {
446         if (lpEventTrack->dwFlags & TME_LEAVE)
447             pDesk->dwDTFlags |= DF_TME_LEAVE;
448 
449         if (lpEventTrack->dwFlags & TME_HOVER)
450         {
451             pDesk->dwDTFlags |= DF_TME_HOVER;
452 
453             if (!lpEventTrack->dwHoverTime || lpEventTrack->dwHoverTime == HOVER_DEFAULT)
454                 pDesk->dwMouseHoverTime = gspv.iMouseHoverTime; // use the system default hover time-out.
455             else
456                 pDesk->dwMouseHoverTime = lpEventTrack->dwHoverTime;
457             // Start timer for the hover period.
458             IntSetTimer(pWnd, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
459             // Get windows thread message points.
460             point = pWnd->head.pti->ptLast;
461             // Set desktop mouse hover from the system default hover rectangle.
462             RECTL_vSetRect(&pDesk->rcMouseHover,
463                            point.x - gspv.iMouseHoverWidth  / 2,
464                            point.y - gspv.iMouseHoverHeight / 2,
465                            point.x + gspv.iMouseHoverWidth  / 2,
466                            point.y + gspv.iMouseHoverHeight / 2);
467         }
468     }
469     return TRUE;
470 }
471 
472 BOOL
473 APIENTRY
474 NtUserTrackMouseEvent(
475     LPTRACKMOUSEEVENT lpEventTrack)
476 {
477     TRACKMOUSEEVENT SafeTME;
478     BOOL bRet = FALSE;
479 
480     TRACE("Enter NtUserTrackMouseEvent\n");
481 
482     _SEH2_TRY
483     {
484         ProbeForRead(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
485         RtlCopyMemory(&SafeTME, lpEventTrack, sizeof(TRACKMOUSEEVENT));
486     }
487     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
488     {
489         SetLastNtError(_SEH2_GetExceptionCode());
490         _SEH2_YIELD(return FALSE);
491     }
492     _SEH2_END;
493 
494     if (SafeTME.cbSize != sizeof(TRACKMOUSEEVENT))
495     {
496         EngSetLastError(ERROR_INVALID_PARAMETER);
497         return FALSE;
498     }
499 
500     if (SafeTME.dwFlags & ~(TME_CANCEL | TME_QUERY | TME_NONCLIENT | TME_LEAVE | TME_HOVER) )
501     {
502         EngSetLastError(ERROR_INVALID_FLAGS);
503         return FALSE;
504     }
505 
506     UserEnterExclusive();
507 
508     if (SafeTME.dwFlags & TME_QUERY)
509     {
510         bRet = IntQueryTrackMouseEvent(&SafeTME);
511         _SEH2_TRY
512         {
513             ProbeForWrite(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
514             RtlCopyMemory(lpEventTrack, &SafeTME, sizeof(TRACKMOUSEEVENT));
515         }
516         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
517         {
518             SetLastNtError(_SEH2_GetExceptionCode());
519             bRet = FALSE;
520         }
521         _SEH2_END;
522     }
523     else
524     {
525         bRet = IntTrackMouseEvent(&SafeTME);
526     }
527 
528     TRACE("Leave NtUserTrackMouseEvent, ret=%i\n", bRet);
529     UserLeave();
530     return bRet;
531 }
532 
533 DWORD
534 APIENTRY
535 NtUserGetMouseMovePointsEx(
536     UINT cbSize,
537     LPMOUSEMOVEPOINT lpptIn,
538     LPMOUSEMOVEPOINT lpptOut,
539     int nBufPoints,
540     DWORD resolution)
541 {
542     MOUSEMOVEPOINT Safeppt;
543     //BOOL Hit;
544     INT iRet = -1;
545 
546     TRACE("Enter NtUserGetMouseMovePointsEx\n");
547 
548     if ((cbSize != sizeof(MOUSEMOVEPOINT)) || (nBufPoints < 0) || (nBufPoints > 64))
549     {
550         EngSetLastError(ERROR_INVALID_PARAMETER);
551         return (DWORD)-1;
552     }
553 
554     if (!lpptIn || (!lpptOut && nBufPoints))
555     {
556         EngSetLastError(ERROR_NOACCESS);
557         return (DWORD)-1;
558     }
559 
560     _SEH2_TRY
561     {
562         ProbeForRead(lpptIn, cbSize, 1);
563         RtlCopyMemory(&Safeppt, lpptIn, cbSize);
564     }
565     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
566     {
567         SetLastNtError(_SEH2_GetExceptionCode());
568         _SEH2_YIELD(return (DWORD)-1);
569     }
570     _SEH2_END;
571 
572     UserEnterShared();
573 
574     // http://msdn.microsoft.com/en-us/library/ms646259(v=vs.85).aspx
575     // This explains the math issues in transforming points.
576     iRet = gcMouseHistoryOfMoves; // FIFO is forward so retrieve backward.
577     //Hit = FALSE;
578     do
579     {
580         if (Safeppt.x == 0 && Safeppt.y == 0)
581             break; // No test.
582         // Finds the point, it returns the last nBufPoints prior to and including the supplied point.
583         if (gMouseHistoryOfMoves[iRet].x == Safeppt.x && gMouseHistoryOfMoves[iRet].y == Safeppt.y)
584         {
585             if (Safeppt.time) // Now test time and it seems to be absolute.
586             {
587                 if (Safeppt.time == gMouseHistoryOfMoves[iRet].time)
588                 {
589                     //Hit = TRUE;
590                     break;
591                 }
592                 else
593                 {
594                     if (--iRet < 0) iRet = 63;
595                     continue;
596                 }
597             }
598             //Hit = TRUE;
599             break;
600         }
601         if (--iRet < 0) iRet = 63;
602     }
603     while (iRet != gcMouseHistoryOfMoves);
604 
605     switch(resolution)
606     {
607         case GMMP_USE_DISPLAY_POINTS:
608             if (nBufPoints)
609             {
610                 _SEH2_TRY
611                 {
612                     ProbeForWrite(lpptOut, cbSize, 1);
613                 }
614                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
615                 {
616                     SetLastNtError(_SEH2_GetExceptionCode());
617                     iRet = -1;
618                     _SEH2_YIELD(goto cleanup);
619                 }
620                 _SEH2_END;
621             }
622             iRet = nBufPoints;
623             break;
624         case GMMP_USE_HIGH_RESOLUTION_POINTS:
625             break;
626         default:
627             EngSetLastError(ERROR_POINT_NOT_FOUND);
628             iRet = -1;
629     }
630 
631 cleanup:
632     TRACE("Leave NtUserGetMouseMovePointsEx, ret=%i\n", iRet);
633     UserLeave();
634     return (DWORD)iRet;
635 }
636