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