xref: /reactos/win32ss/user/ntuser/mouse.c (revision 94a42d43)
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         Msg.time = EngGetTickCount32();
214     }
215 
216     /* Do GetMouseMovePointsEx FIFO. */
217     gMouseHistoryOfMoves[gcMouseHistoryOfMoves].x = ptCursor.x;
218     gMouseHistoryOfMoves[gcMouseHistoryOfMoves].y = ptCursor.y;
219     gMouseHistoryOfMoves[gcMouseHistoryOfMoves].time = Msg.time;
220     gMouseHistoryOfMoves[gcMouseHistoryOfMoves].dwExtraInfo = pmi->dwExtraInfo;
221     if (++gcMouseHistoryOfMoves == ARRAYSIZE(gMouseHistoryOfMoves))
222        gcMouseHistoryOfMoves = 0; // 0 - 63 is 64, FIFO forwards.
223 
224     /* Update cursor position */
225     if (dwFlags & MOUSEEVENTF_MOVE)
226     {
227         UserSetCursorPos(ptCursor.x, ptCursor.y, bInjected, pmi->dwExtraInfo, TRUE);
228     }
229 
230     /* Left button */
231     if (dwFlags & MOUSEEVENTF_LEFTDOWN)
232     {
233         SET_KEY_DOWN(gafAsyncKeyState, VK_LBUTTON, TRUE);
234         Msg.message = WM_LBUTTONDOWN;
235         pCurInfo->ButtonsDown |= MK_LBUTTON;
236         Msg.wParam |= MK_LBUTTON;
237         co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
238     }
239     else if (dwFlags & MOUSEEVENTF_LEFTUP)
240     {
241         SET_KEY_DOWN(gafAsyncKeyState, VK_LBUTTON, FALSE);
242         Msg.message = WM_LBUTTONUP;
243         pCurInfo->ButtonsDown &= ~MK_LBUTTON;
244         Msg.wParam &= ~MK_LBUTTON;
245         co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
246     }
247 
248     /* Middle button */
249     if (dwFlags & MOUSEEVENTF_MIDDLEDOWN)
250     {
251         SET_KEY_DOWN(gafAsyncKeyState, VK_MBUTTON, TRUE);
252         Msg.message = WM_MBUTTONDOWN;
253         pCurInfo->ButtonsDown |= MK_MBUTTON;
254         Msg.wParam |= MK_MBUTTON;
255         co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
256     }
257     else if (dwFlags & MOUSEEVENTF_MIDDLEUP)
258     {
259         SET_KEY_DOWN(gafAsyncKeyState, VK_MBUTTON, FALSE);
260         Msg.message = WM_MBUTTONUP;
261         pCurInfo->ButtonsDown &= ~MK_MBUTTON;
262         Msg.wParam &= ~MK_MBUTTON;
263         co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
264     }
265 
266     /* Right button */
267     if (dwFlags & MOUSEEVENTF_RIGHTDOWN)
268     {
269         SET_KEY_DOWN(gafAsyncKeyState, VK_RBUTTON, TRUE);
270         Msg.message = WM_RBUTTONDOWN;
271         pCurInfo->ButtonsDown |= MK_RBUTTON;
272         Msg.wParam |= MK_RBUTTON;
273         co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
274     }
275     else if (dwFlags & MOUSEEVENTF_RIGHTUP)
276     {
277         SET_KEY_DOWN(gafAsyncKeyState, VK_RBUTTON, FALSE);
278         Msg.message = WM_RBUTTONUP;
279         pCurInfo->ButtonsDown &= ~MK_RBUTTON;
280         Msg.wParam &= ~MK_RBUTTON;
281         co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
282     }
283 
284     if((dwFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)) &&
285        (dwFlags & MOUSEEVENTF_WHEEL))
286     {
287         /* Fail because both types of events use the mouseData field */
288         WARN("Invalid flags!\n");
289         return FALSE;
290     }
291 
292     /* X-Button (4 or 5) */
293     if (dwFlags & MOUSEEVENTF_XDOWN)
294     {
295         Msg.message = WM_XBUTTONDOWN;
296         if (pmi->mouseData & XBUTTON1)
297         {
298             SET_KEY_DOWN(gafAsyncKeyState, VK_XBUTTON1, TRUE);
299             pCurInfo->ButtonsDown |= MK_XBUTTON1;
300             Msg.wParam |= MAKEWPARAM(MK_XBUTTON1, XBUTTON1);
301             co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
302         }
303         if (pmi->mouseData & XBUTTON2)
304         {
305             SET_KEY_DOWN(gafAsyncKeyState, VK_XBUTTON2, TRUE);
306             pCurInfo->ButtonsDown |= MK_XBUTTON2;
307             Msg.wParam |= MAKEWPARAM(MK_XBUTTON2, XBUTTON2);
308             co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
309         }
310     }
311     else if (dwFlags & MOUSEEVENTF_XUP)
312     {
313         Msg.message = WM_XBUTTONUP;
314         if(pmi->mouseData & XBUTTON1)
315         {
316             SET_KEY_DOWN(gafAsyncKeyState, VK_XBUTTON1, FALSE);
317             pCurInfo->ButtonsDown &= ~MK_XBUTTON1;
318             Msg.wParam &= ~MK_XBUTTON1;
319             Msg.wParam |= MAKEWPARAM(0, XBUTTON2);
320             co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
321         }
322         if (pmi->mouseData & XBUTTON2)
323         {
324             SET_KEY_DOWN(gafAsyncKeyState, VK_XBUTTON2, FALSE);
325             pCurInfo->ButtonsDown &= ~MK_XBUTTON2;
326             Msg.wParam &= ~MK_XBUTTON2;
327             Msg.wParam |= MAKEWPARAM(0, XBUTTON2);
328             co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
329         }
330     }
331 
332     /* Mouse wheel */
333     if (dwFlags & MOUSEEVENTF_WHEEL)
334     {
335         Msg.message = WM_MOUSEWHEEL;
336         Msg.wParam = MAKEWPARAM(pCurInfo->ButtonsDown, pmi->mouseData);
337         co_MsqInsertMouseMessage(&Msg, bInjected, pmi->dwExtraInfo, TRUE);
338     }
339 
340     return TRUE;
341 }
342 
343 VOID
344 FASTCALL
345 IntRemoveTrackMouseEvent(
346     PDESKTOP pDesk)
347 {
348     /* Generate a leave message */
349     if (pDesk->dwDTFlags & DF_TME_LEAVE)
350     {
351         UINT uMsg = (pDesk->htEx != HTCLIENT) ? WM_NCMOUSELEAVE : WM_MOUSELEAVE;
352         UserPostMessage(UserHMGetHandle(pDesk->spwndTrack), uMsg, 0, 0);
353     }
354     /* Kill the timer */
355     if (pDesk->dwDTFlags & DF_TME_HOVER)
356         IntKillTimer(pDesk->spwndTrack, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
357 
358     /* Reset state */
359     pDesk->dwDTFlags &= ~(DF_TME_LEAVE|DF_TME_HOVER);
360     pDesk->spwndTrack = NULL;
361 }
362 
363 BOOL
364 FASTCALL
365 IntQueryTrackMouseEvent(
366     LPTRACKMOUSEEVENT lpEventTrack)
367 {
368     PDESKTOP pDesk;
369     PTHREADINFO pti;
370 
371     pti = PsGetCurrentThreadWin32Thread();
372     pDesk = pti->rpdesk;
373 
374     /* Always cleared with size set and return true. */
375     RtlZeroMemory(lpEventTrack , sizeof(TRACKMOUSEEVENT));
376     lpEventTrack->cbSize = sizeof(TRACKMOUSEEVENT);
377 
378     if (pDesk->dwDTFlags & (DF_TME_LEAVE | DF_TME_HOVER) &&
379         pDesk->spwndTrack &&
380         pti->MessageQueue == pDesk->spwndTrack->head.pti->MessageQueue)
381     {
382         if (pDesk->htEx != HTCLIENT)
383             lpEventTrack->dwFlags |= TME_NONCLIENT;
384 
385         if (pDesk->dwDTFlags & DF_TME_LEAVE)
386             lpEventTrack->dwFlags |= TME_LEAVE;
387 
388         if (pDesk->dwDTFlags & DF_TME_HOVER)
389         {
390             lpEventTrack->dwFlags |= TME_HOVER;
391             lpEventTrack->dwHoverTime = pDesk->dwMouseHoverTime;
392         }
393         lpEventTrack->hwndTrack = UserHMGetHandle(pDesk->spwndTrack);
394     }
395     return TRUE;
396 }
397 
398 BOOL
399 FASTCALL
400 IntTrackMouseEvent(
401     LPTRACKMOUSEEVENT lpEventTrack)
402 {
403     PDESKTOP pDesk;
404     PTHREADINFO pti;
405     PWND pWnd;
406     POINT point;
407 
408     pti = PsGetCurrentThreadWin32Thread();
409     pDesk = pti->rpdesk;
410 
411     if (!(pWnd = UserGetWindowObject(lpEventTrack->hwndTrack)))
412         return FALSE;
413 
414     if ( pDesk->spwndTrack != pWnd ||
415             (pDesk->htEx != HTCLIENT) ^ !!(lpEventTrack->dwFlags & TME_NONCLIENT) )
416     {
417         if ( lpEventTrack->dwFlags & TME_LEAVE && !(lpEventTrack->dwFlags & TME_CANCEL) )
418         {
419             UserPostMessage( lpEventTrack->hwndTrack,
420                              lpEventTrack->dwFlags & TME_NONCLIENT ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
421                              0, 0);
422         }
423         TRACE("IntTrackMouseEvent spwndTrack %p pwnd %p\n", pDesk->spwndTrack, pWnd);
424         return TRUE;
425     }
426 
427     /* Tracking spwndTrack same as pWnd */
428     if (lpEventTrack->dwFlags & TME_CANCEL) // Canceled mode.
429     {
430         if (lpEventTrack->dwFlags & TME_LEAVE)
431             pDesk->dwDTFlags &= ~DF_TME_LEAVE;
432 
433         if (lpEventTrack->dwFlags & TME_HOVER)
434         {
435             if (pDesk->dwDTFlags & DF_TME_HOVER)
436             {   // Kill hover timer.
437                 IntKillTimer(pWnd, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
438                 pDesk->dwDTFlags &= ~DF_TME_HOVER;
439             }
440         }
441     }
442     else // Not Canceled.
443     {
444         if (lpEventTrack->dwFlags & TME_LEAVE)
445             pDesk->dwDTFlags |= DF_TME_LEAVE;
446 
447         if (lpEventTrack->dwFlags & TME_HOVER)
448         {
449             pDesk->dwDTFlags |= DF_TME_HOVER;
450 
451             if (!lpEventTrack->dwHoverTime || lpEventTrack->dwHoverTime == HOVER_DEFAULT)
452                 pDesk->dwMouseHoverTime = gspv.iMouseHoverTime; // use the system default hover time-out.
453             else
454                 pDesk->dwMouseHoverTime = lpEventTrack->dwHoverTime;
455             // Start timer for the hover period.
456             IntSetTimer(pWnd, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
457             // Get windows thread message points.
458             point = pWnd->head.pti->ptLast;
459             // Set desktop mouse hover from the system default hover rectangle.
460             RECTL_vSetRect(&pDesk->rcMouseHover,
461                            point.x - gspv.iMouseHoverWidth  / 2,
462                            point.y - gspv.iMouseHoverHeight / 2,
463                            point.x + gspv.iMouseHoverWidth  / 2,
464                            point.y + gspv.iMouseHoverHeight / 2);
465         }
466     }
467     return TRUE;
468 }
469 
470 BOOL
471 APIENTRY
472 NtUserTrackMouseEvent(
473     LPTRACKMOUSEEVENT lpEventTrack)
474 {
475     TRACKMOUSEEVENT SafeTME;
476     BOOL bRet = FALSE;
477 
478     TRACE("Enter NtUserTrackMouseEvent\n");
479 
480     _SEH2_TRY
481     {
482         ProbeForRead(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
483         RtlCopyMemory(&SafeTME, lpEventTrack, sizeof(TRACKMOUSEEVENT));
484     }
485     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
486     {
487         SetLastNtError(_SEH2_GetExceptionCode());
488         _SEH2_YIELD(return FALSE);
489     }
490     _SEH2_END;
491 
492     if (SafeTME.cbSize != sizeof(TRACKMOUSEEVENT))
493     {
494         EngSetLastError(ERROR_INVALID_PARAMETER);
495         return FALSE;
496     }
497 
498     if (SafeTME.dwFlags & ~(TME_CANCEL | TME_QUERY | TME_NONCLIENT | TME_LEAVE | TME_HOVER) )
499     {
500         EngSetLastError(ERROR_INVALID_FLAGS);
501         return FALSE;
502     }
503 
504     UserEnterExclusive();
505 
506     if (SafeTME.dwFlags & TME_QUERY)
507     {
508         bRet = IntQueryTrackMouseEvent(&SafeTME);
509         _SEH2_TRY
510         {
511             ProbeForWrite(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
512             RtlCopyMemory(lpEventTrack, &SafeTME, sizeof(TRACKMOUSEEVENT));
513         }
514         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
515         {
516             SetLastNtError(_SEH2_GetExceptionCode());
517             bRet = FALSE;
518         }
519         _SEH2_END;
520     }
521     else
522     {
523         bRet = IntTrackMouseEvent(&SafeTME);
524     }
525 
526     TRACE("Leave NtUserTrackMouseEvent, ret=%i\n", bRet);
527     UserLeave();
528     return bRet;
529 }
530 
531 DWORD
532 APIENTRY
533 NtUserGetMouseMovePointsEx(
534     UINT cbSize,
535     LPMOUSEMOVEPOINT lpptIn,
536     LPMOUSEMOVEPOINT lpptOut,
537     int nBufPoints,
538     DWORD resolution)
539 {
540     MOUSEMOVEPOINT Safeppt;
541     //BOOL Hit;
542     INT iRet = -1;
543 
544     TRACE("Enter NtUserGetMouseMovePointsEx\n");
545 
546     if ((cbSize != sizeof(MOUSEMOVEPOINT)) || (nBufPoints < 0) || (nBufPoints > 64))
547     {
548         EngSetLastError(ERROR_INVALID_PARAMETER);
549         return (DWORD)-1;
550     }
551 
552     if (!lpptIn || (!lpptOut && nBufPoints))
553     {
554         EngSetLastError(ERROR_NOACCESS);
555         return (DWORD)-1;
556     }
557 
558     _SEH2_TRY
559     {
560         ProbeForRead(lpptIn, cbSize, 1);
561         RtlCopyMemory(&Safeppt, lpptIn, cbSize);
562     }
563     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
564     {
565         SetLastNtError(_SEH2_GetExceptionCode());
566         _SEH2_YIELD(return (DWORD)-1);
567     }
568     _SEH2_END;
569 
570     UserEnterShared();
571 
572     // http://msdn.microsoft.com/en-us/library/ms646259(v=vs.85).aspx
573     // This explains the math issues in transforming points.
574     iRet = gcMouseHistoryOfMoves; // FIFO is forward so retrieve backward.
575     //Hit = FALSE;
576     do
577     {
578         if (Safeppt.x == 0 && Safeppt.y == 0)
579             break; // No test.
580         // Finds the point, it returns the last nBufPoints prior to and including the supplied point.
581         if (gMouseHistoryOfMoves[iRet].x == Safeppt.x && gMouseHistoryOfMoves[iRet].y == Safeppt.y)
582         {
583             if (Safeppt.time) // Now test time and it seems to be absolute.
584             {
585                 if (Safeppt.time == gMouseHistoryOfMoves[iRet].time)
586                 {
587                     //Hit = TRUE;
588                     break;
589                 }
590                 else
591                 {
592                     if (--iRet < 0) iRet = 63;
593                     continue;
594                 }
595             }
596             //Hit = TRUE;
597             break;
598         }
599         if (--iRet < 0) iRet = 63;
600     }
601     while (iRet != gcMouseHistoryOfMoves);
602 
603     switch(resolution)
604     {
605         case GMMP_USE_DISPLAY_POINTS:
606             if (nBufPoints)
607             {
608                 _SEH2_TRY
609                 {
610                     ProbeForWrite(lpptOut, cbSize, 1);
611                 }
612                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
613                 {
614                     SetLastNtError(_SEH2_GetExceptionCode());
615                     iRet = -1;
616                     _SEH2_YIELD(goto cleanup);
617                 }
618                 _SEH2_END;
619             }
620             iRet = nBufPoints;
621             break;
622         case GMMP_USE_HIGH_RESOLUTION_POINTS:
623             break;
624         default:
625             EngSetLastError(ERROR_POINT_NOT_FOUND);
626             iRet = -1;
627     }
628 
629 cleanup:
630     TRACE("Leave NtUserGetMouseMovePointsEx, ret=%i\n", iRet);
631     UserLeave();
632     return (DWORD)iRet;
633 }
634