2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Keyboard functions
5 * FILE: subsystems/win32/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
[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};
18 /* FUNCTIONS *****************************************************************/
23 * Initialization -- Right now, just zero the key state
28 InitKeyboardImpl(VOID
)
30 RtlZeroMemory(&gafAsyncKeyState
, sizeof(gafAsyncKeyState
));
31 RtlZeroMemory(&gafAsyncKeyStateRecentDown
, sizeof(gafAsyncKeyStateRecentDown
));
32 return STATUS_SUCCESS
;
36 * IntKeyboardGetIndicatorTrans
38 * Asks the keyboard driver to send a small table that shows which
39 * lights should connect with which scancodes
43 IntKeyboardGetIndicatorTrans(HANDLE hKeyboardDevice
,
44 PKEYBOARD_INDICATOR_TRANSLATION
*ppIndicatorTrans
)
48 IO_STATUS_BLOCK Block
;
49 PKEYBOARD_INDICATOR_TRANSLATION pRet
;
51 dwSize
= sizeof(KEYBOARD_INDICATOR_TRANSLATION
);
53 pRet
= ExAllocatePoolWithTag(PagedPool
,
59 Status
= NtDeviceIoControlFile(hKeyboardDevice
,
64 IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION
,
68 if (Status
!= STATUS_BUFFER_TOO_SMALL
)
71 ExFreePoolWithTag(pRet
, USERTAG_KBDTABLE
);
73 dwSize
+= sizeof(KEYBOARD_INDICATOR_TRANSLATION
);
75 pRet
= ExAllocatePoolWithTag(PagedPool
,
81 return STATUS_INSUFFICIENT_RESOURCES
;
83 if (!NT_SUCCESS(Status
))
85 ExFreePoolWithTag(pRet
, USERTAG_KBDTABLE
);
89 *ppIndicatorTrans
= pRet
;
94 * IntKeyboardUpdateLeds
96 * Sends the keyboard commands to turn on/off the lights
100 IntKeyboardUpdateLeds(HANDLE hKeyboardDevice
,
108 IO_STATUS_BLOCK Block
;
110 if (!gpKeyboardIndicatorTrans
)
111 return STATUS_NOT_SUPPORTED
;
115 case VK_CAPITAL
: LedFlag
= KEYBOARD_CAPS_LOCK_ON
; break;
116 case VK_NUMLOCK
: LedFlag
= KEYBOARD_NUM_LOCK_ON
; break;
117 case VK_SCROLL
: LedFlag
= KEYBOARD_SCROLL_LOCK_ON
; break;
119 for (i
= 0; i
< gpKeyboardIndicatorTrans
->NumberOfIndicatorKeys
; i
++)
121 if (gpKeyboardIndicatorTrans
->IndicatorList
[i
].MakeCode
== wScanCode
)
123 LedFlag
= gpKeyboardIndicatorTrans
->IndicatorList
[i
].IndicatorFlags
;
132 gIndicators
.LedFlags
|= LedFlag
;
134 gIndicators
.LedFlags
= ~LedFlag
;
136 /* Update the lights on the hardware */
137 Status
= NtDeviceIoControlFile(hKeyboardDevice
,
142 IOCTL_KEYBOARD_SET_INDICATORS
,
143 &gIndicators
, sizeof(gIndicators
),
149 return STATUS_SUCCESS
;
155 * Initializes keyboard indicators translation and their state
158 UserInitKeyboard(HANDLE hKeyboardDevice
)
161 IO_STATUS_BLOCK Block
;
163 IntKeyboardGetIndicatorTrans(hKeyboardDevice
, &gpKeyboardIndicatorTrans
);
165 Status
= NtDeviceIoControlFile(hKeyboardDevice
,
170 IOCTL_KEYBOARD_QUERY_INDICATORS
,
172 &gIndicators
, sizeof(gIndicators
));
174 if (!NT_SUCCESS(Status
))
176 WARN("NtDeviceIoControlFile() failed, ignored\n");
179 SET_KEY_LOCKED(gafAsyncKeyState
, VK_CAPITAL
,
180 gIndicators
.LedFlags
& KEYBOARD_CAPS_LOCK_ON
);
181 SET_KEY_LOCKED(gafAsyncKeyState
, VK_NUMLOCK
,
182 gIndicators
.LedFlags
& KEYBOARD_NUM_LOCK_ON
);
183 SET_KEY_LOCKED(gafAsyncKeyState
, VK_SCROLL
,
184 gIndicators
.LedFlags
& KEYBOARD_SCROLL_LOCK_ON
);
190 * Changes virtual keys which distinguish between left and right hand, to keys which don't distinguish
194 IntSimplifyVk(WORD wVk
)
218 * Changes virtual keys which don't not distinguish between left and right hand to proper keys
222 IntFixVk(WORD wVk
, BOOL bExt
)
227 return bExt
? VK_RSHIFT
: VK_LSHIFT
;
230 return bExt
? VK_RCONTROL
: VK_LCONTROL
;
233 return bExt
? VK_RMENU
: VK_LMENU
;
241 * IntTranslateNumpadKey
243 * Translates numpad keys when numlock is enabled
247 IntTranslateNumpadKey(WORD wVk
)
251 case VK_INSERT
: return VK_NUMPAD0
;
252 case VK_END
: return VK_NUMPAD1
;
253 case VK_DOWN
: return VK_NUMPAD2
;
254 case VK_NEXT
: return VK_NUMPAD3
;
255 case VK_LEFT
: return VK_NUMPAD4
;
256 case VK_CLEAR
: return VK_NUMPAD5
;
257 case VK_RIGHT
: return VK_NUMPAD6
;
258 case VK_HOME
: return VK_NUMPAD7
;
259 case VK_UP
: return VK_NUMPAD8
;
260 case VK_PRIOR
: return VK_NUMPAD9
;
261 case VK_DELETE
: return VK_DECIMAL
;
269 * Gets layout specific modification bits, for example KBDSHIFT, KBDCTRL, KBDALT
273 IntGetModBits(PKBDTABLES pKbdTbl
, PBYTE pKeyState
)
275 DWORD i
, dwModBits
= 0;
277 /* DumpKeyState( KeyState ); */
279 for (i
= 0; pKbdTbl
->pCharModifiers
->pVkToBit
[i
].Vk
; i
++)
280 if (IS_KEY_DOWN(pKeyState
, pKbdTbl
->pCharModifiers
->pVkToBit
[i
].Vk
))
281 dwModBits
|= pKbdTbl
->pCharModifiers
->pVkToBit
[i
].ModBits
;
283 TRACE("Current Mod Bits: %lx\n", dwModBits
);
291 * Translates virtual key to character
295 IntTranslateChar(WORD wVirtKey
,
299 PWCHAR pwcTranslatedChar
,
302 PVK_TO_WCHAR_TABLE pVkToVchTbl
;
303 PVK_TO_WCHARS10 pVkToVch
;
304 DWORD i
, dwModBits
, dwVkModBits
, dwModNumber
= 0;
309 dwModBits
= pKeyState
? IntGetModBits(pKbdTbl
, pKeyState
) : 0;
310 bAltGr
= pKeyState
&& (pKbdTbl
->fLocaleFlags
& KLLF_ALTGR
) && IS_KEY_DOWN(pKeyState
, VK_RMENU
);
311 wCaplokAttr
= bAltGr
? CAPLOKALTGR
: CAPLOK
;
313 TRACE("TryToTranslate: %04x %x\n", wVirtKey
, dwModBits
);
315 /* If ALT without CTRL has ben used, remove ALT flag */
316 if ((dwModBits
& (KBDALT
|KBDCTRL
)) == KBDALT
)
317 dwModBits
&= ~KBDALT
;
319 if (dwModBits
> pKbdTbl
->pCharModifiers
->wMaxModBits
)
321 TRACE("dwModBits %x > wMaxModBits %x\n", dwModBits
, pKbdTbl
->pCharModifiers
->wMaxModBits
);
325 for (i
= 0; pKbdTbl
->pVkToWcharTable
[i
].pVkToWchars
; i
++)
327 pVkToVchTbl
= &pKbdTbl
->pVkToWcharTable
[i
];
328 pVkToVch
= (PVK_TO_WCHARS10
)(pVkToVchTbl
->pVkToWchars
);
329 while (pVkToVch
->VirtualKey
)
331 if (wVirtKey
== (pVkToVch
->VirtualKey
& 0xFF))
333 dwVkModBits
= dwModBits
;
335 /* If CapsLock is enabled for this key and locked, add SHIFT bit */
336 if ((pVkToVch
->Attributes
& wCaplokAttr
) &&
338 IS_KEY_LOCKED(pKeyState
, VK_CAPITAL
))
340 /* Note: we use special value here instead of getting VK_SHIFT mod bit - it's verified */
341 dwVkModBits
^= KBDSHIFT
;
344 if (dwVkModBits
> pKbdTbl
->pCharModifiers
->wMaxModBits
)
347 /* Get modification number */
348 dwModNumber
= pKbdTbl
->pCharModifiers
->ModNumber
[dwVkModBits
];
349 if (dwModNumber
>= pVkToVchTbl
->nModifications
)
351 TRACE("dwModNumber %u >= nModifications %u\n", dwModNumber
, pVkToVchTbl
->nModifications
);
356 wch
= pVkToVch
->wch
[dwModNumber
];
360 *pbDead
= (wch
== WCH_DEAD
);
361 *pbLigature
= (wch
== WCH_LGTR
);
362 *pwcTranslatedChar
= wch
;
364 TRACE("%d %04x: dwModNumber %08x Char %04x\n",
365 i
, wVirtKey
, dwModNumber
, wch
);
369 /* After WCH_DEAD, real character is located */
370 pVkToVch
= (PVK_TO_WCHARS10
)(((BYTE
*)pVkToVch
) + pVkToVchTbl
->cbSize
);
371 if (pVkToVch
->VirtualKey
!= 0xFF)
373 WARN("Found dead key with no trailer in the table.\n");
374 WARN("VK: %04x, ADDR: %p\n", wVirtKey
, pVkToVch
);
377 *pwcTranslatedChar
= pVkToVch
->wch
[dwModNumber
];
381 pVkToVch
= (PVK_TO_WCHARS10
)(((BYTE
*)pVkToVch
) + pVkToVchTbl
->cbSize
);
385 /* If nothing has been found in layout, check if this is ASCII control character.
386 Note: we could add it to layout table, but windows does not have it there */
387 if (wVirtKey
>= 'A' && wVirtKey
<= 'Z' &&
388 IS_KEY_DOWN(pKeyState
, VK_CONTROL
) &&
389 !IS_KEY_DOWN(pKeyState
, VK_MENU
))
391 *pwcTranslatedChar
= (wVirtKey
- 'A') + 1; /* ASCII control character */
403 * Translates virtual key to characters
407 IntToUnicodeEx(UINT wVirtKey
,
415 WCHAR wchTranslatedChar
;
416 BOOL bDead
, bLigature
;
417 static WCHAR wchDead
= 0;
422 if (!IntTranslateChar(wVirtKey
,
434 WARN("Not handling ligature (yet)\n" );
438 /* If we got dead char in previous call check dead keys in keyboard layout */
442 WCHAR wchFirst
, wchSecond
;
443 TRACE("Previous dead char: %lc (%x)\n", wchDead
, wchDead
);
445 for (i
= 0; pKbdTbl
->pDeadKey
[i
].dwBoth
; i
++)
447 wchFirst
= pKbdTbl
->pDeadKey
[i
].dwBoth
>> 16;
448 wchSecond
= pKbdTbl
->pDeadKey
[i
].dwBoth
& 0xFFFF;
449 if (wchFirst
== wchDead
&& wchSecond
== wchTranslatedChar
)
451 wchTranslatedChar
= pKbdTbl
->pDeadKey
[i
].wchComposed
;
458 TRACE("Final char: %lc (%x)\n", wchTranslatedChar
, wchTranslatedChar
);
461 /* Dead char has not been not found */
464 /* Treat both characters normally */
466 pwszBuff
[iRet
++] = wchDead
;
470 /* Add character to the buffer */
472 pwszBuff
[iRet
++] = wchTranslatedChar
;
474 /* Save dead character */
475 wchDead
= bDead
? wchTranslatedChar
: 0;
477 return bDead
? -iRet
: iRet
;
483 * Translates virtual key to scan code
487 IntVkToVsc(WORD wVk
, PKBDTABLES pKbdTbl
)
493 /* Check standard keys first */
494 for (i
= 0; i
< pKbdTbl
->bMaxVSCtoVK
; i
++)
496 if ((pKbdTbl
->pusVSCtoVK
[i
] & 0xFF) == wVk
)
500 /* Check extended keys now */
501 for (i
= 0; pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
; i
++)
503 if ((pKbdTbl
->pVSCtoVK_E0
[i
].Vk
& 0xFF) == wVk
)
504 return 0xE000 | pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
;
507 for (i
= 0; pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
; i
++)
509 if ((pKbdTbl
->pVSCtoVK_E1
[i
].Vk
& 0xFF) == wVk
)
510 return 0xE100 | pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
;
513 /* Virtual key has not been found */
520 * Translates prefixed scancode to virtual key
524 IntVscToVk(WORD wScanCode
, PKBDTABLES pKbdTbl
)
531 if ((wScanCode
& 0xFF00) == 0xE000)
533 for (i
= 0; pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
; i
++)
535 if (pKbdTbl
->pVSCtoVK_E0
[i
].Vsc
== (wScanCode
& 0xFF))
537 wVk
= pKbdTbl
->pVSCtoVK_E0
[i
].Vk
;
541 else if ((wScanCode
& 0xFF00) == 0xE100)
543 for (i
= 0; pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
; i
++)
545 if (pKbdTbl
->pVSCtoVK_E1
[i
].Vsc
== (wScanCode
& 0xFF))
547 wVk
= pKbdTbl
->pVSCtoVK_E1
[i
].Vk
;
551 else if (wScanCode
< pKbdTbl
->bMaxVSCtoVK
)
553 wVk
= pKbdTbl
->pusVSCtoVK
[wScanCode
];
556 /* 0xFF nad 0x00 are invalid VKs */
557 return wVk
!= 0xFF ? wVk
: 0;
563 * Translates virtual key to character, ignoring shift state
567 IntVkToChar(WORD wVk
, PKBDTABLES pKbdTbl
)
570 BOOL bDead
, bLigature
;
574 if (IntTranslateChar(wVk
,
588 * NtUserGetAsyncKeyState
590 * Gets key state from global bitmap
594 NtUserGetAsyncKeyState(INT Key
)
598 TRACE("Enter NtUserGetAsyncKeyState\n");
602 EngSetLastError(ERROR_INVALID_PARAMETER
);
603 ERR("Invalid parameter Key\n");
607 UserEnterExclusive();
609 if (IS_KEY_DOWN(gafAsyncKeyState
, Key
))
610 wRet
|= 0x8000; // If down, windows returns 0x8000.
611 if (gafAsyncKeyStateRecentDown
[Key
/ 8] & (1 << (Key
% 8)))
613 gafAsyncKeyStateRecentDown
[Key
/ 8] &= ~(1 << (Key
% 8));
617 TRACE("Leave NtUserGetAsyncKeyState, ret=%i\n", wRet
);
622 * UpdateAsyncKeyState
624 * Updates gafAsyncKeyState array
628 UpdateAsyncKeyState(WORD wVk
, BOOL bIsDown
)
632 /* If it's first key down event, xor lock bit */
633 if (!IS_KEY_DOWN(gafAsyncKeyState
, wVk
))
634 SET_KEY_LOCKED(gafAsyncKeyState
, wVk
, !IS_KEY_LOCKED(gafAsyncKeyState
, wVk
));
636 SET_KEY_DOWN(gafAsyncKeyState
, wVk
, TRUE
);
637 gafAsyncKeyStateRecentDown
[wVk
/ 8] |= (1 << (wVk
% 8));
640 SET_KEY_DOWN(gafAsyncKeyState
, wVk
, FALSE
);
644 * co_CallLowLevelKeyboardHook
646 * Calls WH_KEYBOARD_LL hook
649 co_CallLowLevelKeyboardHook(WORD wVk
, WORD wScanCode
, DWORD dwFlags
, BOOL bInjected
, DWORD dwTime
, DWORD dwExtraInfo
)
651 KBDLLHOOKSTRUCT KbdHookData
;
654 KbdHookData
.vkCode
= wVk
;
655 KbdHookData
.scanCode
= wScanCode
;
656 KbdHookData
.flags
= 0;
657 if (dwFlags
& KEYEVENTF_EXTENDEDKEY
)
658 KbdHookData
.flags
|= LLKHF_EXTENDED
;
659 if (IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
))
660 KbdHookData
.flags
|= LLKHF_ALTDOWN
;
661 if (dwFlags
& KEYEVENTF_KEYUP
)
662 KbdHookData
.flags
|= LLKHF_UP
;
664 KbdHookData
.flags
|= LLKHF_INJECTED
;
665 KbdHookData
.time
= dwTime
;
666 KbdHookData
.dwExtraInfo
= dwExtraInfo
;
668 /* Note: it doesnt support WM_SYSKEYUP */
669 if (dwFlags
& KEYEVENTF_KEYUP
)
671 else if (IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
) && !IS_KEY_DOWN(gafAsyncKeyState
, VK_CONTROL
))
672 uMsg
= WM_SYSKEYDOWN
;
676 return co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, uMsg
, (LPARAM
)&KbdHookData
);
682 * Saves snapshot of specified window or whole screen in the clipboard
685 SnapWindow(HWND hWnd
)
687 HBITMAP hbm
= NULL
, hbmOld
;
688 HDC hdc
= NULL
, hdcMem
;
693 TRACE("SnapWindow(%p)\n", hWnd
);
695 /* If no windows is given, make snapshot of desktop window */
697 hWnd
= IntGetDesktopWindow();
699 pWnd
= UserGetWindowObject(hWnd
);
702 ERR("Invalid window\n");
706 hdc
= UserGetDCEx(pWnd
, NULL
, DCX_USESTYLE
| DCX_WINDOW
);
709 ERR("UserGetDCEx failed!\n");
713 cx
= pWnd
->rcWindow
.right
- pWnd
->rcWindow
.left
;
714 cy
= pWnd
->rcWindow
.bottom
- pWnd
->rcWindow
.top
;
716 hbm
= NtGdiCreateCompatibleBitmap(hdc
, cx
, cy
);
719 ERR("NtGdiCreateCompatibleBitmap failed!\n");
723 hdcMem
= NtGdiCreateCompatibleDC(hdc
);
726 ERR("NtGdiCreateCompatibleDC failed!\n");
730 hbmOld
= NtGdiSelectBitmap(hdcMem
, hbm
);
731 NtGdiBitBlt(hdcMem
, 0, 0, cx
, cy
, hdc
, 0, 0, SRCCOPY
, 0, 0);
732 NtGdiSelectBitmap(hdcMem
, hbmOld
);
733 IntGdiDeleteDC(hdcMem
, FALSE
);
735 /* Save snapshot in clipboard */
736 if (UserOpenClipboard(NULL
))
738 UserEmptyClipboard();
739 scd
.fIncSerialNumber
= TRUE
;
740 scd
.fGlobalHandle
= FALSE
;
741 if (UserSetClipboardData(CF_BITMAP
, hbm
, &scd
))
743 /* Bitmap is managed by system now */
746 UserCloseClipboard();
751 GreDeleteObject(hbm
);
753 UserReleaseDC(pWnd
, hdc
, FALSE
);
757 * UserSendKeyboardInput
759 * Process keyboard input from input devices and SendInput API
762 ProcessKeyEvent(WORD wVk
, WORD wScanCode
, DWORD dwFlags
, BOOL bInjected
, DWORD dwTime
, DWORD dwExtraInfo
)
764 WORD wSimpleVk
= 0, wFixedVk
, wVk2
;
765 PUSER_MESSAGE_QUEUE pFocusQueue
;
766 BOOL bExt
= (dwFlags
& KEYEVENTF_EXTENDEDKEY
) ? TRUE
: FALSE
;
767 BOOL bIsDown
= (dwFlags
& KEYEVENTF_KEYUP
) ? FALSE
: TRUE
;
768 BOOL bPacket
= (dwFlags
& KEYEVENTF_UNICODE
) ? TRUE
: FALSE
;
769 BOOL bWasSimpleDown
= FALSE
, bPostMsg
= TRUE
, bIsSimpleDown
;
771 static BOOL bMenuDownRecently
= FALSE
;
773 /* Get virtual key without shifts (VK_(L|R)* -> VK_*) */
774 wSimpleVk
= IntSimplifyVk(wVk
);
775 bWasSimpleDown
= IS_KEY_DOWN(gafAsyncKeyState
, wSimpleVk
);
777 /* Update key without shifts */
778 wVk2
= IntFixVk(wSimpleVk
, !bExt
);
779 bIsSimpleDown
= bIsDown
|| IS_KEY_DOWN(gafAsyncKeyState
, wVk2
);
780 UpdateAsyncKeyState(wSimpleVk
, bIsSimpleDown
);
784 /* Update keyboard LEDs */
785 IntKeyboardUpdateLeds(ghKeyboardDevice
,
788 IS_KEY_LOCKED(gafAsyncKeyState
, wSimpleVk
));
791 /* Call WH_KEYBOARD_LL hook */
792 if (co_CallLowLevelKeyboardHook(wVk
, wScanCode
, dwFlags
, bInjected
, dwTime
, dwExtraInfo
))
794 ERR("Kbd msg dropped by WH_KEYBOARD_LL hook\n");
798 /* Check if this is a hotkey */
799 if (co_UserProcessHotKeys(wSimpleVk
, bIsDown
))
802 wFixedVk
= IntFixVk(wSimpleVk
, bExt
); /* LSHIFT + EXT = RSHIFT */
803 if (wSimpleVk
== VK_SHIFT
) /* shift can't be extended */
806 /* If it is F10 or ALT is down and CTRL is up, it's a system key */
808 (wSimpleVk
== VK_MENU
&& bMenuDownRecently
) ||
809 (IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
) &&
810 !IS_KEY_DOWN(gafAsyncKeyState
, VK_CONTROL
)))
812 bMenuDownRecently
= FALSE
; // reset
815 Msg
.message
= WM_SYSKEYDOWN
;
816 if (wSimpleVk
== VK_MENU
)
818 // Note: If only LALT is pressed WM_SYSKEYUP is generated instead of WM_KEYUP
819 bMenuDownRecently
= TRUE
;
823 Msg
.message
= WM_SYSKEYUP
;
828 Msg
.message
= WM_KEYDOWN
;
830 Msg
.message
= WM_KEYUP
;
833 /* Update async state of not simplified vk here.
834 See user32_apitest:GetKeyState */
835 UpdateAsyncKeyState(wFixedVk
, bIsDown
);
837 /* Alt-Tab/Esc Check. Use FocusQueue or RIT Queue */
838 if (bIsSimpleDown
&& !bWasSimpleDown
&&
839 IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
) &&
840 !IS_KEY_DOWN(gafAsyncKeyState
, VK_CONTROL
) &&
841 (wVk
== VK_ESCAPE
|| wVk
== VK_TAB
))
843 TRACE("Alt-Tab/Esc Pressed wParam %x\n",wVk
);
846 /* If we have a focus queue, post a keyboard message */
847 pFocusQueue
= IntGetFocusMessageQueue();
848 if (bIsDown
&& wVk
== VK_SNAPSHOT
)
851 IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
) &&
852 !IS_KEY_DOWN(gafAsyncKeyState
, VK_CONTROL
))
854 SnapWindow(pFocusQueue
->spwndFocus
? UserHMGetHandle(pFocusQueue
->spwndFocus
) : 0);
859 else if (pFocusQueue
&& bPostMsg
)
862 Msg
.hwnd
= pFocusQueue
->spwndFocus
? UserHMGetHandle(pFocusQueue
->spwndFocus
) : 0;
863 Msg
.wParam
= wFixedVk
& 0xFF; /* Note: It's simplified by msg queue */
864 Msg
.lParam
= MAKELPARAM(1, wScanCode
);
866 Msg
.pt
= gpsi
->ptCursor
;
868 /* If it is VK_PACKET, high word of wParam is used for wchar */
872 Msg
.lParam
|= KF_EXTENDED
<< 16;
873 if (IS_KEY_DOWN(gafAsyncKeyState
, VK_MENU
))
874 Msg
.lParam
|= KF_ALTDOWN
<< 16;
876 Msg
.lParam
|= KF_REPEAT
<< 16;
878 Msg
.lParam
|= KF_UP
<< 16;
879 /* FIXME: Set KF_DLGMODE and KF_MENUMODE when needed */
880 if (pFocusQueue
->QF_flags
& QF_DIALOGACTIVE
)
881 Msg
.lParam
|= KF_DLGMODE
<< 16;
882 if (pFocusQueue
->MenuOwner
) // pFocusQueue->MenuState) // MenuState needs a start flag...
883 Msg
.lParam
|= KF_MENUMODE
<< 16;
886 /* Post a keyboard message */
887 TRACE("Posting keyboard msg %u wParam 0x%x lParam 0x%x\n", Msg
.message
, Msg
.wParam
, Msg
.lParam
);
888 MsqPostMessage(pFocusQueue
, &Msg
, TRUE
, QS_KEY
);
895 UserSendKeyboardInput(KEYBDINPUT
*pKbdInput
, BOOL bInjected
)
900 PUSER_MESSAGE_QUEUE pFocusQueue
;
901 struct _ETHREAD
*pFocusThread
;
902 LARGE_INTEGER LargeTickCount
;
904 BOOL bExt
= (pKbdInput
->dwFlags
& KEYEVENTF_EXTENDEDKEY
) ? TRUE
: FALSE
;
906 gppiInputProvider
= ((PTHREADINFO
)PsGetCurrentThreadWin32Thread())->ppi
;
908 /* Find the target thread whose locale is in effect */
909 pFocusQueue
= IntGetFocusMessageQueue();
913 pFocusThread
= pFocusQueue
->Thread
;
914 if (pFocusThread
&& pFocusThread
->Tcb
.Win32Thread
)
915 pKl
= ((PTHREADINFO
)pFocusThread
->Tcb
.Win32Thread
)->KeyboardLayout
;
919 pKl
= W32kGetDefaultKeyLayout();
922 ERR("No keyboard layout!\n");
926 pKbdTbl
= pKl
->spkf
->pKbdTbl
;
928 /* Note: wScan field is always used */
929 wScanCode
= pKbdInput
->wScan
;
931 if (pKbdInput
->dwFlags
& KEYEVENTF_UNICODE
)
933 /* Generate WM_KEYDOWN msg with wParam == VK_PACKET and
934 high order word of lParam == pKbdInput->wScan */
940 if (pKbdInput
->dwFlags
& KEYEVENTF_SCANCODE
)
942 /* Don't ignore invalid scan codes */
943 wVk
= IntVscToVk(wScanCode
| (bExt
? 0xE000 : 0), pKbdTbl
);
944 if (!wVk
) /* use 0xFF if vsc is invalid */
949 wVk
= pKbdInput
->wVk
& 0xFF;
953 /* If time is given, use it */
955 dwTime
= pKbdInput
->time
;
958 KeQueryTickCount(&LargeTickCount
);
959 dwTime
= MsqCalculateMessageTime(&LargeTickCount
);
962 if (wVk
== VK_RMENU
&& (pKbdTbl
->fLocaleFlags
& KLLF_ALTGR
))
964 /* For AltGr keyboards RALT generates CTRL events */
965 ProcessKeyEvent(VK_LCONTROL
, 0, pKbdInput
->dwFlags
& KEYEVENTF_KEYUP
, bInjected
, dwTime
, 0);
968 /* Finally process this key */
969 return ProcessKeyEvent(wVk
, wScanCode
, pKbdInput
->dwFlags
, bInjected
, dwTime
, pKbdInput
->dwExtraInfo
);
973 * UserProcessKeyboardInput
975 * Process raw keyboard input data
978 UserProcessKeyboardInput(
979 PKEYBOARD_INPUT_DATA pKbdInputData
)
984 PUSER_MESSAGE_QUEUE pFocusQueue
;
985 struct _ETHREAD
*pFocusThread
;
987 /* Calculate scan code with prefix */
988 wScanCode
= pKbdInputData
->MakeCode
& 0x7F;
989 if (pKbdInputData
->Flags
& KEY_E0
)
991 if (pKbdInputData
->Flags
& KEY_E1
)
994 /* Find the target thread whose locale is in effect */
995 pFocusQueue
= IntGetFocusMessageQueue();
999 pFocusThread
= pFocusQueue
->Thread
;
1000 if (pFocusThread
&& pFocusThread
->Tcb
.Win32Thread
)
1001 pKl
= ((PTHREADINFO
)pFocusThread
->Tcb
.Win32Thread
)->KeyboardLayout
;
1005 pKl
= W32kGetDefaultKeyLayout();
1009 pKbdTbl
= pKl
->spkf
->pKbdTbl
;
1011 /* Convert scan code to virtual key.
1012 Note: We could call UserSendKeyboardInput using scan code,
1013 but it wouldn't interpret E1 key(s) properly */
1014 wVk
= IntVscToVk(wScanCode
, pKbdTbl
);
1015 TRACE("UserProcessKeyboardInput: %x (break: %u) -> %x\n",
1016 wScanCode
, (pKbdInputData
->Flags
& KEY_BREAK
) ? 1 : 0, wVk
);
1020 KEYBDINPUT KbdInput
;
1022 /* Support numlock */
1023 if ((wVk
& KBDNUMPAD
) && IS_KEY_LOCKED(gafAsyncKeyState
, VK_NUMLOCK
))
1025 wVk
= IntTranslateNumpadKey(wVk
& 0xFF);
1028 /* Send keyboard input */
1029 KbdInput
.wVk
= wVk
& 0xFF;
1030 KbdInput
.wScan
= wScanCode
& 0x7F;
1031 KbdInput
.dwFlags
= 0;
1032 if (pKbdInputData
->Flags
& KEY_BREAK
)
1033 KbdInput
.dwFlags
|= KEYEVENTF_KEYUP
;
1035 KbdInput
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
1037 KbdInput
.dwExtraInfo
= pKbdInputData
->ExtraInformation
;
1038 UserSendKeyboardInput(&KbdInput
, FALSE
);
1040 /* E1 keys don't have break code */
1041 if (pKbdInputData
->Flags
& KEY_E1
)
1043 /* Send key up event */
1044 KbdInput
.dwFlags
|= KEYEVENTF_KEYUP
;
1045 UserSendKeyboardInput(&KbdInput
, FALSE
);
1051 * IntTranslateKbdMessage
1053 * Addes WM_(SYS)CHAR messages to message queue if message
1054 * describes key which produce character.
1057 IntTranslateKbdMessage(LPMSG lpMsg
,
1062 WCHAR wch
[3] = { 0 };
1066 LARGE_INTEGER LargeTickCount
;
1067 BOOL bResult
= FALSE
;
1069 switch(lpMsg
->message
)
1080 pWnd
= UserGetWindowObject(lpMsg
->hwnd
);
1081 if (!pWnd
) // Must have a window!
1083 ERR("No Window for Translate.\n");
1087 pti
= pWnd
->head
.pti
;
1089 if (!pti
->KeyboardLayout
)
1091 pti
->KeyboardLayout
= W32kGetDefaultKeyLayout();
1092 pti
->pClientInfo
->hKL
= pti
->KeyboardLayout
? pti
->KeyboardLayout
->hkl
: NULL
;
1093 pKbdTbl
= pti
->KeyboardLayout
->spkf
->pKbdTbl
;
1096 pKbdTbl
= pti
->KeyboardLayout
->spkf
->pKbdTbl
;
1100 if (lpMsg
->message
!= WM_KEYDOWN
&& lpMsg
->message
!= WM_SYSKEYDOWN
)
1103 /* Init pt, hwnd and time msg fields */
1104 NewMsg
.pt
= gpsi
->ptCursor
;
1105 NewMsg
.hwnd
= lpMsg
->hwnd
;
1106 KeQueryTickCount(&LargeTickCount
);
1107 NewMsg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
1109 TRACE("Enter IntTranslateKbdMessage msg %s, vk %x\n",
1110 lpMsg
->message
== WM_SYSKEYDOWN
? "WM_SYSKEYDOWN" : "WM_KEYDOWN", lpMsg
->wParam
);
1112 if (lpMsg
->wParam
== VK_PACKET
)
1114 NewMsg
.message
= (lpMsg
->message
== WM_KEYDOWN
) ? WM_CHAR
: WM_SYSCHAR
;
1115 NewMsg
.wParam
= HIWORD(lpMsg
->lParam
);
1116 NewMsg
.lParam
= LOWORD(lpMsg
->lParam
);
1117 MsqPostMessage(pti
->MessageQueue
, &NewMsg
, FALSE
, QS_KEY
);
1121 cch
= IntToUnicodeEx(lpMsg
->wParam
,
1122 HIWORD(lpMsg
->lParam
) & 0xFF,
1123 pti
->MessageQueue
->afKeyState
,
1125 sizeof(wch
) / sizeof(wch
[0]),
1131 if (cch
> 0) /* Normal characters */
1132 NewMsg
.message
= (lpMsg
->message
== WM_KEYDOWN
) ? WM_CHAR
: WM_SYSCHAR
;
1133 else /* Dead character */
1137 (lpMsg
->message
== WM_KEYDOWN
) ? WM_DEADCHAR
: WM_SYSDEADCHAR
;
1139 NewMsg
.lParam
= lpMsg
->lParam
;
1141 /* Send all characters */
1142 for (i
= 0; i
< cch
; ++i
)
1144 TRACE("Msg: %x '%lc' (%04x) %08x\n", NewMsg
.message
, wch
[i
], wch
[i
], NewMsg
.lParam
);
1145 NewMsg
.wParam
= wch
[i
];
1146 MsqPostMessage(pti
->MessageQueue
, &NewMsg
, FALSE
, QS_KEY
);
1151 TRACE("Leave IntTranslateKbdMessage ret %u, cch %d, msg %x, wch %x\n",
1152 bResult
, cch
, NewMsg
.message
, NewMsg
.wParam
);
1157 * Map a virtual key code, or virtual scan code, to a scan code, key code,
1158 * or unshifted unicode character.
1162 * 0 -- Code is a virtual key code that is converted into a virtual scan code
1163 * that does not distinguish between left and right shift keys.
1164 * 1 -- Code is a virtual scan code that is converted into a virtual key code
1165 * that does not distinguish between left and right shift keys.
1166 * 2 -- Code is a virtual key code that is converted into an unshifted unicode
1168 * 3 -- Code is a virtual scan code that is converted into a virtual key code
1169 * that distinguishes left and right shift keys.
1170 * KeyLayout: Keyboard layout handle
1175 IntMapVirtualKeyEx(UINT uCode
, UINT Type
, PKBDTABLES pKbdTbl
)
1181 case MAPVK_VK_TO_VSC
:
1182 uCode
= IntFixVk(uCode
, FALSE
);
1183 uRet
= IntVkToVsc(uCode
, pKbdTbl
);
1184 if (uRet
> 0xFF) // Fail for scancodes with prefix (e0, e1)
1188 case MAPVK_VSC_TO_VK
:
1189 uRet
= IntVscToVk(uCode
, pKbdTbl
) & 0xFF;
1190 uRet
= IntSimplifyVk(uRet
);
1193 case MAPVK_VK_TO_CHAR
:
1194 uRet
= (UINT
)IntVkToChar(uCode
, pKbdTbl
);
1197 case MAPVK_VSC_TO_VK_EX
:
1198 uRet
= IntVscToVk(uCode
, pKbdTbl
) & 0xFF;
1201 case MAPVK_VK_TO_VSC_EX
:
1202 uRet
= IntVkToVsc(uCode
, pKbdTbl
);
1206 EngSetLastError(ERROR_INVALID_PARAMETER
);
1207 ERR("Wrong type value: %u\n", Type
);
1214 * NtUserMapVirtualKeyEx
1216 * Map a virtual key code, or virtual scan code, to a scan code, key code,
1217 * or unshifted unicode character. See IntMapVirtualKeyEx.
1221 NtUserMapVirtualKeyEx(UINT uCode
, UINT uType
, DWORD keyboardId
, HKL dwhkl
)
1223 PKBDTABLES pKbdTbl
= NULL
;
1226 TRACE("Enter NtUserMapVirtualKeyEx\n");
1233 pti
= PsGetCurrentThreadWin32Thread();
1234 if (pti
&& pti
->KeyboardLayout
)
1235 pKbdTbl
= pti
->KeyboardLayout
->spkf
->pKbdTbl
;
1241 pKl
= UserHklToKbl(dwhkl
);
1243 pKbdTbl
= pKl
->spkf
->pKbdTbl
;
1247 ret
= IntMapVirtualKeyEx(uCode
, uType
, pKbdTbl
);
1250 TRACE("Leave NtUserMapVirtualKeyEx, ret=%i\n", ret
);
1257 * Translates virtual key to characters
1264 PBYTE pKeyStateUnsafe
,
1265 LPWSTR pwszBuffUnsafe
,
1271 BYTE afKeyState
[256 * 2 / 8] = {0};
1272 PWCHAR pwszBuff
= NULL
;
1276 TRACE("Enter NtUserSetKeyboardState\n");
1278 /* Return 0 if SC_KEY_UP bit is set */
1279 if (wScanCode
& SC_KEY_UP
|| wVirtKey
>= 0x100)
1281 ERR("Invalid parameter\n");
1287 /* Probe and copy key state to smaller bitmap */
1288 ProbeForRead(pKeyStateUnsafe
, 256 * sizeof(BYTE
), 1);
1289 for (i
= 0; i
< 256; ++i
)
1291 if (pKeyStateUnsafe
[i
] & KS_DOWN_BIT
)
1292 SET_KEY_DOWN(afKeyState
, i
, TRUE
);
1293 if (pKeyStateUnsafe
[i
] & KS_LOCK_BIT
)
1294 SET_KEY_LOCKED(afKeyState
, i
, TRUE
);
1297 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1299 ERR("Cannot copy key state\n");
1300 SetLastNtError(_SEH2_GetExceptionCode());
1301 _SEH2_YIELD(return 0);
1305 pwszBuff
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(WCHAR
) * cchBuff
, TAG_STRING
);
1308 ERR("ExAllocatePoolWithTag(%d) failed\n", sizeof(WCHAR
) * cchBuff
);
1311 RtlZeroMemory(pwszBuff
, sizeof(WCHAR
) * cchBuff
);
1313 UserEnterExclusive(); // Note: We modify wchDead static variable
1316 pKl
= UserHklToKbl(dwhkl
);
1320 pti
= PsGetCurrentThreadWin32Thread();
1321 pKl
= pti
->KeyboardLayout
;
1324 iRet
= IntToUnicodeEx(wVirtKey
,
1330 pKl
? pKl
->spkf
->pKbdTbl
: NULL
);
1332 MmCopyToCaller(pwszBuffUnsafe
, pwszBuff
, cchBuff
* sizeof(WCHAR
));
1333 ExFreePoolWithTag(pwszBuff
, TAG_STRING
);
1336 TRACE("Leave NtUserSetKeyboardState, ret=%i\n", iRet
);
1341 * NtUserGetKeyNameText
1343 * Gets key name from keyboard layout
1347 NtUserGetKeyNameText(LONG lParam
, LPWSTR lpString
, int cchSize
)
1350 DWORD i
, cchKeyName
, dwRet
= 0;
1351 WORD wScanCode
= (lParam
>> 16) & 0xFF;
1352 BOOL bExtKey
= (HIWORD(lParam
) & KF_EXTENDED
) ? TRUE
: FALSE
;
1354 VSC_LPWSTR
*pKeyNames
= NULL
;
1355 CONST WCHAR
*pKeyName
= NULL
;
1356 WCHAR KeyNameBuf
[2];
1358 TRACE("Enter NtUserGetKeyNameText\n");
1362 /* Get current keyboard layout */
1363 pti
= PsGetCurrentThreadWin32Thread();
1364 pKbdTbl
= pti
? pti
->KeyboardLayout
->spkf
->pKbdTbl
: 0;
1366 if (!pKbdTbl
|| cchSize
< 1)
1368 ERR("Invalid parameter\n");
1372 /* "Do not care" flag */
1373 if(lParam
& LP_DO_NOT_CARE_BIT
)
1375 /* Note: We could do vsc -> vk -> vsc conversion, instead of using
1376 hardcoded scan codes, but it's not what Windows does */
1377 if (wScanCode
== SCANCODE_RSHIFT
&& !bExtKey
)
1378 wScanCode
= SCANCODE_LSHIFT
;
1379 else if (wScanCode
== SCANCODE_CTRL
|| wScanCode
== SCANCODE_ALT
)
1384 pKeyNames
= pKbdTbl
->pKeyNamesExt
;
1386 pKeyNames
= pKbdTbl
->pKeyNames
;
1388 for (i
= 0; pKeyNames
[i
].pwsz
; i
++)
1390 if (pKeyNames
[i
].vsc
== wScanCode
)
1392 pKeyName
= pKeyNames
[i
].pwsz
;
1399 WORD wVk
= IntVscToVk(wScanCode
, pKbdTbl
);
1403 KeyNameBuf
[0] = IntVkToChar(wVk
, pKbdTbl
);
1406 pKeyName
= KeyNameBuf
;
1412 cchKeyName
= wcslen(pKeyName
);
1413 if (cchKeyName
> (cchSize
- 1UL))
1414 cchKeyName
= cchSize
- 1UL; // Don't count '\0'
1418 ProbeForWrite(lpString
, (cchKeyName
+ 1) * sizeof(WCHAR
), 1);
1419 RtlCopyMemory(lpString
, pKeyName
, cchKeyName
* sizeof(WCHAR
));
1420 lpString
[cchKeyName
] = UNICODE_NULL
;
1423 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1425 SetLastNtError(_SEH2_GetExceptionCode());
1431 EngSetLastError(ERROR_INVALID_PARAMETER
);
1436 TRACE("Leave NtUserGetKeyNameText, ret=%i\n", dwRet
);
1441 * UserGetKeyboardType
1443 * Returns some keyboard specific information
1446 UserGetKeyboardType(
1451 case 0: /* Keyboard type */
1452 return 4; /* AT-101 */
1453 case 1: /* Keyboard Subtype */
1454 return 0; /* There are no defined subtypes */
1455 case 2: /* Number of F-keys */
1456 return 12; /* We're doing an 101 for now, so return 12 F-keys */
1458 ERR("Unknown type!\n");
1459 return 0; /* Note: we don't have to set last error here */
1466 * Based on IntTranslateChar, instead of processing VirtualKey match,
1467 * look for wChar match.
1477 PVK_TO_WCHAR_TABLE pVkToWchTbl
;
1478 PVK_TO_WCHARS10 pVkToWch
;
1480 DWORD i
, dwModBits
= 0, dwModNumber
= 0, Ret
= (DWORD
)-1;
1482 TRACE("NtUserVkKeyScanEx() wch %d, KbdLayout 0x%p\n", wch
, dwhkl
);
1487 // Use given keyboard layout
1489 pKl
= UserHklToKbl(dwhkl
);
1493 // Use thread keyboard layout
1494 pKl
= ((PTHREADINFO
)PsGetCurrentThreadWin32Thread())->KeyboardLayout
;
1500 pKbdTbl
= pKl
->spkf
->pKbdTbl
;
1502 // Interate through all VkToWchar tables while pVkToWchars is not NULL
1503 for (i
= 0; pKbdTbl
->pVkToWcharTable
[i
].pVkToWchars
; i
++)
1505 pVkToWchTbl
= &pKbdTbl
->pVkToWcharTable
[i
];
1506 pVkToWch
= (PVK_TO_WCHARS10
)(pVkToWchTbl
->pVkToWchars
);
1508 // Interate through all virtual keys
1509 while (pVkToWch
->VirtualKey
)
1511 for (dwModNumber
= 0; dwModNumber
< pVkToWchTbl
->nModifications
; dwModNumber
++)
1513 if (pVkToWch
->wch
[dwModNumber
] == wch
)
1515 dwModBits
= pKbdTbl
->pCharModifiers
->ModNumber
[dwModNumber
];
1516 TRACE("i %d wC %04x: dwModBits %08x dwModNumber %08x MaxModBits %08x\n",
1517 i
, wch
, dwModBits
, dwModNumber
, pKbdTbl
->pCharModifiers
->wMaxModBits
);
1518 Ret
= (dwModBits
<< 8) | (pVkToWch
->VirtualKey
& 0xFF);
1522 pVkToWch
= (PVK_TO_WCHARS10
)(((BYTE
*)pVkToWch
) + pVkToWchTbl
->cbSize
);