2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Keyboard functions
5 * FILE: win32ss/user/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
[256 * 2 / 8]; // 2 bits per key
14 static BYTE gafAsyncKeyStateRecentDown
[256 / 8]; // 1 bit per key
15 static PKEYBOARD_INDICATOR_TRANSLATION gpKeyboardIndicatorTrans
= NULL
;
16 static KEYBOARD_INDICATOR_PARAMETERS gIndicators
= {0, 0};
17 KEYBOARD_ATTRIBUTES gKeyboardInfo
;
18 INT gLanguageToggleKeyState
= 0;
19 DWORD gdwLanguageToggleKey
= 1;
20 INT gLayoutToggleKeyState
= 0;
21 DWORD gdwLayoutToggleKey
= 2;
23 /* FUNCTIONS *****************************************************************/
28 * Initialization -- Right now, just zero the key state
33 InitKeyboardImpl(VOID
)
35 RtlZeroMemory(&gafAsyncKeyState
, sizeof(gafAsyncKeyState
));
36 RtlZeroMemory(&gafAsyncKeyStateRecentDown
, sizeof(gafAsyncKeyStateRecentDown
));
37 // Clear and set default information.
38 RtlZeroMemory(&gKeyboardInfo
, sizeof(gKeyboardInfo
));
39 gKeyboardInfo
.KeyboardIdentifier
.Type
= 4; /* AT-101 */
40 gKeyboardInfo
.NumberOfFunctionKeys
= 12; /* We're doing an 101 for now, so return 12 F-keys */
41 return STATUS_SUCCESS
;
45 * IntKeyboardGetIndicatorTrans
47 * Asks the keyboard driver to send a small table that shows which
48 * lights should connect with which scancodes
52 IntKeyboardGetIndicatorTrans(HANDLE hKeyboardDevice
,
53 PKEYBOARD_INDICATOR_TRANSLATION
*ppIndicatorTrans
)
57 IO_STATUS_BLOCK Block
;
58 PKEYBOARD_INDICATOR_TRANSLATION pRet
;
60 dwSize
= sizeof(KEYBOARD_INDICATOR_TRANSLATION
);
62 pRet
= ExAllocatePoolWithTag(PagedPool
,
68 Status
= ZwDeviceIoControlFile(hKeyboardDevice
,
73 IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION
,
77 if (Status
!= STATUS_BUFFER_TOO_SMALL
)
80 ExFreePoolWithTag(pRet
, USERTAG_KBDTABLE
);
82 dwSize
+= sizeof(KEYBOARD_INDICATOR_TRANSLATION
);
84 pRet
= ExAllocatePoolWithTag(PagedPool
,
90 return STATUS_INSUFFICIENT_RESOURCES
;
92 if (!NT_SUCCESS(Status
))
94 ExFreePoolWithTag(pRet
, USERTAG_KBDTABLE
);
98 *ppIndicatorTrans
= pRet
;
103 * IntKeyboardUpdateLeds
105 * Sends the keyboard commands to turn on/off the lights
109 IntKeyboardUpdateLeds(HANDLE hKeyboardDevice
,
116 IO_STATUS_BLOCK Block
;
118 if (!gpKeyboardIndicatorTrans
)
119 return STATUS_NOT_SUPPORTED
;
123 case VK_CAPITAL
: LedFlag
= KEYBOARD_CAPS_LOCK_ON
; break;
124 case VK_NUMLOCK
: LedFlag
= KEYBOARD_NUM_LOCK_ON
; break;
125 case VK_SCROLL
: LedFlag
= KEYBOARD_SCROLL_LOCK_ON
; break;
127 for (i
= 0; i
< gpKeyboardIndicatorTrans
->NumberOfIndicatorKeys
; i
++)
129 if (gpKeyboardIndicatorTrans
->IndicatorList
[i
].MakeCode
== wScanCode
)
131 LedFlag
= gpKeyboardIndicatorTrans
->IndicatorList
[i
].IndicatorFlags
;
139 gIndicators
.LedFlags
^= LedFlag
;
141 /* Update the lights on the hardware */
142 Status
= ZwDeviceIoControlFile(hKeyboardDevice
,
147 IOCTL_KEYBOARD_SET_INDICATORS
,
148 &gIndicators
, sizeof(gIndicators
),
154 return STATUS_SUCCESS
;
160 * Initializes keyboard indicators translation and their state
163 UserInitKeyboard(HANDLE hKeyboardDevice
)
166 IO_STATUS_BLOCK Block
;
168 IntKeyboardGetIndicatorTrans(hKeyboardDevice
, &gpKeyboardIndicatorTrans
);
170 Status
= ZwDeviceIoControlFile(hKeyboardDevice
,
175 IOCTL_KEYBOARD_QUERY_INDICATORS
,
178 sizeof(gIndicators
));
180 if (!NT_SUCCESS(Status
))
182 WARN("NtDeviceIoControlFile() failed, ignored\n");
183 gIndicators
.LedFlags
= 0;
184 gIndicators
.UnitId
= 0;
187 SET_KEY_LOCKED(gafAsyncKeyState
, VK_CAPITAL
,
188 gIndicators
.LedFlags
& KEYBOARD_CAPS_LOCK_ON
);
189 SET_KEY_LOCKED(gafAsyncKeyState
, VK_NUMLOCK
,
190 gIndicators
.LedFlags
& KEYBOARD_NUM_LOCK_ON
);
191 SET_KEY_LOCKED(gafAsyncKeyState
, VK_SCROLL
,
192 gIndicators
.LedFlags
& KEYBOARD_SCROLL_LOCK_ON
);
194 // FIXME: Need device driver to work! HID support more than one!!!!
195 Status
= ZwDeviceIoControlFile(hKeyboardDevice
,
200 IOCTL_KEYBOARD_QUERY_ATTRIBUTES
,
202 &gKeyboardInfo
, sizeof(gKeyboardInfo
));
204 if (!NT_SUCCESS(Status
))
206 ERR("NtDeviceIoControlFile() failed, ignored\n");
208 TRACE("Keyboard type %u, subtype %u and number of func keys %u\n",
209 gKeyboardInfo
.KeyboardIdentifier
.Type
,
210 gKeyboardInfo
.KeyboardIdentifier
.Subtype
,
211 gKeyboardInfo
.NumberOfFunctionKeys
);
217 * Changes virtual keys which distinguish between left and right hand, to keys which don't distinguish
221 IntSimplifyVk(WORD wVk
)
245 * Changes virtual keys which don't not distinguish between left and right hand to proper keys
249 IntFixVk(WORD wVk
, BOOL bExt
)
254 return bExt
? VK_RSHIFT
: VK_LSHIFT
;
257 return bExt
? VK_RCONTROL
: VK_LCONTROL
;
260 return bExt
? VK_RMENU
: VK_LMENU
;
268 * IntTranslateNumpadKey
270 * Translates numpad keys when numlock is enabled
274 IntTranslateNumpadKey(WORD wVk
)
278 case VK_INSERT
: return VK_NUMPAD0
;
279 case VK_END
: return VK_NUMPAD1
;
280 case VK_DOWN
: return VK_NUMPAD2
;
281 case VK_NEXT
: return VK_NUMPAD3
;
282 case VK_LEFT
: return VK_NUMPAD4
;
283 case VK_CLEAR
: return VK_NUMPAD5
;
284 case VK_RIGHT
: return VK_NUMPAD6
;
285 case VK_HOME
: return VK_NUMPAD7
;
286 case VK_UP
: return VK_NUMPAD8
;
287 case VK_PRIOR
: return VK_NUMPAD9
;
288 case VK_DELETE
: return VK_DECIMAL
;
296 * Gets layout specific modification bits, for example KBDSHIFT, KBDCTRL, KBDALT
300 IntGetModBits(PKBDTABLES pKbdTbl
, PBYTE pKeyState
)
302 DWORD i
, dwModBits
= 0;
304 /* DumpKeyState( KeyState ); */
306 for (i
= 0; pKbdTbl
->pCharModifiers
->pVkToBit
[i
].Vk
; i
++)
307 if (IS_KEY_DOWN(pKeyState
, pKbdTbl
->pCharModifiers
->pVkToBit
[i
].Vk
))
308 dwModBits
|= pKbdTbl
->pCharModifiers
->pVkToBit
[i
].ModBits
;
310 TRACE("Current Mod Bits: %lx\n", dwModBits
);
318 * Translates virtual key to character
322 IntTranslateChar(WORD wVirtKey
,
326 PWCHAR pwcTranslatedChar
,
329 PVK_TO_WCHAR_TABLE pVkToVchTbl
;
330 PVK_TO_WCHARS10 pVkToVch
;
331 DWORD i
, dwModBits
, dwVkModBits
, dwModNumber
= 0;
336 dwModBits
= pKeyState
? IntGetModBits(pKbdTbl
, pKeyState
) : 0;
337 bAltGr
= pKeyState
&& (pKbdTbl
->fLocaleFlags
& KLLF_ALTGR
) && IS_KEY_DOWN(pKeyState
, VK_RMENU
);
338 wCaplokAttr
= bAltGr
? CAPLOKALTGR
: CAPLOK
;
340 TRACE("TryToTranslate: %04x %x\n", wVirtKey
, dwModBits
);
342 /* If ALT without CTRL has ben used, remove ALT flag */
343 if ((dwModBits
& (KBDALT
|KBDCTRL
)) == KBDALT
)
344 dwModBits
&= ~KBDALT
;
346 if (dwModBits
> pKbdTbl
->pCharModifiers
->wMaxModBits
)
348 TRACE("dwModBits %x > wMaxModBits %x\n", dwModBits
, pKbdTbl
->pCharModifiers
->wMaxModBits
);
352 for (i
= 0; pKbdTbl
->pVkToWcharTable
[i
].pVkToWchars
; i
++)
354 pVkToVchTbl
= &pKbdTbl
->pVkToWcharTable
[i
];
355 pVkToVch
= (PVK_TO_WCHARS10
)(pVkToVchTbl
->pVkToWchars
);
356 while (pVkToVch
->VirtualKey
)
358 if (wVirtKey
== (pVkToVch
->VirtualKey
& 0xFF))
360 dwVkModBits
= dwModBits
;
362 /* If CapsLock is enabled for this key and locked, add SHIFT bit */
363 if ((pVkToVch
->Attributes
& wCaplokAttr
) &&
365 IS_KEY_LOCKED(pKeyState
, VK_CAPITAL
))
367 /* Note: we use special value here instead of getting VK_SHIFT mod bit - it's verified */
368 dwVkModBits
^= KBDSHIFT
;
371 if (dwVkModBits
> pKbdTbl
->pCharModifiers
->wMaxModBits
)
374 /* Get modification number */
375 dwModNumber
= pKbdTbl
->pCharModifiers
->ModNumber
[dwVkModBits
];
376 if (dwModNumber
>= pVkToVchTbl
->nModifications
)
378 TRACE("dwModNumber %u >= nModifications %u\n", dwModNumber
, pVkToVchTbl
->nModifications
);
383 wch
= pVkToVch
->wch
[dwModNumber
];
387 *pbDead
= (wch
== WCH_DEAD
);
388 *pbLigature
= (wch
== WCH_LGTR
);
389 *pwcTranslatedChar
= wch
;
391 TRACE("%lu %04x: dwModNumber %08x Char %04x\n",
392 i
, wVirtKey
, dwModNumber
, wch
);
396 /* After WCH_DEAD, real character is located */
397 pVkToVch
= (PVK_TO_WCHARS10
)(((BYTE
*)pVkToVch
) + pVkToVchTbl
->cbSize
);
398 if (pVkToVch
->VirtualKey
!= 0xFF)
400 WARN("Found dead key with no trailer in the table.\n");
401 WARN("VK: %04x, ADDR: %p\n", wVirtKey
, pVkToVch
);
404 *pwcTranslatedChar
= pVkToVch
->wch
[dwModNumber
];
408 pVkToVch
= (PVK_TO_WCHARS10
)(((BYTE
*)pVkToVch
) + pVkToVchTbl
->cbSize
);
412 /* If nothing has been found in layout, check if this is ASCII control character.
413 Note: we could add it to layout table, but windows does not have it there */
414 if (wVirtKey
>= 'A' && wVirtKey
<= 'Z' &&
415 pKeyState
&& IS_KEY_DOWN(pKeyState
, VK_CONTROL
) &&
416 !IS_KEY_DOWN(pKeyState
, VK_MENU
))
418 *pwcTranslatedChar
= (wVirtKey
- 'A') + 1; /* ASCII control character */
430 * Translates virtual key to characters
434 IntToUnicodeEx(UINT wVirtKey
,
442 WCHAR wchTranslatedChar
;
443 BOOL bDead
, bLigature
;
444 static WCHAR wchDead
= 0;
449 if (!IntTranslateChar(wVirtKey
,
461 WARN("Not handling ligature (yet)\n" );
465 /* If we got dead char in previous call check dead keys in keyboard layout */
469 WCHAR wchFirst
, wchSecond
;
470 TRACE("Previous dead char: %lc (%x)\n", wchDead
, wchDead
);
472 if (pKbdTbl
->pDeadKey
)
474 for (i
= 0; pKbdTbl
->pDeadKey
[i
].dwBoth
; i
++)
476 wchFirst
= pKbdTbl
->pDeadKey
[i
].dwBoth
>> 16;
477 wchSecond
= pKbdTbl
->pDeadKey
[i
].dwBoth
& 0xFFFF;
478 if (wchFirst
== wchDead
&& wchSecond
== wchTranslatedChar
)
480 wchTranslatedChar
= pKbdTbl
->pDeadKey
[i
].wchComposed
;
489 #if defined(__GNUC__)
490 if (wchDead
== 0x8000)
492 ERR("GCC is inventing bits, ignoring fake dead key\n");
498 TRACE("Final char: %lc (%x)\n", wchTranslatedChar
, wchTranslatedChar
);
501 /* Dead char has not been not found */
504 /* Treat both characters normally */
506 pwszBuff
[iRet
++] = wchDead
;
510 /* Add character to the buffer */
512 pwszBuff
[iRet
++] = wchTranslatedChar
;
514 /* Save dead character */
515 wchDead
= bDead
? wchTranslatedChar
: 0;
517 return bDead
? -iRet
: iRet
;
523 * Translates virtual key to scan code
527 IntVkToVsc(WORD wVk
, PKBDTABLES pKbdTbl
)
533 /* Check standard keys first */
534 for (i
= 0; i
< pKbdTbl
->bMaxVSCtoVK
; i
++)
536 if ((pKbdTbl
->pusVSCtoVK
[i
] & 0xFF) == wVk
)
540 /* Check extended keys now */
541 for (i
= 0; pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
; i
++)
543 if ((pKbdTbl
->pVSCtoVK_E0
[i
].Vk
& 0xFF) == wVk
)
544 return 0xE000 | pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
;
547 for (i
= 0; pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
; i
++)
549 if ((pKbdTbl
->pVSCtoVK_E1
[i
].Vk
& 0xFF) == wVk
)
550 return 0xE100 | pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
;
553 /* Virtual key has not been found */
560 * Translates prefixed scancode to virtual key
564 IntVscToVk(WORD wScanCode
, PKBDTABLES pKbdTbl
)
571 if ((wScanCode
& 0xFF00) == 0xE000)
573 for (i
= 0; pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
; i
++)
575 if (pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
== (wScanCode
& 0xFF))
577 wVk
= pKbdTbl
->pVSCtoVK_E0
[i
].Vk
;
581 else if ((wScanCode
& 0xFF00) == 0xE100)
583 for (i
= 0; pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
; i
++)
585 if (pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
== (wScanCode
& 0xFF))
587 wVk
= pKbdTbl
->pVSCtoVK_E1
[i
].Vk
;
591 else if (wScanCode
< pKbdTbl
->bMaxVSCtoVK
)
593 wVk
= pKbdTbl
->pusVSCtoVK
[wScanCode
];
596 /* 0xFF nad 0x00 are invalid VKs */
597 return wVk
!= 0xFF ? wVk
: 0;
603 * Translates virtual key to character, ignoring shift state
607 IntVkToChar(WORD wVk
, PKBDTABLES pKbdTbl
)
610 BOOL bDead
, bLigature
;
614 if (IntTranslateChar(wVk
,
628 * NtUserGetAsyncKeyState
630 * Gets key state from global bitmap
634 NtUserGetAsyncKeyState(INT Key
)
638 TRACE("Enter NtUserGetAsyncKeyState\n");
640 if (Key
>= 0x100 || Key
< 0)
642 EngSetLastError(ERROR_INVALID_PARAMETER
);
643 ERR("Invalid parameter Key\n");
647 UserEnterExclusive();
649 if (IS_KEY_DOWN(gafAsyncKeyState
, Key
))
650 wRet
|= 0x8000; // If down, windows returns 0x8000.
651 if (gafAsyncKeyStateRecentDown
[Key
/ 8] & (1 << (Key
% 8)))
653 gafAsyncKeyStateRecentDown
[Key
/ 8] &= ~(1 << (Key
% 8));
657 TRACE("Leave NtUserGetAsyncKeyState, ret=%u\n", wRet
);
662 * UpdateAsyncKeyState
664 * Updates gafAsyncKeyState array
668 UpdateAsyncKeyState(WORD wVk
, BOOL bIsDown
)
672 /* If it's first key down event, xor lock bit */
673 if (!IS_KEY_DOWN(gafAsyncKeyState
, wVk
))
674 SET_KEY_LOCKED(gafAsyncKeyState
, wVk
, !IS_KEY_LOCKED(gafAsyncKeyState
, wVk
));
676 SET_KEY_DOWN(gafAsyncKeyState
, wVk
, TRUE
);
677 gafAsyncKeyStateRecentDown
[wVk
/ 8] |= (1 << (wVk
% 8));
680 SET_KEY_DOWN(gafAsyncKeyState
, wVk
, FALSE
);
684 * co_CallLowLevelKeyboardHook
686 * Calls WH_KEYBOARD_LL hook
689 co_CallLowLevelKeyboardHook(WORD wVk
, WORD wScanCode
, DWORD dwFlags
, BOOL bInjected
, DWORD dwTime
, DWORD dwExtraInfo
)
691 KBDLLHOOKSTRUCT KbdHookData
;
694 KbdHookData
.vkCode
= wVk
;
695 KbdHookData
.scanCode
= wScanCode
;
696 KbdHookData
.flags
= 0;
697 if (dwFlags
& KEYEVENTF_EXTENDEDKEY
)
698 KbdHookData
.flags
|= LLKHF_EXTENDED
;
699 if (IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
))
700 KbdHookData
.flags
|= LLKHF_ALTDOWN
;
701 if (dwFlags
& KEYEVENTF_KEYUP
)
702 KbdHookData
.flags
|= LLKHF_UP
;
704 KbdHookData
.flags
|= LLKHF_INJECTED
;
705 KbdHookData
.time
= dwTime
;
706 KbdHookData
.dwExtraInfo
= dwExtraInfo
;
708 /* Note: it doesnt support WM_SYSKEYUP */
709 if (dwFlags
& KEYEVENTF_KEYUP
)
711 else if (IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
) && !IS_KEY_DOWN(gafAsyncKeyState
, VK_CONTROL
))
712 uMsg
= WM_SYSKEYDOWN
;
716 return co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, uMsg
, (LPARAM
)&KbdHookData
);
722 * Saves snapshot of specified window or whole screen in the clipboard
725 SnapWindow(HWND hWnd
)
727 HBITMAP hbm
= NULL
, hbmOld
;
728 HDC hdc
= NULL
, hdcMem
;
733 TRACE("SnapWindow(%p)\n", hWnd
);
735 /* If no windows is given, make snapshot of desktop window */
737 hWnd
= IntGetDesktopWindow();
739 pWnd
= UserGetWindowObject(hWnd
);
742 ERR("Invalid window\n");
746 hdc
= UserGetDCEx(pWnd
, NULL
, DCX_USESTYLE
| DCX_WINDOW
);
749 ERR("UserGetDCEx failed!\n");
753 cx
= pWnd
->rcWindow
.right
- pWnd
->rcWindow
.left
;
754 cy
= pWnd
->rcWindow
.bottom
- pWnd
->rcWindow
.top
;
756 hbm
= NtGdiCreateCompatibleBitmap(hdc
, cx
, cy
);
759 ERR("NtGdiCreateCompatibleBitmap failed!\n");
763 hdcMem
= NtGdiCreateCompatibleDC(hdc
);
766 ERR("NtGdiCreateCompatibleDC failed!\n");
770 hbmOld
= NtGdiSelectBitmap(hdcMem
, hbm
);
771 NtGdiBitBlt(hdcMem
, 0, 0, cx
, cy
, hdc
, 0, 0, SRCCOPY
, 0, 0);
772 NtGdiSelectBitmap(hdcMem
, hbmOld
);
773 IntGdiDeleteDC(hdcMem
, FALSE
);
775 /* Save snapshot in clipboard */
776 if (UserOpenClipboard(NULL
))
778 UserEmptyClipboard();
779 scd
.fIncSerialNumber
= TRUE
;
780 scd
.fGlobalHandle
= FALSE
;
781 if (UserSetClipboardData(CF_BITMAP
, hbm
, &scd
))
783 /* Bitmap is managed by system now */
786 UserCloseClipboard();
791 GreDeleteObject(hbm
);
793 UserReleaseDC(pWnd
, hdc
, FALSE
);
796 /* Find the next/previous keyboard layout of the same/different language */
804 LANGID LangID
= LOWORD(pKL
->hkl
);
808 pKL
= (bNext
? pKL
->pklNext
: pKL
->pklPrev
);
810 if (!(pKL
->dwKL_Flags
& KLF_UNLOAD
) && bSameLang
== (LangID
== LOWORD(pKL
->hkl
)))
812 } while (pKL
!= pFirstKL
);
817 /* Perform layout toggle by [Left Alt]+Shift or Ctrl+Shift */
820 _In_ PUSER_MESSAGE_QUEUE pFocusQueue
,
824 PWND pWnd
= pFocusQueue
->spwndFocus
;
831 pWnd
= pFocusQueue
->spwndActive
;
835 pti
= pWnd
->head
.pti
;
836 pkl
= pti
->KeyboardLayout
;
838 if (nKeyState
== INPUTLANGCHANGE_FORWARD
)
839 pkl
= IntGetNextKL(pkl
, TRUE
, bSameLang
);
840 else if (nKeyState
== INPUTLANGCHANGE_BACKWARD
)
841 pkl
= IntGetNextKL(pkl
, FALSE
, bSameLang
);
843 if (gSystemFS
& pkl
->dwFontSigs
)
844 wParam
|= INPUTLANGCHANGE_SYSCHARSET
;
846 hWnd
= UserHMGetHandle(pWnd
);
847 UserPostMessage(hWnd
, WM_INPUTLANGCHANGEREQUEST
, wParam
, (LPARAM
)pkl
->hkl
);
850 /* Check Language Toggle by [Left Alt]+Shift or Ctrl+Shift */
852 IntCheckLanguageToggle(
853 _In_ PUSER_MESSAGE_QUEUE pFocusQueue
,
856 _Inout_ PINT pKeyState
)
858 if (bIsDown
) /* Toggle key combination is pressed? */
860 if (wVk
== VK_LSHIFT
)
861 *pKeyState
= INPUTLANGCHANGE_FORWARD
;
862 else if (wVk
== VK_RSHIFT
)
863 *pKeyState
= INPUTLANGCHANGE_BACKWARD
;
864 else if (!wVk
&& IS_KEY_DOWN(gafAsyncKeyState
, VK_LSHIFT
))
865 *pKeyState
= INPUTLANGCHANGE_FORWARD
;
866 else if (!wVk
&& IS_KEY_DOWN(gafAsyncKeyState
, VK_RSHIFT
))
867 *pKeyState
= INPUTLANGCHANGE_BACKWARD
;
876 IntLanguageToggle(pFocusQueue
, (pKeyState
== &gLayoutToggleKeyState
), *pKeyState
);
883 * UserSendKeyboardInput
885 * Process keyboard input from input devices and SendInput API
888 ProcessKeyEvent(WORD wVk
, WORD wScanCode
, DWORD dwFlags
, BOOL bInjected
, DWORD dwTime
, DWORD dwExtraInfo
)
890 WORD wSimpleVk
= 0, wFixedVk
, wVk2
;
891 PUSER_MESSAGE_QUEUE pFocusQueue
;
893 BOOL bExt
= (dwFlags
& KEYEVENTF_EXTENDEDKEY
) ? TRUE
: FALSE
;
894 BOOL bIsDown
= (dwFlags
& KEYEVENTF_KEYUP
) ? FALSE
: TRUE
;
895 BOOL bPacket
= (dwFlags
& KEYEVENTF_UNICODE
) ? TRUE
: FALSE
;
896 BOOL bWasSimpleDown
= FALSE
, bPostMsg
= TRUE
, bIsSimpleDown
;
898 static BOOL bMenuDownRecently
= FALSE
;
899 BOOL bLangToggled
= FALSE
;
901 /* Get virtual key without shifts (VK_(L|R)* -> VK_*) */
902 wSimpleVk
= IntSimplifyVk(wVk
);
904 if (PRIMARYLANGID(gusLanguageID
) == LANG_JAPANESE
)
906 /* Japanese special! */
907 if (IS_KEY_DOWN(gafAsyncKeyState
, VK_SHIFT
))
909 if (wSimpleVk
== VK_OEM_ATTN
)
910 wSimpleVk
= VK_CAPITAL
;
911 else if (wSimpleVk
== VK_OEM_COPY
)
912 wSimpleVk
= VK_OEM_FINISH
;
916 bWasSimpleDown
= IS_KEY_DOWN(gafAsyncKeyState
, wSimpleVk
);
918 /* Update key without shifts */
919 wVk2
= IntFixVk(wSimpleVk
, !bExt
);
920 bIsSimpleDown
= bIsDown
|| IS_KEY_DOWN(gafAsyncKeyState
, wVk2
);
921 UpdateAsyncKeyState(wSimpleVk
, bIsSimpleDown
);
925 /* Update keyboard LEDs */
926 IntKeyboardUpdateLeds(ghKeyboardDevice
,
931 /* Call WH_KEYBOARD_LL hook */
932 if (co_CallLowLevelKeyboardHook(wVk
, wScanCode
, dwFlags
, bInjected
, dwTime
, dwExtraInfo
))
934 ERR("Kbd msg dropped by WH_KEYBOARD_LL hook\n");
938 /* Check if this is a hotkey */
939 if (co_UserProcessHotKeys(wSimpleVk
, bIsDown
)) //// Check if this is correct, refer to hotkey sequence message tests.
941 TRACE("HotKey Processed\n");
945 wFixedVk
= IntFixVk(wSimpleVk
, bExt
); /* LSHIFT + EXT = RSHIFT */
946 if (wSimpleVk
== VK_SHIFT
) /* shift can't be extended */
949 /* If we have a focus queue, post a keyboard message */
950 pFocusQueue
= IntGetFocusMessageQueue();
951 TRACE("ProcessKeyEvent Q 0x%p Active pWnd 0x%p Focus pWnd 0x%p\n",
953 (pFocusQueue
? pFocusQueue
->spwndActive
: 0),
954 (pFocusQueue
? pFocusQueue
->spwndFocus
: 0));
956 /* If it is F10 or ALT is down and CTRL is up, it's a system key */
957 if ( wVk
== VK_F10
||
958 (wSimpleVk
== VK_MENU
&& bMenuDownRecently
) ||
959 (IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
) &&
960 !IS_KEY_DOWN(gafAsyncKeyState
, VK_CONTROL
)) ||
961 // See MSDN WM_SYSKEYDOWN/UP fixes last wine Win test_keyboard_input.
962 (pFocusQueue
&& !pFocusQueue
->spwndFocus
) )
964 bMenuDownRecently
= FALSE
; // reset
967 Msg
.message
= WM_SYSKEYDOWN
;
968 if (wSimpleVk
== VK_MENU
)
970 // Note: If only LALT is pressed WM_SYSKEYUP is generated instead of WM_KEYUP
971 bMenuDownRecently
= TRUE
;
975 Msg
.message
= WM_SYSKEYUP
;
980 Msg
.message
= WM_KEYDOWN
;
982 Msg
.message
= WM_KEYUP
;
985 /* Update async state of not simplified vk here.
986 See user32_apitest:GetKeyState */
987 UpdateAsyncKeyState(wFixedVk
, bIsDown
);
989 /* Alt-Tab/Esc Check. Use FocusQueue or RIT Queue */
990 if (bIsSimpleDown
&& !bWasSimpleDown
&&
991 IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
) &&
992 !IS_KEY_DOWN(gafAsyncKeyState
, VK_CONTROL
) &&
993 (wVk
== VK_ESCAPE
|| wVk
== VK_TAB
))
995 TRACE("Alt-Tab/Esc Pressed wParam %x\n",wVk
);
999 * Check Language/Layout Toggle by [Left Alt]+Shift or Ctrl+Shift.
1000 * @see https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc976564%28v=technet.10%29
1002 if (gdwLanguageToggleKey
== 1 || gdwLanguageToggleKey
== 2)
1004 if (wSimpleVk
== VK_SHIFT
) /* Shift key is pressed or released */
1006 UINT targetKey
= ((gdwLanguageToggleKey
== 1) ? VK_LMENU
: VK_CONTROL
);
1007 if (IS_KEY_DOWN(gafAsyncKeyState
, targetKey
))
1008 bLangToggled
= IntCheckLanguageToggle(pFocusQueue
, bIsDown
, wVk
, &gLanguageToggleKeyState
);
1010 else if ((wSimpleVk
== VK_MENU
&& gdwLanguageToggleKey
== 1) ||
1011 (wSimpleVk
== VK_CONTROL
&& gdwLanguageToggleKey
== 2))
1013 if (IS_KEY_DOWN(gafAsyncKeyState
, VK_SHIFT
))
1014 bLangToggled
= IntCheckLanguageToggle(pFocusQueue
, bIsDown
, 0, &gLanguageToggleKeyState
);
1017 if (!bLangToggled
&& (gdwLayoutToggleKey
== 1 || gdwLayoutToggleKey
== 2))
1019 if (wSimpleVk
== VK_SHIFT
) /* Shift key is pressed or released */
1021 UINT targetKey
= ((gdwLayoutToggleKey
== 1) ? VK_LMENU
: VK_CONTROL
);
1022 if (IS_KEY_DOWN(gafAsyncKeyState
, targetKey
))
1023 IntCheckLanguageToggle(pFocusQueue
, bIsDown
, wVk
, &gLayoutToggleKeyState
);
1025 else if ((wSimpleVk
== VK_MENU
&& gdwLayoutToggleKey
== 1) ||
1026 (wSimpleVk
== VK_CONTROL
&& gdwLayoutToggleKey
== 2))
1028 if (IS_KEY_DOWN(gafAsyncKeyState
, VK_SHIFT
))
1029 IntCheckLanguageToggle(pFocusQueue
, bIsDown
, 0, &gLayoutToggleKeyState
);
1033 if (bIsDown
&& wVk
== VK_SNAPSHOT
)
1036 IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
) &&
1037 !IS_KEY_DOWN(gafAsyncKeyState
, VK_CONTROL
))
1039 // Snap from Active Window, Focus can be null.
1040 SnapWindow(pFocusQueue
->spwndActive
? UserHMGetHandle(pFocusQueue
->spwndActive
) : 0);
1043 SnapWindow(NULL
); // Snap Desktop.
1045 else if (pFocusQueue
&& bPostMsg
)
1047 PWND Wnd
= pFocusQueue
->spwndFocus
; // SysInit.....
1049 pti
= pFocusQueue
->ptiKeyboard
;
1051 if (!Wnd
&& pFocusQueue
->spwndActive
) // SysInit.....
1053 // Going with Active. WM_SYSKEYXXX last wine Win test_keyboard_input.
1054 Wnd
= pFocusQueue
->spwndActive
;
1056 if (Wnd
) pti
= Wnd
->head
.pti
;
1059 Msg
.hwnd
= Wnd
? UserHMGetHandle(Wnd
) : NULL
;
1060 Msg
.wParam
= wFixedVk
& 0xFF; /* Note: It's simplified by msg queue */
1061 Msg
.lParam
= MAKELPARAM(1, wScanCode
);
1063 Msg
.pt
= gpsi
->ptCursor
;
1065 if ( Msg
.message
== WM_KEYDOWN
|| Msg
.message
== WM_SYSKEYDOWN
)
1067 if ( (Msg
.wParam
== VK_SHIFT
||
1068 Msg
.wParam
== VK_CONTROL
||
1069 Msg
.wParam
== VK_MENU
) &&
1070 !IS_KEY_DOWN(gafAsyncKeyState
, Msg
.wParam
))
1072 ERR("Set last input\n");
1073 //ptiLastInput = pti;
1077 /* If it is VK_PACKET, high word of wParam is used for wchar */
1081 Msg
.lParam
|= KF_EXTENDED
<< 16;
1082 if (IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
))
1083 Msg
.lParam
|= KF_ALTDOWN
<< 16;
1085 Msg
.lParam
|= KF_REPEAT
<< 16;
1087 Msg
.lParam
|= KF_UP
<< 16;
1088 /* FIXME: Set KF_DLGMODE and KF_MENUMODE when needed */
1089 if (pFocusQueue
->QF_flags
& QF_DIALOGACTIVE
)
1090 Msg
.lParam
|= KF_DLGMODE
<< 16;
1091 if (pFocusQueue
->MenuOwner
) // pti->pMenuState->fMenuStarted
1092 Msg
.lParam
|= KF_MENUMODE
<< 16;
1095 // Post mouse move before posting key buttons, to keep it syned.
1096 if (pFocusQueue
->QF_flags
& QF_MOUSEMOVED
)
1098 IntCoalesceMouseMove(pti
);
1101 /* Post a keyboard message */
1102 TRACE("Posting keyboard msg %u wParam 0x%x lParam 0x%x\n", Msg
.message
, Msg
.wParam
, Msg
.lParam
);
1103 if (!Wnd
) {ERR("Window is NULL\n");}
1104 MsqPostMessage(pti
, &Msg
, TRUE
, QS_KEY
, 0, dwExtraInfo
);
1110 UserSendKeyboardInput(KEYBDINPUT
*pKbdInput
, BOOL bInjected
)
1112 WORD wScanCode
, wVk
;
1115 PUSER_MESSAGE_QUEUE pFocusQueue
;
1117 BOOL bExt
= (pKbdInput
->dwFlags
& KEYEVENTF_EXTENDEDKEY
) ? TRUE
: FALSE
;
1119 gppiInputProvider
= ((PTHREADINFO
)PsGetCurrentThreadWin32Thread())->ppi
;
1121 /* Find the target thread whose locale is in effect */
1122 pFocusQueue
= IntGetFocusMessageQueue();
1124 if (pFocusQueue
&& pFocusQueue
->ptiKeyboard
)
1126 pKl
= pFocusQueue
->ptiKeyboard
->KeyboardLayout
;
1130 pKl
= W32kGetDefaultKeyLayout();
1133 ERR("No keyboard layout!\n");
1137 pKbdTbl
= pKl
->spkf
->pKbdTbl
;
1139 /* Note: wScan field is always used */
1140 wScanCode
= pKbdInput
->wScan
;
1142 if (pKbdInput
->dwFlags
& KEYEVENTF_UNICODE
)
1144 /* Generate WM_KEYDOWN msg with wParam == VK_PACKET and
1145 high order word of lParam == pKbdInput->wScan */
1151 if (pKbdInput
->dwFlags
& KEYEVENTF_SCANCODE
)
1153 /* Don't ignore invalid scan codes */
1154 wVk
= IntVscToVk(wScanCode
| (bExt
? 0xE000 : 0), pKbdTbl
);
1155 if (!wVk
) /* use 0xFF if vsc is invalid */
1160 wVk
= pKbdInput
->wVk
;
1163 /* Remove all virtual key flags (KBDEXT, KBDMULTIVK, KBDSPECIAL, KBDNUMPAD) */
1167 /* If time is given, use it */
1168 if (pKbdInput
->time
)
1169 dwTime
= pKbdInput
->time
;
1172 dwTime
= EngGetTickCount32();
1175 if (wVk
== VK_RMENU
&& (pKbdTbl
->fLocaleFlags
& KLLF_ALTGR
))
1177 /* For AltGr keyboards RALT generates CTRL events */
1178 ProcessKeyEvent(VK_LCONTROL
, 0, pKbdInput
->dwFlags
& KEYEVENTF_KEYUP
, bInjected
, dwTime
, 0);
1181 /* Finally process this key */
1182 return ProcessKeyEvent(wVk
, wScanCode
, pKbdInput
->dwFlags
, bInjected
, dwTime
, pKbdInput
->dwExtraInfo
);
1186 * UserProcessKeyboardInput
1188 * Process raw keyboard input data
1191 UserProcessKeyboardInput(
1192 PKEYBOARD_INPUT_DATA pKbdInputData
)
1194 WORD wScanCode
, wVk
;
1197 PUSER_MESSAGE_QUEUE pFocusQueue
;
1199 /* Calculate scan code with prefix */
1200 wScanCode
= pKbdInputData
->MakeCode
& 0x7F;
1201 if (pKbdInputData
->Flags
& KEY_E0
)
1202 wScanCode
|= 0xE000;
1203 if (pKbdInputData
->Flags
& KEY_E1
)
1204 wScanCode
|= 0xE100;
1206 /* Find the target thread whose locale is in effect */
1207 pFocusQueue
= IntGetFocusMessageQueue();
1209 if (pFocusQueue
&& pFocusQueue
->ptiKeyboard
)
1211 pKl
= pFocusQueue
->ptiKeyboard
->KeyboardLayout
;
1215 pKl
= W32kGetDefaultKeyLayout();
1219 pKbdTbl
= pKl
->spkf
->pKbdTbl
;
1221 /* Convert scan code to virtual key.
1222 Note: We could call UserSendKeyboardInput using scan code,
1223 but it wouldn't interpret E1 key(s) properly */
1224 wVk
= IntVscToVk(wScanCode
, pKbdTbl
);
1225 TRACE("UserProcessKeyboardInput: %x (break: %u) -> %x\n",
1226 wScanCode
, (pKbdInputData
->Flags
& KEY_BREAK
) ? 1u : 0, wVk
);
1230 KEYBDINPUT KbdInput
;
1232 /* Support numlock */
1233 if ((wVk
& KBDNUMPAD
) && IS_KEY_LOCKED(gafAsyncKeyState
, VK_NUMLOCK
))
1235 wVk
= IntTranslateNumpadKey(wVk
& 0xFF);
1238 /* Send keyboard input */
1239 KbdInput
.wVk
= wVk
& 0xFF;
1240 KbdInput
.wScan
= wScanCode
& 0x7F;
1241 KbdInput
.dwFlags
= 0;
1242 if (pKbdInputData
->Flags
& KEY_BREAK
)
1243 KbdInput
.dwFlags
|= KEYEVENTF_KEYUP
;
1246 KbdInput
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
1248 // Based on wine input:test_Input_blackbox this is okay. It seems the
1249 // bit did not get set and more research is needed. Now the right
1252 if (wVk
== VK_RSHIFT
)
1253 KbdInput
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
1256 KbdInput
.dwExtraInfo
= pKbdInputData
->ExtraInformation
;
1257 UserSendKeyboardInput(&KbdInput
, FALSE
);
1259 /* E1 keys don't have break code */
1260 if (pKbdInputData
->Flags
& KEY_E1
)
1262 /* Send key up event */
1263 KbdInput
.dwFlags
|= KEYEVENTF_KEYUP
;
1264 UserSendKeyboardInput(&KbdInput
, FALSE
);
1270 * IntTranslateKbdMessage
1272 * Addes WM_(SYS)CHAR messages to message queue if message
1273 * describes key which produce character.
1276 IntTranslateKbdMessage(LPMSG lpMsg
,
1281 WCHAR wch
[3] = { 0 };
1284 BOOL bResult
= FALSE
;
1286 switch(lpMsg
->message
)
1297 pti
= PsGetCurrentThreadWin32Thread();
1299 if (!pti
->KeyboardLayout
)
1301 PKL pDefKL
= W32kGetDefaultKeyLayout();
1302 UserAssignmentLock((PVOID
*)&(pti
->KeyboardLayout
), pDefKL
);
1305 pti
->pClientInfo
->hKL
= pDefKL
->hkl
;
1306 pKbdTbl
= pDefKL
->spkf
->pKbdTbl
;
1310 pti
->pClientInfo
->hKL
= NULL
;
1315 pKbdTbl
= pti
->KeyboardLayout
->spkf
->pKbdTbl
;
1319 if (lpMsg
->message
!= WM_KEYDOWN
&& lpMsg
->message
!= WM_SYSKEYDOWN
)
1322 /* Init pt, hwnd and time msg fields */
1323 NewMsg
.pt
= gpsi
->ptCursor
;
1324 NewMsg
.hwnd
= lpMsg
->hwnd
;
1325 NewMsg
.time
= EngGetTickCount32();
1327 TRACE("Enter IntTranslateKbdMessage msg %s, vk %x\n",
1328 lpMsg
->message
== WM_SYSKEYDOWN
? "WM_SYSKEYDOWN" : "WM_KEYDOWN", lpMsg
->wParam
);
1330 if (lpMsg
->wParam
== VK_PACKET
)
1332 NewMsg
.message
= (lpMsg
->message
== WM_KEYDOWN
) ? WM_CHAR
: WM_SYSCHAR
;
1333 NewMsg
.wParam
= HIWORD(lpMsg
->lParam
);
1334 NewMsg
.lParam
= LOWORD(lpMsg
->lParam
);
1335 MsqPostMessage(pti
, &NewMsg
, FALSE
, QS_KEY
, 0, 0);
1339 cch
= IntToUnicodeEx(lpMsg
->wParam
,
1340 HIWORD(lpMsg
->lParam
) & 0xFF,
1341 pti
->MessageQueue
->afKeyState
,
1343 sizeof(wch
) / sizeof(wch
[0]),
1349 if (cch
> 0) /* Normal characters */
1350 NewMsg
.message
= (lpMsg
->message
== WM_KEYDOWN
) ? WM_CHAR
: WM_SYSCHAR
;
1351 else /* Dead character */
1355 (lpMsg
->message
== WM_KEYDOWN
) ? WM_DEADCHAR
: WM_SYSDEADCHAR
;
1357 NewMsg
.lParam
= lpMsg
->lParam
;
1359 /* Send all characters */
1360 for (i
= 0; i
< cch
; ++i
)
1362 TRACE("Msg: %x '%lc' (%04x) %08x\n", NewMsg
.message
, wch
[i
], wch
[i
], NewMsg
.lParam
);
1363 NewMsg
.wParam
= wch
[i
];
1364 MsqPostMessage(pti
, &NewMsg
, FALSE
, QS_KEY
, 0, 0);
1369 TRACE("Leave IntTranslateKbdMessage ret %d, cch %d, msg %x, wch %x\n",
1370 bResult
, cch
, NewMsg
.message
, NewMsg
.wParam
);
1375 * Map a virtual key code, or virtual scan code, to a scan code, key code,
1376 * or unshifted unicode character.
1380 * 0 -- Code is a virtual key code that is converted into a virtual scan code
1381 * that does not distinguish between left and right shift keys.
1382 * 1 -- Code is a virtual scan code that is converted into a virtual key code
1383 * that does not distinguish between left and right shift keys.
1384 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
1386 * 3 -- Code is a virtual scan code that is converted into a virtual key code
1387 * that distinguishes left and right shift keys.
1388 * KeyLayout: Keyboard layout handle
1393 IntMapVirtualKeyEx(UINT uCode
, UINT Type
, PKBDTABLES pKbdTbl
)
1399 case MAPVK_VK_TO_VSC
:
1400 uCode
= IntFixVk(uCode
, FALSE
);
1401 uRet
= IntVkToVsc(uCode
, pKbdTbl
);
1402 if (uRet
> 0xFF) // Fail for scancodes with prefix (e0, e1)
1406 case MAPVK_VSC_TO_VK
:
1407 uRet
= IntVscToVk(uCode
, pKbdTbl
) & 0xFF;
1408 uRet
= IntSimplifyVk(uRet
);
1411 case MAPVK_VK_TO_CHAR
:
1412 uRet
= (UINT
)IntVkToChar(uCode
, pKbdTbl
);
1415 case MAPVK_VSC_TO_VK_EX
:
1416 uRet
= IntVscToVk(uCode
, pKbdTbl
) & 0xFF;
1419 case MAPVK_VK_TO_VSC_EX
:
1420 uRet
= IntVkToVsc(uCode
, pKbdTbl
);
1424 EngSetLastError(ERROR_INVALID_PARAMETER
);
1425 ERR("Wrong type value: %u\n", Type
);
1432 * NtUserMapVirtualKeyEx
1434 * Map a virtual key code, or virtual scan code, to a scan code, key code,
1435 * or unshifted unicode character. See IntMapVirtualKeyEx.
1439 NtUserMapVirtualKeyEx(UINT uCode
, UINT uType
, DWORD keyboardId
, HKL dwhkl
)
1441 PKBDTABLES pKbdTbl
= NULL
;
1444 TRACE("Enter NtUserMapVirtualKeyEx\n");
1451 pti
= PsGetCurrentThreadWin32Thread();
1452 if (pti
&& pti
->KeyboardLayout
)
1453 pKbdTbl
= pti
->KeyboardLayout
->spkf
->pKbdTbl
;
1459 pKl
= UserHklToKbl(dwhkl
);
1461 pKbdTbl
= pKl
->spkf
->pKbdTbl
;
1465 ret
= IntMapVirtualKeyEx(uCode
, uType
, pKbdTbl
);
1468 TRACE("Leave NtUserMapVirtualKeyEx, ret=%u\n", ret
);
1475 * Translates virtual key to characters
1482 PBYTE pKeyStateUnsafe
,
1483 LPWSTR pwszBuffUnsafe
,
1489 BYTE afKeyState
[256 * 2 / 8] = {0};
1490 PWCHAR pwszBuff
= NULL
;
1493 NTSTATUS Status
= STATUS_SUCCESS
;
1495 TRACE("Enter NtUserSetKeyboardState\n");
1497 /* Return 0 if SC_KEY_UP bit is set */
1498 if (wScanCode
& SC_KEY_UP
|| wVirtKey
>= 0x100)
1500 ERR("Invalid parameter\n");
1506 /* Probe and copy key state to smaller bitmap */
1507 ProbeForRead(pKeyStateUnsafe
, 256 * sizeof(BYTE
), 1);
1508 for (i
= 0; i
< 256; ++i
)
1510 if (pKeyStateUnsafe
[i
] & KS_DOWN_BIT
)
1511 SET_KEY_DOWN(afKeyState
, i
, TRUE
);
1512 if (pKeyStateUnsafe
[i
] & KS_LOCK_BIT
)
1513 SET_KEY_LOCKED(afKeyState
, i
, TRUE
);
1516 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1518 ERR("Cannot copy key state\n");
1519 SetLastNtError(_SEH2_GetExceptionCode());
1520 _SEH2_YIELD(return 0);
1524 pwszBuff
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(WCHAR
) * cchBuff
, TAG_STRING
);
1527 ERR("ExAllocatePoolWithTag(%u) failed\n", sizeof(WCHAR
) * cchBuff
);
1530 RtlZeroMemory(pwszBuff
, sizeof(WCHAR
) * cchBuff
);
1532 UserEnterExclusive(); // Note: We modify wchDead static variable
1535 pKl
= UserHklToKbl(dwhkl
);
1539 pti
= PsGetCurrentThreadWin32Thread();
1540 pKl
= pti
->KeyboardLayout
;
1545 iRet
= IntToUnicodeEx(wVirtKey
,
1551 pKl
->spkf
->pKbdTbl
);
1555 Status
= MmCopyToCaller(pwszBuffUnsafe
, pwszBuff
, cchBuff
* sizeof(WCHAR
));
1560 ERR("No keyboard layout ?!\n");
1561 Status
= STATUS_INVALID_HANDLE
;
1564 ExFreePoolWithTag(pwszBuff
, TAG_STRING
);
1566 if (!NT_SUCCESS(Status
))
1569 SetLastNtError(Status
);
1573 TRACE("Leave NtUserSetKeyboardState, ret=%i\n", iRet
);
1578 * NtUserGetKeyNameText
1580 * Gets key name from keyboard layout
1584 NtUserGetKeyNameText(LONG lParam
, LPWSTR lpString
, int cchSize
)
1589 WORD wScanCode
= (lParam
>> 16) & 0xFF;
1590 BOOL bExtKey
= (HIWORD(lParam
) & KF_EXTENDED
) ? TRUE
: FALSE
;
1592 VSC_LPWSTR
*pKeyNames
= NULL
;
1593 CONST WCHAR
*pKeyName
= NULL
;
1594 WCHAR KeyNameBuf
[2];
1596 TRACE("Enter NtUserGetKeyNameText\n");
1600 /* Get current keyboard layout */
1601 pti
= PsGetCurrentThreadWin32Thread();
1602 pKbdTbl
= pti
? pti
->KeyboardLayout
->spkf
->pKbdTbl
: 0;
1604 if (!pKbdTbl
|| cchSize
< 1)
1606 ERR("Invalid parameter\n");
1610 /* "Do not care" flag */
1611 if(lParam
& LP_DO_NOT_CARE_BIT
)
1613 /* Note: We could do vsc -> vk -> vsc conversion, instead of using
1614 hardcoded scan codes, but it's not what Windows does */
1615 if (wScanCode
== SCANCODE_RSHIFT
&& !bExtKey
)
1616 wScanCode
= SCANCODE_LSHIFT
;
1617 else if (wScanCode
== SCANCODE_CTRL
|| wScanCode
== SCANCODE_ALT
)
1622 pKeyNames
= pKbdTbl
->pKeyNamesExt
;
1624 pKeyNames
= pKbdTbl
->pKeyNames
;
1626 for (i
= 0; pKeyNames
[i
].pwsz
; i
++)
1628 if (pKeyNames
[i
].vsc
== wScanCode
)
1630 pKeyName
= pKeyNames
[i
].pwsz
;
1637 WORD wVk
= IntVscToVk(wScanCode
, pKbdTbl
);
1641 KeyNameBuf
[0] = IntVkToChar(wVk
, pKbdTbl
);
1644 pKeyName
= KeyNameBuf
;
1650 cchKeyName
= wcslen(pKeyName
);
1651 if (cchKeyName
> (cchSize
- 1UL))
1652 cchKeyName
= cchSize
- 1UL; // Don't count '\0'
1656 ProbeForWrite(lpString
, (cchKeyName
+ 1) * sizeof(WCHAR
), 1);
1657 RtlCopyMemory(lpString
, pKeyName
, cchKeyName
* sizeof(WCHAR
));
1658 lpString
[cchKeyName
] = UNICODE_NULL
;
1661 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1663 SetLastNtError(_SEH2_GetExceptionCode());
1669 EngSetLastError(ERROR_INVALID_PARAMETER
);
1674 TRACE("Leave NtUserGetKeyNameText, ret=%lu\n", dwRet
);
1679 * UserGetKeyboardType
1681 * Returns some keyboard specific information
1684 UserGetKeyboardType(
1689 case 0: /* Keyboard type */
1690 return (DWORD
)gKeyboardInfo
.KeyboardIdentifier
.Type
;
1691 case 1: /* Keyboard Subtype */
1692 return (DWORD
)gKeyboardInfo
.KeyboardIdentifier
.Subtype
;
1693 case 2: /* Number of F-keys */
1694 return (DWORD
)gKeyboardInfo
.NumberOfFunctionKeys
;
1696 ERR("Unknown type!\n");
1697 return 0; /* Note: we don't have to set last error here */
1704 * Based on IntTranslateChar, instead of processing VirtualKey match,
1705 * look for wChar match.
1715 PVK_TO_WCHAR_TABLE pVkToWchTbl
;
1716 PVK_TO_WCHARS10 pVkToWch
;
1718 DWORD i
, dwModBits
= 0, dwModNumber
= 0, Ret
= (DWORD
)-1;
1720 TRACE("NtUserVkKeyScanEx() wch %u, KbdLayout 0x%p\n", wch
, dwhkl
);
1725 // Use given keyboard layout
1727 pKl
= UserHklToKbl(dwhkl
);
1731 // Use thread keyboard layout
1732 pKl
= ((PTHREADINFO
)PsGetCurrentThreadWin32Thread())->KeyboardLayout
;
1738 pKbdTbl
= pKl
->spkf
->pKbdTbl
;
1740 // Interate through all VkToWchar tables while pVkToWchars is not NULL
1741 for (i
= 0; pKbdTbl
->pVkToWcharTable
[i
].pVkToWchars
; i
++)
1743 pVkToWchTbl
= &pKbdTbl
->pVkToWcharTable
[i
];
1744 pVkToWch
= (PVK_TO_WCHARS10
)(pVkToWchTbl
->pVkToWchars
);
1746 // Interate through all virtual keys
1747 while (pVkToWch
->VirtualKey
)
1749 for (dwModNumber
= 0; dwModNumber
< pVkToWchTbl
->nModifications
; dwModNumber
++)
1751 if (pVkToWch
->wch
[dwModNumber
] == wch
)
1753 dwModBits
= pKbdTbl
->pCharModifiers
->ModNumber
[dwModNumber
];
1754 TRACE("i %lu wC %04x: dwModBits %08x dwModNumber %08x MaxModBits %08x\n",
1755 i
, wch
, dwModBits
, dwModNumber
, pKbdTbl
->pCharModifiers
->wMaxModBits
);
1756 Ret
= (dwModBits
<< 8) | (pVkToWch
->VirtualKey
& 0xFF);
1760 pVkToWch
= (PVK_TO_WCHARS10
)(((BYTE
*)pVkToWch
) + pVkToWchTbl
->cbSize
);