-surround user part (most of it) with a single lock (currently a mutex, but will...
[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 ExAcquireFastMutex(&CurInfo->CursorMutex);
934 IntGetCursorLocation(WinSta, &MousePos);
935 OrgPos.x = MousePos.x;
936 OrgPos.y = MousePos.y;
937
938 if(mi->dwFlags & MOUSEEVENTF_MOVE)
939 {
940 if(mi->dwFlags & MOUSEEVENTF_ABSOLUTE)
941 {
942 MousePos.x = mi->dx;
943 MousePos.y = mi->dy;
944 }
945 else
946 {
947 MousePos.x += mi->dx;
948 MousePos.y += mi->dy;
949 }
950
951 Status = ObmReferenceObjectByHandle(WinSta->HandleTable,
952 WinSta->ActiveDesktop->DesktopWindow, otWindow, (PVOID*)&DesktopWindow);
953 if (NT_SUCCESS(Status))
954 {
955 if(MousePos.x >= DesktopWindow->ClientRect.right)
956 MousePos.x = DesktopWindow->ClientRect.right - 1;
957 if(MousePos.y >= DesktopWindow->ClientRect.bottom)
958 MousePos.y = DesktopWindow->ClientRect.bottom - 1;
959 ObmDereferenceObject(DesktopWindow);
960 }
961
962 if(MousePos.x < 0)
963 MousePos.x = 0;
964 if(MousePos.y < 0)
965 MousePos.y = 0;
966
967 if(CurInfo->CursorClipInfo.IsClipped)
968 {
969 /* The mouse cursor needs to be clipped */
970
971 if(MousePos.x >= (LONG)CurInfo->CursorClipInfo.Right)
972 MousePos.x = (LONG)CurInfo->CursorClipInfo.Right;
973 if(MousePos.x < (LONG)CurInfo->CursorClipInfo.Left)
974 MousePos.x = (LONG)CurInfo->CursorClipInfo.Left;
975 if(MousePos.y >= (LONG)CurInfo->CursorClipInfo.Bottom)
976 MousePos.y = (LONG)CurInfo->CursorClipInfo.Bottom;
977 if(MousePos.y < (LONG)CurInfo->CursorClipInfo.Top)
978 MousePos.y = (LONG)CurInfo->CursorClipInfo.Top;
979 }
980
981 DoMove = (MousePos.x != OrgPos.x || MousePos.y != OrgPos.y);
982 }
983
984 ExReleaseFastMutex(&CurInfo->CursorMutex);
985
986 if (DoMove)
987 {
988 dc = DC_LockDc(hDC);
989 if (dc)
990 {
991 hBitmap = dc->w.hBitmap;
992 DC_UnlockDc(dc);
993
994 BitmapObj = BITMAPOBJ_LockBitmap(hBitmap);
995 if (BitmapObj)
996 {
997 SurfObj = &BitmapObj->SurfObj;
998
999 IntEngMovePointer(SurfObj, MousePos.x, MousePos.y, &(GDIDEV(SurfObj)->Pointer.Exclude));
1000 /* Only now, update the info in the GDIDEVICE, so EngMovePointer can
1001 * use the old values to move the pointer image */
1002 GDIDEV(SurfObj)->Pointer.Pos.x = MousePos.x;
1003 GDIDEV(SurfObj)->Pointer.Pos.y = MousePos.y;
1004
1005 BITMAPOBJ_UnlockBitmap(BitmapObj);
1006 }
1007 }
1008 }
1009
1010 /*
1011 * Insert the messages into the system queue
1012 */
1013
1014 Msg.wParam = CurInfo->ButtonsDown;
1015 Msg.lParam = MAKELPARAM(MousePos.x, MousePos.y);
1016 Msg.pt = MousePos;
1017 if(DoMove)
1018 {
1019 Msg.message = WM_MOUSEMOVE;
1020 MsqInsertSystemMessage(&Msg);
1021 }
1022
1023 Msg.message = 0;
1024 if(mi->dwFlags & MOUSEEVENTF_LEFTDOWN)
1025 {
1026 QueueKeyStateTable[VK_LBUTTON] |= 0xc0;
1027 Msg.message = SwapBtnMsg[0][SwapButtons];
1028 CurInfo->ButtonsDown |= SwapBtn[SwapButtons];
1029 MsqInsertSystemMessage(&Msg);
1030 }
1031 else if(mi->dwFlags & MOUSEEVENTF_LEFTUP)
1032 {
1033 QueueKeyStateTable[VK_LBUTTON] &= ~0x80;
1034 Msg.message = SwapBtnMsg[1][SwapButtons];
1035 CurInfo->ButtonsDown &= ~SwapBtn[SwapButtons];
1036 MsqInsertSystemMessage(&Msg);
1037 }
1038 if(mi->dwFlags & MOUSEEVENTF_MIDDLEDOWN)
1039 {
1040 QueueKeyStateTable[VK_MBUTTON] |= 0xc0;
1041 Msg.message = WM_MBUTTONDOWN;
1042 CurInfo->ButtonsDown |= MK_MBUTTON;
1043 MsqInsertSystemMessage(&Msg);
1044 }
1045 else if(mi->dwFlags & MOUSEEVENTF_MIDDLEUP)
1046 {
1047 QueueKeyStateTable[VK_MBUTTON] &= ~0x80;
1048 Msg.message = WM_MBUTTONUP;
1049 CurInfo->ButtonsDown &= ~MK_MBUTTON;
1050 MsqInsertSystemMessage(&Msg);
1051 }
1052 if(mi->dwFlags & MOUSEEVENTF_RIGHTDOWN)
1053 {
1054 QueueKeyStateTable[VK_RBUTTON] |= 0xc0;
1055 Msg.message = SwapBtnMsg[0][!SwapButtons];
1056 CurInfo->ButtonsDown |= SwapBtn[!SwapButtons];
1057 MsqInsertSystemMessage(&Msg);
1058 }
1059 else if(mi->dwFlags & MOUSEEVENTF_RIGHTUP)
1060 {
1061 QueueKeyStateTable[VK_RBUTTON] &= ~0x80;
1062 Msg.message = SwapBtnMsg[1][!SwapButtons];
1063 CurInfo->ButtonsDown &= ~SwapBtn[!SwapButtons];
1064 MsqInsertSystemMessage(&Msg);
1065 }
1066
1067 if((mi->dwFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)) &&
1068 (mi->dwFlags & MOUSEEVENTF_WHEEL))
1069 {
1070 /* fail because both types of events use the mouseData field */
1071 return FALSE;
1072 }
1073
1074 if(mi->dwFlags & MOUSEEVENTF_XDOWN)
1075 {
1076 Msg.message = WM_XBUTTONDOWN;
1077 if(mi->mouseData & XBUTTON1)
1078 {
1079 QueueKeyStateTable[VK_XBUTTON1] |= 0xc0;
1080 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
1081 CurInfo->ButtonsDown |= XBUTTON1;
1082 MsqInsertSystemMessage(&Msg);
1083 }
1084 if(mi->mouseData & XBUTTON2)
1085 {
1086 QueueKeyStateTable[VK_XBUTTON2] |= 0xc0;
1087 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
1088 CurInfo->ButtonsDown |= XBUTTON2;
1089 MsqInsertSystemMessage(&Msg);
1090 }
1091 }
1092 else if(mi->dwFlags & MOUSEEVENTF_XUP)
1093 {
1094 Msg.message = WM_XBUTTONUP;
1095 if(mi->mouseData & XBUTTON1)
1096 {
1097 QueueKeyStateTable[VK_XBUTTON1] &= ~0x80;
1098 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
1099 CurInfo->ButtonsDown &= ~XBUTTON1;
1100 MsqInsertSystemMessage(&Msg);
1101 }
1102 if(mi->mouseData & XBUTTON2)
1103 {
1104 QueueKeyStateTable[VK_XBUTTON2] &= ~0x80;
1105 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
1106 CurInfo->ButtonsDown &= ~XBUTTON2;
1107 MsqInsertSystemMessage(&Msg);
1108 }
1109 }
1110 if(mi->dwFlags & MOUSEEVENTF_WHEEL)
1111 {
1112 Msg.message = WM_MOUSEWHEEL;
1113 Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, mi->mouseData);
1114 MsqInsertSystemMessage(&Msg);
1115 }
1116
1117 return TRUE;
1118 }
1119
1120 BOOL FASTCALL
1121 IntKeyboardInput(KEYBDINPUT *ki)
1122 {
1123 return FALSE;
1124 }
1125
1126 UINT
1127 STDCALL
1128 NtUserSendInput(
1129 UINT nInputs,
1130 LPINPUT pInput,
1131 INT cbSize)
1132 {
1133 PW32THREAD W32Thread;
1134 UINT cnt;
1135 DECLARE_RETURN(UINT);
1136
1137 DPRINT("Enter NtUserSendInput\n");
1138 UserEnterExclusive();
1139
1140 W32Thread = PsGetWin32Thread();
1141 ASSERT(W32Thread);
1142
1143 if(!W32Thread->Desktop)
1144 {
1145 RETURN( 0);
1146 }
1147
1148 if(!nInputs || !pInput || (cbSize != sizeof(INPUT)))
1149 {
1150 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1151 RETURN( 0);
1152 }
1153
1154 /*
1155 * FIXME - check access rights of the window station
1156 * e.g. services running in the service window station cannot block input
1157 */
1158 if(!ThreadHasInputAccess(W32Thread) ||
1159 !IntIsActiveDesktop(W32Thread->Desktop))
1160 {
1161 SetLastWin32Error(ERROR_ACCESS_DENIED);
1162 RETURN( 0);
1163 }
1164
1165 cnt = 0;
1166 while(nInputs--)
1167 {
1168 INPUT SafeInput;
1169 NTSTATUS Status;
1170
1171 Status = MmCopyFromCaller(&SafeInput, pInput++, sizeof(INPUT));
1172 if(!NT_SUCCESS(Status))
1173 {
1174 SetLastNtError(Status);
1175 RETURN( cnt);
1176 }
1177
1178 switch(SafeInput.type)
1179 {
1180 case INPUT_MOUSE:
1181 if(IntMouseInput(&SafeInput.mi))
1182 {
1183 cnt++;
1184 }
1185 break;
1186 case INPUT_KEYBOARD:
1187 if(IntKeyboardInput(&SafeInput.ki))
1188 {
1189 cnt++;
1190 }
1191 break;
1192 case INPUT_HARDWARE:
1193 break;
1194 #ifndef NDEBUG
1195 default:
1196 DPRINT1("SendInput(): Invalid input type: 0x%x\n", SafeInput.type);
1197 break;
1198 #endif
1199 }
1200 }
1201
1202 RETURN( cnt);
1203
1204 CLEANUP:
1205 DPRINT("Leave NtUserSendInput, ret=%i\n",_ret_);
1206 UserLeave();
1207 END_CLEANUP;
1208 }
1209
1210 /* EOF */