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.

2188 lines
68 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: kbdlyout.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Keyboard Layout API
  7. *
  8. * History:
  9. * 04-14-92 IanJa Created
  10. \***************************************************************************/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. /*
  14. * Workers (forward declarations)
  15. */
  16. BOOL xxxInternalUnloadKeyboardLayout(PWINDOWSTATION, PKL, UINT);
  17. VOID ReorderKeyboardLayouts(PWINDOWSTATION, PKL);
  18. /****************************************************************************\
  19. * HKLtoPKL
  20. *
  21. * pti - thread to look in
  22. * hkl - HKL_NEXT or HKL_PREV
  23. * Finds the the next/prev LOADED layout, NULL if none.
  24. * (Starts from the pti's active layout, may return pklActive itself)
  25. * - a real HKL (Keyboard Layout handle):
  26. * Finds the kbd layout struct (loaded or not), NULL if no match found.
  27. *
  28. * History:
  29. * 1997-02-05 IanJa added pti parameter
  30. \****************************************************************************/
  31. PKL HKLtoPKL(
  32. PTHREADINFO pti,
  33. HKL hkl)
  34. {
  35. PKL pklActive;
  36. PKL pkl;
  37. UserAssert(pti != NULL);
  38. if ((pklActive = pti->spklActive) == NULL) {
  39. return NULL;
  40. }
  41. pkl = pklActive;
  42. if (hkl == (HKL)HKL_PREV) {
  43. do {
  44. pkl = pkl->pklPrev;
  45. if (!(pkl->dwKL_Flags & KL_UNLOADED)) {
  46. return pkl;
  47. }
  48. } while (pkl != pklActive);
  49. return NULL;
  50. } else if (hkl == (HKL)HKL_NEXT) {
  51. do {
  52. pkl = pkl->pklNext;
  53. if (!(pkl->dwKL_Flags & KL_UNLOADED)) {
  54. return pkl;
  55. }
  56. } while (pkl != pklActive);
  57. return NULL;
  58. }
  59. /*
  60. * Find the pkl for this hkl.
  61. * If the kbd layout isn't specified (in the HIWORD), ignore it and look
  62. * for a Locale match only. (Mohamed Hamid's fix for Word bug)
  63. */
  64. if (HandleToUlong(hkl) & 0xffff0000) {
  65. do {
  66. if (pkl->hkl == hkl) {
  67. return pkl;
  68. }
  69. pkl = pkl->pklNext;
  70. } while (pkl != pklActive);
  71. } else {
  72. do {
  73. if (LOWORD(HandleToUlong(pkl->hkl)) == LOWORD(HandleToUlong(hkl))) {
  74. return pkl;
  75. }
  76. pkl = pkl->pklNext;
  77. } while (pkl != pklActive);
  78. }
  79. return NULL;
  80. }
  81. /***************************************************************************\
  82. * ReadLayoutFile
  83. *
  84. * Maps layout file into memory and initializes layout table.
  85. *
  86. * History:
  87. * 01-10-95 JimA Created.
  88. \***************************************************************************/
  89. #define GET_HEADER_FIELD(x) \
  90. ((fWin64Header) ? (NtHeader64->x) : (NtHeader32->x))
  91. /*
  92. * Note that this only works for sections < 64K
  93. * Implicitly assumes pBaseVirt, pBaseDst and dwDataSize.
  94. */
  95. #if DBG
  96. BOOL gfEnableChecking = TRUE;
  97. #else
  98. BOOL gfEnableChecking = FALSE;
  99. #endif
  100. #define EXIT_READ(p) \
  101. RIPMSGF1(RIP_WARNING, #p " is @ invalid address %p", p); \
  102. if (gfEnableChecking) { \
  103. goto exitread; \
  104. }
  105. #define VALIDATE_PTR(p) \
  106. if ((PBYTE)(p) < (PBYTE)pBaseDst || (PBYTE)(p) + sizeof *(p) > (PBYTE)pBaseDst + dwDataSize) { \
  107. EXIT_READ(p); \
  108. }
  109. #define FIXUP_PTR(p) \
  110. if (p) { \
  111. p = (PVOID)((PBYTE)pBaseVirt + (WORD)(ULONG_PTR)(p)); \
  112. VALIDATE_PTR(p); \
  113. } \
  114. TAGMSGF1(DBGTAG_KBD, #p " validation finished %p", p);
  115. PKBDTABLES ReadLayoutFile(
  116. PKBDFILE pkf,
  117. HANDLE hFile,
  118. UINT offTable,
  119. UINT offNlsTable
  120. )
  121. {
  122. HANDLE hmap = NULL;
  123. OBJECT_ATTRIBUTES ObjectAttributes;
  124. SIZE_T ulViewSize = 0;
  125. NTSTATUS Status;
  126. PIMAGE_DOS_HEADER DosHdr = NULL;
  127. BOOLEAN fWin64Header;
  128. PIMAGE_NT_HEADERS32 NtHeader32;
  129. PIMAGE_NT_HEADERS64 NtHeader64;
  130. PIMAGE_SECTION_HEADER SectionTableEntry;
  131. ULONG NumberOfSubsections;
  132. ULONG OffsetToSectionTable;
  133. PBYTE pBaseDst = NULL, pBaseVirt = NULL;
  134. PKBDTABLES pktNew = NULL;
  135. DWORD dwDataSize;
  136. PKBDNLSTABLES pknlstNew = NULL;
  137. BOOL fSucceeded = FALSE;
  138. TAGMSGF1(DBGTAG_KBD, "entering for '%ls'", pkf->awchDllName);
  139. /*
  140. * Mask off hiword.
  141. */
  142. UserAssert((offTable & ~0xffff) == 0);
  143. UserAssert((offNlsTable & ~0xffff) == 0);
  144. /*
  145. * Initialize KbdNlsTables with NULL.
  146. */
  147. pkf->pKbdNlsTbl = NULL;
  148. InitializeObjectAttributes(&ObjectAttributes,
  149. NULL,
  150. OBJ_KERNEL_HANDLE,
  151. NULL,
  152. NULL);
  153. /*
  154. * Map the layout file into memory
  155. */
  156. Status = ZwCreateSection(&hmap, SECTION_MAP_READ, &ObjectAttributes,
  157. NULL, PAGE_READONLY, SEC_COMMIT, hFile);
  158. if (!NT_SUCCESS(Status)) {
  159. RIPMSGF3(RIP_WARNING, "failed to create a section for %ls, hFile=%p, stat=%08x", pkf->awchDllName, hFile, Status);
  160. goto exitread;
  161. }
  162. Status = ZwMapViewOfSection(hmap, NtCurrentProcess(), &DosHdr, 0, 0, NULL,
  163. &ulViewSize, ViewUnmap, 0, PAGE_READONLY);
  164. if (!NT_SUCCESS(Status)) {
  165. RIPMSGF1(RIP_WARNING, "failed to map the view for %ls", pkf->awchDllName);
  166. goto exitread;
  167. }
  168. if (ulViewSize < sizeof *DosHdr || ulViewSize > (128 * 1024)) {
  169. RIPMSGF1(RIP_WARNING, "ViewSize is too small or large %08x", ulViewSize);
  170. goto exitread;
  171. }
  172. /*
  173. * We find the .data section in the file header and by
  174. * subtracting the virtual address from offTable find
  175. * the offset in the section of the layout table.
  176. */
  177. UserAssert(sizeof *NtHeader64 >= sizeof *NtHeader32);
  178. try {
  179. NtHeader64 = (PIMAGE_NT_HEADERS64)((PBYTE)DosHdr + (ULONG)DosHdr->e_lfanew);
  180. NtHeader32 = (PIMAGE_NT_HEADERS32)NtHeader64;
  181. #if defined(_WIN64)
  182. if ((PBYTE)NtHeader64 < (PBYTE)DosHdr || // signed Overflow
  183. (PBYTE)NtHeader64 + sizeof *NtHeader64 >= (PBYTE)DosHdr + ulViewSize) {
  184. RIPMSGF1(RIP_WARNING, "Header is out of Range %p", NtHeader64);
  185. goto exitread;
  186. }
  187. fWin64Header = (NtHeader64->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64) ||
  188. (NtHeader64->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64);
  189. #else
  190. if ((PBYTE)NtHeader32 < (PBYTE)DosHdr || // signed Overflow
  191. (PBYTE)NtHeader32 + sizeof *NtHeader32 >= (PBYTE)DosHdr + ulViewSize) {
  192. RIPMSGF1(RIP_WARNING, "Header is out of Range %p", NtHeader32);
  193. goto exitread;
  194. }
  195. fWin64Header = FALSE;
  196. #endif
  197. TAGMSGF2(DBGTAG_KBD, "DLL='%ls', Is64=%d", pkf->awchDllName, fWin64Header);
  198. /*
  199. * At this point the object table is read in (if it was not
  200. * already read in) and may displace the image header.
  201. */
  202. NumberOfSubsections = GET_HEADER_FIELD(FileHeader.NumberOfSections);
  203. OffsetToSectionTable = sizeof(ULONG) + // Signature
  204. sizeof(IMAGE_FILE_HEADER) + // FileHeader
  205. GET_HEADER_FIELD(FileHeader.SizeOfOptionalHeader);
  206. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PBYTE)NtHeader32 +
  207. OffsetToSectionTable);
  208. while (NumberOfSubsections > 0) {
  209. /*
  210. * Validate the SectionTableEntry.
  211. */
  212. if ((PBYTE)SectionTableEntry < (PBYTE)DosHdr ||
  213. (PBYTE)SectionTableEntry + sizeof *SectionTableEntry >= (PBYTE)DosHdr + ulViewSize) {
  214. RIPMSGF1(RIP_WARNING, "SectionTableEntry @ %p is not within the view section.",
  215. SectionTableEntry);
  216. goto exitread;
  217. }
  218. /*
  219. * Is this the .data section that we are looking for?
  220. */
  221. if (strcmp(SectionTableEntry->Name, ".data") == 0) {
  222. break;
  223. }
  224. SectionTableEntry++;
  225. NumberOfSubsections--;
  226. }
  227. if (NumberOfSubsections == 0) {
  228. RIPMSGF1(RIP_WARNING, "number of sections is 0 for %ls.", pkf->awchDllName);
  229. goto exitread;
  230. }
  231. /*
  232. * We found the section, now compute starting offset and the table size.
  233. */
  234. offTable -= SectionTableEntry->VirtualAddress;
  235. dwDataSize = SectionTableEntry->Misc.VirtualSize;
  236. /*
  237. * Validate the offTable to see if it fits in the section.
  238. */
  239. if (offTable >= dwDataSize) {
  240. RIPMSGF3(RIP_WARNING, "illegal offTable=0x%x or offNlsTable=0x%x, dwDataSize=0x%x",
  241. offTable, offNlsTable, dwDataSize);
  242. goto exitread;
  243. }
  244. /*
  245. * Validate the .data size not exceeding our assumption (<64KB).
  246. */
  247. if (dwDataSize >= 0xffff) {
  248. RIPMSGF1(RIP_WARNING, "unexpected size in .data: 0x%x", dwDataSize);
  249. goto exitread;
  250. }
  251. /*
  252. * Validate we are not over-shooting our view
  253. */
  254. if ((PBYTE)DosHdr + SectionTableEntry->PointerToRawData + dwDataSize >=
  255. (PBYTE)DosHdr + ulViewSize) {
  256. RIPMSGF1(RIP_WARNING, "Layout Table @ %p is not within the view section.",
  257. (PBYTE)DosHdr + SectionTableEntry->PointerToRawData);
  258. goto exitread;
  259. }
  260. /*
  261. * Allocate layout table and copy from file.
  262. */
  263. TAGMSGF2(DBGTAG_KBD, "data size for '%S' = %x", pkf->awchDllName, dwDataSize);
  264. pBaseDst = UserAllocPool(dwDataSize, TAG_KBDTABLE);
  265. #if DBG
  266. if (pBaseDst == NULL) {
  267. RIPMSGF2(RIP_WARNING, "failed to allocate 0x%x bytes of memory for %ls", dwDataSize, pkf->awchDllName);
  268. }
  269. #endif
  270. if (pBaseDst != NULL) {
  271. VK_TO_WCHAR_TABLE *pVkToWcharTable;
  272. VSC_LPWSTR *pKeyName;
  273. pkf->hBase = (HANDLE)pBaseDst;
  274. RtlMoveMemory(pBaseDst,
  275. (PBYTE)DosHdr + SectionTableEntry->PointerToRawData,
  276. dwDataSize);
  277. if (ISTS()) {
  278. pkf->Size = dwDataSize; // For shadow hotkey processing
  279. }
  280. /*
  281. * Compute table address and fixup pointers in table.
  282. */
  283. pktNew = (PKBDTABLES)(pBaseDst + offTable);
  284. /*
  285. * The address in the data section has the virtual address
  286. * added in, so we need to adjust the fixup pointer to
  287. * compensate.
  288. */
  289. pBaseVirt = pBaseDst - SectionTableEntry->VirtualAddress;
  290. FIXUP_PTR(pktNew->pCharModifiers);
  291. FIXUP_PTR(pktNew->pCharModifiers->pVkToBit);
  292. /*
  293. * Validate pVkToBit table.
  294. */
  295. {
  296. PVK_TO_BIT pVkToBit;
  297. for (pVkToBit = pktNew->pCharModifiers->pVkToBit; ; pVkToBit++) {
  298. VALIDATE_PTR(pVkToBit);
  299. if (pVkToBit->Vk == 0) {
  300. break;
  301. }
  302. }
  303. }
  304. FIXUP_PTR(pktNew->pVkToWcharTable);
  305. #if DBG
  306. if (pktNew->pVkToWcharTable == NULL) {
  307. RIPMSGF1(RIP_WARNING, "KL %ls does not have pVkToWcharTable???", pkf->awchDllName);
  308. }
  309. #endif
  310. if (pktNew->pVkToWcharTable) {
  311. /*
  312. * Fix up and validate VkToWchar table.
  313. */
  314. for (pVkToWcharTable = pktNew->pVkToWcharTable; ; pVkToWcharTable++) {
  315. VALIDATE_PTR(pVkToWcharTable);
  316. if (pVkToWcharTable->pVkToWchars == NULL) {
  317. break;
  318. }
  319. FIXUP_PTR(pVkToWcharTable->pVkToWchars);
  320. }
  321. }
  322. FIXUP_PTR(pktNew->pDeadKey);
  323. /*
  324. * Validate pDeadKey array.
  325. */
  326. {
  327. PDEADKEY pDeadKey = pktNew->pDeadKey;
  328. while (pDeadKey) {
  329. VALIDATE_PTR(pDeadKey);
  330. if (pDeadKey->dwBoth == 0) {
  331. break;
  332. }
  333. pDeadKey++;
  334. }
  335. }
  336. /*
  337. * Version 1 layouts support ligatures.
  338. */
  339. if (GET_KBD_VERSION(pktNew)) {
  340. FIXUP_PTR(pktNew->pLigature);
  341. }
  342. FIXUP_PTR(pktNew->pKeyNames);
  343. if (pktNew->pKeyNames == NULL) {
  344. RIPMSGF1(RIP_WARNING, "KL %ls does not have pKeyNames???", pkf->awchDllName);
  345. }
  346. if (pktNew->pKeyNames) {
  347. for (pKeyName = pktNew->pKeyNames; ; pKeyName++) {
  348. VALIDATE_PTR(pKeyName);
  349. if (pKeyName->vsc == 0) {
  350. break;
  351. }
  352. FIXUP_PTR(pKeyName->pwsz);
  353. }
  354. }
  355. FIXUP_PTR(pktNew->pKeyNamesExt);
  356. if (pktNew->pKeyNamesExt) {
  357. for (pKeyName = pktNew->pKeyNamesExt; ; pKeyName++) {
  358. VALIDATE_PTR(pKeyName);
  359. if (pKeyName->vsc == 0) {
  360. break;
  361. }
  362. FIXUP_PTR(pKeyName->pwsz);
  363. }
  364. }
  365. FIXUP_PTR(pktNew->pKeyNamesDead);
  366. if (pktNew->pKeyNamesDead) {
  367. LPWSTR *lpDeadKey;
  368. for (lpDeadKey = pktNew->pKeyNamesDead; ; lpDeadKey++) {
  369. LPCWSTR lpwstr;
  370. VALIDATE_PTR(lpDeadKey);
  371. if (*lpDeadKey == NULL) {
  372. break;
  373. }
  374. FIXUP_PTR(*lpDeadKey);
  375. UserAssert(*lpDeadKey);
  376. for (lpwstr = *lpDeadKey; ; lpwstr++) {
  377. VALIDATE_PTR(lpwstr);
  378. if (*lpwstr == L'\0') {
  379. break;
  380. }
  381. }
  382. };
  383. }
  384. /*
  385. * Fix up and validate Virtual Scan Code to VK table.
  386. */
  387. if (pktNew->pusVSCtoVK == NULL) {
  388. RIPMSGF1(RIP_WARNING, "KL %ls does not have the basic VSC to VK table", pkf->awchDllName);
  389. goto exitread;
  390. }
  391. FIXUP_PTR(pktNew->pusVSCtoVK);
  392. VALIDATE_PTR(pktNew->pusVSCtoVK + pktNew->bMaxVSCtoVK);
  393. FIXUP_PTR(pktNew->pVSCtoVK_E0);
  394. if (pktNew->pVSCtoVK_E0) {
  395. PVSC_VK pVscVk;
  396. for (pVscVk = pktNew->pVSCtoVK_E0; pVscVk->Vk; pVscVk++) {
  397. VALIDATE_PTR(pVscVk);
  398. }
  399. }
  400. FIXUP_PTR(pktNew->pVSCtoVK_E1);
  401. if (pktNew->pVSCtoVK_E1) {
  402. PVSC_VK pVscVk;
  403. for (pVscVk = pktNew->pVSCtoVK_E1; ; pVscVk++) {
  404. VALIDATE_PTR(pVscVk);
  405. if (pVscVk->Vk == 0) {
  406. break;
  407. }
  408. }
  409. }
  410. if (offNlsTable) {
  411. /*
  412. * Compute table address and fixup pointers in table.
  413. */
  414. offNlsTable -= SectionTableEntry->VirtualAddress;
  415. pknlstNew = (PKBDNLSTABLES)(pBaseDst + offNlsTable);
  416. VALIDATE_PTR(pknlstNew);
  417. /*
  418. * Fixup and validate the address.
  419. */
  420. FIXUP_PTR(pknlstNew->pVkToF);
  421. if (pknlstNew->pVkToF) {
  422. VALIDATE_PTR(&pknlstNew->pVkToF[pknlstNew->NumOfVkToF - 1]);
  423. }
  424. FIXUP_PTR(pknlstNew->pusMouseVKey);
  425. if (pknlstNew->pusMouseVKey) {
  426. VALIDATE_PTR(&pknlstNew->pusMouseVKey[pknlstNew->NumOfMouseVKey - 1]);
  427. }
  428. /*
  429. * Save the pointer.
  430. */
  431. pkf->pKbdNlsTbl = pknlstNew;
  432. #if DBG_FE
  433. {
  434. UINT NumOfVkToF = pknlstNew->NumOfVkToF;
  435. DbgPrint("NumOfVkToF - %d\n",NumOfVkToF);
  436. while(NumOfVkToF) {
  437. DbgPrint("VK = %x\n",pknlstNew->pVkToF[NumOfVkToF-1].Vk);
  438. NumOfVkToF--;
  439. }
  440. }
  441. #endif // DBG_FE
  442. }
  443. }
  444. } except(W32ExceptionHandler(FALSE, RIP_WARNING)) {
  445. RIPMSGF1(RIP_WARNING, "took exception reading from %ls", pkf->awchDllName);
  446. goto exitread;
  447. }
  448. fSucceeded = TRUE;
  449. exitread:
  450. if (!fSucceeded && pBaseDst) {
  451. UserFreePool(pBaseDst);
  452. }
  453. /*
  454. * Unmap and release the mapped section.
  455. */
  456. if (DosHdr) {
  457. ZwUnmapViewOfSection(NtCurrentProcess(), DosHdr);
  458. }
  459. if (hmap != NULL) {
  460. ZwClose(hmap);
  461. }
  462. TAGMSGF1(DBGTAG_KBD, "returning pkl = %p", pktNew);
  463. if (!fSucceeded) {
  464. return NULL;
  465. }
  466. return pktNew;
  467. }
  468. PKBDTABLES PrepareFallbackKeyboardFile(PKBDFILE pkf)
  469. {
  470. PBYTE pBaseDst;
  471. pBaseDst = UserAllocPool(sizeof(KBDTABLES), TAG_KBDTABLE);
  472. if (pBaseDst != NULL) {
  473. RtlCopyMemory(pBaseDst, &KbdTablesFallback, sizeof KbdTablesFallback);
  474. // Note: Unlike ReadLayoutFile(),
  475. // we don't need to fix up pointers in struct KBDFILE.
  476. }
  477. pkf->hBase = (HANDLE)pBaseDst;
  478. pkf->pKbdNlsTbl = NULL;
  479. return (PKBDTABLES)pBaseDst;
  480. }
  481. /***************************************************************************\
  482. * LoadKeyboardLayoutFile
  483. *
  484. * History:
  485. * 10-29-95 GregoryW Created.
  486. \***************************************************************************/
  487. PKBDFILE LoadKeyboardLayoutFile(
  488. HANDLE hFile,
  489. UINT offTable,
  490. UINT offNlsTable,
  491. LPCWSTR pwszKLID,
  492. LPWSTR pwszDllName,
  493. DWORD dwType,
  494. DWORD dwSubType)
  495. {
  496. PKBDFILE pkf = gpkfList;
  497. TAGMSG4(DBGTAG_KBD | RIP_THERESMORE, "LoadKeyboardLayoutFile: new KL=%S, dllName='%S', %d:%d",
  498. pwszKLID, pwszDllName ? pwszDllName : L"",
  499. dwType, dwSubType);
  500. UNREFERENCED_PARAMETER(pwszKLID);
  501. /*
  502. * Search for the existing layout file.
  503. */
  504. if (pkf) {
  505. do {
  506. TAGMSG3(DBGTAG_KBD | RIP_THERESMORE, "LoadKeyboardLayoutFile: looking at dll=%S, %d:%d",
  507. pkf->awchDllName,
  508. pkf->pKbdTbl->dwType, pkf->pKbdTbl->dwSubType);
  509. if (pwszDllName && _wcsicmp(pkf->awchDllName, pwszDllName) == 0) {
  510. /*
  511. * The layout is already loaded.
  512. */
  513. TAGMSG1(DBGTAG_KBD, "LoadKeyboardLayoutFile: duplicated KBDFILE found(#1). pwszDllName='%ls'\n", pwszDllName);
  514. return pkf;
  515. }
  516. pkf = pkf->pkfNext;
  517. } while (pkf);
  518. }
  519. TAGMSG1(DBGTAG_KBD, "LoadKeyboardLayoutFile: layout %S is not yet loaded.", pwszDllName);
  520. /*
  521. * Allocate a new Keyboard File structure.
  522. */
  523. pkf = (PKBDFILE)HMAllocObject(NULL, NULL, TYPE_KBDFILE, sizeof(KBDFILE));
  524. if (!pkf) {
  525. RIPMSG0(RIP_WARNING, "Keyboard Layout File: out of memory");
  526. return (PKBDFILE)NULL;
  527. }
  528. /*
  529. * Load layout table.
  530. */
  531. if (hFile != NULL) {
  532. /*
  533. * Load NLS layout table also...
  534. */
  535. wcsncpycch(pkf->awchDllName, pwszDllName, ARRAY_SIZE(pkf->awchDllName));
  536. pkf->awchDllName[ARRAY_SIZE(pkf->awchDllName) - 1] = 0;
  537. pkf->pKbdTbl = ReadLayoutFile(pkf, hFile, offTable, offNlsTable);
  538. if (dwType || dwSubType) {
  539. pkf->pKbdTbl->dwType = dwType;
  540. pkf->pKbdTbl->dwSubType = dwSubType;
  541. }
  542. } else {
  543. /*
  544. * We failed to open the keyboard layout file in client side
  545. * because the dll was missing.
  546. * If this ever happens, we used to fail creating
  547. * a window station, but we should allow a user
  548. * at least to boot the system.
  549. */
  550. TAGMSG1(DBGTAG_KBD, "LoadKeyboardLayoutFile: hFile is NULL for %ls, preparing the fallback.", pwszDllName);
  551. pkf->pKbdTbl = PrepareFallbackKeyboardFile(pkf);
  552. // Note: pkf->pKbdNlsTbl has been NULL'ed in PrepareFallbackKeyboardFile()
  553. }
  554. if (pkf->pKbdTbl == NULL) {
  555. RIPMSG0(RIP_WARNING, "LoadKeyboardLayoutFile: pkf->pKbdTbl is NULL.");
  556. HMFreeObject(pkf);
  557. return (PKBDFILE)NULL;
  558. }
  559. /*
  560. * Put keyboard layout file at front of list.
  561. */
  562. pkf->pkfNext = gpkfList;
  563. gpkfList = pkf;
  564. return pkf;
  565. }
  566. /***************************************************************************\
  567. * RemoveKeyboardLayoutFile
  568. *
  569. * History:
  570. * 10-29-95 GregoryW Created.
  571. \***************************************************************************/
  572. VOID RemoveKeyboardLayoutFile(
  573. PKBDFILE pkf)
  574. {
  575. PKBDFILE pkfPrev, pkfCur;
  576. // FE: NT4 SP4 #107809
  577. if (gpKbdTbl == pkf->pKbdTbl) {
  578. gpKbdTbl = &KbdTablesFallback;
  579. }
  580. if (gpKbdNlsTbl == pkf->pKbdNlsTbl) {
  581. gpKbdNlsTbl = NULL;
  582. }
  583. /*
  584. * Good old linked list management 101
  585. */
  586. if (pkf == gpkfList) {
  587. /*
  588. * Head of the list.
  589. */
  590. gpkfList = pkf->pkfNext;
  591. return;
  592. }
  593. pkfPrev = gpkfList;
  594. pkfCur = gpkfList->pkfNext;
  595. while (pkf != pkfCur) {
  596. pkfPrev = pkfCur;
  597. pkfCur = pkfCur->pkfNext;
  598. }
  599. /*
  600. * Found it!
  601. */
  602. pkfPrev->pkfNext = pkfCur->pkfNext;
  603. }
  604. /***************************************************************************\
  605. * DestroyKF
  606. *
  607. * Called when a keyboard layout file is destroyed due to an unlock.
  608. *
  609. * History:
  610. * 24-Feb-1997 adams Created.
  611. \***************************************************************************/
  612. void
  613. DestroyKF(PKBDFILE pkf)
  614. {
  615. if (!HMMarkObjectDestroy(pkf))
  616. return;
  617. RemoveKeyboardLayoutFile(pkf);
  618. UserFreePool(pkf->hBase);
  619. HMFreeObject(pkf);
  620. }
  621. INT GetThreadsWithPKL(
  622. PTHREADINFO **ppptiList,
  623. PKL pkl)
  624. {
  625. PTHREADINFO ptiT, *pptiT, *pptiListAllocated;
  626. INT cThreads, cThreadsAllocated;
  627. PWINDOWSTATION pwinsta;
  628. PDESKTOP pdesk;
  629. PLIST_ENTRY pHead, pEntry;
  630. if (ppptiList != NULL)
  631. *ppptiList = NULL;
  632. cThreads = 0;
  633. /*
  634. * allocate a first list for 128 entries
  635. */
  636. cThreadsAllocated = 128;
  637. pptiListAllocated = UserAllocPool(cThreadsAllocated * sizeof(PTHREADINFO),
  638. TAG_SYSTEM);
  639. if (pptiListAllocated == NULL) {
  640. RIPMSG0(RIP_WARNING, "GetPKLinThreads: out of memory");
  641. return 0;
  642. }
  643. // for all the winstations
  644. for (pwinsta = grpWinStaList; pwinsta != NULL ; pwinsta = pwinsta->rpwinstaNext) {
  645. // for all the desktops in that winstation
  646. for (pdesk = pwinsta->rpdeskList; pdesk != NULL ; pdesk = pdesk->rpdeskNext) {
  647. pHead = &pdesk->PtiList;
  648. // for all the threads in that desktop
  649. for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
  650. ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
  651. if (ptiT == NULL) {
  652. continue;
  653. }
  654. if (pkl && (pkl != ptiT->spklActive)) { // #99321 cmp pkls, not hkls?
  655. continue;
  656. }
  657. /*
  658. * WindowsBug 349045
  659. * Unload IMEs only for the normal apps.... leave them as is if they are
  660. * loaded for services.
  661. * Note, this is not really a clean fix, but some customers demand it.
  662. */
  663. UserAssert(PsGetCurrentProcessId() == gpidLogon);
  664. if (ptiT->ppi->Process != gpepCSRSS && ptiT->ppi->Process != PsGetCurrentProcess()) {
  665. /*
  666. * By the time this routine is called (solely by WinLogon), all the other
  667. * applications should be gone or terminated. So skipping like above
  668. * leaves IMEs loaded in the services.
  669. */
  670. continue;
  671. }
  672. if (cThreads == cThreadsAllocated) {
  673. cThreadsAllocated += 128;
  674. pptiT = UserReAllocPool(pptiListAllocated,
  675. cThreads * sizeof(PTHREADINFO),
  676. cThreadsAllocated * sizeof(PTHREADINFO),
  677. TAG_SYSTEM);
  678. if (pptiT == NULL) {
  679. RIPMSG0(RIP_ERROR, "GetPKLinThreads: Out of memory");
  680. UserFreePool(pptiListAllocated);
  681. return 0;
  682. }
  683. pptiListAllocated = pptiT;
  684. }
  685. pptiListAllocated[cThreads++] = ptiT;
  686. }
  687. }
  688. }
  689. /*
  690. * add CSRSS threads
  691. */
  692. for (ptiT = PpiFromProcess(gpepCSRSS)->ptiList; ptiT != NULL; ptiT = ptiT->ptiSibling) {
  693. if (pkl && (pkl != ptiT->spklActive)) { // #99321 cmp pkls, not hkls?
  694. continue;
  695. }
  696. if (cThreads == cThreadsAllocated) {
  697. cThreadsAllocated += 128;
  698. pptiT = UserReAllocPool(pptiListAllocated,
  699. cThreads * sizeof(PTHREADINFO),
  700. cThreadsAllocated * sizeof(PTHREADINFO),
  701. TAG_SYSTEM);
  702. if (pptiT == NULL) {
  703. RIPMSG0(RIP_ERROR, "GetPKLinThreads: Out of memory");
  704. UserFreePool(pptiListAllocated);
  705. return 0;
  706. }
  707. pptiListAllocated = pptiT;
  708. }
  709. pptiListAllocated[cThreads++] = ptiT;
  710. }
  711. if (cThreads == 0) {
  712. UserFreePool(pptiListAllocated);
  713. } else if (ppptiList != NULL) {
  714. *ppptiList = pptiListAllocated;
  715. } else {
  716. UserFreePool(pptiListAllocated);
  717. }
  718. return cThreads;
  719. }
  720. VOID xxxSetPKLinThreads(
  721. PKL pklNew,
  722. PKL pklToBeReplaced)
  723. {
  724. PTHREADINFO *pptiList;
  725. INT cThreads, i;
  726. UserAssert(pklNew != pklToBeReplaced);
  727. CheckLock(pklNew);
  728. CheckLock(pklToBeReplaced);
  729. cThreads = GetThreadsWithPKL(&pptiList, pklToBeReplaced);
  730. /*
  731. * Will the foreground thread's keyboard layout change?
  732. */
  733. if (pklNew && gptiForeground && gptiForeground->spklActive == pklToBeReplaced) {
  734. ChangeForegroundKeyboardTable(pklToBeReplaced, pklNew);
  735. }
  736. if (pptiList != NULL) {
  737. if (pklToBeReplaced == NULL) {
  738. for (i = 0; i < cThreads; i++) {
  739. Lock(&pptiList[i]->spklActive, pklNew);
  740. }
  741. } else {
  742. /*
  743. * This is a replace. First, deactivate the *replaced* IME by
  744. * activating the pklNew. Second, unload the *replaced* IME.
  745. */
  746. xxxImmActivateAndUnloadThreadsLayout(pptiList, cThreads, NULL,
  747. pklNew, HandleToUlong(pklToBeReplaced->hkl));
  748. }
  749. UserFreePool(pptiList);
  750. }
  751. /*
  752. * If this is a replace, link the new layout immediately after the
  753. * layout being replaced. This maintains ordering of layouts when
  754. * the *replaced* layout is unloaded. The input locale panel in the
  755. * regional settings applet depends on this.
  756. */
  757. if (pklToBeReplaced) {
  758. if (pklToBeReplaced->pklNext == pklNew) {
  759. /*
  760. * Ordering already correct. Nothing to do.
  761. */
  762. return;
  763. }
  764. /*
  765. * Move new layout immediately after layout being replaced.
  766. * 1. Remove new layout from current position.
  767. * 2. Update links in new layout.
  768. * 3. Link new layout into desired position.
  769. */
  770. pklNew->pklPrev->pklNext = pklNew->pklNext;
  771. pklNew->pklNext->pklPrev = pklNew->pklPrev;
  772. pklNew->pklNext = pklToBeReplaced->pklNext;
  773. pklNew->pklPrev = pklToBeReplaced;
  774. pklToBeReplaced->pklNext->pklPrev = pklNew;
  775. pklToBeReplaced->pklNext = pklNew;
  776. }
  777. }
  778. VOID xxxFreeImeKeyboardLayouts(
  779. PWINDOWSTATION pwinsta)
  780. {
  781. PTHREADINFO *pptiList;
  782. INT cThreads;
  783. if (pwinsta->dwWSF_Flags & WSF_NOIO)
  784. return;
  785. /*
  786. * should make GetThreadsWithPKL aware of pwinsta?
  787. */
  788. cThreads = GetThreadsWithPKL(&pptiList, NULL);
  789. if (pptiList != NULL) {
  790. xxxImmUnloadThreadsLayout(pptiList, cThreads, NULL, IFL_UNLOADIME);
  791. UserFreePool(pptiList);
  792. }
  793. return;
  794. }
  795. /***************************************************************************\
  796. * xxxLoadKeyboardLayoutEx
  797. *
  798. * History:
  799. \***************************************************************************/
  800. HKL xxxLoadKeyboardLayoutEx(
  801. PWINDOWSTATION pwinsta,
  802. HANDLE hFile,
  803. HKL hklToBeReplaced,
  804. UINT offTable,
  805. PKBDTABLE_MULTI_INTERNAL pKbdTableMulti,
  806. LPCWSTR pwszKLID,
  807. UINT KbdInputLocale,
  808. UINT Flags)
  809. {
  810. PKL pkl, pklFirst, pklToBeReplaced;
  811. PKBDFILE pkf;
  812. CHARSETINFO cs;
  813. TL tlpkl;
  814. PTHREADINFO ptiCurrent;
  815. UNICODE_STRING strLcidKF;
  816. UNICODE_STRING strKLID;
  817. LCID lcidKF;
  818. BOOL bCharSet;
  819. PIMEINFOEX piiex;
  820. TAGMSG1(DBGTAG_KBD, "xxxLoadKeyboardLayoutEx: new KL: pwszKLID=\"%ls\"", pwszKLID);
  821. /*
  822. * If the windowstation does not do I/O, don't load the
  823. * layout. Also check KdbInputLocale for #307132
  824. */
  825. if ((KbdInputLocale == 0) || (pwinsta->dwWSF_Flags & WSF_NOIO)) {
  826. return NULL;
  827. }
  828. /*
  829. * If hklToBeReplaced is non-NULL make sure it's valid.
  830. * NOTE: may want to verify they're not passing HKL_NEXT or HKL_PREV.
  831. */
  832. ptiCurrent = PtiCurrent();
  833. if (hklToBeReplaced && !(pklToBeReplaced = HKLtoPKL(ptiCurrent, hklToBeReplaced))) {
  834. return NULL;
  835. }
  836. if (KbdInputLocale == HandleToUlong(hklToBeReplaced)) {
  837. /*
  838. * Replacing a layout/lang pair with itself. Nothing to do.
  839. */
  840. return pklToBeReplaced->hkl;
  841. }
  842. if (Flags & KLF_RESET) {
  843. /*
  844. * Only WinLogon can use this flag
  845. */
  846. if (PsGetThreadProcessId(ptiCurrent->pEThread) != gpidLogon) {
  847. RIPERR0(ERROR_INVALID_FLAGS, RIP_WARNING,
  848. "Invalid flag passed to LoadKeyboardLayout" );
  849. return NULL;
  850. }
  851. xxxFreeImeKeyboardLayouts(pwinsta);
  852. /*
  853. * Make sure we don't lose track of the left-over layouts
  854. * They have been unloaded, but are still in use by some threads).
  855. * The FALSE will prevent xxxFreeKeyboardLayouts from unlocking the
  856. * unloaded layouts.
  857. */
  858. xxxFreeKeyboardLayouts(pwinsta, FALSE);
  859. }
  860. /*
  861. * Does this hkl already exist?
  862. */
  863. pkl = pklFirst = pwinsta->spklList;
  864. if (pkl) {
  865. do {
  866. if (pkl->hkl == (HKL)IntToPtr( KbdInputLocale )) {
  867. /*
  868. * The hkl already exists.
  869. */
  870. /*
  871. * If it is unloaded (but not yet destroyed because it is
  872. * still is use), recover it.
  873. */
  874. if (pkl->dwKL_Flags & KL_UNLOADED) {
  875. // stop it from being destroyed if not is use.
  876. PHE phe = HMPheFromObject(pkl);
  877. // An unloaded layout must be marked for destroy.
  878. UserAssert(phe->bFlags & HANDLEF_DESTROY);
  879. phe->bFlags &= ~HANDLEF_DESTROY;
  880. #if DBG
  881. phe->bFlags &= ~HANDLEF_MARKED_OK;
  882. #endif
  883. pkl->dwKL_Flags &= ~KL_UNLOADED;
  884. } else if (!(Flags & KLF_RESET)) {
  885. /*
  886. * If it was already loaded and we didn't change all layouts
  887. * with KLF_RESET, there is nothing to tell the shell about
  888. */
  889. Flags &= ~KLF_NOTELLSHELL;
  890. }
  891. goto AllPresentAndCorrectSir;
  892. }
  893. pkl = pkl->pklNext;
  894. } while (pkl != pklFirst);
  895. }
  896. if (IS_IME_KBDLAYOUT((HKL)IntToPtr( KbdInputLocale ))
  897. #ifdef CUAS_ENABLE
  898. ||
  899. IS_CICERO_ENABLED_AND_NOT16BIT()
  900. #endif // CUAS_ENABLE
  901. ) {
  902. /*
  903. * This is an IME keyboard layout, do a callback
  904. * to read the extended IME information structure.
  905. * Note: We can't fail the call so easily if
  906. * KLF_RESET is specified.
  907. */
  908. piiex = xxxImmLoadLayout((HKL)IntToPtr( KbdInputLocale ));
  909. if (piiex == NULL && (Flags & (KLF_RESET | KLF_INITTIME)) == 0) {
  910. /*
  911. * Not Resetting, not creating a window station
  912. */
  913. RIPMSG1(RIP_WARNING,
  914. "Keyboard Layout: xxxImmLoadLayout(%lx) failed", KbdInputLocale);
  915. return NULL;
  916. }
  917. } else {
  918. piiex = NULL;
  919. }
  920. /*
  921. * Get the system font's font signature. These are 64-bit FS_xxx values,
  922. * but we are only asking for an ANSI ones, so gSystemFS is just a DWORD.
  923. * gSystemFS is consulted when posting WM_INPUTLANGCHANGEREQUEST (input.c)
  924. */
  925. if (gSystemFS == 0) {
  926. LCID lcid;
  927. ZwQueryDefaultLocale(FALSE, &lcid);
  928. if (xxxClientGetCharsetInfo(lcid, &cs)) {
  929. gSystemFS = cs.fs.fsCsb[0];
  930. gSystemCPCharSet = (BYTE)cs.ciCharset;
  931. } else {
  932. gSystemFS = 0xFFFF;
  933. gSystemCPCharSet = ANSI_CHARSET;
  934. }
  935. }
  936. /*
  937. * Use the Keyboard Layout's LCID to calculate the charset, codepage etc,
  938. * so that characters from that layout don't just becomes ?s if the input
  939. * locale doesn't match. This allows "dumb" applications to display the
  940. * text if the user chooses the right font.
  941. * We can't just use the HIWORD of KbdInputLocale because if a variant
  942. * keyboard layout was chosen, this will be something like F008 - have to
  943. * look inside the KF to get the real LCID of the kbdfile: this will be
  944. * something like L"00010419", and we want the last 4 digits.
  945. */
  946. RtlInitUnicodeString(&strLcidKF, pwszKLID + 4);
  947. RtlUnicodeStringToInteger(&strLcidKF, 16, (PULONG)&lcidKF);
  948. bCharSet = xxxClientGetCharsetInfo(lcidKF, &cs);
  949. /*
  950. * Keyboard Layout Handle object does not exist. Load keyboard layout file,
  951. * if not already loaded.
  952. */
  953. if ((pkf = LoadKeyboardLayoutFile(hFile, LOWORD(offTable), HIWORD(offTable), pwszKLID, pKbdTableMulti->wszDllName, 0, 0)) == NULL) {
  954. goto freePiiex;
  955. }
  956. /*
  957. * Allocate a new Keyboard Layout structure (hkl)
  958. */
  959. pkl = (PKL)HMAllocObject(NULL, NULL, TYPE_KBDLAYOUT, sizeof(KL));
  960. if (!pkl) {
  961. RIPMSG0(RIP_WARNING, "Keyboard Layout: out of memory");
  962. UserFreePool(pkf->hBase);
  963. HMMarkObjectDestroy(pkf);
  964. HMUnlockObject(pkf);
  965. freePiiex:
  966. if (piiex) {
  967. UserFreePool(piiex);
  968. }
  969. return NULL;
  970. }
  971. Lock(&pkl->spkfPrimary, pkf);
  972. /*
  973. * Load extra keyboard layouts.
  974. */
  975. UserAssert(pKbdTableMulti);
  976. if (pKbdTableMulti->multi.nTables) {
  977. RIPMSG0(RIP_WARNING, "xxxLoadKeyboardLayoutEx: going to read multiple tables.");
  978. /*
  979. * Allocate the array for extra keyboard layouts.
  980. */
  981. UserAssert(pKbdTableMulti->multi.nTables < KBDTABLE_MULTI_MAX); // check exists in the stub
  982. pkl->pspkfExtra = UserAllocPoolZInit(pKbdTableMulti->multi.nTables * sizeof(PKBDFILE), TAG_KBDTABLE);
  983. if (pkl->pspkfExtra) {
  984. UINT i;
  985. UINT n;
  986. /*
  987. * Load the extra keyboard layouts and lock them.
  988. */
  989. for (i = 0, n = 0; i < pKbdTableMulti->multi.nTables; ++i) {
  990. UserAssert(i < KBDTABLE_MULTI_MAX);
  991. if (pKbdTableMulti->files[i].hFile) {
  992. // make sure dll name is null terminated.
  993. pKbdTableMulti->multi.aKbdTables[i].wszDllName[ARRAY_SIZE(pKbdTableMulti->multi.aKbdTables[i].wszDllName) - 1] = 0;
  994. // load it.
  995. pkf = LoadKeyboardLayoutFile(pKbdTableMulti->files[i].hFile,
  996. pKbdTableMulti->files[i].wTable,
  997. pKbdTableMulti->files[i].wNls,
  998. pwszKLID,
  999. pKbdTableMulti->multi.aKbdTables[i].wszDllName,
  1000. pKbdTableMulti->multi.aKbdTables[i].dwType,
  1001. pKbdTableMulti->multi.aKbdTables[i].dwSubType);
  1002. if (pkf == NULL) {
  1003. // If allocation fails, simply exit this loop and continue KL creation.
  1004. RIPMSG0(RIP_WARNING, "xxxLoadKeyboardLayoutEx: failed to load the extra keyboard layout file(s).");
  1005. break;
  1006. }
  1007. Lock(&pkl->pspkfExtra[n], pkf);
  1008. ++n;
  1009. } else {
  1010. RIPMSG2(RIP_WARNING, "xxxLoadKeyboardLayoutEx: pKbdTableMulti(%#p)->files[%x].hFile is NULL",
  1011. pKbdTableMulti, i);
  1012. }
  1013. }
  1014. pkl->uNumTbl = n;
  1015. }
  1016. }
  1017. /*
  1018. * Link to itself in case we have to DestroyKL
  1019. */
  1020. pkl->pklNext = pkl;
  1021. pkl->pklPrev = pkl;
  1022. /*
  1023. * Init KL
  1024. */
  1025. pkl->dwKL_Flags = 0;
  1026. pkl->wchDiacritic = 0;
  1027. pkl->hkl = (HKL)IntToPtr( KbdInputLocale );
  1028. RtlInitUnicodeString(&strKLID, pwszKLID);
  1029. RtlUnicodeStringToInteger(&strKLID, 16, &pkl->dwKLID);
  1030. TAGMSG2(DBGTAG_KBD, "xxxLoadKeyboardLayoutEx: hkl %08p KLID:%08x", pkl->hkl, pkl->dwKLID);
  1031. Lock(&pkl->spkf, pkl->spkfPrimary);
  1032. pkl->dwLastKbdType = pkl->spkf->pKbdTbl->dwType;
  1033. pkl->dwLastKbdSubType = pkl->spkf->pKbdTbl->dwSubType;
  1034. pkl->spkf->pKbdTbl->fLocaleFlags |= KLL_LAYOUT_ATTR_FROM_KLF(Flags);
  1035. pkl->piiex = piiex;
  1036. if (bCharSet) {
  1037. pkl->CodePage = (WORD)cs.ciACP;
  1038. pkl->dwFontSigs = cs.fs.fsCsb[1]; // font signature mask (FS_xxx values)
  1039. pkl->iBaseCharset = cs.ciCharset; // charset value
  1040. } else {
  1041. pkl->CodePage = CP_ACP;
  1042. pkl->dwFontSigs = FS_LATIN1;
  1043. pkl->iBaseCharset = ANSI_CHARSET;
  1044. }
  1045. /*
  1046. * Insert KL in the double-linked circular list, at the end.
  1047. */
  1048. pklFirst = pwinsta->spklList;
  1049. if (pklFirst == NULL) {
  1050. Lock(&pwinsta->spklList, pkl);
  1051. } else {
  1052. pkl->pklNext = pklFirst;
  1053. pkl->pklPrev = pklFirst->pklPrev;
  1054. pklFirst->pklPrev->pklNext = pkl;
  1055. pklFirst->pklPrev = pkl;
  1056. }
  1057. AllPresentAndCorrectSir:
  1058. // FE_IME
  1059. ThreadLockAlwaysWithPti(ptiCurrent, pkl, &tlpkl);
  1060. if (hklToBeReplaced) {
  1061. TL tlPKLToBeReplaced;
  1062. ThreadLockAlwaysWithPti(ptiCurrent, pklToBeReplaced, &tlPKLToBeReplaced);
  1063. xxxSetPKLinThreads(pkl, pklToBeReplaced);
  1064. xxxInternalUnloadKeyboardLayout(pwinsta, pklToBeReplaced, KLF_INITTIME);
  1065. ThreadUnlock(&tlPKLToBeReplaced);
  1066. }
  1067. if (Flags & KLF_REORDER) {
  1068. ReorderKeyboardLayouts(pwinsta, pkl);
  1069. }
  1070. if (!(Flags & KLF_NOTELLSHELL) && IsHooked(PtiCurrent(), WHF_SHELL)) {
  1071. xxxCallHook(HSHELL_LANGUAGE, (WPARAM)NULL, (LPARAM)0, WH_SHELL);
  1072. gLCIDSentToShell = 0;
  1073. }
  1074. if (Flags & KLF_ACTIVATE) {
  1075. TL tlPKL;
  1076. ThreadLockAlwaysWithPti(ptiCurrent, pkl, &tlPKL);
  1077. xxxInternalActivateKeyboardLayout(pkl, Flags, NULL);
  1078. ThreadUnlock(&tlPKL);
  1079. }
  1080. if (Flags & KLF_RESET) {
  1081. RIPMSG2(RIP_VERBOSE, "Flag & KLF_RESET, locking gspklBaseLayout(%08x) with new kl(%08x)",
  1082. gspklBaseLayout ? gspklBaseLayout->hkl : 0,
  1083. pkl->hkl);
  1084. Lock(&gspklBaseLayout, pkl);
  1085. xxxSetPKLinThreads(pkl, NULL);
  1086. }
  1087. /*
  1088. * Use the hkl as the layout handle
  1089. * If the KL is freed somehow, return NULL for safety. -- ianja --
  1090. */
  1091. pkl = ThreadUnlock(&tlpkl);
  1092. if (pkl == NULL) {
  1093. return NULL;
  1094. }
  1095. return pkl->hkl;
  1096. }
  1097. HKL xxxActivateKeyboardLayout(
  1098. PWINDOWSTATION pwinsta,
  1099. HKL hkl,
  1100. UINT Flags,
  1101. PWND pwnd)
  1102. {
  1103. PKL pkl;
  1104. TL tlPKL;
  1105. HKL hklRet;
  1106. PTHREADINFO ptiCurrent = PtiCurrent();
  1107. CheckLock(pwnd);
  1108. pkl = HKLtoPKL(ptiCurrent, hkl);
  1109. if (pkl == NULL) {
  1110. return 0;
  1111. }
  1112. if (Flags & KLF_REORDER) {
  1113. ReorderKeyboardLayouts(pwinsta, pkl);
  1114. }
  1115. ThreadLockAlwaysWithPti(ptiCurrent, pkl, &tlPKL);
  1116. hklRet = xxxInternalActivateKeyboardLayout(pkl, Flags, pwnd);
  1117. ThreadUnlock(&tlPKL);
  1118. return hklRet;
  1119. }
  1120. VOID ReorderKeyboardLayouts(
  1121. PWINDOWSTATION pwinsta,
  1122. PKL pkl)
  1123. {
  1124. PKL pklFirst = pwinsta->spklList;
  1125. if (pwinsta->dwWSF_Flags & WSF_NOIO) {
  1126. RIPMSG1(RIP_WARNING, "ReorderKeyboardLayouts called for non-interactive windowstation %#p",
  1127. pwinsta);
  1128. return;
  1129. }
  1130. UserAssert(pklFirst != NULL);
  1131. /*
  1132. * If the layout is already at the front of the list there's nothing to do.
  1133. */
  1134. if (pkl == pklFirst) {
  1135. return;
  1136. }
  1137. /*
  1138. * Cut pkl from circular list:
  1139. */
  1140. pkl->pklPrev->pklNext = pkl->pklNext;
  1141. pkl->pklNext->pklPrev = pkl->pklPrev;
  1142. /*
  1143. * Insert pkl at front of list
  1144. */
  1145. pkl->pklNext = pklFirst;
  1146. pkl->pklPrev = pklFirst->pklPrev;
  1147. pklFirst->pklPrev->pklNext = pkl;
  1148. pklFirst->pklPrev = pkl;
  1149. Lock(&pwinsta->spklList, pkl);
  1150. }
  1151. extern VOID AdjustPushStateForKL(PTHREADINFO ptiCurrent, PBYTE pbDone, PKL pklTarget, PKL pklPrev, PKL pklNew);
  1152. extern void ResetPushState(PTHREADINFO pti, UINT uVk);
  1153. VOID ManageKeyboardModifiers(PKL pklPrev, PKL pkl)
  1154. {
  1155. PTHREADINFO ptiCurrent = PtiCurrent();
  1156. if (ptiCurrent->pq) {
  1157. if (pklPrev) {
  1158. BYTE baDone[256 / 8];
  1159. RtlZeroMemory(baDone, sizeof baDone);
  1160. /*
  1161. * Clear the toggle state if needed. First check the modifier keys
  1162. * of pklPrev. Next check the modifier keys of pklNew.
  1163. */
  1164. TAGMSG2(DBGTAG_IMM, "Changing KL from %08lx to %08lx", pklPrev->hkl, pkl->hkl);
  1165. AdjustPushStateForKL(ptiCurrent, baDone, pklPrev, pklPrev, pkl);
  1166. AdjustPushStateForKL(ptiCurrent, baDone, pkl, pklPrev, pkl);
  1167. if (pklPrev->spkf && (pklPrev->spkf->pKbdTbl->fLocaleFlags & KLLF_ALTGR)) {
  1168. if (!TestRawKeyDown(VK_CONTROL)) {
  1169. /*
  1170. * If the previous keyboard has AltGr, and if the Ctrl key is not
  1171. * physically down, clear the left control.
  1172. * See xxxAltGr().
  1173. */
  1174. TAGMSG0(DBGTAG_KBD, "Clearing VK_LCONTROL for AltGr\n");
  1175. xxxKeyEvent(VK_LCONTROL | KBDBREAK, 0x1D | SCANCODE_SIMULATED, 0, 0,
  1176. #ifdef GENERIC_INPUT
  1177. NULL,
  1178. NULL,
  1179. #endif
  1180. FALSE);
  1181. }
  1182. }
  1183. }
  1184. else {
  1185. /*
  1186. * If the current keyboard is unknown, clear all the push state.
  1187. */
  1188. int i;
  1189. for (i = 0; i < CBKEYSTATE; i++) {
  1190. ptiCurrent->pq->afKeyState[i] &= KEYSTATE_TOGGLE_BYTEMASK;
  1191. gafAsyncKeyState[i] &= KEYSTATE_TOGGLE_BYTEMASK;
  1192. gafRawKeyState[i] &= KEYSTATE_TOGGLE_BYTEMASK;
  1193. }
  1194. }
  1195. }
  1196. }
  1197. void SetGlobalKeyboardTableInfo(PKL pklNew)
  1198. {
  1199. CheckCritIn();
  1200. UserAssert(pklNew);
  1201. /*
  1202. * Set gpKbdTbl so foreground thread processes AltGr appropriately
  1203. */
  1204. gpKbdTbl = pklNew->spkf->pKbdTbl;
  1205. if (gpKL != pklNew) {
  1206. gpKL = pklNew;
  1207. }
  1208. if (ISTS()) {
  1209. ghKbdTblBase = pklNew->spkf->hBase;
  1210. guKbdTblSize = pklNew->spkf->Size;
  1211. }
  1212. TAGMSG1(DBGTAG_KBD, "SetGlobalKeyboardTableInfo:Changing KL NLS Table: new HKL=%#p\n", pklNew->hkl);
  1213. TAGMSG1(DBGTAG_KBD, "SetGlobalKeyboardTableInfo: new gpKbdNlsTbl=%#p\n", pklNew->spkf->pKbdNlsTbl);
  1214. gpKbdNlsTbl = pklNew->spkf->pKbdNlsTbl;
  1215. }
  1216. VOID ChangeForegroundKeyboardTable(PKL pklOld, PKL pklNew)
  1217. {
  1218. CheckCritIn();
  1219. UserAssert(pklNew != NULL);
  1220. if ((pklOld == pklNew || (pklOld != NULL && pklOld->spkf == pklNew->spkf)) && gpKL) {
  1221. return;
  1222. }
  1223. /*
  1224. * Some keys (pressed to switch layout) may still be down. When these come
  1225. * back up, they may have different VK values due to the new layout, so the
  1226. * original key will be left stuck down. (eg: an ISV layout from Attachmate
  1227. * and the CAN/CSA layout, both of which redefine the right-hand Ctrl key's
  1228. * VK so switching to that layout with right Ctrl+Shift will leave the Ctrl
  1229. * stuck down).
  1230. * The solution is to clear all the keydown bits whenever we switch layouts
  1231. * (leaving the toggle bits alone to preserve CapsLock, NumLock etc.). This
  1232. * also solves the AltGr problem, where the simulated Ctrl key doesn't come
  1233. * back up if we switch to a non-AltGr layout before releasing AltGr - IanJa
  1234. *
  1235. * Clear down bits only if necessary --- i.e. if the VK value differs between
  1236. * old and new keyboard layout. We have to take complex path for some of the
  1237. * keys, like Ctrl or Alt, may have left and right equivalents. - HiroYama
  1238. */
  1239. ManageKeyboardModifiers(pklOld, pklNew);
  1240. // Manage the VK_KANA toggle key for Japanese KL.
  1241. // Since VK_HANGUL and VK_KANA share the same VK value and
  1242. // VK_KANA is a toggle key, when keyboard layouts are switched,
  1243. // VK_KANA toggle status should be restored.
  1244. //
  1245. // If:
  1246. // 1) Old and New keyboard layouts are both Japanese, do nothing.
  1247. // 2) Old and New keyboard layouts are not Japanese, do nothing.
  1248. // 3) Old keyboard is Japanese and new one is not, clear the KANA toggle.
  1249. // 4) New keyboard is Japanese and old one is not, restore the KANA toggle.
  1250. //
  1251. {
  1252. enum { KANA_NOOP, KANA_SET, KANA_CLEAR } opKanaToggle = KANA_NOOP;
  1253. if (JAPANESE_KBD_LAYOUT(pklNew->hkl)) {
  1254. if (pklOld == NULL) {
  1255. /*
  1256. * Let's honor the current async toggle state
  1257. * if the old KL is not specified.
  1258. */
  1259. TAGMSG0(DBGTAG_KBD, "VK_KANA: previous KL is NULL, honoring the async toggle state.");
  1260. gfKanaToggle = (TestAsyncKeyStateToggle(VK_KANA) != 0);
  1261. opKanaToggle = gfKanaToggle ? KANA_SET : KANA_CLEAR;
  1262. } else if (!JAPANESE_KBD_LAYOUT(pklOld->hkl)) {
  1263. /*
  1264. * We're switching from non JPN KL to JPN.
  1265. * Need to restore the KANA toggle state.
  1266. */
  1267. opKanaToggle = gfKanaToggle ? KANA_SET : KANA_CLEAR;
  1268. }
  1269. } else if (pklOld && JAPANESE_KBD_LAYOUT(pklOld->hkl)) {
  1270. /*
  1271. * Previous KL was Japanese, and we're switching to the other language.
  1272. * Let's clear the KANA toggle status and preserve it for the future
  1273. * switch back to the Japanese KL.
  1274. */
  1275. gfKanaToggle = (TestAsyncKeyStateToggle(VK_KANA) != 0);
  1276. opKanaToggle = KANA_CLEAR;
  1277. }
  1278. if (opKanaToggle == KANA_SET) {
  1279. TAGMSG0(DBGTAG_KBD, "VK_KANA is being set.\n");
  1280. SetAsyncKeyStateToggle(VK_KANA);
  1281. SetRawKeyToggle(VK_KANA);
  1282. if (gptiForeground && gptiForeground->pq) {
  1283. SetKeyStateToggle(gptiForeground->pq, VK_KANA);
  1284. }
  1285. } else if (opKanaToggle == KANA_CLEAR) {
  1286. TAGMSG0(DBGTAG_KBD, "VK_KANA is beging cleared.\n");
  1287. ClearAsyncKeyStateToggle(VK_KANA);
  1288. ClearRawKeyToggle(VK_KANA);
  1289. if (gptiForeground && gptiForeground->pq) {
  1290. ClearKeyStateToggle(gptiForeground->pq, VK_KANA);
  1291. }
  1292. }
  1293. if (opKanaToggle != KANA_NOOP) {
  1294. UpdateKeyLights(TRUE);
  1295. }
  1296. }
  1297. UserAssert(pklNew);
  1298. SetGlobalKeyboardTableInfo(pklNew);
  1299. }
  1300. //
  1301. // Toggle and push state adjusters:
  1302. //
  1303. // ResetPushState, AdjustPushState, AdjustPushStateForKL
  1304. //
  1305. void ResetPushState(PTHREADINFO pti, UINT uVk)
  1306. {
  1307. TAGMSG1(DBGTAG_IMM, "ResetPushState: has to reset the push state of vk=%x\n", uVk);
  1308. if (uVk != 0) {
  1309. ClearAsyncKeyStateDown(uVk);
  1310. ClearAsyncKeyStateDown(uVk);
  1311. ClearRawKeyDown(uVk);
  1312. ClearRawKeyToggle(uVk);
  1313. ClearKeyStateDown(pti->pq, uVk);
  1314. ClearKeyStateToggle(pti->pq, uVk);
  1315. }
  1316. }
  1317. void AdjustPushState(PTHREADINFO ptiCurrent, BYTE bBaseVk, BYTE bVkL, BYTE bVkR, PKL pklPrev, PKL pklNew)
  1318. {
  1319. BOOLEAN fDownL = FALSE, fDownR = FALSE;
  1320. BOOLEAN fVanishL = FALSE, fVanishR = FALSE;
  1321. UINT uScanCode1, uScanCode2;
  1322. if (bVkL) {
  1323. fDownL = TestRawKeyDown(bVkL) || TestAsyncKeyStateDown(bVkL) || TestKeyStateDown(ptiCurrent->pq, bVkL);
  1324. if (fDownL) {
  1325. uScanCode1 = InternalMapVirtualKeyEx(bVkL, 0, pklPrev->spkf->pKbdTbl);
  1326. uScanCode2 = InternalMapVirtualKeyEx(bVkL, 0, pklNew->spkf->pKbdTbl);
  1327. fVanishL = (uScanCode1 && uScanCode2 == 0);
  1328. if (fVanishL) {
  1329. TAGMSG2(DBGTAG_KBD, "AdjustPushState: clearing %02x (%02x)", bVkL, uScanCode1);
  1330. xxxKeyEvent((WORD)(bVkL | KBDBREAK), (WORD)(uScanCode1 | SCANCODE_SIMULATED), 0, 0,
  1331. #ifdef GENERIC_INPUT
  1332. NULL,
  1333. NULL,
  1334. #endif
  1335. FALSE);
  1336. }
  1337. }
  1338. }
  1339. if (bVkR) {
  1340. fDownR = TestRawKeyDown(bVkR) || TestAsyncKeyStateDown(bVkR) || TestKeyStateDown(ptiCurrent->pq, bVkR);
  1341. if (fDownR) {
  1342. uScanCode1 = InternalMapVirtualKeyEx(bVkR, 0, pklPrev->spkf->pKbdTbl);
  1343. uScanCode2 = InternalMapVirtualKeyEx(bVkR, 0, pklNew->spkf->pKbdTbl);
  1344. fVanishR = (uScanCode1 && uScanCode2 == 0);
  1345. if (fVanishR) {
  1346. TAGMSG2(DBGTAG_KBD, "AdjustPushState: clearing %02x (%02x)", bVkR, uScanCode1);
  1347. xxxKeyEvent((WORD)(bVkR | KBDBREAK), (WORD)(uScanCode1 | SCANCODE_SIMULATED), 0, 0,
  1348. #ifdef GENERIC_INPUT
  1349. NULL,
  1350. NULL,
  1351. #endif
  1352. FALSE);
  1353. }
  1354. }
  1355. }
  1356. UNREFERENCED_PARAMETER(bBaseVk);
  1357. }
  1358. VOID AdjustPushStateForKL(PTHREADINFO ptiCurrent, PBYTE pbDone, PKL pklTarget, PKL pklPrev, PKL pklNew)
  1359. {
  1360. CONST VK_TO_BIT* pVkToBits;
  1361. UserAssert(pklPrev);
  1362. UserAssert(pklNew);
  1363. if (pklTarget->spkf == NULL || pklPrev->spkf == NULL) {
  1364. return;
  1365. }
  1366. pVkToBits = pklTarget->spkf->pKbdTbl->pCharModifiers->pVkToBit;
  1367. for (; pVkToBits->Vk; ++pVkToBits) {
  1368. BYTE bVkVar1 = 0, bVkVar2 = 0;
  1369. //
  1370. // Is it already processed ?
  1371. //
  1372. UserAssert(pVkToBits->Vk < 0x100);
  1373. if (pbDone[pVkToBits->Vk >> 3] & (1 << (pVkToBits->Vk & 7))) {
  1374. continue;
  1375. }
  1376. switch (pVkToBits->Vk) {
  1377. case VK_SHIFT:
  1378. bVkVar1 = VK_LSHIFT;
  1379. bVkVar2 = VK_RSHIFT;
  1380. break;
  1381. case VK_CONTROL:
  1382. bVkVar1 = VK_LCONTROL;
  1383. bVkVar2 = VK_RCONTROL;
  1384. break;
  1385. case VK_MENU:
  1386. bVkVar1 = VK_LMENU;
  1387. bVkVar2 = VK_RMENU;
  1388. break;
  1389. }
  1390. TAGMSG3(DBGTAG_IMM, "Adjusting VK=%x var1=%x var2=%x\n", pVkToBits->Vk, bVkVar1, bVkVar2);
  1391. AdjustPushState(ptiCurrent, pVkToBits->Vk, bVkVar1, bVkVar2, pklPrev, pklNew);
  1392. pbDone[pVkToBits->Vk >> 3] |= (1 << (pVkToBits->Vk & 7));
  1393. }
  1394. }
  1395. __inline BOOL IsWinSrvInputThread(
  1396. PTHREADINFO pti)
  1397. {
  1398. UserAssert(pti);
  1399. UserAssert(pti->TIF_flags & TIF_CSRSSTHREAD);
  1400. if (gptiForeground && gptiForeground->rpdesk &&
  1401. gptiForeground->rpdesk->dwConsoleThreadId == TIDq(pti)) {
  1402. return TRUE;
  1403. }
  1404. return FALSE;
  1405. }
  1406. /*****************************************************************************\
  1407. * xxxInternalActivateKeyboardLayout
  1408. *
  1409. * pkl - pointer to keyboard layout to switch the current thread to
  1410. * Flags - KLF_RESET
  1411. * KLF_SETFORPROCESS
  1412. * KLLF_SHIFTLOCK (any of KLLF_GLOBAL_ATTRS)
  1413. * others are ignored
  1414. * pwnd - If the current thread has no focus or active window, send the
  1415. * WM_INPUTLANGCHANGE message to this window (unless it is NULL too)
  1416. *
  1417. * History:
  1418. * 1998-10-14 IanJa Added pwnd parameter
  1419. \*****************************************************************************/
  1420. HKL xxxInternalActivateKeyboardLayout(
  1421. PKL pkl,
  1422. UINT Flags,
  1423. PWND pwnd)
  1424. {
  1425. HKL hklPrev;
  1426. PKL pklPrev;
  1427. TL tlpklPrev;
  1428. PTHREADINFO ptiCurrent = PtiCurrent();
  1429. CheckLock(pkl);
  1430. CheckLock(pwnd);
  1431. /*
  1432. * Remember what is about to become the "previously" active hkl
  1433. * for the return value.
  1434. */
  1435. if (ptiCurrent->spklActive != (PKL)NULL) {
  1436. pklPrev = ptiCurrent->spklActive;
  1437. hklPrev = ptiCurrent->spklActive->hkl;
  1438. } else {
  1439. pklPrev = NULL;
  1440. hklPrev = (HKL)0;
  1441. }
  1442. /*
  1443. * ShiftLock/CapsLock is a global feature applying to all layouts
  1444. * Only Winlogon and the Input Locales cpanel applet set KLF_RESET.
  1445. */
  1446. if (Flags & KLF_RESET) {
  1447. gdwKeyboardAttributes = KLL_GLOBAL_ATTR_FROM_KLF(Flags);
  1448. }
  1449. /*
  1450. * Early out
  1451. */
  1452. if (!(Flags & KLF_SETFORPROCESS) && (pkl == ptiCurrent->spklActive)) {
  1453. return hklPrev;
  1454. }
  1455. /*
  1456. * Clear out diacritics when switching kbd layouts #102838
  1457. */
  1458. pkl->wchDiacritic = 0;
  1459. /*
  1460. * Update the active layout in the pti. KLF_SETFORPROCESS will always be set
  1461. * when the keyboard layout switch is initiated by the keyboard hotkey.
  1462. */
  1463. /*
  1464. * Lock the previous keyboard layout for it's used later.
  1465. */
  1466. ThreadLockWithPti(ptiCurrent, pklPrev, &tlpklPrev);
  1467. /*
  1468. * Is this is a console thread, apply this change to any process in it's
  1469. * window. This can really help character-mode apps! (#58025)
  1470. */
  1471. if (ptiCurrent->TIF_flags & TIF_CSRSSTHREAD) {
  1472. Lock(&ptiCurrent->spklActive, pkl);
  1473. try {
  1474. ptiCurrent->pClientInfo->CodePage = pkl->CodePage;
  1475. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1476. goto UnlockAndGo;
  1477. }
  1478. } else if ((Flags & KLF_SETFORPROCESS) && !(ptiCurrent->TIF_flags & TIF_16BIT)) {
  1479. /*
  1480. * For 16 bit app., only the calling thread will have its active layout updated.
  1481. */
  1482. PTHREADINFO ptiT;
  1483. if (IS_IME_ENABLED()) {
  1484. /*
  1485. * Only allow *NOT* CSRSS to make this call
  1486. */
  1487. UserAssert(PsGetCurrentProcess() != gpepCSRSS);
  1488. // pti->pClientInfo is updated in xxxImmActivateThreadsLayout()
  1489. if (!xxxImmActivateThreadsLayout(ptiCurrent->ppi->ptiList, NULL, pkl)) {
  1490. RIPMSG1(RIP_WARNING, "no layout change necessary via xxxImmActivateThreadLayout() for process %lx", ptiCurrent->ppi);
  1491. goto UnlockAndGo;
  1492. }
  1493. } else {
  1494. BOOL fKLChanged = FALSE;
  1495. for (ptiT = ptiCurrent->ppi->ptiList; ptiT != NULL; ptiT = ptiT->ptiSibling) {
  1496. if (ptiT->spklActive != pkl && (ptiT->TIF_flags & TIF_INCLEANUP) == 0) {
  1497. Lock(&ptiT->spklActive, pkl);
  1498. UserAssert(ptiT->pClientInfo != NULL);
  1499. try {
  1500. ptiT->pClientInfo->CodePage = pkl->CodePage;
  1501. ptiT->pClientInfo->hKL = pkl->hkl;
  1502. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1503. goto UnlockAndGo;
  1504. }
  1505. fKLChanged = TRUE;
  1506. }
  1507. }
  1508. if (!fKLChanged) {
  1509. RIPMSG1(RIP_WARNING, "no layout change necessary for process %lx ?", ptiCurrent->ppi);
  1510. goto UnlockAndGo;
  1511. }
  1512. }
  1513. } else {
  1514. if (IS_IME_ENABLED()) {
  1515. xxxImmActivateLayout(ptiCurrent, pkl);
  1516. } else {
  1517. Lock(&ptiCurrent->spklActive, pkl);
  1518. }
  1519. UserAssert(ptiCurrent->pClientInfo != NULL);
  1520. if ((ptiCurrent->TIF_flags & TIF_INCLEANUP) == 0) {
  1521. try {
  1522. ptiCurrent->pClientInfo->CodePage = pkl->CodePage;
  1523. ptiCurrent->pClientInfo->hKL = pkl->hkl;
  1524. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1525. goto UnlockAndGo;
  1526. }
  1527. }
  1528. }
  1529. /*
  1530. * If (
  1531. * 1a. The process is not CSRSS. or
  1532. * b. it's CSRSS input thread.
  1533. * 2. and, the process is foreground.
  1534. * )
  1535. * update gpKbdTbl for the proper AltGr processing,
  1536. * and let the shell hook (primarily Internat.exe)
  1537. * know the foreground app's new keyboard layout.
  1538. */
  1539. // if ((ptiCurrent->TIF_flags & TIF_CSRSSTHREAD) == 0 || IsWinSrvInputThread(ptiCurrent)) {
  1540. if (gptiForeground && (gptiForeground->ppi == ptiCurrent->ppi)) {
  1541. ChangeForegroundKeyboardTable(pklPrev, pkl);
  1542. /*
  1543. * Call the Shell hook with the new language.
  1544. * Only call the hook if we are the foreground process, to prevent
  1545. * background apps from changing the indicator. (All console apps
  1546. * are part of the same process, but I have never seen a cmd window
  1547. * app change the layout, let alone in the background)
  1548. */
  1549. if (gLCIDSentToShell != pkl->hkl && (ptiCurrent != gptiRit)) {
  1550. if (IsHooked(ptiCurrent, WHF_SHELL)) {
  1551. gLCIDSentToShell = pkl->hkl;
  1552. xxxCallHook(HSHELL_LANGUAGE, (WPARAM)NULL, (LPARAM)pkl->hkl, WH_SHELL);
  1553. }
  1554. }
  1555. }
  1556. // }
  1557. /*
  1558. * Tell the app what happened
  1559. */
  1560. if (ptiCurrent->pq) {
  1561. PWND pwndT;
  1562. TL tlpwndT;
  1563. /*
  1564. * If we have no Focus window, use the Active window.
  1565. * eg: Console full-screen has NULL focus window.
  1566. */
  1567. pwndT = ptiCurrent->pq->spwndFocus;
  1568. if (pwndT == NULL) {
  1569. pwndT = ptiCurrent->pq->spwndActive;
  1570. if (pwndT == NULL) {
  1571. pwndT = pwnd;
  1572. }
  1573. }
  1574. if (pwndT != NULL) {
  1575. ThreadLockAlwaysWithPti( ptiCurrent, pwndT, &tlpwndT);
  1576. xxxSendMessage(pwndT, WM_INPUTLANGCHANGE, (WPARAM)pkl->iBaseCharset, (LPARAM)pkl->hkl);
  1577. ThreadUnlock(&tlpwndT);
  1578. }
  1579. }
  1580. /*
  1581. * Tell IME to send mode update notification
  1582. */
  1583. if (ptiCurrent && ptiCurrent->spwndDefaultIme &&
  1584. (ptiCurrent->TIF_flags & TIF_CSRSSTHREAD) == 0) {
  1585. if (IS_IME_KBDLAYOUT(pkl->hkl)
  1586. #ifdef CUAS_ENABLE
  1587. ||
  1588. IS_CICERO_ENABLED_AND_NOT16BIT()
  1589. #endif // CUAS_ENABLE
  1590. ) {
  1591. BOOL fForProcess = (ptiCurrent->TIF_flags & KLF_SETFORPROCESS) && !(ptiCurrent->TIF_flags & TIF_16BIT);
  1592. TL tlpwndIme;
  1593. TAGMSG1(DBGTAG_IMM, "Sending IMS_SENDNOTIFICATION to pwnd=%#p", ptiCurrent->spwndDefaultIme);
  1594. ThreadLockAlwaysWithPti(ptiCurrent, ptiCurrent->spwndDefaultIme, &tlpwndIme);
  1595. xxxSendMessage(ptiCurrent->spwndDefaultIme, WM_IME_SYSTEM, IMS_SENDNOTIFICATION, fForProcess);
  1596. ThreadUnlock(&tlpwndIme);
  1597. }
  1598. }
  1599. UnlockAndGo:
  1600. ThreadUnlock(&tlpklPrev);
  1601. return hklPrev;
  1602. }
  1603. BOOL xxxUnloadKeyboardLayout(
  1604. PWINDOWSTATION pwinsta,
  1605. HKL hkl)
  1606. {
  1607. PKL pkl;
  1608. /*
  1609. * Validate HKL and check to make sure an app isn't attempting to unload a system
  1610. * preloaded layout.
  1611. */
  1612. pkl = HKLtoPKL(PtiCurrent(), hkl);
  1613. if (pkl == NULL) {
  1614. return FALSE;
  1615. }
  1616. return xxxInternalUnloadKeyboardLayout(pwinsta, pkl, 0);
  1617. }
  1618. HKL _GetKeyboardLayout(
  1619. DWORD idThread)
  1620. {
  1621. PTHREADINFO ptiT;
  1622. PLIST_ENTRY pHead, pEntry;
  1623. CheckCritIn();
  1624. /*
  1625. * If idThread is NULL return hkl of the current thread
  1626. */
  1627. if (idThread == 0) {
  1628. PKL pklActive = PtiCurrentShared()->spklActive;
  1629. if (pklActive == NULL) {
  1630. return (HKL)0;
  1631. }
  1632. return pklActive->hkl;
  1633. }
  1634. /*
  1635. * Look for idThread
  1636. */
  1637. pHead = &PtiCurrent()->rpdesk->PtiList;
  1638. for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
  1639. ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
  1640. if (GETPTIID(ptiT) == (HANDLE)LongToHandle(idThread)) {
  1641. if (ptiT->spklActive == NULL) {
  1642. return (HKL)0;
  1643. }
  1644. return ptiT->spklActive->hkl;
  1645. }
  1646. }
  1647. /*
  1648. * idThread doesn't exist
  1649. */
  1650. return (HKL)0;
  1651. }
  1652. UINT _GetKeyboardLayoutList(
  1653. PWINDOWSTATION pwinsta,
  1654. UINT nItems,
  1655. HKL *ccxlpBuff)
  1656. {
  1657. UINT nHKL = 0;
  1658. PKL pkl, pklFirst;
  1659. if (!pwinsta) {
  1660. return 0;
  1661. }
  1662. pkl = pwinsta->spklList;
  1663. /*
  1664. * Windowstations that do not take input could have no layouts
  1665. */
  1666. if (pkl == NULL) {
  1667. // SetLastError() ????
  1668. return 0;
  1669. }
  1670. /*
  1671. * The client/server thunk sets nItems to 0 if ccxlpBuff == NULL
  1672. */
  1673. UserAssert(ccxlpBuff || (nItems == 0));
  1674. pklFirst = pkl;
  1675. if (nItems) {
  1676. try {
  1677. do {
  1678. if (!(pkl->dwKL_Flags & KL_UNLOADED)) {
  1679. if (nItems-- == 0) {
  1680. break;
  1681. }
  1682. nHKL++;
  1683. *ccxlpBuff++ = pkl->hkl;
  1684. }
  1685. pkl = pkl->pklNext;
  1686. } while (pkl != pklFirst);
  1687. } except (EXCEPTION_EXECUTE_HANDLER) {
  1688. RIPERR1(ERROR_INVALID_PARAMETER, RIP_ERROR,
  1689. "_GetKeyBoardLayoutList: exception writing ccxlpBuff %lx", ccxlpBuff);
  1690. return 0;
  1691. }
  1692. } else do {
  1693. if (!(pkl->dwKL_Flags & KL_UNLOADED)) {
  1694. nHKL++;
  1695. }
  1696. pkl = pkl->pklNext;
  1697. } while (pkl != pklFirst);
  1698. return nHKL;
  1699. }
  1700. /*
  1701. * Layouts are locked by each thread using them and possibly by:
  1702. * - pwinsta->spklList (head of windowstation's list)
  1703. * - gspklBaseLayout (default layout for new threads)
  1704. * The layout is marked for destruction when gets unloaded, so that it will be
  1705. * unlinked and freed as soon as an Unlock causes the lock count to go to 0.
  1706. * If it is reloaded before that time, it is unmarked for destruction. This
  1707. * ensures that laoded layouts stay around even when they go out of use.
  1708. */
  1709. BOOL xxxInternalUnloadKeyboardLayout(
  1710. PWINDOWSTATION pwinsta,
  1711. PKL pkl,
  1712. UINT Flags)
  1713. {
  1714. PTHREADINFO ptiCurrent = PtiCurrent();
  1715. TL tlpkl;
  1716. UserAssert(pkl);
  1717. /*
  1718. * Never unload the default layout, unless we are destroying the current
  1719. * windowstation or replacing one user's layouts with another's.
  1720. */
  1721. if ((pkl == gspklBaseLayout) && !(Flags & KLF_INITTIME)) {
  1722. return FALSE;
  1723. }
  1724. /*
  1725. * Keeps pkl good, but also allows destruction when unlocked later
  1726. */
  1727. ThreadLockAlwaysWithPti(ptiCurrent, pkl, &tlpkl);
  1728. /*
  1729. * Mark it for destruction so it gets removed when the lock count reaches 0
  1730. * Mark it KL_UNLOADED so that it appears to be gone from the toggle list
  1731. */
  1732. HMMarkObjectDestroy(pkl);
  1733. pkl->dwKL_Flags |= KL_UNLOADED;
  1734. /*
  1735. * If unloading this thread's active layout, helpfully activate the next one
  1736. * (Don't bother if KLF_INITTIME - unloading all previous user's layouts)
  1737. */
  1738. if (!(Flags & KLF_INITTIME)) {
  1739. UserAssert(ptiCurrent->spklActive != NULL);
  1740. if (ptiCurrent->spklActive == pkl) {
  1741. PKL pklNext;
  1742. pklNext = HKLtoPKL(ptiCurrent, (HKL)HKL_NEXT);
  1743. if (pklNext != NULL) {
  1744. TL tlPKL;
  1745. ThreadLockAlwaysWithPti(ptiCurrent, pklNext, &tlPKL);
  1746. xxxInternalActivateKeyboardLayout(pklNext, Flags, NULL);
  1747. ThreadUnlock(&tlPKL);
  1748. }
  1749. }
  1750. }
  1751. /*
  1752. * If this pkl == pwinsta->spklList, give it a chance to be destroyed by
  1753. * unlocking it from pwinsta->spklList.
  1754. */
  1755. if (pwinsta->spklList == pkl) {
  1756. UserAssert(pkl != NULL);
  1757. if (pkl != pkl->pklNext) {
  1758. pkl = Lock(&pwinsta->spklList, pkl->pklNext);
  1759. UserAssert(pkl != NULL); // gspklBaseLayout and ThreadLocked pkl
  1760. }
  1761. }
  1762. /*
  1763. * This finally destroys the unloaded layout if it is not in use anywhere
  1764. */
  1765. ThreadUnlock(&tlpkl);
  1766. /*
  1767. * Update keyboard list.
  1768. */
  1769. if (IsHooked(ptiCurrent, WHF_SHELL)) {
  1770. xxxCallHook(HSHELL_LANGUAGE, (WPARAM)NULL, (LPARAM)0, WH_SHELL);
  1771. gLCIDSentToShell = 0;
  1772. }
  1773. return TRUE;
  1774. }
  1775. VOID xxxFreeKeyboardLayouts(
  1776. PWINDOWSTATION pwinsta, BOOL bUnlock)
  1777. {
  1778. PKL pkl;
  1779. /*
  1780. * Unload all of the windowstation's layouts.
  1781. * They may still be locked by some threads (eg: console), so this
  1782. * may not destroy them all, but it will mark them all KL_UNLOADED.
  1783. * Set KLF_INITTIME to ensure that the default layout (gspklBaseLayout)
  1784. * gets unloaded too.
  1785. * Note: it's much faster to unload non-active layouts, so start with
  1786. * the next loaded layout, leaving the active layout till last.
  1787. */
  1788. while ((pkl = HKLtoPKL(PtiCurrent(), (HKL)HKL_NEXT)) != NULL) {
  1789. xxxInternalUnloadKeyboardLayout(pwinsta, pkl, KLF_INITTIME);
  1790. }
  1791. /*
  1792. * The WindowStation is being destroyed, or one user's layouts are being
  1793. * replaced by another user's, so it's OK to Unlock spklList.
  1794. * Any layout still in the double-linked circular KL list will still be
  1795. * pointed to by gspklBaseLayout: this is important, since we don't want
  1796. * to leak any KL or KBDFILE objects by losing pointers to them.
  1797. * There are no layouts when we first come here (during bootup).
  1798. */
  1799. if (bUnlock) {
  1800. Unlock(&pwinsta->spklList);
  1801. }
  1802. }
  1803. /***************************************************************************\
  1804. * DestroyKL
  1805. *
  1806. * Destroys a keyboard layout. Note that this function does not
  1807. * follow normal destroy function semantics. See IanJa.
  1808. *
  1809. * History:
  1810. * 25-Feb-1997 adams Created.
  1811. \***************************************************************************/
  1812. VOID DestroyKL(
  1813. PKL pkl)
  1814. {
  1815. PKBDFILE pkf;
  1816. /*
  1817. * Cut it out of the pwinsta->spklList circular bidirectional list.
  1818. * We know pwinsta->spklList != pkl, since pkl is unlocked.
  1819. */
  1820. pkl->pklPrev->pklNext = pkl->pklNext;
  1821. pkl->pklNext->pklPrev = pkl->pklPrev;
  1822. /*
  1823. * Unlock its pkf
  1824. */
  1825. pkf = Unlock(&pkl->spkf);
  1826. if (pkf && (pkf = Unlock(&pkl->spkfPrimary))) {
  1827. DestroyKF(pkf);
  1828. }
  1829. if (pkl->pspkfExtra) {
  1830. UINT i;
  1831. for (i = 0; i < pkl->uNumTbl && pkl->pspkfExtra[i]; ++i) {
  1832. pkf = Unlock(&pkl->pspkfExtra[i]);
  1833. if (pkf) {
  1834. DestroyKF(pkf);
  1835. }
  1836. }
  1837. UserFreePool(pkl->pspkfExtra);
  1838. }
  1839. if (pkl->piiex != NULL) {
  1840. UserFreePool(pkl->piiex);
  1841. }
  1842. if (pkl == gpKL) {
  1843. /*
  1844. * Nuke gpKL.
  1845. */
  1846. gpKL = NULL;
  1847. }
  1848. /*
  1849. * Free the pkl itself.
  1850. */
  1851. HMFreeObject(pkl);
  1852. }
  1853. /***************************************************************************\
  1854. * CleanupKeyboardLayouts
  1855. *
  1856. * Frees the all keyboard layouts in this session.
  1857. *
  1858. \***************************************************************************/
  1859. VOID CleanupKeyboardLayouts()
  1860. {
  1861. /*
  1862. * Unlock the keyboard layouts
  1863. */
  1864. if (gspklBaseLayout != NULL) {
  1865. PKL pkl;
  1866. PKL pklNext;
  1867. pkl = gspklBaseLayout->pklNext;
  1868. while (pkl->pklNext != pkl) {
  1869. pklNext = pkl->pklNext;
  1870. DestroyKL(pkl);
  1871. pkl = pklNext;
  1872. }
  1873. UserAssert(pkl == gspklBaseLayout);
  1874. if (!HMIsMarkDestroy(gspklBaseLayout)) {
  1875. HMMarkObjectDestroy(gspklBaseLayout);
  1876. }
  1877. HYDRA_HINT(HH_KBDLYOUTGLOBALCLEANUP);
  1878. if (Unlock(&gspklBaseLayout)) {
  1879. DestroyKL(pkl);
  1880. }
  1881. }
  1882. UserAssert(gpkfList == NULL);
  1883. }