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.

712 lines
21 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: xlate.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * History:
  7. * 12-07-90 GregoryW Created.
  8. \***************************************************************************/
  9. #include "precomp.h"
  10. #pragma hdrstop
  11. /*
  12. * "The great artist is the simplifier."
  13. * - Henri Frederic Amiel (1821-1881)
  14. * Ibid., November 25, 1861
  15. */
  16. /*
  17. * Determine the state of all the Modifier Keys (a Modifier Key
  18. * is any key that may modify values produced by other keys: these are
  19. * commonly SHIFT, CTRL and/or ALT)
  20. * Build a bit-mask (wModBits) to encode which modifier keys are depressed.
  21. */
  22. WORD GetModifierBits(
  23. PMODIFIERS pModifiers,
  24. LPBYTE afKeyState)
  25. {
  26. PVK_TO_BIT pVkToBit = pModifiers->pVkToBit;
  27. WORD wModBits = 0;
  28. CheckCritIn();
  29. while (pVkToBit->Vk) {
  30. if (TestKeyDownBit(afKeyState, pVkToBit->Vk)) {
  31. wModBits |= pVkToBit->ModBits;
  32. }
  33. pVkToBit++;
  34. }
  35. return wModBits;
  36. }
  37. /*
  38. * Given modifier bits, return the modification number.
  39. */
  40. WORD GetModificationNumber(
  41. PMODIFIERS pModifiers,
  42. WORD wModBits)
  43. {
  44. CheckCritInShared();
  45. if (wModBits > pModifiers->wMaxModBits) {
  46. return SHFT_INVALID;
  47. }
  48. return pModifiers->ModNumber[wModBits];
  49. }
  50. /*****************************************************************************\
  51. * VKFromVSC
  52. *
  53. * This function is called from KeyEvent() after each call to VSCFromSC. The
  54. * keyboard input data passed in is translated to a virtual key code.
  55. * This translation is dependent upon the currently depressed modifier keys.
  56. *
  57. * For instance, scan codes representing the number pad keys may be
  58. * translated into VK_NUMPAD codes or cursor movement codes depending
  59. * upon the state of NumLock and the modifier keys.
  60. *
  61. * History:
  62. *
  63. \*****************************************************************************/
  64. BYTE VKFromVSC(
  65. PKE pke,
  66. BYTE bPrefix,
  67. LPBYTE afKeyState
  68. )
  69. {
  70. USHORT usVKey;
  71. PVSC_VK pVscVk;
  72. PKBDTABLES pKbdTbl;
  73. static BOOL fVkPause;
  74. CheckCritIn();
  75. DBG_UNREFERENCED_PARAMETER(afKeyState);
  76. /*
  77. * Initialize as an unknown VK (unrecognised scancode)
  78. */
  79. pke->usFlaggedVk = usVKey = VK_UNKNOWN;
  80. /* HACK ALERT
  81. * For Korean 103 keyboard:
  82. * Check this is Korean keyboard layout or not.
  83. */
  84. if (IS_IME_ENABLED() &&
  85. KOREAN_KBD_LAYOUT(GetActiveHKL())) {
  86. if ((pke->bScanCode == 0x71) || (pke->bScanCode == 0x72)) {
  87. pke->bScanCode |= 0x80;
  88. bPrefix = 0xE0;
  89. } else {
  90. pke->bScanCode &= 0x7F;
  91. }
  92. } else {
  93. pke->bScanCode &= 0x7F;
  94. }
  95. if (gptiForeground == NULL) {
  96. RIPMSG0(RIP_VERBOSE, "VKFromVSC: NULL gptiForeground\n");
  97. pKbdTbl = gpKbdTbl;
  98. } else {
  99. if (gptiForeground->spklActive) {
  100. pKbdTbl = gptiForeground->spklActive->spkf->pKbdTbl;
  101. } else {
  102. RIPMSG0(RIP_VERBOSE, "VKFromVSC: NULL spklActive\n");
  103. pKbdTbl = gpKbdTbl;
  104. }
  105. }
  106. if (bPrefix == 0) {
  107. if (pke->bScanCode < pKbdTbl->bMaxVSCtoVK) {
  108. /*
  109. * direct index into non-prefix table
  110. */
  111. usVKey = pKbdTbl->pusVSCtoVK[pke->bScanCode];
  112. if (usVKey == 0) {
  113. return 0xFF;
  114. }
  115. } else {
  116. /*
  117. * unexpected scancode
  118. */
  119. RIPMSG2(RIP_VERBOSE, "unrecognized scancode 0x%x, prefix %x",
  120. pke->bScanCode, bPrefix);
  121. return 0xFF;
  122. }
  123. } else {
  124. /*
  125. * Scan the E0 or E1 prefix table for a match
  126. */
  127. if (bPrefix == 0xE0) {
  128. /*
  129. * Set the KBDEXT (extended key) bit in case the scancode is not
  130. * found in the table (eg: FUJITSU POS keyboard #65436)
  131. */
  132. usVKey |= KBDEXT;
  133. /*
  134. * Ignore the SHIFT keystrokes generated by the hardware
  135. */
  136. if ((pke->bScanCode == SCANCODE_LSHIFT) ||
  137. (pke->bScanCode == SCANCODE_RSHIFT)) {
  138. TAGMSG1(DBGTAG_KBD, "VKFromVSC: E0, %02x ignored", pke->bScanCode);
  139. return 0;
  140. }
  141. pVscVk = pKbdTbl->pVSCtoVK_E0;
  142. } else if (bPrefix == 0xE1) {
  143. pVscVk = pKbdTbl->pVSCtoVK_E1;
  144. } else {
  145. /*
  146. * Unrecognized prefix (from ScancodeMap?) produces an
  147. * unextended and unrecognized VK.
  148. */
  149. return 0xFF;
  150. }
  151. while (pVscVk->Vk) {
  152. if (pVscVk->Vsc == pke->bScanCode) {
  153. usVKey = pVscVk->Vk;
  154. break;
  155. }
  156. pVscVk++;
  157. }
  158. }
  159. /*
  160. * Scancode set 1 returns PAUSE button as E1 1D 45 (E1 Ctrl NumLock)
  161. * so convert E1 Ctrl to VK_PAUSE, and remember to discard the NumLock
  162. */
  163. if (fVkPause) {
  164. /*
  165. * This is the "45" part of the Pause scancode sequence.
  166. * Discard this key event: it is a false NumLock
  167. */
  168. fVkPause = FALSE;
  169. return 0;
  170. }
  171. if (usVKey == VK_PAUSE) {
  172. /*
  173. * This is the "E1 1D" part of the Pause scancode sequence.
  174. * Alter the scancode to the value Windows expects for Pause,
  175. * and remember to discard the "45" scancode that will follow
  176. */
  177. pke->bScanCode = 0x45;
  178. fVkPause = TRUE;
  179. }
  180. /*
  181. * Convert to a different VK if some modifier keys are depressed.
  182. */
  183. if (usVKey & KBDMULTIVK) {
  184. WORD nMod;
  185. PULONG pul;
  186. nMod = GetModificationNumber(
  187. gpModifiers_VK,
  188. GetModifierBits(gpModifiers_VK, gafRawKeyState));
  189. /*
  190. * Scan gapulCvt_VK[nMod] for matching VK.
  191. */
  192. if ((nMod != SHFT_INVALID) && ((pul = gapulCvt_VK[nMod]) != NULL)) {
  193. while (*pul != 0) {
  194. if (LOBYTE(*pul) == LOBYTE(usVKey)) {
  195. pke->usFlaggedVk = (USHORT)HIWORD(*pul);
  196. return (BYTE)pke->usFlaggedVk;
  197. }
  198. pul++;
  199. }
  200. }
  201. }
  202. pke->usFlaggedVk = usVKey;
  203. return (BYTE)usVKey;
  204. }
  205. /***************************************************************************\
  206. * UINT InternalMapVirtualKeyEx(UINT wCode, UINT wType, PKBDTABLES pKbdTbl);
  207. *
  208. * History:
  209. * IanJa 5/13/91 from Win3.1 \\pucus\win31ro!drivers\keyboard\getname.asm
  210. * GregoryW 2/21/95 renamed from _MapVirtualKey and added third parameter.
  211. \***************************************************************************/
  212. UINT InternalMapVirtualKeyEx(
  213. UINT wCode,
  214. UINT wType,
  215. PKBDTABLES pKbdTbl)
  216. {
  217. PVK_TO_WCHARS1 pVK;
  218. PVK_TO_WCHAR_TABLE pVKT;
  219. UINT VkRet = 0;
  220. USHORT usScanCode;
  221. PVSC_VK pVscVk;
  222. PBYTE pVkNumpad;
  223. switch (wType) {
  224. case 0:
  225. /*
  226. * Convert Virtual Key (wCode) to Scan Code
  227. */
  228. if ((wCode >= VK_SHIFT) && (wCode <= VK_MENU)) {
  229. /*
  230. * Convert ambiguous Shift/Control/Alt keys to left-hand keys
  231. */
  232. wCode = (UINT)((wCode - VK_SHIFT) * 2 + VK_LSHIFT);
  233. }
  234. /*
  235. * Scan through the table that maps Virtual Scancodes to Virtual Keys
  236. * for non-extended keys.
  237. */
  238. for (usScanCode = 0; usScanCode < pKbdTbl->bMaxVSCtoVK; usScanCode++) {
  239. if ((UINT)LOBYTE(pKbdTbl->pusVSCtoVK[usScanCode]) == wCode) {
  240. return usScanCode & 0xFF;
  241. }
  242. }
  243. /*
  244. * Scan through the table that maps Virtual Scancodes to Virtual Keys
  245. * for extended keys.
  246. */
  247. for (pVscVk = pKbdTbl->pVSCtoVK_E0; pVscVk->Vk; pVscVk++) {
  248. if ((UINT)LOBYTE(pVscVk->Vk) == wCode) {
  249. return (UINT)pVscVk->Vsc;
  250. }
  251. }
  252. /*
  253. * There was no match: maybe the Virtual Key can only be generated
  254. * with Numlock on. Scan through aVkNumpad[] to determine scancode.
  255. */
  256. for (pVkNumpad = aVkNumpad; *pVkNumpad != 0; pVkNumpad++) {
  257. if ((UINT)(*pVkNumpad) == wCode) {
  258. return (UINT)(pVkNumpad - aVkNumpad) + SCANCODE_NUMPAD_FIRST;
  259. }
  260. }
  261. return 0; // No match found!
  262. case 1:
  263. case 3:
  264. /*
  265. * Convert Scan Code (wCode) to Virtual Key, disregarding modifier keys
  266. * and NumLock key etc. Returns 0 for no corresponding Virtual Key
  267. */
  268. if (wCode < (UINT)(pKbdTbl->bMaxVSCtoVK)) {
  269. VkRet = (UINT)LOBYTE(pKbdTbl->pusVSCtoVK[wCode]);
  270. } else {
  271. /*
  272. * Scan the E0 prefix table for a match
  273. */
  274. for (pVscVk = pKbdTbl->pVSCtoVK_E0; pVscVk->Vk; pVscVk++) {
  275. if ((UINT)pVscVk->Vsc == wCode) {
  276. VkRet = (UINT)LOBYTE(pVscVk->Vk);
  277. break;
  278. }
  279. }
  280. }
  281. if ((wType == 1) && (VkRet >= VK_LSHIFT) && (VkRet <= VK_RMENU)) {
  282. /*
  283. * Convert left/right Shift/Control/Alt keys to ambiguous keys
  284. * (neither left nor right)
  285. */
  286. VkRet = (UINT)((VkRet - VK_LSHIFT) / 2 + VK_SHIFT);
  287. }
  288. if (VkRet == 0xFF) {
  289. VkRet = 0;
  290. }
  291. return VkRet;
  292. case 2:
  293. /*
  294. * Bogus Win3.1 functionality: despite SDK documenation, return uppercase for
  295. * VK_A through VK_Z
  296. */
  297. if ((wCode >= (WORD)'A') && (wCode <= (WORD)'Z')) {
  298. return wCode;
  299. }
  300. // HIWORD is no loner the wchar, due to app compat problems #287134
  301. // We should not return the wchar from pti->wchInjected that cached
  302. // at GetMessage time.
  303. // (delete this commented-out section by end of March 1999 - IanJa)
  304. //
  305. // if (LOWORD(wCode) == VK_PACKET) {
  306. // return HIWORD(wCode);
  307. // }
  308. /*
  309. * Convert Virtual Key (wCode) to ANSI.
  310. * Search each Shift-state table in turn, looking for the Virtual Key.
  311. */
  312. for (pVKT = pKbdTbl->pVkToWcharTable; pVKT->pVkToWchars != NULL; pVKT++) {
  313. pVK = pVKT->pVkToWchars;
  314. while (pVK->VirtualKey != 0) {
  315. if ((UINT)pVK->VirtualKey == wCode) {
  316. /*
  317. * Match found: return the unshifted character
  318. */
  319. if (pVK->wch[0] == WCH_DEAD) {
  320. /*
  321. * It is a dead character: the next entry contains its
  322. * value. Set the high bit to indicate dead key
  323. * (undocumented behaviour)
  324. */
  325. pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
  326. return pVK->wch[0] | (UINT)0x80000000;
  327. } else if (pVK->wch[0] == WCH_NONE) {
  328. return 0; // 9013
  329. }
  330. if (pVK->wch[0] == WCH_NONE) {
  331. return 0;
  332. }
  333. return pVK->wch[0];
  334. }
  335. pVK = (PVK_TO_WCHARS1)((PBYTE)pVK + pVKT->cbSize);
  336. }
  337. }
  338. }
  339. /*
  340. * Can't find translation, or wType was invalid
  341. */
  342. return 0;
  343. }
  344. /***************************************************************************\
  345. * _GetKeyNameText (API)
  346. *
  347. * int _GetKeyNameText(DWORD lParam, LPSTR lpStr, UINT size);
  348. *
  349. * lParam: value from WM_KEYDOWN message, etc.
  350. *
  351. * Byte 3 (bits 16..23) of lParam contains a scan code.
  352. *
  353. * Bit 20 of lParam is the Extended bit (distingushes some keys on
  354. * Enhanced keyboard).
  355. *
  356. * Bit 21 of lParam is a don't care bit (don't distingush between
  357. * left and right control, shift, Enter keys, between edit keys
  358. * in edit area and on numeric pad, etc). The app calling this
  359. * function sets this bit in lParam, if it so desires.
  360. *
  361. * lpStr: Pointer to output string.
  362. *
  363. * iSize: Maximum length of output string, not including null byte.
  364. *
  365. * History:
  366. * IanJa 4/11/91 from Win3.1 \\pucus\win31ro!drivers\keyboard\getname.asm
  367. \***************************************************************************/
  368. int APIENTRY _GetKeyNameText(
  369. LONG lParam,
  370. LPWSTR ccxlpStr,
  371. int cchSize)
  372. {
  373. BYTE Vsc = LOBYTE(HIWORD(lParam));
  374. PVSC_LPWSTR pKN;
  375. PTHREADINFO ptiT = PtiCurrentShared();
  376. PKBDTABLES pKbdTbl;
  377. UINT Vk;
  378. UINT Char;
  379. /*
  380. * NOTE -- lpStr can be a client-side address, so access through it
  381. * must be guarded with try blocks.
  382. */
  383. if (cchSize < 1)
  384. return 0;
  385. /*
  386. * If bit 25 set (don't care about left vs. right) then:
  387. * 1) convert right-Shift into left-Shift
  388. * 2) clear the extended bit for Ctrl and Alt only (effectively converting
  389. * right-Ctrl & right-Alt into left-Ctrl & right-Alt)
  390. * For Windows '95 compatibility, the DONTCARE_BIT doesn't apply to other
  391. * extended keys (eg: NumPad cursor movement keys, NumPad Enter). Some
  392. * applications (Word '95) depend on this. #37796
  393. */
  394. if (lParam & DONTCARE_BIT) {
  395. if (Vsc == SCANCODE_RSHIFT) {
  396. Vsc = SCANCODE_LSHIFT;
  397. }
  398. if (lParam & EXTENDED_BIT) {
  399. if ((Vsc == SCANCODE_CTRL) || (Vsc == SCANCODE_ALT)) {
  400. lParam &= ~EXTENDED_BIT;
  401. }
  402. }
  403. lParam &= ~DONTCARE_BIT;
  404. }
  405. if (ptiT->spklActive == (PKL)NULL) {
  406. return 0;
  407. }
  408. pKbdTbl = ptiT->spklActive->spkf->pKbdTbl;
  409. /*
  410. * Scan pKbdTbl->pKeyNames[] or pKeyNamesExt[] for matching Virtual Scan Code
  411. */
  412. if (lParam & EXTENDED_BIT) {
  413. pKN = pKbdTbl->pKeyNamesExt;
  414. } else {
  415. pKN = pKbdTbl->pKeyNames;
  416. }
  417. if (pKN) {
  418. while (pKN->vsc != 0) {
  419. if (Vsc == pKN->vsc) {
  420. try {
  421. cchSize = wcsncpycch(ccxlpStr, pKN->pwsz, cchSize);
  422. cchSize--;
  423. ccxlpStr[cchSize] = L'\0';
  424. } except(W32ExceptionHandler(TRUE, RIP_ERROR)) {
  425. return 0;
  426. }
  427. return cchSize;
  428. }
  429. pKN++;
  430. }
  431. }
  432. /*
  433. * The name of the key was not found in the table, so we
  434. * now attempt to construct the key name from the character produced by
  435. * the key. Translate Scancode -> Virtual Key -> character.
  436. */
  437. /*
  438. * Translate Scancode to Virtual Key (ignoring modifier keys etc.)
  439. */
  440. Vk = InternalMapVirtualKeyEx((UINT)Vsc, 1, pKbdTbl);
  441. if (Vk == 0) {
  442. return 0;
  443. }
  444. /*
  445. * Now translate Virtual Key to character (ignoring modifier keys etc.)
  446. */
  447. Char = InternalMapVirtualKeyEx((UINT)Vk, 2, pKbdTbl);
  448. if (Char == 0) {
  449. return 0;
  450. }
  451. if (Char & 0x80000000) {
  452. LPWSTR *ppwsz;
  453. ppwsz = pKbdTbl->pKeyNamesDead;
  454. if (ppwsz) {
  455. while (*ppwsz != NULL) {
  456. if (*ppwsz[0] == (WCHAR)Char) {
  457. try {
  458. cchSize = wcsncpycch(ccxlpStr, (*ppwsz)+1, cchSize);
  459. cchSize--;
  460. ccxlpStr[cchSize] = L'\0';
  461. } except(W32ExceptionHandler(TRUE, RIP_ERROR)) {
  462. return 0;
  463. }
  464. return cchSize;
  465. }
  466. ppwsz++;
  467. }
  468. }
  469. }
  470. /*
  471. * Construct a single character name (adding null-terminator if possible)
  472. */
  473. try {
  474. ccxlpStr[0] = (WCHAR)Char;
  475. if (cchSize >= 2) {
  476. ccxlpStr[1] = L'\0';
  477. }
  478. } except(W32ExceptionHandler(TRUE, RIP_ERROR)) {
  479. return 0;
  480. }
  481. return 1;
  482. }
  483. /***************************************************************************\
  484. * xxxAltGr() - handle special case Right-hand ALT key (Locale dependent)
  485. *
  486. * Note: gbAltGrDown reminds us to send the fake Ctrl key back up if we
  487. * switched to a non-AltGr layout while ALtGr was down: otherwise the Ctrl key
  488. * gets stuck down. (For example: using AltGr with KBDSEL to switch from
  489. * German to US layout).
  490. \***************************************************************************/
  491. BOOL gbAltGrDown = FALSE;
  492. VOID xxxAltGr(
  493. PKE pKe)
  494. {
  495. if ((pKe->usFlaggedVk & 0xFF) != VK_RMENU) {
  496. return;
  497. }
  498. if (!(pKe->usFlaggedVk & KBDBREAK)) {
  499. /*
  500. * If neither CTRL key is down, then fake one going down so that
  501. * the right hand ALT key is converted to CTRL + ALT.
  502. */
  503. if (!TestRawKeyDown(VK_CONTROL)) {
  504. gbAltGrDown = TRUE;
  505. xxxKeyEvent(VK_LCONTROL, 0x1D | SCANCODE_SIMULATED,
  506. pKe->dwTime, 0,
  507. #ifdef GENERIC_INPUT // LATER: NULL, or pKe->hDevice?
  508. NULL,
  509. NULL,
  510. #endif
  511. FALSE);
  512. }
  513. } else {
  514. /*
  515. * If the physical Left Ctrl key is not really down, fake the
  516. * Left Ctrl key coming back up (undo earlier faked Left Ctrl down)
  517. */
  518. gbAltGrDown = FALSE;
  519. if (!TestRawKeyDown(VK_LCONTROL)) {
  520. xxxKeyEvent(VK_LCONTROL | KBDBREAK, 0x1D | SCANCODE_SIMULATED,
  521. pKe->dwTime, 0,
  522. #ifdef GENERIC_INPUT
  523. NULL,
  524. NULL,
  525. #endif
  526. FALSE);
  527. }
  528. }
  529. }
  530. /*****************************************************************************\
  531. * xxxShiftLock()
  532. * handle ShiftLock feature, where only hitting Shift turns CapsLock off
  533. *
  534. \*****************************************************************************/
  535. BOOL xxxShiftLock(
  536. PKE pKe)
  537. {
  538. USHORT Vk;
  539. /*
  540. * We only mess with downstrokes: return TRUE to let it pass unmolested.
  541. */
  542. if (pKe->usFlaggedVk & KBDBREAK) {
  543. return TRUE;
  544. }
  545. Vk = pKe->usFlaggedVk & 0xFF;
  546. /*
  547. * If CapsLock is pressed when CapsLock is already on, lose the keystroke.
  548. */
  549. if ((Vk == VK_CAPITAL) && TestAsyncKeyStateToggle(VK_CAPITAL)) {
  550. return FALSE;
  551. }
  552. /*
  553. * If a Shift key goes down when CapsLock is on, turn CapsLock off
  554. * by simulating a click on the CapsLock key.
  555. * Let the Shift down through FIRST, since it might be part of an
  556. * input lang toggle (tough luck that this toggle turns off CapsLock!)
  557. */
  558. if (((Vk == VK_LSHIFT) || (Vk == VK_RSHIFT) || (Vk == VK_SHIFT)) &&
  559. TestAsyncKeyStateToggle(VK_CAPITAL)) {
  560. xxxKeyEvent(pKe->usFlaggedVk, pKe->bScanCode,
  561. pKe->dwTime, 0,
  562. #ifdef GENERIC_INPUT
  563. NULL,
  564. NULL,
  565. #endif
  566. FALSE);
  567. xxxKeyEvent(VK_CAPITAL, 0x3A | SCANCODE_SIMULATED,
  568. pKe->dwTime, 0,
  569. #ifdef GENERIC_INPUT
  570. NULL,
  571. NULL,
  572. #endif
  573. FALSE);
  574. xxxKeyEvent(VK_CAPITAL | KBDBREAK, 0x3A | SCANCODE_SIMULATED,
  575. pKe->dwTime, 0,
  576. #ifdef GENERIC_INPUT
  577. NULL,
  578. NULL,
  579. #endif
  580. FALSE);
  581. return FALSE;
  582. }
  583. return TRUE;
  584. }
  585. /*
  586. * Returning FALSE means the Key Event has been deleted by a special-case
  587. * KeyEvent processor.
  588. * Returning TRUE means the Key Event should be passed on (although it may
  589. * have been altered.
  590. */
  591. BOOL KEOEMProcs(PKE pKe)
  592. {
  593. int i;
  594. CheckCritIn();
  595. for (i = 0; aKEProcOEM[i] != NULL; i++) {
  596. if (!aKEProcOEM[i](pKe)) {
  597. /*
  598. * Eat the key event
  599. */
  600. return FALSE;
  601. }
  602. }
  603. /*
  604. * Pass the (possibly altered) key event on.
  605. */
  606. return TRUE;
  607. }
  608. /*
  609. * Returning FALSE means the Key Event has been deleted by a special-case
  610. * KeyEvent processor.
  611. * Returning TRUE means the Key Event should be passed on (although it may
  612. * have been altered.
  613. */
  614. BOOL xxxKELocaleProcs(PKE pKe)
  615. {
  616. CheckCritIn();
  617. /*
  618. * AltGr is a layout-specific behavior
  619. * Modifier keys are sent up as necessary in xxxInternalActivateKeyboardLayout
  620. * (#139178), so left-Ctrl won't be left stuck down if we switch from an
  621. * AltGr keyboard to a non-AltGr keybord while the AltGr is held down.
  622. */
  623. if ((gpKbdTbl->fLocaleFlags & KLLF_ALTGR) || gbAltGrDown) {
  624. xxxAltGr(pKe);
  625. }
  626. /*
  627. * ShiftLock/CapsLock is per-user (global) behavior as well as (for
  628. * backward compatibility) per-layout behavior.
  629. */
  630. if ((gdwKeyboardAttributes & KLLF_SHIFTLOCK) ||
  631. (gpKbdTbl->fLocaleFlags & KLLF_SHIFTLOCK)) {
  632. if (!xxxShiftLock(pKe)) {
  633. return FALSE;
  634. }
  635. }
  636. /*
  637. * Other special Key Event processors
  638. */
  639. return TRUE;
  640. }