Leaked source code of windows server 2003
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.
 
 
 
 
 
 

925 lines
33 KiB

/****************************** Module Header ******************************\
* Module Name: tounicod.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* History:
* 02-08-92 GregoryW Created.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/*
* "To a new truth there is nothing more hurtful than an old error."
* - Johann Wolfgang von Goethe (1749-1832)
*/
/*
* macros used locally to make life easier
*/
#define ISCAPSLOCKON(pf) (TestKeyToggleBit(pf, VK_CAPITAL) != 0)
#define ISNUMLOCKON(pf) (TestKeyToggleBit(pf, VK_NUMLOCK) != 0)
#define ISSHIFTDOWN(w) (w & 0x01)
#define ISKANALOCKON(pf) (TestKeyToggleBit(pf, VK_KANA) != 0)
WCHAR xxxClientCharToWchar(
IN WORD CodePage,
IN WORD wch);
/***************************************************************************\
* _ToUnicodeEx (API)
*
* This routine provides Unicode translation for the virtual key code
* passed in.
*
* History:
* 02-10-92 GregoryW Created.
* 01-23-95 GregoryW Expanded from _ToUnicode to _ToUnicodeEx
\***************************************************************************/
int xxxToUnicodeEx(
UINT wVirtKey,
UINT wScanCode,
CONST BYTE *pbKeyState,
LPWSTR pwszBuff,
int cchBuff,
UINT wKeyFlags,
HKL hkl)
{
int i;
BYTE afKeyState[CBKEYSTATE];
DWORD dwDummy;
/*
* pKeyState is an array of 256 bytes, each byte representing the
* following virtual key state: 0x80 means down, 0x01 means toggled.
* InternalToUnicode() takes an array of bits, so pKeyState needs to
* be translated. _ToAscii only a public api and rarely gets called,
* so this is no big deal.
*/
for (i = 0; i < 256; i++, pbKeyState++) {
if (*pbKeyState & 0x80) {
SetKeyDownBit(afKeyState, i);
} else {
ClearKeyDownBit(afKeyState, i);
}
if (*pbKeyState & 0x01) {
SetKeyToggleBit(afKeyState, i);
} else {
ClearKeyToggleBit(afKeyState, i);
}
}
i = xxxInternalToUnicode(wVirtKey, wScanCode, afKeyState, pwszBuff, cchBuff,
wKeyFlags, &dwDummy, hkl);
return i;
}
int ComposeDeadKeys(
PKL pkl,
PDEADKEY pDeadKey,
WCHAR wchTyped,
WORD *pUniChar,
INT cChar,
BOOL bBreak)
{
/*
* Attempt to compose this sequence:
*/
DWORD dwBoth;
TAGMSG4(DBGTAG_ToUnicode | RIP_THERESMORE,
"ComposeDeadKeys dead '%C'(%x)+base '%C'(%x)",
pkl->wchDiacritic, pkl->wchDiacritic,
wchTyped, wchTyped);
TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME | RIP_THERESMORE,
"cChar = %d, bBreak = %d", cChar, bBreak);
UserAssert(pDeadKey);
if (cChar < 1) {
TAGMSG0(DBGTAG_ToUnicode | RIP_NONAME,
"return 0 because cChar < 1");
return 0;
}
/*
* Use the layout's built-in table for dead char composition
*/
dwBoth = MAKELONG(wchTyped, pkl->wchDiacritic);
if (pDeadKey != NULL) {
/*
* Don't let character upstrokes erase the cached dead char: else
* if this was the dead char key again (being released after the
* AltGr is released) the dead char would be prematurely cleared.
*/
if (!bBreak) {
pkl->wchDiacritic = 0;
}
while (pDeadKey->dwBoth != 0) {
if (pDeadKey->dwBoth == dwBoth) {
/*
* found a composition
*/
if (pDeadKey->uFlags & DKF_DEAD) {
/*
* Dead again! Save the new 'dead' key
*/
if (!bBreak) {
pkl->wchDiacritic = (WORD)pDeadKey->wchComposed;
}
TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME,
"return -1 with dead char '%C'(%x)",
pkl->wchDiacritic, pkl->wchDiacritic);
return -1;
}
*pUniChar = (WORD)pDeadKey->wchComposed;
TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME,
"return 1 with char '%C'(%x)",
*pUniChar, *pUniChar);
return 1;
}
pDeadKey++;
}
}
*pUniChar++ = HIWORD(dwBoth);
if (cChar > 1) {
*pUniChar = LOWORD(dwBoth);
TAGMSG4(DBGTAG_ToUnicode | RIP_NONAME,
"return 2 with uncomposed chars '%C'(%x), '%C'(%x)",
*(pUniChar-1), *(pUniChar-1), *pUniChar, *pUniChar);
return 2;
}
TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME | RIP_THERESMORE,
"return 1 - only one char '%C'(%x) because cChar is 1, '%C'(%x)",
*(pUniChar-1), *(pUniChar-1));
TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME,
" the second char would have been '%C'(%x)",
LOWORD(dwBoth), LOWORD(dwBoth));
return 1;
}
/*
* TranslateInjectedVKey
*
* Returns the number of characters (cch) translated.
*
* Note on VK_PACKET:
* Currently, the only purpose of VK_PACKET is to inject a Unicode character
* into the input stream, but it is intended to be extensible to include other
* manipulations of the input stream (including the message loop so that IMEs
* can be involved). For example, we might send commands to the IME or other
* parts of the system.
* For Unicode character injection, we tried widening virtual keys to 32 bits
* of the form nnnn00e7, where nnnn is 0x0000 - 0xFFFF (representing Unicode
* characters 0x0000 - 0xFFFF) See KEYEVENTF_UNICODE.
* But many apps truncate wParam to 16-bits (poorly ported from 16-bits?) and
* several AV with these VKs (indexing into a table by WM_KEYDOWN wParam?) so
* we have to cache the character in pti->wchInjected for TranslateMessage to
* pick up (cf. GetMessagePos, GetMessageExtraInfo and GetMessageTime)
*/
int TranslateInjectedVKey(
IN UINT uScanCode,
OUT PWCHAR awchChars,
IN UINT uiTMFlags)
{
UserAssert(LOBYTE(uScanCode) == 0);
if (!(uScanCode & KBDBREAK) || (uiTMFlags & TM_POSTCHARBREAKS)) {
awchChars[0] = PtiCurrent()->wchInjected;
return 1;
}
return 0;
}
enum {
NUMPADCONV_OEMCP = 0,
NUMPADCONV_HKLCP,
NUMPADCONV_HEX_HKLCP,
NUMPADCONV_HEX_UNICODE,
};
#define NUMPADSPC_INVALID (-1)
int NumPadScanCodeToHex(UINT uScanCode, UINT uVirKey)
{
if (uScanCode >= SCANCODE_NUMPAD_FIRST && uScanCode <= SCANCODE_NUMPAD_LAST) {
int digit = aVkNumpad[uScanCode - SCANCODE_NUMPAD_FIRST];
if (digit != 0xff) {
return digit - VK_NUMPAD0;
}
return NUMPADSPC_INVALID;
}
if (gfInNumpadHexInput & NUMPAD_HEXMODE_HL) {
//
// Full keyboard
//
if (uVirKey >= L'A' && uVirKey <= L'F') {
return uVirKey - L'A' + 0xa;
}
if (uVirKey >= L'0' && uVirKey <= L'9') {
return uVirKey - L'0';
}
}
return NUMPADSPC_INVALID;
}
/*
* IsDbcsExemptionForHighAnsi
*
* returns TRUE if Unicode to ANSI conversion should be
* done on CP 1252 (Latin-1).
*
* If this function is changed, winsrv's equivalent
* routine should be changed too.
*/
BOOL IsDbcsExemptionForHighAnsi(
WORD wCodePage,
WORD wNumpadChar)
{
UserAssert(HIBYTE(wNumpadChar) == 0);
if (wCodePage == CP_JAPANESE && IS_JPN_1BYTE_KATAKANA(wNumpadChar)) {
/*
* If hkl is JAPANESE and NumpadChar is in KANA range,
* NumpadChar should be handled by the input locale.
*/
return FALSE;
}
else if (wNumpadChar >= 0x80 && wNumpadChar <= 0xff) {
/*
* Otherwise if NumpadChar is in High ANSI range,
* use 1252 for conversion.
*/
return TRUE;
}
/*
* None of the above.
* This case includes the compound Leading Byte and Trailing Byte,
* which is larger than 0xff.
*/
return FALSE;
}
#undef MODIFIER_FOR_ALT_NUMPAD
#define MODIFIER_FOR_ALT_NUMPAD(wModBit) \
((((wModBits) & ~KBDKANA) == KBDALT) || (((wModBits) & ~KBDKANA) == (KBDALT | KBDSHIFT)))
int xxxInternalToUnicode(
IN UINT uVirtKey,
IN UINT uScanCode,
CONST IN PBYTE pfvk,
OUT PWCHAR awchChars,
IN INT cChar,
IN UINT uiTMFlags,
OUT PDWORD pdwKeyFlags,
IN HKL hkl)
{
WORD wModBits;
WORD nShift;
WCHAR *pUniChar;
PVK_TO_WCHARS1 pVK;
PVK_TO_WCHAR_TABLE pVKT;
static WORD NumpadChar;
static WORD VKLastDown;
static BYTE ConvMode; // 0 == NUMPADCONV_OEMCP
PTHREADINFO ptiCurrent = PtiCurrentShared();
PKL pkl;
PKBDTABLES pKbdTbl;
PLIGATURE1 pLigature;
*pdwKeyFlags = (uScanCode & KBDBREAK);
if ((BYTE)uVirtKey == VK_UNKNOWN) {
/*
* WindowsBug 311712: this could be the case of
* unrecognized scancode.
*/
RIPMSG1(RIP_WARNING, "xxxInternalToUnicode: VK_UNKNOWN, vsc=%02x", uScanCode);
return 0;
}
if ((hkl == NULL) && ptiCurrent->spklActive) {
pkl = ptiCurrent->spklActive;
pKbdTbl = pkl->spkf->pKbdTbl;
} else {
pkl = HKLtoPKL(ptiCurrent, hkl);
if (!pkl) {
return 0;
}
pKbdTbl = pkl->spkf->pKbdTbl;
}
UserAssert(pkl != NULL);
UserAssert(pKbdTbl != NULL);
pUniChar = awchChars;
uScanCode &= (0xFF | KBDEXT);
if (*pdwKeyFlags & KBDBREAK) { // break code processing
/*
* Finalize number pad processing
*
*/
if (uVirtKey == VK_MENU) {
if (NumpadChar) {
if (ConvMode == NUMPADCONV_HEX_UNICODE) {
*pUniChar = NumpadChar;
} else if (ConvMode == NUMPADCONV_OEMCP &&
(ptiCurrent->TIF_flags & TIF_CSRSSTHREAD)) {
/*
* Pass the OEM char to Console to be converted to Unicode
* there, since we don't know the OEM codepage it is using.
* Set ALTNUMPAD_BIT for console so it knows!
*/
*pdwKeyFlags |= ALTNUMPAD_BIT;
*pUniChar = NumpadChar;
} else {
/*
* Conversion based on OEMCP or current input language.
*/
WORD wCodePage;
if (ConvMode == NUMPADCONV_OEMCP) {
// NlsOemCodePage is exported from ntoskrnl.exe.
extern __declspec(dllimport) USHORT NlsOemCodePage;
wCodePage = (WORD)NlsOemCodePage;
} else {
wCodePage = pkl->CodePage;
}
if (IS_DBCS_CODEPAGE(wCodePage)) {
if (NumpadChar & (WORD)~0xff) {
/*
* Might be a double byte character.
* Let's swab it so that NumpadChar has LB in LOBYTE,
* TB in HIBYTE.
*/
NumpadChar = MAKEWORD(HIBYTE(NumpadChar), LOBYTE(NumpadChar));
} else if (IsDbcsExemptionForHighAnsi(wCodePage, NumpadChar)) {
/*
* FarEast hack:
* treat characters in High ANSI area as if they are
* the ones of Codepage 1252.
*/
wCodePage = 1252;
}
} else {
/*
* Backward compatibility:
* Simulate the legacy modulo behavior for non-FarEast keyboard layouts.
*/
NumpadChar &= 0xff;
}
*pUniChar = xxxClientCharToWchar(wCodePage, NumpadChar);
}
/*
* Clear Alt-Numpad state, the ALT key-release generates 1 character.
*/
VKLastDown = 0;
ConvMode = NUMPADCONV_OEMCP;
NumpadChar = 0;
gfInNumpadHexInput &= ~NUMPAD_HEXMODE_HL;
return 1;
} else if (ConvMode != NUMPADCONV_OEMCP) {
ConvMode = NUMPADCONV_OEMCP;
}
} else if (uVirtKey == VKLastDown) {
/*
* The most recently depressed key has now come up: we are now
* ready to accept a new NumPad key for Alt-Numpad processing.
*/
VKLastDown = 0;
}
}
if (!(*pdwKeyFlags & KBDBREAK) || (uiTMFlags & TM_POSTCHARBREAKS)) {
/*
* Get the character modification bits.
* The bit-mask (wModBits) encodes depressed modifier keys:
* these bits are commonly KBDSHIFT, KBDALT and/or KBDCTRL
* (representing Shift, Alt and Ctrl keys respectively)
*/
wModBits = GetModifierBits(pKbdTbl->pCharModifiers, pfvk);
/*
* If the current shift state is either Alt or Alt-Shift:
*
* 1. If a menu is currently displayed then clear the
* alt bit from wModBits and proceed with normal
* translation.
*
* 2. If this is a number pad key then do alt-<numpad>
* calculations.
*
* 3. Otherwise, clear alt bit and proceed with normal
* translation.
*/
/*
* Equivalent code is in xxxKeyEvent() to check the
* low level mode. If you change this code, you may
* need to change xxxKeyEvent() as well.
*/
if (!(*pdwKeyFlags & KBDBREAK) && MODIFIER_FOR_ALT_NUMPAD(wModBits)) {
/*
* If this is a numeric numpad key
*/
if ((uiTMFlags & TM_INMENUMODE) == 0) {
if (gfEnableHexNumpad && uScanCode == SCANCODE_NUMPAD_DOT) {
if ((gfInNumpadHexInput & NUMPAD_HEXMODE_HL) == 0) {
/*
* If the first key is '.', then we're
* entering hex input lang input mode.
*/
ConvMode = NUMPADCONV_HEX_HKLCP;
/*
* Inidicate to the rest of the system
* we're in Hex Alt+Numpad mode.
*/
gfInNumpadHexInput |= NUMPAD_HEXMODE_HL;
TAGMSG0(DBGTAG_ToUnicode, "NUMPADCONV_HEX_HKLCP");
} else {
goto ExitNumpadMode;
}
} else if (gfEnableHexNumpad && uScanCode == SCANCODE_NUMPAD_PLUS) {
if ((gfInNumpadHexInput & NUMPAD_HEXMODE_HL) == 0) {
/*
* If the first key is '+', then we're
* entering hex UNICODE input mode.
*/
ConvMode = NUMPADCONV_HEX_UNICODE;
/*
* Inidicate to the rest of the system
* we're in Hex Alt+Numpad mode.
*/
gfInNumpadHexInput |= NUMPAD_HEXMODE_HL;
TAGMSG0(DBGTAG_ToUnicode, "NUMPADCONV_HEX_UNICODE");
} else {
goto ExitNumpadMode;
}
} else {
int digit = NumPadScanCodeToHex(uScanCode, uVirtKey);
if (digit < 0) {
goto ExitNumpadMode;
}
/*
* Ignore repeats
*/
if (VKLastDown == uVirtKey) {
return 0;
}
switch (ConvMode) {
case NUMPADCONV_HEX_HKLCP:
case NUMPADCONV_HEX_UNICODE:
/*
* Input is treated as hex number.
*/
TAGMSG1(DBGTAG_ToUnicode, "->NUMPADCONV_HEX_*: old NumpadChar=%02x\n", NumpadChar);
NumpadChar = NumpadChar * 0x10 + digit;
TAGMSG1(DBGTAG_ToUnicode, "<-NUMPADCONV_HEX_*: new NumpadChar=%02x\n", NumpadChar);
break;
default:
/*
* Input is treated as decimal number.
*/
NumpadChar = NumpadChar * 10 + digit;
/*
* Do Alt-Numpad0 processing
*/
if (NumpadChar == 0 && digit == 0) {
ConvMode = NUMPADCONV_HKLCP;
}
break;
}
}
VKLastDown = (WORD)uVirtKey;
} else {
ExitNumpadMode:
/*
* Clear Alt-Numpad state and the ALT shift state.
*/
VKLastDown = 0;
ConvMode = NUMPADCONV_OEMCP;
NumpadChar = 0;
wModBits &= ~KBDALT;
gfInNumpadHexInput &= ~NUMPAD_HEXMODE_HL;
}
}
/*
* LShift/RSHift+Backspace -> Left-to-Right and Right-to-Left marker
*/
if ((uVirtKey == VK_BACK) && (pKbdTbl->fLocaleFlags & KLLF_LRM_RLM)) {
if (TestKeyDownBit(pfvk, VK_LSHIFT)) {
*pUniChar = 0x200E; // LRM
return 1;
} else if (TestKeyDownBit(pfvk, VK_RSHIFT)) {
*pUniChar = 0x200F; // RLM
return 1;
}
} else if (((WORD)uVirtKey == VK_PACKET) && (LOBYTE(uScanCode) == 0)) {
return TranslateInjectedVKey(uScanCode, awchChars, uiTMFlags);
}
/*
* Scan through all the shift-state tables until a matching Virtual
* Key is found.
*/
for (pVKT = pKbdTbl->pVkToWcharTable; pVKT->pVkToWchars != NULL; pVKT++) {
pVK = pVKT->pVkToWchars;
while (pVK->VirtualKey != 0) {
if (pVK->VirtualKey == (BYTE)uVirtKey) {
goto VK_Found;
}
pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
}
}
/*
* Not found: virtual key is not a character.
*/
goto ReturnBadCharacter;
VK_Found:
/*
* The virtual key has been found in table pVKT, at entry pVK
*/
/*
* If KanaLock affects this key and it is on: toggle KANA state
* only if no other state is on. "KANALOK" attributes only exist
* in Japanese keyboard layout, and only Japanese keyboard hardware
* can be "KANA" lock on state.
*/
if ((pVK->Attributes & KANALOK) && (ISKANALOCKON(pfvk))) {
wModBits |= KBDKANA;
} else {
/*
* If CapsLock affects this key and it is on: toggle SHIFT state
* only if no other state is on.
* (CapsLock doesn't affect SHIFT state if Ctrl or Alt are down).
* OR
* If CapsLockAltGr affects this key and it is on: toggle SHIFT
* state only if both Alt & Control are down.
* (CapsLockAltGr only affects SHIFT if AltGr is being used).
*/
if ((pVK->Attributes & CAPLOK) && ((wModBits & ~KBDSHIFT) == 0) &&
ISCAPSLOCKON(pfvk)) {
wModBits ^= KBDSHIFT;
} else if ((pVK->Attributes & CAPLOKALTGR) &&
((wModBits & (KBDALT | KBDCTRL)) == (KBDALT | KBDCTRL)) &&
ISCAPSLOCKON(pfvk)) {
wModBits ^= KBDSHIFT;
}
}
/*
* If SGCAPS affects this key and CapsLock is on: use the next entry
* in the table, but not is Ctrl or Alt are down.
* (SGCAPS is used in Swiss-German, Czech and Czech 101 layouts)
*/
if ((pVK->Attributes & SGCAPS) && ((wModBits & ~KBDSHIFT) == 0) &&
ISCAPSLOCKON(pfvk)) {
pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
}
/*
* Convert the shift-state bitmask into one of the enumerated
* logical shift states.
*/
nShift = GetModificationNumber(pKbdTbl->pCharModifiers, wModBits);
if (nShift == SHFT_INVALID) {
/*
* An invalid combination of Shifter Keys
*/
goto ReturnBadCharacter;
} else if ((nShift < pVKT->nModifications) &&
(pVK->wch[nShift] != WCH_NONE)) {
/*
* There is an entry in the table for this combination of
* Shift State (nShift) and Virtual Key (uVirtKey).
*/
if (pVK->wch[nShift] == WCH_DEAD) {
/*
* It is a dead character: the next entry contains
* its value.
*/
pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
/*
* If the previous char was not dead return a dead character.
*/
if (pkl->wchDiacritic == 0) {
TAGMSG2(DBGTAG_ToUnicode,
"xxxInternalToUnicode: new dead char '%C'(%x), goto ReturnDeadCharacter",
pVK->wch[nShift], pVK->wch[nShift]);
goto ReturnDeadCharacter;
}
/*
* Else go to ReturnGoodCharacter which will attempt to
* compose this dead character with the previous dead char.
*/
/*
* N.B. NTBUG 6141
* If dead key is hit twice in sequence, Win95/98 gives
* two composed characters from dead chars...
*/
TAGMSG4(DBGTAG_ToUnicode,
"xxxInternalToUnicode: 2 dead chars '%C'(%x)+'%C'(%x)",
pkl->wchDiacritic, pkl->wchDiacritic,
pVK->wch[nShift], pVK->wch[nShift]);
if (GetAppCompatFlags2(VER40) & GACF2_NOCHAR_DEADKEY) {
/*
* AppCompat 377217: Publisher calls TranslateMessage and ToUnicode for
* the same dead key when it's not expecting real characters.
* On NT4, this resulted like "pushing the dead key in the stack and
* no character is compossed", but on NT5 with fix to 6141,
* two dead keys compose real characters clearing the internal
* dead key cache. The app shouldn't call both TranslateMessage and ToUnicode
* for the same key stroke in the first place -- in a way the app was working on
* NT4 by just a thin luck.
* In any case, since the app has been shipped broadly and hard to fix,
* let's simulate the NT4 behavior here, but with just one level cache (not the
* stack).
*/
goto ReturnDeadCharacter;
}
goto ReturnGoodCharacter;
} else if (pVK->wch[nShift] == WCH_LGTR) {
/*
* It is a ligature. Look in ligature table for a match.
*/
if ((GET_KBD_VERSION(pKbdTbl) == 0) || ((pLigature = pKbdTbl->pLigature) == NULL)) {
/*
* Hey, where's the table?
*/
xxxMessageBeep(0);
goto ReturnBadCharacter;
}
while (pLigature->VirtualKey != 0) {
int iLig = 0;
int cwchT = 0;
if ((pLigature->VirtualKey == pVK->VirtualKey) &&
(pLigature->ModificationNumber == nShift)) {
/*
* Found the ligature!
*/
while ((iLig < pKbdTbl->nLgMax) && (cwchT < cChar)) {
if (pLigature->wch[iLig] == WCH_NONE) {
/*
* End of ligature.
*/
return cwchT;
}
if (pkl->wchDiacritic != 0) {
int cComposed;
/*
* Attempt to compose the previous deadkey with current
* ligature character. If this generates yet another
* dead key, go round again without adding to pUniChar
* or cwchT.
*/
cComposed = ComposeDeadKeys(
pkl,
pKbdTbl->pDeadKey,
pLigature->wch[iLig],
pUniChar + cwchT,
cChar - cwchT,
*pdwKeyFlags & KBDBREAK
);
if (cComposed > 0) {
cwchT += cComposed;
} else {
RIPMSG2(RIP_ERROR, // we really don't expect this
"InternalToUnicode: dead+lig(%x)->dead(%x)",
pLigature->wch[0], pkl->wchDiacritic);
}
} else {
pUniChar[cwchT++] = pLigature->wch[iLig];
}
iLig++;
}
return cwchT;
}
/*
* Not a match, try the next entry.
*/
pLigature = (PLIGATURE1)((PBYTE)pLigature + pKbdTbl->cbLgEntry);
}
/*
* No match found!
*/
xxxMessageBeep(0);
goto ReturnBadCharacter;
}
/*
* Match found: return the unshifted character
*/
TAGMSG2(DBGTAG_ToUnicode,
"xxxInternalToUnicode: Match found '%C'(%x), goto ReturnGoodChar",
pVK->wch[nShift], pVK->wch[nShift]);
goto ReturnGoodCharacter;
} else if ((wModBits == KBDCTRL) || (wModBits == (KBDCTRL|KBDSHIFT)) ||
(wModBits == (KBDKANA|KBDCTRL)) || (wModBits == (KBDKANA|KBDCTRL|KBDSHIFT))) {
/*
* There was no entry for this combination of Modification (nShift)
* and Virtual Key (uVirtKey). It may still be an ASCII control
* character though:
*/
if ((uVirtKey >= 'A') && (uVirtKey <= 'Z')) {
/*
* If the virtual key is in the range A-Z we can convert
* it directly to a control character. Otherwise, we
* need to search the control key conversion table for
* a match to the virtual key.
*/
*pUniChar = (WORD)(uVirtKey & 0x1f);
return 1;
} else if ((uVirtKey >= 0xFF61) && (uVirtKey <= 0xFF91)) {
/*
* If the virtual key is in range FF61-FF91 (halfwidth
* katakana), we convert it to Virtual scan code with
* KANA modifier.
*/
*pUniChar = (WORD)(InternalVkKeyScanEx((WCHAR)uVirtKey,pKbdTbl) & 0x1f);
return 1;
}
}
}
ReturnBadCharacter:
// pkl->wchDiacritic = 0;
return 0;
ReturnDeadCharacter:
*pUniChar = pVK->wch[nShift];
/*
* Save 'dead' key: overwrite an existing one.
*/
if (!(*pdwKeyFlags & KBDBREAK)) {
pkl->wchDiacritic = *pUniChar;
}
UserAssert(pKbdTbl->pDeadKey);
/*
* return negative count for dead characters
*/
return -1;
ReturnGoodCharacter:
if ((pKbdTbl->pDeadKey != NULL) && (pkl->wchDiacritic != 0)) {
return ComposeDeadKeys(
pkl,
pKbdTbl->pDeadKey,
pVK->wch[nShift],
pUniChar,
cChar,
*pdwKeyFlags & KBDBREAK
);
}
*pUniChar = (WORD)pVK->wch[nShift];
return 1;
}
SHORT InternalVkKeyScanEx(
WCHAR wchChar,
PKBDTABLES pKbdTbl)
{
PVK_TO_WCHARS1 pVK;
PVK_TO_WCHAR_TABLE pVKT;
BYTE nShift;
WORD wModBits;
WORD wModNumCtrl, wModNumShiftCtrl;
SHORT shRetvalCtrl = 0;
SHORT shRetvalShiftCtrl = 0;
if (pKbdTbl == NULL) {
pKbdTbl = gspklBaseLayout->spkf->pKbdTbl;
}
/*
* Ctrl and Shift-Control combinations are less favored, so determine
* the values for nShift which we prefer not to use if at all possible.
* This is for compatibility with Windows 95/98, which only returns a
* Ctrl or Shift+Ctrl combo as a last resort. See bugs #78891 & #229141
*/
wModNumCtrl = GetModificationNumber(pKbdTbl->pCharModifiers, KBDCTRL);
wModNumShiftCtrl = GetModificationNumber(pKbdTbl->pCharModifiers, KBDSHIFT | KBDCTRL);
for (pVKT = pKbdTbl->pVkToWcharTable; pVKT->pVkToWchars != NULL; pVKT++) {
for (pVK = pVKT->pVkToWchars;
pVK->VirtualKey != 0;
pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize)) {
for (nShift = 0; nShift < pVKT->nModifications; nShift++) {
if (pVK->wch[nShift] == wchChar) {
/*
* A matching character has been found!
*/
if (pVK->VirtualKey == 0xff) {
/*
* dead char: back up to previous line to get the VK.
*/
pVK = (PVK_TO_WCHARS1)((PBYTE)pVK - pVKT->cbSize);
}
/*
* If this is the first Ctrl or the first Shift+Ctrl match,
* remember in case we don't find any better match.
* In the meantime, keep on looking.
*/
if (nShift == wModNumCtrl) {
if (shRetvalCtrl == 0) {
shRetvalCtrl = (SHORT)MAKEWORD(pVK->VirtualKey, KBDCTRL);
}
} else if (nShift == wModNumShiftCtrl) {
if (shRetvalShiftCtrl == 0) {
shRetvalShiftCtrl = (SHORT)MAKEWORD(pVK->VirtualKey, KBDCTRL | KBDSHIFT);
}
} else {
/*
* this seems like a very good match!
*/
goto GoodMatchFound;
}
}
}
}
}
/*
* Didn't find a good match: use whatever Ctrl/Shift+Ctrl match was found
*/
if (shRetvalCtrl) {
return shRetvalCtrl;
}
if (shRetvalShiftCtrl) {
return shRetvalShiftCtrl;
}
/*
* May be a control character not explicitly in the layout tables
*/
if (wchChar < 0x0020) {
/*
* Ctrl+char -> char - 0x40
*/
return (SHORT)MAKEWORD((wchChar + 0x40), KBDCTRL);
}
return -1;
GoodMatchFound:
/*
* Scan aModification[] to find nShift: the index will be a bitmask
* representing the Shifter Keys that need to be pressed to produce
* this Shift State.
*/
for (wModBits = 0;
wModBits <= pKbdTbl->pCharModifiers->wMaxModBits;
wModBits++)
{
if (pKbdTbl->pCharModifiers->ModNumber[wModBits] == nShift) {
if (pVK->VirtualKey == 0xff) {
/*
* The previous entry contains the actual virtual key in this case.
*/
pVK = (PVK_TO_WCHARS1)((PBYTE)pVK - pVKT->cbSize);
}
return (SHORT)MAKEWORD(pVK->VirtualKey, wModBits);
}
}
/*
* huh? should never reach here! (IanJa)
*/
UserAssertMsg1(FALSE, "InternalVkKeyScanEx error: wchChar = 0x%x", wchChar);
return -1;
}