xref: /reactos/win32ss/user/ntuser/kbdlayout.c (revision 372a445a)
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     UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT);
501     return TRUE;
502 }
503 
504 /*
505  * W32kGetDefaultKeyLayout
506  *
507  * Returns default layout for new threads
508  */
509 PKL
510 W32kGetDefaultKeyLayout(VOID)
511 {
512     PKL pKl = gspklBaseLayout;
513 
514     if (!pKl)
515         return NULL;
516 
517     /* Return not unloaded layout */
518     do
519     {
520         if (!(pKl->dwKL_Flags & KLF_UNLOAD))
521             return pKl;
522 
523         pKl = pKl->pklPrev; /* Confirmed on Win2k */
524     } while(pKl != gspklBaseLayout);
525 
526     /* We have not found proper KL */
527     return NULL;
528 }
529 
530 /*
531  * UserHklToKbl
532  *
533  * Gets KL object from hkl value
534  */
535 PKL
536 NTAPI
537 UserHklToKbl(HKL hKl)
538 {
539     PKL pKl = gspklBaseLayout;
540 
541     if (!gspklBaseLayout)
542         return NULL;
543 
544     do
545     {
546         if (pKl->hkl == hKl)
547             return pKl;
548 
549         pKl = pKl->pklNext;
550     } while (pKl != gspklBaseLayout);
551 
552     return NULL;
553 }
554 
555 /*
556  * UserSetDefaultInputLang
557  *
558  * Sets default kyboard layout for system. Called from UserSystemParametersInfo.
559  */
560 BOOL
561 NTAPI
562 UserSetDefaultInputLang(HKL hKl)
563 {
564     PKL pKl;
565 
566     pKl = UserHklToKbl(hKl);
567     if (!pKl)
568         return FALSE;
569 
570     gspklBaseLayout = pKl;
571     return TRUE;
572 }
573 
574 /*
575  * co_UserActivateKbl
576  *
577  * Activates given layout in specified thread
578  */
579 static PKL
580 co_UserActivateKbl(PTHREADINFO pti, PKL pKl, UINT Flags)
581 {
582     PKL pklPrev;
583     PWND pWnd;
584 
585     pklPrev = pti->KeyboardLayout;
586 
587     UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pKl);
588     pti->pClientInfo->hKL = pKl->hkl;
589 
590     if (Flags & KLF_SETFORPROCESS)
591     {
592         FIXME("KLF_SETFORPROCESS\n");
593     }
594 
595     if (!(pWnd = pti->MessageQueue->spwndFocus))
596     {
597          pWnd = pti->MessageQueue->spwndActive;
598     }
599 
600     // Send WM_INPUTLANGCHANGE to thread's focus window
601     co_IntSendMessage( pWnd ? UserHMGetHandle(pWnd) : 0,
602                       WM_INPUTLANGCHANGE,
603                       (WPARAM)pKl->iBaseCharset, // FIXME: How to set it?
604                       (LPARAM)pKl->hkl); // hkl
605 
606     return pklPrev;
607 }
608 
609 // Win: xxxImmActivateLayout
610 VOID APIENTRY
611 IntImmActivateLayout(
612     _Inout_ PTHREADINFO pti,
613     _Inout_ PKL pKL)
614 {
615     PWND pImeWnd;
616     HWND hImeWnd;
617     USER_REFERENCE_ENTRY Ref;
618 
619     if (pti->KeyboardLayout == pKL)
620         return;
621 
622     pImeWnd = pti->spwndDefaultIme;
623     if (pImeWnd)
624     {
625         UserRefObjectCo(pImeWnd, &Ref);
626         hImeWnd = UserHMGetHandle(pImeWnd);
627         co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_ACTIVATELAYOUT, (LPARAM)pKL->hkl);
628         UserDerefObjectCo(pImeWnd);
629     }
630 
631     UserAssignmentLock((PVOID*)&(pti->KeyboardLayout), pKL);
632     pti->pClientInfo->hKL = pKL->hkl;
633 }
634 
635 /* Win: xxxInternalActivateKeyboardLayout */
636 HKL APIENTRY
637 co_UserActivateKeyboardLayout(
638     _Inout_ PKL     pKL,
639     _In_    ULONG   uFlags,
640     _Inout_ PWND    pWnd)
641 {
642     HKL hOldKL = NULL;
643     PKL pOldKL = NULL;
644     PTHREADINFO pti = GetW32ThreadInfo();
645     PWND pTargetWnd, pImeWnd;
646     HWND hTargetWnd, hImeWnd;
647     USER_REFERENCE_ENTRY Ref1, Ref2;
648     PCLIENTINFO ClientInfo = pti->pClientInfo;
649 
650     if (pti->KeyboardLayout)
651     {
652         pOldKL = pti->KeyboardLayout;
653         if (pOldKL)
654             hOldKL = pOldKL->hkl;
655     }
656 
657     if (uFlags & KLF_RESET)
658     {
659         FIXME("KLF_RESET\n");
660     }
661 
662     if (!(uFlags & KLF_SETFORPROCESS) && pKL == pti->KeyboardLayout)
663         return hOldKL;
664 
665     pKL->wchDiacritic = 0;
666 
667     if (pOldKL)
668         UserRefObjectCo(pOldKL, &Ref1);
669 
670     if (pti->TIF_flags & TIF_CSRSSTHREAD)
671     {
672         UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL);
673         ClientInfo->CodePage = pKL->CodePage;
674         ClientInfo->hKL = pKL->hkl;
675     }
676     else if (uFlags & KLF_SETFORPROCESS)
677     {
678         FIXME("KLF_SETFORPROCESS\n");
679     }
680     else
681     {
682         if (IS_IMM_MODE())
683             IntImmActivateLayout(pti, pKL);
684         else
685             UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pKL);
686 
687         ClientInfo->CodePage = pKL->CodePage;
688         ClientInfo->hKL = pKL->hkl;
689     }
690 
691     if (gptiForeground && (gptiForeground->ppi == pti->ppi))
692     {
693         /* Send shell message */
694         co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)pKL->hkl);
695     }
696 
697     if (pti->MessageQueue)
698     {
699         /* Determine the target window */
700         pTargetWnd = pti->MessageQueue->spwndFocus;
701         if (!pTargetWnd)
702         {
703             pTargetWnd = pti->MessageQueue->spwndActive;
704             if (!pTargetWnd)
705                 pTargetWnd = pWnd;
706         }
707 
708         /* Send WM_INPUTLANGCHANGE message */
709         if (pTargetWnd)
710         {
711             UserRefObjectCo(pTargetWnd, &Ref2);
712             hTargetWnd = UserHMGetHandle(pTargetWnd);
713             co_IntSendMessage(hTargetWnd, WM_INPUTLANGCHANGE, pKL->iBaseCharset, (LPARAM)pKL->hkl);
714             UserDerefObjectCo(pTargetWnd);
715         }
716     }
717 
718     /* Send WM_IME_SYSTEM:IMS_SENDNOTIFICATION message if necessary */
719     if (pti && !(pti->TIF_flags & TIF_CSRSSTHREAD))
720     {
721         if (IS_IME_HKL(pKL->hkl) || (gpsi->dwSRVIFlags & SRVINFO_CICERO_ENABLED))
722         {
723             pImeWnd = pti->spwndDefaultIme;
724             if (pImeWnd)
725             {
726                 UserRefObjectCo(pImeWnd, &Ref2);
727                 BOOL bProcess = !!(pti->TIF_flags & KLF_SETFORPROCESS);
728                 hImeWnd = UserHMGetHandle(pImeWnd);
729                 co_IntSendMessage(hImeWnd, WM_IME_SYSTEM, IMS_SENDNOTIFICATION, bProcess);
730                 UserDerefObjectCo(pImeWnd);
731             }
732         }
733     }
734 
735     if (pOldKL)
736         UserDerefObjectCo(pOldKL);
737     return hOldKL;
738 }
739 
740 // Win: ReorderKeyboardLayouts
741 VOID FASTCALL
742 IntReorderKeyboardLayouts(
743     _Inout_ PWINSTATION_OBJECT pWinSta,
744     _Inout_ PKL pKL)
745 {
746     /* FIXME */
747     gspklBaseLayout = pKL;
748 }
749 
750 /* Win: xxxActivateKeyboardLayout */
751 HKL APIENTRY
752 co_IntActivateKeyboardLayout(
753     _Inout_ PWINSTATION_OBJECT pWinSta,
754     _In_ HKL hKL,
755     _In_ ULONG uFlags,
756     _Inout_ PWND pWnd)
757 {
758     PKL pKL;
759     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
760 
761     pKL = IntHKLtoPKL(pti, hKL);
762     if (!pKL)
763     {
764         ERR("Invalid HKL %p!\n", hKL);
765         return NULL;
766     }
767 
768     if (uFlags & KLF_REORDER)
769         IntReorderKeyboardLayouts(pWinSta, pKL);
770 
771     return co_UserActivateKeyboardLayout(pKL, uFlags, pWnd);
772 }
773 
774 // Win: xxxInternalUnloadKeyboardLayout
775 static BOOL APIENTRY
776 co_IntUnloadKeyboardLayoutEx(
777     _Inout_ PWINSTATION_OBJECT pWinSta,
778     _Inout_ PKL pKL,
779     _In_ DWORD dwFlags)
780 {
781     PKL pNextKL;
782     USER_REFERENCE_ENTRY Ref1, Ref2;
783     PTHREADINFO pti = gptiCurrent;
784 
785     if (pKL == gspklBaseLayout && !(dwFlags & 0x80000000))
786         return FALSE;
787 
788     UserRefObjectCo(pKL, &Ref1); /* Add reference */
789 
790     /* Regard as unloaded */
791     UserMarkObjectDestroy(pKL);
792     pKL->dwKL_Flags |= KLF_UNLOAD;
793 
794     if (!(dwFlags & 0x80000000) && pti->KeyboardLayout == pKL)
795     {
796         pNextKL = IntHKLtoPKL(pti, (HKL)(ULONG_PTR)HKL_NEXT);
797         if (pNextKL)
798         {
799             UserRefObjectCo(pNextKL, &Ref2); /* Add reference */
800             co_UserActivateKeyboardLayout(pNextKL, dwFlags, NULL);
801             UserDerefObjectCo(pNextKL); /* Release reference */
802         }
803     }
804 
805     if (gspklBaseLayout == pKL && pKL != pKL->pklNext)
806     {
807         /* Set next layout as default (FIXME: Use UserAssignmentLock?) */
808         gspklBaseLayout = pKL->pklNext;
809     }
810 
811     UserDerefObjectCo(pKL); /* Release reference */
812 
813     if (pti->pDeskInfo->fsHooks)
814     {
815         co_IntShellHookNotify(HSHELL_LANGUAGE, 0, 0);
816         gLCIDSentToShell = 0;
817     }
818 
819     return TRUE;
820 }
821 
822 // Win: xxxUnloadKeyboardLayout
823 static BOOL APIENTRY
824 IntUnloadKeyboardLayout(_Inout_ PWINSTATION_OBJECT pWinSta, _In_ HKL hKL)
825 {
826     PKL pKL = IntHKLtoPKL(gptiCurrent, hKL);
827     if (!pKL)
828     {
829         ERR("Invalid HKL %p!\n", hKL);
830         return FALSE;
831     }
832     return co_IntUnloadKeyboardLayoutEx(pWinSta, pKL, 0);
833 }
834 
835 /* EXPORTS *******************************************************************/
836 
837 /*
838  * UserGetKeyboardLayout
839  *
840  * Returns hkl of given thread keyboard layout
841  */
842 HKL FASTCALL
843 UserGetKeyboardLayout(
844     DWORD dwThreadId)
845 {
846     PTHREADINFO pti;
847     PLIST_ENTRY ListEntry;
848     PKL pKl;
849 
850     pti = PsGetCurrentThreadWin32Thread();
851 
852     if (!dwThreadId)
853     {
854         pKl = pti->KeyboardLayout;
855         return pKl ? pKl->hkl : NULL;
856     }
857 
858     ListEntry = pti->rpdesk->PtiList.Flink;
859 
860     //
861     // Search the Desktop Thread list for related Desktop active Threads.
862     //
863     while(ListEntry != &pti->rpdesk->PtiList)
864     {
865         pti = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink);
866 
867         if (PsGetThreadId(pti->pEThread) == UlongToHandle(dwThreadId))
868         {
869            pKl = pti->KeyboardLayout;
870            return pKl ? pKl->hkl : NULL;
871         }
872 
873         ListEntry = ListEntry->Flink;
874     }
875 
876     return NULL;
877 }
878 
879 /*
880  * NtUserGetKeyboardLayoutList
881  *
882  * Returns list of loaded keyboard layouts in system
883  */
884 UINT
885 APIENTRY
886 NtUserGetKeyboardLayoutList(
887     ULONG nBuff,
888     HKL *pHklBuff)
889 {
890     UINT ret = 0;
891     PWINSTATION_OBJECT pWinSta;
892 
893     if (!pHklBuff)
894         nBuff = 0;
895 
896     UserEnterShared();
897 
898     if (nBuff > MAXULONG / sizeof(HKL))
899     {
900         SetLastNtError(ERROR_INVALID_PARAMETER);
901         goto Quit;
902     }
903 
904     _SEH2_TRY
905     {
906         ProbeForWrite(pHklBuff, nBuff * sizeof(HKL), 1);
907     }
908     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
909     {
910         SetLastNtError(_SEH2_GetExceptionCode());
911         goto Quit;
912     }
913     _SEH2_END;
914 
915     pWinSta = IntGetProcessWindowStation(NULL);
916 
917     _SEH2_TRY
918     {
919         ret = IntGetKeyboardLayoutList(pWinSta, nBuff, pHklBuff);
920     }
921     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
922     {
923         SetLastNtError(_SEH2_GetExceptionCode());
924         goto Quit;
925     }
926     _SEH2_END;
927 
928 Quit:
929     UserLeave();
930     return ret;
931 }
932 
933 /*
934  * NtUserGetKeyboardLayoutName
935  *
936  * Returns KLID of current thread keyboard layout
937  */
938 BOOL
939 APIENTRY
940 NtUserGetKeyboardLayoutName(
941     _Inout_ PUNICODE_STRING pustrName)
942 {
943     BOOL bRet = FALSE;
944     PKL pKl;
945     PTHREADINFO pti;
946     UNICODE_STRING ustrNameSafe;
947     NTSTATUS Status;
948 
949     UserEnterShared();
950 
951     pti = PsGetCurrentThreadWin32Thread();
952     pKl = pti->KeyboardLayout;
953 
954     if (!pKl)
955         goto cleanup;
956 
957     _SEH2_TRY
958     {
959         ProbeForWriteUnicodeString(pustrName);
960         ustrNameSafe = *pustrName;
961 
962         ProbeForWrite(ustrNameSafe.Buffer, ustrNameSafe.MaximumLength, 1);
963 
964         if (IS_IME_HKL(pKl->hkl))
965         {
966             Status = RtlIntegerToUnicodeString((ULONG)(ULONG_PTR)pKl->hkl, 16, &ustrNameSafe);
967         }
968         else
969         {
970             if (ustrNameSafe.MaximumLength < KL_NAMELENGTH * sizeof(WCHAR))
971             {
972                 EngSetLastError(ERROR_INVALID_PARAMETER);
973                 goto cleanup;
974             }
975 
976             /* FIXME: Do not use awchKF */
977             ustrNameSafe.Length = 0;
978             Status = RtlAppendUnicodeToString(&ustrNameSafe, pKl->spkf->awchKF);
979         }
980 
981         if (NT_SUCCESS(Status))
982         {
983             *pustrName = ustrNameSafe;
984             bRet = TRUE;
985         }
986     }
987     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
988     {
989         SetLastNtError(_SEH2_GetExceptionCode());
990     }
991     _SEH2_END;
992 
993 cleanup:
994     UserLeave();
995     return bRet;
996 }
997 
998 /*
999  * NtUserLoadKeyboardLayoutEx
1000  *
1001  * Loads keyboard layout with given locale id
1002  */
1003 HKL
1004 APIENTRY
1005 NtUserLoadKeyboardLayoutEx(
1006     IN HANDLE Handle, // hFile (See downloads.securityfocus.com/vulnerabilities/exploits/43774.c)
1007     IN DWORD offTable, // Offset to KbdTables
1008     IN PUNICODE_STRING puszKeyboardName, // Not used?
1009     IN HKL hklUnload,
1010     IN PUNICODE_STRING pustrKLID,
1011     IN DWORD hkl,
1012     IN UINT Flags)
1013 {
1014     HKL hklRet = NULL;
1015     PKL pKl = NULL, pklLast;
1016     WCHAR Buffer[9];
1017     UNICODE_STRING ustrSafeKLID;
1018 
1019     if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG|
1020                   KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS|
1021                   KLF_RESET|KLF_SHIFTLOCK))
1022     {
1023         ERR("Invalid flags: %x\n", Flags);
1024         EngSetLastError(ERROR_INVALID_FLAGS);
1025         return NULL;
1026     }
1027 
1028     /* FIXME: It seems KLF_RESET is only supported for WINLOGON */
1029 
1030     RtlInitEmptyUnicodeString(&ustrSafeKLID, Buffer, sizeof(Buffer));
1031     _SEH2_TRY
1032     {
1033         ProbeForRead(pustrKLID, sizeof(*pustrKLID), 1);
1034         ProbeForRead(pustrKLID->Buffer, sizeof(pustrKLID->Length), 1);
1035         RtlCopyUnicodeString(&ustrSafeKLID, pustrKLID);
1036     }
1037     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1038     {
1039         SetLastNtError(_SEH2_GetExceptionCode());
1040         _SEH2_YIELD(return NULL);
1041     }
1042     _SEH2_END;
1043 
1044     UserEnterExclusive();
1045 
1046     /* If hklUnload is specified, unload it and load new layput as default */
1047     if (hklUnload && (hklUnload != UlongToHandle(hkl)))
1048     {
1049         pKl = UserHklToKbl(hklUnload);
1050         if (pKl)
1051             UserUnloadKbl(pKl);
1052     }
1053 
1054     /* Let's see if layout was already loaded. */
1055     pKl = UserHklToKbl(UlongToHandle(hkl));
1056     if (!pKl)
1057     {
1058         /* It wasn't, so load it. */
1059         pKl = UserLoadKbdLayout(&ustrSafeKLID, UlongToHandle(hkl));
1060         if (!pKl)
1061             goto cleanup;
1062 
1063         if (gspklBaseLayout)
1064         {
1065             /* Find last not unloaded layout */
1066             pklLast = gspklBaseLayout->pklPrev;
1067             while (pklLast != gspklBaseLayout && pklLast->dwKL_Flags & KLF_UNLOAD)
1068                 pklLast = pklLast->pklPrev;
1069 
1070             /* Add new layout to the list */
1071             pKl->pklNext = pklLast->pklNext;
1072             pKl->pklPrev = pklLast;
1073             pKl->pklNext->pklPrev = pKl;
1074             pKl->pklPrev->pklNext = pKl;
1075         }
1076         else
1077         {
1078             /* This is the first layout */
1079             pKl->pklNext = pKl;
1080             pKl->pklPrev = pKl;
1081             gspklBaseLayout = pKl;
1082         }
1083     }
1084 
1085     /* If this layout was prepared to unload, undo it */
1086     pKl->dwKL_Flags &= ~KLF_UNLOAD;
1087 
1088     /* Activate this layout in current thread */
1089     if (Flags & KLF_ACTIVATE)
1090         co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl, Flags);
1091 
1092     /* Send shell message */
1093     if (!(Flags & KLF_NOTELLSHELL))
1094         co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl);
1095 
1096     /* Return hkl on success */
1097     hklRet = UlongToHandle(hkl);
1098 
1099     /* FIXME: KLF_REPLACELANG
1100               KLF_REORDER */
1101 
1102 cleanup:
1103     UserLeave();
1104     return hklRet;
1105 }
1106 
1107 /*
1108  * NtUserActivateKeyboardLayout
1109  *
1110  * Activates specified layout for thread or process
1111  */
1112 HKL
1113 NTAPI
1114 NtUserActivateKeyboardLayout(
1115     HKL hKL,
1116     ULONG Flags)
1117 {
1118     PWINSTATION_OBJECT pWinSta;
1119     HKL hOldKL;
1120 
1121     UserEnterExclusive();
1122 
1123     /* FIXME */
1124 
1125     pWinSta = IntGetProcessWindowStation(NULL);
1126     hOldKL = co_IntActivateKeyboardLayout(pWinSta, hKL, Flags, NULL);
1127     UserLeave();
1128 
1129     return hOldKL;
1130 }
1131 
1132 /*
1133  * NtUserUnloadKeyboardLayout
1134  *
1135  * Unloads keyboard layout with specified hkl value
1136  */
1137 BOOL
1138 APIENTRY
1139 NtUserUnloadKeyboardLayout(
1140     HKL hKl)
1141 {
1142     BOOL ret;
1143     PWINSTATION_OBJECT pWinSta;
1144 
1145     UserEnterExclusive();
1146 
1147     pWinSta = IntGetProcessWindowStation(NULL);
1148     ret = IntUnloadKeyboardLayout(pWinSta, hKl);
1149 
1150     UserLeave();
1151     return ret;
1152 }
1153 
1154 /* EOF */
1155