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