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
15 DBG_DEFAULT_CHANNEL(UserKbdLayout
);
17 PKL gspklBaseLayout
= NULL
;
18 PKBDFILE gpkfList
= NULL
;
20 UINT gSystemCPCharSet
= 0;
22 typedef PVOID (*PFN_KBDLAYERDESCRIPTOR
)(VOID
);
25 /* PRIVATE FUNCTIONS ******************************************************/
30 * Loads keyboard layout DLL and gets address to KbdTables
33 UserLoadKbdDll(WCHAR
*pwszLayoutPath
,
35 PKBDTABLES
*pKbdTables
)
37 PFN_KBDLAYERDESCRIPTOR pfnKbdLayerDescriptor
;
39 /* Load keyboard layout DLL */
40 TRACE("Loading Keyboard DLL %ws\n", pwszLayoutPath
);
41 *phModule
= EngLoadImage(pwszLayoutPath
);
44 ERR("Failed to load dll %ws\n", pwszLayoutPath
);
48 /* Find KbdLayerDescriptor function and get layout tables */
49 TRACE("Loaded %ws\n", pwszLayoutPath
);
50 pfnKbdLayerDescriptor
= EngFindImageProcAddress(*phModule
, "KbdLayerDescriptor");
52 /* FIXME: Windows reads file instead of executing!
53 It's not safe to kbdlayout DLL in kernel mode! */
55 if (pfnKbdLayerDescriptor
)
56 *pKbdTables
= pfnKbdLayerDescriptor();
58 ERR("Error: %ws has no KbdLayerDescriptor()\n", pwszLayoutPath
);
60 if (!pfnKbdLayerDescriptor
|| !*pKbdTables
)
62 ERR("Failed to load the keyboard layout.\n");
63 EngUnloadImage(*phModule
);
67 #if 0 /* Dump keyboard layout */
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
);
77 DbgPrint("VkToBit %x -> %x\n", pVkToBit
->Vk
, pVkToBit
->ModBits
);
80 for (i
= 0; i
<= (*pKbdTables
)->pCharModifiers
->wMaxModBits
; ++i
)
81 DbgPrint("ModNumber %x -> %x\n", i
, (*pKbdTables
)->pCharModifiers
->ModNumber
[i
]);
82 while (pVkToWchTbl
->pVkToWchars
)
84 PVK_TO_WCHARS1 pVkToWch
= pVkToWchTbl
->pVkToWchars
;
85 DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n", pVkToWchTbl
->nModifications
, pVkToWchTbl
->cbSize
);
86 while (pVkToWch
->VirtualKey
)
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
]);
92 pVkToWch
= (PVK_TO_WCHARS1
)(((PBYTE
)pVkToWch
) + pVkToWchTbl
->cbSize
);
96 DbgPrint("pusVSCtoVK: { ");
97 for (i
= 0; i
< (*pKbdTables
)->bMaxVSCtoVK
; ++i
)
98 DbgPrint("%x -> %x, ", i
, (*pKbdTables
)->pusVSCtoVK
[i
]);
100 DbgPrint("pVSCtoVK_E0: { ");
103 DbgPrint("%x -> %x, ", pVscVk
->Vsc
, pVscVk
->Vk
);
107 pVscVk
= (*pKbdTables
)->pVSCtoVK_E1
;
108 DbgPrint("pVSCtoVK_E1: { ");
111 DbgPrint("%x -> %x, ", pVscVk
->Vsc
, pVscVk
->Vk
);
125 * Loads keyboard layout DLL and creates KBDFILE object
128 UserLoadKbdFile(PUNICODE_STRING pwszKLID
)
130 PKBDFILE pkf
, pRet
= NULL
;
134 WCHAR wszLayoutPath
[MAX_PATH
] = L
"\\SystemRoot\\System32\\";
135 WCHAR wszLayoutRegKey
[256] = L
"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\"
136 L
"Control\\Keyboard Layouts\\";
138 /* Create keyboard layout file object */
139 pkf
= UserCreateObject(gHandleTable
, NULL
, NULL
, NULL
, TYPE_KBDFILE
, sizeof(KBDFILE
));
142 ERR("Failed to create object!\n");
146 /* Set keyboard layout name */
147 swprintf(pkf
->awchKF
, L
"%wZ", pwszKLID
);
149 /* Open layout registry key */
150 RtlStringCbCatW(wszLayoutRegKey
, sizeof(wszLayoutRegKey
), pkf
->awchKF
);
151 Status
= RegOpenKey(wszLayoutRegKey
, &hKey
);
152 if (!NT_SUCCESS(Status
))
154 ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey
, Status
);
158 /* Read filename of layout DLL */
159 cbSize
= sizeof(wszLayoutPath
) - wcslen(wszLayoutPath
)*sizeof(WCHAR
);
160 Status
= RegQueryValue(hKey
,
163 wszLayoutPath
+ wcslen(wszLayoutPath
),
166 if (!NT_SUCCESS(Status
))
168 ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID
, Status
);
172 /* Load keyboard file now */
173 if (!UserLoadKbdDll(wszLayoutPath
, &pkf
->hBase
, &pkf
->pKbdTbl
))
175 ERR("Failed to load %ws dll!\n", wszLayoutPath
);
179 /* Update next field */
180 pkf
->pkfNext
= gpkfList
;
183 /* Return keyboard file */
190 UserDereferenceObject(pkf
); // we dont need ptr anymore
193 /* We have failed - destroy created object */
195 UserDeleteObject(pkf
->head
.h
, TYPE_KBDFILE
);
204 * Loads keyboard layout and creates KL object
207 UserLoadKbdLayout(PUNICODE_STRING pustrKLID
, HKL hKL
)
213 /* Create keyboard layout object */
214 pKl
= UserCreateObject(gHandleTable
, NULL
, NULL
, NULL
, TYPE_KBDLAYOUT
, sizeof(KL
));
217 ERR("Failed to create object!\n");
222 pKl
->spkf
= UserLoadKbdFile(pustrKLID
);
224 /* Dereference keyboard layout */
225 UserDereferenceObject(pKl
);
227 /* If we failed, remove KL object */
230 ERR("UserLoadKbdFile(%wZ) failed!\n", pustrKLID
);
231 UserDeleteObject(pKl
->head
.h
, TYPE_KBDLAYOUT
);
235 // Up to Language Identifiers..
236 if (!NT_SUCCESS(RtlUnicodeStringToInteger(pustrKLID
, 16, (PULONG
)&lCid
)))
238 ERR("RtlUnicodeStringToInteger failed for '%wZ'\n", pustrKLID
);
239 UserDeleteObject(pKl
->head
.h
, TYPE_KBDLAYOUT
);
243 TRACE("Language Identifiers %wZ LCID 0x%x\n", pustrKLID
, lCid
);
244 if (co_IntGetCharsetInfo(lCid
, &cs
))
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
);
254 pKl
->iBaseCharset
= ANSI_CHARSET
;
255 pKl
->dwFontSigs
= FS_LATIN1
;
256 pKl
->CodePage
= CP_ACP
;
259 // Set initial system character set and font signature.
262 gSystemCPCharSet
= pKl
->iBaseCharset
;
263 gSystemFS
= pKl
->dwFontSigs
;
272 * Destroys specified Keyboard File object
276 UnloadKbdFile(_In_ PKBDFILE pkf
)
278 PKBDFILE
*ppkfLink
= &gpkfList
;
279 NT_ASSERT(pkf
!= NULL
);
281 /* Find previous object */
284 if (*ppkfLink
== pkf
)
287 ppkfLink
= &(*ppkfLink
)->pkfNext
;
290 if (*ppkfLink
== pkf
)
291 *ppkfLink
= pkf
->pkfNext
;
293 EngUnloadImage(pkf
->hBase
);
294 UserDeleteObject(pkf
->head
.h
, TYPE_KBDFILE
);
300 * Unloads specified Keyboard Layout if possible
303 UserUnloadKbl(PKL pKl
)
305 /* According to msdn, UnloadKeyboardLayout can fail
306 if the keyboard layout identifier was preloaded. */
307 if (pKl
== gspklBaseLayout
)
309 if (pKl
->pklNext
== pKl
->pklPrev
)
311 /* There is only one layout */
315 /* Set next layout as default */
316 gspklBaseLayout
= pKl
->pklNext
;
319 if (pKl
->head
.cLockObj
> 1)
321 /* Layout is used by other threads */
322 pKl
->dwKL_Flags
|= KLF_UNLOAD
;
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
);
335 * W32kGetDefaultKeyLayout
337 * Returns default layout for new threads
340 W32kGetDefaultKeyLayout(VOID
)
342 PKL pKl
= gspklBaseLayout
;
347 /* Return not unloaded layout */
350 if (!(pKl
->dwKL_Flags
& KLF_UNLOAD
))
353 pKl
= pKl
->pklPrev
; /* Confirmed on Win2k */
354 } while(pKl
!= gspklBaseLayout
);
356 /* We have not found proper KL */
363 * Gets KL object from hkl value
367 UserHklToKbl(HKL hKl
)
369 PKL pKl
= gspklBaseLayout
;
371 if (!gspklBaseLayout
)
380 } while (pKl
!= gspklBaseLayout
);
386 * UserSetDefaultInputLang
388 * Sets default kyboard layout for system. Called from UserSystemParametersInfo.
392 UserSetDefaultInputLang(HKL hKl
)
396 pKl
= UserHklToKbl(hKl
);
400 gspklBaseLayout
= pKl
;
407 * Activates given layout in specified thread
410 co_UserActivateKbl(PTHREADINFO pti
, PKL pKl
, UINT Flags
)
414 pklPrev
= pti
->KeyboardLayout
;
416 UserDereferenceObject(pklPrev
);
418 pti
->KeyboardLayout
= pKl
;
419 pti
->pClientInfo
->hKL
= pKl
->hkl
;
420 UserReferenceObject(pKl
);
422 if (Flags
& KLF_SETFORPROCESS
)
427 // Send WM_INPUTLANGCHANGE to thread's focus window
428 co_IntSendMessage(pti
->MessageQueue
->spwndFocus
? UserHMGetHandle(pti
->MessageQueue
->spwndFocus
) : 0,
430 (WPARAM
)pKl
->iBaseCharset
, // FIXME: How to set it?
431 (LPARAM
)pKl
->hkl
); // hkl
436 /* EXPORTS *******************************************************************/
439 * UserGetKeyboardLayout
441 * Returns hkl of given thread keyboard layout
444 UserGetKeyboardLayout(
455 pti
= PsGetCurrentThreadWin32Thread();
456 pKl
= pti
->KeyboardLayout
;
457 return pKl
? pKl
->hkl
: NULL
;
460 Status
= PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
)dwThreadId
, &pThread
);
461 if (!NT_SUCCESS(Status
))
463 EngSetLastError(ERROR_INVALID_PARAMETER
);
467 pti
= PsGetThreadWin32Thread(pThread
);
468 pKl
= pti
->KeyboardLayout
;
469 hKl
= pKl
? pKl
->hkl
: NULL
;
470 ObDereferenceObject(pThread
);
475 * NtUserGetKeyboardLayoutList
477 * Returns list of loaded keyboard layouts in system
481 NtUserGetKeyboardLayoutList(
493 if (!gspklBaseLayout
)
498 pKl
= gspklBaseLayout
;
506 } while (pKl
!= gspklBaseLayout
);
512 ProbeForWrite(pHklBuff
, nBuff
*sizeof(HKL
), 4);
516 pHklBuff
[uRet
] = pKl
->hkl
;
519 if (pKl
== gspklBaseLayout
)
523 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
525 SetLastNtError(_SEH2_GetExceptionCode());
536 * NtUserGetKeyboardLayoutName
538 * Returns KLID of current thread keyboard layout
542 NtUserGetKeyboardLayoutName(
551 pti
= PsGetCurrentThreadWin32Thread();
552 pKl
= pti
->KeyboardLayout
;
559 ProbeForWrite(pwszName
, KL_NAMELENGTH
*sizeof(WCHAR
), 1);
560 wcscpy(pwszName
, pKl
->spkf
->awchKF
);
563 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
565 SetLastNtError(_SEH2_GetExceptionCode());
575 * NtUserLoadKeyboardLayoutEx
577 * Loads keyboard layout with given locale id
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?
586 IN PUNICODE_STRING pustrKLID
,
591 PKL pKl
= NULL
, pklLast
;
593 UNICODE_STRING ustrSafeKLID
;
595 if (Flags
& ~(KLF_ACTIVATE
|KLF_NOTELLSHELL
|KLF_REORDER
|KLF_REPLACELANG
|
596 KLF_SUBSTITUTE_OK
|KLF_SETFORPROCESS
|KLF_UNLOADPREVIOUS
|
597 KLF_RESET
|KLF_SHIFTLOCK
))
599 ERR("Invalid flags: %x\n", Flags
);
600 EngSetLastError(ERROR_INVALID_FLAGS
);
604 /* FIXME: It seems KLF_RESET is only supported for WINLOGON */
606 RtlInitEmptyUnicodeString(&ustrSafeKLID
, Buffer
, sizeof(Buffer
));
609 ProbeForRead(pustrKLID
, sizeof(*pustrKLID
), 1);
610 ProbeForRead(pustrKLID
->Buffer
, sizeof(pustrKLID
->Length
), 1);
611 RtlCopyUnicodeString(&ustrSafeKLID
, pustrKLID
);
613 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
615 SetLastNtError(_SEH2_GetExceptionCode());
616 _SEH2_YIELD(return NULL
);
620 UserEnterExclusive();
622 /* If hklUnload is specified, unload it and load new layput as default */
623 if (hklUnload
&& hklUnload
!= (HKL
)hkl
)
625 pKl
= UserHklToKbl(hklUnload
);
630 /* Let's see if layout was already loaded. */
631 pKl
= UserHklToKbl((HKL
)hkl
);
634 /* It wasn't, so load it. */
635 pKl
= UserLoadKbdLayout(&ustrSafeKLID
, (HKL
)hkl
);
641 /* Find last not unloaded layout */
642 pklLast
= gspklBaseLayout
->pklPrev
;
643 while (pklLast
!= gspklBaseLayout
&& pklLast
->dwKL_Flags
& KLF_UNLOAD
)
644 pklLast
= pklLast
->pklPrev
;
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
;
654 /* This is the first layout */
657 gspklBaseLayout
= pKl
;
661 /* If this layout was prepared to unload, undo it */
662 pKl
->dwKL_Flags
&= ~KLF_UNLOAD
;
664 /* Activate this layout in current thread */
665 if (Flags
& KLF_ACTIVATE
)
666 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl
, Flags
);
668 /* Send shell message */
669 if (!(Flags
& KLF_NOTELLSHELL
))
670 co_IntShellHookNotify(HSHELL_LANGUAGE
, 0, (LPARAM
)hkl
);
672 /* Return hkl on success */
675 /* FIXME: KLF_REPLACELANG
684 * NtUserActivateKeyboardLayout
686 * Activates specified layout for thread or process
690 NtUserActivateKeyboardLayout(
698 UserEnterExclusive();
700 pti
= PsGetCurrentThreadWin32Thread();
702 /* hKl can have special value HKL_NEXT or HKL_PREV */
703 if (hKl
== (HKL
)HKL_NEXT
)
705 /* Get next keyboard layout starting with current */
706 if (pti
->KeyboardLayout
)
707 pKl
= pti
->KeyboardLayout
->pklNext
;
709 else if (hKl
== (HKL
)HKL_PREV
)
711 /* Get previous keyboard layout starting with current */
712 if (pti
->KeyboardLayout
)
713 pKl
= pti
->KeyboardLayout
->pklNext
;
716 pKl
= UserHklToKbl(hKl
);
720 ERR("Invalid HKL %p!\n", hKl
);
729 if (Flags
& KLF_REORDER
)
730 gspklBaseLayout
= pKl
;
732 if (pKl
!= pti
->KeyboardLayout
)
734 /* Activate layout for current thread */
735 pKl
= co_UserActivateKbl(pti
, pKl
, Flags
);
737 /* Send shell message */
738 if (!(Flags
& KLF_NOTELLSHELL
))
739 co_IntShellHookNotify(HSHELL_LANGUAGE
, 0, (LPARAM
)hkl
);
748 * NtUserUnloadKeyboardLayout
750 * Unloads keyboard layout with specified hkl value
754 NtUserUnloadKeyboardLayout(
760 UserEnterExclusive();
762 pKl
= UserHklToKbl(hKl
);
764 bRet
= UserUnloadKbl(pKl
);
766 ERR("Invalid HKL %p!\n", hKl
);