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

  1. /****************************** Module Header ******************************\
  2. * Module Name: tounicod.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * History:
  7. * 02-08-92 GregoryW Created.
  8. \***************************************************************************/
  9. #include "precomp.h"
  10. #pragma hdrstop
  11. /*
  12. * "To a new truth there is nothing more hurtful than an old error."
  13. * - Johann Wolfgang von Goethe (1749-1832)
  14. */
  15. /*
  16. * macros used locally to make life easier
  17. */
  18. #define ISCAPSLOCKON(pf) (TestKeyToggleBit(pf, VK_CAPITAL) != 0)
  19. #define ISNUMLOCKON(pf) (TestKeyToggleBit(pf, VK_NUMLOCK) != 0)
  20. #define ISSHIFTDOWN(w) (w & 0x01)
  21. #define ISKANALOCKON(pf) (TestKeyToggleBit(pf, VK_KANA) != 0)
  22. WCHAR xxxClientCharToWchar(
  23. IN WORD CodePage,
  24. IN WORD wch);
  25. /***************************************************************************\
  26. * _ToUnicodeEx (API)
  27. *
  28. * This routine provides Unicode translation for the virtual key code
  29. * passed in.
  30. *
  31. * History:
  32. * 02-10-92 GregoryW Created.
  33. * 01-23-95 GregoryW Expanded from _ToUnicode to _ToUnicodeEx
  34. \***************************************************************************/
  35. int xxxToUnicodeEx(
  36. UINT wVirtKey,
  37. UINT wScanCode,
  38. CONST BYTE *pbKeyState,
  39. LPWSTR pwszBuff,
  40. int cchBuff,
  41. UINT wKeyFlags,
  42. HKL hkl)
  43. {
  44. int i;
  45. BYTE afKeyState[CBKEYSTATE];
  46. DWORD dwDummy;
  47. /*
  48. * pKeyState is an array of 256 bytes, each byte representing the
  49. * following virtual key state: 0x80 means down, 0x01 means toggled.
  50. * InternalToUnicode() takes an array of bits, so pKeyState needs to
  51. * be translated. _ToAscii only a public api and rarely gets called,
  52. * so this is no big deal.
  53. */
  54. for (i = 0; i < 256; i++, pbKeyState++) {
  55. if (*pbKeyState & 0x80) {
  56. SetKeyDownBit(afKeyState, i);
  57. } else {
  58. ClearKeyDownBit(afKeyState, i);
  59. }
  60. if (*pbKeyState & 0x01) {
  61. SetKeyToggleBit(afKeyState, i);
  62. } else {
  63. ClearKeyToggleBit(afKeyState, i);
  64. }
  65. }
  66. i = xxxInternalToUnicode(wVirtKey, wScanCode, afKeyState, pwszBuff, cchBuff,
  67. wKeyFlags, &dwDummy, hkl);
  68. return i;
  69. }
  70. int ComposeDeadKeys(
  71. PKL pkl,
  72. PDEADKEY pDeadKey,
  73. WCHAR wchTyped,
  74. WORD *pUniChar,
  75. INT cChar,
  76. BOOL bBreak)
  77. {
  78. /*
  79. * Attempt to compose this sequence:
  80. */
  81. DWORD dwBoth;
  82. TAGMSG4(DBGTAG_ToUnicode | RIP_THERESMORE,
  83. "ComposeDeadKeys dead '%C'(%x)+base '%C'(%x)",
  84. pkl->wchDiacritic, pkl->wchDiacritic,
  85. wchTyped, wchTyped);
  86. TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME | RIP_THERESMORE,
  87. "cChar = %d, bBreak = %d", cChar, bBreak);
  88. UserAssert(pDeadKey);
  89. if (cChar < 1) {
  90. TAGMSG0(DBGTAG_ToUnicode | RIP_NONAME,
  91. "return 0 because cChar < 1");
  92. return 0;
  93. }
  94. /*
  95. * Use the layout's built-in table for dead char composition
  96. */
  97. dwBoth = MAKELONG(wchTyped, pkl->wchDiacritic);
  98. if (pDeadKey != NULL) {
  99. /*
  100. * Don't let character upstrokes erase the cached dead char: else
  101. * if this was the dead char key again (being released after the
  102. * AltGr is released) the dead char would be prematurely cleared.
  103. */
  104. if (!bBreak) {
  105. pkl->wchDiacritic = 0;
  106. }
  107. while (pDeadKey->dwBoth != 0) {
  108. if (pDeadKey->dwBoth == dwBoth) {
  109. /*
  110. * found a composition
  111. */
  112. if (pDeadKey->uFlags & DKF_DEAD) {
  113. /*
  114. * Dead again! Save the new 'dead' key
  115. */
  116. if (!bBreak) {
  117. pkl->wchDiacritic = (WORD)pDeadKey->wchComposed;
  118. }
  119. TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME,
  120. "return -1 with dead char '%C'(%x)",
  121. pkl->wchDiacritic, pkl->wchDiacritic);
  122. return -1;
  123. }
  124. *pUniChar = (WORD)pDeadKey->wchComposed;
  125. TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME,
  126. "return 1 with char '%C'(%x)",
  127. *pUniChar, *pUniChar);
  128. return 1;
  129. }
  130. pDeadKey++;
  131. }
  132. }
  133. *pUniChar++ = HIWORD(dwBoth);
  134. if (cChar > 1) {
  135. *pUniChar = LOWORD(dwBoth);
  136. TAGMSG4(DBGTAG_ToUnicode | RIP_NONAME,
  137. "return 2 with uncomposed chars '%C'(%x), '%C'(%x)",
  138. *(pUniChar-1), *(pUniChar-1), *pUniChar, *pUniChar);
  139. return 2;
  140. }
  141. TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME | RIP_THERESMORE,
  142. "return 1 - only one char '%C'(%x) because cChar is 1, '%C'(%x)",
  143. *(pUniChar-1), *(pUniChar-1));
  144. TAGMSG2(DBGTAG_ToUnicode | RIP_NONAME,
  145. " the second char would have been '%C'(%x)",
  146. LOWORD(dwBoth), LOWORD(dwBoth));
  147. return 1;
  148. }
  149. /*
  150. * TranslateInjectedVKey
  151. *
  152. * Returns the number of characters (cch) translated.
  153. *
  154. * Note on VK_PACKET:
  155. * Currently, the only purpose of VK_PACKET is to inject a Unicode character
  156. * into the input stream, but it is intended to be extensible to include other
  157. * manipulations of the input stream (including the message loop so that IMEs
  158. * can be involved). For example, we might send commands to the IME or other
  159. * parts of the system.
  160. * For Unicode character injection, we tried widening virtual keys to 32 bits
  161. * of the form nnnn00e7, where nnnn is 0x0000 - 0xFFFF (representing Unicode
  162. * characters 0x0000 - 0xFFFF) See KEYEVENTF_UNICODE.
  163. * But many apps truncate wParam to 16-bits (poorly ported from 16-bits?) and
  164. * several AV with these VKs (indexing into a table by WM_KEYDOWN wParam?) so
  165. * we have to cache the character in pti->wchInjected for TranslateMessage to
  166. * pick up (cf. GetMessagePos, GetMessageExtraInfo and GetMessageTime)
  167. */
  168. int TranslateInjectedVKey(
  169. IN UINT uScanCode,
  170. OUT PWCHAR awchChars,
  171. IN UINT uiTMFlags)
  172. {
  173. UserAssert(LOBYTE(uScanCode) == 0);
  174. if (!(uScanCode & KBDBREAK) || (uiTMFlags & TM_POSTCHARBREAKS)) {
  175. awchChars[0] = PtiCurrent()->wchInjected;
  176. return 1;
  177. }
  178. return 0;
  179. }
  180. enum {
  181. NUMPADCONV_OEMCP = 0,
  182. NUMPADCONV_HKLCP,
  183. NUMPADCONV_HEX_HKLCP,
  184. NUMPADCONV_HEX_UNICODE,
  185. };
  186. #define NUMPADSPC_INVALID (-1)
  187. int NumPadScanCodeToHex(UINT uScanCode, UINT uVirKey)
  188. {
  189. if (uScanCode >= SCANCODE_NUMPAD_FIRST && uScanCode <= SCANCODE_NUMPAD_LAST) {
  190. int digit = aVkNumpad[uScanCode - SCANCODE_NUMPAD_FIRST];
  191. if (digit != 0xff) {
  192. return digit - VK_NUMPAD0;
  193. }
  194. return NUMPADSPC_INVALID;
  195. }
  196. if (gfInNumpadHexInput & NUMPAD_HEXMODE_HL) {
  197. //
  198. // Full keyboard
  199. //
  200. if (uVirKey >= L'A' && uVirKey <= L'F') {
  201. return uVirKey - L'A' + 0xa;
  202. }
  203. if (uVirKey >= L'0' && uVirKey <= L'9') {
  204. return uVirKey - L'0';
  205. }
  206. }
  207. return NUMPADSPC_INVALID;
  208. }
  209. /*
  210. * IsDbcsExemptionForHighAnsi
  211. *
  212. * returns TRUE if Unicode to ANSI conversion should be
  213. * done on CP 1252 (Latin-1).
  214. *
  215. * If this function is changed, winsrv's equivalent
  216. * routine should be changed too.
  217. */
  218. BOOL IsDbcsExemptionForHighAnsi(
  219. WORD wCodePage,
  220. WORD wNumpadChar)
  221. {
  222. UserAssert(HIBYTE(wNumpadChar) == 0);
  223. if (wCodePage == CP_JAPANESE && IS_JPN_1BYTE_KATAKANA(wNumpadChar)) {
  224. /*
  225. * If hkl is JAPANESE and NumpadChar is in KANA range,
  226. * NumpadChar should be handled by the input locale.
  227. */
  228. return FALSE;
  229. }
  230. else if (wNumpadChar >= 0x80 && wNumpadChar <= 0xff) {
  231. /*
  232. * Otherwise if NumpadChar is in High ANSI range,
  233. * use 1252 for conversion.
  234. */
  235. return TRUE;
  236. }
  237. /*
  238. * None of the above.
  239. * This case includes the compound Leading Byte and Trailing Byte,
  240. * which is larger than 0xff.
  241. */
  242. return FALSE;
  243. }
  244. #undef MODIFIER_FOR_ALT_NUMPAD
  245. #define MODIFIER_FOR_ALT_NUMPAD(wModBit) \
  246. ((((wModBits) & ~KBDKANA) == KBDALT) || (((wModBits) & ~KBDKANA) == (KBDALT | KBDSHIFT)))
  247. int xxxInternalToUnicode(
  248. IN UINT uVirtKey,
  249. IN UINT uScanCode,
  250. CONST IN PBYTE pfvk,
  251. OUT PWCHAR awchChars,
  252. IN INT cChar,
  253. IN UINT uiTMFlags,
  254. OUT PDWORD pdwKeyFlags,
  255. IN HKL hkl)
  256. {
  257. WORD wModBits;
  258. WORD nShift;
  259. WCHAR *pUniChar;
  260. PVK_TO_WCHARS1 pVK;
  261. PVK_TO_WCHAR_TABLE pVKT;
  262. static WORD NumpadChar;
  263. static WORD VKLastDown;
  264. static BYTE ConvMode; // 0 == NUMPADCONV_OEMCP
  265. PTHREADINFO ptiCurrent = PtiCurrentShared();
  266. PKL pkl;
  267. PKBDTABLES pKbdTbl;
  268. PLIGATURE1 pLigature;
  269. *pdwKeyFlags = (uScanCode & KBDBREAK);
  270. if ((BYTE)uVirtKey == VK_UNKNOWN) {
  271. /*
  272. * WindowsBug 311712: this could be the case of
  273. * unrecognized scancode.
  274. */
  275. RIPMSG1(RIP_WARNING, "xxxInternalToUnicode: VK_UNKNOWN, vsc=%02x", uScanCode);
  276. return 0;
  277. }
  278. if ((hkl == NULL) && ptiCurrent->spklActive) {
  279. pkl = ptiCurrent->spklActive;
  280. pKbdTbl = pkl->spkf->pKbdTbl;
  281. } else {
  282. pkl = HKLtoPKL(ptiCurrent, hkl);
  283. if (!pkl) {
  284. return 0;
  285. }
  286. pKbdTbl = pkl->spkf->pKbdTbl;
  287. }
  288. UserAssert(pkl != NULL);
  289. UserAssert(pKbdTbl != NULL);
  290. pUniChar = awchChars;
  291. uScanCode &= (0xFF | KBDEXT);
  292. if (*pdwKeyFlags & KBDBREAK) { // break code processing
  293. /*
  294. * Finalize number pad processing
  295. *
  296. */
  297. if (uVirtKey == VK_MENU) {
  298. if (NumpadChar) {
  299. if (ConvMode == NUMPADCONV_HEX_UNICODE) {
  300. *pUniChar = NumpadChar;
  301. } else if (ConvMode == NUMPADCONV_OEMCP &&
  302. (ptiCurrent->TIF_flags & TIF_CSRSSTHREAD)) {
  303. /*
  304. * Pass the OEM char to Console to be converted to Unicode
  305. * there, since we don't know the OEM codepage it is using.
  306. * Set ALTNUMPAD_BIT for console so it knows!
  307. */
  308. *pdwKeyFlags |= ALTNUMPAD_BIT;
  309. *pUniChar = NumpadChar;
  310. } else {
  311. /*
  312. * Conversion based on OEMCP or current input language.
  313. */
  314. WORD wCodePage;
  315. if (ConvMode == NUMPADCONV_OEMCP) {
  316. // NlsOemCodePage is exported from ntoskrnl.exe.
  317. extern __declspec(dllimport) USHORT NlsOemCodePage;
  318. wCodePage = (WORD)NlsOemCodePage;
  319. } else {
  320. wCodePage = pkl->CodePage;
  321. }
  322. if (IS_DBCS_CODEPAGE(wCodePage)) {
  323. if (NumpadChar & (WORD)~0xff) {
  324. /*
  325. * Might be a double byte character.
  326. * Let's swab it so that NumpadChar has LB in LOBYTE,
  327. * TB in HIBYTE.
  328. */
  329. NumpadChar = MAKEWORD(HIBYTE(NumpadChar), LOBYTE(NumpadChar));
  330. } else if (IsDbcsExemptionForHighAnsi(wCodePage, NumpadChar)) {
  331. /*
  332. * FarEast hack:
  333. * treat characters in High ANSI area as if they are
  334. * the ones of Codepage 1252.
  335. */
  336. wCodePage = 1252;
  337. }
  338. } else {
  339. /*
  340. * Backward compatibility:
  341. * Simulate the legacy modulo behavior for non-FarEast keyboard layouts.
  342. */
  343. NumpadChar &= 0xff;
  344. }
  345. *pUniChar = xxxClientCharToWchar(wCodePage, NumpadChar);
  346. }
  347. /*
  348. * Clear Alt-Numpad state, the ALT key-release generates 1 character.
  349. */
  350. VKLastDown = 0;
  351. ConvMode = NUMPADCONV_OEMCP;
  352. NumpadChar = 0;
  353. gfInNumpadHexInput &= ~NUMPAD_HEXMODE_HL;
  354. return 1;
  355. } else if (ConvMode != NUMPADCONV_OEMCP) {
  356. ConvMode = NUMPADCONV_OEMCP;
  357. }
  358. } else if (uVirtKey == VKLastDown) {
  359. /*
  360. * The most recently depressed key has now come up: we are now
  361. * ready to accept a new NumPad key for Alt-Numpad processing.
  362. */
  363. VKLastDown = 0;
  364. }
  365. }
  366. if (!(*pdwKeyFlags & KBDBREAK) || (uiTMFlags & TM_POSTCHARBREAKS)) {
  367. /*
  368. * Get the character modification bits.
  369. * The bit-mask (wModBits) encodes depressed modifier keys:
  370. * these bits are commonly KBDSHIFT, KBDALT and/or KBDCTRL
  371. * (representing Shift, Alt and Ctrl keys respectively)
  372. */
  373. wModBits = GetModifierBits(pKbdTbl->pCharModifiers, pfvk);
  374. /*
  375. * If the current shift state is either Alt or Alt-Shift:
  376. *
  377. * 1. If a menu is currently displayed then clear the
  378. * alt bit from wModBits and proceed with normal
  379. * translation.
  380. *
  381. * 2. If this is a number pad key then do alt-<numpad>
  382. * calculations.
  383. *
  384. * 3. Otherwise, clear alt bit and proceed with normal
  385. * translation.
  386. */
  387. /*
  388. * Equivalent code is in xxxKeyEvent() to check the
  389. * low level mode. If you change this code, you may
  390. * need to change xxxKeyEvent() as well.
  391. */
  392. if (!(*pdwKeyFlags & KBDBREAK) && MODIFIER_FOR_ALT_NUMPAD(wModBits)) {
  393. /*
  394. * If this is a numeric numpad key
  395. */
  396. if ((uiTMFlags & TM_INMENUMODE) == 0) {
  397. if (gfEnableHexNumpad && uScanCode == SCANCODE_NUMPAD_DOT) {
  398. if ((gfInNumpadHexInput & NUMPAD_HEXMODE_HL) == 0) {
  399. /*
  400. * If the first key is '.', then we're
  401. * entering hex input lang input mode.
  402. */
  403. ConvMode = NUMPADCONV_HEX_HKLCP;
  404. /*
  405. * Inidicate to the rest of the system
  406. * we're in Hex Alt+Numpad mode.
  407. */
  408. gfInNumpadHexInput |= NUMPAD_HEXMODE_HL;
  409. TAGMSG0(DBGTAG_ToUnicode, "NUMPADCONV_HEX_HKLCP");
  410. } else {
  411. goto ExitNumpadMode;
  412. }
  413. } else if (gfEnableHexNumpad && uScanCode == SCANCODE_NUMPAD_PLUS) {
  414. if ((gfInNumpadHexInput & NUMPAD_HEXMODE_HL) == 0) {
  415. /*
  416. * If the first key is '+', then we're
  417. * entering hex UNICODE input mode.
  418. */
  419. ConvMode = NUMPADCONV_HEX_UNICODE;
  420. /*
  421. * Inidicate to the rest of the system
  422. * we're in Hex Alt+Numpad mode.
  423. */
  424. gfInNumpadHexInput |= NUMPAD_HEXMODE_HL;
  425. TAGMSG0(DBGTAG_ToUnicode, "NUMPADCONV_HEX_UNICODE");
  426. } else {
  427. goto ExitNumpadMode;
  428. }
  429. } else {
  430. int digit = NumPadScanCodeToHex(uScanCode, uVirtKey);
  431. if (digit < 0) {
  432. goto ExitNumpadMode;
  433. }
  434. /*
  435. * Ignore repeats
  436. */
  437. if (VKLastDown == uVirtKey) {
  438. return 0;
  439. }
  440. switch (ConvMode) {
  441. case NUMPADCONV_HEX_HKLCP:
  442. case NUMPADCONV_HEX_UNICODE:
  443. /*
  444. * Input is treated as hex number.
  445. */
  446. TAGMSG1(DBGTAG_ToUnicode, "->NUMPADCONV_HEX_*: old NumpadChar=%02x\n", NumpadChar);
  447. NumpadChar = NumpadChar * 0x10 + digit;
  448. TAGMSG1(DBGTAG_ToUnicode, "<-NUMPADCONV_HEX_*: new NumpadChar=%02x\n", NumpadChar);
  449. break;
  450. default:
  451. /*
  452. * Input is treated as decimal number.
  453. */
  454. NumpadChar = NumpadChar * 10 + digit;
  455. /*
  456. * Do Alt-Numpad0 processing
  457. */
  458. if (NumpadChar == 0 && digit == 0) {
  459. ConvMode = NUMPADCONV_HKLCP;
  460. }
  461. break;
  462. }
  463. }
  464. VKLastDown = (WORD)uVirtKey;
  465. } else {
  466. ExitNumpadMode:
  467. /*
  468. * Clear Alt-Numpad state and the ALT shift state.
  469. */
  470. VKLastDown = 0;
  471. ConvMode = NUMPADCONV_OEMCP;
  472. NumpadChar = 0;
  473. wModBits &= ~KBDALT;
  474. gfInNumpadHexInput &= ~NUMPAD_HEXMODE_HL;
  475. }
  476. }
  477. /*
  478. * LShift/RSHift+Backspace -> Left-to-Right and Right-to-Left marker
  479. */
  480. if ((uVirtKey == VK_BACK) && (pKbdTbl->fLocaleFlags & KLLF_LRM_RLM)) {
  481. if (TestKeyDownBit(pfvk, VK_LSHIFT)) {
  482. *pUniChar = 0x200E; // LRM
  483. return 1;
  484. } else if (TestKeyDownBit(pfvk, VK_RSHIFT)) {
  485. *pUniChar = 0x200F; // RLM
  486. return 1;
  487. }
  488. } else if (((WORD)uVirtKey == VK_PACKET) && (LOBYTE(uScanCode) == 0)) {
  489. return TranslateInjectedVKey(uScanCode, awchChars, uiTMFlags);
  490. }
  491. /*
  492. * Scan through all the shift-state tables until a matching Virtual
  493. * Key is found.
  494. */
  495. for (pVKT = pKbdTbl->pVkToWcharTable; pVKT->pVkToWchars != NULL; pVKT++) {
  496. pVK = pVKT->pVkToWchars;
  497. while (pVK->VirtualKey != 0) {
  498. if (pVK->VirtualKey == (BYTE)uVirtKey) {
  499. goto VK_Found;
  500. }
  501. pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
  502. }
  503. }
  504. /*
  505. * Not found: virtual key is not a character.
  506. */
  507. goto ReturnBadCharacter;
  508. VK_Found:
  509. /*
  510. * The virtual key has been found in table pVKT, at entry pVK
  511. */
  512. /*
  513. * If KanaLock affects this key and it is on: toggle KANA state
  514. * only if no other state is on. "KANALOK" attributes only exist
  515. * in Japanese keyboard layout, and only Japanese keyboard hardware
  516. * can be "KANA" lock on state.
  517. */
  518. if ((pVK->Attributes & KANALOK) && (ISKANALOCKON(pfvk))) {
  519. wModBits |= KBDKANA;
  520. } else {
  521. /*
  522. * If CapsLock affects this key and it is on: toggle SHIFT state
  523. * only if no other state is on.
  524. * (CapsLock doesn't affect SHIFT state if Ctrl or Alt are down).
  525. * OR
  526. * If CapsLockAltGr affects this key and it is on: toggle SHIFT
  527. * state only if both Alt & Control are down.
  528. * (CapsLockAltGr only affects SHIFT if AltGr is being used).
  529. */
  530. if ((pVK->Attributes & CAPLOK) && ((wModBits & ~KBDSHIFT) == 0) &&
  531. ISCAPSLOCKON(pfvk)) {
  532. wModBits ^= KBDSHIFT;
  533. } else if ((pVK->Attributes & CAPLOKALTGR) &&
  534. ((wModBits & (KBDALT | KBDCTRL)) == (KBDALT | KBDCTRL)) &&
  535. ISCAPSLOCKON(pfvk)) {
  536. wModBits ^= KBDSHIFT;
  537. }
  538. }
  539. /*
  540. * If SGCAPS affects this key and CapsLock is on: use the next entry
  541. * in the table, but not is Ctrl or Alt are down.
  542. * (SGCAPS is used in Swiss-German, Czech and Czech 101 layouts)
  543. */
  544. if ((pVK->Attributes & SGCAPS) && ((wModBits & ~KBDSHIFT) == 0) &&
  545. ISCAPSLOCKON(pfvk)) {
  546. pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
  547. }
  548. /*
  549. * Convert the shift-state bitmask into one of the enumerated
  550. * logical shift states.
  551. */
  552. nShift = GetModificationNumber(pKbdTbl->pCharModifiers, wModBits);
  553. if (nShift == SHFT_INVALID) {
  554. /*
  555. * An invalid combination of Shifter Keys
  556. */
  557. goto ReturnBadCharacter;
  558. } else if ((nShift < pVKT->nModifications) &&
  559. (pVK->wch[nShift] != WCH_NONE)) {
  560. /*
  561. * There is an entry in the table for this combination of
  562. * Shift State (nShift) and Virtual Key (uVirtKey).
  563. */
  564. if (pVK->wch[nShift] == WCH_DEAD) {
  565. /*
  566. * It is a dead character: the next entry contains
  567. * its value.
  568. */
  569. pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
  570. /*
  571. * If the previous char was not dead return a dead character.
  572. */
  573. if (pkl->wchDiacritic == 0) {
  574. TAGMSG2(DBGTAG_ToUnicode,
  575. "xxxInternalToUnicode: new dead char '%C'(%x), goto ReturnDeadCharacter",
  576. pVK->wch[nShift], pVK->wch[nShift]);
  577. goto ReturnDeadCharacter;
  578. }
  579. /*
  580. * Else go to ReturnGoodCharacter which will attempt to
  581. * compose this dead character with the previous dead char.
  582. */
  583. /*
  584. * N.B. NTBUG 6141
  585. * If dead key is hit twice in sequence, Win95/98 gives
  586. * two composed characters from dead chars...
  587. */
  588. TAGMSG4(DBGTAG_ToUnicode,
  589. "xxxInternalToUnicode: 2 dead chars '%C'(%x)+'%C'(%x)",
  590. pkl->wchDiacritic, pkl->wchDiacritic,
  591. pVK->wch[nShift], pVK->wch[nShift]);
  592. if (GetAppCompatFlags2(VER40) & GACF2_NOCHAR_DEADKEY) {
  593. /*
  594. * AppCompat 377217: Publisher calls TranslateMessage and ToUnicode for
  595. * the same dead key when it's not expecting real characters.
  596. * On NT4, this resulted like "pushing the dead key in the stack and
  597. * no character is compossed", but on NT5 with fix to 6141,
  598. * two dead keys compose real characters clearing the internal
  599. * dead key cache. The app shouldn't call both TranslateMessage and ToUnicode
  600. * for the same key stroke in the first place -- in a way the app was working on
  601. * NT4 by just a thin luck.
  602. * In any case, since the app has been shipped broadly and hard to fix,
  603. * let's simulate the NT4 behavior here, but with just one level cache (not the
  604. * stack).
  605. */
  606. goto ReturnDeadCharacter;
  607. }
  608. goto ReturnGoodCharacter;
  609. } else if (pVK->wch[nShift] == WCH_LGTR) {
  610. /*
  611. * It is a ligature. Look in ligature table for a match.
  612. */
  613. if ((GET_KBD_VERSION(pKbdTbl) == 0) || ((pLigature = pKbdTbl->pLigature) == NULL)) {
  614. /*
  615. * Hey, where's the table?
  616. */
  617. xxxMessageBeep(0);
  618. goto ReturnBadCharacter;
  619. }
  620. while (pLigature->VirtualKey != 0) {
  621. int iLig = 0;
  622. int cwchT = 0;
  623. if ((pLigature->VirtualKey == pVK->VirtualKey) &&
  624. (pLigature->ModificationNumber == nShift)) {
  625. /*
  626. * Found the ligature!
  627. */
  628. while ((iLig < pKbdTbl->nLgMax) && (cwchT < cChar)) {
  629. if (pLigature->wch[iLig] == WCH_NONE) {
  630. /*
  631. * End of ligature.
  632. */
  633. return cwchT;
  634. }
  635. if (pkl->wchDiacritic != 0) {
  636. int cComposed;
  637. /*
  638. * Attempt to compose the previous deadkey with current
  639. * ligature character. If this generates yet another
  640. * dead key, go round again without adding to pUniChar
  641. * or cwchT.
  642. */
  643. cComposed = ComposeDeadKeys(
  644. pkl,
  645. pKbdTbl->pDeadKey,
  646. pLigature->wch[iLig],
  647. pUniChar + cwchT,
  648. cChar - cwchT,
  649. *pdwKeyFlags & KBDBREAK
  650. );
  651. if (cComposed > 0) {
  652. cwchT += cComposed;
  653. } else {
  654. RIPMSG2(RIP_ERROR, // we really don't expect this
  655. "InternalToUnicode: dead+lig(%x)->dead(%x)",
  656. pLigature->wch[0], pkl->wchDiacritic);
  657. }
  658. } else {
  659. pUniChar[cwchT++] = pLigature->wch[iLig];
  660. }
  661. iLig++;
  662. }
  663. return cwchT;
  664. }
  665. /*
  666. * Not a match, try the next entry.
  667. */
  668. pLigature = (PLIGATURE1)((PBYTE)pLigature + pKbdTbl->cbLgEntry);
  669. }
  670. /*
  671. * No match found!
  672. */
  673. xxxMessageBeep(0);
  674. goto ReturnBadCharacter;
  675. }
  676. /*
  677. * Match found: return the unshifted character
  678. */
  679. TAGMSG2(DBGTAG_ToUnicode,
  680. "xxxInternalToUnicode: Match found '%C'(%x), goto ReturnGoodChar",
  681. pVK->wch[nShift], pVK->wch[nShift]);
  682. goto ReturnGoodCharacter;
  683. } else if ((wModBits == KBDCTRL) || (wModBits == (KBDCTRL|KBDSHIFT)) ||
  684. (wModBits == (KBDKANA|KBDCTRL)) || (wModBits == (KBDKANA|KBDCTRL|KBDSHIFT))) {
  685. /*
  686. * There was no entry for this combination of Modification (nShift)
  687. * and Virtual Key (uVirtKey). It may still be an ASCII control
  688. * character though:
  689. */
  690. if ((uVirtKey >= 'A') && (uVirtKey <= 'Z')) {
  691. /*
  692. * If the virtual key is in the range A-Z we can convert
  693. * it directly to a control character. Otherwise, we
  694. * need to search the control key conversion table for
  695. * a match to the virtual key.
  696. */
  697. *pUniChar = (WORD)(uVirtKey & 0x1f);
  698. return 1;
  699. } else if ((uVirtKey >= 0xFF61) && (uVirtKey <= 0xFF91)) {
  700. /*
  701. * If the virtual key is in range FF61-FF91 (halfwidth
  702. * katakana), we convert it to Virtual scan code with
  703. * KANA modifier.
  704. */
  705. *pUniChar = (WORD)(InternalVkKeyScanEx((WCHAR)uVirtKey,pKbdTbl) & 0x1f);
  706. return 1;
  707. }
  708. }
  709. }
  710. ReturnBadCharacter:
  711. // pkl->wchDiacritic = 0;
  712. return 0;
  713. ReturnDeadCharacter:
  714. *pUniChar = pVK->wch[nShift];
  715. /*
  716. * Save 'dead' key: overwrite an existing one.
  717. */
  718. if (!(*pdwKeyFlags & KBDBREAK)) {
  719. pkl->wchDiacritic = *pUniChar;
  720. }
  721. UserAssert(pKbdTbl->pDeadKey);
  722. /*
  723. * return negative count for dead characters
  724. */
  725. return -1;
  726. ReturnGoodCharacter:
  727. if ((pKbdTbl->pDeadKey != NULL) && (pkl->wchDiacritic != 0)) {
  728. return ComposeDeadKeys(
  729. pkl,
  730. pKbdTbl->pDeadKey,
  731. pVK->wch[nShift],
  732. pUniChar,
  733. cChar,
  734. *pdwKeyFlags & KBDBREAK
  735. );
  736. }
  737. *pUniChar = (WORD)pVK->wch[nShift];
  738. return 1;
  739. }
  740. SHORT InternalVkKeyScanEx(
  741. WCHAR wchChar,
  742. PKBDTABLES pKbdTbl)
  743. {
  744. PVK_TO_WCHARS1 pVK;
  745. PVK_TO_WCHAR_TABLE pVKT;
  746. BYTE nShift;
  747. WORD wModBits;
  748. WORD wModNumCtrl, wModNumShiftCtrl;
  749. SHORT shRetvalCtrl = 0;
  750. SHORT shRetvalShiftCtrl = 0;
  751. if (pKbdTbl == NULL) {
  752. pKbdTbl = gspklBaseLayout->spkf->pKbdTbl;
  753. }
  754. /*
  755. * Ctrl and Shift-Control combinations are less favored, so determine
  756. * the values for nShift which we prefer not to use if at all possible.
  757. * This is for compatibility with Windows 95/98, which only returns a
  758. * Ctrl or Shift+Ctrl combo as a last resort. See bugs #78891 & #229141
  759. */
  760. wModNumCtrl = GetModificationNumber(pKbdTbl->pCharModifiers, KBDCTRL);
  761. wModNumShiftCtrl = GetModificationNumber(pKbdTbl->pCharModifiers, KBDSHIFT | KBDCTRL);
  762. for (pVKT = pKbdTbl->pVkToWcharTable; pVKT->pVkToWchars != NULL; pVKT++) {
  763. for (pVK = pVKT->pVkToWchars;
  764. pVK->VirtualKey != 0;
  765. pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize)) {
  766. for (nShift = 0; nShift < pVKT->nModifications; nShift++) {
  767. if (pVK->wch[nShift] == wchChar) {
  768. /*
  769. * A matching character has been found!
  770. */
  771. if (pVK->VirtualKey == 0xff) {
  772. /*
  773. * dead char: back up to previous line to get the VK.
  774. */
  775. pVK = (PVK_TO_WCHARS1)((PBYTE)pVK - pVKT->cbSize);
  776. }
  777. /*
  778. * If this is the first Ctrl or the first Shift+Ctrl match,
  779. * remember in case we don't find any better match.
  780. * In the meantime, keep on looking.
  781. */
  782. if (nShift == wModNumCtrl) {
  783. if (shRetvalCtrl == 0) {
  784. shRetvalCtrl = (SHORT)MAKEWORD(pVK->VirtualKey, KBDCTRL);
  785. }
  786. } else if (nShift == wModNumShiftCtrl) {
  787. if (shRetvalShiftCtrl == 0) {
  788. shRetvalShiftCtrl = (SHORT)MAKEWORD(pVK->VirtualKey, KBDCTRL | KBDSHIFT);
  789. }
  790. } else {
  791. /*
  792. * this seems like a very good match!
  793. */
  794. goto GoodMatchFound;
  795. }
  796. }
  797. }
  798. }
  799. }
  800. /*
  801. * Didn't find a good match: use whatever Ctrl/Shift+Ctrl match was found
  802. */
  803. if (shRetvalCtrl) {
  804. return shRetvalCtrl;
  805. }
  806. if (shRetvalShiftCtrl) {
  807. return shRetvalShiftCtrl;
  808. }
  809. /*
  810. * May be a control character not explicitly in the layout tables
  811. */
  812. if (wchChar < 0x0020) {
  813. /*
  814. * Ctrl+char -> char - 0x40
  815. */
  816. return (SHORT)MAKEWORD((wchChar + 0x40), KBDCTRL);
  817. }
  818. return -1;
  819. GoodMatchFound:
  820. /*
  821. * Scan aModification[] to find nShift: the index will be a bitmask
  822. * representing the Shifter Keys that need to be pressed to produce
  823. * this Shift State.
  824. */
  825. for (wModBits = 0;
  826. wModBits <= pKbdTbl->pCharModifiers->wMaxModBits;
  827. wModBits++)
  828. {
  829. if (pKbdTbl->pCharModifiers->ModNumber[wModBits] == nShift) {
  830. if (pVK->VirtualKey == 0xff) {
  831. /*
  832. * The previous entry contains the actual virtual key in this case.
  833. */
  834. pVK = (PVK_TO_WCHARS1)((PBYTE)pVK - pVKT->cbSize);
  835. }
  836. return (SHORT)MAKEWORD(pVK->VirtualKey, wModBits);
  837. }
  838. }
  839. /*
  840. * huh? should never reach here! (IanJa)
  841. */
  842. UserAssertMsg1(FALSE, "InternalVkKeyScanEx error: wchChar = 0x%x", wchChar);
  843. return -1;
  844. }