Downgrade various debug prints from DPRINT1 to DPRINT, ranging from "useless" to...
[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: subsys/win32k/ntuser/class.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISION HISTORY:
8 * 06-06-2001 CSH Created
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <w32k.h>
14 #include <ntddkbd.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 extern BYTE gQueueKeyStateTable[];
20 extern NTSTATUS Win32kInitWin32Thread(PETHREAD Thread);
21
22 /* GLOBALS *******************************************************************/
23
24 PTHREADINFO ptiRawInput;
25 PKTIMER MasterTimer;
26 PATTACHINFO gpai = NULL;
27
28 static HANDLE MouseDeviceHandle;
29 static HANDLE MouseThreadHandle;
30 static CLIENT_ID MouseThreadId;
31 static HANDLE KeyboardThreadHandle;
32 static CLIENT_ID KeyboardThreadId;
33 static HANDLE KeyboardDeviceHandle;
34 static HANDLE RawInputThreadHandle;
35 static CLIENT_ID RawInputThreadId;
36 static KEVENT InputThreadsStart;
37 static BOOLEAN InputThreadsRunning = FALSE;
38
39 /* FUNCTIONS *****************************************************************/
40 DWORD IntLastInputTick(BOOL LastInputTickSetGet);
41
42 #define ClearMouseInput(mi) \
43 mi.dx = 0; \
44 mi.dy = 0; \
45 mi.mouseData = 0; \
46 mi.dwFlags = 0;
47
48 #define SendMouseEvent(mi) \
49 if(mi.dx != 0 || mi.dy != 0) \
50 mi.dwFlags |= MOUSEEVENTF_MOVE; \
51 if(mi.dwFlags) \
52 IntMouseInput(&mi); \
53 ClearMouseInput(mi);
54
55
56 DWORD IntLastInputTick(BOOL LastInputTickSetGet)
57 {
58 static DWORD LastInputTick = 0;
59 if (LastInputTickSetGet == TRUE)
60 {
61 LARGE_INTEGER TickCount;
62 KeQueryTickCount(&TickCount);
63 LastInputTick = TickCount.u.LowPart * (KeQueryTimeIncrement() / 10000);
64 }
65 return LastInputTick;
66 }
67
68 BOOL
69 APIENTRY
70 NtUserGetLastInputInfo(PLASTINPUTINFO plii)
71 {
72 BOOL ret = TRUE;
73
74 UserEnterShared();
75
76 _SEH2_TRY
77 {
78 if (ProbeForReadUint(&plii->cbSize) != sizeof(LASTINPUTINFO))
79 {
80 SetLastWin32Error(ERROR_INVALID_PARAMETER);
81 ret = FALSE;
82 _SEH2_LEAVE;
83 }
84
85 ProbeForWrite(plii, sizeof(LASTINPUTINFO), sizeof(DWORD));
86
87 plii->dwTime = IntLastInputTick(FALSE);
88 }
89 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
90 {
91 SetLastNtError(_SEH2_GetExceptionCode());
92 ret = FALSE;
93 }
94 _SEH2_END;
95
96 UserLeave();
97
98 return ret;
99 }
100
101
102 VOID FASTCALL
103 ProcessMouseInputData(PMOUSE_INPUT_DATA Data, ULONG InputCount)
104 {
105 PMOUSE_INPUT_DATA mid;
106 MOUSEINPUT mi;
107 ULONG i;
108
109 ClearMouseInput(mi);
110 mi.time = 0;
111 mi.dwExtraInfo = 0;
112 for(i = 0; i < InputCount; i++)
113 {
114 mid = (Data + i);
115 mi.dx += mid->LastX;
116 mi.dy += mid->LastY;
117
118 /* Check if the mouse move is absolute */
119 if (mid->Flags == MOUSE_MOVE_ABSOLUTE)
120 {
121 /* Set flag and convert to screen location */
122 mi.dwFlags |= MOUSEEVENTF_ABSOLUTE;
123 mi.dx = mi.dx / (65535 / (UserGetSystemMetrics(SM_CXVIRTUALSCREEN) - 1));
124 mi.dy = mi.dy / (65535 / (UserGetSystemMetrics(SM_CYVIRTUALSCREEN) - 1));
125 }
126
127 if(mid->ButtonFlags)
128 {
129 if(mid->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN)
130 {
131 mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
132 SendMouseEvent(mi);
133 }
134 if(mid->ButtonFlags & MOUSE_LEFT_BUTTON_UP)
135 {
136 mi.dwFlags |= MOUSEEVENTF_LEFTUP;
137 SendMouseEvent(mi);
138 }
139 if(mid->ButtonFlags & MOUSE_MIDDLE_BUTTON_DOWN)
140 {
141 mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
142 SendMouseEvent(mi);
143 }
144 if(mid->ButtonFlags & MOUSE_MIDDLE_BUTTON_UP)
145 {
146 mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
147 SendMouseEvent(mi);
148 }
149 if(mid->ButtonFlags & MOUSE_RIGHT_BUTTON_DOWN)
150 {
151 mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
152 SendMouseEvent(mi);
153 }
154 if(mid->ButtonFlags & MOUSE_RIGHT_BUTTON_UP)
155 {
156 mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
157 SendMouseEvent(mi);
158 }
159 if(mid->ButtonFlags & MOUSE_BUTTON_4_DOWN)
160 {
161 mi.mouseData |= XBUTTON1;
162 mi.dwFlags |= MOUSEEVENTF_XDOWN;
163 SendMouseEvent(mi);
164 }
165 if(mid->ButtonFlags & MOUSE_BUTTON_4_UP)
166 {
167 mi.mouseData |= XBUTTON1;
168 mi.dwFlags |= MOUSEEVENTF_XUP;
169 SendMouseEvent(mi);
170 }
171 if(mid->ButtonFlags & MOUSE_BUTTON_5_DOWN)
172 {
173 mi.mouseData |= XBUTTON2;
174 mi.dwFlags |= MOUSEEVENTF_XDOWN;
175 SendMouseEvent(mi);
176 }
177 if(mid->ButtonFlags & MOUSE_BUTTON_5_UP)
178 {
179 mi.mouseData |= XBUTTON2;
180 mi.dwFlags |= MOUSEEVENTF_XUP;
181 SendMouseEvent(mi);
182 }
183 if(mid->ButtonFlags & MOUSE_WHEEL)
184 {
185 mi.mouseData = mid->ButtonData;
186 mi.dwFlags |= MOUSEEVENTF_WHEEL;
187 SendMouseEvent(mi);
188 }
189 }
190 }
191
192 SendMouseEvent(mi);
193 }
194
195
196
197
198 VOID APIENTRY
199 MouseThreadMain(PVOID StartContext)
200 {
201 UNICODE_STRING MouseDeviceName = RTL_CONSTANT_STRING(L"\\Device\\PointerClass0");
202 OBJECT_ATTRIBUTES MouseObjectAttributes;
203 IO_STATUS_BLOCK Iosb;
204 NTSTATUS Status;
205 MOUSE_ATTRIBUTES MouseAttr;
206
207 InitializeObjectAttributes(&MouseObjectAttributes,
208 &MouseDeviceName,
209 0,
210 NULL,
211 NULL);
212 do
213 {
214 LARGE_INTEGER DueTime;
215 KEVENT Event;
216 DueTime.QuadPart = (LONGLONG)(-10000000);
217 KeInitializeEvent(&Event, NotificationEvent, FALSE);
218 Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
219 Status = NtOpenFile(&MouseDeviceHandle,
220 FILE_ALL_ACCESS,
221 &MouseObjectAttributes,
222 &Iosb,
223 0,
224 FILE_SYNCHRONOUS_IO_ALERT);
225 } while (!NT_SUCCESS(Status));
226
227 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
228 LOW_REALTIME_PRIORITY + 3);
229
230 for(;;)
231 {
232 /*
233 * Wait to start input.
234 */
235 DPRINT("Mouse Input Thread Waiting for start event\n");
236 Status = KeWaitForSingleObject(&InputThreadsStart,
237 0,
238 KernelMode,
239 TRUE,
240 NULL);
241 DPRINT("Mouse Input Thread Starting...\n");
242
243 /*FIXME: Does mouse attributes need to be used for anything */
244 Status = NtDeviceIoControlFile(MouseDeviceHandle,
245 NULL,
246 NULL,
247 NULL,
248 &Iosb,
249 IOCTL_MOUSE_QUERY_ATTRIBUTES,
250 &MouseAttr, sizeof(MOUSE_ATTRIBUTES),
251 NULL, 0);
252 if(!NT_SUCCESS(Status))
253 {
254 DPRINT("Failed to get mouse attributes\n");
255 }
256
257 /*
258 * Receive and process mouse input.
259 */
260 while(InputThreadsRunning)
261 {
262 MOUSE_INPUT_DATA MouseInput;
263 Status = NtReadFile(MouseDeviceHandle,
264 NULL,
265 NULL,
266 NULL,
267 &Iosb,
268 &MouseInput,
269 sizeof(MOUSE_INPUT_DATA),
270 NULL,
271 NULL);
272 if(Status == STATUS_ALERTED && !InputThreadsRunning)
273 {
274 break;
275 }
276 if(Status == STATUS_PENDING)
277 {
278 NtWaitForSingleObject(MouseDeviceHandle, FALSE, NULL);
279 Status = Iosb.Status;
280 }
281 if(!NT_SUCCESS(Status))
282 {
283 DPRINT1("Win32K: Failed to read from mouse.\n");
284 return; //(Status);
285 }
286 DPRINT("MouseEvent\n");
287 IntLastInputTick(TRUE);
288
289 UserEnterExclusive();
290
291 ProcessMouseInputData(&MouseInput, Iosb.Information / sizeof(MOUSE_INPUT_DATA));
292
293 UserLeave();
294 }
295 DPRINT("Mouse Input Thread Stopped...\n");
296 }
297 }
298
299 /* Returns a value that indicates if the key is a modifier key, and
300 * which one.
301 */
302 static UINT APIENTRY
303 IntKeyboardGetModifiers(KEYBOARD_INPUT_DATA *InputData)
304 {
305 if (InputData->Flags & KEY_E1)
306 return 0;
307
308 if (!(InputData->Flags & KEY_E0))
309 {
310 switch (InputData->MakeCode)
311 {
312 case 0x2a: /* left shift */
313 case 0x36: /* right shift */
314 return MOD_SHIFT;
315
316 case 0x1d: /* left control */
317 return MOD_CONTROL;
318
319 case 0x38: /* left alt */
320 return MOD_ALT;
321
322 default:
323 return 0;
324 }
325 }
326 else
327 {
328 switch (InputData->MakeCode)
329 {
330 case 0x1d: /* right control */
331 return MOD_CONTROL;
332
333 case 0x38: /* right alt */
334 return MOD_ALT;
335
336 case 0x5b: /* left gui (windows) */
337 case 0x5c: /* right gui (windows) */
338 return MOD_WIN;
339
340 default:
341 return 0;
342 }
343 }
344 }
345
346 /* Asks the keyboard driver to send a small table that shows which
347 * lights should connect with which scancodes
348 */
349 static NTSTATUS APIENTRY
350 IntKeyboardGetIndicatorTrans(HANDLE KeyboardDeviceHandle,
351 PKEYBOARD_INDICATOR_TRANSLATION *IndicatorTrans)
352 {
353 NTSTATUS Status;
354 DWORD Size = 0;
355 IO_STATUS_BLOCK Block;
356 PKEYBOARD_INDICATOR_TRANSLATION Ret;
357
358 Size = sizeof(KEYBOARD_INDICATOR_TRANSLATION);
359
360 Ret = ExAllocatePoolWithTag(PagedPool,
361 Size,
362 TAG_KEYBOARD);
363
364 while (Ret)
365 {
366 Status = NtDeviceIoControlFile(KeyboardDeviceHandle,
367 NULL,
368 NULL,
369 NULL,
370 &Block,
371 IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION,
372 NULL, 0,
373 Ret, Size);
374
375 if (Status != STATUS_BUFFER_TOO_SMALL)
376 break;
377
378 ExFreePoolWithTag(Ret, TAG_KEYBOARD);
379
380 Size += sizeof(KEYBOARD_INDICATOR_TRANSLATION);
381
382 Ret = ExAllocatePoolWithTag(PagedPool,
383 Size,
384 TAG_KEYBOARD);
385 }
386
387 if (!Ret)
388 return STATUS_INSUFFICIENT_RESOURCES;
389
390 if (Status != STATUS_SUCCESS)
391 {
392 ExFreePoolWithTag(Ret, TAG_KEYBOARD);
393 return Status;
394 }
395
396 *IndicatorTrans = Ret;
397 return Status;
398 }
399
400 /* Sends the keyboard commands to turn on/off the lights.
401 */
402 static NTSTATUS APIENTRY
403 IntKeyboardUpdateLeds(HANDLE KeyboardDeviceHandle,
404 PKEYBOARD_INPUT_DATA KeyInput,
405 PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans)
406 {
407 NTSTATUS Status;
408 UINT Count;
409 static KEYBOARD_INDICATOR_PARAMETERS Indicators;
410 IO_STATUS_BLOCK Block;
411
412 if (!IndicatorTrans)
413 return STATUS_NOT_SUPPORTED;
414
415 if (KeyInput->Flags & (KEY_E0 | KEY_E1 | KEY_BREAK))
416 return STATUS_SUCCESS;
417
418 for (Count = 0; Count < IndicatorTrans->NumberOfIndicatorKeys; Count++)
419 {
420 if (KeyInput->MakeCode == IndicatorTrans->IndicatorList[Count].MakeCode)
421 {
422 Indicators.LedFlags ^=
423 IndicatorTrans->IndicatorList[Count].IndicatorFlags;
424
425 /* Update the lights on the hardware */
426
427 Status = NtDeviceIoControlFile(KeyboardDeviceHandle,
428 NULL,
429 NULL,
430 NULL,
431 &Block,
432 IOCTL_KEYBOARD_SET_INDICATORS,
433 &Indicators, sizeof(Indicators),
434 NULL, 0);
435
436 return Status;
437 }
438 }
439
440 return STATUS_SUCCESS;
441 }
442
443 static VOID APIENTRY
444 IntKeyboardSendWinKeyMsg()
445 {
446 PWINDOW_OBJECT Window;
447 MSG Mesg;
448
449 if (!(Window = UserGetWindowObject(InputWindowStation->ShellWindow)))
450 {
451 DPRINT1("Couldn't find window to send Windows key message!\n");
452 return;
453 }
454
455 Mesg.hwnd = InputWindowStation->ShellWindow;
456 Mesg.message = WM_SYSCOMMAND;
457 Mesg.wParam = SC_TASKLIST;
458 Mesg.lParam = 0;
459
460 /* The QS_HOTKEY is just a guess */
461 MsqPostMessage(Window->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
462 }
463
464 static VOID APIENTRY
465 co_IntKeyboardSendAltKeyMsg()
466 {
467 co_MsqPostKeyboardMessage(WM_SYSCOMMAND,SC_KEYMENU,0);
468 }
469
470 static VOID APIENTRY
471 KeyboardThreadMain(PVOID StartContext)
472 {
473 UNICODE_STRING KeyboardDeviceName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardClass0");
474 OBJECT_ATTRIBUTES KeyboardObjectAttributes;
475 IO_STATUS_BLOCK Iosb;
476 NTSTATUS Status;
477 MSG msg;
478 PUSER_MESSAGE_QUEUE FocusQueue;
479 struct _ETHREAD *FocusThread;
480
481 PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans = NULL;
482 UINT ModifierState = 0;
483 USHORT LastMakeCode = 0;
484 USHORT LastFlags = 0;
485 UINT RepeatCount = 0;
486
487 InitializeObjectAttributes(&KeyboardObjectAttributes,
488 &KeyboardDeviceName,
489 0,
490 NULL,
491 NULL);
492 do
493 {
494 LARGE_INTEGER DueTime;
495 KEVENT Event;
496 DueTime.QuadPart = (LONGLONG)(-10000000);
497 KeInitializeEvent(&Event, NotificationEvent, FALSE);
498 Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
499 Status = NtOpenFile(&KeyboardDeviceHandle,
500 FILE_ALL_ACCESS,
501 &KeyboardObjectAttributes,
502 &Iosb,
503 0,
504 FILE_SYNCHRONOUS_IO_ALERT);
505 } while (!NT_SUCCESS(Status));
506
507 /* Not sure if converting this thread to a win32 thread is such
508 a great idea. Since we're posting keyboard messages to the focus
509 window message queue, we'll be (indirectly) doing sendmessage
510 stuff from this thread (for WH_KEYBOARD_LL processing), which
511 means we need our own message queue. If keyboard messages were
512 instead queued to the system message queue, the thread removing
513 the message from the system message queue would be responsible
514 for WH_KEYBOARD_LL processing and we wouldn't need this thread
515 to be a win32 thread. */
516 Status = Win32kInitWin32Thread(PsGetCurrentThread());
517 if (!NT_SUCCESS(Status))
518 {
519 DPRINT1("Win32K: Failed making keyboard thread a win32 thread.\n");
520 return; //(Status);
521 }
522
523 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
524 LOW_REALTIME_PRIORITY + 3);
525
526 IntKeyboardGetIndicatorTrans(KeyboardDeviceHandle,
527 &IndicatorTrans);
528
529 for (;;)
530 {
531 /*
532 * Wait to start input.
533 */
534 DPRINT( "Keyboard Input Thread Waiting for start event\n" );
535 Status = KeWaitForSingleObject(&InputThreadsStart,
536 0,
537 KernelMode,
538 TRUE,
539 NULL);
540
541 DPRINT( "Keyboard Input Thread Starting...\n" );
542 /*
543 * Receive and process keyboard input.
544 */
545 while (InputThreadsRunning)
546 {
547 BOOLEAN NumKeys = 1;
548 BOOLEAN bLeftAlt;
549 KEYBOARD_INPUT_DATA KeyInput;
550 KEYBOARD_INPUT_DATA NextKeyInput;
551 LPARAM lParam = 0;
552 UINT fsModifiers, fsNextModifiers;
553 struct _ETHREAD *Thread;
554 HWND hWnd;
555 int id;
556
557 DPRINT("KeyInput @ %08x\n", &KeyInput);
558
559 Status = NtReadFile (KeyboardDeviceHandle,
560 NULL,
561 NULL,
562 NULL,
563 &Iosb,
564 &KeyInput,
565 sizeof(KEYBOARD_INPUT_DATA),
566 NULL,
567 NULL);
568
569 if(Status == STATUS_ALERTED && !InputThreadsRunning)
570 {
571 break;
572 }
573 if(Status == STATUS_PENDING)
574 {
575 NtWaitForSingleObject(KeyboardDeviceHandle, FALSE, NULL);
576 Status = Iosb.Status;
577 }
578 if(!NT_SUCCESS(Status))
579 {
580 DPRINT1("Win32K: Failed to read from mouse.\n");
581 return; //(Status);
582 }
583
584 DPRINT("KeyRaw: %s %04x\n",
585 (KeyInput.Flags & KEY_BREAK) ? "up" : "down",
586 KeyInput.MakeCode );
587
588 if (Status == STATUS_ALERTED && !InputThreadsRunning)
589 break;
590
591 if (!NT_SUCCESS(Status))
592 {
593 DPRINT1("Win32K: Failed to read from keyboard.\n");
594 return; //(Status);
595 }
596
597 /* Set LastInputTick */
598 IntLastInputTick(TRUE);
599
600 /* Update modifier state */
601 fsModifiers = IntKeyboardGetModifiers(&KeyInput);
602
603 if (fsModifiers)
604 {
605 if (KeyInput.Flags & KEY_BREAK)
606 {
607 ModifierState &= ~fsModifiers;
608 if(fsModifiers == MOD_ALT)
609 {
610 if(KeyInput.Flags & KEY_E0)
611 {
612 gQueueKeyStateTable[VK_RMENU] = 0;
613 }
614 else
615 {
616 gQueueKeyStateTable[VK_LMENU] = 0;
617 }
618 if (gQueueKeyStateTable[VK_RMENU] == 0 &&
619 gQueueKeyStateTable[VK_LMENU] == 0)
620 {
621 gQueueKeyStateTable[VK_MENU] = 0;
622 }
623 }
624 }
625 else
626 {
627 ModifierState |= fsModifiers;
628
629 if (ModifierState == fsModifiers &&
630 (fsModifiers == MOD_ALT || fsModifiers == MOD_WIN))
631 {
632 /* First send out special notifications
633 * (For alt, the message that turns on accelerator
634 * display, not sure what for win. Both TODO though.)
635 */
636 bLeftAlt = FALSE;
637 if(fsModifiers == MOD_ALT)
638 {
639 if(KeyInput.Flags & KEY_E0)
640 {
641 gQueueKeyStateTable[VK_RMENU] = 0x80;
642 }
643 else
644 {
645 gQueueKeyStateTable[VK_LMENU] = 0x80;
646 bLeftAlt = TRUE;
647 }
648
649 gQueueKeyStateTable[VK_MENU] = 0x80;
650 }
651
652 /* Read the next key before sending this one */
653 do
654 {
655 Status = NtReadFile (KeyboardDeviceHandle,
656 NULL,
657 NULL,
658 NULL,
659 &Iosb,
660 &NextKeyInput,
661 sizeof(KEYBOARD_INPUT_DATA),
662 NULL,
663 NULL);
664 DPRINT("KeyRaw: %s %04x\n",
665 (NextKeyInput.Flags & KEY_BREAK) ? "up":"down",
666 NextKeyInput.MakeCode );
667
668 if (Status == STATUS_ALERTED && !InputThreadsRunning)
669 goto KeyboardEscape;
670
671 }
672 while ((!(NextKeyInput.Flags & KEY_BREAK)) &&
673 NextKeyInput.MakeCode == KeyInput.MakeCode);
674 /* ^ Ignore repeats, they'll be KEY_MAKE and the same
675 * code. I'm not caring about the counting, not sure
676 * if that matters. I think not.
677 */
678
679 /* If the ModifierState is now empty again, send a
680 * special notification and eat both keypresses
681 */
682
683 fsNextModifiers = IntKeyboardGetModifiers(&NextKeyInput);
684
685 if (fsNextModifiers)
686 ModifierState ^= fsNextModifiers;
687
688 if (ModifierState == 0)
689 {
690 if (fsModifiers == MOD_WIN)
691 IntKeyboardSendWinKeyMsg();
692 else if (fsModifiers == MOD_ALT)
693 {
694 gQueueKeyStateTable[VK_MENU] = 0;
695 if(bLeftAlt)
696 {
697 gQueueKeyStateTable[VK_LMENU] = 0;
698 }
699 else
700 {
701 gQueueKeyStateTable[VK_RMENU] = 0;
702 }
703 co_IntKeyboardSendAltKeyMsg();
704 }
705 continue;
706 }
707
708 NumKeys = 2;
709 }
710 }
711 }
712
713 for (;NumKeys;memcpy(&KeyInput, &NextKeyInput, sizeof(KeyInput)),
714 NumKeys--)
715 {
716 lParam = 0;
717
718 IntKeyboardUpdateLeds(KeyboardDeviceHandle,
719 &KeyInput,
720 IndicatorTrans);
721
722 /* While we are working, we set up lParam. The format is:
723 * 0-15: The number of times this key has autorepeated
724 * 16-23: The keyboard scancode
725 * 24: Set if it's and extended key (I assume KEY_E0 | KEY_E1)
726 * Note that E1 is only used for PAUSE (E1-1D-45) and
727 * E0-45 happens not to be anything.
728 * 29: Alt is pressed ('Context code')
729 * 30: Previous state, if the key was down before this message
730 * This is a cheap way to ignore autorepeat keys
731 * 31: 1 if the key is being pressed
732 */
733
734 /* If it's a KEY_MAKE (which is 0, so test using !KEY_BREAK)
735 * and it's the same key as the last one, increase the repeat
736 * count.
737 */
738
739 if (!(KeyInput.Flags & KEY_BREAK))
740 {
741 if (((KeyInput.Flags & (KEY_E0 | KEY_E1)) == LastFlags) &&
742 (KeyInput.MakeCode == LastMakeCode))
743 {
744 RepeatCount++;
745 lParam |= (1 << 30);
746 }
747 else
748 {
749 RepeatCount = 1;
750 LastFlags = KeyInput.Flags & (KEY_E0 | KEY_E1);
751 LastMakeCode = KeyInput.MakeCode;
752 }
753 }
754 else
755 {
756 LastFlags = 0;
757 LastMakeCode = 0; /* Should never match */
758 lParam |= (1 << 30) | (1 << 31);
759 }
760
761 lParam |= RepeatCount;
762
763 lParam |= (KeyInput.MakeCode & 0xff) << 16;
764
765 if (KeyInput.Flags & KEY_E0)
766 lParam |= (1 << 24);
767
768 if (ModifierState & MOD_ALT)
769 {
770 lParam |= (1 << 29);
771
772 if (!(KeyInput.Flags & KEY_BREAK))
773 msg.message = WM_SYSKEYDOWN;
774 else
775 msg.message = WM_SYSKEYUP;
776 }
777 else
778 {
779 if (!(KeyInput.Flags & KEY_BREAK))
780 msg.message = WM_KEYDOWN;
781 else
782 msg.message = WM_KEYUP;
783 }
784
785 /* Find the target thread whose locale is in effect */
786 FocusQueue = IntGetFocusMessageQueue();
787
788 /* This might cause us to lose hot keys, which are important
789 * (ctrl-alt-del secure attention sequence). Not sure if it
790 * can happen though.
791 */
792 if (!FocusQueue)
793 continue;
794
795 msg.lParam = lParam;
796 msg.hwnd = FocusQueue->FocusWindow;
797
798 FocusThread = FocusQueue->Thread;
799
800 if (!(FocusThread && FocusThread->Tcb.Win32Thread &&
801 ((PTHREADINFO)FocusThread->Tcb.Win32Thread)->KeyboardLayout))
802 continue;
803
804 /* This function uses lParam to fill wParam according to the
805 * keyboard layout in use.
806 */
807 W32kKeyProcessMessage(&msg,
808 ((PTHREADINFO)FocusThread->Tcb.Win32Thread)->KeyboardLayout->KBTables,
809 KeyInput.Flags & KEY_E0 ? 0xE0 :
810 (KeyInput.Flags & KEY_E1 ? 0xE1 : 0));
811
812 if (GetHotKey(ModifierState,
813 msg.wParam,
814 &Thread,
815 &hWnd,
816 &id))
817 {
818 if (!(KeyInput.Flags & KEY_BREAK))
819 {
820 DPRINT("Hot key pressed (hWnd %lx, id %d)\n", hWnd, id);
821 MsqPostHotKeyMessage (Thread,
822 hWnd,
823 (WPARAM)id,
824 MAKELPARAM((WORD)ModifierState,
825 (WORD)msg.wParam));
826 }
827 continue; /* Eat key up motion too */
828 }
829
830 /*
831 * Post a keyboard message.
832 */
833 co_MsqPostKeyboardMessage(msg.message,msg.wParam,msg.lParam);
834 }
835 }
836
837 KeyboardEscape:
838 DPRINT( "KeyboardInput Thread Stopped...\n" );
839 }
840 }
841
842
843 static PVOID Objects[2];
844 /*
845 Raw Input Thread.
846 Since this relies on InputThreadsStart, just fake it.
847 */
848 static VOID APIENTRY
849 RawInputThreadMain(PVOID StartContext)
850 {
851 NTSTATUS Status;
852 LARGE_INTEGER DueTime;
853
854 DueTime.QuadPart = (LONGLONG)(-10000000);
855
856 do
857 {
858 KEVENT Event;
859 KeInitializeEvent(&Event, NotificationEvent, FALSE);
860 Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
861 } while (!NT_SUCCESS(Status));
862
863
864 Objects[0] = &InputThreadsStart;
865
866 MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), TAG_INPUT);
867 if (!MasterTimer)
868 {
869 DPRINT1("Win32K: Failed making Raw Input thread a win32 thread.\n");
870 return;
871 }
872 KeInitializeTimer(MasterTimer);
873 Objects[1] = MasterTimer;
874
875 // This thread requires win32k!
876 Status = Win32kInitWin32Thread(PsGetCurrentThread());
877 if (!NT_SUCCESS(Status))
878 {
879 DPRINT1("Win32K: Failed making Raw Input thread a win32 thread.\n");
880 return; //(Status);
881 }
882
883 ptiRawInput = PsGetCurrentThreadWin32Thread();
884 DPRINT("\nRaw Input Thread 0x%x \n", ptiRawInput);
885
886
887 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
888 LOW_REALTIME_PRIORITY + 3);
889
890 UserEnterExclusive();
891 StartTheTimers();
892 UserLeave();
893
894 //
895 // ATM, we just have one job to handle, merge the other two later.
896 //
897 for(;;)
898 {
899 DPRINT( "Raw Input Thread Waiting for start event\n" );
900
901 Status = KeWaitForMultipleObjects( 2,
902 Objects,
903 WaitAll, //WaitAny,
904 WrUserRequest,
905 KernelMode,
906 TRUE,
907 NULL,
908 NULL);
909 DPRINT( "Raw Input Thread Starting...\n" );
910
911 ProcessTimers();
912 }
913 DPRINT1("Raw Input Thread Exit!\n");
914 }
915
916 NTSTATUS FASTCALL
917 InitInputImpl(VOID)
918 {
919 NTSTATUS Status;
920
921 KeInitializeEvent(&InputThreadsStart, NotificationEvent, FALSE);
922
923 /* Initialize the default keyboard layout */
924 if(!UserInitDefaultKeyboardLayout())
925 {
926 DPRINT1("Failed to initialize default keyboard layout!\n");
927 }
928
929 Status = PsCreateSystemThread(&RawInputThreadHandle,
930 THREAD_ALL_ACCESS,
931 NULL,
932 NULL,
933 &RawInputThreadId,
934 RawInputThreadMain,
935 NULL);
936 if (!NT_SUCCESS(Status))
937 {
938 DPRINT1("Win32K: Failed to create raw thread.\n");
939 }
940
941 Status = PsCreateSystemThread(&KeyboardThreadHandle,
942 THREAD_ALL_ACCESS,
943 NULL,
944 NULL,
945 &KeyboardThreadId,
946 KeyboardThreadMain,
947 NULL);
948 if (!NT_SUCCESS(Status))
949 {
950 DPRINT1("Win32K: Failed to create keyboard thread.\n");
951 }
952
953 Status = PsCreateSystemThread(&MouseThreadHandle,
954 THREAD_ALL_ACCESS,
955 NULL,
956 NULL,
957 &MouseThreadId,
958 MouseThreadMain,
959 NULL);
960 if (!NT_SUCCESS(Status))
961 {
962 DPRINT1("Win32K: Failed to create mouse thread.\n");
963 }
964
965 InputThreadsRunning = TRUE;
966 KeSetEvent(&InputThreadsStart, IO_NO_INCREMENT, FALSE);
967
968 return STATUS_SUCCESS;
969 }
970
971 NTSTATUS FASTCALL
972 CleanupInputImp(VOID)
973 {
974 return(STATUS_SUCCESS);
975 }
976
977 BOOL
978 APIENTRY
979 NtUserDragDetect(
980 HWND hWnd,
981 POINT pt) // Just like the User call.
982 {
983 UNIMPLEMENTED
984 return 0;
985 }
986
987 BOOL FASTCALL
988 IntBlockInput(PTHREADINFO W32Thread, BOOL BlockIt)
989 {
990 PTHREADINFO OldBlock;
991 ASSERT(W32Thread);
992
993 if(!W32Thread->Desktop || ((W32Thread->TIF_flags & TIF_INCLEANUP) && BlockIt))
994 {
995 /*
996 * fail blocking if exiting the thread
997 */
998
999 return FALSE;
1000 }
1001
1002 /*
1003 * FIXME - check access rights of the window station
1004 * e.g. services running in the service window station cannot block input
1005 */
1006 if(!ThreadHasInputAccess(W32Thread) ||
1007 !IntIsActiveDesktop(W32Thread->Desktop))
1008 {
1009 SetLastWin32Error(ERROR_ACCESS_DENIED);
1010 return FALSE;
1011 }
1012
1013 ASSERT(W32Thread->Desktop);
1014 OldBlock = W32Thread->Desktop->BlockInputThread;
1015 if(OldBlock)
1016 {
1017 if(OldBlock != W32Thread)
1018 {
1019 SetLastWin32Error(ERROR_ACCESS_DENIED);
1020 return FALSE;
1021 }
1022 W32Thread->Desktop->BlockInputThread = (BlockIt ? W32Thread : NULL);
1023 return OldBlock == NULL;
1024 }
1025
1026 W32Thread->Desktop->BlockInputThread = (BlockIt ? W32Thread : NULL);
1027 return OldBlock == NULL;
1028 }
1029
1030 BOOL
1031 APIENTRY
1032 NtUserBlockInput(
1033 BOOL BlockIt)
1034 {
1035 DECLARE_RETURN(BOOLEAN);
1036
1037 DPRINT("Enter NtUserBlockInput\n");
1038 UserEnterExclusive();
1039
1040 RETURN( IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt));
1041
1042 CLEANUP:
1043 DPRINT("Leave NtUserBlockInput, ret=%i\n",_ret_);
1044 UserLeave();
1045 END_CLEANUP;
1046 }
1047
1048 BOOL FASTCALL
1049 IntMouseInput(MOUSEINPUT *mi)
1050 {
1051 const UINT SwapBtnMsg[2][2] =
1052 {
1053 {
1054 WM_LBUTTONDOWN, WM_RBUTTONDOWN
1055 },
1056 {WM_LBUTTONUP, WM_RBUTTONUP}
1057 };
1058 const WPARAM SwapBtn[2] =
1059 {
1060 MK_LBUTTON, MK_RBUTTON
1061 };
1062 POINT MousePos = {0}, OrgPos;
1063 PSYSTEM_CURSORINFO CurInfo;
1064 PWINSTATION_OBJECT WinSta;
1065 BOOL DoMove, SwapButtons;
1066 MSG Msg;
1067 SURFACE *psurf;
1068 SURFOBJ *pso;
1069 PDC dc;
1070 PWINDOW_OBJECT DesktopWindow;
1071
1072 #if 1
1073
1074 HDC hDC;
1075
1076 /* FIXME - get the screen dc from the window station or desktop */
1077 if(!(hDC = IntGetScreenDC()))
1078 {
1079 return FALSE;
1080 }
1081 #endif
1082
1083 ASSERT(mi);
1084 #if 0
1085
1086 WinSta = PsGetCurrentProcessWin32Process()->WindowStation;
1087 #else
1088 /* FIXME - ugly hack but as long as we're using this dumb callback from the
1089 mouse class driver, we can't access the window station from the calling
1090 process */
1091 WinSta = InputWindowStation;
1092 #endif
1093
1094 ASSERT(WinSta);
1095
1096 CurInfo = IntGetSysCursorInfo(WinSta);
1097
1098 if(!mi->time)
1099 {
1100 LARGE_INTEGER LargeTickCount;
1101 KeQueryTickCount(&LargeTickCount);
1102 mi->time = MsqCalculateMessageTime(&LargeTickCount);
1103 }
1104
1105 SwapButtons = gspv.bMouseBtnSwap;
1106 DoMove = FALSE;
1107
1108 IntGetCursorLocation(WinSta, &MousePos);
1109 OrgPos.x = MousePos.x;
1110 OrgPos.y = MousePos.y;
1111
1112 if(mi->dwFlags & MOUSEEVENTF_MOVE)
1113 {
1114 if(mi->dwFlags & MOUSEEVENTF_ABSOLUTE)
1115 {
1116 MousePos.x = mi->dx;
1117 MousePos.y = mi->dy;
1118 }
1119 else
1120 {
1121 MousePos.x += mi->dx;
1122 MousePos.y += mi->dy;
1123 }
1124
1125 DesktopWindow = IntGetWindowObject(WinSta->ActiveDesktop->DesktopWindow);
1126
1127 if (DesktopWindow)
1128 {
1129 if(MousePos.x >= DesktopWindow->Wnd->rcClient.right)
1130 MousePos.x = DesktopWindow->Wnd->rcClient.right - 1;
1131 if(MousePos.y >= DesktopWindow->Wnd->rcClient.bottom)
1132 MousePos.y = DesktopWindow->Wnd->rcClient.bottom - 1;
1133 UserDereferenceObject(DesktopWindow);
1134 }
1135
1136 if(MousePos.x < 0)
1137 MousePos.x = 0;
1138 if(MousePos.y < 0)
1139 MousePos.y = 0;
1140
1141 if(CurInfo->CursorClipInfo.IsClipped)
1142 {
1143 /* The mouse cursor needs to be clipped */
1144
1145 if(MousePos.x >= (LONG)CurInfo->CursorClipInfo.Right)
1146 MousePos.x = (LONG)CurInfo->CursorClipInfo.Right;
1147 if(MousePos.x < (LONG)CurInfo->CursorClipInfo.Left)
1148 MousePos.x = (LONG)CurInfo->CursorClipInfo.Left;
1149 if(MousePos.y >= (LONG)CurInfo->CursorClipInfo.Bottom)
1150 MousePos.y = (LONG)CurInfo->CursorClipInfo.Bottom;
1151 if(MousePos.y < (LONG)CurInfo->CursorClipInfo.Top)
1152 MousePos.y = (LONG)CurInfo->CursorClipInfo.Top;
1153 }
1154
1155 DoMove = (MousePos.x != OrgPos.x || MousePos.y != OrgPos.y);
1156 }
1157
1158 if (DoMove)
1159 {
1160 dc = DC_LockDc(hDC);
1161 if (dc)
1162 {
1163 psurf = dc->dclevel.pSurface;
1164 if (psurf)
1165 {
1166 pso = &psurf->SurfObj;
1167
1168 if (CurInfo->ShowingCursor)
1169 {
1170 IntEngMovePointer(pso, MousePos.x, MousePos.y, &(GDIDEV(pso)->Pointer.Exclude));
1171 }
1172 /* Only now, update the info in the PDEVOBJ, so EngMovePointer can
1173 * use the old values to move the pointer image */
1174 gpsi->ptCursor.x = MousePos.x;
1175 gpsi->ptCursor.y = MousePos.y;
1176 }
1177
1178 DC_UnlockDc(dc);
1179 }
1180 }
1181
1182 /*
1183 * Insert the messages into the system queue
1184 */
1185
1186 Msg.wParam = CurInfo->ButtonsDown;
1187 Msg.lParam = MAKELPARAM(MousePos.x, MousePos.y);
1188 Msg.pt = MousePos;
1189
1190 if (gQueueKeyStateTable[VK_SHIFT] & 0xc0)
1191 {
1192 Msg.wParam |= MK_SHIFT;
1193 }
1194
1195 if (gQueueKeyStateTable[VK_CONTROL] & 0xc0)
1196 {
1197 Msg.wParam |= MK_CONTROL;
1198 }
1199
1200 if(DoMove)
1201 {
1202 Msg.message = WM_MOUSEMOVE;
1203 MsqInsertSystemMessage(&Msg);
1204 }
1205
1206 Msg.message = 0;
1207 if(mi->dwFlags & MOUSEEVENTF_LEFTDOWN)
1208 {
1209 gQueueKeyStateTable[VK_LBUTTON] |= 0xc0;
1210 Msg.message = SwapBtnMsg[0][SwapButtons];
1211 CurInfo->ButtonsDown |= SwapBtn[SwapButtons];
1212 MsqInsertSystemMessage(&Msg);
1213 }
1214 else if(mi->dwFlags & MOUSEEVENTF_LEFTUP)
1215 {
1216 gQueueKeyStateTable[VK_LBUTTON] &= ~0x80;
1217 Msg.message = SwapBtnMsg[1][SwapButtons];
1218 CurInfo->ButtonsDown &= ~SwapBtn[SwapButtons];
1219 MsqInsertSystemMessage(&Msg);
1220 }
1221 if(mi->dwFlags & MOUSEEVENTF_MIDDLEDOWN)
1222 {
1223 gQueueKeyStateTable[VK_MBUTTON] |= 0xc0;
1224 Msg.message = WM_MBUTTONDOWN;
1225 CurInfo->ButtonsDown |= MK_MBUTTON;
1226 MsqInsertSystemMessage(&Msg);
1227 }
1228 else if(mi->dwFlags & MOUSEEVENTF_MIDDLEUP)
1229 {
1230 gQueueKeyStateTable[VK_MBUTTON] &= ~0x80;
1231 Msg.message = WM_MBUTTONUP;
1232 CurInfo->ButtonsDown &= ~MK_MBUTTON;
1233 MsqInsertSystemMessage(&Msg);
1234 }
1235 if(mi->dwFlags & MOUSEEVENTF_RIGHTDOWN)
1236 {
1237 gQueueKeyStateTable[VK_RBUTTON] |= 0xc0;
1238 Msg.message = SwapBtnMsg[0][!SwapButtons];
1239 CurInfo->ButtonsDown |= SwapBtn[!SwapButtons];
1240 MsqInsertSystemMessage(&Msg);
1241 }
1242 else if(mi->dwFlags & MOUSEEVENTF_RIGHTUP)
1243 {
1244 gQueueKeyStateTable[VK_RBUTTON] &= ~0x80;
1245 Msg.message = SwapBtnMsg[1][!SwapButtons];
1246 CurInfo->ButtonsDown &= ~SwapBtn[!SwapButtons];
1247 MsqInsertSystemMessage(&Msg);
1248 }
1249
1250 if((mi->dwFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)) &&
1251 (mi->dwFlags & MOUSEEVENTF_WHEEL))
1252 {
1253 /* fail because both types of events use the mouseData field */
1254 return FALSE;
1255 }
1256
1257 if(mi->dwFlags & MOUSEEVENTF_XDOWN)
1258 {
1259 Msg.message = WM_XBUTTONDOWN;
1260 if(mi->mouseData & XBUTTON1)
1261 {
1262 gQueueKeyStateTable[VK_XBUTTON1] |= 0xc0;
1263 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
1264 CurInfo->ButtonsDown |= XBUTTON1;
1265 MsqInsertSystemMessage(&Msg);
1266 }
1267 if(mi->mouseData & XBUTTON2)
1268 {
1269 gQueueKeyStateTable[VK_XBUTTON2] |= 0xc0;
1270 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
1271 CurInfo->ButtonsDown |= XBUTTON2;
1272 MsqInsertSystemMessage(&Msg);
1273 }
1274 }
1275 else if(mi->dwFlags & MOUSEEVENTF_XUP)
1276 {
1277 Msg.message = WM_XBUTTONUP;
1278 if(mi->mouseData & XBUTTON1)
1279 {
1280 gQueueKeyStateTable[VK_XBUTTON1] &= ~0x80;
1281 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
1282 CurInfo->ButtonsDown &= ~XBUTTON1;
1283 MsqInsertSystemMessage(&Msg);
1284 }
1285 if(mi->mouseData & XBUTTON2)
1286 {
1287 gQueueKeyStateTable[VK_XBUTTON2] &= ~0x80;
1288 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
1289 CurInfo->ButtonsDown &= ~XBUTTON2;
1290 MsqInsertSystemMessage(&Msg);
1291 }
1292 }
1293 if(mi->dwFlags & MOUSEEVENTF_WHEEL)
1294 {
1295 Msg.message = WM_MOUSEWHEEL;
1296 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, mi->mouseData);
1297 MsqInsertSystemMessage(&Msg);
1298 }
1299
1300 return TRUE;
1301 }
1302
1303 BOOL FASTCALL
1304 IntKeyboardInput(KEYBDINPUT *ki)
1305 {
1306 return FALSE;
1307 }
1308
1309 BOOL FASTCALL
1310 UserAttachThreadInput( PTHREADINFO pti, PTHREADINFO ptiTo, BOOL fAttach)
1311 {
1312 PATTACHINFO pai;
1313
1314 /* Can not be the same thread.*/
1315 if (pti == ptiTo) return FALSE;
1316
1317 /* Do not attach to system threads or between different desktops. */
1318 if ( pti->TIF_flags & TIF_DONTATTACHQUEUE ||
1319 ptiTo->TIF_flags & TIF_DONTATTACHQUEUE ||
1320 pti->Desktop != ptiTo->Desktop )
1321 return FALSE;
1322
1323 /* If Attach set, allocate and link. */
1324 if ( fAttach )
1325 {
1326 pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), TAG_ATTACHINFO);
1327 if ( !pai ) return FALSE;
1328
1329 pai->paiNext = gpai;
1330 pai->pti1 = pti;
1331 pai->pti2 = ptiTo;
1332 gpai = pai;
1333 }
1334 else /* If clear, unlink and free it. */
1335 {
1336 PATTACHINFO paiprev = NULL;
1337
1338 if ( !gpai ) return FALSE;
1339
1340 pai = gpai;
1341
1342 /* Search list and free if found or return false. */
1343 do
1344 {
1345 if ( pai->pti2 == ptiTo && pai->pti1 == pti ) break;
1346 paiprev = pai;
1347 pai = pai->paiNext;
1348 } while (pai);
1349
1350 if ( !pai ) return FALSE;
1351
1352 if (paiprev) paiprev->paiNext = pai->paiNext;
1353
1354 ExFreePoolWithTag(pai, TAG_ATTACHINFO);
1355 }
1356
1357 return TRUE;
1358 }
1359
1360 UINT
1361 APIENTRY
1362 NtUserSendInput(
1363 UINT nInputs,
1364 LPINPUT pInput,
1365 INT cbSize)
1366 {
1367 PTHREADINFO W32Thread;
1368 UINT cnt;
1369 DECLARE_RETURN(UINT);
1370
1371 DPRINT("Enter NtUserSendInput\n");
1372 UserEnterExclusive();
1373
1374 W32Thread = PsGetCurrentThreadWin32Thread();
1375 ASSERT(W32Thread);
1376
1377 if(!W32Thread->Desktop)
1378 {
1379 RETURN( 0);
1380 }
1381
1382 if(!nInputs || !pInput || (cbSize != sizeof(INPUT)))
1383 {
1384 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1385 RETURN( 0);
1386 }
1387
1388 /*
1389 * FIXME - check access rights of the window station
1390 * e.g. services running in the service window station cannot block input
1391 */
1392 if(!ThreadHasInputAccess(W32Thread) ||
1393 !IntIsActiveDesktop(W32Thread->Desktop))
1394 {
1395 SetLastWin32Error(ERROR_ACCESS_DENIED);
1396 RETURN( 0);
1397 }
1398
1399 cnt = 0;
1400 while(nInputs--)
1401 {
1402 INPUT SafeInput;
1403 NTSTATUS Status;
1404
1405 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
1406 if(!NT_SUCCESS(Status))
1407 {
1408 SetLastNtError(Status);
1409 RETURN( cnt);
1410 }
1411
1412 switch(SafeInput.type)
1413 {
1414 case INPUT_MOUSE:
1415 if(IntMouseInput(&SafeInput.mi))
1416 {
1417 cnt++;
1418 }
1419 break;
1420 case INPUT_KEYBOARD:
1421 if(IntKeyboardInput(&SafeInput.ki))
1422 {
1423 cnt++;
1424 }
1425 break;
1426 case INPUT_HARDWARE:
1427 break;
1428 #ifndef NDEBUG
1429
1430 default:
1431 DPRINT1("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
1432 break;
1433 #endif
1434
1435 }
1436 }
1437
1438 RETURN( cnt);
1439
1440 CLEANUP:
1441 DPRINT("Leave NtUserSendInput, ret=%i\n",_ret_);
1442 UserLeave();
1443 END_CLEANUP;
1444 }
1445
1446 /* EOF */