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