xref: /reactos/win32ss/user/ntuser/kbdlayout.c (revision 3f976713)
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  */
10 
11 #include <win32k.h>
12 
13 // Was included only because of CP_ACP and required  the
14 // definition of SYSTEMTIME in ndk\rtltypes.h
15 //#include <winnls.h>
16 #define CP_ACP 0
17 
18 DBG_DEFAULT_CHANNEL(UserKbdLayout);
19 
20 PKL gspklBaseLayout = NULL;
21 PKBDFILE gpkfList = NULL;
22 DWORD gSystemFS = 0;
23 UINT gSystemCPCharSet = 0;
24 
25 typedef PVOID (*PFN_KBDLAYERDESCRIPTOR)(VOID);
26 
27 
28 /* PRIVATE FUNCTIONS ******************************************************/
29 
30 #if 0 && DBG
31 
32 static VOID
33 DumpKbdLayout(
34     IN PKBDTABLES pKbdTbl)
35 {
36     PVK_TO_BIT pVkToBit;
37     PVK_TO_WCHAR_TABLE pVkToWchTbl;
38     PVSC_VK pVscVk;
39     ULONG i;
40 
41     DbgPrint("Kbd layout: fLocaleFlags %x bMaxVSCtoVK %x\n",
42              pKbdTbl->fLocaleFlags, pKbdTbl->bMaxVSCtoVK);
43     DbgPrint("wMaxModBits %x\n",
44              pKbdTbl->pCharModifiers ? pKbdTbl->pCharModifiers->wMaxModBits
45                                      : 0);
46 
47     if (pKbdTbl->pCharModifiers)
48     {
49         pVkToBit = pKbdTbl->pCharModifiers->pVkToBit;
50         if (pVkToBit)
51         {
52             for (; pVkToBit->Vk; ++pVkToBit)
53             {
54                 DbgPrint("VkToBit %x -> %x\n", pVkToBit->Vk, pVkToBit->ModBits);
55             }
56         }
57 
58         for (i = 0; i <= pKbdTbl->pCharModifiers->wMaxModBits; ++i)
59         {
60             DbgPrint("ModNumber %x -> %x\n", i, pKbdTbl->pCharModifiers->ModNumber[i]);
61         }
62     }
63 
64     pVkToWchTbl = pKbdTbl->pVkToWcharTable;
65     if (pVkToWchTbl)
66     {
67         for (; pVkToWchTbl->pVkToWchars; ++pVkToWchTbl)
68         {
69             PVK_TO_WCHARS1 pVkToWch = pVkToWchTbl->pVkToWchars;
70 
71             DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n",
72                      pVkToWchTbl->nModifications, pVkToWchTbl->cbSize);
73             if (pVkToWch)
74             {
75                 while (pVkToWch->VirtualKey)
76                 {
77                     DbgPrint("pVkToWch VirtualKey %x Attributes %x wc { ",
78                              pVkToWch->VirtualKey, pVkToWch->Attributes);
79                     for (i = 0; i < pVkToWchTbl->nModifications; ++i)
80                     {
81                         DbgPrint("%x ", pVkToWch->wch[i]);
82                     }
83                     DbgPrint("}\n");
84                     pVkToWch = (PVK_TO_WCHARS1)(((PBYTE)pVkToWch) + pVkToWchTbl->cbSize);
85                 }
86             }
87         }
88     }
89 
90 // TODO: DeadKeys, KeyNames, KeyNamesExt, KeyNamesDead
91 
92     DbgPrint("pusVSCtoVK: { ");
93     if (pKbdTbl->pusVSCtoVK)
94     {
95         for (i = 0; i < pKbdTbl->bMaxVSCtoVK; ++i)
96         {
97             DbgPrint("%x -> %x, ", i, pKbdTbl->pusVSCtoVK[i]);
98         }
99     }
100     DbgPrint("}\n");
101 
102     DbgPrint("pVSCtoVK_E0: { ");
103     pVscVk = pKbdTbl->pVSCtoVK_E0;
104     if (pVscVk)
105     {
106         for (; pVscVk->Vsc; ++pVscVk)
107         {
108             DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk);
109         }
110     }
111     DbgPrint("}\n");
112 
113     DbgPrint("pVSCtoVK_E1: { ");
114     pVscVk = pKbdTbl->pVSCtoVK_E1;
115     if (pVscVk)
116     {
117         for (; pVscVk->Vsc; ++pVscVk)
118         {
119             DbgPrint("%x -> %x, ", pVscVk->Vsc, pVscVk->Vk);
120         }
121     }
122     DbgPrint("}\n");
123 
124 // TODO: Ligatures
125 }
126 
127 #endif // DBG
128 
129 
130 /*
131  * UserLoadKbdDll
132  *
133  * Loads keyboard layout DLL and gets address to KbdTables
134  */
135 static BOOL
136 UserLoadKbdDll(WCHAR *pwszLayoutPath,
137                HANDLE *phModule,
138                PKBDTABLES *pKbdTables)
139 {
140     PFN_KBDLAYERDESCRIPTOR pfnKbdLayerDescriptor;
141 
142     /* Load keyboard layout DLL */
143     TRACE("Loading Keyboard DLL %ws\n", pwszLayoutPath);
144     *phModule = EngLoadImage(pwszLayoutPath);
145     if (!(*phModule))
146     {
147         ERR("Failed to load dll %ws\n", pwszLayoutPath);
148         return FALSE;
149     }
150 
151     /* Find KbdLayerDescriptor function and get layout tables */
152     TRACE("Loaded %ws\n", pwszLayoutPath);
153     pfnKbdLayerDescriptor = EngFindImageProcAddress(*phModule, "KbdLayerDescriptor");
154 
155     /* FIXME: Windows reads file instead of executing!
156               It's not safe to kbdlayout DLL in kernel mode! */
157 
158     if (pfnKbdLayerDescriptor)
159         *pKbdTables = pfnKbdLayerDescriptor();
160     else
161         ERR("Error: %ws has no KbdLayerDescriptor()\n", pwszLayoutPath);
162 
163     if (!pfnKbdLayerDescriptor || !*pKbdTables)
164     {
165         ERR("Failed to load the keyboard layout.\n");
166         EngUnloadImage(*phModule);
167         return FALSE;
168     }
169 
170 #if 0 && DBG
171     /* Dump keyboard layout */
172     DumpKbdLayout(*pKbdTables);
173 #endif
174 
175     return TRUE;
176 }
177 
178 /*
179  * UserLoadKbdFile
180  *
181  * Loads keyboard layout DLL and creates KBDFILE object
182  */
183 static PKBDFILE
184 UserLoadKbdFile(PUNICODE_STRING pwszKLID)
185 {
186     PKBDFILE pkf, pRet = NULL;
187     NTSTATUS Status;
188     ULONG cbSize;
189     HKEY hKey = NULL;
190     WCHAR wszLayoutPath[MAX_PATH] = L"\\SystemRoot\\System32\\";
191     WCHAR wszLayoutRegKey[256] = L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\"
192                                  L"Control\\Keyboard Layouts\\";
193 
194     /* Create keyboard layout file object */
195     pkf = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDFILE, sizeof(KBDFILE));
196     if (!pkf)
197     {
198         ERR("Failed to create object!\n");
199         return NULL;
200     }
201 
202     /* Set keyboard layout name */
203     swprintf(pkf->awchKF, L"%wZ", pwszKLID);
204 
205     /* Open layout registry key */
206     RtlStringCbCatW(wszLayoutRegKey, sizeof(wszLayoutRegKey), pkf->awchKF);
207     Status = RegOpenKey(wszLayoutRegKey, &hKey);
208     if (!NT_SUCCESS(Status))
209     {
210         ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey, Status);
211         goto cleanup;
212     }
213 
214     /* Read filename of layout DLL */
215     cbSize = (ULONG)(sizeof(wszLayoutPath) - wcslen(wszLayoutPath)*sizeof(WCHAR));
216     Status = RegQueryValue(hKey,
217                            L"Layout File",
218                            REG_SZ,
219                            wszLayoutPath + wcslen(wszLayoutPath),
220                            &cbSize);
221 
222     if (!NT_SUCCESS(Status))
223     {
224         ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID, Status);
225         goto cleanup;
226     }
227 
228     /* Load keyboard file now */
229     if (!UserLoadKbdDll(wszLayoutPath, &pkf->hBase, &pkf->pKbdTbl))
230     {
231         ERR("Failed to load %ws dll!\n", wszLayoutPath);
232         goto cleanup;
233     }
234 
235     /* Update next field */
236     pkf->pkfNext = gpkfList;
237     gpkfList = pkf;
238 
239     /* Return keyboard file */
240     pRet = pkf;
241 
242 cleanup:
243     if (hKey)
244         ZwClose(hKey);
245     if (pkf)
246         UserDereferenceObject(pkf); // we dont need ptr anymore
247     if (!pRet)
248     {
249         /* We have failed - destroy created object */
250         if (pkf)
251             UserDeleteObject(pkf->head.h, TYPE_KBDFILE);
252     }
253 
254     return pRet;
255 }
256 
257 /*
258  * UserLoadKbdLayout
259  *
260  * Loads keyboard layout and creates KL object
261  */
262 static PKL
263 UserLoadKbdLayout(PUNICODE_STRING pustrKLID, HKL hKL)
264 {
265     LCID lCid;
266     CHARSETINFO cs;
267     PKL pKl;
268 
269     /* Create keyboard layout object */
270     pKl = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDLAYOUT, sizeof(KL));
271     if (!pKl)
272     {
273         ERR("Failed to create object!\n");
274         return NULL;
275     }
276 
277     pKl->hkl = hKL;
278     pKl->spkf = UserLoadKbdFile(pustrKLID);
279 
280     /* Dereference keyboard layout */
281     UserDereferenceObject(pKl);
282 
283     /* If we failed, remove KL object */
284     if (!pKl->spkf)
285     {
286         ERR("UserLoadKbdFile(%wZ) failed!\n", pustrKLID);
287         UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT);
288         return NULL;
289     }
290 
291     // Up to Language Identifiers..
292     if (!NT_SUCCESS(RtlUnicodeStringToInteger(pustrKLID, 16, (PULONG)&lCid)))
293     {
294         ERR("RtlUnicodeStringToInteger failed for '%wZ'\n", pustrKLID);
295         UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT);
296         return NULL;
297     }
298 
299     TRACE("Language Identifiers %wZ LCID 0x%x\n", pustrKLID, lCid);
300     if (co_IntGetCharsetInfo(lCid, &cs))
301     {
302        pKl->iBaseCharset = cs.ciCharset;
303        pKl->dwFontSigs = cs.fs.fsCsb[0];
304        pKl->CodePage = (USHORT)cs.ciACP;
305        TRACE("Charset %u Font Sig %lu CodePage %u\n",
306              pKl->iBaseCharset, pKl->dwFontSigs, pKl->CodePage);
307     }
308     else
309     {
310        pKl->iBaseCharset = ANSI_CHARSET;
311        pKl->dwFontSigs = FS_LATIN1;
312        pKl->CodePage = CP_ACP;
313     }
314 
315     // Set initial system character set and font signature.
316     if (gSystemFS == 0)
317     {
318        gSystemCPCharSet = pKl->iBaseCharset;
319        gSystemFS = pKl->dwFontSigs;
320     }
321 
322     return pKl;
323 }
324 
325 /*
326  * UnloadKbdFile
327  *
328  * Destroys specified Keyboard File object
329  */
330 static
331 VOID
332 UnloadKbdFile(_In_ PKBDFILE pkf)
333 {
334     PKBDFILE *ppkfLink = &gpkfList;
335     NT_ASSERT(pkf != NULL);
336 
337     /* Find previous object */
338     while (*ppkfLink)
339     {
340         if (*ppkfLink == pkf)
341             break;
342 
343         ppkfLink = &(*ppkfLink)->pkfNext;
344     }
345 
346     if (*ppkfLink == pkf)
347         *ppkfLink = pkf->pkfNext;
348 
349     EngUnloadImage(pkf->hBase);
350     UserDeleteObject(pkf->head.h, TYPE_KBDFILE);
351 }
352 
353 /*
354  * UserUnloadKbl
355  *
356  * Unloads specified Keyboard Layout if possible
357  */
358 BOOL
359 UserUnloadKbl(PKL pKl)
360 {
361     /* According to msdn, UnloadKeyboardLayout can fail
362        if the keyboard layout identifier was preloaded. */
363     if (pKl == gspklBaseLayout)
364     {
365         if (pKl->pklNext == pKl->pklPrev)
366         {
367             /* There is only one layout */
368             return FALSE;
369         }
370 
371         /* Set next layout as default */
372         gspklBaseLayout = pKl->pklNext;
373     }
374 
375     if (pKl->head.cLockObj > 1)
376     {
377         /* Layout is used by other threads */
378         pKl->dwKL_Flags |= KLF_UNLOAD;
379         return FALSE;
380     }
381 
382     /* Unload the layout */
383     pKl->pklPrev->pklNext = pKl->pklNext;
384     pKl->pklNext->pklPrev = pKl->pklPrev;
385     UnloadKbdFile(pKl->spkf);
386     UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT);
387     return TRUE;
388 }
389 
390 /*
391  * W32kGetDefaultKeyLayout
392  *
393  * Returns default layout for new threads
394  */
395 PKL
396 W32kGetDefaultKeyLayout(VOID)
397 {
398     PKL pKl = gspklBaseLayout;
399 
400     if (!pKl)
401         return NULL;
402 
403     /* Return not unloaded layout */
404     do
405     {
406         if (!(pKl->dwKL_Flags & KLF_UNLOAD))
407             return pKl;
408 
409         pKl = pKl->pklPrev; /* Confirmed on Win2k */
410     } while(pKl != gspklBaseLayout);
411 
412     /* We have not found proper KL */
413     return NULL;
414 }
415 
416 /*
417  * UserHklToKbl
418  *
419  * Gets KL object from hkl value
420  */
421 PKL
422 NTAPI
423 UserHklToKbl(HKL hKl)
424 {
425     PKL pKl = gspklBaseLayout;
426 
427     if (!gspklBaseLayout)
428         return NULL;
429 
430     do
431     {
432         if (pKl->hkl == hKl)
433             return pKl;
434 
435         pKl = pKl->pklNext;
436     } while (pKl != gspklBaseLayout);
437 
438     return NULL;
439 }
440 
441 /*
442  * UserSetDefaultInputLang
443  *
444  * Sets default kyboard layout for system. Called from UserSystemParametersInfo.
445  */
446 BOOL
447 NTAPI
448 UserSetDefaultInputLang(HKL hKl)
449 {
450     PKL pKl;
451 
452     pKl = UserHklToKbl(hKl);
453     if (!pKl)
454         return FALSE;
455 
456     gspklBaseLayout = pKl;
457     return TRUE;
458 }
459 
460 /*
461  * co_UserActivateKbl
462  *
463  * Activates given layout in specified thread
464  */
465 static PKL
466 co_UserActivateKbl(PTHREADINFO pti, PKL pKl, UINT Flags)
467 {
468     PKL pklPrev;
469     PWND pWnd;
470 
471     pklPrev = pti->KeyboardLayout;
472     if (pklPrev)
473         UserDereferenceObject(pklPrev);
474 
475     pti->KeyboardLayout = pKl;
476     pti->pClientInfo->hKL = pKl->hkl;
477     UserReferenceObject(pKl);
478 
479     if (Flags & KLF_SETFORPROCESS)
480     {
481         // FIXME
482     }
483 
484     if (!(pWnd = pti->MessageQueue->spwndFocus))
485     {
486          pWnd = pti->MessageQueue->spwndActive;
487     }
488 
489     // Send WM_INPUTLANGCHANGE to thread's focus window
490     co_IntSendMessage( pWnd ? UserHMGetHandle(pWnd) : 0,
491                       WM_INPUTLANGCHANGE,
492                       (WPARAM)pKl->iBaseCharset, // FIXME: How to set it?
493                       (LPARAM)pKl->hkl); // hkl
494 
495     return pklPrev;
496 }
497 
498 /* EXPORTS *******************************************************************/
499 
500 /*
501  * UserGetKeyboardLayout
502  *
503  * Returns hkl of given thread keyboard layout
504  */
505 HKL FASTCALL
506 UserGetKeyboardLayout(
507     DWORD dwThreadId)
508 {
509     PTHREADINFO pti;
510     PLIST_ENTRY ListEntry;
511     PKL pKl;
512 
513     pti = PsGetCurrentThreadWin32Thread();
514 
515     if (!dwThreadId)
516     {
517         pKl = pti->KeyboardLayout;
518         return pKl ? pKl->hkl : NULL;
519     }
520 
521     ListEntry = pti->rpdesk->PtiList.Flink;
522 
523     //
524     // Search the Desktop Thread list for related Desktop active Threads.
525     //
526     while(ListEntry != &pti->rpdesk->PtiList)
527     {
528         pti = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink);
529 
530         if (PsGetThreadId(pti->pEThread) == UlongToHandle(dwThreadId))
531         {
532            pKl = pti->KeyboardLayout;
533            return pKl ? pKl->hkl : NULL;
534         }
535 
536         ListEntry = ListEntry->Flink;
537     }
538 
539     return NULL;
540 }
541 
542 /*
543  * NtUserGetKeyboardLayoutList
544  *
545  * Returns list of loaded keyboard layouts in system
546  */
547 UINT
548 APIENTRY
549 NtUserGetKeyboardLayoutList(
550     ULONG nBuff,
551     HKL *pHklBuff)
552 {
553     UINT uRet = 0;
554     PKL pKl;
555 
556     if (!pHklBuff)
557         nBuff = 0;
558 
559     UserEnterShared();
560 
561     if (!gspklBaseLayout)
562     {
563         UserLeave();
564         return 0;
565     }
566     pKl = gspklBaseLayout;
567 
568     if (nBuff == 0)
569     {
570         do
571         {
572             uRet++;
573             pKl = pKl->pklNext;
574         } while (pKl != gspklBaseLayout);
575     }
576     else
577     {
578         _SEH2_TRY
579         {
580             ProbeForWrite(pHklBuff, nBuff*sizeof(HKL), 4);
581 
582             while (uRet < nBuff)
583             {
584                 pHklBuff[uRet] = pKl->hkl;
585                 uRet++;
586                 pKl = pKl->pklNext;
587                 if (pKl == gspklBaseLayout)
588                     break;
589             }
590         }
591         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
592         {
593             SetLastNtError(_SEH2_GetExceptionCode());
594             uRet = 0;
595         }
596         _SEH2_END;
597     }
598 
599     UserLeave();
600     return uRet;
601 }
602 
603 /*
604  * NtUserGetKeyboardLayoutName
605  *
606  * Returns KLID of current thread keyboard layout
607  */
608 BOOL
609 APIENTRY
610 NtUserGetKeyboardLayoutName(
611     LPWSTR pwszName)
612 {
613     BOOL bRet = FALSE;
614     PKL pKl;
615     PTHREADINFO pti;
616 
617     UserEnterShared();
618 
619     pti = PsGetCurrentThreadWin32Thread();
620     pKl = pti->KeyboardLayout;
621 
622     if (!pKl)
623         goto cleanup;
624 
625     _SEH2_TRY
626     {
627         ProbeForWrite(pwszName, KL_NAMELENGTH*sizeof(WCHAR), 1);
628         wcscpy(pwszName, pKl->spkf->awchKF);
629         bRet = TRUE;
630     }
631     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
632     {
633         SetLastNtError(_SEH2_GetExceptionCode());
634     }
635     _SEH2_END;
636 
637 cleanup:
638     UserLeave();
639     return bRet;
640 }
641 
642 /*
643  * NtUserLoadKeyboardLayoutEx
644  *
645  * Loads keyboard layout with given locale id
646  */
647 HKL
648 APIENTRY
649 NtUserLoadKeyboardLayoutEx(
650     IN HANDLE Handle, // hFile (See downloads.securityfocus.com/vulnerabilities/exploits/43774.c)
651     IN DWORD offTable, // Offset to KbdTables
652     IN PUNICODE_STRING puszKeyboardName, // Not used?
653     IN HKL hklUnload,
654     IN PUNICODE_STRING pustrKLID,
655     IN DWORD hkl,
656     IN UINT Flags)
657 {
658     HKL hklRet = NULL;
659     PKL pKl = NULL, pklLast;
660     WCHAR Buffer[9];
661     UNICODE_STRING ustrSafeKLID;
662 
663     if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG|
664                   KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS|
665                   KLF_RESET|KLF_SHIFTLOCK))
666     {
667         ERR("Invalid flags: %x\n", Flags);
668         EngSetLastError(ERROR_INVALID_FLAGS);
669         return NULL;
670     }
671 
672     /* FIXME: It seems KLF_RESET is only supported for WINLOGON */
673 
674     RtlInitEmptyUnicodeString(&ustrSafeKLID, Buffer, sizeof(Buffer));
675     _SEH2_TRY
676     {
677         ProbeForRead(pustrKLID, sizeof(*pustrKLID), 1);
678         ProbeForRead(pustrKLID->Buffer, sizeof(pustrKLID->Length), 1);
679         RtlCopyUnicodeString(&ustrSafeKLID, pustrKLID);
680     }
681     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
682     {
683         SetLastNtError(_SEH2_GetExceptionCode());
684         _SEH2_YIELD(return NULL);
685     }
686     _SEH2_END;
687 
688     UserEnterExclusive();
689 
690     /* If hklUnload is specified, unload it and load new layput as default */
691     if (hklUnload && (hklUnload != UlongToHandle(hkl)))
692     {
693         pKl = UserHklToKbl(hklUnload);
694         if (pKl)
695             UserUnloadKbl(pKl);
696     }
697 
698     /* Let's see if layout was already loaded. */
699     pKl = UserHklToKbl(UlongToHandle(hkl));
700     if (!pKl)
701     {
702         /* It wasn't, so load it. */
703         pKl = UserLoadKbdLayout(&ustrSafeKLID, UlongToHandle(hkl));
704         if (!pKl)
705             goto cleanup;
706 
707         if (gspklBaseLayout)
708         {
709             /* Find last not unloaded layout */
710             pklLast = gspklBaseLayout->pklPrev;
711             while (pklLast != gspklBaseLayout && pklLast->dwKL_Flags & KLF_UNLOAD)
712                 pklLast = pklLast->pklPrev;
713 
714             /* Add new layout to the list */
715             pKl->pklNext = pklLast->pklNext;
716             pKl->pklPrev = pklLast;
717             pKl->pklNext->pklPrev = pKl;
718             pKl->pklPrev->pklNext = pKl;
719         }
720         else
721         {
722             /* This is the first layout */
723             pKl->pklNext = pKl;
724             pKl->pklPrev = pKl;
725             gspklBaseLayout = pKl;
726         }
727     }
728 
729     /* If this layout was prepared to unload, undo it */
730     pKl->dwKL_Flags &= ~KLF_UNLOAD;
731 
732     /* Activate this layout in current thread */
733     if (Flags & KLF_ACTIVATE)
734         co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl, Flags);
735 
736     /* Send shell message */
737     if (!(Flags & KLF_NOTELLSHELL))
738         co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl);
739 
740     /* Return hkl on success */
741     hklRet = UlongToHandle(hkl);
742 
743     /* FIXME: KLF_REPLACELANG
744               KLF_REORDER */
745 
746 cleanup:
747     UserLeave();
748     return hklRet;
749 }
750 
751 /*
752  * NtUserActivateKeyboardLayout
753  *
754  * Activates specified layout for thread or process
755  */
756 HKL
757 APIENTRY
758 NtUserActivateKeyboardLayout(
759     HKL hKl,
760     ULONG Flags)
761 {
762     PKL pKl = NULL;
763     HKL hkl = NULL;
764     PTHREADINFO pti;
765 
766     UserEnterExclusive();
767 
768     pti = PsGetCurrentThreadWin32Thread();
769 
770     /* hKl can have special value HKL_NEXT or HKL_PREV */
771     if (hKl == (HKL)HKL_NEXT)
772     {
773         /* Get next keyboard layout starting with current */
774         if (pti->KeyboardLayout)
775             pKl = pti->KeyboardLayout->pklNext;
776     }
777     else if (hKl == (HKL)HKL_PREV)
778     {
779         /* Get previous keyboard layout starting with current */
780         if (pti->KeyboardLayout)
781             pKl = pti->KeyboardLayout->pklPrev;
782     }
783     else
784         pKl = UserHklToKbl(hKl);
785 
786     if (!pKl)
787     {
788         ERR("Invalid HKL %p!\n", hKl);
789         goto cleanup;
790     }
791 
792     hkl = pKl->hkl;
793 
794     /* FIXME: KLF_RESET
795               KLF_SHIFTLOCK */
796 
797     if (Flags & KLF_REORDER)
798         gspklBaseLayout = pKl;
799 
800     if (pKl != pti->KeyboardLayout)
801     {
802         /* Activate layout for current thread */
803         pKl = co_UserActivateKbl(pti, pKl, Flags);
804 
805         /* Send shell message */
806         if (!(Flags & KLF_NOTELLSHELL))
807             co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl);
808     }
809 
810 cleanup:
811     UserLeave();
812     return hkl;
813 }
814 
815 /*
816  * NtUserUnloadKeyboardLayout
817  *
818  * Unloads keyboard layout with specified hkl value
819  */
820 BOOL
821 APIENTRY
822 NtUserUnloadKeyboardLayout(
823     HKL hKl)
824 {
825     PKL pKl;
826     BOOL bRet = FALSE;
827 
828     UserEnterExclusive();
829 
830     pKl = UserHklToKbl(hKl);
831     if (pKl)
832         bRet = UserUnloadKbl(pKl);
833     else
834         ERR("Invalid HKL %p!\n", hKl);
835 
836     UserLeave();
837     return bRet;
838 }
839 
840 /* EOF */
841