remove user handle table lock, cursor locks, message queue locks, hook lock
[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 QueueKeyStateTable[];
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"\\??\\Mouse");
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 Status = NtOpenFile(&MouseDeviceHandle,
165 FILE_ALL_ACCESS,
166 &MouseObjectAttributes,
167 &Iosb,
168 0,
169 FILE_SYNCHRONOUS_IO_ALERT);
170 if(!NT_SUCCESS(Status))
171 {
172 DPRINT1("Win32K: Failed to open mouse.\n");
173 return; //(Status);
174 }
175
176 for(;;)
177 {
178 /*
179 * Wait to start input.
180 */
181 DPRINT("Mouse Input Thread Waiting for start event\n");
182 Status = KeWaitForSingleObject(&InputThreadsStart,
183 0,
184 KernelMode,
185 TRUE,
186 NULL);
187 DPRINT("Mouse Input Thread Starting...\n");
188
189 /*
190 * Receive and process mouse input.
191 */
192 while(InputThreadsRunning)
193 {
194 MOUSE_INPUT_DATA MouseInput;
195 Status = NtReadFile(MouseDeviceHandle,
196 NULL,
197 NULL,
198 NULL,
199 &Iosb,
200 &MouseInput,
201 sizeof(MOUSE_INPUT_DATA),
202 NULL,
203 NULL);
204 if(Status == STATUS_ALERTED && !InputThreadsRunning)
205 {
206 break;
207 }
208 if(Status == STATUS_PENDING)
209 {
210 NtWaitForSingleObject(MouseDeviceHandle, FALSE, NULL);
211 Status = Iosb.Status;
212 }
213 if(!NT_SUCCESS(Status))
214 {
215 DPRINT1("Win32K: Failed to read from mouse.\n");
216 return; //(Status);
217 }
218 DPRINT("MouseEvent\n");
219
220 UserEnterExclusive();
221
222 ProcessMouseInputData(&MouseInput, Iosb.Information / sizeof(MOUSE_INPUT_DATA));
223
224 UserLeave();
225 }
226 DPRINT("Mouse Input Thread Stopped...\n");
227 }
228 }
229
230 /* Returns a value that indicates if the key is a modifier key, and
231 * which one.
232 */
233 STATIC UINT STDCALL
234 IntKeyboardGetModifiers(KEYBOARD_INPUT_DATA *InputData)
235 {
236 if (InputData->Flags & KEY_E1)
237 return 0;
238
239 if (!(InputData->Flags & KEY_E0))
240 {
241 switch (InputData->MakeCode)
242 {
243 case 0x2a: /* left shift */
244 case 0x36: /* right shift */
245 return MOD_SHIFT;
246
247 case 0x1d: /* left control */
248 return MOD_CONTROL;
249
250 case 0x38: /* left alt */
251 return MOD_ALT;
252
253 default:
254 return 0;
255 }
256 }
257 else
258 {
259 switch (InputData->MakeCode)
260 {
261 case 0x1d: /* right control */
262 return MOD_CONTROL;
263
264 case 0x38: /* right alt */
265 return MOD_ALT;
266
267 case 0x5b: /* left gui (windows) */
268 case 0x5c: /* right gui (windows) */
269 return MOD_WIN;
270
271 default:
272 return 0;
273 }
274 }
275 }
276
277 /* Asks the keyboard driver to send a small table that shows which
278 * lights should connect with which scancodes
279 */
280 STATIC NTSTATUS STDCALL
281 IntKeyboardGetIndicatorTrans(HANDLE KeyboardDeviceHandle,
282 PKEYBOARD_INDICATOR_TRANSLATION *IndicatorTrans)
283 {
284 NTSTATUS Status;
285 DWORD Size = 0;
286 IO_STATUS_BLOCK Block;
287 PKEYBOARD_INDICATOR_TRANSLATION Ret;
288
289 Size = sizeof(KEYBOARD_INDICATOR_TRANSLATION);
290
291 Ret = ExAllocatePoolWithTag(PagedPool,
292 Size,
293 TAG_KEYBOARD);
294
295 while (Ret)
296 {
297 Status = NtDeviceIoControlFile(KeyboardDeviceHandle,
298 NULL,
299 NULL,
300 NULL,
301 &Block,
302 IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION,
303 NULL, 0,
304 Ret, Size);
305
306 if (Status != STATUS_BUFFER_TOO_SMALL)
307 break;
308
309 ExFreePool(Ret);
310
311 Size += sizeof(KEYBOARD_INDICATOR_TRANSLATION);
312
313 Ret = ExAllocatePoolWithTag(PagedPool,
314 Size,
315 TAG_KEYBOARD);
316 }
317
318 if (!Ret)
319 return STATUS_INSUFFICIENT_RESOURCES;
320
321 if (Status != STATUS_SUCCESS)
322 {
323 ExFreePool(Ret);
324 return Status;
325 }
326
327 *IndicatorTrans = Ret;
328 return Status;
329 }
330
331 /* Sends the keyboard commands to turn on/off the lights.
332 */
333 STATIC NTSTATUS STDCALL
334 IntKeyboardUpdateLeds(HANDLE KeyboardDeviceHandle,
335 PKEYBOARD_INPUT_DATA KeyInput,
336 PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans)
337 {
338 NTSTATUS Status;
339 UINT Count;
340 static KEYBOARD_INDICATOR_PARAMETERS Indicators;
341 IO_STATUS_BLOCK Block;
342
343 if (!IndicatorTrans)
344 return STATUS_NOT_SUPPORTED;
345
346 if (KeyInput->Flags & (KEY_E0 | KEY_E1 | KEY_BREAK))
347 return STATUS_SUCCESS;
348
349 for (Count = 0; Count < IndicatorTrans->NumberOfIndicatorKeys; Count++)
350 {
351 if (KeyInput->MakeCode == IndicatorTrans->IndicatorList[Count].MakeCode)
352 {
353 Indicators.LedFlags ^=
354 IndicatorTrans->IndicatorList[Count].IndicatorFlags;
355
356 /* Update the lights on the hardware */
357
358 Status = NtDeviceIoControlFile(KeyboardDeviceHandle,
359 NULL,
360 NULL,
361 NULL,
362 &Block,
363 IOCTL_KEYBOARD_SET_INDICATORS,
364 &Indicators, sizeof(Indicators),
365 NULL, 0);
366
367 return Status;
368 }
369 }
370
371 return STATUS_SUCCESS;
372 }
373
374 STATIC VOID STDCALL
375 IntKeyboardSendWinKeyMsg()
376 {
377 PWINDOW_OBJECT Window;
378 MSG Mesg;
379 NTSTATUS Status;
380
381 Status = ObmReferenceObjectByHandle(InputWindowStation->HandleTable,
382 InputWindowStation->ShellWindow,
383 otWindow,
384 (PVOID *)&Window);
385
386 if (!NT_SUCCESS(Status))
387 {
388 DPRINT1("Couldn't find window to send Windows key message!\n");
389 return;
390 }
391
392 Mesg.hwnd = InputWindowStation->ShellWindow;
393 Mesg.message = WM_SYSCOMMAND;
394 Mesg.wParam = SC_TASKLIST;
395 Mesg.lParam = 0;
396
397 /* The QS_HOTKEY is just a guess */
398 MsqPostMessage(Window->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
399
400 ObmDereferenceObject(Window);
401 }
402
403 STATIC VOID STDCALL
404 co_IntKeyboardSendAltKeyMsg()
405 {
406 co_MsqPostKeyboardMessage(WM_SYSCOMMAND,SC_KEYMENU,0);
407 }
408
409 STATIC VOID STDCALL
410 KeyboardThreadMain(PVOID StartContext)
411 {
412 UNICODE_STRING KeyboardDeviceName = RTL_CONSTANT_STRING(L"\\??\\Keyboard");
413 OBJECT_ATTRIBUTES KeyboardObjectAttributes;
414 IO_STATUS_BLOCK Iosb;
415 NTSTATUS Status;
416 MSG msg;
417 PUSER_MESSAGE_QUEUE FocusQueue;
418 struct _ETHREAD *FocusThread;
419 extern NTSTATUS Win32kInitWin32Thread(PETHREAD Thread);
420
421
422 PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans = NULL;
423 UINT ModifierState = 0;
424 USHORT LastMakeCode = 0;
425 USHORT LastFlags = 0;
426 UINT RepeatCount = 0;
427
428 InitializeObjectAttributes(&KeyboardObjectAttributes,
429 &KeyboardDeviceName,
430 0,
431 NULL,
432 NULL);
433 Status = NtOpenFile(&KeyboardDeviceHandle,
434 FILE_ALL_ACCESS,
435 &KeyboardObjectAttributes,
436 &Iosb,
437 0,
438 FILE_SYNCHRONOUS_IO_ALERT);
439 if (!NT_SUCCESS(Status))
440 {
441 DPRINT1("Win32K: Failed to open keyboard.\n");
442 return; //(Status);
443 }
444
445 /* Not sure if converting this thread to a win32 thread is such
446 a great idea. Since we're posting keyboard messages to the focus
447 window message queue, we'll be (indirectly) doing sendmessage
448 stuff from this thread (for WH_KEYBOARD_LL processing), which
449 means we need our own message queue. If keyboard messages were
450 instead queued to the system message queue, the thread removing
451 the message from the system message queue would be responsible
452 for WH_KEYBOARD_LL processing and we wouldn't need this thread
453 to be a win32 thread. */
454 Status = Win32kInitWin32Thread(PsGetCurrentThread());
455 if (!NT_SUCCESS(Status))
456 {
457 DPRINT1("Win32K: Failed making keyboard thread a win32 thread.\n");
458 return; //(Status);
459 }
460
461 IntKeyboardGetIndicatorTrans(KeyboardDeviceHandle,
462 &IndicatorTrans);
463
464 for (;;)
465 {
466 /*
467 * Wait to start input.
468 */
469 DPRINT( "Keyboard Input Thread Waiting for start event\n" );
470 Status = KeWaitForSingleObject(&InputThreadsStart,
471 0,
472 KernelMode,
473 TRUE,
474 NULL);
475 DPRINT( "Keyboard Input Thread Starting...\n" );
476
477 /*
478 * Receive and process keyboard input.
479 */
480 while (InputThreadsRunning)
481 {
482 BOOLEAN NumKeys = 1;
483 KEYBOARD_INPUT_DATA KeyInput;
484 KEYBOARD_INPUT_DATA NextKeyInput;
485 LPARAM lParam = 0;
486 UINT fsModifiers, fsNextModifiers;
487 struct _ETHREAD *Thread;
488 HWND hWnd;
489 int id;
490
491 Status = NtReadFile (KeyboardDeviceHandle,
492 NULL,
493 NULL,
494 NULL,
495 &Iosb,
496 &KeyInput,
497 sizeof(KEYBOARD_INPUT_DATA),
498 NULL,
499 NULL);
500 DPRINT("KeyRaw: %s %04x\n",
501 (KeyInput.Flags & KEY_BREAK) ? "up" : "down",
502 KeyInput.MakeCode );
503
504 if (Status == STATUS_ALERTED && !InputThreadsRunning)
505 break;
506
507 if (!NT_SUCCESS(Status))
508 {
509 DPRINT1("Win32K: Failed to read from keyboard.\n");
510 return; //(Status);
511 }
512
513 /* Update modifier state */
514 fsModifiers = IntKeyboardGetModifiers(&KeyInput);
515
516 if (fsModifiers)
517 {
518 if (KeyInput.Flags & KEY_BREAK)
519 {
520 ModifierState &= ~fsModifiers;
521 }
522 else
523 {
524 ModifierState |= fsModifiers;
525
526 if (ModifierState == fsModifiers &&
527 (fsModifiers == MOD_ALT || fsModifiers == MOD_WIN))
528 {
529 /* First send out special notifications
530 * (For alt, the message that turns on accelerator
531 * display, not sure what for win. Both TODO though.)
532 */
533
534 /* Read the next key before sending this one */
535 do
536 {
537 Status = NtReadFile (KeyboardDeviceHandle,
538 NULL,
539 NULL,
540 NULL,
541 &Iosb,
542 &NextKeyInput,
543 sizeof(KEYBOARD_INPUT_DATA),
544 NULL,
545 NULL);
546 DPRINT("KeyRaw: %s %04x\n",
547 (NextKeyInput.Flags & KEY_BREAK) ? "up":"down",
548 NextKeyInput.MakeCode );
549
550 if (Status == STATUS_ALERTED && !InputThreadsRunning)
551 goto KeyboardEscape;
552
553 } while ((!(NextKeyInput.Flags & KEY_BREAK)) &&
554 NextKeyInput.MakeCode == KeyInput.MakeCode);
555 /* ^ Ignore repeats, they'll be KEY_MAKE and the same
556 * code. I'm not caring about the counting, not sure
557 * if that matters. I think not.
558 */
559
560 /* If the ModifierState is now empty again, send a
561 * special notification and eat both keypresses
562 */
563
564 fsNextModifiers = IntKeyboardGetModifiers(&NextKeyInput);
565
566 if (fsNextModifiers)
567 ModifierState ^= fsNextModifiers;
568
569 if (ModifierState == 0)
570 {
571 if (fsModifiers == MOD_WIN)
572 IntKeyboardSendWinKeyMsg();
573 else if (fsModifiers == MOD_ALT)
574 co_IntKeyboardSendAltKeyMsg();
575 continue;
576 }
577
578 NumKeys = 2;
579 }
580 }
581 }
582
583 for (;NumKeys;memcpy(&KeyInput, &NextKeyInput, sizeof(KeyInput)),
584 NumKeys--)
585 {
586 lParam = 0;
587
588 IntKeyboardUpdateLeds(KeyboardDeviceHandle,
589 &KeyInput,
590 IndicatorTrans);
591
592 /* While we are working, we set up lParam. The format is:
593 * 0-15: The number of times this key has autorepeated
594 * 16-23: The keyboard scancode
595 * 24: Set if it's and extended key (I assume KEY_E0 | KEY_E1)
596 * Note that E1 is only used for PAUSE (E1-1D-45) and
597 * E0-45 happens not to be anything.
598 * 29: Alt is pressed ('Context code')
599 * 30: Previous state, if the key was down before this message
600 * This is a cheap way to ignore autorepeat keys
601 * 31: 1 if the key is being pressed
602 */
603
604 /* If it's a KEY_MAKE (which is 0, so test using !KEY_BREAK)
605 * and it's the same key as the last one, increase the repeat
606 * count.
607 */
608
609 if (!(KeyInput.Flags & KEY_BREAK))
610 {
611 if (((KeyInput.Flags & (KEY_E0 | KEY_E1)) == LastFlags) &&
612 (KeyInput.MakeCode == LastMakeCode))
613 {
614 RepeatCount++;
615 lParam |= (1 << 30);
616 }
617 else
618 {
619 RepeatCount = 0;
620 LastFlags = KeyInput.Flags & (KEY_E0 | KEY_E1);
621 LastMakeCode = KeyInput.MakeCode;
622 }
623 }
624 else
625 {
626 LastFlags = 0;
627 LastMakeCode = 0; /* Should never match */
628 lParam |= (1 << 30) | (1 << 31);
629 }
630
631 lParam |= RepeatCount;
632
633 lParam |= (KeyInput.MakeCode & 0xff) << 16;
634
635 if (KeyInput.Flags & KEY_E0)
636 lParam |= (1 << 24);
637
638 if (ModifierState & MOD_ALT)
639 {
640 lParam |= (1 << 29);
641
642 if (!(KeyInput.Flags & KEY_BREAK))
643 msg.message = WM_SYSKEYDOWN;
644 else
645 msg.message = WM_SYSKEYUP;
646 }
647 else
648 {
649 if (!(KeyInput.Flags & KEY_BREAK))
650 msg.message = WM_KEYDOWN;
651 else
652 msg.message = WM_KEYUP;
653 }
654
655 /* Find the target thread whose locale is in effect */
656 if (!IntGetScreenDC())
657 FocusQueue = W32kGetPrimitiveMessageQueue();
658 else
659 FocusQueue = IntGetFocusMessageQueue();
660
661 /* This might cause us to lose hot keys, which are important
662 * (ctrl-alt-del secure attention sequence). Not sure if it
663 * can happen though.
664 */
665 if (!FocusQueue) 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(InputWindowStation,
685 ModifierState,
686 msg.wParam,
687 &Thread,
688 &hWnd,
689 &id))
690 {
691 if (!(KeyInput.Flags & KEY_BREAK))
692 {
693 DPRINT("Hot key pressed (hWnd %lx, id %d)\n", hWnd, id);
694 MsqPostHotKeyMessage (Thread,
695 hWnd,
696 (WPARAM)id,
697 MAKELPARAM((WORD)ModifierState,
698 (WORD)msg.wParam));
699 }
700 continue; /* Eat key up motion too */
701 }
702
703 /*
704 * Post a keyboard message.
705 */
706 co_MsqPostKeyboardMessage(msg.message,msg.wParam,msg.lParam);
707 }
708 }
709
710 KeyboardEscape:
711 DPRINT( "KeyboardInput Thread Stopped...\n" );
712 }
713 }
714
715
716 NTSTATUS FASTCALL
717 UserAcquireOrReleaseInputOwnership(BOOLEAN Release)
718 {
719 if (Release && InputThreadsRunning && !pmPrimitiveMessageQueue)
720 {
721 DPRINT( "Releasing input: PM = %08x\n", pmPrimitiveMessageQueue );
722 KeClearEvent(&InputThreadsStart);
723 InputThreadsRunning = FALSE;
724
725 NtAlertThread(KeyboardThreadHandle);
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] = {{WM_LBUTTONDOWN, WM_RBUTTONDOWN},
886 {WM_LBUTTONUP, WM_RBUTTONUP}};
887 const WPARAM SwapBtn[2] = {MK_LBUTTON, MK_RBUTTON};
888 POINT MousePos, OrgPos;
889 PSYSTEM_CURSORINFO CurInfo;
890 PWINSTATION_OBJECT WinSta;
891 BOOL DoMove, SwapButtons;
892 MSG Msg;
893 HBITMAP hBitmap;
894 BITMAPOBJ *BitmapObj;
895 SURFOBJ *SurfObj;
896 PDC dc;
897 PWINDOW_OBJECT DesktopWindow;
898 NTSTATUS Status;
899
900 #if 1
901 HDC hDC;
902
903 /* FIXME - get the screen dc from the window station or desktop */
904 if(!(hDC = IntGetScreenDC()))
905 {
906 return FALSE;
907 }
908 #endif
909
910 ASSERT(mi);
911 #if 0
912 WinSta = PsGetWin32Process()->WindowStation;
913 #else
914 /* FIXME - ugly hack but as long as we're using this dumb callback from the
915 mouse class driver, we can't access the window station from the calling
916 process */
917 WinSta = InputWindowStation;
918 #endif
919 ASSERT(WinSta);
920
921 CurInfo = IntGetSysCursorInfo(WinSta);
922
923 if(!mi->time)
924 {
925 LARGE_INTEGER LargeTickCount;
926 KeQueryTickCount(&LargeTickCount);
927 mi->time = LargeTickCount.u.LowPart;
928 }
929
930 SwapButtons = CurInfo->SwapButtons;
931 DoMove = FALSE;
932
933 IntGetCursorLocation(WinSta, &MousePos);
934 OrgPos.x = MousePos.x;
935 OrgPos.y = MousePos.y;
936
937 if(mi->dwFlags & MOUSEEVENTF_MOVE)
938 {
939 if(mi->dwFlags & MOUSEEVENTF_ABSOLUTE)
940 {
941 MousePos.x = mi->dx;
942 MousePos.y = mi->dy;
943 }
944 else
945 {
946 MousePos.x += mi->dx;
947 MousePos.y += mi->dy;
948 }
949
950 Status = ObmReferenceObjectByHandle(WinSta->HandleTable,
951 WinSta->ActiveDesktop->DesktopWindow, otWindow, (PVOID*)&DesktopWindow);
952 if (NT_SUCCESS(Status))
953 {
954 if(MousePos.x >= DesktopWindow->ClientRect.right)
955 MousePos.x = DesktopWindow->ClientRect.right - 1;
956 if(MousePos.y >= DesktopWindow->ClientRect.bottom)
957 MousePos.y = DesktopWindow->ClientRect.bottom - 1;
958 ObmDereferenceObject(DesktopWindow);
959 }
960
961 if(MousePos.x < 0)
962 MousePos.x = 0;
963 if(MousePos.y < 0)
964 MousePos.y = 0;
965
966 if(CurInfo->CursorClipInfo.IsClipped)
967 {
968 /* The mouse cursor needs to be clipped */
969
970 if(MousePos.x >= (LONG)CurInfo->CursorClipInfo.Right)
971 MousePos.x = (LONG)CurInfo->CursorClipInfo.Right;
972 if(MousePos.x < (LONG)CurInfo->CursorClipInfo.Left)
973 MousePos.x = (LONG)CurInfo->CursorClipInfo.Left;
974 if(MousePos.y >= (LONG)CurInfo->CursorClipInfo.Bottom)
975 MousePos.y = (LONG)CurInfo->CursorClipInfo.Bottom;
976 if(MousePos.y < (LONG)CurInfo->CursorClipInfo.Top)
977 MousePos.y = (LONG)CurInfo->CursorClipInfo.Top;
978 }
979
980 DoMove = (MousePos.x != OrgPos.x || MousePos.y != OrgPos.y);
981 }
982
983 if (DoMove)
984 {
985 dc = DC_LockDc(hDC);
986 if (dc)
987 {
988 hBitmap = dc->w.hBitmap;
989 DC_UnlockDc(dc);
990
991 BitmapObj = BITMAPOBJ_LockBitmap(hBitmap);
992 if (BitmapObj)
993 {
994 SurfObj = &BitmapObj->SurfObj;
995
996 IntEngMovePointer(SurfObj, MousePos.x, MousePos.y, &(GDIDEV(SurfObj)->Pointer.Exclude));
997 /* Only now, update the info in the GDIDEVICE, so EngMovePointer can
998 * use the old values to move the pointer image */
999 GDIDEV(SurfObj)->Pointer.Pos.x = MousePos.x;
1000 GDIDEV(SurfObj)->Pointer.Pos.y = MousePos.y;
1001
1002 BITMAPOBJ_UnlockBitmap(BitmapObj);
1003 }
1004 }
1005 }
1006
1007 /*
1008 * Insert the messages into the system queue
1009 */
1010
1011 Msg.wParam = CurInfo->ButtonsDown;
1012 Msg.lParam = MAKELPARAM(MousePos.x, MousePos.y);
1013 Msg.pt = MousePos;
1014 if(DoMove)
1015 {
1016 Msg.message = WM_MOUSEMOVE;
1017 MsqInsertSystemMessage(&Msg);
1018 }
1019
1020 Msg.message = 0;
1021 if(mi->dwFlags & MOUSEEVENTF_LEFTDOWN)
1022 {
1023 QueueKeyStateTable[VK_LBUTTON] |= 0xc0;
1024 Msg.message = SwapBtnMsg[0][SwapButtons];
1025 CurInfo->ButtonsDown |= SwapBtn[SwapButtons];
1026 MsqInsertSystemMessage(&Msg);
1027 }
1028 else if(mi->dwFlags & MOUSEEVENTF_LEFTUP)
1029 {
1030 QueueKeyStateTable[VK_LBUTTON] &= ~0x80;
1031 Msg.message = SwapBtnMsg[1][SwapButtons];
1032 CurInfo->ButtonsDown &= ~SwapBtn[SwapButtons];
1033 MsqInsertSystemMessage(&Msg);
1034 }
1035 if(mi->dwFlags & MOUSEEVENTF_MIDDLEDOWN)
1036 {
1037 QueueKeyStateTable[VK_MBUTTON] |= 0xc0;
1038 Msg.message = WM_MBUTTONDOWN;
1039 CurInfo->ButtonsDown |= MK_MBUTTON;
1040 MsqInsertSystemMessage(&Msg);
1041 }
1042 else if(mi->dwFlags & MOUSEEVENTF_MIDDLEUP)
1043 {
1044 QueueKeyStateTable[VK_MBUTTON] &= ~0x80;
1045 Msg.message = WM_MBUTTONUP;
1046 CurInfo->ButtonsDown &= ~MK_MBUTTON;
1047 MsqInsertSystemMessage(&Msg);
1048 }
1049 if(mi->dwFlags & MOUSEEVENTF_RIGHTDOWN)
1050 {
1051 QueueKeyStateTable[VK_RBUTTON] |= 0xc0;
1052 Msg.message = SwapBtnMsg[0][!SwapButtons];
1053 CurInfo->ButtonsDown |= SwapBtn[!SwapButtons];
1054 MsqInsertSystemMessage(&Msg);
1055 }
1056 else if(mi->dwFlags & MOUSEEVENTF_RIGHTUP)
1057 {
1058 QueueKeyStateTable[VK_RBUTTON] &= ~0x80;
1059 Msg.message = SwapBtnMsg[1][!SwapButtons];
1060 CurInfo->ButtonsDown &= ~SwapBtn[!SwapButtons];
1061 MsqInsertSystemMessage(&Msg);
1062 }
1063
1064 if((mi->dwFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)) &&
1065 (mi->dwFlags & MOUSEEVENTF_WHEEL))
1066 {
1067 /* fail because both types of events use the mouseData field */
1068 return FALSE;
1069 }
1070
1071 if(mi->dwFlags & MOUSEEVENTF_XDOWN)
1072 {
1073 Msg.message = WM_XBUTTONDOWN;
1074 if(mi->mouseData & XBUTTON1)
1075 {
1076 QueueKeyStateTable[VK_XBUTTON1] |= 0xc0;
1077 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
1078 CurInfo->ButtonsDown |= XBUTTON1;
1079 MsqInsertSystemMessage(&Msg);
1080 }
1081 if(mi->mouseData & XBUTTON2)
1082 {
1083 QueueKeyStateTable[VK_XBUTTON2] |= 0xc0;
1084 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
1085 CurInfo->ButtonsDown |= XBUTTON2;
1086 MsqInsertSystemMessage(&Msg);
1087 }
1088 }
1089 else if(mi->dwFlags & MOUSEEVENTF_XUP)
1090 {
1091 Msg.message = WM_XBUTTONUP;
1092 if(mi->mouseData & XBUTTON1)
1093 {
1094 QueueKeyStateTable[VK_XBUTTON1] &= ~0x80;
1095 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
1096 CurInfo->ButtonsDown &= ~XBUTTON1;
1097 MsqInsertSystemMessage(&Msg);
1098 }
1099 if(mi->mouseData & XBUTTON2)
1100 {
1101 QueueKeyStateTable[VK_XBUTTON2] &= ~0x80;
1102 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
1103 CurInfo->ButtonsDown &= ~XBUTTON2;
1104 MsqInsertSystemMessage(&Msg);
1105 }
1106 }
1107 if(mi->dwFlags & MOUSEEVENTF_WHEEL)
1108 {
1109 Msg.message = WM_MOUSEWHEEL;
1110 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, mi->mouseData);
1111 MsqInsertSystemMessage(&Msg);
1112 }
1113
1114 return TRUE;
1115 }
1116
1117 BOOL FASTCALL
1118 IntKeyboardInput(KEYBDINPUT *ki)
1119 {
1120 return FALSE;
1121 }
1122
1123 UINT
1124 STDCALL
1125 NtUserSendInput(
1126 UINT nInputs,
1127 LPINPUT pInput,
1128 INT cbSize)
1129 {
1130 PW32THREAD W32Thread;
1131 UINT cnt;
1132 DECLARE_RETURN(UINT);
1133
1134 DPRINT("Enter NtUserSendInput\n");
1135 UserEnterExclusive();
1136
1137 W32Thread = PsGetWin32Thread();
1138 ASSERT(W32Thread);
1139
1140 if(!W32Thread->Desktop)
1141 {
1142 RETURN( 0);
1143 }
1144
1145 if(!nInputs || !pInput || (cbSize != sizeof(INPUT)))
1146 {
1147 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1148 RETURN( 0);
1149 }
1150
1151 /*
1152 * FIXME - check access rights of the window station
1153 * e.g. services running in the service window station cannot block input
1154 */
1155 if(!ThreadHasInputAccess(W32Thread) ||
1156 !IntIsActiveDesktop(W32Thread->Desktop))
1157 {
1158 SetLastWin32Error(ERROR_ACCESS_DENIED);
1159 RETURN( 0);
1160 }
1161
1162 cnt = 0;
1163 while(nInputs--)
1164 {
1165 INPUT SafeInput;
1166 NTSTATUS Status;
1167
1168 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
1169 if(!NT_SUCCESS(Status))
1170 {
1171 SetLastNtError(Status);
1172 RETURN( cnt);
1173 }
1174
1175 switch(SafeInput.type)
1176 {
1177 case INPUT_MOUSE:
1178 if(IntMouseInput(&SafeInput.mi))
1179 {
1180 cnt++;
1181 }
1182 break;
1183 case INPUT_KEYBOARD:
1184 if(IntKeyboardInput(&SafeInput.ki))
1185 {
1186 cnt++;
1187 }
1188 break;
1189 case INPUT_HARDWARE:
1190 break;
1191 #ifndef NDEBUG
1192 default:
1193 DPRINT1("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
1194 break;
1195 #endif
1196 }
1197 }
1198
1199 RETURN( cnt);
1200
1201 CLEANUP:
1202 DPRINT("Leave NtUserSendInput, ret=%i\n",_ret_);
1203 UserLeave();
1204 END_CLEANUP;
1205 }
1206
1207 /* EOF */