0842e227e34520534376784c9f8cbafc32ef0961
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / input.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window classes
5 * FILE: subsystems/win32/win32k/ntuser/input.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 extern BYTE gKeyStateTable[];
14 extern NTSTATUS Win32kInitWin32Thread(PETHREAD Thread);
15 extern PPROCESSINFO ppiScrnSaver;
16
17 /* GLOBALS *******************************************************************/
18
19 PTHREADINFO ptiRawInput;
20 PTHREADINFO ptiKeyboard;
21 PTHREADINFO ptiMouse;
22 PKTIMER MasterTimer = NULL;
23 PATTACHINFO gpai = NULL;
24 HANDLE ghKeyboardDevice;
25
26 static DWORD LastInputTick = 0;
27 static HANDLE MouseDeviceHandle;
28 static HANDLE MouseThreadHandle;
29 static CLIENT_ID MouseThreadId;
30 static HANDLE KeyboardThreadHandle;
31 static CLIENT_ID KeyboardThreadId;
32 static HANDLE RawInputThreadHandle;
33 static CLIENT_ID RawInputThreadId;
34 static KEVENT InputThreadsStart;
35 static BOOLEAN InputThreadsRunning = FALSE;
36
37 /* FUNCTIONS *****************************************************************/
38
39 #define ClearMouseInput(mi) \
40 mi.dx = 0; \
41 mi.dy = 0; \
42 mi.mouseData = 0; \
43 mi.dwFlags = 0;
44
45 #define SendMouseEvent(mi) \
46 if(mi.dx != 0 || mi.dy != 0) \
47 mi.dwFlags |= MOUSEEVENTF_MOVE; \
48 if(mi.dwFlags) \
49 IntMouseInput(&mi,FALSE); \
50 ClearMouseInput(mi);
51
52 static DWORD FASTCALL
53 IntLastInputTick(BOOL bUpdate)
54 {
55 if (bUpdate)
56 {
57 LARGE_INTEGER TickCount;
58 KeQueryTickCount(&TickCount);
59 LastInputTick = MsqCalculateMessageTime(&TickCount);
60 if (gpsi) gpsi->dwLastRITEventTickCount = LastInputTick;
61 }
62 return LastInputTick;
63 }
64
65 VOID FASTCALL
66 DoTheScreenSaver(VOID)
67 {
68 LARGE_INTEGER TickCount;
69 DWORD Test, TO;
70
71 if (gspv.iScrSaverTimeout > 0) // Zero means Off.
72 {
73 KeQueryTickCount(&TickCount);
74 Test = MsqCalculateMessageTime(&TickCount);
75 Test = Test - LastInputTick;
76 TO = 1000 * gspv.iScrSaverTimeout;
77 if (Test > TO)
78 {
79 TRACE("Screensaver Message Start! Tick %d Timeout %d \n", Test, gspv.iScrSaverTimeout);
80
81 if (ppiScrnSaver) // We are or we are not the screensaver, prevent reentry...
82 {
83 if (!(ppiScrnSaver->W32PF_flags & W32PF_IDLESCREENSAVER))
84 {
85 ppiScrnSaver->W32PF_flags |= W32PF_IDLESCREENSAVER;
86 ERR("Screensaver is Idle\n");
87 }
88 }
89 else
90 {
91 PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
92 if (ForegroundQueue && ForegroundQueue->ActiveWindow)
93 UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 1); // lParam 1 == Secure
94 else
95 UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_START_SCREENSAVE, 0);
96 }
97 }
98 }
99 }
100
101 static VOID NTAPI
102 IntProcessMouseInputData(PMOUSE_INPUT_DATA Data, ULONG InputCount)
103 {
104 PMOUSE_INPUT_DATA mid;
105 MOUSEINPUT mi;
106 ULONG i;
107
108 ClearMouseInput(mi);
109 mi.time = 0;
110 mi.dwExtraInfo = 0;
111 for(i = 0; i < InputCount; i++)
112 {
113 mid = (Data + i);
114 mi.dx += mid->LastX;
115 mi.dy += mid->LastY;
116
117 /* Check if the mouse move is absolute */
118 if (mid->Flags == MOUSE_MOVE_ABSOLUTE)
119 {
120 /* Set flag to convert to screen location */
121 mi.dwFlags |= MOUSEEVENTF_ABSOLUTE;
122 }
123
124 if(mid->ButtonFlags)
125 {
126 if(mid->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN)
127 {
128 mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
129 SendMouseEvent(mi);
130 }
131 if(mid->ButtonFlags & MOUSE_LEFT_BUTTON_UP)
132 {
133 mi.dwFlags |= MOUSEEVENTF_LEFTUP;
134 SendMouseEvent(mi);
135 }
136 if(mid->ButtonFlags & MOUSE_MIDDLE_BUTTON_DOWN)
137 {
138 mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
139 SendMouseEvent(mi);
140 }
141 if(mid->ButtonFlags & MOUSE_MIDDLE_BUTTON_UP)
142 {
143 mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
144 SendMouseEvent(mi);
145 }
146 if(mid->ButtonFlags & MOUSE_RIGHT_BUTTON_DOWN)
147 {
148 mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
149 SendMouseEvent(mi);
150 }
151 if(mid->ButtonFlags & MOUSE_RIGHT_BUTTON_UP)
152 {
153 mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
154 SendMouseEvent(mi);
155 }
156 if(mid->ButtonFlags & MOUSE_BUTTON_4_DOWN)
157 {
158 mi.mouseData |= XBUTTON1;
159 mi.dwFlags |= MOUSEEVENTF_XDOWN;
160 SendMouseEvent(mi);
161 }
162 if(mid->ButtonFlags & MOUSE_BUTTON_4_UP)
163 {
164 mi.mouseData |= XBUTTON1;
165 mi.dwFlags |= MOUSEEVENTF_XUP;
166 SendMouseEvent(mi);
167 }
168 if(mid->ButtonFlags & MOUSE_BUTTON_5_DOWN)
169 {
170 mi.mouseData |= XBUTTON2;
171 mi.dwFlags |= MOUSEEVENTF_XDOWN;
172 SendMouseEvent(mi);
173 }
174 if(mid->ButtonFlags & MOUSE_BUTTON_5_UP)
175 {
176 mi.mouseData |= XBUTTON2;
177 mi.dwFlags |= MOUSEEVENTF_XUP;
178 SendMouseEvent(mi);
179 }
180 if(mid->ButtonFlags & MOUSE_WHEEL)
181 {
182 mi.mouseData = mid->ButtonData;
183 mi.dwFlags |= MOUSEEVENTF_WHEEL;
184 SendMouseEvent(mi);
185 }
186 }
187 }
188
189 SendMouseEvent(mi);
190 }
191
192 static VOID APIENTRY
193 MouseThreadMain(PVOID StartContext)
194 {
195 UNICODE_STRING MouseDeviceName = RTL_CONSTANT_STRING(L"\\Device\\PointerClass0");
196 OBJECT_ATTRIBUTES MouseObjectAttributes;
197 IO_STATUS_BLOCK Iosb;
198 NTSTATUS Status;
199 MOUSE_ATTRIBUTES MouseAttr;
200
201 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
202 LOW_REALTIME_PRIORITY + 3);
203
204 InitializeObjectAttributes(&MouseObjectAttributes,
205 &MouseDeviceName,
206 0,
207 NULL,
208 NULL);
209 do
210 {
211 LARGE_INTEGER DueTime;
212 KEVENT Event;
213 DueTime.QuadPart = (LONGLONG)(-10000000);
214 KeInitializeEvent(&Event, NotificationEvent, FALSE);
215 Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
216 Status = NtOpenFile(&MouseDeviceHandle,
217 FILE_ALL_ACCESS,
218 &MouseObjectAttributes,
219 &Iosb,
220 0,
221 FILE_SYNCHRONOUS_IO_ALERT);
222 } while (!NT_SUCCESS(Status));
223
224 /* Need to setup basic win32k for this thread to process WH_MOUSE_LL messages. */
225 Status = Win32kInitWin32Thread(PsGetCurrentThread());
226 if (!NT_SUCCESS(Status))
227 {
228 ERR("Win32K: Failed making mouse thread a win32 thread.\n");
229 return; //(Status);
230 }
231
232 ptiMouse = PsGetCurrentThreadWin32Thread();
233 ptiMouse->TIF_flags |= TIF_SYSTEMTHREAD;
234 TRACE("Mouse Thread 0x%x \n", ptiMouse);
235
236 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
237 LOW_REALTIME_PRIORITY + 3);
238
239 for(;;)
240 {
241 /*
242 * Wait to start input.
243 */
244 TRACE("Mouse Input Thread Waiting for start event\n");
245 Status = KeWaitForSingleObject(&InputThreadsStart,
246 0,
247 KernelMode,
248 TRUE,
249 NULL);
250 TRACE("Mouse Input Thread Starting...\n");
251
252 /*FIXME: Does mouse attributes need to be used for anything */
253 Status = NtDeviceIoControlFile(MouseDeviceHandle,
254 NULL,
255 NULL,
256 NULL,
257 &Iosb,
258 IOCTL_MOUSE_QUERY_ATTRIBUTES,
259 &MouseAttr, sizeof(MOUSE_ATTRIBUTES),
260 NULL, 0);
261 if(!NT_SUCCESS(Status))
262 {
263 TRACE("Failed to get mouse attributes\n");
264 }
265
266 /*
267 * Receive and process mouse input.
268 */
269 while(InputThreadsRunning)
270 {
271 MOUSE_INPUT_DATA MouseInput;
272 Status = NtReadFile(MouseDeviceHandle,
273 NULL,
274 NULL,
275 NULL,
276 &Iosb,
277 &MouseInput,
278 sizeof(MOUSE_INPUT_DATA),
279 NULL,
280 NULL);
281 if(Status == STATUS_ALERTED && !InputThreadsRunning)
282 {
283 break;
284 }
285 if(Status == STATUS_PENDING)
286 {
287 NtWaitForSingleObject(MouseDeviceHandle, FALSE, NULL);
288 Status = Iosb.Status;
289 }
290 if(!NT_SUCCESS(Status))
291 {
292 ERR("Win32K: Failed to read from mouse.\n");
293 return; //(Status);
294 }
295 TRACE("MouseEvent\n");
296 IntLastInputTick(TRUE);
297
298 UserEnterExclusive();
299
300 IntProcessMouseInputData(&MouseInput, Iosb.Information / sizeof(MOUSE_INPUT_DATA));
301
302 UserLeave();
303 }
304 TRACE("Mouse Input Thread Stopped...\n");
305 }
306 }
307
308 static VOID APIENTRY
309 KeyboardThreadMain(PVOID StartContext)
310 {
311 UNICODE_STRING KeyboardDeviceName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardClass0");
312 OBJECT_ATTRIBUTES KeyboardObjectAttributes;
313 IO_STATUS_BLOCK Iosb;
314 NTSTATUS Status;
315
316 InitializeObjectAttributes(&KeyboardObjectAttributes,
317 &KeyboardDeviceName,
318 0,
319 NULL,
320 NULL);
321 do
322 {
323 LARGE_INTEGER DueTime;
324 KEVENT Event;
325 DueTime.QuadPart = (LONGLONG)(-10000000);
326 KeInitializeEvent(&Event, NotificationEvent, FALSE);
327 Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
328 Status = NtOpenFile(&ghKeyboardDevice,
329 FILE_ALL_ACCESS,
330 &KeyboardObjectAttributes,
331 &Iosb,
332 0,
333 FILE_SYNCHRONOUS_IO_ALERT);
334 } while (!NT_SUCCESS(Status));
335
336 /* Not sure if converting this thread to a win32 thread is such
337 a great idea. Since we're posting keyboard messages to the focus
338 window message queue, we'll be (indirectly) doing sendmessage
339 stuff from this thread (for WH_KEYBOARD_LL processing), which
340 means we need our own message queue. If keyboard messages were
341 instead queued to the system message queue, the thread removing
342 the message from the system message queue would be responsible
343 for WH_KEYBOARD_LL processing and we wouldn't need this thread
344 to be a win32 thread. */
345 Status = Win32kInitWin32Thread(PsGetCurrentThread());
346 if (!NT_SUCCESS(Status))
347 {
348 ERR("Win32K: Failed making keyboard thread a win32 thread.\n");
349 return; //(Status);
350 }
351
352 ptiKeyboard = PsGetCurrentThreadWin32Thread();
353 ptiKeyboard->TIF_flags |= TIF_SYSTEMTHREAD;
354 TRACE("Keyboard Thread 0x%x \n", ptiKeyboard);
355
356 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
357 LOW_REALTIME_PRIORITY + 3);
358
359 //IntKeyboardGetIndicatorTrans(ghKeyboardDevice,
360 // &IndicatorTrans);
361
362 for (;;)
363 {
364 /*
365 * Wait to start input.
366 */
367 TRACE( "Keyboard Input Thread Waiting for start event\n" );
368 Status = KeWaitForSingleObject(&InputThreadsStart,
369 0,
370 KernelMode,
371 TRUE,
372 NULL);
373
374 TRACE( "Keyboard Input Thread Starting...\n" );
375 /*
376 * Receive and process keyboard input.
377 */
378 while (InputThreadsRunning)
379 {
380 KEYBOARD_INPUT_DATA KeyInput;
381
382 TRACE("KeyInput @ %08x\n", &KeyInput);
383
384 Status = NtReadFile (ghKeyboardDevice,
385 NULL,
386 NULL,
387 NULL,
388 &Iosb,
389 &KeyInput,
390 sizeof(KEYBOARD_INPUT_DATA),
391 NULL,
392 NULL);
393
394 if(Status == STATUS_ALERTED && !InputThreadsRunning)
395 {
396 break;
397 }
398 if(Status == STATUS_PENDING)
399 {
400 NtWaitForSingleObject(ghKeyboardDevice, FALSE, NULL);
401 Status = Iosb.Status;
402 }
403 if(!NT_SUCCESS(Status))
404 {
405 ERR("Win32K: Failed to read from keyboard.\n");
406 return; //(Status);
407 }
408
409 TRACE("KeyRaw: %s %04x\n",
410 (KeyInput.Flags & KEY_BREAK) ? "up" : "down",
411 KeyInput.MakeCode );
412
413 if (Status == STATUS_ALERTED && !InputThreadsRunning)
414 break;
415
416 if (!NT_SUCCESS(Status))
417 {
418 ERR("Win32K: Failed to read from keyboard.\n");
419 return; //(Status);
420 }
421
422 /* Set LastInputTick */
423 IntLastInputTick(TRUE);
424
425 /* Process data */
426 UserEnterExclusive();
427 UserProcessKeyboardInput(&KeyInput);
428 UserLeave();
429 }
430
431 TRACE( "KeyboardInput Thread Stopped...\n" );
432 }
433 }
434
435
436 static PVOID Objects[2];
437 /*
438 Raw Input Thread.
439 Since this relies on InputThreadsStart, just fake it.
440 */
441 static VOID APIENTRY
442 RawInputThreadMain(PVOID StartContext)
443 {
444 NTSTATUS Status;
445 LARGE_INTEGER DueTime;
446
447 DueTime.QuadPart = (LONGLONG)(-10000000);
448
449 do
450 {
451 KEVENT Event;
452 KeInitializeEvent(&Event, NotificationEvent, FALSE);
453 Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
454 } while (!NT_SUCCESS(Status));
455
456
457 Objects[0] = &InputThreadsStart;
458 Objects[1] = MasterTimer;
459
460 // This thread requires win32k!
461 Status = Win32kInitWin32Thread(PsGetCurrentThread());
462 if (!NT_SUCCESS(Status))
463 {
464 ERR("Win32K: Failed making Raw Input thread a win32 thread.\n");
465 return; //(Status);
466 }
467
468 ptiRawInput = PsGetCurrentThreadWin32Thread();
469 ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD;
470 TRACE("Raw Input Thread 0x%x \n", ptiRawInput);
471
472 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
473 LOW_REALTIME_PRIORITY + 3);
474
475 UserEnterExclusive();
476 StartTheTimers();
477 UserLeave();
478
479 //
480 // ATM, we just have one job to handle, merge the other two later.
481 //
482 for(;;)
483 {
484 TRACE( "Raw Input Thread Waiting for start event\n" );
485
486 Status = KeWaitForMultipleObjects( 2,
487 Objects,
488 WaitAll, //WaitAny,
489 WrUserRequest,
490 KernelMode,
491 TRUE,
492 NULL,
493 NULL);
494 TRACE( "Raw Input Thread Starting...\n" );
495
496 ProcessTimers();
497 }
498 ERR("Raw Input Thread Exit!\n");
499 }
500
501 INIT_FUNCTION
502 NTSTATUS
503 NTAPI
504 InitInputImpl(VOID)
505 {
506 NTSTATUS Status;
507
508 KeInitializeEvent(&InputThreadsStart, NotificationEvent, FALSE);
509
510 MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), USERTAG_SYSTEM);
511 if (!MasterTimer)
512 {
513 ERR("Win32K: Failed making Raw Input thread a win32 thread.\n");
514 ASSERT(FALSE);
515 return STATUS_UNSUCCESSFUL;
516 }
517 KeInitializeTimer(MasterTimer);
518
519 /* Initialize the default keyboard layout */
520 if(!UserInitDefaultKeyboardLayout())
521 {
522 ERR("Failed to initialize default keyboard layout!\n");
523 }
524
525 Status = PsCreateSystemThread(&RawInputThreadHandle,
526 THREAD_ALL_ACCESS,
527 NULL,
528 NULL,
529 &RawInputThreadId,
530 RawInputThreadMain,
531 NULL);
532 if (!NT_SUCCESS(Status))
533 {
534 ERR("Win32K: Failed to create raw thread.\n");
535 }
536
537 Status = PsCreateSystemThread(&KeyboardThreadHandle,
538 THREAD_ALL_ACCESS,
539 NULL,
540 NULL,
541 &KeyboardThreadId,
542 KeyboardThreadMain,
543 NULL);
544 if (!NT_SUCCESS(Status))
545 {
546 ERR("Win32K: Failed to create keyboard thread.\n");
547 }
548
549 Status = PsCreateSystemThread(&MouseThreadHandle,
550 THREAD_ALL_ACCESS,
551 NULL,
552 NULL,
553 &MouseThreadId,
554 MouseThreadMain,
555 NULL);
556 if (!NT_SUCCESS(Status))
557 {
558 ERR("Win32K: Failed to create mouse thread.\n");
559 }
560
561 InputThreadsRunning = TRUE;
562 KeSetEvent(&InputThreadsStart, IO_NO_INCREMENT, FALSE);
563
564 return STATUS_SUCCESS;
565 }
566
567 NTSTATUS FASTCALL
568 CleanupInputImp(VOID)
569 {
570 return(STATUS_SUCCESS);
571 }
572
573 BOOL FASTCALL
574 IntBlockInput(PTHREADINFO pti, BOOL BlockIt)
575 {
576 PTHREADINFO OldBlock;
577 ASSERT(pti);
578
579 if(!pti->rpdesk || ((pti->TIF_flags & TIF_INCLEANUP) && BlockIt))
580 {
581 /*
582 * fail blocking if exiting the thread
583 */
584
585 return FALSE;
586 }
587
588 /*
589 * FIXME - check access rights of the window station
590 * e.g. services running in the service window station cannot block input
591 */
592 if(!ThreadHasInputAccess(pti) ||
593 !IntIsActiveDesktop(pti->rpdesk))
594 {
595 EngSetLastError(ERROR_ACCESS_DENIED);
596 return FALSE;
597 }
598
599 ASSERT(pti->rpdesk);
600 OldBlock = pti->rpdesk->BlockInputThread;
601 if(OldBlock)
602 {
603 if(OldBlock != pti)
604 {
605 EngSetLastError(ERROR_ACCESS_DENIED);
606 return FALSE;
607 }
608 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
609 return OldBlock == NULL;
610 }
611
612 pti->rpdesk->BlockInputThread = (BlockIt ? pti : NULL);
613 return OldBlock == NULL;
614 }
615
616 BOOL
617 APIENTRY
618 NtUserBlockInput(
619 BOOL BlockIt)
620 {
621 DECLARE_RETURN(BOOLEAN);
622
623 TRACE("Enter NtUserBlockInput\n");
624 UserEnterExclusive();
625
626 RETURN( IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt));
627
628 CLEANUP:
629 TRACE("Leave NtUserBlockInput, ret=%i\n", _ret_);
630 UserLeave();
631 END_CLEANUP;
632 }
633
634 BOOL FASTCALL
635 IntMouseInput(MOUSEINPUT *mi, BOOL Injected)
636 {
637 const UINT SwapBtnMsg[2][2] =
638 {
639 {WM_LBUTTONDOWN, WM_RBUTTONDOWN},
640 {WM_LBUTTONUP, WM_RBUTTONUP}
641 };
642 const WPARAM SwapBtn[2] =
643 {
644 MK_LBUTTON, MK_RBUTTON
645 };
646 POINT MousePos;
647 PSYSTEM_CURSORINFO CurInfo;
648 BOOL SwapButtons;
649 MSG Msg;
650
651 ASSERT(mi);
652
653 CurInfo = IntGetSysCursorInfo();
654
655 if(!mi->time)
656 {
657 LARGE_INTEGER LargeTickCount;
658 KeQueryTickCount(&LargeTickCount);
659 mi->time = MsqCalculateMessageTime(&LargeTickCount);
660 }
661
662 SwapButtons = gspv.bMouseBtnSwap;
663
664 MousePos = gpsi->ptCursor;
665
666 if(mi->dwFlags & MOUSEEVENTF_MOVE)
667 {
668 if(mi->dwFlags & MOUSEEVENTF_ABSOLUTE)
669 {
670 MousePos.x = mi->dx * UserGetSystemMetrics(SM_CXVIRTUALSCREEN) >> 16;
671 MousePos.y = mi->dy * UserGetSystemMetrics(SM_CYVIRTUALSCREEN) >> 16;
672 }
673 else
674 {
675 MousePos.x += mi->dx;
676 MousePos.y += mi->dy;
677 }
678 }
679
680 /*
681 * Insert the messages into the system queue
682 */
683 Msg.wParam = 0;
684 Msg.lParam = MAKELPARAM(MousePos.x, MousePos.y);
685 Msg.pt = MousePos;
686
687 if (gKeyStateTable[VK_SHIFT] & KS_DOWN_BIT)
688 {
689 Msg.wParam |= MK_SHIFT;
690 }
691
692 if (gKeyStateTable[VK_CONTROL] & KS_DOWN_BIT)
693 {
694 Msg.wParam |= MK_CONTROL;
695 }
696
697 if(mi->dwFlags & MOUSEEVENTF_MOVE)
698 {
699 UserSetCursorPos(MousePos.x, MousePos.y, Injected, mi->dwExtraInfo, TRUE);
700 }
701 if(mi->dwFlags & MOUSEEVENTF_LEFTDOWN)
702 {
703 gKeyStateTable[VK_LBUTTON] |= KS_DOWN_BIT;
704 Msg.message = SwapBtnMsg[0][SwapButtons];
705 CurInfo->ButtonsDown |= SwapBtn[SwapButtons];
706 Msg.wParam |= CurInfo->ButtonsDown;
707 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
708 }
709 else if(mi->dwFlags & MOUSEEVENTF_LEFTUP)
710 {
711 gKeyStateTable[VK_LBUTTON] &= ~KS_DOWN_BIT;
712 Msg.message = SwapBtnMsg[1][SwapButtons];
713 CurInfo->ButtonsDown &= ~SwapBtn[SwapButtons];
714 Msg.wParam |= CurInfo->ButtonsDown;
715 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
716 }
717 if(mi->dwFlags & MOUSEEVENTF_MIDDLEDOWN)
718 {
719 gKeyStateTable[VK_MBUTTON] |= KS_DOWN_BIT;
720 Msg.message = WM_MBUTTONDOWN;
721 CurInfo->ButtonsDown |= MK_MBUTTON;
722 Msg.wParam |= CurInfo->ButtonsDown;
723 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
724 }
725 else if(mi->dwFlags & MOUSEEVENTF_MIDDLEUP)
726 {
727 gKeyStateTable[VK_MBUTTON] &= ~KS_DOWN_BIT;
728 Msg.message = WM_MBUTTONUP;
729 CurInfo->ButtonsDown &= ~MK_MBUTTON;
730 Msg.wParam |= CurInfo->ButtonsDown;
731 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
732 }
733 if(mi->dwFlags & MOUSEEVENTF_RIGHTDOWN)
734 {
735 gKeyStateTable[VK_RBUTTON] |= KS_DOWN_BIT;
736 Msg.message = SwapBtnMsg[0][!SwapButtons];
737 CurInfo->ButtonsDown |= SwapBtn[!SwapButtons];
738 Msg.wParam |= CurInfo->ButtonsDown;
739 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
740 }
741 else if(mi->dwFlags & MOUSEEVENTF_RIGHTUP)
742 {
743 gKeyStateTable[VK_RBUTTON] &= ~KS_DOWN_BIT;
744 Msg.message = SwapBtnMsg[1][!SwapButtons];
745 CurInfo->ButtonsDown &= ~SwapBtn[!SwapButtons];
746 Msg.wParam |= CurInfo->ButtonsDown;
747 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
748 }
749
750 if((mi->dwFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)) &&
751 (mi->dwFlags & MOUSEEVENTF_WHEEL))
752 {
753 /* fail because both types of events use the mouseData field */
754 return FALSE;
755 }
756
757 if(mi->dwFlags & MOUSEEVENTF_XDOWN)
758 {
759 Msg.message = WM_XBUTTONDOWN;
760 if(mi->mouseData & XBUTTON1)
761 {
762 gKeyStateTable[VK_XBUTTON1] |= KS_DOWN_BIT;
763 CurInfo->ButtonsDown |= MK_XBUTTON1;
764 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
765 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
766 }
767 if(mi->mouseData & XBUTTON2)
768 {
769 gKeyStateTable[VK_XBUTTON2] |= KS_DOWN_BIT;
770 CurInfo->ButtonsDown |= MK_XBUTTON2;
771 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
772 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
773 }
774 }
775 else if(mi->dwFlags & MOUSEEVENTF_XUP)
776 {
777 Msg.message = WM_XBUTTONUP;
778 if(mi->mouseData & XBUTTON1)
779 {
780 gKeyStateTable[VK_XBUTTON1] &= ~KS_DOWN_BIT;
781 CurInfo->ButtonsDown &= ~MK_XBUTTON1;
782 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
783 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
784 }
785 if(mi->mouseData & XBUTTON2)
786 {
787 gKeyStateTable[VK_XBUTTON2] &= ~KS_DOWN_BIT;
788 CurInfo->ButtonsDown &= ~MK_XBUTTON2;
789 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
790 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
791 }
792 }
793 if(mi->dwFlags & MOUSEEVENTF_WHEEL)
794 {
795 Msg.message = WM_MOUSEWHEEL;
796 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, mi->mouseData);
797 co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
798 }
799
800 return TRUE;
801 }
802
803 BOOL FASTCALL
804 UserAttachThreadInput(PTHREADINFO pti, PTHREADINFO ptiTo, BOOL fAttach)
805 {
806 PATTACHINFO pai;
807
808 /* Can not be the same thread.*/
809 if (pti == ptiTo) return FALSE;
810
811 /* Do not attach to system threads or between different desktops. */
812 if ( pti->TIF_flags & TIF_DONTATTACHQUEUE ||
813 ptiTo->TIF_flags & TIF_DONTATTACHQUEUE ||
814 pti->rpdesk != ptiTo->rpdesk )
815 return FALSE;
816
817 /* If Attach set, allocate and link. */
818 if ( fAttach )
819 {
820 pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO);
821 if ( !pai ) return FALSE;
822
823 pai->paiNext = gpai;
824 pai->pti1 = pti;
825 pai->pti2 = ptiTo;
826 gpai = pai;
827 }
828 else /* If clear, unlink and free it. */
829 {
830 PATTACHINFO paiprev = NULL;
831
832 if ( !gpai ) return FALSE;
833
834 pai = gpai;
835
836 /* Search list and free if found or return false. */
837 do
838 {
839 if ( pai->pti2 == ptiTo && pai->pti1 == pti ) break;
840 paiprev = pai;
841 pai = pai->paiNext;
842 } while (pai);
843
844 if ( !pai ) return FALSE;
845
846 if (paiprev) paiprev->paiNext = pai->paiNext;
847
848 ExFreePoolWithTag(pai, USERTAG_ATTACHINFO);
849 }
850
851 return TRUE;
852 }
853
854 UINT
855 APIENTRY
856 NtUserSendInput(
857 UINT nInputs,
858 LPINPUT pInput,
859 INT cbSize)
860 {
861 PTHREADINFO pti;
862 UINT cnt;
863 DECLARE_RETURN(UINT);
864
865 TRACE("Enter NtUserSendInput\n");
866 UserEnterExclusive();
867
868 pti = PsGetCurrentThreadWin32Thread();
869 ASSERT(pti);
870
871 if (!pti->rpdesk)
872 {
873 RETURN( 0);
874 }
875
876 if (!nInputs || !pInput || cbSize != sizeof(INPUT))
877 {
878 EngSetLastError(ERROR_INVALID_PARAMETER);
879 RETURN( 0);
880 }
881
882 /*
883 * FIXME - check access rights of the window station
884 * e.g. services running in the service window station cannot block input
885 */
886 if (!ThreadHasInputAccess(pti) ||
887 !IntIsActiveDesktop(pti->rpdesk))
888 {
889 EngSetLastError(ERROR_ACCESS_DENIED);
890 RETURN( 0);
891 }
892
893 cnt = 0;
894 while (nInputs--)
895 {
896 INPUT SafeInput;
897 NTSTATUS Status;
898
899 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
900 if (!NT_SUCCESS(Status))
901 {
902 SetLastNtError(Status);
903 RETURN( cnt);
904 }
905
906 switch (SafeInput.type)
907 {
908 case INPUT_MOUSE:
909 if (IntMouseInput(&SafeInput.mi, TRUE))
910 cnt++;
911 break;
912 case INPUT_KEYBOARD:
913 if (UserSendKeyboardInput(&SafeInput.ki, TRUE))
914 cnt++;
915 break;
916 case INPUT_HARDWARE:
917 break;
918 default:
919 ERR("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
920 break;
921 }
922 }
923
924 RETURN( cnt);
925
926 CLEANUP:
927 TRACE("Leave NtUserSendInput, ret=%i\n", _ret_);
928 UserLeave();
929 END_CLEANUP;
930 }
931
932 BOOL
933 FASTCALL
934 IntQueryTrackMouseEvent(
935 LPTRACKMOUSEEVENT lpEventTrack)
936 {
937 PDESKTOP pDesk;
938 PTHREADINFO pti;
939
940 pti = PsGetCurrentThreadWin32Thread();
941 pDesk = pti->rpdesk;
942
943 /* Always cleared with size set and return true. */
944 RtlZeroMemory(lpEventTrack , sizeof(TRACKMOUSEEVENT));
945 lpEventTrack->cbSize = sizeof(TRACKMOUSEEVENT);
946
947 if ( pDesk->dwDTFlags & (DF_TME_LEAVE | DF_TME_HOVER) &&
948 pDesk->spwndTrack &&
949 pti->MessageQueue == pDesk->spwndTrack->head.pti->MessageQueue )
950 {
951 if ( pDesk->htEx != HTCLIENT )
952 lpEventTrack->dwFlags |= TME_NONCLIENT;
953
954 if ( pDesk->dwDTFlags & DF_TME_LEAVE )
955 lpEventTrack->dwFlags |= TME_LEAVE;
956
957 if ( pDesk->dwDTFlags & DF_TME_HOVER )
958 {
959 lpEventTrack->dwFlags |= TME_HOVER;
960 lpEventTrack->dwHoverTime = pDesk->dwMouseHoverTime;
961 }
962 lpEventTrack->hwndTrack = UserHMGetHandle(pDesk->spwndTrack);
963 }
964 return TRUE;
965 }
966
967 BOOL
968 FASTCALL
969 IntTrackMouseEvent(
970 LPTRACKMOUSEEVENT lpEventTrack)
971 {
972 PDESKTOP pDesk;
973 PTHREADINFO pti;
974 PWND pWnd;
975 POINT point;
976
977 pti = PsGetCurrentThreadWin32Thread();
978 pDesk = pti->rpdesk;
979
980 if (!(pWnd = UserGetWindowObject(lpEventTrack->hwndTrack)))
981 return FALSE;
982
983 /* Tracking spwndTrack same as pWnd */
984 if ( lpEventTrack->dwFlags & TME_CANCEL ) // Canceled mode.
985 {
986 if ( lpEventTrack->dwFlags & TME_LEAVE )
987 pDesk->dwDTFlags &= ~DF_TME_LEAVE;
988
989 if ( lpEventTrack->dwFlags & TME_HOVER )
990 {
991 if ( pDesk->dwDTFlags & DF_TME_HOVER )
992 { // Kill hover timer.
993 IntKillTimer(pWnd, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
994 pDesk->dwDTFlags &= ~DF_TME_HOVER;
995 }
996 }
997 }
998 else // Not Canceled.
999 {
1000 pDesk->spwndTrack = pWnd;
1001 if ( lpEventTrack->dwFlags & TME_LEAVE )
1002 pDesk->dwDTFlags |= DF_TME_LEAVE;
1003
1004 if ( lpEventTrack->dwFlags & TME_HOVER )
1005 {
1006 pDesk->dwDTFlags |= DF_TME_HOVER;
1007
1008 if ( !lpEventTrack->dwHoverTime || lpEventTrack->dwHoverTime == HOVER_DEFAULT )
1009 pDesk->dwMouseHoverTime = gspv.iMouseHoverTime; // use the system default hover time-out.
1010 else
1011 pDesk->dwMouseHoverTime = lpEventTrack->dwHoverTime;
1012 // Start timer for the hover period.
1013 IntSetTimer( pWnd, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
1014 // Get windows thread message points.
1015 point = pWnd->head.pti->ptLast;
1016 // Set desktop mouse hover from the system default hover rectangle.
1017 RECTL_vSetRect(&pDesk->rcMouseHover,
1018 point.x - gspv.iMouseHoverWidth / 2,
1019 point.y - gspv.iMouseHoverHeight / 2,
1020 point.x + gspv.iMouseHoverWidth / 2,
1021 point.y + gspv.iMouseHoverHeight / 2);
1022 }
1023 }
1024 return TRUE;
1025 }
1026
1027 BOOL
1028 APIENTRY
1029 NtUserTrackMouseEvent(
1030 LPTRACKMOUSEEVENT lpEventTrack)
1031 {
1032 TRACKMOUSEEVENT saveTME;
1033 BOOL Ret = FALSE;
1034
1035 TRACE("Enter NtUserTrackMouseEvent\n");
1036 UserEnterExclusive();
1037
1038 _SEH2_TRY
1039 {
1040 ProbeForRead(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
1041 RtlCopyMemory(&saveTME, lpEventTrack, sizeof(TRACKMOUSEEVENT));
1042 }
1043 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1044 {
1045 SetLastNtError(_SEH2_GetExceptionCode());
1046 _SEH2_YIELD(goto Exit;)
1047 }
1048 _SEH2_END;
1049
1050 if ( saveTME.cbSize != sizeof(TRACKMOUSEEVENT) )
1051 {
1052 EngSetLastError(ERROR_INVALID_PARAMETER);
1053 goto Exit;
1054 }
1055
1056 if (saveTME.dwFlags & ~(TME_CANCEL | TME_QUERY | TME_NONCLIENT | TME_LEAVE | TME_HOVER) )
1057 {
1058 EngSetLastError(ERROR_INVALID_FLAGS);
1059 goto Exit;
1060 }
1061
1062 if ( saveTME.dwFlags & TME_QUERY )
1063 {
1064 Ret = IntQueryTrackMouseEvent(&saveTME);
1065 _SEH2_TRY
1066 {
1067 ProbeForWrite(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
1068 RtlCopyMemory(lpEventTrack, &saveTME, sizeof(TRACKMOUSEEVENT));
1069 }
1070 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1071 {
1072 SetLastNtError(_SEH2_GetExceptionCode());
1073 Ret = FALSE;
1074 }
1075 _SEH2_END;
1076 }
1077 else
1078 {
1079 Ret = IntTrackMouseEvent(&saveTME);
1080 }
1081
1082 Exit:
1083 TRACE("Leave NtUserTrackMouseEvent, ret=%i\n", Ret);
1084 UserLeave();
1085 return Ret;
1086 }
1087
1088 extern MOUSEMOVEPOINT MouseHistoryOfMoves[];
1089 extern INT gcur_count;
1090
1091 DWORD
1092 APIENTRY
1093 NtUserGetMouseMovePointsEx(
1094 UINT cbSize,
1095 LPMOUSEMOVEPOINT lpptIn,
1096 LPMOUSEMOVEPOINT lpptOut,
1097 int nBufPoints,
1098 DWORD resolution)
1099 {
1100 MOUSEMOVEPOINT Safeppt;
1101 //BOOL Hit;
1102 INT Count = -1;
1103 DECLARE_RETURN(DWORD);
1104
1105 TRACE("Enter NtUserGetMouseMovePointsEx\n");
1106 UserEnterExclusive();
1107
1108 if ((cbSize != sizeof(MOUSEMOVEPOINT)) || (nBufPoints < 0) || (nBufPoints > 64))
1109 {
1110 EngSetLastError(ERROR_INVALID_PARAMETER);
1111 RETURN( -1);
1112 }
1113
1114 if (!lpptIn || (!lpptOut && nBufPoints))
1115 {
1116 EngSetLastError(ERROR_NOACCESS);
1117 RETURN( -1);
1118 }
1119
1120 _SEH2_TRY
1121 {
1122 ProbeForRead( lpptIn, cbSize, 1);
1123 RtlCopyMemory(&Safeppt, lpptIn, cbSize);
1124 }
1125 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1126 {
1127 SetLastNtError(_SEH2_GetExceptionCode());
1128 _SEH2_YIELD(RETURN( -1))
1129 }
1130 _SEH2_END;
1131
1132 // http://msdn.microsoft.com/en-us/library/ms646259(v=vs.85).aspx
1133 // This explains the math issues in transforming points.
1134 Count = gcur_count; // FIFO is forward so retrieve backward.
1135 //Hit = FALSE;
1136 do
1137 {
1138 if (Safeppt.x == 0 && Safeppt.y == 0)
1139 break; // No test.
1140 // Finds the point, it returns the last nBufPoints prior to and including the supplied point.
1141 if (MouseHistoryOfMoves[Count].x == Safeppt.x && MouseHistoryOfMoves[Count].y == Safeppt.y)
1142 {
1143 if ( Safeppt.time ) // Now test time and it seems to be absolute.
1144 {
1145 if (Safeppt.time == MouseHistoryOfMoves[Count].time)
1146 {
1147 //Hit = TRUE;
1148 break;
1149 }
1150 else
1151 {
1152 if (--Count < 0) Count = 63;
1153 continue;
1154 }
1155 }
1156 //Hit = TRUE;
1157 break;
1158 }
1159 if (--Count < 0) Count = 63;
1160 }
1161 while ( Count != gcur_count);
1162
1163 switch(resolution)
1164 {
1165 case GMMP_USE_DISPLAY_POINTS:
1166 if (nBufPoints)
1167 {
1168 _SEH2_TRY
1169 {
1170 ProbeForWrite(lpptOut, cbSize, 1);
1171 }
1172 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1173 {
1174 SetLastNtError(_SEH2_GetExceptionCode());
1175 _SEH2_YIELD(RETURN( -1))
1176 }
1177 _SEH2_END;
1178 }
1179 Count = nBufPoints;
1180 break;
1181 case GMMP_USE_HIGH_RESOLUTION_POINTS:
1182 break;
1183 default:
1184 EngSetLastError(ERROR_POINT_NOT_FOUND);
1185 RETURN( -1);
1186 }
1187
1188 RETURN( Count);
1189
1190 CLEANUP:
1191 TRACE("Leave NtUserGetMouseMovePointsEx, ret=%i\n", _ret_);
1192 UserLeave();
1193 END_CLEANUP;
1194 }
1195
1196 /* EOF */