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