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
13 // Was included only because of CP_ACP and required the
14 // definition of SYSTEMTIME in ndk\rtltypes.h
18 DBG_DEFAULT_CHANNEL(UserKbdLayout
);
20 PKL gspklBaseLayout
= NULL
;
21 PKBDFILE gpkfList
= NULL
;
23 UINT gSystemCPCharSet
= 0;
25 typedef PVOID (*PFN_KBDLAYERDESCRIPTOR
)(VOID
);
28 /* PRIVATE FUNCTIONS ******************************************************/
33 * Loads keyboard layout DLL and gets address to KbdTables
36 UserLoadKbdDll(WCHAR
*pwszLayoutPath
,
38 PKBDTABLES
*pKbdTables
)
40 PFN_KBDLAYERDESCRIPTOR pfnKbdLayerDescriptor
;
42 /* Load keyboard layout DLL */
43 TRACE("Loading Keyboard DLL %ws\n", pwszLayoutPath
);
44 *phModule
= EngLoadImage(pwszLayoutPath
);
47 ERR("Failed to load dll %ws\n", pwszLayoutPath
);
51 /* Find KbdLayerDescriptor function and get layout tables */
52 TRACE("Loaded %ws\n", pwszLayoutPath
);
53 pfnKbdLayerDescriptor
= EngFindImageProcAddress(*phModule
, "KbdLayerDescriptor");
55 /* FIXME: Windows reads file instead of executing!
56 It's not safe to kbdlayout DLL in kernel mode! */
58 if (pfnKbdLayerDescriptor
)
59 *pKbdTables
= pfnKbdLayerDescriptor();
61 ERR("Error: %ws has no KbdLayerDescriptor()\n", pwszLayoutPath
);
63 if (!pfnKbdLayerDescriptor
|| !*pKbdTables
)
65 ERR("Failed to load the keyboard layout.\n");
66 EngUnloadImage(*phModule
);
70 #if 0 /* Dump keyboard layout */
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
);
80 DbgPrint("VkToBit %x -> %x\n", pVkToBit
->Vk
, pVkToBit
->ModBits
);
83 for (i
= 0; i
<= (*pKbdTables
)->pCharModifiers
->wMaxModBits
; ++i
)
84 DbgPrint("ModNumber %x -> %x\n", i
, (*pKbdTables
)->pCharModifiers
->ModNumber
[i
]);
85 while (pVkToWchTbl
->pVkToWchars
)
87 PVK_TO_WCHARS1 pVkToWch
= pVkToWchTbl
->pVkToWchars
;
88 DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n", pVkToWchTbl
->nModifications
, pVkToWchTbl
->cbSize
);
89 while (pVkToWch
->VirtualKey
)
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
]);
95 pVkToWch
= (PVK_TO_WCHARS1
)(((PBYTE
)pVkToWch
) + pVkToWchTbl
->cbSize
);
99 DbgPrint("pusVSCtoVK: { ");
100 for (i
= 0; i
< (*pKbdTables
)->bMaxVSCtoVK
; ++i
)
101 DbgPrint("%x -> %x, ", i
, (*pKbdTables
)->pusVSCtoVK
[i
]);
103 DbgPrint("pVSCtoVK_E0: { ");
106 DbgPrint("%x -> %x, ", pVscVk
->Vsc
, pVscVk
->Vk
);
110 pVscVk
= (*pKbdTables
)->pVSCtoVK_E1
;
111 DbgPrint("pVSCtoVK_E1: { ");
114 DbgPrint("%x -> %x, ", pVscVk
->Vsc
, pVscVk
->Vk
);
128 * Loads keyboard layout DLL and creates KBDFILE object
131 UserLoadKbdFile(PUNICODE_STRING pwszKLID
)
133 PKBDFILE pkf
, pRet
= NULL
;
137 WCHAR wszLayoutPath
[MAX_PATH
] = L
"\\SystemRoot\\System32\\";
138 WCHAR wszLayoutRegKey
[256] = L
"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\"
139 L
"Control\\Keyboard Layouts\\";
141 /* Create keyboard layout file object */
142 pkf
= UserCreateObject(gHandleTable
, NULL
, NULL
, NULL
, TYPE_KBDFILE
, sizeof(KBDFILE
));
145 ERR("Failed to create object!\n");
149 /* Set keyboard layout name */
150 swprintf(pkf
->awchKF
, L
"%wZ", pwszKLID
);
152 /* Open layout registry key */
153 RtlStringCbCatW(wszLayoutRegKey
, sizeof(wszLayoutRegKey
), pkf
->awchKF
);
154 Status
= RegOpenKey(wszLayoutRegKey
, &hKey
);
155 if (!NT_SUCCESS(Status
))
157 ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey
, Status
);
161 /* Read filename of layout DLL */
162 cbSize
= (ULONG
)(sizeof(wszLayoutPath
) - wcslen(wszLayoutPath
)*sizeof(WCHAR
));
163 Status
= RegQueryValue(hKey
,
166 wszLayoutPath
+ wcslen(wszLayoutPath
),
169 if (!NT_SUCCESS(Status
))
171 ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID
, Status
);
175 /* Load keyboard file now */
176 if (!UserLoadKbdDll(wszLayoutPath
, &pkf
->hBase
, &pkf
->pKbdTbl
))
178 ERR("Failed to load %ws dll!\n", wszLayoutPath
);
182 /* Update next field */
183 pkf
->pkfNext
= gpkfList
;
186 /* Return keyboard file */
193 UserDereferenceObject(pkf
); // we dont need ptr anymore
196 /* We have failed - destroy created object */
198 UserDeleteObject(pkf
->head
.h
, TYPE_KBDFILE
);
207 * Loads keyboard layout and creates KL object
210 UserLoadKbdLayout(PUNICODE_STRING pustrKLID
, HKL hKL
)
216 /* Create keyboard layout object */
217 pKl
= UserCreateObject(gHandleTable
, NULL
, NULL
, NULL
, TYPE_KBDLAYOUT
, sizeof(KL
));
220 ERR("Failed to create object!\n");
225 pKl
->spkf
= UserLoadKbdFile(pustrKLID
);
227 /* Dereference keyboard layout */
228 UserDereferenceObject(pKl
);
230 /* If we failed, remove KL object */
233 ERR("UserLoadKbdFile(%wZ) failed!\n", pustrKLID
);
234 UserDeleteObject(pKl
->head
.h
, TYPE_KBDLAYOUT
);
238 // Up to Language Identifiers..
239 if (!NT_SUCCESS(RtlUnicodeStringToInteger(pustrKLID
, 16, (PULONG
)&lCid
)))
241 ERR("RtlUnicodeStringToInteger failed for '%wZ'\n", pustrKLID
);
242 UserDeleteObject(pKl
->head
.h
, TYPE_KBDLAYOUT
);
246 TRACE("Language Identifiers %wZ LCID 0x%x\n", pustrKLID
, lCid
);
247 if (co_IntGetCharsetInfo(lCid
, &cs
))
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
);
257 pKl
->iBaseCharset
= ANSI_CHARSET
;
258 pKl
->dwFontSigs
= FS_LATIN1
;
259 pKl
->CodePage
= CP_ACP
;
262 // Set initial system character set and font signature.
265 gSystemCPCharSet
= pKl
->iBaseCharset
;
266 gSystemFS
= pKl
->dwFontSigs
;
275 * Destroys specified Keyboard File object
279 UnloadKbdFile(_In_ PKBDFILE pkf
)
281 PKBDFILE
*ppkfLink
= &gpkfList
;
282 NT_ASSERT(pkf
!= NULL
);
284 /* Find previous object */
287 if (*ppkfLink
== pkf
)
290 ppkfLink
= &(*ppkfLink
)->pkfNext
;
293 if (*ppkfLink
== pkf
)
294 *ppkfLink
= pkf
->pkfNext
;
296 EngUnloadImage(pkf
->hBase
);
297 UserDeleteObject(pkf
->head
.h
, TYPE_KBDFILE
);
303 * Unloads specified Keyboard Layout if possible
306 UserUnloadKbl(PKL pKl
)
308 /* According to msdn, UnloadKeyboardLayout can fail
309 if the keyboard layout identifier was preloaded. */
310 if (pKl
== gspklBaseLayout
)
312 if (pKl
->pklNext
== pKl
->pklPrev
)
314 /* There is only one layout */
318 /* Set next layout as default */
319 gspklBaseLayout
= pKl
->pklNext
;
322 if (pKl
->head
.cLockObj
> 1)
324 /* Layout is used by other threads */
325 pKl
->dwKL_Flags
|= KLF_UNLOAD
;
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
);
338 * W32kGetDefaultKeyLayout
340 * Returns default layout for new threads
343 W32kGetDefaultKeyLayout(VOID
)
345 PKL pKl
= gspklBaseLayout
;
350 /* Return not unloaded layout */
353 if (!(pKl
->dwKL_Flags
& KLF_UNLOAD
))
356 pKl
= pKl
->pklPrev
; /* Confirmed on Win2k */
357 } while(pKl
!= gspklBaseLayout
);
359 /* We have not found proper KL */
366 * Gets KL object from hkl value
370 UserHklToKbl(HKL hKl
)
372 PKL pKl
= gspklBaseLayout
;
374 if (!gspklBaseLayout
)
383 } while (pKl
!= gspklBaseLayout
);
389 * UserSetDefaultInputLang
391 * Sets default kyboard layout for system. Called from UserSystemParametersInfo.
395 UserSetDefaultInputLang(HKL hKl
)
399 pKl
= UserHklToKbl(hKl
);
403 gspklBaseLayout
= pKl
;
410 * Activates given layout in specified thread
413 co_UserActivateKbl(PTHREADINFO pti
, PKL pKl
, UINT Flags
)
418 pklPrev
= pti
->KeyboardLayout
;
420 UserDereferenceObject(pklPrev
);
422 pti
->KeyboardLayout
= pKl
;
423 pti
->pClientInfo
->hKL
= pKl
->hkl
;
424 UserReferenceObject(pKl
);
426 if (Flags
& KLF_SETFORPROCESS
)
431 if (!(pWnd
= pti
->MessageQueue
->spwndFocus
))
433 pWnd
= pti
->MessageQueue
->spwndActive
;
436 // Send WM_INPUTLANGCHANGE to thread's focus window
437 co_IntSendMessage( pWnd
? UserHMGetHandle(pWnd
) : 0,
439 (WPARAM
)pKl
->iBaseCharset
, // FIXME: How to set it?
440 (LPARAM
)pKl
->hkl
); // hkl
445 /* EXPORTS *******************************************************************/
448 * UserGetKeyboardLayout
450 * Returns hkl of given thread keyboard layout
453 UserGetKeyboardLayout(
457 PLIST_ENTRY ListEntry
;
460 pti
= PsGetCurrentThreadWin32Thread();
464 pKl
= pti
->KeyboardLayout
;
465 return pKl
? pKl
->hkl
: NULL
;
468 ListEntry
= pti
->rpdesk
->PtiList
.Flink
;
471 // Search the Desktop Thread list for related Desktop active Threads.
473 while(ListEntry
!= &pti
->rpdesk
->PtiList
)
475 pti
= CONTAINING_RECORD(ListEntry
, THREADINFO
, PtiLink
);
477 if (PsGetThreadId(pti
->pEThread
) == UlongToHandle(dwThreadId
))
479 pKl
= pti
->KeyboardLayout
;
480 return pKl
? pKl
->hkl
: NULL
;
483 ListEntry
= ListEntry
->Flink
;
490 * NtUserGetKeyboardLayoutList
492 * Returns list of loaded keyboard layouts in system
496 NtUserGetKeyboardLayoutList(
508 if (!gspklBaseLayout
)
513 pKl
= gspklBaseLayout
;
521 } while (pKl
!= gspklBaseLayout
);
527 ProbeForWrite(pHklBuff
, nBuff
*sizeof(HKL
), 4);
531 pHklBuff
[uRet
] = pKl
->hkl
;
534 if (pKl
== gspklBaseLayout
)
538 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
540 SetLastNtError(_SEH2_GetExceptionCode());
551 * NtUserGetKeyboardLayoutName
553 * Returns KLID of current thread keyboard layout
557 NtUserGetKeyboardLayoutName(
566 pti
= PsGetCurrentThreadWin32Thread();
567 pKl
= pti
->KeyboardLayout
;
574 ProbeForWrite(pwszName
, KL_NAMELENGTH
*sizeof(WCHAR
), 1);
575 wcscpy(pwszName
, pKl
->spkf
->awchKF
);
578 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
580 SetLastNtError(_SEH2_GetExceptionCode());
590 * NtUserLoadKeyboardLayoutEx
592 * Loads keyboard layout with given locale id
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?
601 IN PUNICODE_STRING pustrKLID
,
606 PKL pKl
= NULL
, pklLast
;
608 UNICODE_STRING ustrSafeKLID
;
610 if (Flags
& ~(KLF_ACTIVATE
|KLF_NOTELLSHELL
|KLF_REORDER
|KLF_REPLACELANG
|
611 KLF_SUBSTITUTE_OK
|KLF_SETFORPROCESS
|KLF_UNLOADPREVIOUS
|
612 KLF_RESET
|KLF_SHIFTLOCK
))
614 ERR("Invalid flags: %x\n", Flags
);
615 EngSetLastError(ERROR_INVALID_FLAGS
);
619 /* FIXME: It seems KLF_RESET is only supported for WINLOGON */
621 RtlInitEmptyUnicodeString(&ustrSafeKLID
, Buffer
, sizeof(Buffer
));
624 ProbeForRead(pustrKLID
, sizeof(*pustrKLID
), 1);
625 ProbeForRead(pustrKLID
->Buffer
, sizeof(pustrKLID
->Length
), 1);
626 RtlCopyUnicodeString(&ustrSafeKLID
, pustrKLID
);
628 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
630 SetLastNtError(_SEH2_GetExceptionCode());
631 _SEH2_YIELD(return NULL
);
635 UserEnterExclusive();
637 /* If hklUnload is specified, unload it and load new layput as default */
638 if (hklUnload
&& (hklUnload
!= UlongToHandle(hkl
)))
640 pKl
= UserHklToKbl(hklUnload
);
645 /* Let's see if layout was already loaded. */
646 pKl
= UserHklToKbl(UlongToHandle(hkl
));
649 /* It wasn't, so load it. */
650 pKl
= UserLoadKbdLayout(&ustrSafeKLID
, UlongToHandle(hkl
));
656 /* Find last not unloaded layout */
657 pklLast
= gspklBaseLayout
->pklPrev
;
658 while (pklLast
!= gspklBaseLayout
&& pklLast
->dwKL_Flags
& KLF_UNLOAD
)
659 pklLast
= pklLast
->pklPrev
;
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
;
669 /* This is the first layout */
672 gspklBaseLayout
= pKl
;
676 /* If this layout was prepared to unload, undo it */
677 pKl
->dwKL_Flags
&= ~KLF_UNLOAD
;
679 /* Activate this layout in current thread */
680 if (Flags
& KLF_ACTIVATE
)
681 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl
, Flags
);
683 /* Send shell message */
684 if (!(Flags
& KLF_NOTELLSHELL
))
685 co_IntShellHookNotify(HSHELL_LANGUAGE
, 0, (LPARAM
)hkl
);
687 /* Return hkl on success */
688 hklRet
= UlongToHandle(hkl
);
690 /* FIXME: KLF_REPLACELANG
699 * NtUserActivateKeyboardLayout
701 * Activates specified layout for thread or process
705 NtUserActivateKeyboardLayout(
713 UserEnterExclusive();
715 pti
= PsGetCurrentThreadWin32Thread();
717 /* hKl can have special value HKL_NEXT or HKL_PREV */
718 if (hKl
== (HKL
)HKL_NEXT
)
720 /* Get next keyboard layout starting with current */
721 if (pti
->KeyboardLayout
)
722 pKl
= pti
->KeyboardLayout
->pklNext
;
724 else if (hKl
== (HKL
)HKL_PREV
)
726 /* Get previous keyboard layout starting with current */
727 if (pti
->KeyboardLayout
)
728 pKl
= pti
->KeyboardLayout
->pklPrev
;
731 pKl
= UserHklToKbl(hKl
);
735 ERR("Invalid HKL %p!\n", hKl
);
744 if (Flags
& KLF_REORDER
)
745 gspklBaseLayout
= pKl
;
747 if (pKl
!= pti
->KeyboardLayout
)
749 /* Activate layout for current thread */
750 pKl
= co_UserActivateKbl(pti
, pKl
, Flags
);
752 /* Send shell message */
753 if (!(Flags
& KLF_NOTELLSHELL
))
754 co_IntShellHookNotify(HSHELL_LANGUAGE
, 0, (LPARAM
)hkl
);
763 * NtUserUnloadKeyboardLayout
765 * Unloads keyboard layout with specified hkl value
769 NtUserUnloadKeyboardLayout(
775 UserEnterExclusive();
777 pKl
= UserHklToKbl(hKl
);
779 bRet
= UserUnloadKbl(pKl
);
781 ERR("Invalid HKL %p!\n", hKl
);