2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Keyboard functions
5 * FILE: subsys/win32k/ntuser/keyboard.c
6 * PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Rafal Harabien (rafalh@reactos.org)
11 DBG_DEFAULT_CHANNEL(UserKbd
);
13 BYTE gafAsyncKeyState
[0x100];
14 static PKEYBOARD_INDICATOR_TRANSLATION gpKeyboardIndicatorTrans
= NULL
;
16 /* FUNCTIONS *****************************************************************/
21 * Initialization -- Right now, just zero the key state
26 InitKeyboardImpl(VOID
)
28 RtlZeroMemory(&gafAsyncKeyState
, sizeof(gafAsyncKeyState
));
29 return STATUS_SUCCESS
;
33 * IntKeyboardGetIndicatorTrans
35 * Asks the keyboard driver to send a small table that shows which
36 * lights should connect with which scancodes
40 IntKeyboardGetIndicatorTrans(HANDLE hKeyboardDevice
,
41 PKEYBOARD_INDICATOR_TRANSLATION
*ppIndicatorTrans
)
45 IO_STATUS_BLOCK Block
;
46 PKEYBOARD_INDICATOR_TRANSLATION pRet
;
48 dwSize
= sizeof(KEYBOARD_INDICATOR_TRANSLATION
);
50 pRet
= ExAllocatePoolWithTag(PagedPool
,
56 Status
= NtDeviceIoControlFile(hKeyboardDevice
,
61 IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION
,
65 if (Status
!= STATUS_BUFFER_TOO_SMALL
)
68 ExFreePoolWithTag(pRet
, USERTAG_KBDTABLE
);
70 dwSize
+= sizeof(KEYBOARD_INDICATOR_TRANSLATION
);
72 pRet
= ExAllocatePoolWithTag(PagedPool
,
78 return STATUS_INSUFFICIENT_RESOURCES
;
80 if (!NT_SUCCESS(Status
))
82 ExFreePoolWithTag(pRet
, USERTAG_KBDTABLE
);
86 *ppIndicatorTrans
= pRet
;
91 * IntKeyboardUpdateLeds
93 * Sends the keyboard commands to turn on/off the lights
97 IntKeyboardUpdateLeds(HANDLE hKeyboardDevice
,
100 PKEYBOARD_INDICATOR_TRANSLATION pIndicatorTrans
)
104 static KEYBOARD_INDICATOR_PARAMETERS Indicators
;
105 IO_STATUS_BLOCK Block
;
107 if (!pIndicatorTrans
)
108 return STATUS_NOT_SUPPORTED
;
110 for (i
= 0; i
< pIndicatorTrans
->NumberOfIndicatorKeys
; i
++)
112 if (pIndicatorTrans
->IndicatorList
[i
].MakeCode
== wScanCode
)
115 Indicators
.LedFlags
|= pIndicatorTrans
->IndicatorList
[i
].IndicatorFlags
;
117 Indicators
.LedFlags
= ~pIndicatorTrans
->IndicatorList
[i
].IndicatorFlags
;
119 /* Update the lights on the hardware */
120 Status
= NtDeviceIoControlFile(hKeyboardDevice
,
125 IOCTL_KEYBOARD_SET_INDICATORS
,
126 &Indicators
, sizeof(Indicators
),
133 return STATUS_SUCCESS
;
139 * Changes virtual keys which distinguish between left and right hand, to keys which don't distinguish
143 IntSimplifyVk(WORD wVk
)
167 * Changes virtual keys which don't not distinguish between left and right hand to proper keys
171 IntFixVk(WORD wVk
, BOOL bExt
)
176 return bExt
? VK_RSHIFT
: VK_LSHIFT
;
179 return bExt
? VK_RCONTROL
: VK_LCONTROL
;
182 return bExt
? VK_RMENU
: VK_LMENU
;
192 * Gets other side of vk: right -> left, left -> right
196 IntGetVkOtherSide(WORD wVk
)
200 case VK_LSHIFT
: return VK_RSHIFT
;
201 case VK_RSHIFT
: return VK_LSHIFT
;
203 case VK_LCONTROL
: return VK_RCONTROL
;
204 case VK_RCONTROL
: return VK_LCONTROL
;
206 case VK_LMENU
: return VK_RMENU
;
207 case VK_RMENU
: return VK_LMENU
;
214 * IntTranslateNumpadKey
216 * Translates numpad keys when numlock is enabled
220 IntTranslateNumpadKey(WORD wVk
)
224 case VK_INSERT
: return VK_NUMPAD0
;
225 case VK_END
: return VK_NUMPAD1
;
226 case VK_DOWN
: return VK_NUMPAD2
;
227 case VK_NEXT
: return VK_NUMPAD3
;
228 case VK_LEFT
: return VK_NUMPAD4
;
229 case VK_CLEAR
: return VK_NUMPAD5
;
230 case VK_RIGHT
: return VK_NUMPAD6
;
231 case VK_HOME
: return VK_NUMPAD7
;
232 case VK_UP
: return VK_NUMPAD8
;
233 case VK_PRIOR
: return VK_NUMPAD9
;
234 case VK_DELETE
: return VK_DECIMAL
;
242 * Returns a value that indicates if the key is a modifier key, and
247 IntGetModifiers(PBYTE pKeyState
)
251 if (pKeyState
[VK_SHIFT
] & KS_DOWN_BIT
)
252 fModifiers
|= MOD_SHIFT
;
254 if (pKeyState
[VK_CONTROL
] & KS_DOWN_BIT
)
255 fModifiers
|= MOD_CONTROL
;
257 if (pKeyState
[VK_MENU
] & KS_DOWN_BIT
)
258 fModifiers
|= MOD_ALT
;
260 if ((pKeyState
[VK_LWIN
] | pKeyState
[VK_RWIN
]) & KS_DOWN_BIT
)
261 fModifiers
|= MOD_WIN
;
269 * Search the keyboard layout modifiers table for the shift bit
273 IntGetShiftBit(PKBDTABLES pKbdTbl
, WORD wVk
)
277 for (i
= 0; pKbdTbl
->pCharModifiers
->pVkToBit
[i
].Vk
; i
++)
278 if (pKbdTbl
->pCharModifiers
->pVkToBit
[i
].Vk
== wVk
)
279 return pKbdTbl
->pCharModifiers
->pVkToBit
[i
].ModBits
;
287 * Gets layout specific modification bits, for example KBDSHIFT, KBDCTRL, KBDALT
291 IntGetModBits(PKBDTABLES pKbdTbl
, PBYTE pKeyState
)
293 DWORD i
, dwModBits
= 0;
295 /* DumpKeyState( KeyState ); */
297 for (i
= 0; pKbdTbl
->pCharModifiers
->pVkToBit
[i
].Vk
; i
++)
298 if (pKeyState
[pKbdTbl
->pCharModifiers
->pVkToBit
[i
].Vk
] & KS_DOWN_BIT
)
299 dwModBits
|= pKbdTbl
->pCharModifiers
->pVkToBit
[i
].ModBits
;
302 if ((pKbdTbl
->fLocaleFlags
& KLLF_ALTGR
) && (pKeyState
[VK_RMENU
] & KS_DOWN_BIT
))
303 dwModBits
|= IntGetShiftBit(pKbdTbl
, VK_CONTROL
); /* Don't use KBDCTRL here */
305 TRACE("Current Mod Bits: %lx\n", dwModBits
);
313 * Translates virtual key to character
317 IntTranslateChar(WORD wVirtKey
,
321 PWCHAR pwcTranslatedChar
,
324 PVK_TO_WCHAR_TABLE pVkToVchTbl
;
325 PVK_TO_WCHARS10 pVkToVch
;
326 DWORD i
, dwModBits
, dwVkModBits
, dwModNumber
= 0;
331 dwModBits
= pKeyState
? IntGetModBits(pKbdTbl
, pKeyState
) : 0;
332 bAltGr
= pKeyState
&& (pKbdTbl
->fLocaleFlags
& KLLF_ALTGR
) && (pKeyState
[VK_RMENU
] & KS_DOWN_BIT
);
333 wCaplokAttr
= bAltGr
? CAPLOKALTGR
: CAPLOK
;
335 TRACE("TryToTranslate: %04x %x\n", wVirtKey
, dwModBits
);
337 if (dwModBits
> pKbdTbl
->pCharModifiers
->wMaxModBits
)
339 TRACE("dwModBits %x > wMaxModBits %x\n", dwModBits
, pKbdTbl
->pCharModifiers
->wMaxModBits
);
343 for (i
= 0; pKbdTbl
->pVkToWcharTable
[i
].pVkToWchars
; i
++)
345 pVkToVchTbl
= &pKbdTbl
->pVkToWcharTable
[i
];
346 pVkToVch
= (PVK_TO_WCHARS10
)(pVkToVchTbl
->pVkToWchars
);
347 while (pVkToVch
->VirtualKey
)
349 if (wVirtKey
== (pVkToVch
->VirtualKey
& 0xFF))
351 dwVkModBits
= dwModBits
;
353 /* If CapsLock is enabled for this key and locked, add SHIFT bit */
354 if ((pVkToVch
->Attributes
& wCaplokAttr
) &&
356 (pKeyState
[VK_CAPITAL
] & KS_LOCK_BIT
))
358 /* Note: we use special value here instead of getting VK_SHIFT mod bit - it's verified */
359 dwVkModBits
^= KBDSHIFT
;
362 /* If ALT without CTRL has ben used, remove ALT flag */
363 if ((dwVkModBits
& (KBDALT
|KBDCTRL
)) == KBDALT
)
364 dwVkModBits
&= ~KBDALT
;
366 if (dwVkModBits
> pKbdTbl
->pCharModifiers
->wMaxModBits
)
369 /* Get modification number */
370 dwModNumber
= pKbdTbl
->pCharModifiers
->ModNumber
[dwVkModBits
];
371 if (dwModNumber
>= pVkToVchTbl
->nModifications
)
373 TRACE("dwModNumber %u >= nModifications %u\n", dwModNumber
, pVkToVchTbl
->nModifications
);
378 wch
= pVkToVch
->wch
[dwModNumber
];
382 *pbDead
= (wch
== WCH_DEAD
);
383 *pbLigature
= (wch
== WCH_LGTR
);
384 *pwcTranslatedChar
= wch
;
386 TRACE("%d %04x: dwModNumber %08x Char %04x\n",
387 i
, wVirtKey
, dwModNumber
, wch
);
391 /* After WCH_DEAD, real character is located */
392 pVkToVch
= (PVK_TO_WCHARS10
)(((BYTE
*)pVkToVch
) + pVkToVchTbl
->cbSize
);
393 if (pVkToVch
->VirtualKey
!= 0xFF)
395 WARN("Found dead key with no trailer in the table.\n");
396 WARN("VK: %04x, ADDR: %p\n", wVirtKey
, pVkToVch
);
399 *pwcTranslatedChar
= pVkToVch
->wch
[dwModNumber
];
403 pVkToVch
= (PVK_TO_WCHARS10
)(((BYTE
*)pVkToVch
) + pVkToVchTbl
->cbSize
);
407 /* If nothing has been found in layout, check if this is ASCII control character.
408 Note: we could add it to layout table, but windows does not have it there */
409 if (wVirtKey
>= 'A' && wVirtKey
<= 'Z' &&
410 (pKeyState
[VK_CONTROL
] & KS_DOWN_BIT
) &&
411 !(pKeyState
[VK_MENU
] & KS_DOWN_BIT
))
413 *pwcTranslatedChar
= (wVirtKey
- 'A') + 1; /* ASCII control character */
425 * Translates virtual key to characters
429 IntToUnicodeEx(UINT wVirtKey
,
437 WCHAR wchTranslatedChar
;
438 BOOL bDead
, bLigature
;
439 static WCHAR wchDead
= 0;
444 if (!IntTranslateChar(wVirtKey
,
456 WARN("Not handling ligature (yet)\n" );
460 /* If we got dead char in previous call check dead keys in keyboard layout */
464 WCHAR wchFirst
, wchSecond
;
465 TRACE("Previous dead char: %lc (%x)\n", wchDead
, wchDead
);
467 for (i
= 0; pKbdTbl
->pDeadKey
[i
].dwBoth
; i
++)
469 wchFirst
= pKbdTbl
->pDeadKey
[i
].dwBoth
>> 16;
470 wchSecond
= pKbdTbl
->pDeadKey
[i
].dwBoth
& 0xFFFF;
471 if (wchFirst
== wchDead
&& wchSecond
== wchTranslatedChar
)
473 wchTranslatedChar
= pKbdTbl
->pDeadKey
[i
].wchComposed
;
480 TRACE("Final char: %lc (%x)\n", wchTranslatedChar
, wchTranslatedChar
);
483 /* dead char has not been not found */
486 /* Treat both characters normally */
488 pwszBuff
[iRet
++] = wchDead
;
492 /* add character to the buffer */
494 pwszBuff
[iRet
++] = wchTranslatedChar
;
496 /* Save dead character */
497 wchDead
= bDead
? wchTranslatedChar
: 0;
499 return bDead
? -iRet
: iRet
;
505 * Translates virtual key to scan code
509 IntVkToVsc(WORD wVk
, PKBDTABLES pKbdTbl
)
515 /* Check standard keys first */
516 for (i
= 0; i
< pKbdTbl
->bMaxVSCtoVK
; i
++)
518 if ((pKbdTbl
->pusVSCtoVK
[i
] & 0xFF) == wVk
)
522 /* Check extended keys now */
523 for (i
= 0; pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
; i
++)
525 if ((pKbdTbl
->pVSCtoVK_E0
[i
].Vk
& 0xFF) == wVk
)
526 return 0xE000 | pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
;
529 for (i
= 0; pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
; i
++)
531 if ((pKbdTbl
->pVSCtoVK_E1
[i
].Vk
& 0xFF) == wVk
)
532 return 0xE100 | pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
;
535 /* Virtual key has not been found */
542 * Translates prefixed scancode to virtual key
546 IntVscToVk(WORD wScanCode
, PKBDTABLES pKbdTbl
)
553 if ((wScanCode
& 0xFF00) == 0xE000)
555 for (i
= 0; pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
; i
++)
557 if (pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
== (wScanCode
& 0xFF))
559 wVk
= pKbdTbl
->pVSCtoVK_E0
[i
].Vk
;
563 else if ((wScanCode
& 0xFF00) == 0xE100)
565 for (i
= 0; pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
; i
++)
567 if (pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
== (wScanCode
& 0xFF))
569 wVk
= pKbdTbl
->pVSCtoVK_E1
[i
].Vk
;
573 else if (wScanCode
< pKbdTbl
->bMaxVSCtoVK
)
575 wVk
= pKbdTbl
->pusVSCtoVK
[wScanCode
];
578 /* 0xFF nad 0x00 are invalid VKs */
579 return wVk
!= 0xFF ? wVk
: 0;
585 * Translates virtual key to character, ignoring shift state
589 IntVkToChar(WORD wVk
, PKBDTABLES pKbdTbl
)
592 BOOL bDead
, bLigature
;
596 if (IntTranslateChar(wVk
,
612 DumpKeyState(PBYTE pKeyState
)
616 DbgPrint("KeyState { ");
617 for (i
= 0; i
< 0x100; i
++)
620 DbgPrint("%02x(%02x) ", i
, pKeyState
[i
]);
627 * IntGetAsyncKeyState
629 * Gets key state from global table
633 IntGetAsyncKeyState(DWORD dwKey
)
639 if (gafAsyncKeyState
[dwKey
] & KS_DOWN_BIT
)
640 dwRet
|= 0xFFFF8000; // If down, windows returns 0xFFFF8000.
641 if (gafAsyncKeyState
[dwKey
] & KS_LOCK_BIT
)
646 EngSetLastError(ERROR_INVALID_PARAMETER
);
653 NtUserGetAsyncKeyState(INT Key
)
655 DECLARE_RETURN(SHORT
);
657 TRACE("Enter NtUserGetAsyncKeyState\n");
658 UserEnterExclusive();
660 RETURN(IntGetAsyncKeyState(Key
));
663 TRACE("Leave NtUserGetAsyncKeyState, ret=%i\n", _ret_
);
669 * IntKeyboardSendWinKeyMsg
671 * Sends syscommand to shell, when WIN key is pressed
675 IntKeyboardSendWinKeyMsg()
680 if (!(pWnd
= UserGetWindowObject(InputWindowStation
->ShellWindow
)))
682 ERR("Couldn't find window to send Windows key message!\n");
686 Msg
.hwnd
= InputWindowStation
->ShellWindow
;
687 Msg
.message
= WM_SYSCOMMAND
;
688 Msg
.wParam
= SC_TASKLIST
;
691 /* The QS_HOTKEY is just a guess */
692 MsqPostMessage(pWnd
->head
.pti
->MessageQueue
, &Msg
, FALSE
, QS_HOTKEY
);
696 * co_IntKeyboardSendAltKeyMsg
698 * Sends syscommand enabling window menu
702 co_IntKeyboardSendAltKeyMsg()
704 FIXME("co_IntKeyboardSendAltKeyMsg\n");
705 //co_MsqPostKeyboardMessage(WM_SYSCOMMAND,SC_KEYMENU,0); // This sends everything into a msg loop!
709 * UserSendKeyboardInput
711 * Process keyboard input from input devices and SendInput API
714 UserSendKeyboardInput(KEYBDINPUT
*pKbdInput
, BOOL bInjected
)
716 WORD wScanCode
, wVk
, wSimpleVk
, wVkOtherSide
, wSysKey
;
719 PUSER_MESSAGE_QUEUE pFocusQueue
;
720 struct _ETHREAD
*pFocusThread
;
722 BYTE PrevKeyState
= 0;
725 struct _ETHREAD
*Thread
;
726 LARGE_INTEGER LargeTickCount
;
727 BOOL bExt
= pKbdInput
->dwFlags
& KEYEVENTF_EXTENDEDKEY
;
728 BOOL bKeyUp
= pKbdInput
->dwFlags
& KEYEVENTF_KEYUP
;
730 /* Find the target thread whose locale is in effect */
731 pFocusQueue
= IntGetFocusMessageQueue();
735 pFocusThread
= pFocusQueue
->Thread
;
736 if (pFocusThread
&& pFocusThread
->Tcb
.Win32Thread
)
737 pKbl
= ((PTHREADINFO
)pFocusThread
->Tcb
.Win32Thread
)->KeyboardLayout
;
741 pKbl
= W32kGetDefaultKeyLayout();
744 ERR("No keyboard layout!\n");
748 pKbdTbl
= pKbl
->KBTables
;
750 /* Note: wScan field is always used */
751 wScanCode
= pKbdInput
->wScan
& 0x7F;
753 if (pKbdInput
->dwFlags
& KEYEVENTF_UNICODE
)
755 /* Generate WM_KEYDOWN msg with wParam == VK_PACKET and
756 high order word of lParam == pKbdInput->wScan */
764 if (pKbdInput
->dwFlags
& KEYEVENTF_SCANCODE
)
766 /* Don't ignore invalid scan codes */
767 wVk
= IntVscToVk(wScanCode
, pKbdTbl
);
768 if (!wVk
) /* use 0xFF if vsc is invalid */
773 wVk
= pKbdInput
->wVk
& 0xFF;
775 /* LSHIFT + EXT = RSHIFT */
776 wVk
= IntSimplifyVk(wVk
);
777 wVk
= IntFixVk(wVk
, bExt
);
781 if (!(pKbdInput
->dwFlags
& KEYEVENTF_UNICODE
))
783 /* Get virtual key without shifts (VK_(L|R)* -> VK_*) */
784 wSimpleVk
= IntSimplifyVk(wVk
);
785 wVkOtherSide
= IntGetVkOtherSide(wVk
);
786 PrevKeyState
= gafAsyncKeyState
[wSimpleVk
];
788 /* Update global keyboard state. Begin from lock bit */
789 if (!bKeyUp
&& !(PrevKeyState
& KS_DOWN_BIT
))
790 gafAsyncKeyState
[wVk
] ^= KS_LOCK_BIT
;
792 /* Update down bit */
794 gafAsyncKeyState
[wVk
] &= ~KS_DOWN_BIT
;
796 gafAsyncKeyState
[wVk
] |= KS_DOWN_BIT
;
798 /* Update key without shifts */
799 gafAsyncKeyState
[wSimpleVk
] = gafAsyncKeyState
[wVk
] | gafAsyncKeyState
[wVkOtherSide
];
803 /* Update keyboard LEDs */
804 if (!gpKeyboardIndicatorTrans
)
805 IntKeyboardGetIndicatorTrans(ghKeyboardDevice
, &gpKeyboardIndicatorTrans
);
806 if (gpKeyboardIndicatorTrans
)
807 IntKeyboardUpdateLeds(ghKeyboardDevice
,
809 gafAsyncKeyState
[wSimpleVk
] & KS_LOCK_BIT
,
810 gpKeyboardIndicatorTrans
);
813 /* Truncate scan code */
816 /* Support VK_*WIN and VK_*MENU keys */
819 if (wVk
== VK_LWIN
|| wVk
== VK_RWIN
)
820 IntKeyboardSendWinKeyMsg();
821 else if(wSimpleVk
== VK_MENU
&& !(gafAsyncKeyState
[VK_CONTROL
] & KS_DOWN_BIT
))
822 co_IntKeyboardSendAltKeyMsg();
825 /* Check if it is a hotkey */
826 fModifiers
= IntGetModifiers(gafAsyncKeyState
);
827 if (GetHotKey(fModifiers
, wSimpleVk
, &Thread
, &hWnd
, &HotkeyId
))
831 TRACE("Hot key pressed (hWnd %lx, id %d)\n", hWnd
, HotkeyId
);
832 MsqPostHotKeyMessage(Thread
,
835 MAKELPARAM((WORD
)fModifiers
, wSimpleVk
));
838 return TRUE
; /* Don't send any message */
842 /* If we have a focus queue, post a keyboard message */
847 /* If it is F10 or ALT is down and CTRL is up, it's a system key */
848 wSysKey
= (pKbdTbl
->fLocaleFlags
& KLLF_ALTGR
) ? VK_LMENU
: VK_MENU
;
850 //uVkNoShift == VK_MENU || // FIXME: If only LALT is pressed WM_SYSKEYUP is generated instead of WM_KEYUP
851 ((gafAsyncKeyState
[wSysKey
] & KS_DOWN_BIT
) && // FIXME
852 !(gafAsyncKeyState
[VK_CONTROL
] & KS_DOWN_BIT
)))
855 Msg
.message
= WM_SYSKEYUP
;
857 Msg
.message
= WM_SYSKEYDOWN
;
862 Msg
.message
= WM_KEYUP
;
864 Msg
.message
= WM_KEYDOWN
;
866 Msg
.hwnd
= pFocusQueue
->FocusWindow
;
867 Msg
.lParam
= MAKELPARAM(1, wScanCode
);
868 /* If it is VK_PACKET, high word of wParam is used for wchar */
869 if (!(pKbdInput
->dwFlags
& KEYEVENTF_UNICODE
))
872 Msg
.lParam
|= LP_EXT_BIT
;
873 if (gafAsyncKeyState
[VK_MENU
] & KS_DOWN_BIT
)
874 Msg
.lParam
|= LP_CONTEXT_BIT
;
875 if (PrevKeyState
& KS_DOWN_BIT
)
876 Msg
.lParam
|= LP_PREV_STATE_BIT
;
878 Msg
.lParam
|= LP_TRANSITION_BIT
;
881 Msg
.wParam
= wVk
; // its "simplified" later
882 Msg
.pt
= gpsi
->ptCursor
;
884 Msg
.time
= pKbdInput
->time
;
887 KeQueryTickCount(&LargeTickCount
);
888 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
891 /* Post a keyboard message */
892 TRACE("Posting keyboard msg %u wParam 0x%x lParam 0x%x\n", Msg
.message
, Msg
.wParam
, Msg
.lParam
);
893 co_MsqPostKeyboardMessage(Msg
.message
, Msg
.wParam
, Msg
.lParam
, bInjected
);
900 * UserProcessKeyboardInput
902 * Process raw keyboard input data
905 UserProcessKeyboardInput(
906 PKEYBOARD_INPUT_DATA pKbdInputData
)
911 PUSER_MESSAGE_QUEUE pFocusQueue
;
912 struct _ETHREAD
*pFocusThread
;
914 /* Calculate scan code with prefix */
915 wScanCode
= pKbdInputData
->MakeCode
& 0x7F;
916 if (pKbdInputData
->Flags
& KEY_E0
)
918 if (pKbdInputData
->Flags
& KEY_E1
)
921 /* Find the target thread whose locale is in effect */
922 pFocusQueue
= IntGetFocusMessageQueue();
926 pFocusThread
= pFocusQueue
->Thread
;
927 if (pFocusThread
&& pFocusThread
->Tcb
.Win32Thread
)
928 pKbl
= ((PTHREADINFO
)pFocusThread
->Tcb
.Win32Thread
)->KeyboardLayout
;
932 pKbl
= W32kGetDefaultKeyLayout();
936 pKbdTbl
= pKbl
->KBTables
;
938 /* Convert scan code to virtual key.
939 Note: we could call UserSendKeyboardInput using scan code,
940 but it wouldn't interpret E1 key(s) properly */
941 wVk
= IntVscToVk(wScanCode
, pKbdTbl
);
942 TRACE("UserProcessKeyboardInput: %x (break: %u) -> %x\n",
943 wScanCode
, (pKbdInputData
->Flags
& KEY_BREAK
) ? 1 : 0, wVk
);
949 /* Support numlock */
950 if ((wVk
& KBDNUMPAD
) && (gafAsyncKeyState
[VK_NUMLOCK
] & KS_LOCK_BIT
))
952 wVk
= IntTranslateNumpadKey(wVk
& 0xFF);
955 /* Send keyboard input */
956 KbdInput
.wVk
= wVk
& 0xFF;
957 KbdInput
.wScan
= wScanCode
& 0x7F;
958 KbdInput
.dwFlags
= 0;
959 if (pKbdInputData
->Flags
& KEY_BREAK
)
960 KbdInput
.dwFlags
|= KEYEVENTF_KEYUP
;
962 KbdInput
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
964 KbdInput
.dwExtraInfo
= pKbdInputData
->ExtraInformation
;
965 UserSendKeyboardInput(&KbdInput
, FALSE
);
967 /* E1 keys don't have break code */
968 if (pKbdInputData
->Flags
& KEY_E1
)
970 /* Send key up event */
971 KbdInput
.dwFlags
|= KEYEVENTF_KEYUP
;
972 UserSendKeyboardInput(&KbdInput
, FALSE
);
978 * IntTranslateKbdMessage
980 * Addes WM_(SYS)CHAR messages to message queue if message
981 * describes key which produce character.
984 IntTranslateKbdMessage(LPMSG lpMsg
,
989 WCHAR wch
[3] = { 0 };
993 LARGE_INTEGER LargeTickCount
;
994 BOOL bResult
= FALSE
;
996 pWnd
= UserGetWindowObject(lpMsg
->hwnd
);
997 if (!pWnd
) // Must have a window!
999 ERR("No Window for Translate.\n");
1003 pti
= pWnd
->head
.pti
;
1004 pKbdTbl
= pti
->KeyboardLayout
->KBTables
;
1008 if (lpMsg
->message
!= WM_KEYDOWN
&& lpMsg
->message
!= WM_SYSKEYDOWN
)
1011 /* Init pt, hwnd and time msg fields */
1012 NewMsg
.pt
= gpsi
->ptCursor
;
1013 NewMsg
.hwnd
= lpMsg
->hwnd
;
1014 KeQueryTickCount(&LargeTickCount
);
1015 NewMsg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
1017 TRACE("Enter IntTranslateKbdMessage msg %s, vk %x\n",
1018 lpMsg
->message
== WM_SYSKEYDOWN
? "WM_SYSKEYDOWN" : "WM_KEYDOWN", lpMsg
->wParam
);
1020 if (lpMsg
->wParam
== VK_PACKET
)
1022 NewMsg
.message
= (lpMsg
->message
== WM_KEYDOWN
) ? WM_CHAR
: WM_SYSCHAR
;
1023 NewMsg
.wParam
= HIWORD(lpMsg
->lParam
);
1024 NewMsg
.lParam
= LOWORD(lpMsg
->lParam
);
1025 MsqPostMessage(pti
->MessageQueue
, &NewMsg
, FALSE
, QS_KEY
);
1029 cch
= IntToUnicodeEx(lpMsg
->wParam
,
1030 HIWORD(lpMsg
->lParam
) & 0xFF,
1031 pti
->MessageQueue
->KeyState
,
1033 sizeof(wch
) / sizeof(wch
[0]),
1039 if (cch
> 0) /* Normal characters */
1040 NewMsg
.message
= (lpMsg
->message
== WM_KEYDOWN
) ? WM_CHAR
: WM_SYSCHAR
;
1041 else /* Dead character */
1045 (lpMsg
->message
== WM_KEYDOWN
) ? WM_DEADCHAR
: WM_SYSDEADCHAR
;
1047 NewMsg
.lParam
= lpMsg
->lParam
;
1049 /* Send all characters */
1050 for (i
= 0; i
< cch
; ++i
)
1052 TRACE("Msg: %x '%lc' (%04x) %08x\n", NewMsg
.message
, wch
[i
], wch
[i
], NewMsg
.lParam
);
1054 NewMsg
.wParam
= wch
[i
];
1058 if (!NT_SUCCESS(RtlUnicodeToMultiByteN(&ch
, sizeof(ch
), NULL
, &wch
[i
], sizeof(wch
[i
]))))
1059 WARN("RtlUnicodeToMultiByteN failed!\n");
1062 MsqPostMessage(pti
->MessageQueue
, &NewMsg
, FALSE
, QS_KEY
);
1067 TRACE("Leave IntTranslateKbdMessage ret %u, cch %d, msg %x, wch %x\n",
1068 bResult
, cch
, NewMsg
.message
, NewMsg
.wParam
);
1073 * Map a virtual key code, or virtual scan code, to a scan code, key code,
1074 * or unshifted unicode character.
1078 * 0 -- Code is a virtual key code that is converted into a virtual scan code
1079 * that does not distinguish between left and right shift keys.
1080 * 1 -- Code is a virtual scan code that is converted into a virtual key code
1081 * that does not distinguish between left and right shift keys.
1082 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
1084 * 3 -- Code is a virtual scan code that is converted into a virtual key code
1085 * that distinguishes left and right shift keys.
1086 * KeyLayout: Keyboard layout handle
1091 IntMapVirtualKeyEx(UINT uCode
, UINT Type
, PKBDTABLES pKbdTbl
)
1097 case MAPVK_VK_TO_VSC
:
1098 uCode
= IntFixVk(uCode
, FALSE
);
1099 uRet
= IntVkToVsc(uCode
, pKbdTbl
);
1100 if (uRet
> 0xFF) // fail for scancodes with prefix (e0, e1)
1104 case MAPVK_VSC_TO_VK
:
1105 uRet
= IntVscToVk(uCode
, pKbdTbl
) & 0xFF;
1106 uRet
= IntSimplifyVk(uRet
);
1109 case MAPVK_VK_TO_CHAR
:
1110 uRet
= (UINT
)IntVkToChar(uCode
, pKbdTbl
);
1113 case MAPVK_VSC_TO_VK_EX
:
1114 uRet
= IntVscToVk(uCode
, pKbdTbl
) & 0xFF;
1117 case MAPVK_VK_TO_VSC_EX
:
1118 uRet
= IntVkToVsc(uCode
, pKbdTbl
);
1122 EngSetLastError(ERROR_INVALID_PARAMETER
);
1123 ERR("Wrong type value: %u\n", Type
);
1130 * NtUserMapVirtualKeyEx
1132 * Map a virtual key code, or virtual scan code, to a scan code, key code,
1133 * or unshifted unicode character. See IntMapVirtualKeyEx.
1137 NtUserMapVirtualKeyEx(UINT uCode
, UINT uType
, DWORD keyboardId
, HKL dwhkl
)
1139 PKBDTABLES pKbdTbl
= NULL
;
1140 DECLARE_RETURN(UINT
);
1142 TRACE("Enter NtUserMapVirtualKeyEx\n");
1143 UserEnterExclusive();
1149 pti
= PsGetCurrentThreadWin32Thread();
1150 if (pti
&& pti
->KeyboardLayout
)
1151 pKbdTbl
= pti
->KeyboardLayout
->KBTables
;
1157 pKbl
= UserHklToKbl(dwhkl
);
1159 pKbdTbl
= pKbl
->KBTables
;
1165 RETURN(IntMapVirtualKeyEx(uCode
, uType
, pKbdTbl
));
1168 TRACE("Leave NtUserMapVirtualKeyEx, ret=%i\n", _ret_
);
1176 * Translates virtual key to characters
1183 PBYTE pKeyStateUnsafe
,
1184 LPWSTR pwszBuffUnsafe
,
1190 BYTE KeyState
[0x100];
1191 PWCHAR pwszBuff
= NULL
;
1194 DECLARE_RETURN(int);
1196 TRACE("Enter NtUserSetKeyboardState\n");
1200 if (wScanCode
& SC_KEY_UP
)
1205 if (!NT_SUCCESS(MmCopyFromCaller(KeyState
,
1209 ERR("Couldn't copy key state from caller.\n");
1213 /* Virtual code is correct? */
1214 if (wVirtKey
< 0x100)
1216 pwszBuff
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(WCHAR
) * cchBuff
, TAG_STRING
);
1219 ERR("ExAllocatePoolWithTag(%d) failed\n", sizeof(WCHAR
) * cchBuff
);
1222 RtlZeroMemory(pwszBuff
, sizeof(WCHAR
) * cchBuff
);
1225 pKbl
= UserHklToKbl(dwhkl
);
1229 pti
= PsGetCurrentThreadWin32Thread();
1230 pKbl
= pti
->KeyboardLayout
;
1233 iRet
= IntToUnicodeEx(wVirtKey
,
1239 pKbl
? pKbl
->KBTables
: NULL
);
1241 MmCopyToCaller(pwszBuffUnsafe
, pwszBuff
, cchBuff
* sizeof(WCHAR
));
1242 ExFreePoolWithTag(pwszBuff
, TAG_STRING
);
1248 TRACE("Leave NtUserSetKeyboardState, ret=%i\n", _ret_
);
1254 * NtUserGetKeyNameText
1256 * Gets key name from keyboard layout
1260 NtUserGetKeyNameText(LONG lParam
, LPWSTR lpString
, int cchSize
)
1263 DWORD i
, cchKeyName
, dwRet
= 0;
1264 WORD wScanCode
= (lParam
>> 16) & 0xFF;
1265 BOOL bExtKey
= (lParam
& LP_EXT_BIT
) ? TRUE
: FALSE
;
1267 VSC_LPWSTR
*pKeyNames
= NULL
;
1268 CONST WCHAR
*pKeyName
= NULL
;
1269 WCHAR KeyNameBuf
[2];
1270 DECLARE_RETURN(DWORD
);
1272 TRACE("Enter NtUserGetKeyNameText\n");
1275 /* Get current keyboard layout */
1276 pti
= PsGetCurrentThreadWin32Thread();
1277 pKbdTbl
= pti
? pti
->KeyboardLayout
->KBTables
: 0;
1279 if (!pKbdTbl
|| cchSize
< 1)
1282 /* "Do not care" flag */
1283 if(lParam
& LP_DO_NOT_CARE_BIT
)
1285 /* Note: we could do vsc -> vk -> vsc conversion, instead of using
1286 hardcoded scan codes, but it's not what Windows does */
1287 if (wScanCode
== SCANCODE_RSHIFT
&& !bExtKey
)
1288 wScanCode
= SCANCODE_LSHIFT
;
1289 else if (wScanCode
== SCANCODE_CTRL
|| wScanCode
== SCANCODE_ALT
)
1294 pKeyNames
= pKbdTbl
->pKeyNamesExt
;
1296 pKeyNames
= pKbdTbl
->pKeyNames
;
1298 for (i
= 0; pKeyNames
[i
].pwsz
; i
++)
1300 if (pKeyNames
[i
].vsc
== wScanCode
)
1302 pKeyName
= pKeyNames
[i
].pwsz
;
1309 WORD wVk
= IntVscToVk(wScanCode
, pKbdTbl
);
1313 KeyNameBuf
[0] = IntVkToChar(wVk
, pKbdTbl
);
1316 pKeyName
= KeyNameBuf
;
1322 cchKeyName
= wcslen(pKeyName
);
1323 if (cchKeyName
> cchSize
- 1)
1324 cchKeyName
= cchSize
- 1; // don't count '\0'
1328 ProbeForWrite(lpString
, (cchKeyName
+ 1) * sizeof(WCHAR
), 1);
1329 RtlCopyMemory(lpString
, pKeyName
, cchKeyName
* sizeof(WCHAR
));
1330 lpString
[cchKeyName
] = UNICODE_NULL
;
1333 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1335 SetLastNtError(_SEH2_GetExceptionCode());
1341 EngSetLastError(ERROR_INVALID_PARAMETER
);
1347 TRACE("Leave NtUserGetKeyNameText, ret=%i\n", _ret_
);
1353 * UserGetKeyboardType
1355 * Returns some keyboard specific information
1358 UserGetKeyboardType(
1363 case 0: /* Keyboard type */
1364 return 4; /* AT-101 */
1365 case 1: /* Keyboard Subtype */
1366 return 0; /* There are no defined subtypes */
1367 case 2: /* Number of F-keys */
1368 return 12; /* We're doing an 101 for now, so return 12 F-keys */
1370 ERR("Unknown type!\n");
1371 return 0; /* Note: we don't have to set last error here */
1378 * Based on IntTranslateChar, instead of processing VirtualKey match,
1379 * look for wChar match.
1389 PVK_TO_WCHAR_TABLE pVkToWchTbl
;
1390 PVK_TO_WCHARS10 pVkToWch
;
1392 DWORD i
, dwModBits
= 0, dwModNumber
= 0, Ret
= (DWORD
)-1;
1394 TRACE("NtUserVkKeyScanEx() wch %d, KbdLayout 0x%p\n", wch
, dwhkl
);
1399 // Use given keyboard layout
1401 pKbl
= UserHklToKbl(dwhkl
);
1405 // Use thread keyboard layout
1406 pKbl
= ((PTHREADINFO
)PsGetCurrentThreadWin32Thread())->KeyboardLayout
;
1412 pKbdTbl
= pKbl
->KBTables
;
1414 // Interate through all VkToWchar tables while pVkToWchars is not NULL
1415 for (i
= 0; pKbdTbl
->pVkToWcharTable
[i
].pVkToWchars
; i
++)
1417 pVkToWchTbl
= &pKbdTbl
->pVkToWcharTable
[i
];
1418 pVkToWch
= (PVK_TO_WCHARS10
)(pVkToWchTbl
->pVkToWchars
);
1420 // Interate through all virtual keys
1421 while (pVkToWch
->VirtualKey
)
1423 for (dwModNumber
= 0; dwModNumber
< pVkToWchTbl
->nModifications
; dwModNumber
++)
1425 if (pVkToWch
->wch
[dwModNumber
] == wch
)
1427 dwModBits
= pKbdTbl
->pCharModifiers
->ModNumber
[dwModNumber
];
1428 TRACE("i %d wC %04x: dwModBits %08x dwModNumber %08x MaxModBits %08x\n",
1429 i
, wch
, dwModBits
, dwModNumber
, pKbdTbl
->pCharModifiers
->wMaxModBits
);
1430 Ret
= (dwModBits
<< 8) | (pVkToWch
->VirtualKey
& 0xFF);
1434 pVkToWch
= (PVK_TO_WCHARS10
)(((BYTE
*)pVkToWch
) + pVkToWchTbl
->cbSize
);
1443 * UserGetMouseButtonsState
1445 * Returns bitfield used in mouse messages
1448 UserGetMouseButtonsState(VOID
)
1452 if (gpsi
->aiSysMet
[SM_SWAPBUTTON
])
1454 if (gafAsyncKeyState
[VK_RBUTTON
] & KS_DOWN_BIT
) ret
|= MK_LBUTTON
;
1455 if (gafAsyncKeyState
[VK_LBUTTON
] & KS_DOWN_BIT
) ret
|= MK_RBUTTON
;
1459 if (gafAsyncKeyState
[VK_LBUTTON
] & KS_DOWN_BIT
) ret
|= MK_LBUTTON
;
1460 if (gafAsyncKeyState
[VK_RBUTTON
] & KS_DOWN_BIT
) ret
|= MK_RBUTTON
;
1462 if (gafAsyncKeyState
[VK_MBUTTON
] & KS_DOWN_BIT
) ret
|= MK_MBUTTON
;
1463 if (gafAsyncKeyState
[VK_SHIFT
] & KS_DOWN_BIT
) ret
|= MK_SHIFT
;
1464 if (gafAsyncKeyState
[VK_CONTROL
] & KS_DOWN_BIT
) ret
|= MK_CONTROL
;
1465 if (gafAsyncKeyState
[VK_XBUTTON1
] & KS_DOWN_BIT
) ret
|= MK_XBUTTON1
;
1466 if (gafAsyncKeyState
[VK_XBUTTON2
] & KS_DOWN_BIT
) ret
|= MK_XBUTTON2
;