2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window classes
5 * FILE: subsys/win32k/ntuser/class.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * 06-06-2001 CSH Created
11 /* INCLUDES ******************************************************************/
19 extern BYTE gQueueKeyStateTable
[];
20 extern NTSTATUS
Win32kInitWin32Thread(PETHREAD Thread
);
22 /* GLOBALS *******************************************************************/
24 PTHREADINFO ptiRawInput
;
26 PATTACHINFO gpai
= NULL
;
28 static HANDLE MouseDeviceHandle
;
29 static HANDLE MouseThreadHandle
;
30 static CLIENT_ID MouseThreadId
;
31 static HANDLE KeyboardThreadHandle
;
32 static CLIENT_ID KeyboardThreadId
;
33 static HANDLE KeyboardDeviceHandle
;
34 static HANDLE RawInputThreadHandle
;
35 static CLIENT_ID RawInputThreadId
;
36 static KEVENT InputThreadsStart
;
37 static BOOLEAN InputThreadsRunning
= FALSE
;
38 static BYTE TrackSysKey
= 0; /* determine whether ALT key up will cause a WM_SYSKEYUP
39 or a WM_KEYUP message */
41 /* FUNCTIONS *****************************************************************/
42 DWORD
IntLastInputTick(BOOL LastInputTickSetGet
);
44 #define ClearMouseInput(mi) \
50 #define SendMouseEvent(mi) \
51 if(mi.dx != 0 || mi.dy != 0) \
52 mi.dwFlags |= MOUSEEVENTF_MOVE; \
58 DWORD
IntLastInputTick(BOOL LastInputTickSetGet
)
60 static DWORD LastInputTick
= 0;
61 if (LastInputTickSetGet
== TRUE
)
63 LARGE_INTEGER TickCount
;
64 KeQueryTickCount(&TickCount
);
65 LastInputTick
= TickCount
.u
.LowPart
* (KeQueryTimeIncrement() / 10000);
72 NtUserGetLastInputInfo(PLASTINPUTINFO plii
)
80 if (ProbeForReadUint(&plii
->cbSize
) != sizeof(LASTINPUTINFO
))
82 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
87 ProbeForWrite(plii
, sizeof(LASTINPUTINFO
), sizeof(DWORD
));
89 plii
->dwTime
= IntLastInputTick(FALSE
);
91 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
93 SetLastNtError(_SEH2_GetExceptionCode());
105 ProcessMouseInputData(PMOUSE_INPUT_DATA Data
, ULONG InputCount
)
107 PMOUSE_INPUT_DATA mid
;
114 for(i
= 0; i
< InputCount
; i
++)
120 /* Check if the mouse move is absolute */
121 if (mid
->Flags
== MOUSE_MOVE_ABSOLUTE
)
123 /* Set flag and convert to screen location */
124 mi
.dwFlags
|= MOUSEEVENTF_ABSOLUTE
;
125 mi
.dx
= mi
.dx
/ (65535 / (UserGetSystemMetrics(SM_CXVIRTUALSCREEN
) - 1));
126 mi
.dy
= mi
.dy
/ (65535 / (UserGetSystemMetrics(SM_CYVIRTUALSCREEN
) - 1));
131 if(mid
->ButtonFlags
& MOUSE_LEFT_BUTTON_DOWN
)
133 mi
.dwFlags
|= MOUSEEVENTF_LEFTDOWN
;
136 if(mid
->ButtonFlags
& MOUSE_LEFT_BUTTON_UP
)
138 mi
.dwFlags
|= MOUSEEVENTF_LEFTUP
;
141 if(mid
->ButtonFlags
& MOUSE_MIDDLE_BUTTON_DOWN
)
143 mi
.dwFlags
|= MOUSEEVENTF_MIDDLEDOWN
;
146 if(mid
->ButtonFlags
& MOUSE_MIDDLE_BUTTON_UP
)
148 mi
.dwFlags
|= MOUSEEVENTF_MIDDLEUP
;
151 if(mid
->ButtonFlags
& MOUSE_RIGHT_BUTTON_DOWN
)
153 mi
.dwFlags
|= MOUSEEVENTF_RIGHTDOWN
;
156 if(mid
->ButtonFlags
& MOUSE_RIGHT_BUTTON_UP
)
158 mi
.dwFlags
|= MOUSEEVENTF_RIGHTUP
;
161 if(mid
->ButtonFlags
& MOUSE_BUTTON_4_DOWN
)
163 mi
.mouseData
|= XBUTTON1
;
164 mi
.dwFlags
|= MOUSEEVENTF_XDOWN
;
167 if(mid
->ButtonFlags
& MOUSE_BUTTON_4_UP
)
169 mi
.mouseData
|= XBUTTON1
;
170 mi
.dwFlags
|= MOUSEEVENTF_XUP
;
173 if(mid
->ButtonFlags
& MOUSE_BUTTON_5_DOWN
)
175 mi
.mouseData
|= XBUTTON2
;
176 mi
.dwFlags
|= MOUSEEVENTF_XDOWN
;
179 if(mid
->ButtonFlags
& MOUSE_BUTTON_5_UP
)
181 mi
.mouseData
|= XBUTTON2
;
182 mi
.dwFlags
|= MOUSEEVENTF_XUP
;
185 if(mid
->ButtonFlags
& MOUSE_WHEEL
)
187 mi
.mouseData
= mid
->ButtonData
;
188 mi
.dwFlags
|= MOUSEEVENTF_WHEEL
;
201 MouseThreadMain(PVOID StartContext
)
203 UNICODE_STRING MouseDeviceName
= RTL_CONSTANT_STRING(L
"\\Device\\PointerClass0");
204 OBJECT_ATTRIBUTES MouseObjectAttributes
;
205 IO_STATUS_BLOCK Iosb
;
207 MOUSE_ATTRIBUTES MouseAttr
;
209 InitializeObjectAttributes(&MouseObjectAttributes
,
216 LARGE_INTEGER DueTime
;
218 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
219 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
220 Status
= KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &DueTime
);
221 Status
= NtOpenFile(&MouseDeviceHandle
,
223 &MouseObjectAttributes
,
226 FILE_SYNCHRONOUS_IO_ALERT
);
227 } while (!NT_SUCCESS(Status
));
229 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
230 LOW_REALTIME_PRIORITY
+ 3);
235 * Wait to start input.
237 DPRINT("Mouse Input Thread Waiting for start event\n");
238 Status
= KeWaitForSingleObject(&InputThreadsStart
,
243 DPRINT("Mouse Input Thread Starting...\n");
245 /*FIXME: Does mouse attributes need to be used for anything */
246 Status
= NtDeviceIoControlFile(MouseDeviceHandle
,
251 IOCTL_MOUSE_QUERY_ATTRIBUTES
,
252 &MouseAttr
, sizeof(MOUSE_ATTRIBUTES
),
254 if(!NT_SUCCESS(Status
))
256 DPRINT("Failed to get mouse attributes\n");
260 * Receive and process mouse input.
262 while(InputThreadsRunning
)
264 MOUSE_INPUT_DATA MouseInput
;
265 Status
= NtReadFile(MouseDeviceHandle
,
271 sizeof(MOUSE_INPUT_DATA
),
274 if(Status
== STATUS_ALERTED
&& !InputThreadsRunning
)
278 if(Status
== STATUS_PENDING
)
280 NtWaitForSingleObject(MouseDeviceHandle
, FALSE
, NULL
);
281 Status
= Iosb
.Status
;
283 if(!NT_SUCCESS(Status
))
285 DPRINT1("Win32K: Failed to read from mouse.\n");
288 DPRINT("MouseEvent\n");
289 IntLastInputTick(TRUE
);
291 UserEnterExclusive();
293 ProcessMouseInputData(&MouseInput
, Iosb
.Information
/ sizeof(MOUSE_INPUT_DATA
));
297 DPRINT("Mouse Input Thread Stopped...\n");
301 /* Returns a value that indicates if the key is a modifier key, and
305 IntKeyboardGetModifiers(KEYBOARD_INPUT_DATA
*InputData
)
307 if (InputData
->Flags
& KEY_E1
)
310 if (!(InputData
->Flags
& KEY_E0
))
312 switch (InputData
->MakeCode
)
314 case 0x2a: /* left shift */
315 case 0x36: /* right shift */
318 case 0x1d: /* left control */
321 case 0x38: /* left alt */
330 switch (InputData
->MakeCode
)
332 case 0x1d: /* right control */
335 case 0x38: /* right alt */
338 case 0x5b: /* left gui (windows) */
339 case 0x5c: /* right gui (windows) */
348 /* Asks the keyboard driver to send a small table that shows which
349 * lights should connect with which scancodes
351 static NTSTATUS APIENTRY
352 IntKeyboardGetIndicatorTrans(HANDLE KeyboardDeviceHandle
,
353 PKEYBOARD_INDICATOR_TRANSLATION
*IndicatorTrans
)
357 IO_STATUS_BLOCK Block
;
358 PKEYBOARD_INDICATOR_TRANSLATION Ret
;
360 Size
= sizeof(KEYBOARD_INDICATOR_TRANSLATION
);
362 Ret
= ExAllocatePoolWithTag(PagedPool
,
368 Status
= NtDeviceIoControlFile(KeyboardDeviceHandle
,
373 IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION
,
377 if (Status
!= STATUS_BUFFER_TOO_SMALL
)
380 ExFreePoolWithTag(Ret
, TAG_KEYBOARD
);
382 Size
+= sizeof(KEYBOARD_INDICATOR_TRANSLATION
);
384 Ret
= ExAllocatePoolWithTag(PagedPool
,
390 return STATUS_INSUFFICIENT_RESOURCES
;
392 if (Status
!= STATUS_SUCCESS
)
394 ExFreePoolWithTag(Ret
, TAG_KEYBOARD
);
398 *IndicatorTrans
= Ret
;
402 /* Sends the keyboard commands to turn on/off the lights.
404 static NTSTATUS APIENTRY
405 IntKeyboardUpdateLeds(HANDLE KeyboardDeviceHandle
,
406 PKEYBOARD_INPUT_DATA KeyInput
,
407 PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans
)
411 static KEYBOARD_INDICATOR_PARAMETERS Indicators
;
412 IO_STATUS_BLOCK Block
;
415 return STATUS_NOT_SUPPORTED
;
417 if (KeyInput
->Flags
& (KEY_E0
| KEY_E1
| KEY_BREAK
))
418 return STATUS_SUCCESS
;
420 for (Count
= 0; Count
< IndicatorTrans
->NumberOfIndicatorKeys
; Count
++)
422 if (KeyInput
->MakeCode
== IndicatorTrans
->IndicatorList
[Count
].MakeCode
)
424 Indicators
.LedFlags
^=
425 IndicatorTrans
->IndicatorList
[Count
].IndicatorFlags
;
427 /* Update the lights on the hardware */
429 Status
= NtDeviceIoControlFile(KeyboardDeviceHandle
,
434 IOCTL_KEYBOARD_SET_INDICATORS
,
435 &Indicators
, sizeof(Indicators
),
442 return STATUS_SUCCESS
;
446 IntKeyboardSendWinKeyMsg()
448 PWINDOW_OBJECT Window
;
451 if (!(Window
= UserGetWindowObject(InputWindowStation
->ShellWindow
)))
453 DPRINT1("Couldn't find window to send Windows key message!\n");
457 Mesg
.hwnd
= InputWindowStation
->ShellWindow
;
458 Mesg
.message
= WM_SYSCOMMAND
;
459 Mesg
.wParam
= SC_TASKLIST
;
462 /* The QS_HOTKEY is just a guess */
463 MsqPostMessage(Window
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
467 co_IntKeyboardSendAltKeyMsg()
469 co_MsqPostKeyboardMessage(WM_SYSCOMMAND
,SC_KEYMENU
,0);
473 KeyboardThreadMain(PVOID StartContext
)
475 UNICODE_STRING KeyboardDeviceName
= RTL_CONSTANT_STRING(L
"\\Device\\KeyboardClass0");
476 OBJECT_ATTRIBUTES KeyboardObjectAttributes
;
477 IO_STATUS_BLOCK Iosb
;
480 PUSER_MESSAGE_QUEUE FocusQueue
;
481 struct _ETHREAD
*FocusThread
;
483 PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans
= NULL
;
484 UINT ModifierState
= 0;
485 USHORT LastMakeCode
= 0;
486 USHORT LastFlags
= 0;
487 UINT RepeatCount
= 0;
489 InitializeObjectAttributes(&KeyboardObjectAttributes
,
496 LARGE_INTEGER DueTime
;
498 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
499 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
500 Status
= KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &DueTime
);
501 Status
= NtOpenFile(&KeyboardDeviceHandle
,
503 &KeyboardObjectAttributes
,
506 FILE_SYNCHRONOUS_IO_ALERT
);
507 } while (!NT_SUCCESS(Status
));
509 /* Not sure if converting this thread to a win32 thread is such
510 a great idea. Since we're posting keyboard messages to the focus
511 window message queue, we'll be (indirectly) doing sendmessage
512 stuff from this thread (for WH_KEYBOARD_LL processing), which
513 means we need our own message queue. If keyboard messages were
514 instead queued to the system message queue, the thread removing
515 the message from the system message queue would be responsible
516 for WH_KEYBOARD_LL processing and we wouldn't need this thread
517 to be a win32 thread. */
518 Status
= Win32kInitWin32Thread(PsGetCurrentThread());
519 if (!NT_SUCCESS(Status
))
521 DPRINT1("Win32K: Failed making keyboard thread a win32 thread.\n");
525 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
526 LOW_REALTIME_PRIORITY
+ 3);
528 IntKeyboardGetIndicatorTrans(KeyboardDeviceHandle
,
534 * Wait to start input.
536 DPRINT( "Keyboard Input Thread Waiting for start event\n" );
537 Status
= KeWaitForSingleObject(&InputThreadsStart
,
543 DPRINT( "Keyboard Input Thread Starting...\n" );
545 * Receive and process keyboard input.
547 while (InputThreadsRunning
)
551 KEYBOARD_INPUT_DATA KeyInput
;
552 KEYBOARD_INPUT_DATA NextKeyInput
;
554 UINT fsModifiers
, fsNextModifiers
;
555 struct _ETHREAD
*Thread
;
559 DPRINT("KeyInput @ %08x\n", &KeyInput
);
561 Status
= NtReadFile (KeyboardDeviceHandle
,
567 sizeof(KEYBOARD_INPUT_DATA
),
571 if(Status
== STATUS_ALERTED
&& !InputThreadsRunning
)
575 if(Status
== STATUS_PENDING
)
577 NtWaitForSingleObject(KeyboardDeviceHandle
, FALSE
, NULL
);
578 Status
= Iosb
.Status
;
580 if(!NT_SUCCESS(Status
))
582 DPRINT1("Win32K: Failed to read from mouse.\n");
586 DPRINT("KeyRaw: %s %04x\n",
587 (KeyInput
.Flags
& KEY_BREAK
) ? "up" : "down",
590 if (Status
== STATUS_ALERTED
&& !InputThreadsRunning
)
593 if (!NT_SUCCESS(Status
))
595 DPRINT1("Win32K: Failed to read from keyboard.\n");
599 /* Set LastInputTick */
600 IntLastInputTick(TRUE
);
602 /* Update modifier state */
603 fsModifiers
= IntKeyboardGetModifiers(&KeyInput
);
607 if (KeyInput
.Flags
& KEY_BREAK
)
609 ModifierState
&= ~fsModifiers
;
610 if(fsModifiers
== MOD_ALT
)
612 if(KeyInput
.Flags
& KEY_E0
)
614 gQueueKeyStateTable
[VK_RMENU
] = 0;
618 gQueueKeyStateTable
[VK_LMENU
] = 0;
620 if (gQueueKeyStateTable
[VK_RMENU
] == 0 &&
621 gQueueKeyStateTable
[VK_LMENU
] == 0)
623 gQueueKeyStateTable
[VK_MENU
] = 0;
629 ModifierState
|= fsModifiers
;
631 if (ModifierState
== fsModifiers
&&
632 (fsModifiers
== MOD_ALT
|| fsModifiers
== MOD_WIN
))
634 /* First send out special notifications
635 * (For alt, the message that turns on accelerator
636 * display, not sure what for win. Both TODO though.)
639 if(fsModifiers
== MOD_ALT
)
641 if(KeyInput
.Flags
& KEY_E0
)
643 gQueueKeyStateTable
[VK_RMENU
] = 0x80;
647 gQueueKeyStateTable
[VK_LMENU
] = 0x80;
651 gQueueKeyStateTable
[VK_MENU
] = 0x80;
654 /* Read the next key before sending this one */
657 Status
= NtReadFile (KeyboardDeviceHandle
,
663 sizeof(KEYBOARD_INPUT_DATA
),
666 DPRINT("KeyRaw: %s %04x\n",
667 (NextKeyInput
.Flags
& KEY_BREAK
) ? "up":"down",
668 NextKeyInput
.MakeCode
);
670 if (Status
== STATUS_ALERTED
&& !InputThreadsRunning
)
674 while ((!(NextKeyInput
.Flags
& KEY_BREAK
)) &&
675 NextKeyInput
.MakeCode
== KeyInput
.MakeCode
);
676 /* ^ Ignore repeats, they'll be KEY_MAKE and the same
677 * code. I'm not caring about the counting, not sure
678 * if that matters. I think not.
681 /* If the ModifierState is now empty again, send a
682 * special notification and eat both keypresses
685 fsNextModifiers
= IntKeyboardGetModifiers(&NextKeyInput
);
688 ModifierState
^= fsNextModifiers
;
690 if (ModifierState
== 0)
692 if (fsModifiers
== MOD_WIN
)
693 IntKeyboardSendWinKeyMsg();
694 else if (fsModifiers
== MOD_ALT
)
696 gQueueKeyStateTable
[VK_MENU
] = 0;
699 gQueueKeyStateTable
[VK_LMENU
] = 0;
703 gQueueKeyStateTable
[VK_RMENU
] = 0;
705 co_IntKeyboardSendAltKeyMsg();
715 for (;NumKeys
;memcpy(&KeyInput
, &NextKeyInput
, sizeof(KeyInput
)),
720 IntKeyboardUpdateLeds(KeyboardDeviceHandle
,
724 /* While we are working, we set up lParam. The format is:
725 * 0-15: The number of times this key has autorepeated
726 * 16-23: The keyboard scancode
727 * 24: Set if it's and extended key (I assume KEY_E0 | KEY_E1)
728 * Note that E1 is only used for PAUSE (E1-1D-45) and
729 * E0-45 happens not to be anything.
730 * 29: Alt is pressed ('Context code')
731 * 30: Previous state, if the key was down before this message
732 * This is a cheap way to ignore autorepeat keys
733 * 31: 1 if the key is being pressed
736 /* If it's a KEY_MAKE (which is 0, so test using !KEY_BREAK)
737 * and it's the same key as the last one, increase the repeat
741 if (!(KeyInput
.Flags
& KEY_BREAK
))
743 if (((KeyInput
.Flags
& (KEY_E0
| KEY_E1
)) == LastFlags
) &&
744 (KeyInput
.MakeCode
== LastMakeCode
))
752 LastFlags
= KeyInput
.Flags
& (KEY_E0
| KEY_E1
);
753 LastMakeCode
= KeyInput
.MakeCode
;
759 LastMakeCode
= 0; /* Should never match */
760 lParam
|= (1 << 30) | (1 << 31);
763 lParam
|= RepeatCount
;
765 lParam
|= (KeyInput
.MakeCode
& 0xff) << 16;
767 if (KeyInput
.Flags
& KEY_E0
)
770 if (ModifierState
& MOD_ALT
)
774 if (!(KeyInput
.Flags
& KEY_BREAK
))
775 msg
.message
= WM_SYSKEYDOWN
;
777 msg
.message
= WM_SYSKEYUP
;
781 if (!(KeyInput
.Flags
& KEY_BREAK
))
782 msg
.message
= WM_KEYDOWN
;
784 msg
.message
= WM_KEYUP
;
787 /* Find the target thread whose locale is in effect */
788 FocusQueue
= IntGetFocusMessageQueue();
790 /* This might cause us to lose hot keys, which are important
791 * (ctrl-alt-del secure attention sequence). Not sure if it
798 msg
.hwnd
= FocusQueue
->FocusWindow
;
800 FocusThread
= FocusQueue
->Thread
;
802 if (!(FocusThread
&& FocusThread
->Tcb
.Win32Thread
&&
803 ((PTHREADINFO
)FocusThread
->Tcb
.Win32Thread
)->KeyboardLayout
))
806 /* This function uses lParam to fill wParam according to the
807 * keyboard layout in use.
809 W32kKeyProcessMessage(&msg
,
810 ((PTHREADINFO
)FocusThread
->Tcb
.Win32Thread
)->KeyboardLayout
->KBTables
,
811 KeyInput
.Flags
& KEY_E0
? 0xE0 :
812 (KeyInput
.Flags
& KEY_E1
? 0xE1 : 0));
814 if (GetHotKey(ModifierState
,
820 if (!(KeyInput
.Flags
& KEY_BREAK
))
822 DPRINT("Hot key pressed (hWnd %lx, id %d)\n", hWnd
, id
);
823 MsqPostHotKeyMessage (Thread
,
826 MAKELPARAM((WORD
)ModifierState
,
829 continue; /* Eat key up motion too */
833 * Post a keyboard message.
835 co_MsqPostKeyboardMessage(msg
.message
,msg
.wParam
,msg
.lParam
);
840 DPRINT( "KeyboardInput Thread Stopped...\n" );
845 static PVOID Objects
[2];
848 Since this relies on InputThreadsStart, just fake it.
851 RawInputThreadMain(PVOID StartContext
)
854 LARGE_INTEGER DueTime
;
856 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
861 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
862 Status
= KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &DueTime
);
863 } while (!NT_SUCCESS(Status
));
866 Objects
[0] = &InputThreadsStart
;
868 MasterTimer
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KTIMER
), TAG_INPUT
);
871 DPRINT1("Win32K: Failed making Raw Input thread a win32 thread.\n");
874 KeInitializeTimer(MasterTimer
);
875 Objects
[1] = MasterTimer
;
877 // This thread requires win32k!
878 Status
= Win32kInitWin32Thread(PsGetCurrentThread());
879 if (!NT_SUCCESS(Status
))
881 DPRINT1("Win32K: Failed making Raw Input thread a win32 thread.\n");
885 ptiRawInput
= PsGetCurrentThreadWin32Thread();
886 DPRINT("\nRaw Input Thread 0x%x \n", ptiRawInput
);
889 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
890 LOW_REALTIME_PRIORITY
+ 3);
892 UserEnterExclusive();
897 // ATM, we just have one job to handle, merge the other two later.
901 DPRINT( "Raw Input Thread Waiting for start event\n" );
903 Status
= KeWaitForMultipleObjects( 2,
911 DPRINT( "Raw Input Thread Starting...\n" );
915 DPRINT1("Raw Input Thread Exit!\n");
923 KeInitializeEvent(&InputThreadsStart
, NotificationEvent
, FALSE
);
925 /* Initialize the default keyboard layout */
926 if(!UserInitDefaultKeyboardLayout())
928 DPRINT1("Failed to initialize default keyboard layout!\n");
931 Status
= PsCreateSystemThread(&RawInputThreadHandle
,
938 if (!NT_SUCCESS(Status
))
940 DPRINT1("Win32K: Failed to create raw thread.\n");
943 Status
= PsCreateSystemThread(&KeyboardThreadHandle
,
950 if (!NT_SUCCESS(Status
))
952 DPRINT1("Win32K: Failed to create keyboard thread.\n");
955 Status
= PsCreateSystemThread(&MouseThreadHandle
,
962 if (!NT_SUCCESS(Status
))
964 DPRINT1("Win32K: Failed to create mouse thread.\n");
967 InputThreadsRunning
= TRUE
;
968 KeSetEvent(&InputThreadsStart
, IO_NO_INCREMENT
, FALSE
);
970 return STATUS_SUCCESS
;
974 CleanupInputImp(VOID
)
976 return(STATUS_SUCCESS
);
983 POINT pt
) // Just like the User call.
990 IntBlockInput(PTHREADINFO W32Thread
, BOOL BlockIt
)
992 PTHREADINFO OldBlock
;
995 if(!W32Thread
->Desktop
|| ((W32Thread
->TIF_flags
& TIF_INCLEANUP
) && BlockIt
))
998 * fail blocking if exiting the thread
1005 * FIXME - check access rights of the window station
1006 * e.g. services running in the service window station cannot block input
1008 if(!ThreadHasInputAccess(W32Thread
) ||
1009 !IntIsActiveDesktop(W32Thread
->Desktop
))
1011 SetLastWin32Error(ERROR_ACCESS_DENIED
);
1015 ASSERT(W32Thread
->Desktop
);
1016 OldBlock
= W32Thread
->Desktop
->BlockInputThread
;
1019 if(OldBlock
!= W32Thread
)
1021 SetLastWin32Error(ERROR_ACCESS_DENIED
);
1024 W32Thread
->Desktop
->BlockInputThread
= (BlockIt
? W32Thread
: NULL
);
1025 return OldBlock
== NULL
;
1028 W32Thread
->Desktop
->BlockInputThread
= (BlockIt
? W32Thread
: NULL
);
1029 return OldBlock
== NULL
;
1037 DECLARE_RETURN(BOOLEAN
);
1039 DPRINT("Enter NtUserBlockInput\n");
1040 UserEnterExclusive();
1042 RETURN( IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt
));
1045 DPRINT("Leave NtUserBlockInput, ret=%i\n",_ret_
);
1051 IntMouseInput(MOUSEINPUT
*mi
)
1053 const UINT SwapBtnMsg
[2][2] =
1056 WM_LBUTTONDOWN
, WM_RBUTTONDOWN
1058 {WM_LBUTTONUP
, WM_RBUTTONUP
}
1060 const WPARAM SwapBtn
[2] =
1062 MK_LBUTTON
, MK_RBUTTON
1064 POINT MousePos
= {0}, OrgPos
;
1065 PSYSTEM_CURSORINFO CurInfo
;
1066 PWINSTATION_OBJECT WinSta
;
1067 BOOL DoMove
, SwapButtons
;
1072 PWINDOW_OBJECT DesktopWindow
;
1078 /* FIXME - get the screen dc from the window station or desktop */
1079 if(!(hDC
= IntGetScreenDC()))
1088 WinSta
= PsGetCurrentProcessWin32Process()->WindowStation
;
1090 /* FIXME - ugly hack but as long as we're using this dumb callback from the
1091 mouse class driver, we can't access the window station from the calling
1093 WinSta
= InputWindowStation
;
1098 CurInfo
= IntGetSysCursorInfo();
1102 LARGE_INTEGER LargeTickCount
;
1103 KeQueryTickCount(&LargeTickCount
);
1104 mi
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1107 SwapButtons
= gspv
.bMouseBtnSwap
;
1110 OrgPos
= MousePos
= gpsi
->ptCursor
;
1112 if(mi
->dwFlags
& MOUSEEVENTF_MOVE
)
1114 if(mi
->dwFlags
& MOUSEEVENTF_ABSOLUTE
)
1116 MousePos
.x
= mi
->dx
;
1117 MousePos
.y
= mi
->dy
;
1121 MousePos
.x
+= mi
->dx
;
1122 MousePos
.y
+= mi
->dy
;
1125 DesktopWindow
= IntGetWindowObject(WinSta
->ActiveDesktop
->DesktopWindow
);
1129 if(MousePos
.x
>= DesktopWindow
->Wnd
->rcClient
.right
)
1130 MousePos
.x
= DesktopWindow
->Wnd
->rcClient
.right
- 1;
1131 if(MousePos
.y
>= DesktopWindow
->Wnd
->rcClient
.bottom
)
1132 MousePos
.y
= DesktopWindow
->Wnd
->rcClient
.bottom
- 1;
1133 UserDereferenceObject(DesktopWindow
);
1141 if(CurInfo
->CursorClipInfo
.IsClipped
)
1143 /* The mouse cursor needs to be clipped */
1145 if(MousePos
.x
>= (LONG
)CurInfo
->CursorClipInfo
.Right
)
1146 MousePos
.x
= (LONG
)CurInfo
->CursorClipInfo
.Right
;
1147 if(MousePos
.x
< (LONG
)CurInfo
->CursorClipInfo
.Left
)
1148 MousePos
.x
= (LONG
)CurInfo
->CursorClipInfo
.Left
;
1149 if(MousePos
.y
>= (LONG
)CurInfo
->CursorClipInfo
.Bottom
)
1150 MousePos
.y
= (LONG
)CurInfo
->CursorClipInfo
.Bottom
;
1151 if(MousePos
.y
< (LONG
)CurInfo
->CursorClipInfo
.Top
)
1152 MousePos
.y
= (LONG
)CurInfo
->CursorClipInfo
.Top
;
1155 DoMove
= (MousePos
.x
!= OrgPos
.x
|| MousePos
.y
!= OrgPos
.y
);
1160 dc
= DC_LockDc(hDC
);
1163 psurf
= dc
->dclevel
.pSurface
;
1166 pso
= &psurf
->SurfObj
;
1168 if (CurInfo
->ShowingCursor
)
1170 IntEngMovePointer(pso
, MousePos
.x
, MousePos
.y
, &(GDIDEV(pso
)->Pointer
.Exclude
));
1172 /* Only now, update the info in the PDEVOBJ, so EngMovePointer can
1173 * use the old values to move the pointer image */
1174 gpsi
->ptCursor
.x
= MousePos
.x
;
1175 gpsi
->ptCursor
.y
= MousePos
.y
;
1183 * Insert the messages into the system queue
1186 Msg
.wParam
= CurInfo
->ButtonsDown
;
1187 Msg
.lParam
= MAKELPARAM(MousePos
.x
, MousePos
.y
);
1190 if (gQueueKeyStateTable
[VK_SHIFT
] & 0xc0)
1192 Msg
.wParam
|= MK_SHIFT
;
1195 if (gQueueKeyStateTable
[VK_CONTROL
] & 0xc0)
1197 Msg
.wParam
|= MK_CONTROL
;
1202 Msg
.message
= WM_MOUSEMOVE
;
1203 MsqInsertSystemMessage(&Msg
);
1207 if(mi
->dwFlags
& MOUSEEVENTF_LEFTDOWN
)
1209 gQueueKeyStateTable
[VK_LBUTTON
] |= 0xc0;
1210 Msg
.message
= SwapBtnMsg
[0][SwapButtons
];
1211 CurInfo
->ButtonsDown
|= SwapBtn
[SwapButtons
];
1212 MsqInsertSystemMessage(&Msg
);
1214 else if(mi
->dwFlags
& MOUSEEVENTF_LEFTUP
)
1216 gQueueKeyStateTable
[VK_LBUTTON
] &= ~0x80;
1217 Msg
.message
= SwapBtnMsg
[1][SwapButtons
];
1218 CurInfo
->ButtonsDown
&= ~SwapBtn
[SwapButtons
];
1219 MsqInsertSystemMessage(&Msg
);
1221 if(mi
->dwFlags
& MOUSEEVENTF_MIDDLEDOWN
)
1223 gQueueKeyStateTable
[VK_MBUTTON
] |= 0xc0;
1224 Msg
.message
= WM_MBUTTONDOWN
;
1225 CurInfo
->ButtonsDown
|= MK_MBUTTON
;
1226 MsqInsertSystemMessage(&Msg
);
1228 else if(mi
->dwFlags
& MOUSEEVENTF_MIDDLEUP
)
1230 gQueueKeyStateTable
[VK_MBUTTON
] &= ~0x80;
1231 Msg
.message
= WM_MBUTTONUP
;
1232 CurInfo
->ButtonsDown
&= ~MK_MBUTTON
;
1233 MsqInsertSystemMessage(&Msg
);
1235 if(mi
->dwFlags
& MOUSEEVENTF_RIGHTDOWN
)
1237 gQueueKeyStateTable
[VK_RBUTTON
] |= 0xc0;
1238 Msg
.message
= SwapBtnMsg
[0][!SwapButtons
];
1239 CurInfo
->ButtonsDown
|= SwapBtn
[!SwapButtons
];
1240 MsqInsertSystemMessage(&Msg
);
1242 else if(mi
->dwFlags
& MOUSEEVENTF_RIGHTUP
)
1244 gQueueKeyStateTable
[VK_RBUTTON
] &= ~0x80;
1245 Msg
.message
= SwapBtnMsg
[1][!SwapButtons
];
1246 CurInfo
->ButtonsDown
&= ~SwapBtn
[!SwapButtons
];
1247 MsqInsertSystemMessage(&Msg
);
1250 if((mi
->dwFlags
& (MOUSEEVENTF_XDOWN
| MOUSEEVENTF_XUP
)) &&
1251 (mi
->dwFlags
& MOUSEEVENTF_WHEEL
))
1253 /* fail because both types of events use the mouseData field */
1257 if(mi
->dwFlags
& MOUSEEVENTF_XDOWN
)
1259 Msg
.message
= WM_XBUTTONDOWN
;
1260 if(mi
->mouseData
& XBUTTON1
)
1262 gQueueKeyStateTable
[VK_XBUTTON1
] |= 0xc0;
1263 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON1
);
1264 CurInfo
->ButtonsDown
|= XBUTTON1
;
1265 MsqInsertSystemMessage(&Msg
);
1267 if(mi
->mouseData
& XBUTTON2
)
1269 gQueueKeyStateTable
[VK_XBUTTON2
] |= 0xc0;
1270 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON2
);
1271 CurInfo
->ButtonsDown
|= XBUTTON2
;
1272 MsqInsertSystemMessage(&Msg
);
1275 else if(mi
->dwFlags
& MOUSEEVENTF_XUP
)
1277 Msg
.message
= WM_XBUTTONUP
;
1278 if(mi
->mouseData
& XBUTTON1
)
1280 gQueueKeyStateTable
[VK_XBUTTON1
] &= ~0x80;
1281 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON1
);
1282 CurInfo
->ButtonsDown
&= ~XBUTTON1
;
1283 MsqInsertSystemMessage(&Msg
);
1285 if(mi
->mouseData
& XBUTTON2
)
1287 gQueueKeyStateTable
[VK_XBUTTON2
] &= ~0x80;
1288 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON2
);
1289 CurInfo
->ButtonsDown
&= ~XBUTTON2
;
1290 MsqInsertSystemMessage(&Msg
);
1293 if(mi
->dwFlags
& MOUSEEVENTF_WHEEL
)
1295 Msg
.message
= WM_MOUSEWHEEL
;
1296 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, mi
->mouseData
);
1297 MsqInsertSystemMessage(&Msg
);
1304 IntKeyboardInput(KEYBDINPUT
*ki
)
1306 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
1309 LARGE_INTEGER LargeTickCount
;
1310 KBDLLHOOKSTRUCT KbdHookData
;
1311 WORD flags
, wVkStripped
, wVkL
, wVkR
, wVk
= ki
->wVk
, vk_hook
= ki
->wVk
;
1312 BOOLEAN Entered
= FALSE
;
1316 // Condition may arise when calling MsqPostMessage and waiting for an event.
1317 if (!UserIsEntered())
1319 // Fixme: Not sure ATM if this thread is locked.
1320 UserEnterExclusive();
1326 flags
= LOBYTE(ki
->wScan
);
1328 if (ki
->dwFlags
& KEYEVENTF_EXTENDEDKEY
) flags
|= KF_EXTENDED
;
1329 /* FIXME: set KF_DLGMODE and KF_MENUMODE when needed */
1331 /* strip left/right for menu, control, shift */
1337 wVk
= (ki
->dwFlags
& KEYEVENTF_EXTENDEDKEY
) ? VK_RMENU
: VK_LMENU
;
1338 wVkStripped
= VK_MENU
;
1345 wVk
= (ki
->dwFlags
& KEYEVENTF_EXTENDEDKEY
) ? VK_RCONTROL
: VK_LCONTROL
;
1346 wVkStripped
= VK_CONTROL
;
1353 wVk
= (ki
->dwFlags
& KEYEVENTF_EXTENDEDKEY
) ? VK_RSHIFT
: VK_LSHIFT
;
1354 wVkStripped
= VK_SHIFT
;
1359 wVkStripped
= wVkL
= wVkR
= wVk
;
1362 if (ki
->dwFlags
& KEYEVENTF_KEYUP
)
1364 Msg
.message
= WM_KEYUP
;
1365 if ((gQueueKeyStateTable
[VK_MENU
] & 0x80) &&
1366 ((wVkStripped
== VK_MENU
) || (wVkStripped
== VK_CONTROL
)
1367 || !(gQueueKeyStateTable
[VK_CONTROL
] & 0x80)))
1369 if( TrackSysKey
== VK_MENU
|| /* <ALT>-down/<ALT>-up sequence */
1370 (wVkStripped
!= VK_MENU
)) /* <ALT>-down...<something else>-up */
1371 Msg
.message
= WM_SYSKEYUP
;
1374 flags
|= KF_REPEAT
| KF_UP
;
1378 Msg
.message
= WM_KEYDOWN
;
1379 if ((gQueueKeyStateTable
[VK_MENU
] & 0x80 || wVkStripped
== VK_MENU
) &&
1380 !(gQueueKeyStateTable
[VK_CONTROL
] & 0x80 || wVkStripped
== VK_CONTROL
))
1382 Msg
.message
= WM_SYSKEYDOWN
;
1383 TrackSysKey
= wVkStripped
;
1385 if (!(ki
->dwFlags
& KEYEVENTF_UNICODE
) && gQueueKeyStateTable
[wVk
] & 0x80) flags
|= KF_REPEAT
;
1388 if (ki
->dwFlags
& KEYEVENTF_UNICODE
)
1390 vk_hook
= Msg
.wParam
= wVk
= VK_PACKET
;
1391 Msg
.lParam
= MAKELPARAM(1 /* repeat count */, ki
->wScan
);
1394 FocusMessageQueue
= IntGetFocusMessageQueue();
1398 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
1399 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
1403 KeQueryTickCount(&LargeTickCount
);
1404 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
1407 Msg
.time
= ki
->time
;
1409 /* All messages have to contain the cursor point. */
1410 pti
= PsGetCurrentThreadWin32Thread();
1411 Msg
.pt
= gpsi
->ptCursor
;
1413 DPRINT1("Kbd Hook msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
1414 Msg
.message
, vk_hook
, Msg
.lParam
);
1416 KbdHookData
.vkCode
= vk_hook
;
1417 KbdHookData
.scanCode
= ki
->wScan
;
1418 KbdHookData
.flags
= flags
>> 8;
1419 KbdHookData
.time
= Msg
.time
;
1420 KbdHookData
.dwExtraInfo
= ki
->dwExtraInfo
;
1421 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
1423 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
1424 Msg
.message
, vk_hook
, Msg
.lParam
);
1425 if (Entered
) UserLeave();
1429 if (!(ki
->dwFlags
& KEYEVENTF_UNICODE
))
1431 if (ki
->dwFlags
& KEYEVENTF_KEYUP
)
1433 gQueueKeyStateTable
[wVk
] &= ~0x80;
1434 gQueueKeyStateTable
[wVkStripped
] = gQueueKeyStateTable
[wVkL
] | gQueueKeyStateTable
[wVkR
];
1438 if (!(gQueueKeyStateTable
[wVk
] & 0x80)) gQueueKeyStateTable
[wVk
] ^= 0x01;
1439 gQueueKeyStateTable
[wVk
] |= 0xc0;
1440 gQueueKeyStateTable
[wVkStripped
] = gQueueKeyStateTable
[wVkL
] | gQueueKeyStateTable
[wVkR
];
1443 if (gQueueKeyStateTable
[VK_MENU
] & 0x80) flags
|= KF_ALTDOWN
;
1445 if (wVkStripped
== VK_SHIFT
) flags
&= ~KF_EXTENDED
;
1447 Msg
.lParam
= MAKELPARAM(1 /* repeat count */, flags
);
1450 if (FocusMessageQueue
== NULL
)
1452 DPRINT("No focus message queue\n");
1453 if (Entered
) UserLeave();
1457 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
1459 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
1460 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
1462 FocusMessageQueue
->Desktop
->pDeskInfo
->LastInputWasKbd
= TRUE
;
1464 Msg
.pt
= gpsi
->ptCursor
;
1466 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
1470 DPRINT("Invalid focus window handle\n");
1473 if (Entered
) UserLeave();
1479 UserAttachThreadInput( PTHREADINFO pti
, PTHREADINFO ptiTo
, BOOL fAttach
)
1483 /* Can not be the same thread.*/
1484 if (pti
== ptiTo
) return FALSE
;
1486 /* Do not attach to system threads or between different desktops. */
1487 if ( pti
->TIF_flags
& TIF_DONTATTACHQUEUE
||
1488 ptiTo
->TIF_flags
& TIF_DONTATTACHQUEUE
||
1489 pti
->Desktop
!= ptiTo
->Desktop
)
1492 /* If Attach set, allocate and link. */
1495 pai
= ExAllocatePoolWithTag(PagedPool
, sizeof(ATTACHINFO
), TAG_ATTACHINFO
);
1496 if ( !pai
) return FALSE
;
1498 pai
->paiNext
= gpai
;
1503 else /* If clear, unlink and free it. */
1505 PATTACHINFO paiprev
= NULL
;
1507 if ( !gpai
) return FALSE
;
1511 /* Search list and free if found or return false. */
1514 if ( pai
->pti2
== ptiTo
&& pai
->pti1
== pti
) break;
1519 if ( !pai
) return FALSE
;
1521 if (paiprev
) paiprev
->paiNext
= pai
->paiNext
;
1523 ExFreePoolWithTag(pai
, TAG_ATTACHINFO
);
1536 PTHREADINFO W32Thread
;
1538 DECLARE_RETURN(UINT
);
1540 DPRINT("Enter NtUserSendInput\n");
1541 UserEnterExclusive();
1543 W32Thread
= PsGetCurrentThreadWin32Thread();
1546 if(!W32Thread
->Desktop
)
1551 if(!nInputs
|| !pInput
|| (cbSize
!= sizeof(INPUT
)))
1553 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
1558 * FIXME - check access rights of the window station
1559 * e.g. services running in the service window station cannot block input
1561 if(!ThreadHasInputAccess(W32Thread
) ||
1562 !IntIsActiveDesktop(W32Thread
->Desktop
))
1564 SetLastWin32Error(ERROR_ACCESS_DENIED
);
1574 Status
= MmCopyFromCaller(&SafeInput
, pInput
++, sizeof(INPUT
));
1575 if(!NT_SUCCESS(Status
))
1577 SetLastNtError(Status
);
1581 switch(SafeInput
.type
)
1584 if(IntMouseInput(&SafeInput
.mi
))
1589 case INPUT_KEYBOARD
:
1590 if(IntKeyboardInput(&SafeInput
.ki
))
1595 case INPUT_HARDWARE
:
1600 DPRINT1("SendInput(): Invalid input type: 0x%x\n", SafeInput
.type
);
1610 DPRINT("Leave NtUserSendInput, ret=%i\n",_ret_
);