[NtUser]
[reactos.git] / reactos / 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 PWND pWnd;
414
415 pklPrev = pti->KeyboardLayout;
416 if (pklPrev)
417 UserDereferenceObject(pklPrev);
418
419 pti->KeyboardLayout = pKl;
420 pti->pClientInfo->hKL = pKl->hkl;
421 UserReferenceObject(pKl);
422
423 if (Flags & KLF_SETFORPROCESS)
424 {
425 // FIXME
426 }
427
428 if (!(pWnd = pti->MessageQueue->spwndFocus))
429 {
430 pWnd = pti->MessageQueue->spwndActive;
431 }
432
433 // Send WM_INPUTLANGCHANGE to thread's focus window
434 co_IntSendMessage( pWnd ? UserHMGetHandle(pWnd) : 0,
435 WM_INPUTLANGCHANGE,
436 (WPARAM)pKl->iBaseCharset, // FIXME: How to set it?
437 (LPARAM)pKl->hkl); // hkl
438
439 return pklPrev;
440 }
441
442 /* EXPORTS *******************************************************************/
443
444 /*
445 * UserGetKeyboardLayout
446 *
447 * Returns hkl of given thread keyboard layout
448 */
449 HKL FASTCALL
450 UserGetKeyboardLayout(
451 DWORD dwThreadId)
452 {
453 PTHREADINFO pti;
454 PLIST_ENTRY ListEntry;
455 PKL pKl;
456
457 pti = PsGetCurrentThreadWin32Thread();
458
459 if (!dwThreadId)
460 {
461 pKl = pti->KeyboardLayout;
462 return pKl ? pKl->hkl : NULL;
463 }
464
465 ListEntry = pti->rpdesk->PtiList.Flink;
466
467 //
468 // Search the Desktop Thread list for related Desktop active Threads.
469 //
470 while(ListEntry != &pti->rpdesk->PtiList)
471 {
472 pti = CONTAINING_RECORD(ListEntry, THREADINFO, PtiLink);
473
474 if (PsGetThreadId(pti->pEThread) == UlongToHandle(dwThreadId))
475 {
476 pKl = pti->KeyboardLayout;
477 return pKl ? pKl->hkl : NULL;
478 }
479
480 ListEntry = ListEntry->Flink;
481 }
482
483 return NULL;
484 }
485
486 /*
487 * NtUserGetKeyboardLayoutList
488 *
489 * Returns list of loaded keyboard layouts in system
490 */
491 UINT
492 APIENTRY
493 NtUserGetKeyboardLayoutList(
494 ULONG nBuff,
495 HKL *pHklBuff)
496 {
497 UINT uRet = 0;
498 PKL pKl;
499
500 if (!pHklBuff)
501 nBuff = 0;
502
503 UserEnterShared();
504
505 if (!gspklBaseLayout)
506 {
507 UserLeave();
508 return 0;
509 }
510 pKl = gspklBaseLayout;
511
512 if (nBuff == 0)
513 {
514 do
515 {
516 uRet++;
517 pKl = pKl->pklNext;
518 } while (pKl != gspklBaseLayout);
519 }
520 else
521 {
522 _SEH2_TRY
523 {
524 ProbeForWrite(pHklBuff, nBuff*sizeof(HKL), 4);
525
526 while (uRet < nBuff)
527 {
528 pHklBuff[uRet] = pKl->hkl;
529 uRet++;
530 pKl = pKl->pklNext;
531 if (pKl == gspklBaseLayout)
532 break;
533 }
534 }
535 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
536 {
537 SetLastNtError(_SEH2_GetExceptionCode());
538 uRet = 0;
539 }
540 _SEH2_END;
541 }
542
543 UserLeave();
544 return uRet;
545 }
546
547 /*
548 * NtUserGetKeyboardLayoutName
549 *
550 * Returns KLID of current thread keyboard layout
551 */
552 BOOL
553 APIENTRY
554 NtUserGetKeyboardLayoutName(
555 LPWSTR pwszName)
556 {
557 BOOL bRet = FALSE;
558 PKL pKl;
559 PTHREADINFO pti;
560
561 UserEnterShared();
562
563 pti = PsGetCurrentThreadWin32Thread();
564 pKl = pti->KeyboardLayout;
565
566 if (!pKl)
567 goto cleanup;
568
569 _SEH2_TRY
570 {
571 ProbeForWrite(pwszName, KL_NAMELENGTH*sizeof(WCHAR), 1);
572 wcscpy(pwszName, pKl->spkf->awchKF);
573 bRet = TRUE;
574 }
575 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
576 {
577 SetLastNtError(_SEH2_GetExceptionCode());
578 }
579 _SEH2_END;
580
581 cleanup:
582 UserLeave();
583 return bRet;
584 }
585
586 /*
587 * NtUserLoadKeyboardLayoutEx
588 *
589 * Loads keyboard layout with given locale id
590 */
591 HKL
592 APIENTRY
593 NtUserLoadKeyboardLayoutEx(
594 IN HANDLE Handle, // hFile (See downloads.securityfocus.com/vulnerabilities/exploits/43774.c)
595 IN DWORD offTable, // Offset to KbdTables
596 IN PUNICODE_STRING puszKeyboardName, // Not used?
597 IN HKL hklUnload,
598 IN PUNICODE_STRING pustrKLID,
599 IN DWORD hkl,
600 IN UINT Flags)
601 {
602 HKL hklRet = NULL;
603 PKL pKl = NULL, pklLast;
604 WCHAR Buffer[9];
605 UNICODE_STRING ustrSafeKLID;
606
607 if (Flags & ~(KLF_ACTIVATE|KLF_NOTELLSHELL|KLF_REORDER|KLF_REPLACELANG|
608 KLF_SUBSTITUTE_OK|KLF_SETFORPROCESS|KLF_UNLOADPREVIOUS|
609 KLF_RESET|KLF_SHIFTLOCK))
610 {
611 ERR("Invalid flags: %x\n", Flags);
612 EngSetLastError(ERROR_INVALID_FLAGS);
613 return NULL;
614 }
615
616 /* FIXME: It seems KLF_RESET is only supported for WINLOGON */
617
618 RtlInitEmptyUnicodeString(&ustrSafeKLID, Buffer, sizeof(Buffer));
619 _SEH2_TRY
620 {
621 ProbeForRead(pustrKLID, sizeof(*pustrKLID), 1);
622 ProbeForRead(pustrKLID->Buffer, sizeof(pustrKLID->Length), 1);
623 RtlCopyUnicodeString(&ustrSafeKLID, pustrKLID);
624 }
625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
626 {
627 SetLastNtError(_SEH2_GetExceptionCode());
628 _SEH2_YIELD(return NULL);
629 }
630 _SEH2_END;
631
632 UserEnterExclusive();
633
634 /* If hklUnload is specified, unload it and load new layput as default */
635 if (hklUnload && hklUnload != (HKL)hkl)
636 {
637 pKl = UserHklToKbl(hklUnload);
638 if (pKl)
639 UserUnloadKbl(pKl);
640 }
641
642 /* Let's see if layout was already loaded. */
643 pKl = UserHklToKbl((HKL)hkl);
644 if (!pKl)
645 {
646 /* It wasn't, so load it. */
647 pKl = UserLoadKbdLayout(&ustrSafeKLID, (HKL)hkl);
648 if (!pKl)
649 goto cleanup;
650
651 if (gspklBaseLayout)
652 {
653 /* Find last not unloaded layout */
654 pklLast = gspklBaseLayout->pklPrev;
655 while (pklLast != gspklBaseLayout && pklLast->dwKL_Flags & KLF_UNLOAD)
656 pklLast = pklLast->pklPrev;
657
658 /* Add new layout to the list */
659 pKl->pklNext = pklLast->pklNext;
660 pKl->pklPrev = pklLast;
661 pKl->pklNext->pklPrev = pKl;
662 pKl->pklPrev->pklNext = pKl;
663 }
664 else
665 {
666 /* This is the first layout */
667 pKl->pklNext = pKl;
668 pKl->pklPrev = pKl;
669 gspklBaseLayout = pKl;
670 }
671 }
672
673 /* If this layout was prepared to unload, undo it */
674 pKl->dwKL_Flags &= ~KLF_UNLOAD;
675
676 /* Activate this layout in current thread */
677 if (Flags & KLF_ACTIVATE)
678 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl, Flags);
679
680 /* Send shell message */
681 if (!(Flags & KLF_NOTELLSHELL))
682 co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl);
683
684 /* Return hkl on success */
685 hklRet = (HKL)hkl;
686
687 /* FIXME: KLF_REPLACELANG
688 KLF_REORDER */
689
690 cleanup:
691 UserLeave();
692 return hklRet;
693 }
694
695 /*
696 * NtUserActivateKeyboardLayout
697 *
698 * Activates specified layout for thread or process
699 */
700 HKL
701 APIENTRY
702 NtUserActivateKeyboardLayout(
703 HKL hKl,
704 ULONG Flags)
705 {
706 PKL pKl = NULL;
707 HKL hkl = NULL;
708 PTHREADINFO pti;
709
710 UserEnterExclusive();
711
712 pti = PsGetCurrentThreadWin32Thread();
713
714 /* hKl can have special value HKL_NEXT or HKL_PREV */
715 if (hKl == (HKL)HKL_NEXT)
716 {
717 /* Get next keyboard layout starting with current */
718 if (pti->KeyboardLayout)
719 pKl = pti->KeyboardLayout->pklNext;
720 }
721 else if (hKl == (HKL)HKL_PREV)
722 {
723 /* Get previous keyboard layout starting with current */
724 if (pti->KeyboardLayout)
725 pKl = pti->KeyboardLayout->pklNext;
726 }
727 else
728 pKl = UserHklToKbl(hKl);
729
730 if (!pKl)
731 {
732 ERR("Invalid HKL %p!\n", hKl);
733 goto cleanup;
734 }
735
736 hkl = pKl->hkl;
737
738 /* FIXME: KLF_RESET
739 KLF_SHIFTLOCK */
740
741 if (Flags & KLF_REORDER)
742 gspklBaseLayout = pKl;
743
744 if (pKl != pti->KeyboardLayout)
745 {
746 /* Activate layout for current thread */
747 pKl = co_UserActivateKbl(pti, pKl, Flags);
748
749 /* Send shell message */
750 if (!(Flags & KLF_NOTELLSHELL))
751 co_IntShellHookNotify(HSHELL_LANGUAGE, 0, (LPARAM)hkl);
752 }
753
754 cleanup:
755 UserLeave();
756 return hkl;
757 }
758
759 /*
760 * NtUserUnloadKeyboardLayout
761 *
762 * Unloads keyboard layout with specified hkl value
763 */
764 BOOL
765 APIENTRY
766 NtUserUnloadKeyboardLayout(
767 HKL hKl)
768 {
769 PKL pKl;
770 BOOL bRet = FALSE;
771
772 UserEnterExclusive();
773
774 pKl = UserHklToKbl(hKl);
775 if (pKl)
776 bRet = UserUnloadKbl(pKl);
777 else
778 ERR("Invalid HKL %p!\n", hKl);
779
780 UserLeave();
781 return bRet;
782 }
783
784 /* EOF */