[WIN32K] Check if the window being destroyed is currently tracked. (#103)
[reactos.git] / win32ss / user / ntuser / mouse.c
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 }