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