mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
581 lines
16 KiB
581 lines
16 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: xlate.c
|
|
*
|
|
* Copyright (c) 1985-95, Microsoft Corporation
|
|
*
|
|
* History:
|
|
* 12-07-90 GregoryW Created.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
/*
|
|
* "The great artist is the simplifier."
|
|
* - Henri Frederic Amiel (1821-1881)
|
|
* Ibid., November 25, 1861
|
|
*/
|
|
|
|
/*
|
|
* Determine the state of all the Modifier Keys (a Modifier Key
|
|
* is any key that may modify values produced by other keys: these are
|
|
* commonly SHIFT, CTRL and/or ALT)
|
|
* Build a bit-mask (wModBits) to encode which modifier keys are depressed.
|
|
*/
|
|
WORD GetModifierBits(
|
|
PMODIFIERS pModifiers,
|
|
LPBYTE afKeyState)
|
|
{
|
|
PVK_TO_BIT pVkToBit = pModifiers->pVkToBit;
|
|
WORD wModBits = 0;
|
|
|
|
while (pVkToBit->Vk) {
|
|
if (TestKeyDownBit(afKeyState, pVkToBit->Vk)) {
|
|
wModBits |= pVkToBit->ModBits;
|
|
}
|
|
pVkToBit++;
|
|
}
|
|
return wModBits;
|
|
}
|
|
|
|
/*
|
|
* Given modifier bits, return the modification number.
|
|
*/
|
|
WORD GetModificationNumber(
|
|
PMODIFIERS pModifiers,
|
|
WORD wModBits)
|
|
{
|
|
if (wModBits > pModifiers->wMaxModBits) {
|
|
return SHFT_INVALID;
|
|
}
|
|
|
|
return pModifiers->ModNumber[wModBits];
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
* VKFromVSC
|
|
*
|
|
* This function is called from KeyEvent() after each call to VSCFromSC. The
|
|
* keyboard input data passed in is translated to a virtual key code.
|
|
* This translation is dependent upon the currently depressed modifier keys.
|
|
*
|
|
* For instance, scan codes representing the number pad keys may be
|
|
* translated into VK_NUMPAD codes or cursor movement codes depending
|
|
* upon the state of NumLock and the modifier keys.
|
|
*
|
|
* History:
|
|
*
|
|
\*****************************************************************************/
|
|
BYTE VKFromVSC(
|
|
PKE pke,
|
|
BYTE bPrefix,
|
|
LPBYTE afKeyState)
|
|
{
|
|
USHORT usVKey = 0xFF;
|
|
PVSC_VK pVscVk;
|
|
PKBDTABLES pKbdTbl;
|
|
static BOOL fVkPause;
|
|
#ifdef FE_IME // For Korea 103 keyboard
|
|
extern USHORT gPrimaryLangID;
|
|
#endif
|
|
|
|
DBG_UNREFERENCED_PARAMETER(afKeyState);
|
|
|
|
if (pke->bScanCode == 0xFF) {
|
|
/*
|
|
* Kbd overrun (kbd hardware and/or keyboard driver) : Beep!
|
|
* (some DELL keyboards send 0xFF if keys are hit hard enough,
|
|
* presumably due to keybounce)
|
|
*/
|
|
xxxMessageBeep(0);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef FE_IME // For Korea 103 keyboard
|
|
if (gPrimaryLangID == LANG_KOREAN && (pke->bScanCode==0x71 || pke->bScanCode==0x72)) {
|
|
pke->bScanCode |= 0x80;
|
|
bPrefix = 0xE0;
|
|
}
|
|
else
|
|
#endif
|
|
pke->bScanCode &= 0x7F;
|
|
|
|
if (gptiForeground == NULL) {
|
|
RIPMSG0(RIP_VERBOSE, "VKFromVSC: NULL gptiForeground\n");
|
|
pKbdTbl = gpKbdTbl;
|
|
} else {
|
|
if (gptiForeground->spklActive) {
|
|
pKbdTbl = gptiForeground->spklActive->spkf->pKbdTbl;
|
|
} else {
|
|
RIPMSG0(RIP_VERBOSE, "VKFromVSC: NULL spklActive\n");
|
|
pKbdTbl = gpKbdTbl;
|
|
}
|
|
}
|
|
if (bPrefix == 0) {
|
|
if (pke->bScanCode < pKbdTbl->bMaxVSCtoVK) {
|
|
/*
|
|
* direct index into non-prefix table
|
|
*/
|
|
usVKey = pKbdTbl->pusVSCtoVK[pke->bScanCode];
|
|
if (usVKey == 0) {
|
|
return 0xFF;
|
|
}
|
|
} else {
|
|
/*
|
|
* unexpected scancode
|
|
*/
|
|
RIPMSG2(RIP_VERBOSE, "unrecognized scancode 0x%x, prefix %x",
|
|
pke->bScanCode, bPrefix);
|
|
return 0xFF;
|
|
}
|
|
} else {
|
|
/*
|
|
* Scan the E0 or E1 prefix table for a match
|
|
*/
|
|
if (bPrefix == 0xE0) {
|
|
/*
|
|
* Ignore the SHIFT keystrokes generated by the hardware
|
|
*/
|
|
if ((pke->bScanCode == SCANCODE_LSHIFT) ||
|
|
(pke->bScanCode == SCANCODE_RSHIFT)) {
|
|
return 0;
|
|
}
|
|
pVscVk = pKbdTbl->pVSCtoVK_E0;
|
|
} else if (bPrefix == 0xE1) {
|
|
pVscVk = pKbdTbl->pVSCtoVK_E1;
|
|
}
|
|
while (pVscVk->Vk) {
|
|
if (pVscVk->Vsc == pke->bScanCode) {
|
|
usVKey = pVscVk->Vk;
|
|
break;
|
|
}
|
|
pVscVk++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scancode set 1 returns PAUSE button as E1 1D 45 (E1 Ctrl NumLock)
|
|
* so convert E1 Ctrl to VK_PAUSE, and remember to discard the NumLock
|
|
*/
|
|
if (fVkPause) {
|
|
/*
|
|
* This is the "45" part of the Pause scancode sequence.
|
|
* Discard this key event: it is a false NumLock
|
|
*/
|
|
fVkPause = FALSE;
|
|
return 0;
|
|
}
|
|
if (usVKey == VK_PAUSE) {
|
|
/*
|
|
* This is the "E1 1D" part of the Pause scancode sequence.
|
|
* Alter the scancode to the value Windows expects for Pause,
|
|
* and remember to discard the "45" scancode that will follow
|
|
*/
|
|
pke->bScanCode = 0x45;
|
|
fVkPause = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Convert to a different VK if some modifier keys are depressed.
|
|
*/
|
|
if (usVKey & KBDMULTIVK) {
|
|
WORD nMod;
|
|
PULONG pul;
|
|
|
|
nMod = GetModificationNumber(
|
|
&Modifiers_VK,
|
|
GetModifierBits(&Modifiers_VK, gafPhysKeyState));
|
|
|
|
/*
|
|
* Scan gapulCvt_VK[nMod] for matching VK.
|
|
*/
|
|
if ((nMod != SHFT_INVALID) && ((pul = gapulCvt_VK[nMod]) != NULL)) {
|
|
while (*pul != 0) {
|
|
if (LOBYTE(*pul) == LOBYTE(usVKey)) {
|
|
pke->usFlaggedVk = (USHORT)HIWORD(*pul);
|
|
return (BYTE)pke->usFlaggedVk;
|
|
}
|
|
pul++;
|
|
}
|
|
}
|
|
}
|
|
|
|
pke->usFlaggedVk = usVKey;
|
|
return (BYTE)usVKey;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UINT InternalMapVirtualKeyEx(UINT wCode, UINT wType, PKBDTABLES pKbdTbl);
|
|
*
|
|
* History:
|
|
* IanJa 5/13/91 from Win3.1 \\pucus\win31ro!drivers\keyboard\getname.asm
|
|
* GregoryW 2/21/95 renamed from _MapVirtualKey and added third parameter.
|
|
\***************************************************************************/
|
|
|
|
UINT InternalMapVirtualKeyEx(
|
|
UINT wCode,
|
|
UINT wType,
|
|
PKBDTABLES pKbdTbl)
|
|
{
|
|
PVK_TO_WCHARS1 pVK;
|
|
PVK_TO_WCHAR_TABLE pVKT;
|
|
UINT VkRet = 0;
|
|
USHORT usScanCode;
|
|
PVSC_VK pVscVk;
|
|
PBYTE pVkNumpad;
|
|
|
|
switch (wType) {
|
|
case 0:
|
|
|
|
/*
|
|
* Convert Virtual Key (wCode) to Scan Code
|
|
*/
|
|
if ((wCode >= VK_SHIFT) && (wCode <= VK_MENU)) {
|
|
|
|
/*
|
|
* Convert ambiguous Shift/Control/Alt keys to left-hand keys
|
|
*/
|
|
wCode = (UINT)((wCode - VK_SHIFT) * 2 + VK_LSHIFT);
|
|
}
|
|
|
|
/*
|
|
* Scan through the table that maps Virtual Scancodes to Virtual Keys
|
|
* for non-extended keys.
|
|
*/
|
|
for (usScanCode = 0; usScanCode < pKbdTbl->bMaxVSCtoVK; usScanCode++) {
|
|
if ((UINT)LOBYTE(pKbdTbl->pusVSCtoVK[usScanCode]) == wCode) {
|
|
return usScanCode & 0xFF;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scan through the table that maps Virtual Scancodes to Virtual Keys
|
|
* for extended keys.
|
|
*/
|
|
for (pVscVk = pKbdTbl->pVSCtoVK_E0; pVscVk->Vk; pVscVk++) {
|
|
if ((UINT)LOBYTE(pVscVk->Vk) == wCode) {
|
|
return (UINT)pVscVk->Vsc;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* There was no match: maybe the Virtual Key can only be generated
|
|
* with Numlock on. Scan through aVkNumpad[] to determine scancode.
|
|
*/
|
|
for (pVkNumpad = aVkNumpad; *pVkNumpad != 0; pVkNumpad++) {
|
|
if ((UINT)(*pVkNumpad) == wCode) {
|
|
return pVkNumpad - aVkNumpad + SCANCODE_NUMPAD_FIRST;
|
|
}
|
|
}
|
|
|
|
return 0; // No match found!
|
|
|
|
case 1:
|
|
case 3:
|
|
|
|
/*
|
|
* Convert Scan Code (wCode) to Virtual Key, disregarding modifier keys
|
|
* and NumLock key etc. Returns 0 for no corresponding Virtual Key
|
|
*/
|
|
if (wCode < (UINT)(pKbdTbl->bMaxVSCtoVK)) {
|
|
VkRet = (UINT)LOBYTE(pKbdTbl->pusVSCtoVK[wCode]);
|
|
} else {
|
|
/*
|
|
* Scan the E0 prefix table for a match
|
|
*/
|
|
for (pVscVk = pKbdTbl->pVSCtoVK_E0; pVscVk->Vk; pVscVk++) {
|
|
if ((UINT)pVscVk->Vsc == wCode) {
|
|
VkRet = (UINT)LOBYTE(pVscVk->Vk);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((wType == 1) && (VkRet >= VK_LSHIFT) && (VkRet <= VK_RMENU)) {
|
|
|
|
/*
|
|
* Convert left/right Shift/Control/Alt keys to ambiguous keys
|
|
* (neither left nor right)
|
|
*/
|
|
VkRet = (UINT)((VkRet - VK_LSHIFT) / 2 + VK_SHIFT);
|
|
}
|
|
|
|
if (VkRet == 0xFF) {
|
|
VkRet = 0;
|
|
}
|
|
return VkRet;
|
|
|
|
case 2:
|
|
|
|
/*
|
|
* Bogus Win3.1 functionality: despite SDK documenation, return uppercase for
|
|
* VK_A through VK_Z
|
|
*/
|
|
if ((wCode >= (WORD)'A') && (wCode <= (WORD)'Z')) {
|
|
return wCode;
|
|
}
|
|
|
|
/*
|
|
* Convert Virtual Key (wCode) to ANSI.
|
|
* Search each Shift-state table in turn, looking for the Virtual Key.
|
|
*/
|
|
for (pVKT = pKbdTbl->pVkToWcharTable; pVKT->pVkToWchars != NULL; pVKT++) {
|
|
pVK = pVKT->pVkToWchars;
|
|
while (pVK->VirtualKey != 0) {
|
|
if ((UINT)pVK->VirtualKey == wCode) {
|
|
|
|
/*
|
|
* Match found: return the unshifted character
|
|
*/
|
|
if (pVK->wch[0] == WCH_DEAD) {
|
|
|
|
/*
|
|
* It is a dead character: the next entry contains its
|
|
* value. Set the high bit to indicate dead key
|
|
* (undocumented behaviour)
|
|
*/
|
|
pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
|
|
return pVK->wch[0] | (UINT)0x80000000;
|
|
} else if (pVK->wch[0] == WCH_NONE) {
|
|
return 0; // 9013
|
|
}
|
|
if (pVK->wch[0] == WCH_NONE) {
|
|
return 0;
|
|
}
|
|
return pVK->wch[0];
|
|
}
|
|
pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Can't find translation, or wType was invalid
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _GetKeyNameText (API)
|
|
*
|
|
* int _GetKeyNameText(DWORD lParam, LPSTR lpStr, UINT size);
|
|
*
|
|
* lParam: value from WM_KEYDOWN message, etc.
|
|
*
|
|
* Byte 3 (bits 16..23) of lParam contains a scan code.
|
|
*
|
|
* Bit 20 of lParam is the Extended bit (distingushes some keys on
|
|
* Enhanced keyboard).
|
|
*
|
|
* Bit 21 of lParam is a don't care bit (don't distingush between
|
|
* left and right control, shift, Enter keys, between edit keys
|
|
* in edit area and on numeric pad, etc). The app calling this
|
|
* function sets this bit in lParam, if it so desires.
|
|
*
|
|
* lpStr: Pointer to output string.
|
|
*
|
|
* iSize: Maximum length of output string, not including null byte.
|
|
*
|
|
* History:
|
|
* IanJa 4/11/91 from Win3.1 \\pucus\win31ro!drivers\keyboard\getname.asm
|
|
\***************************************************************************/
|
|
|
|
int APIENTRY _GetKeyNameText(
|
|
LONG lParam,
|
|
LPWSTR lpStr,
|
|
int cchSize)
|
|
{
|
|
BYTE Vsc = LOBYTE(HIWORD(lParam));
|
|
PVSC_LPWSTR pKN;
|
|
PTHREADINFO ptiT = PtiCurrentShared();
|
|
PKBDTABLES pKbdTbl;
|
|
UINT Vk;
|
|
UINT Char;
|
|
|
|
if (cchSize < 1)
|
|
return 0;
|
|
|
|
/*
|
|
* If bit 25 set (don't care about the extended bit) then clear bit 24,
|
|
* unless this is the Delete/Num-Del key (0x53), which is bass-ackward.
|
|
*/
|
|
if (lParam & DONTCARE_BIT) {
|
|
if (Vsc == 0x53) {
|
|
lParam |= EXTENDED_BIT;
|
|
} else {
|
|
lParam &= ~EXTENDED_BIT;
|
|
}
|
|
}
|
|
|
|
if (ptiT->spklActive == (PKL)NULL) {
|
|
return 0;
|
|
}
|
|
pKbdTbl = ptiT->spklActive->spkf->pKbdTbl;
|
|
|
|
/*
|
|
* Scan pKbdTbl->pKeyNames[] or pKeyNamesExt[] for matching Virtual Scan Code
|
|
*/
|
|
if (lParam & EXTENDED_BIT) {
|
|
pKN = pKbdTbl->pKeyNamesExt;
|
|
} else {
|
|
pKN = pKbdTbl->pKeyNames;
|
|
}
|
|
|
|
if (pKN) {
|
|
while (pKN->vsc != 0) {
|
|
if (Vsc == pKN->vsc) {
|
|
|
|
cchSize = wcsncpycch(lpStr, pKN->pwsz, cchSize);
|
|
cchSize--;
|
|
lpStr[cchSize] = L'\0';
|
|
return cchSize;
|
|
}
|
|
pKN++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The name of the key was not found in the table, so we
|
|
* now attempt to construct the key name from the character produced by
|
|
* the key. Translate Scancode -> Virtual Key -> character.
|
|
*/
|
|
|
|
/*
|
|
* Translate Scancode to Virtual Key (ignoring modifier keys etc.)
|
|
*/
|
|
Vk = InternalMapVirtualKeyEx((UINT)Vsc, 1, pKbdTbl);
|
|
if (Vk == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Now translate Virtual Key to character (ignoring modifier keys etc.)
|
|
*/
|
|
Char = InternalMapVirtualKeyEx((UINT)Vk, 2, pKbdTbl);
|
|
if (Char == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (Char & 0x80000000) {
|
|
LPWSTR *ppwsz;
|
|
|
|
ppwsz = pKbdTbl->pKeyNamesDead;
|
|
if (ppwsz) {
|
|
while (*ppwsz != NULL) {
|
|
if (*ppwsz[0] == (WCHAR)Char) {
|
|
cchSize = wcsncpycch(lpStr, (*ppwsz)+1, cchSize);
|
|
cchSize--;
|
|
lpStr[cchSize] = L'\0';
|
|
return cchSize;
|
|
}
|
|
ppwsz++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Construct a single character name (adding null-terminator if possible)
|
|
*/
|
|
lpStr[0] = (WCHAR)Char;
|
|
if (cchSize >= 2) {
|
|
lpStr[1] = L'\0';
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* AltGr() - handle special case Right-hand ALT key (Locale dependent)
|
|
*
|
|
* Note: gbAltGrDown reminds us to send the fake Ctrl key back up if we
|
|
* switched to a non-AltGr layout while ALtGr was down: otherwise the Ctrl key
|
|
* gets stuck down. (For example: using AltGr with KBDSEL to switch from
|
|
* German to US layout).
|
|
\***************************************************************************/
|
|
BOOL gbAltGrDown = FALSE;
|
|
|
|
AltGr(
|
|
PKE pKe)
|
|
{
|
|
if (pKe == NULL) {
|
|
/*
|
|
* Clear state and deactivate this key processor
|
|
*/
|
|
return FALSE;
|
|
}
|
|
|
|
if ((pKe->usFlaggedVk & 0xFF) != VK_RMENU) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (!(pKe->usFlaggedVk & KBDBREAK)) {
|
|
/*
|
|
* If neither CTRL key is down, then fake one going down so that
|
|
* the right hand ALT key is converted to CTRL + ALT.
|
|
*/
|
|
gbAltGrDown = TRUE;
|
|
if (!TestKeyDownBit(gafPhysKeyState, VK_CONTROL)) {
|
|
_KeyEvent(VK_LCONTROL, 0x1D | SCANCODE_SIMULATED, 0);
|
|
}
|
|
} else {
|
|
/*
|
|
* If the physical Left Ctrl key is not really down, fake the
|
|
* Left Ctrl key coming back up (undo earlier faked Left Ctrl down)
|
|
*/
|
|
gbAltGrDown = FALSE;
|
|
if (!TestKeyDownBit(gafPhysKeyState, VK_LCONTROL)) {
|
|
_KeyEvent(VK_LCONTROL | KBDBREAK, 0x1D | SCANCODE_SIMULATED, 0);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Returning FALSE means the Key Event has been deleted by a special-case
|
|
* KeyEvent processor.
|
|
* Returning TRUE means the Key Event should be passed on (although it may
|
|
* have been altered.
|
|
*/
|
|
BOOL KEOEMProcs(PKE pKe)
|
|
{
|
|
int i;
|
|
|
|
CheckCritIn();
|
|
|
|
for (i = 0; aKEProcOEM[i] != NULL; i++) {
|
|
if (!aKEProcOEM[i](pKe)) {
|
|
/*
|
|
* Eat the key event
|
|
*/
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Pass the (possibly altered) key event on.
|
|
*/
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Returning FALSE means the Key Event has been deleted by a special-case
|
|
* KeyEvent processor.
|
|
* Returning TRUE means the Key Event should be passed on (although it may
|
|
* have been altered.
|
|
*/
|
|
BOOL KELocaleProcs(PKE pKe)
|
|
{
|
|
CheckCritIn();
|
|
|
|
if ((gpKbdTbl->fLocaleFlags & KLLF_ALTGR) || gbAltGrDown) {
|
|
if (!AltGr(pKe)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Other special Key Event processors
|
|
*/
|
|
|
|
return TRUE;
|
|
}
|