xref: /reactos/win32ss/user/ntuser/kbdlayout.c (revision d6eebaa4)
1 /*
2  * PROJECT:         ReactOS Win32k subsystem
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            win32ss/user/ntuser/kbdlayout.c
5  * PURPOSE:         Keyboard layout management
6  * COPYRIGHT:       Copyright 2007 Saveliy Tretiakov
7  *                  Copyright 2008 Colin Finck
8  *                  Copyright 2011 Rafal Harabien
9  *                  Copyright 2022 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
10  */
11 
12 #include <win32k.h>
13 #include <immdev.h>
14 
15 // Was included only because of CP_ACP and required  the
16 // definition of SYSTEMTIME in ndk\rtltypes.h
17 //#include <winnls.h>
18 #define CP_ACP 0
19 
20 DBG_DEFAULT_CHANNEL(UserKbdLayout);
21 
22 PKL gspklBaseLayout = NULL; /* FIXME: Please move this to pWinSta->spklList */
23 PKBDFILE gpkfList = NULL;
24 DWORD gSystemFS = 0;
25 UINT gSystemCPCharSet = 0;
26 HKL ghKLSentToShell = NULL;
27 
28 typedef PVOID (*PFN_KBDLAYERDESCRIPTOR)(VOID);
29 
30 /* PRIVATE FUNCTIONS ******************************************************/
31 
32 /*
33  * Retrieves a PKL by an input locale identifier (HKL).
34  * @implemented
35  */
36 PKL FASTCALL IntHKLtoPKL(_Inout_ PTHREADINFO pti, _In_ HKL hKL)
37 {
38     PKL pFirstKL, pKL;
39 
40     pFirstKL = pti->KeyboardLayout;
41     if (!pFirstKL)
42         return NULL;
43 
44     pKL = pFirstKL;
45 
46     /* hKL can have special value HKL_NEXT or HKL_PREV */
47     if (hKL == UlongToHandle(HKL_NEXT)) /* Looking forward */
48     {
49         do
50         {
51             pKL = pKL->pklNext;
52             if (!(pKL->dwKL_Flags & KL_UNLOAD))
53                 return pKL;
54         } while (pKL != pFirstKL);
55     }
56     else if (hKL == UlongToHandle(HKL_PREV)) /* Looking backward */
57     {
58         do
59         {
60             pKL = pKL->pklPrev;
61             if (!(pKL->dwKL_Flags & KL_UNLOAD))
62                 return pKL;
63         } while (pKL != pFirstKL);
64     }
65     else if (HIWORD(hKL)) /* hKL is a full input locale identifier */
66     {
67         /* No KL_UNLOAD check */
68         do
69         {
70             if (pKL->hkl == hKL)
71                 return pKL;
72 
73             pKL = pKL->pklNext;
74         } while (pKL != pFirstKL);
75     }
76     else  /* Language only specified */
77     {
78         /* No KL_UNLOAD check */
79         do
80         {
81             if (LOWORD(pKL->hkl) == LOWORD(hKL)) /* Low word is language ID */
82                 return pKL;
83 
84             pKL = pKL->pklNext;
85         } while (pKL != pFirstKL);
86     }
87 
88     return NULL;
89 }
90 
91 /*
92  * A helper function for NtUserGetKeyboardLayoutList.
93  * @implemented
94  */
95 static UINT APIENTRY
96 IntGetKeyboardLayoutList(
97     _Inout_ PWINSTATION_OBJECT pWinSta,
98     _In_ ULONG nBuff,
99     _Out_ HKL *pHklBuff)
100 {
101     UINT ret = 0;
102     PKL pKL, pFirstKL;
103 
104     pFirstKL = gspklBaseLayout; /* FIXME: Use pWinSta->spklList instead */
105     if (!pWinSta || !pFirstKL)
106         return 0;
107 
108     pKL = pFirstKL;
109 
110     if (nBuff == 0)
111     {
112         /* Count the effective PKLs */
113         do
114         {
115             if (!(pKL->dwKL_Flags & KL_UNLOAD))
116                 ++ret;
117             pKL = pKL->pklNext;
118         } while (pKL != pFirstKL);
119     }
120     else
121     {
122         /* Copy the effective HKLs to pHklBuff */
123         do
124         {
125             if (!(pKL->dwKL_Flags & KL_UNLOAD))
126             {
127                 *pHklBuff = pKL->hkl;
128                 ++pHklBuff;
129                 ++ret;
130                 --nBuff;
131 
132                 if (nBuff == 0)
133                     break;
134             }
135             pKL = pKL->pklNext;
136         } while (pKL != pFirstKL);
137     }
138 
139     return ret;
140 }
141 
142 #if 0 && DBG
143 
144 static VOID
145 DumpKbdLayout(
146     IN PKBDTABLES pKbdTbl)
147 {
148     PVK_TO_BIT pVkToBit;
149     PVK_TO_WCHAR_TABLE pVkToWchTbl;
150     PVSC_VK pVscVk;
151     ULONG i;
152 
153     DbgPrint("Kbd layout: fLocaleFlags %x bMaxVSCtoVK %x\n",
154              pKbdTbl->fLocaleFlags, pKbdTbl->bMaxVSCtoVK);
155     DbgPrint("wMaxModBits %x\n",
156              pKbdTbl->pCharModifiers ? pKbdTbl->pCharModifiers->wMaxModBits
157                                      : 0);
158 
159     if (pKbdTbl->pCharModifiers)
160     {
161         pVkToBit = pKbdTbl->pCharModifiers->pVkToBit;
162         if (pVkToBit)
163         {
164             for (; pVkToBit->Vk; ++pVkToBit)
165             {
166                 DbgPrint("VkToBit %x -> %x\n", pVkToBit->Vk, pVkToBit->ModBits);
167             }
168         }
169 
170         for (i = 0; i <= pKbdTbl->pCharModifiers->wMaxModBits; ++i)
171         {
172             DbgPrint("ModNumber %x -> %x\n", i, pKbdTbl->pCharModifiers->ModNumber[i]);
173         }
174     }
175 
176     pVkToWchTbl = pKbdTbl->pVkToWcharTable;
177     if (pVkToWchTbl)
178     {
179         for (; pVkToWchTbl->pVkToWchars; ++pVkToWchTbl)
180         {
181             PVK_TO_WCHARS1 pVkToWch = pVkToWchTbl->pVkToWchars;
182 
183             DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n",
184                      pVkToWchTbl->nModifications, pVkToWchTbl->cbSize);
185             if (pVkToWch)
186             {
187                 while (pVkToWch->VirtualKey)
188                 {
189                     DbgPrint("pVkToWch VirtualKey %x Attributes %x wc { ",
190                              pVkToWch->VirtualKey, pVkToWch->Attributes);
191                     for (i = 0; i < pVkToWchTbl->nModifications; ++i)
192                     {
193                         DbgPrint("%x ", pVkToWch->wch[i]);
194                     }
195                     DbgPrint("}\n");
196                     pVkToWch = (PVK_TO_WCHARS1)(((PBYTE)pVkToWch) + pVkToWchTbl->cbSize);
197                 }
198             }
199         }
200     }
201 
202 // TODO: DeadKeys, KeyNames, KeyNamesExt, KeyNamesDead
203 
204     DbgPrint("pusVSCtoVK: { ");
205     if (pKbdTbl->pusVSCtoVK)
206     {
207         for (i = 0; i < pKbdTbl->bMaxVSCtoVK; ++i)
208         {
209             DbgPrint("%x -> %x, ", i, pKbdTbl->pusVSCtoVK[i]);
210         }
211     }
212     DbgPrint("}\n");
213 
214     DbgPrint("pVSCtoVK_E0: { ");
215     pVscVk = pKbdTbl->pVSCtoVK_E0;
216     if (pVscVk)
217     {
218         for (; pVscVk->Vsc; ++pVscVk)
219         {
220             DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk);
221         }
222     }
223     DbgPrint("}\n");
224 
225     DbgPrint("pVSCtoVK_E1: { ");
226     pVscVk = pKbdTbl->pVSCtoVK_E1;
227     if (pVscVk)
228     {
229         for (; pVscVk->Vsc; ++pVscVk)
230         {
231             DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk);
232         }
233     }
234     DbgPrint("}\n");
235 
236 // TODO: Ligatures
237 }
238 
239 #endif // DBG
240 
241 
242 /*
243  * UserLoadKbdDll
244  *
245  * Loads keyboard layout DLL and gets address to KbdTables
246  */
247 static BOOL
248 UserLoadKbdDll(WCHAR *pwszLayoutPath,
249                HANDLE *phModule,
250                PKBDTABLES *pKbdTables)
251 {
252     PFN_KBDLAYERDESCRIPTOR pfnKbdLayerDescriptor;
253 
254     /* Load keyboard layout DLL */
255     TRACE("Loading Keyboard DLL %ws\n", pwszLayoutPath);
256     *phModule = EngLoadImage(pwszLayoutPath);
257     if (!(*phModule))
258     {
259         ERR("Failed to load dll %ws\n", pwszLayoutPath);
260         return FALSE;
261     }
262 
263     /* Find KbdLayerDescriptor function and get layout tables */
264     TRACE("Loaded %ws\n", pwszLayoutPath);
265     pfnKbdLayerDescriptor = EngFindImageProcAddress(*phModule, "KbdLayerDescriptor");
266 
267     /* FIXME: Windows reads file instead of executing!
268               It's not safe to kbdlayout DLL in kernel mode! */
269 
270     if (pfnKbdLayerDescriptor)
271         *pKbdTables = pfnKbdLayerDescriptor();
272     else
273         ERR("Error: %ws has no KbdLayerDescriptor()\n", pwszLayoutPath);
274 
275     if (!pfnKbdLayerDescriptor || !*pKbdTables)
276     {
277         ERR("Failed to load the keyboard layout.\n");
278         EngUnloadImage(*phModule);
279         return FALSE;
280     }
281 
282 #if 0 && DBG
283     /* Dump keyboard layout */
284     DumpKbdLayout(*pKbdTables);
285 #endif
286 
287     return TRUE;
288 }
289 
290 /*
291  * UserLoadKbdFile
292  *
293  * Loads keyboard layout DLL and creates KBDFILE object
294  */
295 static PKBDFILE
296 UserLoadKbdFile(PUNICODE_STRING pwszKLID)
297 {
298     PKBDFILE pkf, pRet = NULL;
299     NTSTATUS Status;
300     ULONG cbSize;
301     HKEY hKey = NULL;
302     WCHAR wszLayoutPath[MAX_PATH] = L"\\SystemRoot\\System32\\";
303     WCHAR wszLayoutRegKey[256] = L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\"
304                                  L"Control\\Keyboard Layouts\\";
305 
306     /* Create keyboard layout file object */
307     pkf = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDFILE, sizeof(KBDFILE));
308     if (!pkf)
309     {
310         ERR("Failed to create object!\n");
311         return NULL;
312     }
313 
314     /* Set keyboard layout name */
315     swprintf(pkf->awchKF, L"%wZ", pwszKLID);
316 
317     /* Open layout registry key */
318     RtlStringCbCatW(wszLayoutRegKey, sizeof(wszLayoutRegKey), pkf->awchKF);
319     Status = RegOpenKey(wszLayoutRegKey, &hKey);
320     if (!NT_SUCCESS(Status))
321     {
322         ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey, Status);
323         goto cleanup;
324     }
325 
326     /* Read filename of layout DLL */
327     cbSize = (ULONG)(sizeof(wszLayoutPath) - wcslen(wszLayoutPath)*sizeof(WCHAR));
328     Status = RegQueryValue(hKey,
329                            L"Layout File",
330                            REG_SZ,
331                            wszLayoutPath + wcslen(wszLayoutPath),
332                            &cbSize);
333 
334     if (!NT_SUCCESS(Status))
335     {
336         ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID, Status);
337         goto cleanup;
338     }
339 
340     /* Load keyboard file now */
341     if (!UserLoadKbdDll(wszLayoutPath, &pkf->hBase, &pkf->pKbdTbl))
342     {
343         ERR("Failed to load %ws dll!\n", wszLayoutPath);
344         goto cleanup;
345     }
346 
347     /* Update next field */
348     pkf->pkfNext = gpkfList;
349     gpkfList = pkf;
350 
351     /* Return keyboard file */
352     pRet = pkf;
353 
354 cleanup:
355     if (hKey)
356         ZwClose(hKey);
357     if (pkf)
358         UserDereferenceObject(pkf); // we dont need ptr anymore
359     if (!pRet)
360     {
361         /* We have failed - destroy created object */
362         if (pkf)
363             UserDeleteObject(UserHMGetHandle(pkf), TYPE_KBDFILE);
364     }
365 
366     return pRet;
367 }
368 
369 /*
370  * co_UserLoadKbdLayout
371  *
372  * Loads keyboard layout and creates KL object
373  */
374 static PKL
375 co_UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL)
376 {
377     LCID lCid;
378     CHARSETINFO cs;
379     PKL pKl;
380 
381     /* Create keyboard layout object */
382     pKl = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDLAYOUT, sizeof(KL));
383     if (!pKl)
384     {
385         ERR("Failed to create object!\n");
386         return NULL;
387     }
388 
389     pKl->hkl = hKL;
390     pKl->spkf = UserLoadKbdFile(pustrKLID);
391 
392     /* Dereference keyboard layout */
393     UserDereferenceObject(pKl);
394 
395     /* If we failed, remove KL object */
396     if (!pKl->spkf)
397     {
398         ERR("UserLoadKbdFile(%wZ) failed!\n", pustrKLID);
399         UserDeleteObject(UserHMGetHandle(pKl), TYPE_KBDLAYOUT);
400         return NULL;
401     }
402 
403     // Up to Language Identifiers..
404     if (!NT_SUCCESS(RtlUnicodeStringToInteger(pustrKLID, 16, (PULONG)&lCid)))
405     {
406         ERR("RtlUnicodeStringToInteger failed for '%wZ'\n", pustrKLID);
407         UserDeleteObject(UserHMGetHandle(pKl), TYPE_KBDLAYOUT);
408         return NULL;
409     }
410 
411     TRACE("Language Identifiers %wZ LCID 0x%x\n", pustrKLID, lCid);
412     if (co_IntGetCharsetInfo(lCid, &cs))
413     {
414        pKl->iBaseCharset = cs.ciCharset;
415        pKl->dwFontSigs = cs.fs.fsCsb[0];
416        pKl->CodePage = (USHORT)cs.ciACP;
417        TRACE("Charset %u Font Sig %lu CodePage %u\n",
418              pKl->iBaseCharset, pKl->dwFontSigs, pKl->CodePage);
419     }
420     else
421     {
422        pKl->iBaseCharset = ANSI_CHARSET;
423        pKl->dwFontSigs = FS_LATIN1;
424        pKl->CodePage = CP_ACP;
425     }
426 
427     // Set initial system character set and font signature.
428     if (gSystemFS == 0)
429     {
430        gSystemCPCharSet = pKl->iBaseCharset;
431        gSystemFS = pKl->dwFontSigs;
432     }
433 
434     return pKl;
435 }
436 
437 /*
438  * UnloadKbdFile
439  *
440  * Destroys specified Keyboard File object
441  */
442 static
443 VOID
444 UnloadKbdFile(_In_ PKBDFILE pkf)
445 {
446     PKBDFILE *ppkfLink = &gpkfList;
447     NT_ASSERT(pkf != NULL);
448 
449     /* Find previous object */
450     while (*ppkfLink)
451     {
452         if (*ppkfLink == pkf)
453             break;
454 
455         ppkfLink = &(*ppkfLink)->pkfNext;
456     }
457 
458     if (*ppkfLink == pkf)
459         *ppkfLink = pkf->pkfNext;
460 
461     EngUnloadImage(pkf->hBase);
462     UserDeleteObject(UserHMGetHandle(pkf), TYPE_KBDFILE);
463 }
464 
465 /*
466  * UserUnloadKbl
467  *
468  * Unloads specified Keyboard Layout if possible
469  */
470 BOOL
471 UserUnloadKbl(PKL pKl)
472 {
473     /* According to msdn, UnloadKeyboardLayout can fail
474        if the keyboard layout identifier was preloaded. */
475     if (pKl == gspklBaseLayout)
476     {
477         if (pKl->pklNext == pKl->pklPrev)
478         {
479             /* There is only one layout */
480             return FALSE;
481         }
482 
483         /* Set next layout as default */
484         gspklBaseLayout = pKl->pklNext;
485     }
486 
487     if (pKl->head.cLockObj > 1)
488     {
489         /* Layout is used by other threads */
490         pKl->dwKL_Flags |= KL_UNLOAD;
491         return FALSE;
492     }
493 
494     /* Unload the layout */
495     pKl->pklPrev->pklNext = pKl->pklNext;
496     pKl->pklNext->pklPrev = pKl->pklPrev;
497     UnloadKbdFile(pKl->spkf);
498     if (pKl->piiex)
499     {
500         ExFreePoolWithTag(pKl->piiex, USERTAG_IME);
501     }
502     UserDeleteObject(UserHMGetHandle(pKl), TYPE_KBDLAYOUT);
503     return TRUE;
504 }
505 
506 /*
507  * W32kGetDefaultKeyLayout
508  *
509  * Returns default layout for new threads
510  */
511 PKL
512 W32kGetDefaultKeyLayout(VOID)
513 {
514     PKL pKl = gspklBaseLayout;
515 
516     if (!pKl)
517         return NULL;
518 
519     /* Return not unloaded layout */
520     do
521     {
522         if (!(pKl->dwKL_Flags & KL_UNLOAD))
523             return pKl;
524 
525         pKl = pKl->pklPrev; /* Confirmed on Win2k */
526     } while(pKl != gspklBaseLayout);
527 
528     /* We have not found proper KL */
529     return NULL;
530 }
531 
532 /*
533  * UserHklToKbl
534  *
535  * Gets KL object from hkl value
536  */
537 PKL
538 NTAPI
539 UserHklToKbl(HKL hKl)
540 {
541     PKL pKl = gspklBaseLayout;
542 
543     if (!gspklBaseLayout)
544         return NULL;
545 
546     do
547     {
548         if (pKl->hkl == hKl)
549             return pKl;
550 
551         pKl = pKl->pklNext;
552     } while (pKl != gspklBaseLayout);
553 
554     return NULL;
555 }
556 
557 VOID FASTCALL
558 IntReorderKeyboardLayouts(
559     _Inout_ PWINSTATION_OBJECT pWinSta,
560     _Inout_ PKL pNewKL)
561 {
562     PKL pOldKL = gspklBaseLayout;
563 
564     if ((pWinSta->Flags & WSS_NOIO) || pNewKL == pOldKL)
565         return;
566 
567     pNewKL->pklPrev->pklNext = pNewKL->pklNext;
568     pNewKL->pklNext->pklPrev = pNewKL->pklPrev;
569     pNewKL->pklNext = pOldKL;
570     pNewKL->pklPrev = pOldKL->pklPrev;
571     pOldKL->pklPrev->pklNext = pNewKL;
572     pOldKL->pklPrev = pNewKL;
573     gspklBaseLayout = pNewKL; /* Should we use UserAssignmentLock? */
574 }
575 
576 /*
577  * UserSetDefaultInputLang
578  *
579  * Sets default keyboard layout for system. Called from UserSystemParametersInfo.
580  */
581 BOOL
582 NTAPI
583 UserSetDefaultInputLang(HKL hKl)
584 {
585     PKL pKl;
586 
587     pKl = UserHklToKbl(hKl);
588     if (!pKl)
589         return FALSE;
590 
591     IntReorderKeyboardLayouts(IntGetProcessWindowStation(NULL), pKl);
592     return TRUE;
593 }
594 
595 VOID APIENTRY
596 IntImmActivateLayout(
597     _Inout_ PTHREADINFO pti,
598     _Inout_ PKL pKL)
599 {
600     PWND pImeWnd;
601     HWND hImeWnd;
602     USER_REFERENCE_ENTRY Ref;
603 
604     if (pti->KeyboardLayout == pKL)
605         return;
606 
607     pImeWnd = pti->spwndDefaultIme;
608     if (pImeWnd)
609     {
610         UserRefObjectCo(pImeWnd, &Ref);
611         hImeWnd = UserHMGetHandle(pImeWnd);
612         co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_ACTIVATELAYOUT, (LPARAM)pKL->hkl);
613         UserDerefObjectCo(pImeWnd);
614     }
615     else
616     {
617         /* Remember old keyboard layout to switch back for Chinese IMEs */
618         pti->hklPrev = pti->KeyboardLayout->hkl;
619 
620         if (pti->spDefaultImc)
621         {
622             /* IME Activation is needed */
623             pti->pClientInfo->CI_flags |= CI_IMMACTIVATE;
624         }
625     }
626 
627     UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pKL);
628     pti->pClientInfo->hKL = pKL->hkl;
629     pti->pClientInfo->CodePage = pKL->CodePage;
630 }
631 
632 static VOID co_IntSetKeyboardLayoutForProcess(PPROCESSINFO ppi, PKL pKL)
633 {
634     PTHREADINFO ptiNode, ptiNext;
635     PCLIENTINFO pClientInfo;
636     BOOL bImmMode = IS_IMM_MODE();
637 
638     for (ptiNode = ppi->ptiList; ptiNode; ptiNode = ptiNext)
639     {
640         IntReferenceThreadInfo(ptiNode);
641         ptiNext = ptiNode->ptiSibling;
642 
643         /* Skip this thread if its keyboard layout is already the correct one, or if it's dying */
644         if (ptiNode->KeyboardLayout == pKL || (ptiNode->TIF_flags & TIF_INCLEANUP))
645         {
646             IntDereferenceThreadInfo(ptiNode);
647             continue;
648         }
649 
650         if (bImmMode)
651         {
652             IntImmActivateLayout(ptiNode, pKL);
653         }
654         else
655         {
656             UserAssignmentLock((PVOID*)&ptiNode->KeyboardLayout, pKL);
657             pClientInfo = ptiNode->pClientInfo;
658             pClientInfo->CodePage = pKL->CodePage;
659             pClientInfo->hKL = pKL->hkl;
660         }
661 
662         IntDereferenceThreadInfo(ptiNode);
663     }
664 }
665 
666 HKL APIENTRY
667 co_UserActivateKeyboardLayout(
668     _Inout_ PKL     pKL,
669     _In_    ULONG   uFlags,
670     _In_opt_ PWND pWnd)
671 {
672     HKL hOldKL = NULL;
673     PKL pOldKL;
674     PTHREADINFO pti = GetW32ThreadInfo();
675     PWND pTargetWnd, pImeWnd;
676     HWND hTargetWnd, hImeWnd;
677     USER_REFERENCE_ENTRY Ref1, Ref2;
678     PCLIENTINFO ClientInfo;
679     BOOL bSetForProcess = !!(uFlags & KLF_SETFORPROCESS);
680 
681     IntReferenceThreadInfo(pti);
682     ClientInfo = pti->pClientInfo;
683 
684     pOldKL = pti->KeyboardLayout;
685     if (pOldKL)
686         hOldKL = pOldKL->hkl;
687 
688     if (uFlags & KLF_RESET)
689     {
690         FIXME("KLF_RESET\n");
691     }
692 
693     if (!bSetForProcess && pKL == pti->KeyboardLayout)
694     {
695         IntDereferenceThreadInfo(pti);
696         return hOldKL;
697     }
698 
699     pKL->wchDiacritic = UNICODE_NULL;
700 
701     if (pOldKL)
702         UserRefObjectCo(pOldKL, &Ref1);
703 
704     if (pti->TIF_flags & TIF_CSRSSTHREAD)
705     {
706         UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL);
707         ClientInfo->CodePage = pKL->CodePage;
708         ClientInfo->hKL = pKL->hkl;
709     }
710     else if (bSetForProcess)
711     {
712         co_IntSetKeyboardLayoutForProcess(pti->ppi, pKL);
713     }
714     else
715     {
716         if (IS_IMM_MODE())
717             IntImmActivateLayout(pti, pKL);
718         else
719             UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL);
720 
721         ClientInfo->CodePage = pKL->CodePage;
722         ClientInfo->hKL = pKL->hkl;
723     }
724 
725     /* Send shell message if necessary */
726     if (gptiForeground && (gptiForeground->ppi == pti->ppi) && ISITHOOKED(WH_SHELL))
727     {
728         /* Send the HKL if needed and remember it */
729         if (ghKLSentToShell != pKL->hkl)
730         {
731             co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)pKL->hkl);
732             ghKLSentToShell = pKL->hkl;
733         }
734     }
735 
736     if (pti->MessageQueue)
737     {
738         /* Determine the target window */
739         pTargetWnd = pti->MessageQueue->spwndFocus;
740         if (!pTargetWnd)
741         {
742             pTargetWnd = pti->MessageQueue->spwndActive;
743             if (!pTargetWnd)
744                 pTargetWnd = pWnd;
745         }
746 
747         /* Send WM_INPUTLANGCHANGE message */
748         if (pTargetWnd)
749         {
750             UserRefObjectCo(pTargetWnd, &Ref2);
751             hTargetWnd = UserHMGetHandle(pTargetWnd);
752             co_IntSendMessage(hTargetWnd, WM_INPUTLANGCHANGE, pKL->iBaseCharset, (LPARAM)pKL->hkl);
753             UserDerefObjectCo(pTargetWnd);
754         }
755     }
756 
757     // Refresh IME UI via WM_IME_SYSTEM:IMS_SENDNOTIFICATION messaging
758     if (!(pti->TIF_flags & TIF_CSRSSTHREAD))
759     {
760         if (IS_IME_HKL(pKL->hkl) || (IS_CICERO_MODE() && !IS_16BIT_MODE()))
761         {
762             pImeWnd = pti->spwndDefaultIme;
763             if (pImeWnd)
764             {
765                 bSetForProcess &= !IS_16BIT_MODE();
766                 UserRefObjectCo(pImeWnd, &Ref2);
767                 hImeWnd = UserHMGetHandle(pImeWnd);
768                 co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_SENDNOTIFICATION, bSetForProcess);
769                 UserDerefObjectCo(pImeWnd);
770             }
771         }
772     }
773 
774     if (pOldKL)
775         UserDerefObjectCo(pOldKL);
776 
777     IntDereferenceThreadInfo(pti);
778     return hOldKL;
779 }
780 
781 HKL APIENTRY
782 co_IntActivateKeyboardLayout(
783     _Inout_ PWINSTATION_OBJECT pWinSta,
784     _In_ HKL hKL,
785     _In_ ULONG uFlags,
786     _In_opt_ PWND pWnd)
787 {
788     PKL pKL;
789     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
790 
791     pKL = IntHKLtoPKL(pti, hKL);
792     if (!pKL)
793     {
794         ERR("Invalid HKL %p!\n", hKL);
795         return NULL;
796     }
797 
798     if (uFlags & KLF_REORDER)
799         IntReorderKeyboardLayouts(pWinSta, pKL);
800 
801     return co_UserActivateKeyboardLayout(pKL, uFlags, pWnd);
802 }
803 
804 static BOOL APIENTRY
805 co_IntUnloadKeyboardLayoutEx(
806     _Inout_ PWINSTATION_OBJECT pWinSta,
807     _Inout_ PKL pKL,
808     _In_ DWORD dwFlags)
809 {
810     PKL pNextKL;
811     USER_REFERENCE_ENTRY Ref1, Ref2;
812     PTHREADINFO pti = gptiCurrent;
813 
814     if (pKL == gspklBaseLayout && !(dwFlags & UKL_NOACTIVATENEXT))
815         return FALSE;
816 
817     UserRefObjectCo(pKL, &Ref1); /* Add reference */
818 
819     /* Regard as unloaded */
820     UserMarkObjectDestroy(pKL);
821     pKL->dwKL_Flags |= KL_UNLOAD;
822 
823     if (!(dwFlags & UKL_NOACTIVATENEXT) && pti->KeyboardLayout == pKL)
824     {
825         pNextKL = IntHKLtoPKL(pti, UlongToHandle(HKL_NEXT));
826         if (pNextKL)
827         {
828             UserRefObjectCo(pNextKL, &Ref2); /* Add reference */
829             co_UserActivateKeyboardLayout(pNextKL, dwFlags, NULL);
830             UserDerefObjectCo(pNextKL); /* Release reference */
831         }
832     }
833 
834     if (gspklBaseLayout == pKL && pKL != pKL->pklNext)
835     {
836         /* Set next layout as default (FIXME: Use UserAssignmentLock?) */
837         gspklBaseLayout = pKL->pklNext;
838     }
839 
840     UserDerefObjectCo(pKL); /* Release reference */
841 
842     if (ISITHOOKED(WH_SHELL))
843     {
844         co_IntShellHookNotify(HSHELL_LANGUAGE, 0, 0);
845         ghKLSentToShell = NULL;
846     }
847 
848     return TRUE;
849 }
850 
851 static BOOL APIENTRY
852 IntUnloadKeyboardLayout(_Inout_ PWINSTATION_OBJECT pWinSta, _In_ HKL hKL)
853 {
854     PKL pKL = IntHKLtoPKL(gptiCurrent, hKL);
855     if (!pKL)
856     {
857         ERR("Invalid HKL %p!\n", hKL);
858         return FALSE;
859     }
860     return co_IntUnloadKeyboardLayoutEx(pWinSta, pKL, 0);
861 }
862 
863 /// Invokes imm32!ImmLoadLayout and returns PIMEINFOEX
864 PIMEINFOEX FASTCALL co_UserImmLoadLayout(_In_ HKL hKL)
865 {
866     PIMEINFOEX piiex;
867 
868     if (!IS_IME_HKL(hKL) && !IS_CICERO_MODE())
869         return NULL;
870 
871     piiex = ExAllocatePoolWithTag(PagedPool, sizeof(IMEINFOEX), USERTAG_IME);
872     if (!piiex)
873         return NULL;
874 
875     if (!co_ClientImmLoadLayout(hKL, piiex))
876     {
877         ExFreePoolWithTag(piiex, USERTAG_IME);
878         return NULL;
879     }
880 
881     return piiex;
882 }
883 
884 HKL APIENTRY
885 co_IntLoadKeyboardLayoutEx(
886     IN OUT PWINSTATION_OBJECT pWinSta,
887     IN HANDLE hSafeFile,
888     IN HKL hOldKL,
889     IN PUNICODE_STRING puszSafeKLID,
890     IN HKL hNewKL,
891     IN UINT Flags)
892 {
893     PKL pOldKL, pNewKL;
894 
895     UNREFERENCED_PARAMETER(hSafeFile);
896 
897     if (hNewKL == NULL || (pWinSta->Flags & WSS_NOIO))
898         return NULL;
899 
900     /* If hOldKL is specified, unload it and load new layput as default */
901     if (hOldKL && hOldKL != hNewKL)
902     {
903         pOldKL = UserHklToKbl(hOldKL);
904         if (pOldKL)
905             UserUnloadKbl(pOldKL);
906     }
907 
908     /* FIXME: It seems KLF_RESET is only supported for WINLOGON */
909 
910     /* Let's see if layout was already loaded. */
911     pNewKL = UserHklToKbl(hNewKL);
912     if (!pNewKL)
913     {
914         /* It wasn't, so load it. */
915         pNewKL = co_UserLoadKbdLayout(puszSafeKLID, hNewKL);
916         if (!pNewKL)
917             return NULL;
918 
919         if (gspklBaseLayout)
920         {
921             /* Find last not unloaded layout */
922             PKL pLastKL = gspklBaseLayout->pklPrev;
923             while (pLastKL != gspklBaseLayout && (pLastKL->dwKL_Flags & KL_UNLOAD))
924                 pLastKL = pLastKL->pklPrev;
925 
926             /* Add new layout to the list */
927             pNewKL->pklNext = pLastKL->pklNext;
928             pNewKL->pklPrev = pLastKL;
929             pNewKL->pklNext->pklPrev = pNewKL;
930             pNewKL->pklPrev->pklNext = pNewKL;
931         }
932         else
933         {
934             /* This is the first layout */
935             pNewKL->pklNext = pNewKL;
936             pNewKL->pklPrev = pNewKL;
937             gspklBaseLayout = pNewKL;
938         }
939 
940         pNewKL->piiex = co_UserImmLoadLayout(hNewKL);
941     }
942 
943     /* If this layout was prepared to unload, undo it */
944     pNewKL->dwKL_Flags &= ~KL_UNLOAD;
945 
946     /* Reorder if necessary */
947     if (Flags & KLF_REORDER)
948         IntReorderKeyboardLayouts(pWinSta, pNewKL);
949 
950     /* Activate this layout in current thread */
951     if (Flags & KLF_ACTIVATE)
952         co_UserActivateKeyboardLayout(pNewKL, Flags, NULL);
953 
954     /* Send shell message */
955     if (!(Flags & KLF_NOTELLSHELL))
956         co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hNewKL);
957 
958     /* FIXME: KLF_REPLACELANG */
959 
960     return hNewKL;
961 }
962 
963 HANDLE FASTCALL IntVerifyKeyboardFileHandle(HANDLE hFile)
964 {
965     PFILE_OBJECT FileObject;
966     NTSTATUS Status;
967 
968     if (hFile == INVALID_HANDLE_VALUE)
969         return NULL;
970 
971     Status = ObReferenceObjectByHandle(hFile, FILE_READ_DATA, NULL, UserMode,
972                                        (PVOID*)&FileObject, NULL);
973     if (!NT_SUCCESS(Status))
974     {
975         ERR("0x%08X\n", Status);
976         return NULL;
977     }
978 
979     /* FIXME: Is the file in the system directory? */
980 
981     if (FileObject)
982         ObDereferenceObject(FileObject);
983 
984     return hFile;
985 }
986 
987 /* EXPORTS *******************************************************************/
988 
989 /*
990  * UserGetKeyboardLayout
991  *
992  * Returns hkl of given thread keyboard layout
993  */
994 HKL FASTCALL
995 UserGetKeyboardLayout(
996     DWORD dwThreadId)
997 {
998     PTHREADINFO pti;
999     PLIST_ENTRY ListEntry;
1000     PKL pKl;
1001 
1002     pti = PsGetCurrentThreadWin32Thread();
1003 
1004     if (!dwThreadId)
1005     {
1006         pKl = pti->KeyboardLayout;
1007         return pKl ? pKl->hkl : NULL;
1008     }
1009 
1010     ListEntry = pti->rpdesk->PtiList.Flink;
1011 
1012     //
1013     // Search the Desktop Thread list for related Desktop active Threads.
1014     //
1015     while(ListEntry != &pti->rpdesk->PtiList)
1016     {
1017         pti = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink);
1018 
1019         if (PsGetThreadId(pti->pEThread) == UlongToHandle(dwThreadId))
1020         {
1021            pKl = pti->KeyboardLayout;
1022            return pKl ? pKl->hkl : NULL;
1023         }
1024 
1025         ListEntry = ListEntry->Flink;
1026     }
1027 
1028     return NULL;
1029 }
1030 
1031 /*
1032  * NtUserGetKeyboardLayoutList
1033  *
1034  * Returns list of loaded keyboard layouts in system
1035  */
1036 UINT
1037 APIENTRY
1038 NtUserGetKeyboardLayoutList(
1039     ULONG nBuff,
1040     HKL *pHklBuff)
1041 {
1042     UINT ret = 0;
1043     PWINSTATION_OBJECT pWinSta;
1044 
1045     if (!pHklBuff)
1046         nBuff = 0;
1047 
1048     UserEnterShared();
1049 
1050     if (nBuff > MAXULONG / sizeof(HKL))
1051     {
1052         SetLastNtError(ERROR_INVALID_PARAMETER);
1053         goto Quit;
1054     }
1055 
1056     _SEH2_TRY
1057     {
1058         ProbeForWrite(pHklBuff, nBuff * sizeof(HKL), 1);
1059     }
1060     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1061     {
1062         SetLastNtError(_SEH2_GetExceptionCode());
1063         goto Quit;
1064     }
1065     _SEH2_END;
1066 
1067     pWinSta = IntGetProcessWindowStation(NULL);
1068 
1069     _SEH2_TRY
1070     {
1071         ret = IntGetKeyboardLayoutList(pWinSta, nBuff, pHklBuff);
1072     }
1073     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1074     {
1075         SetLastNtError(_SEH2_GetExceptionCode());
1076         goto Quit;
1077     }
1078     _SEH2_END;
1079 
1080 Quit:
1081     UserLeave();
1082     return ret;
1083 }
1084 
1085 /*
1086  * NtUserGetKeyboardLayoutName
1087  *
1088  * Returns KLID of current thread keyboard layout
1089  */
1090 BOOL
1091 APIENTRY
1092 NtUserGetKeyboardLayoutName(
1093     _Inout_ PUNICODE_STRING pustrName)
1094 {
1095     BOOL bRet = FALSE;
1096     PKL pKl;
1097     PTHREADINFO pti;
1098     UNICODE_STRING ustrNameSafe;
1099     NTSTATUS Status;
1100 
1101     UserEnterShared();
1102 
1103     pti = PsGetCurrentThreadWin32Thread();
1104     pKl = pti->KeyboardLayout;
1105 
1106     if (!pKl)
1107         goto cleanup;
1108 
1109     _SEH2_TRY
1110     {
1111         ProbeForWriteUnicodeString(pustrName);
1112         ustrNameSafe = *pustrName;
1113 
1114         ProbeForWrite(ustrNameSafe.Buffer, ustrNameSafe.MaximumLength, 1);
1115 
1116         if (IS_IME_HKL(pKl->hkl))
1117         {
1118             Status = RtlIntegerToUnicodeString(HandleToUlong(pKl->hkl), 16, &ustrNameSafe);
1119         }
1120         else
1121         {
1122             if (ustrNameSafe.MaximumLength < KL_NAMELENGTH * sizeof(WCHAR))
1123             {
1124                 EngSetLastError(ERROR_INVALID_PARAMETER);
1125                 goto cleanup;
1126             }
1127 
1128             /* FIXME: Do not use awchKF */
1129             ustrNameSafe.Length = 0;
1130             Status = RtlAppendUnicodeToString(&ustrNameSafe, pKl->spkf->awchKF);
1131         }
1132 
1133         if (NT_SUCCESS(Status))
1134         {
1135             *pustrName = ustrNameSafe;
1136             bRet = TRUE;
1137         }
1138     }
1139     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1140     {
1141         SetLastNtError(_SEH2_GetExceptionCode());
1142     }
1143     _SEH2_END;
1144 
1145 cleanup:
1146     UserLeave();
1147     return bRet;
1148 }
1149 
1150 /*
1151  * NtUserLoadKeyboardLayoutEx
1152  *
1153  * Loads keyboard layout with given locale id
1154  *
1155  * NOTE: We adopt a different design from Microsoft's one due to security reason.
1156  *       We don't use the 3rd parameter of NtUserLoadKeyboardLayoutEx.
1157  *       See https://bugtraq.securityfocus.com/detail/50056B96.6040306
1158  */
1159 HKL
1160 NTAPI
1161 NtUserLoadKeyboardLayoutEx(
1162     IN HANDLE hFile,
1163     IN DWORD offTable,
1164     IN PVOID pTables,
1165     IN HKL hOldKL,
1166     IN PUNICODE_STRING puszKLID,
1167     IN DWORD dwNewKL,
1168     IN UINT Flags)
1169 {
1170     HKL hRetKL;
1171     WCHAR Buffer[KL_NAMELENGTH];
1172     UNICODE_STRING uszSafeKLID;
1173     PWINSTATION_OBJECT pWinSta;
1174     HANDLE hSafeFile;
1175 
1176     UNREFERENCED_PARAMETER(offTable);
1177     UNREFERENCED_PARAMETER(pTables);
1178 
1179     if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG|
1180                   KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS|
1181                   KLF_RESET|KLF_SHIFTLOCK))
1182     {
1183         ERR("Invalid flags: %x\n", Flags);
1184         EngSetLastError(ERROR_INVALID_FLAGS);
1185         return NULL;
1186     }
1187 
1188     RtlInitEmptyUnicodeString(&uszSafeKLID, Buffer, sizeof(Buffer));
1189     _SEH2_TRY
1190     {
1191         ProbeForRead(puszKLID, sizeof(*puszKLID), 1);
1192         ProbeForRead(puszKLID->Buffer, sizeof(puszKLID->Length), 1);
1193         RtlCopyUnicodeString(&uszSafeKLID, puszKLID);
1194     }
1195     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1196     {
1197         SetLastNtError(_SEH2_GetExceptionCode());
1198         _SEH2_YIELD(return NULL);
1199     }
1200     _SEH2_END;
1201 
1202     UserEnterExclusive();
1203 
1204     hSafeFile = (hFile ? IntVerifyKeyboardFileHandle(hFile) : NULL);
1205     pWinSta = IntGetProcessWindowStation(NULL);
1206     hRetKL = co_IntLoadKeyboardLayoutEx(pWinSta,
1207                                         hSafeFile,
1208                                         hOldKL,
1209                                         &uszSafeKLID,
1210                                         UlongToHandle(dwNewKL),
1211                                         Flags);
1212     if (hSafeFile)
1213         ZwClose(hSafeFile);
1214 
1215     UserLeave();
1216     return hRetKL;
1217 }
1218 
1219 /*
1220  * NtUserActivateKeyboardLayout
1221  *
1222  * Activates specified layout for thread or process
1223  */
1224 HKL
1225 NTAPI
1226 NtUserActivateKeyboardLayout(
1227     HKL hKL,
1228     ULONG Flags)
1229 {
1230     PWINSTATION_OBJECT pWinSta;
1231     HKL hOldKL;
1232 
1233     UserEnterExclusive();
1234 
1235     /* FIXME */
1236 
1237     pWinSta = IntGetProcessWindowStation(NULL);
1238     hOldKL = co_IntActivateKeyboardLayout(pWinSta, hKL, Flags, NULL);
1239     UserLeave();
1240 
1241     return hOldKL;
1242 }
1243 
1244 /*
1245  * NtUserUnloadKeyboardLayout
1246  *
1247  * Unloads keyboard layout with specified hkl value
1248  */
1249 BOOL
1250 APIENTRY
1251 NtUserUnloadKeyboardLayout(
1252     HKL hKl)
1253 {
1254     BOOL ret;
1255     PWINSTATION_OBJECT pWinSta;
1256 
1257     UserEnterExclusive();
1258 
1259     pWinSta = IntGetProcessWindowStation(NULL);
1260     ret = IntUnloadKeyboardLayout(pWinSta, hKl);
1261 
1262     UserLeave();
1263     return ret;
1264 }
1265 
1266 /* EOF */
1267