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