reactos/win32ss/user/ntuser/keyboard.c
Katayama Hirofumi MZ 25b7447818
[SETUPLIB][NTUSER] Toggle input language/layout on Alt+Shift / Ctrl+Shift (#5839)
- Respect the toggle key settings.
- Change the hot key settings in 
  base/setup/lib/mui.c.
- Revert IntDefWindowProc function about
  Alt+Shift handling.
- Delete some code in
  co_IntProcessKeyboardMessage for Alt+Shift
  handling.
- Add IntGetNextKL, IntLanguageToggle, and
  IntCheckLanguageToggle helper functions.
- Modify ProcessKeyEvent and
  UserGetLanguageToggle functions to
  support [Left Alt]+Shift and Ctrl+Shift.
- Improve WM_INPUTLANGCHANGEREQUEST
  handling.
- Message handling shouldn't access kbswitch
  directly.
CORE-10667
2023-10-31 22:37:49 +09:00

1769 lines
48 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* PURPOSE: Keyboard functions
* FILE: win32ss/user/ntuser/keyboard.c
* PROGRAMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* Rafal Harabien (rafalh@reactos.org)
*/
#include <win32k.h>
DBG_DEFAULT_CHANNEL(UserKbd);
BYTE gafAsyncKeyState[256 * 2 / 8]; // 2 bits per key
static BYTE gafAsyncKeyStateRecentDown[256 / 8]; // 1 bit per key
static PKEYBOARD_INDICATOR_TRANSLATION gpKeyboardIndicatorTrans = NULL;
static KEYBOARD_INDICATOR_PARAMETERS gIndicators = {0, 0};
KEYBOARD_ATTRIBUTES gKeyboardInfo;
INT gLanguageToggleKeyState = 0;
DWORD gdwLanguageToggleKey = 1;
INT gLayoutToggleKeyState = 0;
DWORD gdwLayoutToggleKey = 2;
/* FUNCTIONS *****************************************************************/
/*
* InitKeyboardImpl
*
* Initialization -- Right now, just zero the key state
*/
CODE_SEG("INIT")
NTSTATUS
NTAPI
InitKeyboardImpl(VOID)
{
RtlZeroMemory(&gafAsyncKeyState, sizeof(gafAsyncKeyState));
RtlZeroMemory(&gafAsyncKeyStateRecentDown, sizeof(gafAsyncKeyStateRecentDown));
// Clear and set default information.
RtlZeroMemory(&gKeyboardInfo, sizeof(gKeyboardInfo));
gKeyboardInfo.KeyboardIdentifier.Type = 4; /* AT-101 */
gKeyboardInfo.NumberOfFunctionKeys = 12; /* We're doing an 101 for now, so return 12 F-keys */
return STATUS_SUCCESS;
}
/*
* IntKeyboardGetIndicatorTrans
*
* Asks the keyboard driver to send a small table that shows which
* lights should connect with which scancodes
*/
//static
NTSTATUS APIENTRY
IntKeyboardGetIndicatorTrans(HANDLE hKeyboardDevice,
PKEYBOARD_INDICATOR_TRANSLATION *ppIndicatorTrans)
{
NTSTATUS Status;
DWORD dwSize = 0;
IO_STATUS_BLOCK Block;
PKEYBOARD_INDICATOR_TRANSLATION pRet;
dwSize = sizeof(KEYBOARD_INDICATOR_TRANSLATION);
pRet = ExAllocatePoolWithTag(PagedPool,
dwSize,
USERTAG_KBDTABLE);
while (pRet)
{
Status = ZwDeviceIoControlFile(hKeyboardDevice,
NULL,
NULL,
NULL,
&Block,
IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION,
NULL, 0,
pRet, dwSize);
if (Status != STATUS_BUFFER_TOO_SMALL)
break;
ExFreePoolWithTag(pRet, USERTAG_KBDTABLE);
dwSize += sizeof(KEYBOARD_INDICATOR_TRANSLATION);
pRet = ExAllocatePoolWithTag(PagedPool,
dwSize,
USERTAG_KBDTABLE);
}
if (!pRet)
return STATUS_INSUFFICIENT_RESOURCES;
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(pRet, USERTAG_KBDTABLE);
return Status;
}
*ppIndicatorTrans = pRet;
return Status;
}
/*
* IntKeyboardUpdateLeds
*
* Sends the keyboard commands to turn on/off the lights
*/
static
NTSTATUS APIENTRY
IntKeyboardUpdateLeds(HANDLE hKeyboardDevice,
WORD wVk,
WORD wScanCode)
{
NTSTATUS Status;
UINT i;
USHORT LedFlag = 0;
IO_STATUS_BLOCK Block;
if (!gpKeyboardIndicatorTrans)
return STATUS_NOT_SUPPORTED;
switch (wVk)
{
case VK_CAPITAL: LedFlag = KEYBOARD_CAPS_LOCK_ON; break;
case VK_NUMLOCK: LedFlag = KEYBOARD_NUM_LOCK_ON; break;
case VK_SCROLL: LedFlag = KEYBOARD_SCROLL_LOCK_ON; break;
default:
for (i = 0; i < gpKeyboardIndicatorTrans->NumberOfIndicatorKeys; i++)
{
if (gpKeyboardIndicatorTrans->IndicatorList[i].MakeCode == wScanCode)
{
LedFlag = gpKeyboardIndicatorTrans->IndicatorList[i].IndicatorFlags;
break;
}
}
}
if (LedFlag)
{
gIndicators.LedFlags ^= LedFlag;
/* Update the lights on the hardware */
Status = ZwDeviceIoControlFile(hKeyboardDevice,
NULL,
NULL,
NULL,
&Block,
IOCTL_KEYBOARD_SET_INDICATORS,
&gIndicators, sizeof(gIndicators),
NULL, 0);
return Status;
}
return STATUS_SUCCESS;
}
/*
* UserInitKeyboard
*
* Initializes keyboard indicators translation and their state
*/
VOID NTAPI
UserInitKeyboard(HANDLE hKeyboardDevice)
{
NTSTATUS Status;
IO_STATUS_BLOCK Block;
IntKeyboardGetIndicatorTrans(hKeyboardDevice, &gpKeyboardIndicatorTrans);
Status = ZwDeviceIoControlFile(hKeyboardDevice,
NULL,
NULL,
NULL,
&Block,
IOCTL_KEYBOARD_QUERY_INDICATORS,
NULL, 0,
&gIndicators,
sizeof(gIndicators));
if (!NT_SUCCESS(Status))
{
WARN("NtDeviceIoControlFile() failed, ignored\n");
gIndicators.LedFlags = 0;
gIndicators.UnitId = 0;
}
SET_KEY_LOCKED(gafAsyncKeyState, VK_CAPITAL,
gIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON);
SET_KEY_LOCKED(gafAsyncKeyState, VK_NUMLOCK,
gIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON);
SET_KEY_LOCKED(gafAsyncKeyState, VK_SCROLL,
gIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON);
// FIXME: Need device driver to work! HID support more than one!!!!
Status = ZwDeviceIoControlFile(hKeyboardDevice,
NULL,
NULL,
NULL,
&Block,
IOCTL_KEYBOARD_QUERY_ATTRIBUTES,
NULL, 0,
&gKeyboardInfo, sizeof(gKeyboardInfo));
if (!NT_SUCCESS(Status))
{
ERR("NtDeviceIoControlFile() failed, ignored\n");
}
TRACE("Keyboard type %u, subtype %u and number of func keys %u\n",
gKeyboardInfo.KeyboardIdentifier.Type,
gKeyboardInfo.KeyboardIdentifier.Subtype,
gKeyboardInfo.NumberOfFunctionKeys);
}
/*
* IntSimplifyVk
*
* Changes virtual keys which distinguish between left and right hand, to keys which don't distinguish
*/
static
WORD
IntSimplifyVk(WORD wVk)
{
switch (wVk)
{
case VK_LSHIFT:
case VK_RSHIFT:
return VK_SHIFT;
case VK_LCONTROL:
case VK_RCONTROL:
return VK_CONTROL;
case VK_LMENU:
case VK_RMENU:
return VK_MENU;
default:
return wVk;
}
}
/*
* IntFixVk
*
* Changes virtual keys which don't not distinguish between left and right hand to proper keys
*/
static
WORD
IntFixVk(WORD wVk, BOOL bExt)
{
switch (wVk)
{
case VK_SHIFT:
return bExt ? VK_RSHIFT : VK_LSHIFT;
case VK_CONTROL:
return bExt ? VK_RCONTROL : VK_LCONTROL;
case VK_MENU:
return bExt ? VK_RMENU : VK_LMENU;
default:
return wVk;
}
}
/*
* IntTranslateNumpadKey
*
* Translates numpad keys when numlock is enabled
*/
static
WORD
IntTranslateNumpadKey(WORD wVk)
{
switch (wVk)
{
case VK_INSERT: return VK_NUMPAD0;
case VK_END: return VK_NUMPAD1;
case VK_DOWN: return VK_NUMPAD2;
case VK_NEXT: return VK_NUMPAD3;
case VK_LEFT: return VK_NUMPAD4;
case VK_CLEAR: return VK_NUMPAD5;
case VK_RIGHT: return VK_NUMPAD6;
case VK_HOME: return VK_NUMPAD7;
case VK_UP: return VK_NUMPAD8;
case VK_PRIOR: return VK_NUMPAD9;
case VK_DELETE: return VK_DECIMAL;
default: return wVk;
}
}
/*
* IntGetModBits
*
* Gets layout specific modification bits, for example KBDSHIFT, KBDCTRL, KBDALT
*/
static
DWORD
IntGetModBits(PKBDTABLES pKbdTbl, PBYTE pKeyState)
{
DWORD i, dwModBits = 0;
/* DumpKeyState( KeyState ); */
for (i = 0; pKbdTbl->pCharModifiers->pVkToBit[i].Vk; i++)
if (IS_KEY_DOWN(pKeyState, pKbdTbl->pCharModifiers->pVkToBit[i].Vk))
dwModBits |= pKbdTbl->pCharModifiers->pVkToBit[i].ModBits;
TRACE("Current Mod Bits: %lx\n", dwModBits);
return dwModBits;
}
/*
* IntTranslateChar
*
* Translates virtual key to character
*/
static
BOOL
IntTranslateChar(WORD wVirtKey,
PBYTE pKeyState,
PBOOL pbDead,
PBOOL pbLigature,
PWCHAR pwcTranslatedChar,
PKBDTABLES pKbdTbl)
{
PVK_TO_WCHAR_TABLE pVkToVchTbl;
PVK_TO_WCHARS10 pVkToVch;
DWORD i, dwModBits, dwVkModBits, dwModNumber = 0;
WCHAR wch;
BOOL bAltGr;
WORD wCaplokAttr;
dwModBits = pKeyState ? IntGetModBits(pKbdTbl, pKeyState) : 0;
bAltGr = pKeyState && (pKbdTbl->fLocaleFlags & KLLF_ALTGR) && IS_KEY_DOWN(pKeyState, VK_RMENU);
wCaplokAttr = bAltGr ? CAPLOKALTGR : CAPLOK;
TRACE("TryToTranslate: %04x %x\n", wVirtKey, dwModBits);
/* If ALT without CTRL has ben used, remove ALT flag */
if ((dwModBits & (KBDALT|KBDCTRL)) == KBDALT)
dwModBits &= ~KBDALT;
if (dwModBits > pKbdTbl->pCharModifiers->wMaxModBits)
{
TRACE("dwModBits %x > wMaxModBits %x\n", dwModBits, pKbdTbl->pCharModifiers->wMaxModBits);
return FALSE;
}
for (i = 0; pKbdTbl->pVkToWcharTable[i].pVkToWchars; i++)
{
pVkToVchTbl = &pKbdTbl->pVkToWcharTable[i];
pVkToVch = (PVK_TO_WCHARS10)(pVkToVchTbl->pVkToWchars);
while (pVkToVch->VirtualKey)
{
if (wVirtKey == (pVkToVch->VirtualKey & 0xFF))
{
dwVkModBits = dwModBits;
/* If CapsLock is enabled for this key and locked, add SHIFT bit */
if ((pVkToVch->Attributes & wCaplokAttr) &&
pKeyState &&
IS_KEY_LOCKED(pKeyState, VK_CAPITAL))
{
/* Note: we use special value here instead of getting VK_SHIFT mod bit - it's verified */
dwVkModBits ^= KBDSHIFT;
}
if (dwVkModBits > pKbdTbl->pCharModifiers->wMaxModBits)
break;
/* Get modification number */
dwModNumber = pKbdTbl->pCharModifiers->ModNumber[dwVkModBits];
if (dwModNumber >= pVkToVchTbl->nModifications)
{
TRACE("dwModNumber %u >= nModifications %u\n", dwModNumber, pVkToVchTbl->nModifications);
break;
}
/* Read character */
wch = pVkToVch->wch[dwModNumber];
if (wch == WCH_NONE)
break;
*pbDead = (wch == WCH_DEAD);
*pbLigature = (wch == WCH_LGTR);
*pwcTranslatedChar = wch;
TRACE("%lu %04x: dwModNumber %08x Char %04x\n",
i, wVirtKey, dwModNumber, wch);
if (*pbDead)
{
/* After WCH_DEAD, real character is located */
pVkToVch = (PVK_TO_WCHARS10)(((BYTE *)pVkToVch) + pVkToVchTbl->cbSize);
if (pVkToVch->VirtualKey != 0xFF)
{
WARN("Found dead key with no trailer in the table.\n");
WARN("VK: %04x, ADDR: %p\n", wVirtKey, pVkToVch);
break;
}
*pwcTranslatedChar = pVkToVch->wch[dwModNumber];
}
return TRUE;
}
pVkToVch = (PVK_TO_WCHARS10)(((BYTE *)pVkToVch) + pVkToVchTbl->cbSize);
}
}
/* If nothing has been found in layout, check if this is ASCII control character.
Note: we could add it to layout table, but windows does not have it there */
if (wVirtKey >= 'A' && wVirtKey <= 'Z' &&
pKeyState && IS_KEY_DOWN(pKeyState, VK_CONTROL) &&
!IS_KEY_DOWN(pKeyState, VK_MENU))
{
*pwcTranslatedChar = (wVirtKey - 'A') + 1; /* ASCII control character */
*pbDead = FALSE;
*pbLigature = FALSE;
return TRUE;
}
return FALSE;
}
/*
* IntToUnicodeEx
*
* Translates virtual key to characters
*/
static
int APIENTRY
IntToUnicodeEx(UINT wVirtKey,
UINT wScanCode,
PBYTE pKeyState,
LPWSTR pwszBuff,
int cchBuff,
UINT wFlags,
PKBDTABLES pKbdTbl)
{
WCHAR wchTranslatedChar;
BOOL bDead, bLigature;
static WCHAR wchDead = 0;
int iRet = 0;
ASSERT(pKbdTbl);
if (!IntTranslateChar(wVirtKey,
pKeyState,
&bDead,
&bLigature,
&wchTranslatedChar,
pKbdTbl))
{
return 0;
}
if (bLigature)
{
WARN("Not handling ligature (yet)\n" );
return 0;
}
/* If we got dead char in previous call check dead keys in keyboard layout */
if (wchDead)
{
UINT i;
WCHAR wchFirst, wchSecond;
TRACE("Previous dead char: %lc (%x)\n", wchDead, wchDead);
if (pKbdTbl->pDeadKey)
{
for (i = 0; pKbdTbl->pDeadKey[i].dwBoth; i++)
{
wchFirst = pKbdTbl->pDeadKey[i].dwBoth >> 16;
wchSecond = pKbdTbl->pDeadKey[i].dwBoth & 0xFFFF;
if (wchFirst == wchDead && wchSecond == wchTranslatedChar)
{
wchTranslatedChar = pKbdTbl->pDeadKey[i].wchComposed;
wchDead = 0;
bDead = FALSE;
break;
}
}
}
else
{
#if defined(__GNUC__)
if (wchDead == 0x8000)
{
ERR("GCC is inventing bits, ignoring fake dead key\n");
wchDead = 0;
}
#endif
}
TRACE("Final char: %lc (%x)\n", wchTranslatedChar, wchTranslatedChar);
}
/* Dead char has not been not found */
if (wchDead)
{
/* Treat both characters normally */
if (cchBuff > iRet)
pwszBuff[iRet++] = wchDead;
bDead = FALSE;
}
/* Add character to the buffer */
if (cchBuff > iRet)
pwszBuff[iRet++] = wchTranslatedChar;
/* Save dead character */
wchDead = bDead ? wchTranslatedChar : 0;
return bDead ? -iRet : iRet;
}
/*
* IntVkToVsc
*
* Translates virtual key to scan code
*/
static
WORD FASTCALL
IntVkToVsc(WORD wVk, PKBDTABLES pKbdTbl)
{
unsigned i;
ASSERT(pKbdTbl);
/* Check standard keys first */
for (i = 0; i < pKbdTbl->bMaxVSCtoVK; i++)
{
if ((pKbdTbl->pusVSCtoVK[i] & 0xFF) == wVk)
return i;
}
/* Check extended keys now */
for (i = 0; pKbdTbl->pVSCtoVK_E0[i].Vsc; i++)
{
if ((pKbdTbl->pVSCtoVK_E0[i].Vk & 0xFF) == wVk)
return 0xE000 | pKbdTbl->pVSCtoVK_E0[i].Vsc;
}
for (i = 0; pKbdTbl->pVSCtoVK_E1[i].Vsc; i++)
{
if ((pKbdTbl->pVSCtoVK_E1[i].Vk & 0xFF) == wVk)
return 0xE100 | pKbdTbl->pVSCtoVK_E1[i].Vsc;
}
/* Virtual key has not been found */
return 0;
}
/*
* IntVscToVk
*
* Translates prefixed scancode to virtual key
*/
static
WORD FASTCALL
IntVscToVk(WORD wScanCode, PKBDTABLES pKbdTbl)
{
unsigned i;
WORD wVk = 0;
ASSERT(pKbdTbl);
if ((wScanCode & 0xFF00) == 0xE000)
{
for (i = 0; pKbdTbl->pVSCtoVK_E0[i].Vsc; i++)
{
if (pKbdTbl->pVSCtoVK_E0[i].Vsc == (wScanCode & 0xFF))
{
wVk = pKbdTbl->pVSCtoVK_E0[i].Vk;
}
}
}
else if ((wScanCode & 0xFF00) == 0xE100)
{
for (i = 0; pKbdTbl->pVSCtoVK_E1[i].Vsc; i++)
{
if (pKbdTbl->pVSCtoVK_E1[i].Vsc == (wScanCode & 0xFF))
{
wVk = pKbdTbl->pVSCtoVK_E1[i].Vk;
}
}
}
else if (wScanCode < pKbdTbl->bMaxVSCtoVK)
{
wVk = pKbdTbl->pusVSCtoVK[wScanCode];
}
/* 0xFF nad 0x00 are invalid VKs */
return wVk != 0xFF ? wVk : 0;
}
/*
* IntVkToChar
*
* Translates virtual key to character, ignoring shift state
*/
static
WCHAR FASTCALL
IntVkToChar(WORD wVk, PKBDTABLES pKbdTbl)
{
WCHAR wch;
BOOL bDead, bLigature;
ASSERT(pKbdTbl);
if (IntTranslateChar(wVk,
NULL,
&bDead,
&bLigature,
&wch,
pKbdTbl))
{
return wch;
}
return 0;
}
/*
* NtUserGetAsyncKeyState
*
* Gets key state from global bitmap
*/
SHORT
APIENTRY
NtUserGetAsyncKeyState(INT Key)
{
WORD wRet = 0;
TRACE("Enter NtUserGetAsyncKeyState\n");
if (Key >= 0x100 || Key < 0)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
ERR("Invalid parameter Key\n");
return 0;
}
UserEnterExclusive();
if (IS_KEY_DOWN(gafAsyncKeyState, Key))
wRet |= 0x8000; // If down, windows returns 0x8000.
if (gafAsyncKeyStateRecentDown[Key / 8] & (1 << (Key % 8)))
wRet |= 0x1;
gafAsyncKeyStateRecentDown[Key / 8] &= ~(1 << (Key % 8));
UserLeave();
TRACE("Leave NtUserGetAsyncKeyState, ret=%u\n", wRet);
return wRet;
}
/*
* UpdateAsyncKeyState
*
* Updates gafAsyncKeyState array
*/
static
VOID NTAPI
UpdateAsyncKeyState(WORD wVk, BOOL bIsDown)
{
if (bIsDown)
{
/* If it's first key down event, xor lock bit */
if (!IS_KEY_DOWN(gafAsyncKeyState, wVk))
SET_KEY_LOCKED(gafAsyncKeyState, wVk, !IS_KEY_LOCKED(gafAsyncKeyState, wVk));
SET_KEY_DOWN(gafAsyncKeyState, wVk, TRUE);
gafAsyncKeyStateRecentDown[wVk / 8] |= (1 << (wVk % 8));
}
else
SET_KEY_DOWN(gafAsyncKeyState, wVk, FALSE);
}
/*
* co_CallLowLevelKeyboardHook
*
* Calls WH_KEYBOARD_LL hook
*/
static LRESULT
co_CallLowLevelKeyboardHook(WORD wVk, WORD wScanCode, DWORD dwFlags, BOOL bInjected, DWORD dwTime, DWORD dwExtraInfo)
{
KBDLLHOOKSTRUCT KbdHookData;
UINT uMsg;
KbdHookData.vkCode = wVk;
KbdHookData.scanCode = wScanCode;
KbdHookData.flags = 0;
if (dwFlags & KEYEVENTF_EXTENDEDKEY)
KbdHookData.flags |= LLKHF_EXTENDED;
if (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU))
KbdHookData.flags |= LLKHF_ALTDOWN;
if (dwFlags & KEYEVENTF_KEYUP)
KbdHookData.flags |= LLKHF_UP;
if (bInjected)
KbdHookData.flags |= LLKHF_INJECTED;
KbdHookData.time = dwTime;
KbdHookData.dwExtraInfo = dwExtraInfo;
/* Note: it doesnt support WM_SYSKEYUP */
if (dwFlags & KEYEVENTF_KEYUP)
uMsg = WM_KEYUP;
else if (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) && !IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL))
uMsg = WM_SYSKEYDOWN;
else
uMsg = WM_KEYDOWN;
return co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, uMsg, (LPARAM)&KbdHookData);
}
/*
* SnapWindow
*
* Saves snapshot of specified window or whole screen in the clipboard
*/
static VOID
SnapWindow(HWND hWnd)
{
HBITMAP hbm = NULL, hbmOld;
HDC hdc = NULL, hdcMem;
SETCLIPBDATA scd;
INT cx, cy;
PWND pWnd = NULL;
TRACE("SnapWindow(%p)\n", hWnd);
/* If no windows is given, make snapshot of desktop window */
if (!hWnd)
hWnd = IntGetDesktopWindow();
pWnd = UserGetWindowObject(hWnd);
if (!pWnd)
{
ERR("Invalid window\n");
goto cleanup;
}
hdc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE | DCX_WINDOW);
if (!hdc)
{
ERR("UserGetDCEx failed!\n");
goto cleanup;
}
cx = pWnd->rcWindow.right - pWnd->rcWindow.left;
cy = pWnd->rcWindow.bottom - pWnd->rcWindow.top;
hbm = NtGdiCreateCompatibleBitmap(hdc, cx, cy);
if (!hbm)
{
ERR("NtGdiCreateCompatibleBitmap failed!\n");
goto cleanup;
}
hdcMem = NtGdiCreateCompatibleDC(hdc);
if (!hdcMem)
{
ERR("NtGdiCreateCompatibleDC failed!\n");
goto cleanup;
}
hbmOld = NtGdiSelectBitmap(hdcMem, hbm);
NtGdiBitBlt(hdcMem, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY, 0, 0);
NtGdiSelectBitmap(hdcMem, hbmOld);
IntGdiDeleteDC(hdcMem, FALSE);
/* Save snapshot in clipboard */
if (UserOpenClipboard(NULL))
{
UserEmptyClipboard();
scd.fIncSerialNumber = TRUE;
scd.fGlobalHandle = FALSE;
if (UserSetClipboardData(CF_BITMAP, hbm, &scd))
{
/* Bitmap is managed by system now */
hbm = NULL;
}
UserCloseClipboard();
}
cleanup:
if (hbm)
GreDeleteObject(hbm);
if (hdc)
UserReleaseDC(pWnd, hdc, FALSE);
}
/* Find the next/previous keyboard layout of the same/different language */
static PKL FASTCALL
IntGetNextKL(
_In_ PKL pKL,
_In_ BOOL bNext,
_In_ BOOL bSameLang)
{
PKL pFirstKL = pKL;
LANGID LangID = LOWORD(pKL->hkl);
do
{
pKL = (bNext ? pKL->pklNext : pKL->pklPrev);
if (!(pKL->dwKL_Flags & KLF_UNLOAD) && bSameLang == (LangID == LOWORD(pKL->hkl)))
return pKL;
} while (pKL != pFirstKL);
return pFirstKL;
}
/* Perform layout toggle by [Left Alt]+Shift or Ctrl+Shift */
static VOID
IntLanguageToggle(
_In_ PUSER_MESSAGE_QUEUE pFocusQueue,
_In_ BOOL bSameLang,
_In_ INT nKeyState)
{
PWND pWnd = pFocusQueue->spwndFocus;
HWND hWnd;
WPARAM wParam = 0;
PTHREADINFO pti;
PKL pkl;
if (!pWnd)
pWnd = pFocusQueue->spwndActive;
if (!pWnd)
return;
pti = pWnd->head.pti;
pkl = pti->KeyboardLayout;
if (nKeyState == INPUTLANGCHANGE_FORWARD)
pkl = IntGetNextKL(pkl, TRUE, bSameLang);
else if (nKeyState == INPUTLANGCHANGE_BACKWARD)
pkl = IntGetNextKL(pkl, FALSE, bSameLang);
if (gSystemFS & pkl->dwFontSigs)
wParam |= INPUTLANGCHANGE_SYSCHARSET;
hWnd = UserHMGetHandle(pWnd);
UserPostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST, wParam, (LPARAM)pkl->hkl);
}
/* Check Language Toggle by [Left Alt]+Shift or Ctrl+Shift */
static BOOL
IntCheckLanguageToggle(
_In_ PUSER_MESSAGE_QUEUE pFocusQueue,
_In_ BOOL bIsDown,
_In_ WORD wVk,
_Inout_ PINT pKeyState)
{
if (bIsDown) /* Toggle key combination is pressed? */
{
if (wVk == VK_LSHIFT)
*pKeyState = INPUTLANGCHANGE_FORWARD;
else if (wVk == VK_RSHIFT)
*pKeyState = INPUTLANGCHANGE_BACKWARD;
else if (!wVk && IS_KEY_DOWN(gafAsyncKeyState, VK_LSHIFT))
*pKeyState = INPUTLANGCHANGE_FORWARD;
else if (!wVk && IS_KEY_DOWN(gafAsyncKeyState, VK_RSHIFT))
*pKeyState = INPUTLANGCHANGE_BACKWARD;
else
return FALSE;
}
else
{
if (*pKeyState == 0)
return FALSE;
IntLanguageToggle(pFocusQueue, (pKeyState == &gLayoutToggleKeyState), *pKeyState);
*pKeyState = 0;
}
return TRUE;
}
/*
* UserSendKeyboardInput
*
* Process keyboard input from input devices and SendInput API
*/
BOOL NTAPI
ProcessKeyEvent(WORD wVk, WORD wScanCode, DWORD dwFlags, BOOL bInjected, DWORD dwTime, DWORD dwExtraInfo)
{
WORD wSimpleVk = 0, wFixedVk, wVk2;
PUSER_MESSAGE_QUEUE pFocusQueue;
PTHREADINFO pti;
BOOL bExt = (dwFlags & KEYEVENTF_EXTENDEDKEY) ? TRUE : FALSE;
BOOL bIsDown = (dwFlags & KEYEVENTF_KEYUP) ? FALSE : TRUE;
BOOL bPacket = (dwFlags & KEYEVENTF_UNICODE) ? TRUE : FALSE;
BOOL bWasSimpleDown = FALSE, bPostMsg = TRUE, bIsSimpleDown;
MSG Msg;
static BOOL bMenuDownRecently = FALSE;
BOOL bLangToggled = FALSE;
/* Get virtual key without shifts (VK_(L|R)* -> VK_*) */
wSimpleVk = IntSimplifyVk(wVk);
if (PRIMARYLANGID(gusLanguageID) == LANG_JAPANESE)
{
/* Japanese special! */
if (IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT))
{
if (wSimpleVk == VK_OEM_ATTN)
wSimpleVk = VK_CAPITAL;
else if (wSimpleVk == VK_OEM_COPY)
wSimpleVk = VK_OEM_FINISH;
}
}
bWasSimpleDown = IS_KEY_DOWN(gafAsyncKeyState, wSimpleVk);
/* Update key without shifts */
wVk2 = IntFixVk(wSimpleVk, !bExt);
bIsSimpleDown = bIsDown || IS_KEY_DOWN(gafAsyncKeyState, wVk2);
UpdateAsyncKeyState(wSimpleVk, bIsSimpleDown);
if (bIsDown)
{
/* Update keyboard LEDs */
IntKeyboardUpdateLeds(ghKeyboardDevice,
wSimpleVk,
wScanCode);
}
/* Call WH_KEYBOARD_LL hook */
if (co_CallLowLevelKeyboardHook(wVk, wScanCode, dwFlags, bInjected, dwTime, dwExtraInfo))
{
ERR("Kbd msg dropped by WH_KEYBOARD_LL hook\n");
bPostMsg = FALSE;
}
/* Check if this is a hotkey */
if (co_UserProcessHotKeys(wSimpleVk, bIsDown)) //// Check if this is correct, refer to hotkey sequence message tests.
{
TRACE("HotKey Processed\n");
bPostMsg = FALSE;
}
wFixedVk = IntFixVk(wSimpleVk, bExt); /* LSHIFT + EXT = RSHIFT */
if (wSimpleVk == VK_SHIFT) /* shift can't be extended */
bExt = FALSE;
/* If we have a focus queue, post a keyboard message */
pFocusQueue = IntGetFocusMessageQueue();
TRACE("ProcessKeyEvent Q 0x%p Active pWnd 0x%p Focus pWnd 0x%p\n",
pFocusQueue,
(pFocusQueue ? pFocusQueue->spwndActive : 0),
(pFocusQueue ? pFocusQueue->spwndFocus : 0));
/* If it is F10 or ALT is down and CTRL is up, it's a system key */
if ( wVk == VK_F10 ||
(wSimpleVk == VK_MENU && bMenuDownRecently) ||
(IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) &&
!IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL)) ||
// See MSDN WM_SYSKEYDOWN/UP fixes last wine Win test_keyboard_input.
(pFocusQueue && !pFocusQueue->spwndFocus) )
{
bMenuDownRecently = FALSE; // reset
if (bIsDown)
{
Msg.message = WM_SYSKEYDOWN;
if (wSimpleVk == VK_MENU)
{
// Note: If only LALT is pressed WM_SYSKEYUP is generated instead of WM_KEYUP
bMenuDownRecently = TRUE;
}
}
else
Msg.message = WM_SYSKEYUP;
}
else
{
if (bIsDown)
Msg.message = WM_KEYDOWN;
else
Msg.message = WM_KEYUP;
}
/* Update async state of not simplified vk here.
See user32_apitest:GetKeyState */
UpdateAsyncKeyState(wFixedVk, bIsDown);
/* Alt-Tab/Esc Check. Use FocusQueue or RIT Queue */
if (bIsSimpleDown && !bWasSimpleDown &&
IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) &&
!IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL) &&
(wVk == VK_ESCAPE || wVk == VK_TAB))
{
TRACE("Alt-Tab/Esc Pressed wParam %x\n",wVk);
}
/*
* Check Language/Layout Toggle by [Left Alt]+Shift or Ctrl+Shift.
* @see https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc976564%28v=technet.10%29
*/
if (gdwLanguageToggleKey == 1 || gdwLanguageToggleKey == 2)
{
if (wSimpleVk == VK_SHIFT) /* Shift key is pressed or released */
{
UINT targetKey = ((gdwLanguageToggleKey == 1) ? VK_LMENU : VK_CONTROL);
if (IS_KEY_DOWN(gafAsyncKeyState, targetKey))
bLangToggled = IntCheckLanguageToggle(pFocusQueue, bIsDown, wVk, &gLanguageToggleKeyState);
}
else if ((wSimpleVk == VK_MENU && gdwLanguageToggleKey == 1) ||
(wSimpleVk == VK_CONTROL && gdwLanguageToggleKey == 2))
{
if (IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT))
bLangToggled = IntCheckLanguageToggle(pFocusQueue, bIsDown, 0, &gLanguageToggleKeyState);
}
}
if (!bLangToggled && (gdwLayoutToggleKey == 1 || gdwLayoutToggleKey == 2))
{
if (wSimpleVk == VK_SHIFT) /* Shift key is pressed or released */
{
UINT targetKey = ((gdwLayoutToggleKey == 1) ? VK_LMENU : VK_CONTROL);
if (IS_KEY_DOWN(gafAsyncKeyState, targetKey))
IntCheckLanguageToggle(pFocusQueue, bIsDown, wVk, &gLayoutToggleKeyState);
}
else if ((wSimpleVk == VK_MENU && gdwLayoutToggleKey == 1) ||
(wSimpleVk == VK_CONTROL && gdwLayoutToggleKey == 2))
{
if (IS_KEY_DOWN(gafAsyncKeyState, VK_SHIFT))
IntCheckLanguageToggle(pFocusQueue, bIsDown, 0, &gLayoutToggleKeyState);
}
}
if (bIsDown && wVk == VK_SNAPSHOT)
{
if (pFocusQueue &&
IS_KEY_DOWN(gafAsyncKeyState, VK_MENU) &&
!IS_KEY_DOWN(gafAsyncKeyState, VK_CONTROL))
{
// Snap from Active Window, Focus can be null.
SnapWindow(pFocusQueue->spwndActive ? UserHMGetHandle(pFocusQueue->spwndActive) : 0);
}
else
SnapWindow(NULL); // Snap Desktop.
}
else if (pFocusQueue && bPostMsg)
{
PWND Wnd = pFocusQueue->spwndFocus; // SysInit.....
pti = pFocusQueue->ptiKeyboard;
if (!Wnd && pFocusQueue->spwndActive) // SysInit.....
{
// Going with Active. WM_SYSKEYXXX last wine Win test_keyboard_input.
Wnd = pFocusQueue->spwndActive;
}
if (Wnd) pti = Wnd->head.pti;
/* Init message */
Msg.hwnd = Wnd ? UserHMGetHandle(Wnd) : NULL;
Msg.wParam = wFixedVk & 0xFF; /* Note: It's simplified by msg queue */
Msg.lParam = MAKELPARAM(1, wScanCode);
Msg.time = dwTime;
Msg.pt = gpsi->ptCursor;
if ( Msg.message == WM_KEYDOWN || Msg.message == WM_SYSKEYDOWN )
{
if ( (Msg.wParam == VK_SHIFT ||
Msg.wParam == VK_CONTROL ||
Msg.wParam == VK_MENU ) &&
!IS_KEY_DOWN(gafAsyncKeyState, Msg.wParam))
{
ERR("Set last input\n");
//ptiLastInput = pti;
}
}
/* If it is VK_PACKET, high word of wParam is used for wchar */
if (!bPacket)
{
if (bExt)
Msg.lParam |= KF_EXTENDED << 16;
if (IS_KEY_DOWN(gafAsyncKeyState, VK_MENU))
Msg.lParam |= KF_ALTDOWN << 16;
if (bWasSimpleDown)
Msg.lParam |= KF_REPEAT << 16;
if (!bIsDown)
Msg.lParam |= KF_UP << 16;
/* FIXME: Set KF_DLGMODE and KF_MENUMODE when needed */
if (pFocusQueue->QF_flags & QF_DIALOGACTIVE)
Msg.lParam |= KF_DLGMODE << 16;
if (pFocusQueue->MenuOwner) // pti->pMenuState->fMenuStarted
Msg.lParam |= KF_MENUMODE << 16;
}
// Post mouse move before posting key buttons, to keep it syned.
if (pFocusQueue->QF_flags & QF_MOUSEMOVED)
{
IntCoalesceMouseMove(pti);
}
/* Post a keyboard message */
TRACE("Posting keyboard msg %u wParam 0x%x lParam 0x%x\n", Msg.message, Msg.wParam, Msg.lParam);
if (!Wnd) {ERR("Window is NULL\n");}
MsqPostMessage(pti, &Msg, TRUE, QS_KEY, 0, dwExtraInfo);
}
return TRUE;
}
BOOL NTAPI
UserSendKeyboardInput(KEYBDINPUT *pKbdInput, BOOL bInjected)
{
WORD wScanCode, wVk;
PKL pKl = NULL;
PKBDTABLES pKbdTbl;
PUSER_MESSAGE_QUEUE pFocusQueue;
DWORD dwTime;
BOOL bExt = (pKbdInput->dwFlags & KEYEVENTF_EXTENDEDKEY) ? TRUE : FALSE;
gppiInputProvider = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->ppi;
/* Find the target thread whose locale is in effect */
pFocusQueue = IntGetFocusMessageQueue();
if (pFocusQueue && pFocusQueue->ptiKeyboard)
{
pKl = pFocusQueue->ptiKeyboard->KeyboardLayout;
}
if (!pKl)
pKl = W32kGetDefaultKeyLayout();
if (!pKl)
{
ERR("No keyboard layout!\n");
return FALSE;
}
pKbdTbl = pKl->spkf->pKbdTbl;
/* Note: wScan field is always used */
wScanCode = pKbdInput->wScan;
if (pKbdInput->dwFlags & KEYEVENTF_UNICODE)
{
/* Generate WM_KEYDOWN msg with wParam == VK_PACKET and
high order word of lParam == pKbdInput->wScan */
wVk = VK_PACKET;
}
else
{
wScanCode &= 0x7F;
if (pKbdInput->dwFlags & KEYEVENTF_SCANCODE)
{
/* Don't ignore invalid scan codes */
wVk = IntVscToVk(wScanCode | (bExt ? 0xE000 : 0), pKbdTbl);
if (!wVk) /* use 0xFF if vsc is invalid */
wVk = 0xFF;
}
else
{
wVk = pKbdInput->wVk;
}
/* Remove all virtual key flags (KBDEXT, KBDMULTIVK, KBDSPECIAL, KBDNUMPAD) */
wVk &= 0xFF;
}
/* If time is given, use it */
if (pKbdInput->time)
dwTime = pKbdInput->time;
else
{
dwTime = EngGetTickCount32();
}
if (wVk == VK_RMENU && (pKbdTbl->fLocaleFlags & KLLF_ALTGR))
{
/* For AltGr keyboards RALT generates CTRL events */
ProcessKeyEvent(VK_LCONTROL, 0, pKbdInput->dwFlags & KEYEVENTF_KEYUP, bInjected, dwTime, 0);
}
/* Finally process this key */
return ProcessKeyEvent(wVk, wScanCode, pKbdInput->dwFlags, bInjected, dwTime, pKbdInput->dwExtraInfo);
}
/*
* UserProcessKeyboardInput
*
* Process raw keyboard input data
*/
VOID NTAPI
UserProcessKeyboardInput(
PKEYBOARD_INPUT_DATA pKbdInputData)
{
WORD wScanCode, wVk;
PKL pKl = NULL;
PKBDTABLES pKbdTbl;
PUSER_MESSAGE_QUEUE pFocusQueue;
/* Calculate scan code with prefix */
wScanCode = pKbdInputData->MakeCode & 0x7F;
if (pKbdInputData->Flags & KEY_E0)
wScanCode |= 0xE000;
if (pKbdInputData->Flags & KEY_E1)
wScanCode |= 0xE100;
/* Find the target thread whose locale is in effect */
pFocusQueue = IntGetFocusMessageQueue();
if (pFocusQueue && pFocusQueue->ptiKeyboard)
{
pKl = pFocusQueue->ptiKeyboard->KeyboardLayout;
}
if (!pKl)
pKl = W32kGetDefaultKeyLayout();
if (!pKl)
return;
pKbdTbl = pKl->spkf->pKbdTbl;
/* Convert scan code to virtual key.
Note: We could call UserSendKeyboardInput using scan code,
but it wouldn't interpret E1 key(s) properly */
wVk = IntVscToVk(wScanCode, pKbdTbl);
TRACE("UserProcessKeyboardInput: %x (break: %u) -> %x\n",
wScanCode, (pKbdInputData->Flags & KEY_BREAK) ? 1u : 0, wVk);
if (wVk)
{
KEYBDINPUT KbdInput;
/* Support numlock */
if ((wVk & KBDNUMPAD) && IS_KEY_LOCKED(gafAsyncKeyState, VK_NUMLOCK))
{
wVk = IntTranslateNumpadKey(wVk & 0xFF);
}
/* Send keyboard input */
KbdInput.wVk = wVk & 0xFF;
KbdInput.wScan = wScanCode & 0x7F;
KbdInput.dwFlags = 0;
if (pKbdInputData->Flags & KEY_BREAK)
KbdInput.dwFlags |= KEYEVENTF_KEYUP;
if (wVk & KBDEXT)
KbdInput.dwFlags |= KEYEVENTF_EXTENDEDKEY;
//
// Based on wine input:test_Input_blackbox this is okay. It seems the
// bit did not get set and more research is needed. Now the right
// shift works.
//
if (wVk == VK_RSHIFT)
KbdInput.dwFlags |= KEYEVENTF_EXTENDEDKEY;
KbdInput.time = 0;
KbdInput.dwExtraInfo = pKbdInputData->ExtraInformation;
UserSendKeyboardInput(&KbdInput, FALSE);
/* E1 keys don't have break code */
if (pKbdInputData->Flags & KEY_E1)
{
/* Send key up event */
KbdInput.dwFlags |= KEYEVENTF_KEYUP;
UserSendKeyboardInput(&KbdInput, FALSE);
}
}
}
/*
* IntTranslateKbdMessage
*
* Addes WM_(SYS)CHAR messages to message queue if message
* describes key which produce character.
*/
BOOL FASTCALL
IntTranslateKbdMessage(LPMSG lpMsg,
UINT flags)
{
PTHREADINFO pti;
INT cch = 0, i;
WCHAR wch[3] = { 0 };
MSG NewMsg = { 0 };
PKBDTABLES pKbdTbl;
BOOL bResult = FALSE;
switch(lpMsg->message)
{
case WM_KEYDOWN:
case WM_KEYUP:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
break;
default:
return FALSE;
}
pti = PsGetCurrentThreadWin32Thread();
if (!pti->KeyboardLayout)
{
PKL pDefKL = W32kGetDefaultKeyLayout();
UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pDefKL);
if (pDefKL)
{
pti->pClientInfo->hKL = pDefKL->hkl;
pKbdTbl = pDefKL->spkf->pKbdTbl;
}
else
{
pti->pClientInfo->hKL = NULL;
pKbdTbl = NULL;
}
}
else
pKbdTbl = pti->KeyboardLayout->spkf->pKbdTbl;
if (!pKbdTbl)
return FALSE;
if (lpMsg->message != WM_KEYDOWN && lpMsg->message != WM_SYSKEYDOWN)
return FALSE;
/* Init pt, hwnd and time msg fields */
NewMsg.pt = gpsi->ptCursor;
NewMsg.hwnd = lpMsg->hwnd;
NewMsg.time = EngGetTickCount32();
TRACE("Enter IntTranslateKbdMessage msg %s, vk %x\n",
lpMsg->message == WM_SYSKEYDOWN ? "WM_SYSKEYDOWN" : "WM_KEYDOWN", lpMsg->wParam);
if (lpMsg->wParam == VK_PACKET)
{
NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
NewMsg.wParam = HIWORD(lpMsg->lParam);
NewMsg.lParam = LOWORD(lpMsg->lParam);
MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0);
return TRUE;
}
cch = IntToUnicodeEx(lpMsg->wParam,
HIWORD(lpMsg->lParam) & 0xFF,
pti->MessageQueue->afKeyState,
wch,
sizeof(wch) / sizeof(wch[0]),
0,
pKbdTbl);
if (cch)
{
if (cch > 0) /* Normal characters */
NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
else /* Dead character */
{
cch = -cch;
NewMsg.message =
(lpMsg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
}
NewMsg.lParam = lpMsg->lParam;
/* Send all characters */
for (i = 0; i < cch; ++i)
{
TRACE("Msg: %x '%lc' (%04x) %08x\n", NewMsg.message, wch[i], wch[i], NewMsg.lParam);
NewMsg.wParam = wch[i];
MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0);
}
bResult = TRUE;
}
TRACE("Leave IntTranslateKbdMessage ret %d, cch %d, msg %x, wch %x\n",
bResult, cch, NewMsg.message, NewMsg.wParam);
return bResult;
}
/*
* Map a virtual key code, or virtual scan code, to a scan code, key code,
* or unshifted unicode character.
*
* Code: See Below
* Type:
* 0 -- Code is a virtual key code that is converted into a virtual scan code
* that does not distinguish between left and right shift keys.
* 1 -- Code is a virtual scan code that is converted into a virtual key code
* that does not distinguish between left and right shift keys.
* 2 -- Code is a virtual key code that is converted into an unshifted unicode
* character.
* 3 -- Code is a virtual scan code that is converted into a virtual key code
* that distinguishes left and right shift keys.
* KeyLayout: Keyboard layout handle
*
* @implemented
*/
static UINT
IntMapVirtualKeyEx(UINT uCode, UINT Type, PKBDTABLES pKbdTbl)
{
UINT uRet = 0;
switch (Type)
{
case MAPVK_VK_TO_VSC:
uCode = IntFixVk(uCode, FALSE);
uRet = IntVkToVsc(uCode, pKbdTbl);
if (uRet > 0xFF) // Fail for scancodes with prefix (e0, e1)
uRet = 0;
break;
case MAPVK_VSC_TO_VK:
uRet = IntVscToVk(uCode, pKbdTbl) & 0xFF;
uRet = IntSimplifyVk(uRet);
break;
case MAPVK_VK_TO_CHAR:
uRet = (UINT)IntVkToChar(uCode, pKbdTbl);
break;
case MAPVK_VSC_TO_VK_EX:
uRet = IntVscToVk(uCode, pKbdTbl) & 0xFF;
break;
case MAPVK_VK_TO_VSC_EX:
uRet = IntVkToVsc(uCode, pKbdTbl);
break;
default:
EngSetLastError(ERROR_INVALID_PARAMETER);
ERR("Wrong type value: %u\n", Type);
}
return uRet;
}
/*
* NtUserMapVirtualKeyEx
*
* Map a virtual key code, or virtual scan code, to a scan code, key code,
* or unshifted unicode character. See IntMapVirtualKeyEx.
*/
UINT
APIENTRY
NtUserMapVirtualKeyEx(UINT uCode, UINT uType, DWORD keyboardId, HKL dwhkl)
{
PKBDTABLES pKbdTbl = NULL;
UINT ret = 0;
TRACE("Enter NtUserMapVirtualKeyEx\n");
UserEnterShared();
if (!dwhkl)
{
PTHREADINFO pti;
pti = PsGetCurrentThreadWin32Thread();
if (pti && pti->KeyboardLayout)
pKbdTbl = pti->KeyboardLayout->spkf->pKbdTbl;
}
else
{
PKL pKl;
pKl = UserHklToKbl(dwhkl);
if (pKl)
pKbdTbl = pKl->spkf->pKbdTbl;
}
if (pKbdTbl)
ret = IntMapVirtualKeyEx(uCode, uType, pKbdTbl);
UserLeave();
TRACE("Leave NtUserMapVirtualKeyEx, ret=%u\n", ret);
return ret;
}
/*
* NtUserToUnicodeEx
*
* Translates virtual key to characters
*/
int
APIENTRY
NtUserToUnicodeEx(
UINT wVirtKey,
UINT wScanCode,
PBYTE pKeyStateUnsafe,
LPWSTR pwszBuffUnsafe,
INT cchBuff,
UINT wFlags,
HKL dwhkl)
{
PTHREADINFO pti;
BYTE afKeyState[256 * 2 / 8] = {0};
PWCHAR pwszBuff = NULL;
INT i, iRet = 0;
PKL pKl = NULL;
NTSTATUS Status = STATUS_SUCCESS;
TRACE("Enter NtUserSetKeyboardState\n");
/* Return 0 if SC_KEY_UP bit is set */
if (wScanCode & SC_KEY_UP || wVirtKey >= 0x100)
{
ERR("Invalid parameter\n");
return 0;
}
_SEH2_TRY
{
/* Probe and copy key state to smaller bitmap */
ProbeForRead(pKeyStateUnsafe, 256 * sizeof(BYTE), 1);
for (i = 0; i < 256; ++i)
{
if (pKeyStateUnsafe[i] & KS_DOWN_BIT)
SET_KEY_DOWN(afKeyState, i, TRUE);
if (pKeyStateUnsafe[i] & KS_LOCK_BIT)
SET_KEY_LOCKED(afKeyState, i, TRUE);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ERR("Cannot copy key state\n");
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(return 0);
}
_SEH2_END;
pwszBuff = ExAllocatePoolWithTag(NonPagedPool, sizeof(WCHAR) * cchBuff, TAG_STRING);
if (!pwszBuff)
{
ERR("ExAllocatePoolWithTag(%u) failed\n", sizeof(WCHAR) * cchBuff);
return 0;
}
RtlZeroMemory(pwszBuff, sizeof(WCHAR) * cchBuff);
UserEnterExclusive(); // Note: We modify wchDead static variable
if (dwhkl)
pKl = UserHklToKbl(dwhkl);
if (!pKl)
{
pti = PsGetCurrentThreadWin32Thread();
pKl = pti->KeyboardLayout;
}
if (pKl)
{
iRet = IntToUnicodeEx(wVirtKey,
wScanCode,
afKeyState,
pwszBuff,
cchBuff,
wFlags,
pKl->spkf->pKbdTbl);
if (iRet)
{
Status = MmCopyToCaller(pwszBuffUnsafe, pwszBuff, cchBuff * sizeof(WCHAR));
}
}
else
{
ERR("No keyboard layout ?!\n");
Status = STATUS_INVALID_HANDLE;
}
ExFreePoolWithTag(pwszBuff, TAG_STRING);
if (!NT_SUCCESS(Status))
{
iRet = 0;
SetLastNtError(Status);
}
UserLeave();
TRACE("Leave NtUserSetKeyboardState, ret=%i\n", iRet);
return iRet;
}
/*
* NtUserGetKeyNameText
*
* Gets key name from keyboard layout
*/
DWORD
APIENTRY
NtUserGetKeyNameText(LONG lParam, LPWSTR lpString, int cchSize)
{
PTHREADINFO pti;
DWORD i, dwRet = 0;
SIZE_T cchKeyName;
WORD wScanCode = (lParam >> 16) & 0xFF;
BOOL bExtKey = (HIWORD(lParam) & KF_EXTENDED) ? TRUE : FALSE;
PKBDTABLES pKbdTbl;
VSC_LPWSTR *pKeyNames = NULL;
CONST WCHAR *pKeyName = NULL;
WCHAR KeyNameBuf[2];
TRACE("Enter NtUserGetKeyNameText\n");
UserEnterShared();
/* Get current keyboard layout */
pti = PsGetCurrentThreadWin32Thread();
pKbdTbl = pti ? pti->KeyboardLayout->spkf->pKbdTbl : 0;
if (!pKbdTbl || cchSize < 1)
{
ERR("Invalid parameter\n");
goto cleanup;
}
/* "Do not care" flag */
if(lParam & LP_DO_NOT_CARE_BIT)
{
/* Note: We could do vsc -> vk -> vsc conversion, instead of using
hardcoded scan codes, but it's not what Windows does */
if (wScanCode == SCANCODE_RSHIFT && !bExtKey)
wScanCode = SCANCODE_LSHIFT;
else if (wScanCode == SCANCODE_CTRL || wScanCode == SCANCODE_ALT)
bExtKey = FALSE;
}
if (bExtKey)
pKeyNames = pKbdTbl->pKeyNamesExt;
else
pKeyNames = pKbdTbl->pKeyNames;
for (i = 0; pKeyNames[i].pwsz; i++)
{
if (pKeyNames[i].vsc == wScanCode)
{
pKeyName = pKeyNames[i].pwsz;
break;
}
}
if (!pKeyName)
{
WORD wVk = IntVscToVk(wScanCode, pKbdTbl);
if (wVk)
{
KeyNameBuf[0] = IntVkToChar(wVk, pKbdTbl);
KeyNameBuf[1] = 0;
if (KeyNameBuf[0])
pKeyName = KeyNameBuf;
}
}
if (pKeyName)
{
cchKeyName = wcslen(pKeyName);
if (cchKeyName > (cchSize - 1UL))
cchKeyName = cchSize - 1UL; // Don't count '\0'
_SEH2_TRY
{
ProbeForWrite(lpString, (cchKeyName + 1) * sizeof(WCHAR), 1);
RtlCopyMemory(lpString, pKeyName, cchKeyName * sizeof(WCHAR));
lpString[cchKeyName] = UNICODE_NULL;
dwRet = cchKeyName;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
}
_SEH2_END;
}
else
{
EngSetLastError(ERROR_INVALID_PARAMETER);
}
cleanup:
UserLeave();
TRACE("Leave NtUserGetKeyNameText, ret=%lu\n", dwRet);
return dwRet;
}
/*
* UserGetKeyboardType
*
* Returns some keyboard specific information
*/
DWORD FASTCALL
UserGetKeyboardType(
DWORD dwTypeFlag)
{
switch (dwTypeFlag)
{
case 0: /* Keyboard type */
return (DWORD)gKeyboardInfo.KeyboardIdentifier.Type;
case 1: /* Keyboard Subtype */
return (DWORD)gKeyboardInfo.KeyboardIdentifier.Subtype;
case 2: /* Number of F-keys */
return (DWORD)gKeyboardInfo.NumberOfFunctionKeys;
default:
ERR("Unknown type!\n");
return 0; /* Note: we don't have to set last error here */
}
}
/*
* NtUserVkKeyScanEx
*
* Based on IntTranslateChar, instead of processing VirtualKey match,
* look for wChar match.
*/
DWORD
APIENTRY
NtUserVkKeyScanEx(
WCHAR wch,
HKL dwhkl,
BOOL bUsehKL)
{
PKBDTABLES pKbdTbl;
PVK_TO_WCHAR_TABLE pVkToWchTbl;
PVK_TO_WCHARS10 pVkToWch;
PKL pKl = NULL;
DWORD i, dwModBits = 0, dwModNumber = 0, Ret = (DWORD)-1;
TRACE("NtUserVkKeyScanEx() wch %u, KbdLayout 0x%p\n", wch, dwhkl);
UserEnterShared();
if (bUsehKL)
{
// Use given keyboard layout
if (dwhkl)
pKl = UserHklToKbl(dwhkl);
}
else
{
// Use thread keyboard layout
pKl = ((PTHREADINFO)PsGetCurrentThreadWin32Thread())->KeyboardLayout;
}
if (!pKl)
goto Exit;
pKbdTbl = pKl->spkf->pKbdTbl;
// Interate through all VkToWchar tables while pVkToWchars is not NULL
for (i = 0; pKbdTbl->pVkToWcharTable[i].pVkToWchars; i++)
{
pVkToWchTbl = &pKbdTbl->pVkToWcharTable[i];
pVkToWch = (PVK_TO_WCHARS10)(pVkToWchTbl->pVkToWchars);
// Interate through all virtual keys
while (pVkToWch->VirtualKey)
{
for (dwModNumber = 0; dwModNumber < pVkToWchTbl->nModifications; dwModNumber++)
{
if (pVkToWch->wch[dwModNumber] == wch)
{
dwModBits = pKbdTbl->pCharModifiers->ModNumber[dwModNumber];
TRACE("i %lu wC %04x: dwModBits %08x dwModNumber %08x MaxModBits %08x\n",
i, wch, dwModBits, dwModNumber, pKbdTbl->pCharModifiers->wMaxModBits);
Ret = (dwModBits << 8) | (pVkToWch->VirtualKey & 0xFF);
goto Exit;
}
}
pVkToWch = (PVK_TO_WCHARS10)(((BYTE *)pVkToWch) + pVkToWchTbl->cbSize);
}
}
Exit:
UserLeave();
return Ret;
}
/* EOF */