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
;
25 PTHREADINFO ptiKeyboard
;
27 PKTIMER MasterTimer
= NULL
;
28 PATTACHINFO gpai
= NULL
;
30 static HANDLE MouseDeviceHandle
;
31 static HANDLE MouseThreadHandle
;
32 static CLIENT_ID MouseThreadId
;
33 static HANDLE KeyboardThreadHandle
;
34 static CLIENT_ID KeyboardThreadId
;
35 static HANDLE KeyboardDeviceHandle
;
36 static HANDLE RawInputThreadHandle
;
37 static CLIENT_ID RawInputThreadId
;
38 static KEVENT InputThreadsStart
;
39 static BOOLEAN InputThreadsRunning
= FALSE
;
40 static BYTE TrackSysKey
= 0; /* determine whether ALT key up will cause a WM_SYSKEYUP
41 or a WM_KEYUP message */
43 /* FUNCTIONS *****************************************************************/
44 DWORD
IntLastInputTick(BOOL LastInputTickSetGet
);
46 #define ClearMouseInput(mi) \
52 #define SendMouseEvent(mi) \
53 if(mi.dx != 0 || mi.dy != 0) \
54 mi.dwFlags |= MOUSEEVENTF_MOVE; \
60 DWORD
IntLastInputTick(BOOL LastInputTickSetGet
)
62 static DWORD LastInputTick
= 0;
63 if (LastInputTickSetGet
== TRUE
)
65 LARGE_INTEGER TickCount
;
66 KeQueryTickCount(&TickCount
);
67 LastInputTick
= TickCount
.u
.LowPart
* (KeQueryTimeIncrement() / 10000);
74 NtUserGetLastInputInfo(PLASTINPUTINFO plii
)
82 if (ProbeForReadUint(&plii
->cbSize
) != sizeof(LASTINPUTINFO
))
84 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
89 ProbeForWrite(plii
, sizeof(LASTINPUTINFO
), sizeof(DWORD
));
91 plii
->dwTime
= IntLastInputTick(FALSE
);
93 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
95 SetLastNtError(_SEH2_GetExceptionCode());
107 ProcessMouseInputData(PMOUSE_INPUT_DATA Data
, ULONG InputCount
)
109 PMOUSE_INPUT_DATA mid
;
116 for(i
= 0; i
< InputCount
; i
++)
122 /* Check if the mouse move is absolute */
123 if (mid
->Flags
== MOUSE_MOVE_ABSOLUTE
)
125 /* Set flag to convert to screen location */
126 mi
.dwFlags
|= MOUSEEVENTF_ABSOLUTE
;
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 Status
= Win32kInitWin32Thread(PsGetCurrentThread());
210 if (!NT_SUCCESS(Status
))
212 DPRINT1("Win32K: Failed making keyboard thread a win32 thread.\n");
216 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
217 LOW_REALTIME_PRIORITY
+ 3);
219 InitializeObjectAttributes(&MouseObjectAttributes
,
226 LARGE_INTEGER DueTime
;
228 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
229 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
230 Status
= KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &DueTime
);
231 Status
= NtOpenFile(&MouseDeviceHandle
,
233 &MouseObjectAttributes
,
236 FILE_SYNCHRONOUS_IO_ALERT
);
237 } while (!NT_SUCCESS(Status
));
239 /* Need to setup basic win32k for this thread to process WH_MOUSE_LL messages. */
240 Status
= Win32kInitWin32Thread(PsGetCurrentThread());
241 if (!NT_SUCCESS(Status
))
243 DPRINT1("Win32K: Failed making mouse thread a win32 thread.\n");
247 ptiMouse
= PsGetCurrentThreadWin32Thread();
248 ptiMouse
->TIF_flags
|= TIF_SYSTEMTHREAD
;
249 DPRINT1("\nMouse Thread 0x%x \n", ptiMouse
);
251 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
252 LOW_REALTIME_PRIORITY
+ 3);
257 * Wait to start input.
259 DPRINT("Mouse Input Thread Waiting for start event\n");
260 Status
= KeWaitForSingleObject(&InputThreadsStart
,
265 DPRINT("Mouse Input Thread Starting...\n");
267 /*FIXME: Does mouse attributes need to be used for anything */
268 Status
= NtDeviceIoControlFile(MouseDeviceHandle
,
273 IOCTL_MOUSE_QUERY_ATTRIBUTES
,
274 &MouseAttr
, sizeof(MOUSE_ATTRIBUTES
),
276 if(!NT_SUCCESS(Status
))
278 DPRINT("Failed to get mouse attributes\n");
282 * Receive and process mouse input.
284 while(InputThreadsRunning
)
286 MOUSE_INPUT_DATA MouseInput
;
287 Status
= NtReadFile(MouseDeviceHandle
,
293 sizeof(MOUSE_INPUT_DATA
),
296 if(Status
== STATUS_ALERTED
&& !InputThreadsRunning
)
300 if(Status
== STATUS_PENDING
)
302 NtWaitForSingleObject(MouseDeviceHandle
, FALSE
, NULL
);
303 Status
= Iosb
.Status
;
305 if(!NT_SUCCESS(Status
))
307 DPRINT1("Win32K: Failed to read from mouse.\n");
310 DPRINT("MouseEvent\n");
311 IntLastInputTick(TRUE
);
313 UserEnterExclusive();
315 ProcessMouseInputData(&MouseInput
, Iosb
.Information
/ sizeof(MOUSE_INPUT_DATA
));
319 DPRINT("Mouse Input Thread Stopped...\n");
323 /* Returns a value that indicates if the key is a modifier key, and
327 IntKeyboardGetModifiers(KEYBOARD_INPUT_DATA
*InputData
)
329 if (InputData
->Flags
& KEY_E1
)
332 if (!(InputData
->Flags
& KEY_E0
))
334 switch (InputData
->MakeCode
)
336 case 0x2a: /* left shift */
337 case 0x36: /* right shift */
340 case 0x1d: /* left control */
343 case 0x38: /* left alt */
352 switch (InputData
->MakeCode
)
354 case 0x1d: /* right control */
357 case 0x38: /* right alt */
360 case 0x5b: /* left gui (windows) */
361 case 0x5c: /* right gui (windows) */
370 /* Asks the keyboard driver to send a small table that shows which
371 * lights should connect with which scancodes
373 static NTSTATUS APIENTRY
374 IntKeyboardGetIndicatorTrans(HANDLE KeyboardDeviceHandle
,
375 PKEYBOARD_INDICATOR_TRANSLATION
*IndicatorTrans
)
379 IO_STATUS_BLOCK Block
;
380 PKEYBOARD_INDICATOR_TRANSLATION Ret
;
382 Size
= sizeof(KEYBOARD_INDICATOR_TRANSLATION
);
384 Ret
= ExAllocatePoolWithTag(PagedPool
,
390 Status
= NtDeviceIoControlFile(KeyboardDeviceHandle
,
395 IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION
,
399 if (Status
!= STATUS_BUFFER_TOO_SMALL
)
402 ExFreePoolWithTag(Ret
, TAG_KEYBOARD
);
404 Size
+= sizeof(KEYBOARD_INDICATOR_TRANSLATION
);
406 Ret
= ExAllocatePoolWithTag(PagedPool
,
412 return STATUS_INSUFFICIENT_RESOURCES
;
414 if (Status
!= STATUS_SUCCESS
)
416 ExFreePoolWithTag(Ret
, TAG_KEYBOARD
);
420 *IndicatorTrans
= Ret
;
424 /* Sends the keyboard commands to turn on/off the lights.
426 static NTSTATUS APIENTRY
427 IntKeyboardUpdateLeds(HANDLE KeyboardDeviceHandle
,
428 PKEYBOARD_INPUT_DATA KeyInput
,
429 PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans
)
433 static KEYBOARD_INDICATOR_PARAMETERS Indicators
;
434 IO_STATUS_BLOCK Block
;
437 return STATUS_NOT_SUPPORTED
;
439 if (KeyInput
->Flags
& (KEY_E0
| KEY_E1
| KEY_BREAK
))
440 return STATUS_SUCCESS
;
442 for (Count
= 0; Count
< IndicatorTrans
->NumberOfIndicatorKeys
; Count
++)
444 if (KeyInput
->MakeCode
== IndicatorTrans
->IndicatorList
[Count
].MakeCode
)
446 Indicators
.LedFlags
^=
447 IndicatorTrans
->IndicatorList
[Count
].IndicatorFlags
;
449 /* Update the lights on the hardware */
451 Status
= NtDeviceIoControlFile(KeyboardDeviceHandle
,
456 IOCTL_KEYBOARD_SET_INDICATORS
,
457 &Indicators
, sizeof(Indicators
),
464 return STATUS_SUCCESS
;
468 IntKeyboardSendWinKeyMsg()
473 if (!(Window
= UserGetWindowObject(InputWindowStation
->ShellWindow
)))
475 DPRINT1("Couldn't find window to send Windows key message!\n");
479 Mesg
.hwnd
= InputWindowStation
->ShellWindow
;
480 Mesg
.message
= WM_SYSCOMMAND
;
481 Mesg
.wParam
= SC_TASKLIST
;
484 /* The QS_HOTKEY is just a guess */
485 MsqPostMessage(Window
->head
.pti
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
489 co_IntKeyboardSendAltKeyMsg()
491 co_MsqPostKeyboardMessage(WM_SYSCOMMAND
,SC_KEYMENU
,0);
495 KeyboardThreadMain(PVOID StartContext
)
497 UNICODE_STRING KeyboardDeviceName
= RTL_CONSTANT_STRING(L
"\\Device\\KeyboardClass0");
498 OBJECT_ATTRIBUTES KeyboardObjectAttributes
;
499 IO_STATUS_BLOCK Iosb
;
502 PUSER_MESSAGE_QUEUE FocusQueue
;
503 struct _ETHREAD
*FocusThread
;
505 PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans
= NULL
;
506 UINT ModifierState
= 0;
507 USHORT LastMakeCode
= 0;
508 USHORT LastFlags
= 0;
509 UINT RepeatCount
= 0;
511 InitializeObjectAttributes(&KeyboardObjectAttributes
,
518 LARGE_INTEGER DueTime
;
520 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
521 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
522 Status
= KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &DueTime
);
523 Status
= NtOpenFile(&KeyboardDeviceHandle
,
525 &KeyboardObjectAttributes
,
528 FILE_SYNCHRONOUS_IO_ALERT
);
529 } while (!NT_SUCCESS(Status
));
531 /* Not sure if converting this thread to a win32 thread is such
532 a great idea. Since we're posting keyboard messages to the focus
533 window message queue, we'll be (indirectly) doing sendmessage
534 stuff from this thread (for WH_KEYBOARD_LL processing), which
535 means we need our own message queue. If keyboard messages were
536 instead queued to the system message queue, the thread removing
537 the message from the system message queue would be responsible
538 for WH_KEYBOARD_LL processing and we wouldn't need this thread
539 to be a win32 thread. */
540 Status
= Win32kInitWin32Thread(PsGetCurrentThread());
541 if (!NT_SUCCESS(Status
))
543 DPRINT1("Win32K: Failed making keyboard thread a win32 thread.\n");
547 ptiKeyboard
= PsGetCurrentThreadWin32Thread();
548 ptiKeyboard
->TIF_flags
|= TIF_SYSTEMTHREAD
;
549 DPRINT1("\nKeyboard Thread 0x%x \n", ptiKeyboard
);
551 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
552 LOW_REALTIME_PRIORITY
+ 3);
554 IntKeyboardGetIndicatorTrans(KeyboardDeviceHandle
,
560 * Wait to start input.
562 DPRINT( "Keyboard Input Thread Waiting for start event\n" );
563 Status
= KeWaitForSingleObject(&InputThreadsStart
,
569 DPRINT( "Keyboard Input Thread Starting...\n" );
571 * Receive and process keyboard input.
573 while (InputThreadsRunning
)
577 KEYBOARD_INPUT_DATA KeyInput
;
578 KEYBOARD_INPUT_DATA NextKeyInput
;
580 UINT fsModifiers
, fsNextModifiers
;
581 struct _ETHREAD
*Thread
;
585 DPRINT("KeyInput @ %08x\n", &KeyInput
);
587 Status
= NtReadFile (KeyboardDeviceHandle
,
593 sizeof(KEYBOARD_INPUT_DATA
),
597 if(Status
== STATUS_ALERTED
&& !InputThreadsRunning
)
601 if(Status
== STATUS_PENDING
)
603 NtWaitForSingleObject(KeyboardDeviceHandle
, FALSE
, NULL
);
604 Status
= Iosb
.Status
;
606 if(!NT_SUCCESS(Status
))
608 DPRINT1("Win32K: Failed to read from mouse.\n");
612 DPRINT("KeyRaw: %s %04x\n",
613 (KeyInput
.Flags
& KEY_BREAK
) ? "up" : "down",
616 if (Status
== STATUS_ALERTED
&& !InputThreadsRunning
)
619 if (!NT_SUCCESS(Status
))
621 DPRINT1("Win32K: Failed to read from keyboard.\n");
625 /* Set LastInputTick */
626 IntLastInputTick(TRUE
);
628 /* Update modifier state */
629 fsModifiers
= IntKeyboardGetModifiers(&KeyInput
);
633 if (KeyInput
.Flags
& KEY_BREAK
)
635 ModifierState
&= ~fsModifiers
;
636 if(fsModifiers
== MOD_ALT
)
638 if(KeyInput
.Flags
& KEY_E0
)
640 gQueueKeyStateTable
[VK_RMENU
] = 0;
644 gQueueKeyStateTable
[VK_LMENU
] = 0;
646 if (gQueueKeyStateTable
[VK_RMENU
] == 0 &&
647 gQueueKeyStateTable
[VK_LMENU
] == 0)
649 gQueueKeyStateTable
[VK_MENU
] = 0;
655 ModifierState
|= fsModifiers
;
657 if (ModifierState
== fsModifiers
&&
658 (fsModifiers
== MOD_ALT
|| fsModifiers
== MOD_WIN
))
660 /* First send out special notifications
661 * (For alt, the message that turns on accelerator
662 * display, not sure what for win. Both TODO though.)
665 if(fsModifiers
== MOD_ALT
)
667 if(KeyInput
.Flags
& KEY_E0
)
669 gQueueKeyStateTable
[VK_RMENU
] = 0x80;
673 gQueueKeyStateTable
[VK_LMENU
] = 0x80;
677 gQueueKeyStateTable
[VK_MENU
] = 0x80;
680 /* Read the next key before sending this one */
683 Status
= NtReadFile (KeyboardDeviceHandle
,
689 sizeof(KEYBOARD_INPUT_DATA
),
692 DPRINT("KeyRaw: %s %04x\n",
693 (NextKeyInput
.Flags
& KEY_BREAK
) ? "up":"down",
694 NextKeyInput
.MakeCode
);
696 if (Status
== STATUS_ALERTED
&& !InputThreadsRunning
)
700 while ((!(NextKeyInput
.Flags
& KEY_BREAK
)) &&
701 NextKeyInput
.MakeCode
== KeyInput
.MakeCode
);
702 /* ^ Ignore repeats, they'll be KEY_MAKE and the same
703 * code. I'm not caring about the counting, not sure
704 * if that matters. I think not.
707 /* If the ModifierState is now empty again, send a
708 * special notification and eat both keypresses
711 fsNextModifiers
= IntKeyboardGetModifiers(&NextKeyInput
);
714 ModifierState
^= fsNextModifiers
;
716 if (ModifierState
== 0)
718 if (fsModifiers
== MOD_WIN
)
719 IntKeyboardSendWinKeyMsg();
720 else if (fsModifiers
== MOD_ALT
)
722 gQueueKeyStateTable
[VK_MENU
] = 0;
725 gQueueKeyStateTable
[VK_LMENU
] = 0;
729 gQueueKeyStateTable
[VK_RMENU
] = 0;
731 co_IntKeyboardSendAltKeyMsg();
741 for (;NumKeys
;memcpy(&KeyInput
, &NextKeyInput
, sizeof(KeyInput
)),
744 PKBL keyboardLayout
= NULL
;
747 IntKeyboardUpdateLeds(KeyboardDeviceHandle
,
751 /* While we are working, we set up lParam. The format is:
752 * 0-15: The number of times this key has autorepeated
753 * 16-23: The keyboard scancode
754 * 24: Set if it's and extended key (I assume KEY_E0 | KEY_E1)
755 * Note that E1 is only used for PAUSE (E1-1D-45) and
756 * E0-45 happens not to be anything.
757 * 29: Alt is pressed ('Context code')
758 * 30: Previous state, if the key was down before this message
759 * This is a cheap way to ignore autorepeat keys
760 * 31: 1 if the key is being pressed
763 /* If it's a KEY_MAKE (which is 0, so test using !KEY_BREAK)
764 * and it's the same key as the last one, increase the repeat
768 if (!(KeyInput
.Flags
& KEY_BREAK
))
770 if (((KeyInput
.Flags
& (KEY_E0
| KEY_E1
)) == LastFlags
) &&
771 (KeyInput
.MakeCode
== LastMakeCode
))
779 LastFlags
= KeyInput
.Flags
& (KEY_E0
| KEY_E1
);
780 LastMakeCode
= KeyInput
.MakeCode
;
786 LastMakeCode
= 0; /* Should never match */
787 lParam
|= (1 << 30) | (1 << 31);
790 lParam
|= RepeatCount
;
792 lParam
|= (KeyInput
.MakeCode
& 0xff) << 16;
794 if (KeyInput
.Flags
& KEY_E0
)
797 if (ModifierState
& MOD_ALT
)
801 if (!(KeyInput
.Flags
& KEY_BREAK
))
802 msg
.message
= WM_SYSKEYDOWN
;
804 msg
.message
= WM_SYSKEYUP
;
808 if (!(KeyInput
.Flags
& KEY_BREAK
))
809 msg
.message
= WM_KEYDOWN
;
811 msg
.message
= WM_KEYUP
;
814 /* Find the target thread whose locale is in effect */
815 FocusQueue
= IntGetFocusMessageQueue();
819 msg
.hwnd
= FocusQueue
->FocusWindow
;
821 FocusThread
= FocusQueue
->Thread
;
822 if (FocusThread
&& FocusThread
->Tcb
.Win32Thread
)
824 keyboardLayout
= ((PTHREADINFO
)FocusThread
->Tcb
.Win32Thread
)->KeyboardLayout
;
829 keyboardLayout
= W32kGetDefaultKeyLayout();
834 /* This function uses lParam to fill wParam according to the
835 * keyboard layout in use.
837 W32kKeyProcessMessage(&msg
,
838 keyboardLayout
->KBTables
,
839 KeyInput
.Flags
& KEY_E0
? 0xE0 :
840 (KeyInput
.Flags
& KEY_E1
? 0xE1 : 0));
842 if (GetHotKey(ModifierState
,
848 if (!(KeyInput
.Flags
& KEY_BREAK
))
850 DPRINT("Hot key pressed (hWnd %lx, id %d)\n", hWnd
, id
);
851 MsqPostHotKeyMessage (Thread
,
854 MAKELPARAM((WORD
)ModifierState
,
857 continue; /* Eat key up motion too */
862 /* There is no focused window to receive a keyboard message */
866 * Post a keyboard message.
868 co_MsqPostKeyboardMessage(msg
.message
,msg
.wParam
,msg
.lParam
);
873 DPRINT( "KeyboardInput Thread Stopped...\n" );
878 static PVOID Objects
[2];
881 Since this relies on InputThreadsStart, just fake it.
884 RawInputThreadMain(PVOID StartContext
)
887 LARGE_INTEGER DueTime
;
889 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
894 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
895 Status
= KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, &DueTime
);
896 } while (!NT_SUCCESS(Status
));
899 Objects
[0] = &InputThreadsStart
;
900 Objects
[1] = MasterTimer
;
902 // This thread requires win32k!
903 Status
= Win32kInitWin32Thread(PsGetCurrentThread());
904 if (!NT_SUCCESS(Status
))
906 DPRINT1("Win32K: Failed making Raw Input thread a win32 thread.\n");
910 ptiRawInput
= PsGetCurrentThreadWin32Thread();
911 ptiRawInput
->TIF_flags
|= TIF_SYSTEMTHREAD
;
912 DPRINT1("\nRaw Input Thread 0x%x \n", ptiRawInput
);
914 KeSetPriorityThread(&PsGetCurrentThread()->Tcb
,
915 LOW_REALTIME_PRIORITY
+ 3);
917 UserEnterExclusive();
922 // ATM, we just have one job to handle, merge the other two later.
926 DPRINT( "Raw Input Thread Waiting for start event\n" );
928 Status
= KeWaitForMultipleObjects( 2,
936 DPRINT( "Raw Input Thread Starting...\n" );
940 DPRINT1("Raw Input Thread Exit!\n");
950 KeInitializeEvent(&InputThreadsStart
, NotificationEvent
, FALSE
);
952 MasterTimer
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KTIMER
), TAG_INPUT
);
955 DPRINT1("Win32K: Failed making Raw Input thread a win32 thread.\n");
957 return STATUS_UNSUCCESSFUL
;
959 KeInitializeTimer(MasterTimer
);
961 /* Initialize the default keyboard layout */
962 if(!UserInitDefaultKeyboardLayout())
964 DPRINT1("Failed to initialize default keyboard layout!\n");
967 Status
= PsCreateSystemThread(&RawInputThreadHandle
,
974 if (!NT_SUCCESS(Status
))
976 DPRINT1("Win32K: Failed to create raw thread.\n");
979 Status
= PsCreateSystemThread(&KeyboardThreadHandle
,
986 if (!NT_SUCCESS(Status
))
988 DPRINT1("Win32K: Failed to create keyboard thread.\n");
991 Status
= PsCreateSystemThread(&MouseThreadHandle
,
998 if (!NT_SUCCESS(Status
))
1000 DPRINT1("Win32K: Failed to create mouse thread.\n");
1003 InputThreadsRunning
= TRUE
;
1004 KeSetEvent(&InputThreadsStart
, IO_NO_INCREMENT
, FALSE
);
1006 return STATUS_SUCCESS
;
1010 CleanupInputImp(VOID
)
1012 return(STATUS_SUCCESS
);
1019 POINT pt
) // Just like the User call.
1026 IntBlockInput(PTHREADINFO W32Thread
, BOOL BlockIt
)
1028 PTHREADINFO OldBlock
;
1031 if(!W32Thread
->rpdesk
|| ((W32Thread
->TIF_flags
& TIF_INCLEANUP
) && BlockIt
))
1034 * fail blocking if exiting the thread
1041 * FIXME - check access rights of the window station
1042 * e.g. services running in the service window station cannot block input
1044 if(!ThreadHasInputAccess(W32Thread
) ||
1045 !IntIsActiveDesktop(W32Thread
->rpdesk
))
1047 SetLastWin32Error(ERROR_ACCESS_DENIED
);
1051 ASSERT(W32Thread
->rpdesk
);
1052 OldBlock
= W32Thread
->rpdesk
->BlockInputThread
;
1055 if(OldBlock
!= W32Thread
)
1057 SetLastWin32Error(ERROR_ACCESS_DENIED
);
1060 W32Thread
->rpdesk
->BlockInputThread
= (BlockIt
? W32Thread
: NULL
);
1061 return OldBlock
== NULL
;
1064 W32Thread
->rpdesk
->BlockInputThread
= (BlockIt
? W32Thread
: NULL
);
1065 return OldBlock
== NULL
;
1073 DECLARE_RETURN(BOOLEAN
);
1075 DPRINT("Enter NtUserBlockInput\n");
1076 UserEnterExclusive();
1078 RETURN( IntBlockInput(PsGetCurrentThreadWin32Thread(), BlockIt
));
1081 DPRINT("Leave NtUserBlockInput, ret=%i\n",_ret_
);
1087 IntMouseInput(MOUSEINPUT
*mi
)
1089 const UINT SwapBtnMsg
[2][2] =
1091 {WM_LBUTTONDOWN
, WM_RBUTTONDOWN
},
1092 {WM_LBUTTONUP
, WM_RBUTTONUP
}
1094 const WPARAM SwapBtn
[2] =
1096 MK_LBUTTON
, MK_RBUTTON
1099 PSYSTEM_CURSORINFO CurInfo
;
1105 CurInfo
= IntGetSysCursorInfo();
1109 LARGE_INTEGER LargeTickCount
;
1110 KeQueryTickCount(&LargeTickCount
);
1111 mi
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1114 SwapButtons
= gspv
.bMouseBtnSwap
;
1116 MousePos
= gpsi
->ptCursor
;
1118 if(mi
->dwFlags
& MOUSEEVENTF_MOVE
)
1120 if(mi
->dwFlags
& MOUSEEVENTF_ABSOLUTE
)
1122 MousePos
.x
= mi
->dx
* UserGetSystemMetrics(SM_CXVIRTUALSCREEN
) >> 16;
1123 MousePos
.y
= mi
->dy
* UserGetSystemMetrics(SM_CYVIRTUALSCREEN
) >> 16;
1127 MousePos
.x
+= mi
->dx
;
1128 MousePos
.y
+= mi
->dy
;
1133 * Insert the messages into the system queue
1136 Msg
.lParam
= MAKELPARAM(MousePos
.x
, MousePos
.y
);
1139 if (gQueueKeyStateTable
[VK_SHIFT
] & 0xc0)
1141 Msg
.wParam
|= MK_SHIFT
;
1144 if (gQueueKeyStateTable
[VK_CONTROL
] & 0xc0)
1146 Msg
.wParam
|= MK_CONTROL
;
1149 if(mi
->dwFlags
& MOUSEEVENTF_MOVE
)
1151 UserSetCursorPos(MousePos
.x
, MousePos
.y
, TRUE
);
1153 if(mi
->dwFlags
& MOUSEEVENTF_LEFTDOWN
)
1155 gQueueKeyStateTable
[VK_LBUTTON
] |= 0xc0;
1156 Msg
.message
= SwapBtnMsg
[0][SwapButtons
];
1157 CurInfo
->ButtonsDown
|= SwapBtn
[SwapButtons
];
1158 Msg
.wParam
|= CurInfo
->ButtonsDown
;
1159 co_MsqInsertMouseMessage(&Msg
);
1161 else if(mi
->dwFlags
& MOUSEEVENTF_LEFTUP
)
1163 gQueueKeyStateTable
[VK_LBUTTON
] &= ~0x80;
1164 Msg
.message
= SwapBtnMsg
[1][SwapButtons
];
1165 CurInfo
->ButtonsDown
&= ~SwapBtn
[SwapButtons
];
1166 Msg
.wParam
|= CurInfo
->ButtonsDown
;
1167 co_MsqInsertMouseMessage(&Msg
);
1169 if(mi
->dwFlags
& MOUSEEVENTF_MIDDLEDOWN
)
1171 gQueueKeyStateTable
[VK_MBUTTON
] |= 0xc0;
1172 Msg
.message
= WM_MBUTTONDOWN
;
1173 CurInfo
->ButtonsDown
|= MK_MBUTTON
;
1174 Msg
.wParam
|= CurInfo
->ButtonsDown
;
1175 co_MsqInsertMouseMessage(&Msg
);
1177 else if(mi
->dwFlags
& MOUSEEVENTF_MIDDLEUP
)
1179 gQueueKeyStateTable
[VK_MBUTTON
] &= ~0x80;
1180 Msg
.message
= WM_MBUTTONUP
;
1181 CurInfo
->ButtonsDown
&= ~MK_MBUTTON
;
1182 Msg
.wParam
|= CurInfo
->ButtonsDown
;
1183 co_MsqInsertMouseMessage(&Msg
);
1185 if(mi
->dwFlags
& MOUSEEVENTF_RIGHTDOWN
)
1187 gQueueKeyStateTable
[VK_RBUTTON
] |= 0xc0;
1188 Msg
.message
= SwapBtnMsg
[0][!SwapButtons
];
1189 CurInfo
->ButtonsDown
|= SwapBtn
[!SwapButtons
];
1190 Msg
.wParam
|= CurInfo
->ButtonsDown
;
1191 co_MsqInsertMouseMessage(&Msg
);
1193 else if(mi
->dwFlags
& MOUSEEVENTF_RIGHTUP
)
1195 gQueueKeyStateTable
[VK_RBUTTON
] &= ~0x80;
1196 Msg
.message
= SwapBtnMsg
[1][!SwapButtons
];
1197 CurInfo
->ButtonsDown
&= ~SwapBtn
[!SwapButtons
];
1198 Msg
.wParam
|= CurInfo
->ButtonsDown
;
1199 co_MsqInsertMouseMessage(&Msg
);
1202 if((mi
->dwFlags
& (MOUSEEVENTF_XDOWN
| MOUSEEVENTF_XUP
)) &&
1203 (mi
->dwFlags
& MOUSEEVENTF_WHEEL
))
1205 /* fail because both types of events use the mouseData field */
1209 if(mi
->dwFlags
& MOUSEEVENTF_XDOWN
)
1211 Msg
.message
= WM_XBUTTONDOWN
;
1212 if(mi
->mouseData
& XBUTTON1
)
1214 gQueueKeyStateTable
[VK_XBUTTON1
] |= 0xc0;
1215 CurInfo
->ButtonsDown
|= MK_XBUTTON1
;
1216 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON1
);
1217 co_MsqInsertMouseMessage(&Msg
);
1219 if(mi
->mouseData
& XBUTTON2
)
1221 gQueueKeyStateTable
[VK_XBUTTON2
] |= 0xc0;
1222 CurInfo
->ButtonsDown
|= MK_XBUTTON2
;
1223 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON2
);
1224 co_MsqInsertMouseMessage(&Msg
);
1227 else if(mi
->dwFlags
& MOUSEEVENTF_XUP
)
1229 Msg
.message
= WM_XBUTTONUP
;
1230 if(mi
->mouseData
& XBUTTON1
)
1232 gQueueKeyStateTable
[VK_XBUTTON1
] &= ~0x80;
1233 CurInfo
->ButtonsDown
&= ~MK_XBUTTON1
;
1234 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON1
);
1235 co_MsqInsertMouseMessage(&Msg
);
1237 if(mi
->mouseData
& XBUTTON2
)
1239 gQueueKeyStateTable
[VK_XBUTTON2
] &= ~0x80;
1240 CurInfo
->ButtonsDown
&= ~MK_XBUTTON2
;
1241 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, XBUTTON2
);
1242 co_MsqInsertMouseMessage(&Msg
);
1245 if(mi
->dwFlags
& MOUSEEVENTF_WHEEL
)
1247 Msg
.message
= WM_MOUSEWHEEL
;
1248 Msg
.wParam
= MAKEWPARAM(CurInfo
->ButtonsDown
, mi
->mouseData
);
1249 co_MsqInsertMouseMessage(&Msg
);
1256 IntKeyboardInput(KEYBDINPUT
*ki
)
1258 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
1260 LARGE_INTEGER LargeTickCount
;
1261 KBDLLHOOKSTRUCT KbdHookData
;
1262 WORD flags
, wVkStripped
, wVkL
, wVkR
, wVk
= ki
->wVk
, vk_hook
= ki
->wVk
;
1263 BOOLEAN Entered
= FALSE
;
1267 // Condition may arise when calling MsqPostMessage and waiting for an event.
1268 if (!UserIsEntered())
1270 // Fixme: Not sure ATM if this thread is locked.
1271 UserEnterExclusive();
1277 flags
= LOBYTE(ki
->wScan
);
1279 if (ki
->dwFlags
& KEYEVENTF_EXTENDEDKEY
) flags
|= KF_EXTENDED
;
1280 /* FIXME: set KF_DLGMODE and KF_MENUMODE when needed */
1282 /* strip left/right for menu, control, shift */
1288 wVk
= (ki
->dwFlags
& KEYEVENTF_EXTENDEDKEY
) ? VK_RMENU
: VK_LMENU
;
1289 wVkStripped
= VK_MENU
;
1296 wVk
= (ki
->dwFlags
& KEYEVENTF_EXTENDEDKEY
) ? VK_RCONTROL
: VK_LCONTROL
;
1297 wVkStripped
= VK_CONTROL
;
1304 wVk
= (ki
->dwFlags
& KEYEVENTF_EXTENDEDKEY
) ? VK_RSHIFT
: VK_LSHIFT
;
1305 wVkStripped
= VK_SHIFT
;
1310 wVkStripped
= wVkL
= wVkR
= wVk
;
1313 if (ki
->dwFlags
& KEYEVENTF_KEYUP
)
1315 Msg
.message
= WM_KEYUP
;
1316 if ((gQueueKeyStateTable
[VK_MENU
] & 0x80) &&
1317 ((wVkStripped
== VK_MENU
) || (wVkStripped
== VK_CONTROL
)
1318 || !(gQueueKeyStateTable
[VK_CONTROL
] & 0x80)))
1320 if( TrackSysKey
== VK_MENU
|| /* <ALT>-down/<ALT>-up sequence */
1321 (wVkStripped
!= VK_MENU
)) /* <ALT>-down...<something else>-up */
1322 Msg
.message
= WM_SYSKEYUP
;
1325 flags
|= KF_REPEAT
| KF_UP
;
1329 Msg
.message
= WM_KEYDOWN
;
1330 if ((gQueueKeyStateTable
[VK_MENU
] & 0x80 || wVkStripped
== VK_MENU
) &&
1331 !(gQueueKeyStateTable
[VK_CONTROL
] & 0x80 || wVkStripped
== VK_CONTROL
))
1333 Msg
.message
= WM_SYSKEYDOWN
;
1334 TrackSysKey
= wVkStripped
;
1336 if (!(ki
->dwFlags
& KEYEVENTF_UNICODE
) && gQueueKeyStateTable
[wVk
] & 0x80) flags
|= KF_REPEAT
;
1339 if (ki
->dwFlags
& KEYEVENTF_UNICODE
)
1341 vk_hook
= Msg
.wParam
= wVk
= VK_PACKET
;
1342 Msg
.lParam
= MAKELPARAM(1 /* repeat count */, ki
->wScan
);
1345 FocusMessageQueue
= IntGetFocusMessageQueue();
1349 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
1350 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
1354 KeQueryTickCount(&LargeTickCount
);
1355 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
1358 Msg
.time
= ki
->time
;
1360 /* All messages have to contain the cursor point. */
1361 Msg
.pt
= gpsi
->ptCursor
;
1363 KbdHookData
.vkCode
= vk_hook
;
1364 KbdHookData
.scanCode
= ki
->wScan
;
1365 KbdHookData
.flags
= flags
>> 8;
1366 KbdHookData
.time
= Msg
.time
;
1367 KbdHookData
.dwExtraInfo
= ki
->dwExtraInfo
;
1368 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
1370 DPRINT1("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
1371 Msg
.message
, vk_hook
, Msg
.lParam
);
1372 if (Entered
) UserLeave();
1376 if (!(ki
->dwFlags
& KEYEVENTF_UNICODE
))
1378 if (ki
->dwFlags
& KEYEVENTF_KEYUP
)
1380 gQueueKeyStateTable
[wVk
] &= ~0x80;
1381 gQueueKeyStateTable
[wVkStripped
] = gQueueKeyStateTable
[wVkL
] | gQueueKeyStateTable
[wVkR
];
1385 if (!(gQueueKeyStateTable
[wVk
] & 0x80)) gQueueKeyStateTable
[wVk
] ^= 0x01;
1386 gQueueKeyStateTable
[wVk
] |= 0xc0;
1387 gQueueKeyStateTable
[wVkStripped
] = gQueueKeyStateTable
[wVkL
] | gQueueKeyStateTable
[wVkR
];
1390 if (gQueueKeyStateTable
[VK_MENU
] & 0x80) flags
|= KF_ALTDOWN
;
1392 if (wVkStripped
== VK_SHIFT
) flags
&= ~KF_EXTENDED
;
1394 Msg
.lParam
= MAKELPARAM(1 /* repeat count */, flags
);
1397 if (FocusMessageQueue
== NULL
)
1399 DPRINT("No focus message queue\n");
1400 if (Entered
) UserLeave();
1404 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
1406 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
1407 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
1409 FocusMessageQueue
->Desktop
->pDeskInfo
->LastInputWasKbd
= TRUE
;
1411 Msg
.pt
= gpsi
->ptCursor
;
1413 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
1417 DPRINT("Invalid focus window handle\n");
1420 if (Entered
) UserLeave();
1426 UserAttachThreadInput( PTHREADINFO pti
, PTHREADINFO ptiTo
, BOOL fAttach
)
1430 /* Can not be the same thread.*/
1431 if (pti
== ptiTo
) return FALSE
;
1433 /* Do not attach to system threads or between different desktops. */
1434 if ( pti
->TIF_flags
& TIF_DONTATTACHQUEUE
||
1435 ptiTo
->TIF_flags
& TIF_DONTATTACHQUEUE
||
1436 pti
->rpdesk
!= ptiTo
->rpdesk
)
1439 /* If Attach set, allocate and link. */
1442 pai
= ExAllocatePoolWithTag(PagedPool
, sizeof(ATTACHINFO
), TAG_ATTACHINFO
);
1443 if ( !pai
) return FALSE
;
1445 pai
->paiNext
= gpai
;
1450 else /* If clear, unlink and free it. */
1452 PATTACHINFO paiprev
= NULL
;
1454 if ( !gpai
) return FALSE
;
1458 /* Search list and free if found or return false. */
1461 if ( pai
->pti2
== ptiTo
&& pai
->pti1
== pti
) break;
1466 if ( !pai
) return FALSE
;
1468 if (paiprev
) paiprev
->paiNext
= pai
->paiNext
;
1470 ExFreePoolWithTag(pai
, TAG_ATTACHINFO
);
1483 PTHREADINFO W32Thread
;
1485 DECLARE_RETURN(UINT
);
1487 DPRINT("Enter NtUserSendInput\n");
1488 UserEnterExclusive();
1490 W32Thread
= PsGetCurrentThreadWin32Thread();
1493 if(!W32Thread
->rpdesk
)
1498 if(!nInputs
|| !pInput
|| (cbSize
!= sizeof(INPUT
)))
1500 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
1505 * FIXME - check access rights of the window station
1506 * e.g. services running in the service window station cannot block input
1508 if(!ThreadHasInputAccess(W32Thread
) ||
1509 !IntIsActiveDesktop(W32Thread
->rpdesk
))
1511 SetLastWin32Error(ERROR_ACCESS_DENIED
);
1521 Status
= MmCopyFromCaller(&SafeInput
, pInput
++, sizeof(INPUT
));
1522 if(!NT_SUCCESS(Status
))
1524 SetLastNtError(Status
);
1528 switch(SafeInput
.type
)
1531 if(IntMouseInput(&SafeInput
.mi
))
1536 case INPUT_KEYBOARD
:
1537 if(IntKeyboardInput(&SafeInput
.ki
))
1542 case INPUT_HARDWARE
:
1547 DPRINT1("SendInput(): Invalid input type: 0x%x\n", SafeInput
.type
);
1557 DPRINT("Leave NtUserSendInput, ret=%i\n",_ret_
);