Reintegrate header branch
[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 static BYTE TrackSysKey = 0; /* determine whether ALT key up will cause a WM_SYSKEYUP
39 or a WM_KEYUP message */
40
41 /* FUNCTIONS *****************************************************************/
42 DWORD IntLastInputTick(BOOL LastInputTickSetGet);
43
44 #define ClearMouseInput(mi) \
45 mi.dx = 0; \
46 mi.dy = 0; \
47 mi.mouseData = 0; \
48 mi.dwFlags = 0;
49
50 #define SendMouseEvent(mi) \
51 if(mi.dx != 0 || mi.dy != 0) \
52 mi.dwFlags |= MOUSEEVENTF_MOVE; \
53 if(mi.dwFlags) \
54 IntMouseInput(&mi); \
55 ClearMouseInput(mi);
56
57
58 DWORD IntLastInputTick(BOOL LastInputTickSetGet)
59 {
60 static DWORD LastInputTick = 0;
61 if (LastInputTickSetGet == TRUE)
62 {
63 LARGE_INTEGER TickCount;
64 KeQueryTickCount(&TickCount);
65 LastInputTick = TickCount.u.LowPart * (KeQueryTimeIncrement() / 10000);
66 }
67 return LastInputTick;
68 }
69
70 BOOL
71 APIENTRY
72 NtUserGetLastInputInfo(PLASTINPUTINFO plii)
73 {
74 BOOL ret = TRUE;
75
76 UserEnterShared();
77
78 _SEH2_TRY
79 {
80 if (ProbeForReadUint(&plii->cbSize) != sizeof(LASTINPUTINFO))
81 {
82 SetLastWin32Error(ERROR_INVALID_PARAMETER);
83 ret = FALSE;
84 _SEH2_LEAVE;
85 }
86
87 ProbeForWrite(plii, sizeof(LASTINPUTINFO), sizeof(DWORD));
88
89 plii->dwTime = IntLastInputTick(FALSE);
90 }
91 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
92 {
93 SetLastNtError(_SEH2_GetExceptionCode());
94 ret = FALSE;
95 }
96 _SEH2_END;
97
98 UserLeave();
99
100 return ret;
101 }
102
103
104 VOID FASTCALL
105 ProcessMouseInputData(PMOUSE_INPUT_DATA Data, ULONG InputCount)
106 {
107 PMOUSE_INPUT_DATA mid;
108 MOUSEINPUT mi;
109 ULONG i;
110
111 ClearMouseInput(mi);
112 mi.time = 0;
113 mi.dwExtraInfo = 0;
114 for(i = 0; i < InputCount; i++)
115 {
116 mid = (Data + i);
117 mi.dx += mid->LastX;
118 mi.dy += mid->LastY;
119
120 /* Check if the mouse move is absolute */
121 if (mid->Flags == MOUSE_MOVE_ABSOLUTE)
122 {
123 /* Set flag to convert to screen location */
124 mi.dwFlags |= MOUSEEVENTF_ABSOLUTE;
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->pti->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->rpdesk || ((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->rpdesk))
1008 {
1009 SetLastWin32Error(ERROR_ACCESS_DENIED);
1010 return FALSE;
1011 }
1012
1013 ASSERT(W32Thread->rpdesk);
1014 OldBlock = W32Thread->rpdesk->BlockInputThread;
1015 if(OldBlock)
1016 {
1017 if(OldBlock != W32Thread)
1018 {
1019 SetLastWin32Error(ERROR_ACCESS_DENIED);
1020 return FALSE;
1021 }
1022 W32Thread->rpdesk->BlockInputThread = (BlockIt ? W32Thread : NULL);
1023 return OldBlock == NULL;
1024 }
1025
1026 W32Thread->rpdesk->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 {WM_LBUTTONDOWN, WM_RBUTTONDOWN},
1054 {WM_LBUTTONUP, WM_RBUTTONUP}
1055 };
1056 const WPARAM SwapBtn[2] =
1057 {
1058 MK_LBUTTON, MK_RBUTTON
1059 };
1060 POINT MousePos;
1061 PSYSTEM_CURSORINFO CurInfo;
1062 BOOL SwapButtons;
1063 MSG Msg;
1064
1065 ASSERT(mi);
1066
1067 CurInfo = IntGetSysCursorInfo();
1068
1069 if(!mi->time)
1070 {
1071 LARGE_INTEGER LargeTickCount;
1072 KeQueryTickCount(&LargeTickCount);
1073 mi->time = MsqCalculateMessageTime(&LargeTickCount);
1074 }
1075
1076 SwapButtons = gspv.bMouseBtnSwap;
1077
1078 MousePos = gpsi->ptCursor;
1079
1080 if(mi->dwFlags & MOUSEEVENTF_MOVE)
1081 {
1082 if(mi->dwFlags & MOUSEEVENTF_ABSOLUTE)
1083 {
1084 MousePos.x = mi->dx * UserGetSystemMetrics(SM_CXVIRTUALSCREEN) >> 16;
1085 MousePos.y = mi->dy * UserGetSystemMetrics(SM_CYVIRTUALSCREEN) >> 16;
1086 }
1087 else
1088 {
1089 MousePos.x += mi->dx;
1090 MousePos.y += mi->dy;
1091 }
1092 }
1093
1094 /*
1095 * Insert the messages into the system queue
1096 */
1097 Msg.wParam = CurInfo->ButtonsDown;
1098 Msg.lParam = MAKELPARAM(MousePos.x, MousePos.y);
1099 Msg.pt = MousePos;
1100
1101 if (gQueueKeyStateTable[VK_SHIFT] & 0xc0)
1102 {
1103 Msg.wParam |= MK_SHIFT;
1104 }
1105
1106 if (gQueueKeyStateTable[VK_CONTROL] & 0xc0)
1107 {
1108 Msg.wParam |= MK_CONTROL;
1109 }
1110
1111 if(mi->dwFlags & MOUSEEVENTF_MOVE)
1112 {
1113 UserSetCursorPos(MousePos.x, MousePos.y);
1114 }
1115 if(mi->dwFlags & MOUSEEVENTF_LEFTDOWN)
1116 {
1117 gQueueKeyStateTable[VK_LBUTTON] |= 0xc0;
1118 Msg.message = SwapBtnMsg[0][SwapButtons];
1119 CurInfo->ButtonsDown |= SwapBtn[SwapButtons];
1120 MsqInsertSystemMessage(&Msg);
1121 }
1122 else if(mi->dwFlags & MOUSEEVENTF_LEFTUP)
1123 {
1124 gQueueKeyStateTable[VK_LBUTTON] &= ~0x80;
1125 Msg.message = SwapBtnMsg[1][SwapButtons];
1126 CurInfo->ButtonsDown &= ~SwapBtn[SwapButtons];
1127 MsqInsertSystemMessage(&Msg);
1128 }
1129 if(mi->dwFlags & MOUSEEVENTF_MIDDLEDOWN)
1130 {
1131 gQueueKeyStateTable[VK_MBUTTON] |= 0xc0;
1132 Msg.message = WM_MBUTTONDOWN;
1133 CurInfo->ButtonsDown |= MK_MBUTTON;
1134 MsqInsertSystemMessage(&Msg);
1135 }
1136 else if(mi->dwFlags & MOUSEEVENTF_MIDDLEUP)
1137 {
1138 gQueueKeyStateTable[VK_MBUTTON] &= ~0x80;
1139 Msg.message = WM_MBUTTONUP;
1140 CurInfo->ButtonsDown &= ~MK_MBUTTON;
1141 MsqInsertSystemMessage(&Msg);
1142 }
1143 if(mi->dwFlags & MOUSEEVENTF_RIGHTDOWN)
1144 {
1145 gQueueKeyStateTable[VK_RBUTTON] |= 0xc0;
1146 Msg.message = SwapBtnMsg[0][!SwapButtons];
1147 CurInfo->ButtonsDown |= SwapBtn[!SwapButtons];
1148 MsqInsertSystemMessage(&Msg);
1149 }
1150 else if(mi->dwFlags & MOUSEEVENTF_RIGHTUP)
1151 {
1152 gQueueKeyStateTable[VK_RBUTTON] &= ~0x80;
1153 Msg.message = SwapBtnMsg[1][!SwapButtons];
1154 CurInfo->ButtonsDown &= ~SwapBtn[!SwapButtons];
1155 MsqInsertSystemMessage(&Msg);
1156 }
1157
1158 if((mi->dwFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)) &&
1159 (mi->dwFlags & MOUSEEVENTF_WHEEL))
1160 {
1161 /* fail because both types of events use the mouseData field */
1162 return FALSE;
1163 }
1164
1165 if(mi->dwFlags & MOUSEEVENTF_XDOWN)
1166 {
1167 Msg.message = WM_XBUTTONDOWN;
1168 if(mi->mouseData & XBUTTON1)
1169 {
1170 gQueueKeyStateTable[VK_XBUTTON1] |= 0xc0;
1171 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
1172 CurInfo->ButtonsDown |= XBUTTON1;
1173 MsqInsertSystemMessage(&Msg);
1174 }
1175 if(mi->mouseData & XBUTTON2)
1176 {
1177 gQueueKeyStateTable[VK_XBUTTON2] |= 0xc0;
1178 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
1179 CurInfo->ButtonsDown |= XBUTTON2;
1180 MsqInsertSystemMessage(&Msg);
1181 }
1182 }
1183 else if(mi->dwFlags & MOUSEEVENTF_XUP)
1184 {
1185 Msg.message = WM_XBUTTONUP;
1186 if(mi->mouseData & XBUTTON1)
1187 {
1188 gQueueKeyStateTable[VK_XBUTTON1] &= ~0x80;
1189 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
1190 CurInfo->ButtonsDown &= ~XBUTTON1;
1191 MsqInsertSystemMessage(&Msg);
1192 }
1193 if(mi->mouseData & XBUTTON2)
1194 {
1195 gQueueKeyStateTable[VK_XBUTTON2] &= ~0x80;
1196 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
1197 CurInfo->ButtonsDown &= ~XBUTTON2;
1198 MsqInsertSystemMessage(&Msg);
1199 }
1200 }
1201 if(mi->dwFlags & MOUSEEVENTF_WHEEL)
1202 {
1203 Msg.message = WM_MOUSEWHEEL;
1204 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, mi->mouseData);
1205 MsqInsertSystemMessage(&Msg);
1206 }
1207
1208 return TRUE;
1209 }
1210
1211 BOOL FASTCALL
1212 IntKeyboardInput(KEYBDINPUT *ki)
1213 {
1214 PUSER_MESSAGE_QUEUE FocusMessageQueue;
1215 PTHREADINFO pti;
1216 MSG Msg;
1217 LARGE_INTEGER LargeTickCount;
1218 KBDLLHOOKSTRUCT KbdHookData;
1219 WORD flags, wVkStripped, wVkL, wVkR, wVk = ki->wVk, vk_hook = ki->wVk;
1220 BOOLEAN Entered = FALSE;
1221
1222 Msg.lParam = 0;
1223
1224 // Condition may arise when calling MsqPostMessage and waiting for an event.
1225 if (!UserIsEntered())
1226 {
1227 // Fixme: Not sure ATM if this thread is locked.
1228 UserEnterExclusive();
1229 Entered = TRUE;
1230 }
1231
1232 wVk = LOBYTE(wVk);
1233 Msg.wParam = wVk;
1234 flags = LOBYTE(ki->wScan);
1235
1236 if (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) flags |= KF_EXTENDED;
1237 /* FIXME: set KF_DLGMODE and KF_MENUMODE when needed */
1238
1239 /* strip left/right for menu, control, shift */
1240 switch (wVk)
1241 {
1242 case VK_MENU:
1243 case VK_LMENU:
1244 case VK_RMENU:
1245 wVk = (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) ? VK_RMENU : VK_LMENU;
1246 wVkStripped = VK_MENU;
1247 wVkL = VK_LMENU;
1248 wVkR = VK_RMENU;
1249 break;
1250 case VK_CONTROL:
1251 case VK_LCONTROL:
1252 case VK_RCONTROL:
1253 wVk = (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) ? VK_RCONTROL : VK_LCONTROL;
1254 wVkStripped = VK_CONTROL;
1255 wVkL = VK_LCONTROL;
1256 wVkR = VK_RCONTROL;
1257 break;
1258 case VK_SHIFT:
1259 case VK_LSHIFT:
1260 case VK_RSHIFT:
1261 wVk = (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) ? VK_RSHIFT : VK_LSHIFT;
1262 wVkStripped = VK_SHIFT;
1263 wVkL = VK_LSHIFT;
1264 wVkR = VK_RSHIFT;
1265 break;
1266 default:
1267 wVkStripped = wVkL = wVkR = wVk;
1268 }
1269
1270 if (ki->dwFlags & KEYEVENTF_KEYUP)
1271 {
1272 Msg.message = WM_KEYUP;
1273 if ((gQueueKeyStateTable[VK_MENU] & 0x80) &&
1274 ((wVkStripped == VK_MENU) || (wVkStripped == VK_CONTROL)
1275 || !(gQueueKeyStateTable[VK_CONTROL] & 0x80)))
1276 {
1277 if( TrackSysKey == VK_MENU || /* <ALT>-down/<ALT>-up sequence */
1278 (wVkStripped != VK_MENU)) /* <ALT>-down...<something else>-up */
1279 Msg.message = WM_SYSKEYUP;
1280 TrackSysKey = 0;
1281 }
1282 flags |= KF_REPEAT | KF_UP;
1283 }
1284 else
1285 {
1286 Msg.message = WM_KEYDOWN;
1287 if ((gQueueKeyStateTable[VK_MENU] & 0x80 || wVkStripped == VK_MENU) &&
1288 !(gQueueKeyStateTable[VK_CONTROL] & 0x80 || wVkStripped == VK_CONTROL))
1289 {
1290 Msg.message = WM_SYSKEYDOWN;
1291 TrackSysKey = wVkStripped;
1292 }
1293 if (!(ki->dwFlags & KEYEVENTF_UNICODE) && gQueueKeyStateTable[wVk] & 0x80) flags |= KF_REPEAT;
1294 }
1295
1296 if (ki->dwFlags & KEYEVENTF_UNICODE)
1297 {
1298 vk_hook = Msg.wParam = wVk = VK_PACKET;
1299 Msg.lParam = MAKELPARAM(1 /* repeat count */, ki->wScan);
1300 }
1301
1302 FocusMessageQueue = IntGetFocusMessageQueue();
1303
1304 Msg.hwnd = 0;
1305
1306 if (FocusMessageQueue && (FocusMessageQueue->FocusWindow != (HWND)0))
1307 Msg.hwnd = FocusMessageQueue->FocusWindow;
1308
1309 if (!ki->time)
1310 {
1311 KeQueryTickCount(&LargeTickCount);
1312 Msg.time = MsqCalculateMessageTime(&LargeTickCount);
1313 }
1314 else
1315 Msg.time = ki->time;
1316
1317 /* All messages have to contain the cursor point. */
1318 pti = PsGetCurrentThreadWin32Thread();
1319 Msg.pt = gpsi->ptCursor;
1320
1321 DPRINT1("Kbd Hook msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
1322 Msg.message, vk_hook, Msg.lParam);
1323
1324 KbdHookData.vkCode = vk_hook;
1325 KbdHookData.scanCode = ki->wScan;
1326 KbdHookData.flags = flags >> 8;
1327 KbdHookData.time = Msg.time;
1328 KbdHookData.dwExtraInfo = ki->dwExtraInfo;
1329 if (co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
1330 {
1331 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
1332 Msg.message, vk_hook, Msg.lParam);
1333 if (Entered) UserLeave();
1334 return FALSE;
1335 }
1336
1337 if (!(ki->dwFlags & KEYEVENTF_UNICODE))
1338 {
1339 if (ki->dwFlags & KEYEVENTF_KEYUP)
1340 {
1341 gQueueKeyStateTable[wVk] &= ~0x80;
1342 gQueueKeyStateTable[wVkStripped] = gQueueKeyStateTable[wVkL] | gQueueKeyStateTable[wVkR];
1343 }
1344 else
1345 {
1346 if (!(gQueueKeyStateTable[wVk] & 0x80)) gQueueKeyStateTable[wVk] ^= 0x01;
1347 gQueueKeyStateTable[wVk] |= 0xc0;
1348 gQueueKeyStateTable[wVkStripped] = gQueueKeyStateTable[wVkL] | gQueueKeyStateTable[wVkR];
1349 }
1350
1351 if (gQueueKeyStateTable[VK_MENU] & 0x80) flags |= KF_ALTDOWN;
1352
1353 if (wVkStripped == VK_SHIFT) flags &= ~KF_EXTENDED;
1354
1355 Msg.lParam = MAKELPARAM(1 /* repeat count */, flags);
1356 }
1357
1358 if (FocusMessageQueue == NULL)
1359 {
1360 DPRINT("No focus message queue\n");
1361 if (Entered) UserLeave();
1362 return FALSE;
1363 }
1364
1365 if (FocusMessageQueue->FocusWindow != (HWND)0)
1366 {
1367 Msg.hwnd = FocusMessageQueue->FocusWindow;
1368 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
1369
1370 FocusMessageQueue->Desktop->pDeskInfo->LastInputWasKbd = TRUE;
1371
1372 Msg.pt = gpsi->ptCursor;
1373
1374 MsqPostMessage(FocusMessageQueue, &Msg, FALSE, QS_KEY);
1375 }
1376 else
1377 {
1378 DPRINT("Invalid focus window handle\n");
1379 }
1380
1381 if (Entered) UserLeave();
1382
1383 return TRUE;
1384 }
1385
1386 BOOL FASTCALL
1387 UserAttachThreadInput( PTHREADINFO pti, PTHREADINFO ptiTo, BOOL fAttach)
1388 {
1389 PATTACHINFO pai;
1390
1391 /* Can not be the same thread.*/
1392 if (pti == ptiTo) return FALSE;
1393
1394 /* Do not attach to system threads or between different desktops. */
1395 if ( pti->TIF_flags & TIF_DONTATTACHQUEUE ||
1396 ptiTo->TIF_flags & TIF_DONTATTACHQUEUE ||
1397 pti->rpdesk != ptiTo->rpdesk )
1398 return FALSE;
1399
1400 /* If Attach set, allocate and link. */
1401 if ( fAttach )
1402 {
1403 pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), TAG_ATTACHINFO);
1404 if ( !pai ) return FALSE;
1405
1406 pai->paiNext = gpai;
1407 pai->pti1 = pti;
1408 pai->pti2 = ptiTo;
1409 gpai = pai;
1410 }
1411 else /* If clear, unlink and free it. */
1412 {
1413 PATTACHINFO paiprev = NULL;
1414
1415 if ( !gpai ) return FALSE;
1416
1417 pai = gpai;
1418
1419 /* Search list and free if found or return false. */
1420 do
1421 {
1422 if ( pai->pti2 == ptiTo && pai->pti1 == pti ) break;
1423 paiprev = pai;
1424 pai = pai->paiNext;
1425 } while (pai);
1426
1427 if ( !pai ) return FALSE;
1428
1429 if (paiprev) paiprev->paiNext = pai->paiNext;
1430
1431 ExFreePoolWithTag(pai, TAG_ATTACHINFO);
1432 }
1433
1434 return TRUE;
1435 }
1436
1437 UINT
1438 APIENTRY
1439 NtUserSendInput(
1440 UINT nInputs,
1441 LPINPUT pInput,
1442 INT cbSize)
1443 {
1444 PTHREADINFO W32Thread;
1445 UINT cnt;
1446 DECLARE_RETURN(UINT);
1447
1448 DPRINT("Enter NtUserSendInput\n");
1449 UserEnterExclusive();
1450
1451 W32Thread = PsGetCurrentThreadWin32Thread();
1452 ASSERT(W32Thread);
1453
1454 if(!W32Thread->rpdesk)
1455 {
1456 RETURN( 0);
1457 }
1458
1459 if(!nInputs || !pInput || (cbSize != sizeof(INPUT)))
1460 {
1461 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1462 RETURN( 0);
1463 }
1464
1465 /*
1466 * FIXME - check access rights of the window station
1467 * e.g. services running in the service window station cannot block input
1468 */
1469 if(!ThreadHasInputAccess(W32Thread) ||
1470 !IntIsActiveDesktop(W32Thread->rpdesk))
1471 {
1472 SetLastWin32Error(ERROR_ACCESS_DENIED);
1473 RETURN( 0);
1474 }
1475
1476 cnt = 0;
1477 while(nInputs--)
1478 {
1479 INPUT SafeInput;
1480 NTSTATUS Status;
1481
1482 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
1483 if(!NT_SUCCESS(Status))
1484 {
1485 SetLastNtError(Status);
1486 RETURN( cnt);
1487 }
1488
1489 switch(SafeInput.type)
1490 {
1491 case INPUT_MOUSE:
1492 if(IntMouseInput(&SafeInput.mi))
1493 {
1494 cnt++;
1495 }
1496 break;
1497 case INPUT_KEYBOARD:
1498 if(IntKeyboardInput(&SafeInput.ki))
1499 {
1500 cnt++;
1501 }
1502 break;
1503 case INPUT_HARDWARE:
1504 break;
1505 #ifndef NDEBUG
1506
1507 default:
1508 DPRINT1("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
1509 break;
1510 #endif
1511
1512 }
1513 }
1514
1515 RETURN( cnt);
1516
1517 CLEANUP:
1518 DPRINT("Leave NtUserSendInput, ret=%i\n",_ret_);
1519 UserLeave();
1520 END_CLEANUP;
1521 }
1522
1523 /* EOF */