- Sync with trunk up to r46941.
[reactos.git] / 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 PKBL keyboardLayout = NULL;
717 lParam = 0;
718
719 IntKeyboardUpdateLeds(KeyboardDeviceHandle,
720 &KeyInput,
721 IndicatorTrans);
722
723 /* While we are working, we set up lParam. The format is:
724 * 0-15: The number of times this key has autorepeated
725 * 16-23: The keyboard scancode
726 * 24: Set if it's and extended key (I assume KEY_E0 | KEY_E1)
727 * Note that E1 is only used for PAUSE (E1-1D-45) and
728 * E0-45 happens not to be anything.
729 * 29: Alt is pressed ('Context code')
730 * 30: Previous state, if the key was down before this message
731 * This is a cheap way to ignore autorepeat keys
732 * 31: 1 if the key is being pressed
733 */
734
735 /* If it's a KEY_MAKE (which is 0, so test using !KEY_BREAK)
736 * and it's the same key as the last one, increase the repeat
737 * count.
738 */
739
740 if (!(KeyInput.Flags & KEY_BREAK))
741 {
742 if (((KeyInput.Flags & (KEY_E0 | KEY_E1)) == LastFlags) &&
743 (KeyInput.MakeCode == LastMakeCode))
744 {
745 RepeatCount++;
746 lParam |= (1 << 30);
747 }
748 else
749 {
750 RepeatCount = 1;
751 LastFlags = KeyInput.Flags & (KEY_E0 | KEY_E1);
752 LastMakeCode = KeyInput.MakeCode;
753 }
754 }
755 else
756 {
757 LastFlags = 0;
758 LastMakeCode = 0; /* Should never match */
759 lParam |= (1 << 30) | (1 << 31);
760 }
761
762 lParam |= RepeatCount;
763
764 lParam |= (KeyInput.MakeCode & 0xff) << 16;
765
766 if (KeyInput.Flags & KEY_E0)
767 lParam |= (1 << 24);
768
769 if (ModifierState & MOD_ALT)
770 {
771 lParam |= (1 << 29);
772
773 if (!(KeyInput.Flags & KEY_BREAK))
774 msg.message = WM_SYSKEYDOWN;
775 else
776 msg.message = WM_SYSKEYUP;
777 }
778 else
779 {
780 if (!(KeyInput.Flags & KEY_BREAK))
781 msg.message = WM_KEYDOWN;
782 else
783 msg.message = WM_KEYUP;
784 }
785
786 /* Find the target thread whose locale is in effect */
787 FocusQueue = IntGetFocusMessageQueue();
788
789 if (FocusQueue)
790 {
791 msg.hwnd = FocusQueue->FocusWindow;
792
793 FocusThread = FocusQueue->Thread;
794 if (FocusThread && FocusThread->Tcb.Win32Thread)
795 {
796 keyboardLayout = ((PTHREADINFO)FocusThread->Tcb.Win32Thread)->KeyboardLayout;
797 }
798 }
799 if (!keyboardLayout)
800 {
801 keyboardLayout = W32kGetDefaultKeyLayout();
802 }
803
804 msg.lParam = lParam;
805
806 /* This function uses lParam to fill wParam according to the
807 * keyboard layout in use.
808 */
809 W32kKeyProcessMessage(&msg,
810 keyboardLayout->KBTables,
811 KeyInput.Flags & KEY_E0 ? 0xE0 :
812 (KeyInput.Flags & KEY_E1 ? 0xE1 : 0));
813
814 if (GetHotKey(ModifierState,
815 msg.wParam,
816 &Thread,
817 &hWnd,
818 &id))
819 {
820 if (!(KeyInput.Flags & KEY_BREAK))
821 {
822 DPRINT("Hot key pressed (hWnd %lx, id %d)\n", hWnd, id);
823 MsqPostHotKeyMessage (Thread,
824 hWnd,
825 (WPARAM)id,
826 MAKELPARAM((WORD)ModifierState,
827 (WORD)msg.wParam));
828 }
829 continue; /* Eat key up motion too */
830 }
831
832 if (!FocusQueue)
833 {
834 /* There is no focused window to receive a keyboard message */
835 continue;
836 }
837 /*
838 * Post a keyboard message.
839 */
840 co_MsqPostKeyboardMessage(msg.message,msg.wParam,msg.lParam);
841 }
842 }
843
844 KeyboardEscape:
845 DPRINT( "KeyboardInput Thread Stopped...\n" );
846 }
847 }
848
849
850 static PVOID Objects[2];
851 /*
852 Raw Input Thread.
853 Since this relies on InputThreadsStart, just fake it.
854 */
855 static VOID APIENTRY
856 RawInputThreadMain(PVOID StartContext)
857 {
858 NTSTATUS Status;
859 LARGE_INTEGER DueTime;
860
861 DueTime.QuadPart = (LONGLONG)(-10000000);
862
863 do
864 {
865 KEVENT Event;
866 KeInitializeEvent(&Event, NotificationEvent, FALSE);
867 Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
868 } while (!NT_SUCCESS(Status));
869
870
871 Objects[0] = &InputThreadsStart;
872
873 MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), TAG_INPUT);
874 if (!MasterTimer)
875 {
876 DPRINT1("Win32K: Failed making Raw Input thread a win32 thread.\n");
877 return;
878 }
879 KeInitializeTimer(MasterTimer);
880 Objects[1] = MasterTimer;
881
882 // This thread requires win32k!
883 Status = Win32kInitWin32Thread(PsGetCurrentThread());
884 if (!NT_SUCCESS(Status))
885 {
886 DPRINT1("Win32K: Failed making Raw Input thread a win32 thread.\n");
887 return; //(Status);
888 }
889
890 ptiRawInput = PsGetCurrentThreadWin32Thread();
891 DPRINT("\nRaw Input Thread 0x%x \n", ptiRawInput);
892
893
894 KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
895 LOW_REALTIME_PRIORITY + 3);
896
897 UserEnterExclusive();
898 StartTheTimers();
899 UserLeave();
900
901 //
902 // ATM, we just have one job to handle, merge the other two later.
903 //
904 for(;;)
905 {
906 DPRINT( "Raw Input Thread Waiting for start event\n" );
907
908 Status = KeWaitForMultipleObjects( 2,
909 Objects,
910 WaitAll, //WaitAny,
911 WrUserRequest,
912 KernelMode,
913 TRUE,
914 NULL,
915 NULL);
916 DPRINT( "Raw Input Thread Starting...\n" );
917
918 ProcessTimers();
919 }
920 DPRINT1("Raw Input Thread Exit!\n");
921 }
922
923 NTSTATUS FASTCALL
924 InitInputImpl(VOID)
925 {
926 NTSTATUS Status;
927
928 KeInitializeEvent(&InputThreadsStart, NotificationEvent, FALSE);
929
930 /* Initialize the default keyboard layout */
931 if(!UserInitDefaultKeyboardLayout())
932 {
933 DPRINT1("Failed to initialize default keyboard layout!\n");
934 }
935
936 Status = PsCreateSystemThread(&RawInputThreadHandle,
937 THREAD_ALL_ACCESS,
938 NULL,
939 NULL,
940 &RawInputThreadId,
941 RawInputThreadMain,
942 NULL);
943 if (!NT_SUCCESS(Status))
944 {
945 DPRINT1("Win32K: Failed to create raw thread.\n");
946 }
947
948 Status = PsCreateSystemThread(&KeyboardThreadHandle,
949 THREAD_ALL_ACCESS,
950 NULL,
951 NULL,
952 &KeyboardThreadId,
953 KeyboardThreadMain,
954 NULL);
955 if (!NT_SUCCESS(Status))
956 {
957 DPRINT1("Win32K: Failed to create keyboard thread.\n");
958 }
959
960 Status = PsCreateSystemThread(&MouseThreadHandle,
961 THREAD_ALL_ACCESS,
962 NULL,
963 NULL,
964 &MouseThreadId,
965 MouseThreadMain,
966 NULL);
967 if (!NT_SUCCESS(Status))
968 {
969 DPRINT1("Win32K: Failed to create mouse thread.\n");
970 }
971
972 InputThreadsRunning = TRUE;
973 KeSetEvent(&InputThreadsStart, IO_NO_INCREMENT, FALSE);
974
975 return STATUS_SUCCESS;
976 }
977
978 NTSTATUS FASTCALL
979 CleanupInputImp(VOID)
980 {
981 return(STATUS_SUCCESS);
982 }
983
984 BOOL
985 APIENTRY
986 NtUserDragDetect(
987 HWND hWnd,
988 POINT pt) // Just like the User call.
989 {
990 UNIMPLEMENTED
991 return 0;
992 }
993
994 BOOL FASTCALL
995 IntBlockInput(PTHREADINFO W32Thread, BOOL BlockIt)
996 {
997 PTHREADINFO OldBlock;
998 ASSERT(W32Thread);
999
1000 if(!W32Thread->rpdesk || ((W32Thread->TIF_flags & TIF_INCLEANUP) && BlockIt))
1001 {
1002 /*
1003 * fail blocking if exiting the thread
1004 */
1005
1006 return FALSE;
1007 }
1008
1009 /*
1010 * FIXME - check access rights of the window station
1011 * e.g. services running in the service window station cannot block input
1012 */
1013 if(!ThreadHasInputAccess(W32Thread) ||
1014 !IntIsActiveDesktop(W32Thread->rpdesk))
1015 {
1016 SetLastWin32Error(ERROR_ACCESS_DENIED);
1017 return FALSE;
1018 }
1019
1020 ASSERT(W32Thread->rpdesk);
1021 OldBlock = W32Thread->rpdesk->BlockInputThread;
1022 if(OldBlock)
1023 {
1024 if(OldBlock != W32Thread)
1025 {
1026 SetLastWin32Error(ERROR_ACCESS_DENIED);
1027 return FALSE;
1028 }
1029 W32Thread->rpdesk->BlockInputThread = (BlockIt ? W32Thread : NULL);
1030 return OldBlock == NULL;
1031 }
1032
1033 W32Thread->rpdesk->BlockInputThread = (BlockIt ? W32Thread : NULL);
1034 return OldBlock == NULL;
1035 }
1036
1037 BOOL
1038 APIENTRY
1039 NtUserBlockInput(
1040 BOOL BlockIt)
1041 {
1042 DECLARE_RETURN(BOOLEAN);
1043
1044 DPRINT("Enter NtUserBlockInput\n");
1045 UserEnterExclusive();
1046
1047 RETURN( IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt));
1048
1049 CLEANUP:
1050 DPRINT("Leave NtUserBlockInput, ret=%i\n",_ret_);
1051 UserLeave();
1052 END_CLEANUP;
1053 }
1054
1055 BOOL FASTCALL
1056 IntMouseInput(MOUSEINPUT *mi)
1057 {
1058 const UINT SwapBtnMsg[2][2] =
1059 {
1060 {WM_LBUTTONDOWN, WM_RBUTTONDOWN},
1061 {WM_LBUTTONUP, WM_RBUTTONUP}
1062 };
1063 const WPARAM SwapBtn[2] =
1064 {
1065 MK_LBUTTON, MK_RBUTTON
1066 };
1067 POINT MousePos;
1068 PSYSTEM_CURSORINFO CurInfo;
1069 BOOL SwapButtons;
1070 MSG Msg;
1071
1072 ASSERT(mi);
1073
1074 CurInfo = IntGetSysCursorInfo();
1075
1076 if(!mi->time)
1077 {
1078 LARGE_INTEGER LargeTickCount;
1079 KeQueryTickCount(&LargeTickCount);
1080 mi->time = MsqCalculateMessageTime(&LargeTickCount);
1081 }
1082
1083 SwapButtons = gspv.bMouseBtnSwap;
1084
1085 MousePos = gpsi->ptCursor;
1086
1087 if(mi->dwFlags & MOUSEEVENTF_MOVE)
1088 {
1089 if(mi->dwFlags & MOUSEEVENTF_ABSOLUTE)
1090 {
1091 MousePos.x = mi->dx * UserGetSystemMetrics(SM_CXVIRTUALSCREEN) >> 16;
1092 MousePos.y = mi->dy * UserGetSystemMetrics(SM_CYVIRTUALSCREEN) >> 16;
1093 }
1094 else
1095 {
1096 MousePos.x += mi->dx;
1097 MousePos.y += mi->dy;
1098 }
1099 }
1100
1101 /*
1102 * Insert the messages into the system queue
1103 */
1104 Msg.wParam = CurInfo->ButtonsDown;
1105 Msg.lParam = MAKELPARAM(MousePos.x, MousePos.y);
1106 Msg.pt = MousePos;
1107
1108 if (gQueueKeyStateTable[VK_SHIFT] & 0xc0)
1109 {
1110 Msg.wParam |= MK_SHIFT;
1111 }
1112
1113 if (gQueueKeyStateTable[VK_CONTROL] & 0xc0)
1114 {
1115 Msg.wParam |= MK_CONTROL;
1116 }
1117
1118 if(mi->dwFlags & MOUSEEVENTF_MOVE)
1119 {
1120 UserSetCursorPos(MousePos.x, MousePos.y);
1121 }
1122 if(mi->dwFlags & MOUSEEVENTF_LEFTDOWN)
1123 {
1124 gQueueKeyStateTable[VK_LBUTTON] |= 0xc0;
1125 Msg.message = SwapBtnMsg[0][SwapButtons];
1126 CurInfo->ButtonsDown |= SwapBtn[SwapButtons];
1127 MsqInsertSystemMessage(&Msg);
1128 }
1129 else if(mi->dwFlags & MOUSEEVENTF_LEFTUP)
1130 {
1131 gQueueKeyStateTable[VK_LBUTTON] &= ~0x80;
1132 Msg.message = SwapBtnMsg[1][SwapButtons];
1133 CurInfo->ButtonsDown &= ~SwapBtn[SwapButtons];
1134 MsqInsertSystemMessage(&Msg);
1135 }
1136 if(mi->dwFlags & MOUSEEVENTF_MIDDLEDOWN)
1137 {
1138 gQueueKeyStateTable[VK_MBUTTON] |= 0xc0;
1139 Msg.message = WM_MBUTTONDOWN;
1140 CurInfo->ButtonsDown |= MK_MBUTTON;
1141 MsqInsertSystemMessage(&Msg);
1142 }
1143 else if(mi->dwFlags & MOUSEEVENTF_MIDDLEUP)
1144 {
1145 gQueueKeyStateTable[VK_MBUTTON] &= ~0x80;
1146 Msg.message = WM_MBUTTONUP;
1147 CurInfo->ButtonsDown &= ~MK_MBUTTON;
1148 MsqInsertSystemMessage(&Msg);
1149 }
1150 if(mi->dwFlags & MOUSEEVENTF_RIGHTDOWN)
1151 {
1152 gQueueKeyStateTable[VK_RBUTTON] |= 0xc0;
1153 Msg.message = SwapBtnMsg[0][!SwapButtons];
1154 CurInfo->ButtonsDown |= SwapBtn[!SwapButtons];
1155 MsqInsertSystemMessage(&Msg);
1156 }
1157 else if(mi->dwFlags & MOUSEEVENTF_RIGHTUP)
1158 {
1159 gQueueKeyStateTable[VK_RBUTTON] &= ~0x80;
1160 Msg.message = SwapBtnMsg[1][!SwapButtons];
1161 CurInfo->ButtonsDown &= ~SwapBtn[!SwapButtons];
1162 MsqInsertSystemMessage(&Msg);
1163 }
1164
1165 if((mi->dwFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)) &&
1166 (mi->dwFlags & MOUSEEVENTF_WHEEL))
1167 {
1168 /* fail because both types of events use the mouseData field */
1169 return FALSE;
1170 }
1171
1172 if(mi->dwFlags & MOUSEEVENTF_XDOWN)
1173 {
1174 Msg.message = WM_XBUTTONDOWN;
1175 if(mi->mouseData & XBUTTON1)
1176 {
1177 gQueueKeyStateTable[VK_XBUTTON1] |= 0xc0;
1178 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
1179 CurInfo->ButtonsDown |= XBUTTON1;
1180 MsqInsertSystemMessage(&Msg);
1181 }
1182 if(mi->mouseData & XBUTTON2)
1183 {
1184 gQueueKeyStateTable[VK_XBUTTON2] |= 0xc0;
1185 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
1186 CurInfo->ButtonsDown |= XBUTTON2;
1187 MsqInsertSystemMessage(&Msg);
1188 }
1189 }
1190 else if(mi->dwFlags & MOUSEEVENTF_XUP)
1191 {
1192 Msg.message = WM_XBUTTONUP;
1193 if(mi->mouseData & XBUTTON1)
1194 {
1195 gQueueKeyStateTable[VK_XBUTTON1] &= ~0x80;
1196 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
1197 CurInfo->ButtonsDown &= ~XBUTTON1;
1198 MsqInsertSystemMessage(&Msg);
1199 }
1200 if(mi->mouseData & XBUTTON2)
1201 {
1202 gQueueKeyStateTable[VK_XBUTTON2] &= ~0x80;
1203 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
1204 CurInfo->ButtonsDown &= ~XBUTTON2;
1205 MsqInsertSystemMessage(&Msg);
1206 }
1207 }
1208 if(mi->dwFlags & MOUSEEVENTF_WHEEL)
1209 {
1210 Msg.message = WM_MOUSEWHEEL;
1211 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, mi->mouseData);
1212 MsqInsertSystemMessage(&Msg);
1213 }
1214
1215 return TRUE;
1216 }
1217
1218 BOOL FASTCALL
1219 IntKeyboardInput(KEYBDINPUT *ki)
1220 {
1221 PUSER_MESSAGE_QUEUE FocusMessageQueue;
1222 PTHREADINFO pti;
1223 MSG Msg;
1224 LARGE_INTEGER LargeTickCount;
1225 KBDLLHOOKSTRUCT KbdHookData;
1226 WORD flags, wVkStripped, wVkL, wVkR, wVk = ki->wVk, vk_hook = ki->wVk;
1227 BOOLEAN Entered = FALSE;
1228
1229 Msg.lParam = 0;
1230
1231 // Condition may arise when calling MsqPostMessage and waiting for an event.
1232 if (!UserIsEntered())
1233 {
1234 // Fixme: Not sure ATM if this thread is locked.
1235 UserEnterExclusive();
1236 Entered = TRUE;
1237 }
1238
1239 wVk = LOBYTE(wVk);
1240 Msg.wParam = wVk;
1241 flags = LOBYTE(ki->wScan);
1242
1243 if (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) flags |= KF_EXTENDED;
1244 /* FIXME: set KF_DLGMODE and KF_MENUMODE when needed */
1245
1246 /* strip left/right for menu, control, shift */
1247 switch (wVk)
1248 {
1249 case VK_MENU:
1250 case VK_LMENU:
1251 case VK_RMENU:
1252 wVk = (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) ? VK_RMENU : VK_LMENU;
1253 wVkStripped = VK_MENU;
1254 wVkL = VK_LMENU;
1255 wVkR = VK_RMENU;
1256 break;
1257 case VK_CONTROL:
1258 case VK_LCONTROL:
1259 case VK_RCONTROL:
1260 wVk = (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) ? VK_RCONTROL : VK_LCONTROL;
1261 wVkStripped = VK_CONTROL;
1262 wVkL = VK_LCONTROL;
1263 wVkR = VK_RCONTROL;
1264 break;
1265 case VK_SHIFT:
1266 case VK_LSHIFT:
1267 case VK_RSHIFT:
1268 wVk = (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) ? VK_RSHIFT : VK_LSHIFT;
1269 wVkStripped = VK_SHIFT;
1270 wVkL = VK_LSHIFT;
1271 wVkR = VK_RSHIFT;
1272 break;
1273 default:
1274 wVkStripped = wVkL = wVkR = wVk;
1275 }
1276
1277 if (ki->dwFlags & KEYEVENTF_KEYUP)
1278 {
1279 Msg.message = WM_KEYUP;
1280 if ((gQueueKeyStateTable[VK_MENU] & 0x80) &&
1281 ((wVkStripped == VK_MENU) || (wVkStripped == VK_CONTROL)
1282 || !(gQueueKeyStateTable[VK_CONTROL] & 0x80)))
1283 {
1284 if( TrackSysKey == VK_MENU || /* <ALT>-down/<ALT>-up sequence */
1285 (wVkStripped != VK_MENU)) /* <ALT>-down...<something else>-up */
1286 Msg.message = WM_SYSKEYUP;
1287 TrackSysKey = 0;
1288 }
1289 flags |= KF_REPEAT | KF_UP;
1290 }
1291 else
1292 {
1293 Msg.message = WM_KEYDOWN;
1294 if ((gQueueKeyStateTable[VK_MENU] & 0x80 || wVkStripped == VK_MENU) &&
1295 !(gQueueKeyStateTable[VK_CONTROL] & 0x80 || wVkStripped == VK_CONTROL))
1296 {
1297 Msg.message = WM_SYSKEYDOWN;
1298 TrackSysKey = wVkStripped;
1299 }
1300 if (!(ki->dwFlags & KEYEVENTF_UNICODE) && gQueueKeyStateTable[wVk] & 0x80) flags |= KF_REPEAT;
1301 }
1302
1303 if (ki->dwFlags & KEYEVENTF_UNICODE)
1304 {
1305 vk_hook = Msg.wParam = wVk = VK_PACKET;
1306 Msg.lParam = MAKELPARAM(1 /* repeat count */, ki->wScan);
1307 }
1308
1309 FocusMessageQueue = IntGetFocusMessageQueue();
1310
1311 Msg.hwnd = 0;
1312
1313 if (FocusMessageQueue && (FocusMessageQueue->FocusWindow != (HWND)0))
1314 Msg.hwnd = FocusMessageQueue->FocusWindow;
1315
1316 if (!ki->time)
1317 {
1318 KeQueryTickCount(&LargeTickCount);
1319 Msg.time = MsqCalculateMessageTime(&LargeTickCount);
1320 }
1321 else
1322 Msg.time = ki->time;
1323
1324 /* All messages have to contain the cursor point. */
1325 pti = PsGetCurrentThreadWin32Thread();
1326 Msg.pt = gpsi->ptCursor;
1327
1328 DPRINT1("Kbd Hook msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
1329 Msg.message, vk_hook, Msg.lParam);
1330
1331 KbdHookData.vkCode = vk_hook;
1332 KbdHookData.scanCode = ki->wScan;
1333 KbdHookData.flags = flags >> 8;
1334 KbdHookData.time = Msg.time;
1335 KbdHookData.dwExtraInfo = ki->dwExtraInfo;
1336 if (co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
1337 {
1338 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
1339 Msg.message, vk_hook, Msg.lParam);
1340 if (Entered) UserLeave();
1341 return FALSE;
1342 }
1343
1344 if (!(ki->dwFlags & KEYEVENTF_UNICODE))
1345 {
1346 if (ki->dwFlags & KEYEVENTF_KEYUP)
1347 {
1348 gQueueKeyStateTable[wVk] &= ~0x80;
1349 gQueueKeyStateTable[wVkStripped] = gQueueKeyStateTable[wVkL] | gQueueKeyStateTable[wVkR];
1350 }
1351 else
1352 {
1353 if (!(gQueueKeyStateTable[wVk] & 0x80)) gQueueKeyStateTable[wVk] ^= 0x01;
1354 gQueueKeyStateTable[wVk] |= 0xc0;
1355 gQueueKeyStateTable[wVkStripped] = gQueueKeyStateTable[wVkL] | gQueueKeyStateTable[wVkR];
1356 }
1357
1358 if (gQueueKeyStateTable[VK_MENU] & 0x80) flags |= KF_ALTDOWN;
1359
1360 if (wVkStripped == VK_SHIFT) flags &= ~KF_EXTENDED;
1361
1362 Msg.lParam = MAKELPARAM(1 /* repeat count */, flags);
1363 }
1364
1365 if (FocusMessageQueue == NULL)
1366 {
1367 DPRINT("No focus message queue\n");
1368 if (Entered) UserLeave();
1369 return FALSE;
1370 }
1371
1372 if (FocusMessageQueue->FocusWindow != (HWND)0)
1373 {
1374 Msg.hwnd = FocusMessageQueue->FocusWindow;
1375 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
1376
1377 FocusMessageQueue->Desktop->pDeskInfo->LastInputWasKbd = TRUE;
1378
1379 Msg.pt = gpsi->ptCursor;
1380
1381 MsqPostMessage(FocusMessageQueue, &Msg, FALSE, QS_KEY);
1382 }
1383 else
1384 {
1385 DPRINT("Invalid focus window handle\n");
1386 }
1387
1388 if (Entered) UserLeave();
1389
1390 return TRUE;
1391 }
1392
1393 BOOL FASTCALL
1394 UserAttachThreadInput( PTHREADINFO pti, PTHREADINFO ptiTo, BOOL fAttach)
1395 {
1396 PATTACHINFO pai;
1397
1398 /* Can not be the same thread.*/
1399 if (pti == ptiTo) return FALSE;
1400
1401 /* Do not attach to system threads or between different desktops. */
1402 if ( pti->TIF_flags & TIF_DONTATTACHQUEUE ||
1403 ptiTo->TIF_flags & TIF_DONTATTACHQUEUE ||
1404 pti->rpdesk != ptiTo->rpdesk )
1405 return FALSE;
1406
1407 /* If Attach set, allocate and link. */
1408 if ( fAttach )
1409 {
1410 pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), TAG_ATTACHINFO);
1411 if ( !pai ) return FALSE;
1412
1413 pai->paiNext = gpai;
1414 pai->pti1 = pti;
1415 pai->pti2 = ptiTo;
1416 gpai = pai;
1417 }
1418 else /* If clear, unlink and free it. */
1419 {
1420 PATTACHINFO paiprev = NULL;
1421
1422 if ( !gpai ) return FALSE;
1423
1424 pai = gpai;
1425
1426 /* Search list and free if found or return false. */
1427 do
1428 {
1429 if ( pai->pti2 == ptiTo && pai->pti1 == pti ) break;
1430 paiprev = pai;
1431 pai = pai->paiNext;
1432 } while (pai);
1433
1434 if ( !pai ) return FALSE;
1435
1436 if (paiprev) paiprev->paiNext = pai->paiNext;
1437
1438 ExFreePoolWithTag(pai, TAG_ATTACHINFO);
1439 }
1440
1441 return TRUE;
1442 }
1443
1444 UINT
1445 APIENTRY
1446 NtUserSendInput(
1447 UINT nInputs,
1448 LPINPUT pInput,
1449 INT cbSize)
1450 {
1451 PTHREADINFO W32Thread;
1452 UINT cnt;
1453 DECLARE_RETURN(UINT);
1454
1455 DPRINT("Enter NtUserSendInput\n");
1456 UserEnterExclusive();
1457
1458 W32Thread = PsGetCurrentThreadWin32Thread();
1459 ASSERT(W32Thread);
1460
1461 if(!W32Thread->rpdesk)
1462 {
1463 RETURN( 0);
1464 }
1465
1466 if(!nInputs || !pInput || (cbSize != sizeof(INPUT)))
1467 {
1468 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1469 RETURN( 0);
1470 }
1471
1472 /*
1473 * FIXME - check access rights of the window station
1474 * e.g. services running in the service window station cannot block input
1475 */
1476 if(!ThreadHasInputAccess(W32Thread) ||
1477 !IntIsActiveDesktop(W32Thread->rpdesk))
1478 {
1479 SetLastWin32Error(ERROR_ACCESS_DENIED);
1480 RETURN( 0);
1481 }
1482
1483 cnt = 0;
1484 while(nInputs--)
1485 {
1486 INPUT SafeInput;
1487 NTSTATUS Status;
1488
1489 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
1490 if(!NT_SUCCESS(Status))
1491 {
1492 SetLastNtError(Status);
1493 RETURN( cnt);
1494 }
1495
1496 switch(SafeInput.type)
1497 {
1498 case INPUT_MOUSE:
1499 if(IntMouseInput(&SafeInput.mi))
1500 {
1501 cnt++;
1502 }
1503 break;
1504 case INPUT_KEYBOARD:
1505 if(IntKeyboardInput(&SafeInput.ki))
1506 {
1507 cnt++;
1508 }
1509 break;
1510 case INPUT_HARDWARE:
1511 break;
1512 #ifndef NDEBUG
1513
1514 default:
1515 DPRINT1("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
1516 break;
1517 #endif
1518
1519 }
1520 }
1521
1522 RETURN( cnt);
1523
1524 CLEANUP:
1525 DPRINT("Leave NtUserSendInput, ret=%i\n",_ret_);
1526 UserLeave();
1527 END_CLEANUP;
1528 }
1529
1530 /* EOF */