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