Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

tounicod.c

Go to the documentation of this file.
00001 /****************************** Module Header ******************************\ 00002 * Module Name: tounicod.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * History: 00007 * 02-08-92 GregoryW Created. 00008 \***************************************************************************/ 00009 00010 #include "precomp.h" 00011 #pragma hdrstop 00012 00013 /* 00014 * "To a new truth there is nothing more hurtful than an old error." 00015 * - Johann Wolfgang von Goethe (1749-1832) 00016 */ 00017 00018 /* 00019 * macros used locally to make life easier 00020 */ 00021 #define ISCAPSLOCKON(pf) (TestKeyToggleBit(pf, VK_CAPITAL) != 0) 00022 #define ISNUMLOCKON(pf) (TestKeyToggleBit(pf, VK_NUMLOCK) != 0) 00023 #define ISSHIFTDOWN(w) (w & 0x01) 00024 #define ISKANALOCKON(pf) (TestKeyToggleBit(pf, VK_KANA) != 0) 00025 00026 WCHAR xxxClientCharToWchar( 00027 IN WORD CodePage, 00028 IN WORD wch); 00029 00030 /***************************************************************************\ 00031 * _ToUnicodeEx (API) 00032 * 00033 * This routine provides Unicode translation for the virtual key code 00034 * passed in. 00035 * 00036 * History: 00037 * 02-10-92 GregoryW Created. 00038 * 01-23-95 GregoryW Expanded from _ToUnicode to _ToUnicodeEx 00039 \***************************************************************************/ 00040 int xxxToUnicodeEx( 00041 UINT wVirtKey, 00042 UINT wScanCode, 00043 CONST BYTE *pbKeyState, 00044 LPWSTR pwszBuff, 00045 int cchBuff, 00046 UINT wKeyFlags, 00047 HKL hkl) 00048 { 00049 int i; 00050 BYTE afKeyState[CBKEYSTATE]; 00051 DWORD dwDummy; 00052 00053 /* 00054 * pKeyState is an array of 256 bytes, each byte representing the 00055 * following virtual key state: 0x80 means down, 0x01 means toggled. 00056 * InternalToUnicode() takes an array of bits, so pKeyState needs to 00057 * be translated. _ToAscii only a public api and rarely gets called, 00058 * so this is no big deal. 00059 */ 00060 for (i = 0; i < 256; i++, pbKeyState++) { 00061 if (*pbKeyState & 0x80) { 00062 SetKeyDownBit(afKeyState, i); 00063 } else { 00064 ClearKeyDownBit(afKeyState, i); 00065 } 00066 00067 if (*pbKeyState & 0x01) { 00068 SetKeyToggleBit(afKeyState, i); 00069 } else { 00070 ClearKeyToggleBit(afKeyState, i); 00071 } 00072 } 00073 00074 i = xxxInternalToUnicode(wVirtKey, wScanCode, afKeyState, pwszBuff, cchBuff, 00075 wKeyFlags, &dwDummy, hkl); 00076 00077 00078 return i; 00079 } 00080 00081 int ComposeDeadKeys( 00082 PKL pkl, 00083 PDEADKEY pDeadKey, 00084 WCHAR wchTyped, 00085 WORD *pUniChar, 00086 INT cChar, 00087 BOOL bBreak) 00088 { 00089 /* 00090 * Attempt to compose this sequence: 00091 */ 00092 DWORD dwBoth; 00093 00094 TAGMSG4(DBGTAG_ToUnicode | RIP_THERESMORE, 00095 "ComposeDeadKeys dead '%C'(%x)+base '%C'(%x)", 00096 pkl->wchDiacritic, pkl->wchDiacritic, 00097 wchTyped, wchTyped); 00098 TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME | RIP_THERESMORE, 00099 "cChar = %d, bBreak = %d", cChar, bBreak); 00100 UserAssert(pDeadKey); 00101 00102 if (cChar < 1) { 00103 TAGMSG0(DBGTAG_ToUnicode | RIP_NONAME, 00104 "return 0 because cChar < 1"); 00105 return 0; 00106 } 00107 00108 /* 00109 * Use the layout's built-in table for dead char composition 00110 */ 00111 dwBoth = MAKELONG(wchTyped, pkl->wchDiacritic); 00112 00113 if (pDeadKey != NULL) { 00114 /* 00115 * Don't let character upstrokes erase the cached dead char: else 00116 * if this was the dead char key again (being released after the 00117 * AltGr is released) the dead char would be prematurely cleared. 00118 */ 00119 if (!bBreak) { 00120 pkl->wchDiacritic = 0; 00121 } 00122 while (pDeadKey->dwBoth != 0) { 00123 if (pDeadKey->dwBoth == dwBoth) { 00124 /* 00125 * found a composition 00126 */ 00127 if (pDeadKey->uFlags & DKF_DEAD) { 00128 /* 00129 * Dead again! Save the new 'dead' key 00130 */ 00131 if (!bBreak) { 00132 pkl->wchDiacritic = (WORD)pDeadKey->wchComposed; 00133 } 00134 TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME, 00135 "return -1 with dead char '%C'(%x)", 00136 pkl->wchDiacritic, pkl->wchDiacritic); 00137 return -1; 00138 } 00139 *pUniChar = (WORD)pDeadKey->wchComposed; 00140 TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME, 00141 "return 1 with char '%C'(%x)", 00142 *pUniChar, *pUniChar); 00143 return 1; 00144 } 00145 pDeadKey++; 00146 } 00147 } 00148 *pUniChar++ = HIWORD(dwBoth); 00149 if (cChar > 1) { 00150 *pUniChar = LOWORD(dwBoth); 00151 TAGMSG4(DBGTAG_ToUnicode | RIP_NONAME, 00152 "return 2 with uncomposed chars '%C'(%x), '%C'(%x)", 00153 *(pUniChar-1), *(pUniChar-1), *pUniChar, *pUniChar); 00154 return 2; 00155 } 00156 TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME | RIP_THERESMORE, 00157 "return 1 - only one char '%C'(%x) because cChar is 1, '%C'(%x)", 00158 *(pUniChar-1), *(pUniChar-1)); 00159 TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME, 00160 " the second char would have been '%C'(%x)", 00161 LOWORD(dwBoth), LOWORD(dwBoth)); 00162 return 1; 00163 } 00164 00165 00166 /* 00167 * TranslateInjectedVKey 00168 * 00169 * Returns the number of characters (cch) translated. 00170 * 00171 * Note on VK_PACKET: 00172 * Currently, the only purpose of VK_PACKET is to inject a Unicode character 00173 * into the input stream, but it is intended to be extensible to include other 00174 * manipulations of the input stream (including the message loop so that IMEs 00175 * can be involved). For example, we might send commands to the IME or other 00176 * parts of the system. 00177 * For Unicode character injection, we tried widening virtual keys to 32 bits 00178 * of the form nnnn00e7, where nnnn is 0x0000 - 0xFFFF (representing Unicode 00179 * characters 0x0000 - 0xFFFF) See KEYEVENTF_UNICODE. 00180 * But many apps truncate wParam to 16-bits (poorly ported from 16-bits?) and 00181 * several AV with these VKs (indexing into a table by WM_KEYDOWN wParam?) so 00182 * we have to cache the character in pti->wchInjected for TranslateMessage to 00183 * pick up (cf. GetMessagePos, GetMessageExtraInfo and GetMessageTime) 00184 */ 00185 int TranslateInjectedVKey( 00186 IN UINT uScanCode, 00187 OUT PWCHAR awchChars, 00188 IN UINT uiTMFlags) 00189 { 00190 UserAssert(LOBYTE(uScanCode) == 0); 00191 if (!(uScanCode & KBDBREAK) || (uiTMFlags & TM_POSTCHARBREAKS)) { 00192 awchChars[0] = PtiCurrent()->wchInjected; 00193 return 1; 00194 } 00195 return 0; 00196 } 00197 00198 00199 00200 enum { 00201 NUMPADCONV_OEMCP = 0, 00202 NUMPADCONV_HKLCP, 00203 NUMPADCONV_HEX_HKLCP, 00204 NUMPADCONV_HEX_UNICODE, 00205 }; 00206 00207 #define NUMPADSPC_INVALID (-1) 00208 00209 int NumPadScanCodeToHex(UINT uScanCode, UINT uVirKey) 00210 { 00211 if (uScanCode >= SCANCODE_NUMPAD_FIRST && uScanCode <= SCANCODE_NUMPAD_LAST) { 00212 int digit = aVkNumpad[uScanCode - SCANCODE_NUMPAD_FIRST]; 00213 00214 if (digit != 0xff) { 00215 return digit - VK_NUMPAD0; 00216 } 00217 return NUMPADSPC_INVALID; 00218 } 00219 00220 if (gfInNumpadHexInput & NUMPAD_HEXMODE_HL) { 00221 // 00222 // Full keyboard 00223 // 00224 if (uVirKey >= L'A' && uVirKey <= L'F') { 00225 return uVirKey - L'A' + 0xa; 00226 } 00227 if (uVirKey >= L'0' && uVirKey <= L'9') { 00228 return uVirKey - L'0'; 00229 } 00230 } 00231 00232 return NUMPADSPC_INVALID; 00233 } 00234 00235 /* 00236 * IsDbcsExemptionForHighAnsi 00237 * 00238 * returns TRUE if Unicode to ANSI conversion should be 00239 * done on CP 1252 (Latin-1). 00240 * 00241 * If this function is changed, winsrv's equivalent 00242 * routine should be changed too. 00243 */ 00244 BOOL IsDbcsExemptionForHighAnsi( 00245 WORD wCodePage, 00246 WORD wNumpadChar) 00247 { 00248 UserAssert(HIBYTE(wNumpadChar) == 0); 00249 00250 if (wCodePage == CP_JAPANESE && IS_JPN_1BYTE_KATAKANA(wNumpadChar)) { 00251 /* 00252 * If hkl is JAPANESE and NumpadChar is in KANA range, 00253 * NumpadChar should be handled by the input locale. 00254 */ 00255 return FALSE; 00256 } 00257 else if (wNumpadChar >= 0x80 && wNumpadChar <= 0xff) { 00258 /* 00259 * Otherwise if NumpadChar is in High ANSI range, 00260 * use 1252 for conversion. 00261 */ 00262 return TRUE; 00263 } 00264 00265 /* 00266 * None of the above. 00267 * This case includes the compound Leading Byte and Trailing Byte, 00268 * which is larger than 0xff. 00269 */ 00270 return FALSE; 00271 } 00272 00273 #undef MODIFIER_FOR_ALT_NUMPAD 00274 00275 #define MODIFIER_FOR_ALT_NUMPAD(wModBit) \ 00276 ((((wModBits) & ~KBDKANA) == KBDALT) || (((wModBits) & ~KBDKANA) == (KBDALT | KBDSHIFT))) 00277 00278 00279 int xxxInternalToUnicode( 00280 IN UINT uVirtKey, 00281 IN UINT uScanCode, 00282 CONST IN PBYTE pfvk, 00283 OUT PWCHAR awchChars, 00284 IN INT cChar, 00285 IN UINT uiTMFlags, 00286 OUT PDWORD pdwKeyFlags, 00287 IN HKL hkl) 00288 { 00289 WORD wModBits; 00290 WORD nShift; 00291 WCHAR *pUniChar; 00292 PVK_TO_WCHARS1 pVK; 00293 PVK_TO_WCHAR_TABLE pVKT; 00294 static WORD NumpadChar; 00295 static WORD VKLastDown; 00296 static BYTE ConvMode; // 0 == NUMPADCONV_OEMCP 00297 PTHREADINFO ptiCurrent = PtiCurrentShared(); 00298 PKL pkl; 00299 PKBDTABLES pKbdTbl; 00300 PLIGATURE1 pLigature; 00301 00302 *pdwKeyFlags = (uScanCode & KBDBREAK); 00303 00304 if ((hkl == NULL) && ptiCurrent->spklActive) { 00305 pkl = ptiCurrent->spklActive; 00306 pKbdTbl = pkl->spkf->pKbdTbl; 00307 } else { 00308 pkl = HKLtoPKL(ptiCurrent, hkl); 00309 if (!pkl) { 00310 return 0; 00311 } 00312 pKbdTbl = pkl->spkf->pKbdTbl; 00313 } 00314 UserAssert(pkl != NULL); 00315 UserAssert(pKbdTbl != NULL); 00316 00317 pUniChar = awchChars; 00318 00319 uScanCode &= (0xFF | KBDEXT); 00320 00321 if (*pdwKeyFlags & KBDBREAK) { // break code processing 00322 /* 00323 * Finalize number pad processing 00324 * 00325 */ 00326 if (uVirtKey == VK_MENU) { 00327 if (NumpadChar) { 00328 if (ConvMode == NUMPADCONV_HEX_UNICODE) { 00329 *pUniChar = NumpadChar; 00330 } else if (ConvMode == NUMPADCONV_OEMCP && 00331 (ptiCurrent->TIF_flags & TIF_CSRSSTHREAD)) { 00332 /* 00333 * Pass the OEM char to Console to be converted to Unicode 00334 * there, since we don't know the OEM codepage it is using. 00335 * Set ALTNUMPAD_BIT for console so it knows! 00336 */ 00337 *pdwKeyFlags |= ALTNUMPAD_BIT; 00338 *pUniChar = NumpadChar; 00339 } else { 00340 /* 00341 * Conversion based on OEMCP or current input language. 00342 */ 00343 WORD wCodePage; 00344 00345 if (ConvMode == NUMPADCONV_OEMCP) { 00346 // NlsOemCodePage is exported from ntoskrnl.exe. 00347 extern __declspec(dllimport) USHORT NlsOemCodePage; 00348 00349 wCodePage = (WORD)NlsOemCodePage; 00350 } else { 00351 wCodePage = pkl->CodePage; 00352 } 00353 if (IS_DBCS_CODEPAGE(wCodePage)) { 00354 if (NumpadChar & (WORD)~0xff) { 00355 /* 00356 * Might be a double byte character. 00357 * Let's swab it so that NumpadChar has LB in LOBYTE, 00358 * TB in HIBYTE. 00359 */ 00360 NumpadChar = MAKEWORD(HIBYTE(NumpadChar), LOBYTE(NumpadChar)); 00361 } else if (IsDbcsExemptionForHighAnsi(wCodePage, NumpadChar)) { 00362 /* 00363 * FarEast hack: 00364 * treat characters in High ANSI area as if they are 00365 * the ones of Codepage 1252. 00366 */ 00367 wCodePage = 1252; 00368 } 00369 } else { 00370 /* 00371 * Backward compatibility: 00372 * Simulate the legacy modulo behavior for non-FarEast keyboard layouts. 00373 */ 00374 NumpadChar &= 0xff; 00375 } 00376 00377 *pUniChar = xxxClientCharToWchar(wCodePage, NumpadChar); 00378 } 00379 00380 /* 00381 * Clear Alt-Numpad state, the ALT key-release generates 1 character. 00382 */ 00383 VKLastDown = 0; 00384 ConvMode = NUMPADCONV_OEMCP; 00385 NumpadChar = 0; 00386 gfInNumpadHexInput &= ~NUMPAD_HEXMODE_HL; 00387 00388 return 1; 00389 } else if (ConvMode != NUMPADCONV_OEMCP) { 00390 ConvMode = NUMPADCONV_OEMCP; 00391 } 00392 } else if (uVirtKey == VKLastDown) { 00393 /* 00394 * The most recently depressed key has now come up: we are now 00395 * ready to accept a new NumPad key for Alt-Numpad processing. 00396 */ 00397 VKLastDown = 0; 00398 } 00399 } 00400 00401 if (!(*pdwKeyFlags & KBDBREAK) || (uiTMFlags & TM_POSTCHARBREAKS)) { 00402 /* 00403 * Get the character modification bits. 00404 * The bit-mask (wModBits) encodes depressed modifier keys: 00405 * these bits are commonly KBDSHIFT, KBDALT and/or KBDCTRL 00406 * (representing Shift, Alt and Ctrl keys respectively) 00407 */ 00408 wModBits = GetModifierBits(pKbdTbl->pCharModifiers, pfvk); 00409 00410 /* 00411 * If the current shift state is either Alt or Alt-Shift: 00412 * 00413 * 1. If a menu is currently displayed then clear the 00414 * alt bit from wModBits and proceed with normal 00415 * translation. 00416 * 00417 * 2. If this is a number pad key then do alt-<numpad> 00418 * calculations. 00419 * 00420 * 3. Otherwise, clear alt bit and proceed with normal 00421 * translation. 00422 */ 00423 00424 /* 00425 * Equivalent code is in xxxKeyEvent() to check the 00426 * low level mode. If you change this code, you may 00427 * need to change xxxKeyEvent() as well. 00428 */ 00429 if (!(*pdwKeyFlags & KBDBREAK) && MODIFIER_FOR_ALT_NUMPAD(wModBits)) { 00430 /* 00431 * If this is a numeric numpad key 00432 */ 00433 if ((uiTMFlags & TM_INMENUMODE) == 0) { 00434 if (gfEnableHexNumpad && uScanCode == SCANCODE_NUMPAD_DOT) { 00435 if ((gfInNumpadHexInput & NUMPAD_HEXMODE_HL) == 0) { 00436 /* 00437 * If the first key is '.', then we're 00438 * entering hex input lang input mode. 00439 */ 00440 ConvMode = NUMPADCONV_HEX_HKLCP; 00441 /* 00442 * Inidicate to the rest of the system 00443 * we're in Hex Alt+Numpad mode. 00444 */ 00445 gfInNumpadHexInput |= NUMPAD_HEXMODE_HL; 00446 TAGMSG0(DBGTAG_ToUnicode, "NUMPADCONV_HEX_HKLCP"); 00447 } else { 00448 goto ExitNumpadMode; 00449 } 00450 } else if (gfEnableHexNumpad && uScanCode == SCANCODE_NUMPAD_PLUS) { 00451 if ((gfInNumpadHexInput & NUMPAD_HEXMODE_HL) == 0) { 00452 /* 00453 * If the first key is '+', then we're 00454 * entering hex UNICODE input mode. 00455 */ 00456 ConvMode = NUMPADCONV_HEX_UNICODE; 00457 /* 00458 * Inidicate to the rest of the system 00459 * we're in Hex Alt+Numpad mode. 00460 */ 00461 gfInNumpadHexInput |= NUMPAD_HEXMODE_HL; 00462 TAGMSG0(DBGTAG_ToUnicode, "NUMPADCONV_HEX_UNICODE"); 00463 } else { 00464 goto ExitNumpadMode; 00465 } 00466 } else { 00467 int digit = NumPadScanCodeToHex(uScanCode, uVirtKey); 00468 00469 if (digit < 0) { 00470 goto ExitNumpadMode; 00471 } 00472 00473 /* 00474 * Ignore repeats 00475 */ 00476 if (VKLastDown == uVirtKey) { 00477 return 0; 00478 } 00479 00480 switch (ConvMode) { 00481 case NUMPADCONV_HEX_HKLCP: 00482 case NUMPADCONV_HEX_UNICODE: 00483 /* 00484 * Input is treated as hex number. 00485 */ 00486 TAGMSG1(DBGTAG_ToUnicode, "->NUMPADCONV_HEX_*: old NumpadChar=%02x\n", NumpadChar); 00487 NumpadChar = NumpadChar * 0x10 + digit; 00488 TAGMSG1(DBGTAG_ToUnicode, "<-NUMPADCONV_HEX_*: new NumpadChar=%02x\n", NumpadChar); 00489 break; 00490 default: 00491 /* 00492 * Input is treated as decimal number. 00493 */ 00494 NumpadChar = NumpadChar * 10 + digit; 00495 00496 /* 00497 * Do Alt-Numpad0 processing 00498 */ 00499 if (NumpadChar == 0 && digit == 0) { 00500 ConvMode = NUMPADCONV_HKLCP; 00501 } 00502 break; 00503 } 00504 } 00505 VKLastDown = (WORD)uVirtKey; 00506 } else { 00507 ExitNumpadMode: 00508 /* 00509 * Clear Alt-Numpad state and the ALT shift state. 00510 */ 00511 VKLastDown = 0; 00512 ConvMode = NUMPADCONV_OEMCP; 00513 NumpadChar = 0; 00514 wModBits &= ~KBDALT; 00515 gfInNumpadHexInput &= ~NUMPAD_HEXMODE_HL; 00516 } 00517 } 00518 00519 /* 00520 * LShift/RSHift+Backspace -> Left-to-Right and Right-to-Left marker 00521 */ 00522 if ((uVirtKey == VK_BACK) && (pKbdTbl->fLocaleFlags & KLLF_LRM_RLM)) { 00523 if (TestKeyDownBit(pfvk, VK_LSHIFT)) { 00524 *pUniChar = 0x200E; // LRM 00525 return 1; 00526 } else if (TestKeyDownBit(pfvk, VK_RSHIFT)) { 00527 *pUniChar = 0x200F; // RLM 00528 return 1; 00529 } 00530 } else if (((WORD)uVirtKey == VK_PACKET) && (LOBYTE(uScanCode) == 0)) { 00531 return TranslateInjectedVKey(uScanCode, awchChars, uiTMFlags); 00532 } 00533 00534 /* 00535 * Scan through all the shift-state tables until a matching Virtual 00536 * Key is found. 00537 */ 00538 for (pVKT = pKbdTbl->pVkToWcharTable; pVKT->pVkToWchars != NULL; pVKT++) { 00539 pVK = pVKT->pVkToWchars; 00540 while (pVK->VirtualKey != 0) { 00541 if (pVK->VirtualKey == (BYTE)uVirtKey) { 00542 goto VK_Found; 00543 } 00544 pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize); 00545 } 00546 } 00547 00548 /* 00549 * Not found: virtual key is not a character. 00550 */ 00551 goto ReturnBadCharacter; 00552 00553 VK_Found: 00554 /* 00555 * The virtual key has been found in table pVKT, at entry pVK 00556 */ 00557 00558 /* 00559 * If KanaLock affects this key and it is on: toggle KANA state 00560 * only if no other state is on. "KANALOK" attributes only exist 00561 * in Japanese keyboard layout, and only Japanese keyboard hardware 00562 * can be "KANA" lock on state. 00563 */ 00564 if ((pVK->Attributes & KANALOK) && (ISKANALOCKON(pfvk))) { 00565 wModBits |= KBDKANA; 00566 } else 00567 /* 00568 * If CapsLock affects this key and it is on: toggle SHIFT state 00569 * only if no other state is on. 00570 * (CapsLock doesn't affect SHIFT state if Ctrl or Alt are down). 00571 * OR 00572 * If CapsLockAltGr affects this key and it is on: toggle SHIFT 00573 * state only if both Alt & Control are down. 00574 * (CapsLockAltGr only affects SHIFT if AltGr is being used). 00575 */ 00576 if ((pVK->Attributes & CAPLOK) && ((wModBits & ~KBDSHIFT) == 0) && 00577 ISCAPSLOCKON(pfvk)) { 00578 wModBits ^= KBDSHIFT; 00579 } else if ((pVK->Attributes & CAPLOKALTGR) && 00580 ((wModBits & (KBDALT | KBDCTRL)) == (KBDALT | KBDCTRL)) && 00581 ISCAPSLOCKON(pfvk)) { 00582 wModBits ^= KBDSHIFT; 00583 } 00584 00585 /* 00586 * If SGCAPS affects this key and CapsLock is on: use the next entry 00587 * in the table, but not is Ctrl or Alt are down. 00588 * (SGCAPS is used in Swiss-German, Czech and Czech 101 layouts) 00589 */ 00590 if ((pVK->Attributes & SGCAPS) && ((wModBits & ~KBDSHIFT) == 0) && 00591 ISCAPSLOCKON(pfvk)) { 00592 pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize); 00593 } 00594 00595 /* 00596 * Convert the shift-state bitmask into one of the enumerated 00597 * logical shift states. 00598 */ 00599 nShift = GetModificationNumber(pKbdTbl->pCharModifiers, wModBits); 00600 00601 if (nShift == SHFT_INVALID) { 00602 /* 00603 * An invalid combination of Shifter Keys 00604 */ 00605 goto ReturnBadCharacter; 00606 00607 } else if ((nShift < pVKT->nModifications) && 00608 (pVK->wch[nShift] != WCH_NONE)) { 00609 /* 00610 * There is an entry in the table for this combination of 00611 * Shift State (nShift) and Virtual Key (uVirtKey). 00612 */ 00613 if (pVK->wch[nShift] == WCH_DEAD) { 00614 /* 00615 * It is a dead character: the next entry contains 00616 * its value. 00617 */ 00618 pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize); 00619 00620 /* 00621 * If the previous char was not dead return a dead character. 00622 */ 00623 if (pkl->wchDiacritic == 0) { 00624 TAGMSG2(DBGTAG_ToUnicode, 00625 "xxxInternalToUnicode: new dead char '%C'(%x), goto ReturnDeadCharacter", 00626 pVK->wch[nShift], pVK->wch[nShift]); 00627 goto ReturnDeadCharacter; 00628 } 00629 /* 00630 * Else go to ReturnGoodCharacter which will attempt to 00631 * compose this dead character with the previous dead char. 00632 */ 00633 /* 00634 * N.B. NTBUG 6141 00635 * If dead key is hit twice in sequence, Win95/98 gives 00636 * two composed characters from dead chars... 00637 */ 00638 TAGMSG4(DBGTAG_ToUnicode, 00639 "xxxInternalToUnicode: 2 dead chars '%C'(%x)+'%C'(%x)", 00640 pkl->wchDiacritic, pkl->wchDiacritic, 00641 pVK->wch[nShift], pVK->wch[nShift]); 00642 if (GetAppCompatFlags2(VER40) & GACF2_NOCHAR_DEADKEY) { 00643 /* 00644 * AppCompat 377217: Publisher calls TranslateMessage and ToUnicode for 00645 * the same dead key when it's not expecting real characters. 00646 * On NT4, this resulted like "pushing the dead key in the stack and 00647 * no character is compossed", but on NT5 with fix to 6141, 00648 * two dead keys compose real characters clearing the internal 00649 * dead key cache. The app shouldn't call both TranslateMessage and ToUnicode 00650 * for the same key stroke in the first place -- in a way the app was working on 00651 * NT4 by just a thin luck. 00652 * In any case, since the app has been shipped broadly and hard to fix, 00653 * let's simulate the NT4 behavior here, but with just one level cache (not the 00654 * stack). 00655 */ 00656 goto ReturnDeadCharacter; 00657 } 00658 00659 goto ReturnGoodCharacter; 00660 00661 } else if (pVK->wch[nShift] == WCH_LGTR) { 00662 /* 00663 * It is a ligature. Look in ligature table for a match. 00664 */ 00665 if ((GET_KBD_VERSION(pKbdTbl) == 0) || ((pLigature = pKbdTbl->pLigature) == NULL)) { 00666 /* 00667 * Hey, where's the table? 00668 */ 00669 xxxMessageBeep(0); 00670 goto ReturnBadCharacter; 00671 } 00672 00673 while (pLigature->VirtualKey != 0) { 00674 int iLig = 0; 00675 int cwchT = 0; 00676 00677 if ((pLigature->VirtualKey == pVK->VirtualKey) && 00678 (pLigature->ModificationNumber == nShift)) { 00679 /* 00680 * Found the ligature! 00681 */ 00682 while ((iLig < pKbdTbl->nLgMax) && (cwchT < cChar)) { 00683 if (pLigature->wch[iLig] == WCH_NONE) { 00684 /* 00685 * End of ligature. 00686 */ 00687 return cwchT; 00688 } 00689 if (pkl->wchDiacritic != 0) { 00690 int cComposed; 00691 /* 00692 * Attempt to compose the previous deadkey with current 00693 * ligature character. If this generates yet another 00694 * dead key, go round again without adding to pUniChar 00695 * or cwchT. 00696 */ 00697 cComposed = ComposeDeadKeys( 00698 pkl, 00699 pKbdTbl->pDeadKey, 00700 pLigature->wch[iLig], 00701 pUniChar + cwchT, 00702 cChar - cwchT, 00703 *pdwKeyFlags & KBDBREAK 00704 ); 00705 if (cComposed > 0) { 00706 cwchT += cComposed; 00707 } else { 00708 RIPMSG2(RIP_ERROR, // we really don't expect this 00709 "InternalToUnicode: dead+lig(%x)->dead(%x)", 00710 pLigature->wch[0], pkl->wchDiacritic); 00711 } 00712 } else { 00713 pUniChar[cwchT++] = pLigature->wch[iLig]; 00714 } 00715 iLig++; 00716 } 00717 return cwchT; 00718 } 00719 /* 00720 * Not a match, try the next entry. 00721 */ 00722 pLigature = (PLIGATURE1)((PBYTE)pLigature + pKbdTbl->cbLgEntry); 00723 } 00724 /* 00725 * No match found! 00726 */ 00727 xxxMessageBeep(0); 00728 goto ReturnBadCharacter; 00729 } 00730 00731 /* 00732 * Match found: return the unshifted character 00733 */ 00734 TAGMSG2(DBGTAG_ToUnicode, 00735 "xxxInternalToUnicode: Match found '%C'(%x), goto ReturnGoodChar", 00736 pVK->wch[nShift], pVK->wch[nShift]); 00737 goto ReturnGoodCharacter; 00738 00739 } else if ((wModBits == KBDCTRL) || (wModBits == (KBDCTRL|KBDSHIFT)) || 00740 (wModBits == (KBDKANA|KBDCTRL)) || (wModBits == (KBDKANA|KBDCTRL|KBDSHIFT))) { 00741 /* 00742 * There was no entry for this combination of Modification (nShift) 00743 * and Virtual Key (uVirtKey). It may still be an ASCII control 00744 * character though: 00745 */ 00746 if ((uVirtKey >= 'A') && (uVirtKey <= 'Z')) { 00747 /* 00748 * If the virtual key is in the range A-Z we can convert 00749 * it directly to a control character. Otherwise, we 00750 * need to search the control key conversion table for 00751 * a match to the virtual key. 00752 */ 00753 *pUniChar = (WORD)(uVirtKey & 0x1f); 00754 return 1; 00755 } else if ((uVirtKey >= 0xFF61) && (uVirtKey <= 0xFF91)) { 00756 /* 00757 * If the virtual key is in range FF61-FF91 (halfwidth 00758 * katakana), we convert it to Virtual scan code with 00759 * KANA modifier. 00760 */ 00761 *pUniChar = (WORD)(InternalVkKeyScanEx((WCHAR)uVirtKey,pKbdTbl) & 0x1f); 00762 return 1; 00763 } 00764 } 00765 } 00766 00767 ReturnBadCharacter: 00768 // pkl->wchDiacritic = 0; 00769 return 0; 00770 00771 ReturnDeadCharacter: 00772 *pUniChar = pVK->wch[nShift]; 00773 00774 /* 00775 * Save 'dead' key: overwrite an existing one. 00776 */ 00777 if (!(*pdwKeyFlags & KBDBREAK)) { 00778 pkl->wchDiacritic = *pUniChar; 00779 } 00780 00781 UserAssert(pKbdTbl->pDeadKey); 00782 00783 /* 00784 * return negative count for dead characters 00785 */ 00786 return -1; 00787 00788 ReturnGoodCharacter: 00789 if ((pKbdTbl->pDeadKey != NULL) && (pkl->wchDiacritic != 0)) { 00790 return ComposeDeadKeys( 00791 pkl, 00792 pKbdTbl->pDeadKey, 00793 pVK->wch[nShift], 00794 pUniChar, 00795 cChar, 00796 *pdwKeyFlags & KBDBREAK 00797 ); 00798 } 00799 *pUniChar = (WORD)pVK->wch[nShift]; 00800 return 1; 00801 } 00802 00803 SHORT InternalVkKeyScanEx( 00804 WCHAR wchChar, 00805 PKBDTABLES pKbdTbl) 00806 { 00807 PVK_TO_WCHARS1 pVK; 00808 PVK_TO_WCHAR_TABLE pVKT; 00809 BYTE nShift; 00810 WORD wModBits; 00811 WORD wModNumCtrl, wModNumShiftCtrl; 00812 SHORT shRetvalCtrl = 0; 00813 SHORT shRetvalShiftCtrl = 0; 00814 00815 if (pKbdTbl == NULL) { 00816 pKbdTbl = gspklBaseLayout->spkf->pKbdTbl; 00817 } 00818 00819 /* 00820 * Ctrl and Shift-Control combinations are less favored, so determine 00821 * the values for nShift which we prefer not to use if at all possible. 00822 * This is for compatibility with Windows 95/98, which only returns a 00823 * Ctrl or Shift+Ctrl combo as a last resort. See bugs #78891 & #229141 00824 */ 00825 wModNumCtrl = GetModificationNumber(pKbdTbl->pCharModifiers, KBDCTRL); 00826 wModNumShiftCtrl = GetModificationNumber(pKbdTbl->pCharModifiers, KBDSHIFT | KBDCTRL); 00827 00828 for (pVKT = pKbdTbl->pVkToWcharTable; pVKT->pVkToWchars != NULL; pVKT++) { 00829 for (pVK = pVKT->pVkToWchars; 00830 pVK->VirtualKey != 0; 00831 pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize)) { 00832 for (nShift = 0; nShift < pVKT->nModifications; nShift++) { 00833 if (pVK->wch[nShift] == wchChar) { 00834 /* 00835 * A matching character has been found! 00836 */ 00837 if (pVK->VirtualKey == 0xff) { 00838 /* 00839 * dead char: back up to previous line to get the VK. 00840 */ 00841 pVK = (PVK_TO_WCHARS1)((PBYTE)pVK - pVKT->cbSize); 00842 } 00843 00844 /* 00845 * If this is the first Ctrl or the first Shift+Ctrl match, 00846 * remember in case we don't find any better match. 00847 * In the meantime, keep on looking. 00848 */ 00849 if (nShift == wModNumCtrl) { 00850 if (shRetvalCtrl == 0) { 00851 shRetvalCtrl = (SHORT)MAKEWORD(pVK->VirtualKey, KBDCTRL); 00852 } 00853 } else if (nShift == wModNumShiftCtrl) { 00854 if (shRetvalShiftCtrl == 0) { 00855 shRetvalShiftCtrl = (SHORT)MAKEWORD(pVK->VirtualKey, KBDCTRL | KBDSHIFT); 00856 } 00857 } else { 00858 /* 00859 * this seems like a very good match! 00860 */ 00861 goto GoodMatchFound; 00862 } 00863 } 00864 } 00865 } 00866 } 00867 00868 /* 00869 * Didn't find a good match: use whatever Ctrl/Shift+Ctrl match was found 00870 */ 00871 if (shRetvalCtrl) { 00872 return shRetvalCtrl; 00873 } 00874 if (shRetvalShiftCtrl) { 00875 return shRetvalShiftCtrl; 00876 } 00877 00878 /* 00879 * May be a control character not explicitly in the layout tables 00880 */ 00881 if (wchChar < 0x0020) { 00882 /* 00883 * Ctrl+char -> char - 0x40 00884 */ 00885 return (SHORT)MAKEWORD((wchChar + 0x40), KBDCTRL); 00886 } 00887 return -1; 00888 00889 GoodMatchFound: 00890 /* 00891 * Scan aModification[] to find nShift: the index will be a bitmask 00892 * representing the Shifter Keys that need to be pressed to produce 00893 * this Shift State. 00894 */ 00895 for (wModBits = 0; 00896 wModBits <= pKbdTbl->pCharModifiers->wMaxModBits; 00897 wModBits++) 00898 { 00899 if (pKbdTbl->pCharModifiers->ModNumber[wModBits] == nShift) { 00900 if (pVK->VirtualKey == 0xff) { 00901 /* 00902 * The previous entry contains the actual virtual key in this case. 00903 */ 00904 pVK = (PVK_TO_WCHARS1)((PBYTE)pVK - pVKT->cbSize); 00905 } 00906 return (SHORT)MAKEWORD(pVK->VirtualKey, wModBits); 00907 } 00908 } 00909 00910 /* 00911 * huh? should never reach here! (IanJa) 00912 */ 00913 UserAssertMsg1(FALSE, "InternalVkKeyScanEx error: wchChar = 0x%x", wchChar); 00914 return -1; 00915 }

Generated on Sat May 15 19:42:02 2004 for test by doxygen 1.3.7