2 * PROJECT: ReactOS Win32k subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: subsystems/win32/win32k/ntuser/kbdlayout.c
5 * PURPOSE: Keyboard layout management
6 * COPYRIGHT: Copyright 2007 Saveliy Tretiakov
7 * Copyright 2008 Colin Finck
8 * Copyright 2011 Rafal Harabien
12 DBG_DEFAULT_CHANNEL(UserKbdLayout
);
14 PKL gspklBaseLayout
= NULL
;
15 PKBDFILE gpkfList
= NULL
;
17 typedef PVOID (*PFN_KBDLAYERDESCRIPTOR
)(VOID
);
20 /* PRIVATE FUNCTIONS ******************************************************/
25 * Loads keyboard layout DLL and gets address to KbdTables
28 UserLoadKbdDll(WCHAR
*pwszLayoutPath
,
30 PKBDTABLES
*pKbdTables
)
32 PFN_KBDLAYERDESCRIPTOR pfnKbdLayerDescriptor
;
34 /* Load keyboard layout DLL */
35 TRACE("Loading Keyboard DLL %ws\n", pwszLayoutPath
);
36 *phModule
= EngLoadImage(pwszLayoutPath
);
39 ERR("Failed to load dll %ws\n", pwszLayoutPath
);
43 /* Find KbdLayerDescriptor function and get layout tables */
44 TRACE("Loaded %ws\n", pwszLayoutPath
);
45 pfnKbdLayerDescriptor
= EngFindImageProcAddress(*phModule
, "KbdLayerDescriptor");
47 /* FIXME: Windows reads file instead of executing!
48 It's not safe to kbdlayout DLL in kernel mode! */
50 if (pfnKbdLayerDescriptor
)
51 *pKbdTables
= pfnKbdLayerDescriptor();
53 ERR("Error: %ws has no KbdLayerDescriptor()\n", pwszLayoutPath
);
55 if (!pfnKbdLayerDescriptor
|| !*pKbdTables
)
57 ERR("Failed to load the keyboard layout.\n");
58 EngUnloadImage(*phModule
);
62 #if 0 /* Dump keyboard layout */
65 PVK_TO_BIT pVkToBit
= (*pKbdTables
)->pCharModifiers
->pVkToBit
;
66 PVK_TO_WCHAR_TABLE pVkToWchTbl
= (*pKbdTables
)->pVkToWcharTable
;
67 PVSC_VK pVscVk
= (*pKbdTables
)->pVSCtoVK_E0
;
68 DbgPrint("Kbd layout: fLocaleFlags %x bMaxVSCtoVK %x\n", (*pKbdTables
)->fLocaleFlags
, (*pKbdTables
)->bMaxVSCtoVK
);
69 DbgPrint("wMaxModBits %x\n", (*pKbdTables
)->pCharModifiers
->wMaxModBits
);
72 DbgPrint("VkToBit %x -> %x\n", pVkToBit
->Vk
, pVkToBit
->ModBits
);
75 for (i
= 0; i
<= (*pKbdTables
)->pCharModifiers
->wMaxModBits
; ++i
)
76 DbgPrint("ModNumber %x -> %x\n", i
, (*pKbdTables
)->pCharModifiers
->ModNumber
[i
]);
77 while (pVkToWchTbl
->pVkToWchars
)
79 PVK_TO_WCHARS1 pVkToWch
= pVkToWchTbl
->pVkToWchars
;
80 DbgPrint("pVkToWchTbl nModifications %x cbSize %x\n", pVkToWchTbl
->nModifications
, pVkToWchTbl
->cbSize
);
81 while (pVkToWch
->VirtualKey
)
83 DbgPrint("pVkToWch VirtualKey %x Attributes %x wc { ", pVkToWch
->VirtualKey
, pVkToWch
->Attributes
);
84 for (i
= 0; i
< pVkToWchTbl
->nModifications
; ++i
)
85 DbgPrint("%x ", pVkToWch
->wch
[i
]);
87 pVkToWch
= (PVK_TO_WCHARS1
)(((PBYTE
)pVkToWch
) + pVkToWchTbl
->cbSize
);
91 DbgPrint("pusVSCtoVK: { ");
92 for (i
= 0; i
< (*pKbdTables
)->bMaxVSCtoVK
; ++i
)
93 DbgPrint("%x -> %x, ", i
, (*pKbdTables
)->pusVSCtoVK
[i
]);
95 DbgPrint("pVSCtoVK_E0: { ");
98 DbgPrint("%x -> %x, ", pVscVk
->Vsc
, pVscVk
->Vk
);
102 pVscVk
= (*pKbdTables
)->pVSCtoVK_E1
;
103 DbgPrint("pVSCtoVK_E1: { ");
106 DbgPrint("%x -> %x, ", pVscVk
->Vsc
, pVscVk
->Vk
);
120 * Loads keyboard layout DLL and creates KBDFILE object
123 UserLoadKbdFile(PUNICODE_STRING pwszKLID
)
125 PKBDFILE pkf
, pRet
= NULL
;
129 WCHAR wszLayoutPath
[MAX_PATH
] = L
"\\SystemRoot\\System32\\";
130 WCHAR wszLayoutRegKey
[256] = L
"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\"
131 L
"Control\\Keyboard Layouts\\";
133 /* Create keyboard layout file object */
134 pkf
= UserCreateObject(gHandleTable
, NULL
, NULL
, otKBDfile
, sizeof(KBDFILE
));
137 ERR("Failed to create object!\n");
141 /* Set keyboard layout name */
142 swprintf(pkf
->awchKF
, L
"%wZ", pwszKLID
);
144 /* Open layout registry key */
145 RtlStringCbCatW(wszLayoutRegKey
, sizeof(wszLayoutRegKey
), pkf
->awchKF
);
146 Status
= RegOpenKey(wszLayoutRegKey
, &hKey
);
147 if (!NT_SUCCESS(Status
))
149 ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey
, Status
);
153 /* Read filename of layout DLL */
154 cbSize
= sizeof(wszLayoutPath
) - wcslen(wszLayoutPath
)*sizeof(WCHAR
);
155 Status
= RegQueryValue(hKey
,
158 wszLayoutPath
+ wcslen(wszLayoutPath
),
161 if (!NT_SUCCESS(Status
))
163 ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID
, Status
);
167 /* Load keyboard file now */
168 if (!UserLoadKbdDll(wszLayoutPath
, &pkf
->hBase
, &pkf
->pKbdTbl
))
170 ERR("Failed to load %ws dll!\n", wszLayoutPath
);
174 /* Update next field */
175 pkf
->pkfNext
= gpkfList
;
178 /* Return keyboard file */
185 UserDereferenceObject(pkf
); // we dont need ptr anymore
188 /* We have failed - destroy created object */
190 UserDeleteObject(pkf
->head
.h
, otKBDfile
);
199 * Loads keyboard layout and creates KL object
202 UserLoadKbdLayout(PUNICODE_STRING pwszKLID
, HKL hKL
)
206 /* Create keyboard layout object */
207 pKl
= UserCreateObject(gHandleTable
, NULL
, NULL
, otKBDlayout
, sizeof(KL
));
210 ERR("Failed to create object!\n");
215 pKl
->spkf
= UserLoadKbdFile(pwszKLID
);
217 /* Dereference keyboard layout */
218 UserDereferenceObject(pKl
);
220 /* If we failed, remove KL object */
223 ERR("UserLoadKbdFile(%wZ) failed!\n", pwszKLID
);
224 UserDeleteObject(pKl
->head
.h
, otKBDlayout
);
234 * Destroys specified Keyboard File object
238 UnloadKbdFile(PKBDFILE pkf
)
240 PKBDFILE
*ppkfLink
= &gpkfList
;
242 /* Find previous object */
245 if (*ppkfLink
== pkf
)
248 ppkfLink
= &(*ppkfLink
)->pkfNext
;
251 if (*ppkfLink
== pkf
)
252 *ppkfLink
= pkf
->pkfNext
;
254 EngUnloadImage(pkf
->hBase
);
255 UserDeleteObject(pkf
->head
.h
, otKBDfile
);
261 * Unloads specified Keyboard Layout if possible
264 UserUnloadKbl(PKL pKl
)
266 /* According to msdn, UnloadKeyboardLayout can fail
267 if the keyboard layout identifier was preloaded. */
268 if (pKl
== gspklBaseLayout
)
270 if (pKl
->pklNext
== pKl
->pklPrev
)
272 /* There is only one layout */
276 /* Set next layout as default */
277 gspklBaseLayout
= pKl
->pklNext
;
280 if (pKl
->head
.cLockObj
> 1)
282 /* Layout is used by other threads */
283 pKl
->dwKL_Flags
|= KLF_UNLOAD
;
287 /* Unload the layout */
288 pKl
->pklPrev
->pklNext
= pKl
->pklNext
;
289 pKl
->pklNext
->pklPrev
= pKl
->pklPrev
;
290 UnloadKbdFile(pKl
->spkf
);
291 UserDeleteObject(pKl
->head
.h
, otKBDlayout
);
296 * W32kGetDefaultKeyLayout
298 * Returns default layout for new threads
301 W32kGetDefaultKeyLayout(VOID
)
303 PKL pKl
= gspklBaseLayout
;
308 /* Return not unloaded layout */
311 if (!(pKl
->dwKL_Flags
& KLF_UNLOAD
))
314 pKl
= pKl
->pklPrev
; /* Confirmed on Win2k */
315 } while(pKl
!= gspklBaseLayout
);
317 /* We have not found proper KL */
324 * Gets KL object from hkl value
328 UserHklToKbl(HKL hKl
)
330 PKL pKl
= gspklBaseLayout
;
332 if (!gspklBaseLayout
)
341 } while (pKl
!= gspklBaseLayout
);
347 * UserSetDefaultInputLang
349 * Sets default kyboard layout for system. Called from UserSystemParametersInfo.
353 UserSetDefaultInputLang(HKL hKl
)
357 pKl
= UserHklToKbl(hKl
);
361 gspklBaseLayout
= pKl
;
368 * Activates given layout in specified thread
371 co_UserActivateKbl(PTHREADINFO pti
, PKL pKl
, UINT Flags
)
375 pklPrev
= pti
->KeyboardLayout
;
377 UserDereferenceObject(pklPrev
);
379 pti
->KeyboardLayout
= pKl
;
380 pti
->pClientInfo
->hKL
= pKl
->hkl
;
381 UserReferenceObject(pKl
);
383 if (Flags
& KLF_SETFORPROCESS
)
388 // Send WM_INPUTLANGCHANGE to thread's focus window
389 co_IntSendMessage(pti
->MessageQueue
->FocusWindow
,
391 (WPARAM
)pKl
->iBaseCharset
, // FIXME: How to set it?
392 (LPARAM
)pKl
->hkl
); // hkl
397 /* EXPORTS *******************************************************************/
400 * UserGetKeyboardLayout
402 * Returns hkl of given thread keyboard layout
405 UserGetKeyboardLayout(
416 pti
= PsGetCurrentThreadWin32Thread();
417 pKl
= pti
->KeyboardLayout
;
418 return pKl
? pKl
->hkl
: NULL
;
421 Status
= PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
)dwThreadId
, &pThread
);
422 if (!NT_SUCCESS(Status
))
424 EngSetLastError(ERROR_INVALID_PARAMETER
);
428 pti
= PsGetThreadWin32Thread(pThread
);
429 pKl
= pti
->KeyboardLayout
;
430 hKl
= pKl
? pKl
->hkl
: NULL
;;
431 ObDereferenceObject(pThread
);
436 * NtUserGetKeyboardLayoutList
438 * Returns list of loaded keyboard layouts in system
442 NtUserGetKeyboardLayoutList(
454 if (!gspklBaseLayout
)
456 pKl
= gspklBaseLayout
;
464 } while (pKl
!= gspklBaseLayout
);
470 ProbeForWrite(pHklBuff
, nBuff
*sizeof(HKL
), 4);
474 pHklBuff
[uRet
] = pKl
->hkl
;
477 if (pKl
== gspklBaseLayout
)
481 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
483 SetLastNtError(_SEH2_GetExceptionCode());
494 * NtUserGetKeyboardLayoutName
496 * Returns KLID of current thread keyboard layout
500 NtUserGetKeyboardLayoutName(
509 pti
= PsGetCurrentThreadWin32Thread();
510 pKl
= pti
->KeyboardLayout
;
517 ProbeForWrite(pwszName
, KL_NAMELENGTH
*sizeof(WCHAR
), 1);
518 wcscpy(pwszName
, pKl
->spkf
->awchKF
);
521 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
523 SetLastNtError(_SEH2_GetExceptionCode());
533 * NtUserLoadKeyboardLayoutEx
535 * Loads keyboard layout with given locale id
539 NtUserLoadKeyboardLayoutEx(
540 IN HANDLE Handle
, // hFile (See downloads.securityfocus.com/vulnerabilities/exploits/43774.c)
541 IN DWORD offTable
, // Offset to KbdTables
542 IN PUNICODE_STRING puszKeyboardName
, // Not used?
544 IN PUNICODE_STRING pustrKLID
,
549 PKL pKl
= NULL
, pklLast
;
551 UNICODE_STRING ustrSafeKLID
;
553 if (Flags
& ~(KLF_ACTIVATE
|KLF_NOTELLSHELL
|KLF_REORDER
|KLF_REPLACELANG
|
554 KLF_SUBSTITUTE_OK
|KLF_SETFORPROCESS
|KLF_UNLOADPREVIOUS
|
555 KLF_RESET
|KLF_SETFORPROCESS
|KLF_SHIFTLOCK
))
557 ERR("Invalid flags: %x\n", Flags
);
558 EngSetLastError(ERROR_INVALID_FLAGS
);
562 /* FIXME: It seems KLF_RESET is only supported for WINLOGON */
564 RtlInitEmptyUnicodeString(&ustrSafeKLID
, Buffer
, sizeof(Buffer
));
567 ProbeForRead(pustrKLID
, sizeof(*pustrKLID
), 1);
568 ProbeForRead(pustrKLID
->Buffer
, sizeof(pustrKLID
->Length
), 1);
569 RtlCopyUnicodeString(&ustrSafeKLID
, pustrKLID
);
571 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
573 SetLastNtError(_SEH2_GetExceptionCode());
574 _SEH2_YIELD(return NULL
);
578 UserEnterExclusive();
580 /* If hklUnload is specified, unload it and load new layput as default */
581 if (hklUnload
&& hklUnload
!= (HKL
)hkl
)
583 pKl
= UserHklToKbl(hklUnload
);
588 /* Let's see if layout was already loaded. */
589 pKl
= UserHklToKbl((HKL
)hkl
);
592 /* It wasn't, so load it. */
593 pKl
= UserLoadKbdLayout(&ustrSafeKLID
, (HKL
)hkl
);
599 /* Find last not unloaded layout */
600 pklLast
= gspklBaseLayout
->pklPrev
;
601 while (pklLast
!= gspklBaseLayout
&& pklLast
->dwKL_Flags
& KLF_UNLOAD
)
602 pklLast
= pklLast
->pklPrev
;
604 /* Add new layout to the list */
605 pKl
->pklNext
= pklLast
->pklNext
;
606 pKl
->pklPrev
= pklLast
;
607 pKl
->pklNext
->pklPrev
= pKl
;
608 pKl
->pklPrev
->pklNext
= pKl
;
612 /* This is the first layout */
615 gspklBaseLayout
= pKl
;
619 /* If this layout was prepared to unload, undo it */
620 pKl
->dwKL_Flags
&= ~KLF_UNLOAD
;
622 /* Activate this layout in current thread */
623 if (Flags
& KLF_ACTIVATE
)
624 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKl
, Flags
);
626 /* Send shell message */
627 if (!(Flags
& KLF_NOTELLSHELL
))
628 co_IntShellHookNotify(HSHELL_LANGUAGE
, (LPARAM
)hkl
);
630 /* Return hkl on success */
633 /* FIXME: KLF_REPLACELANG
642 * NtUserActivateKeyboardLayout
644 * Activates specified layout for thread or process
648 NtUserActivateKeyboardLayout(
656 UserEnterExclusive();
658 pti
= PsGetCurrentThreadWin32Thread();
660 /* hKl can have special value HKL_NEXT or HKL_PREV */
661 if (hKl
== (HKL
)HKL_NEXT
)
663 /* Get next keyboard layout starting with current */
664 if (pti
->KeyboardLayout
)
665 pKl
= pti
->KeyboardLayout
->pklNext
;
667 else if (hKl
== (HKL
)HKL_PREV
)
669 /* Get previous keyboard layout starting with current */
670 if (pti
->KeyboardLayout
)
671 pKl
= pti
->KeyboardLayout
->pklNext
;
674 pKl
= UserHklToKbl(hKl
);
678 ERR("Invalid HKL %x!\n", hKl
);
687 if (Flags
& KLF_REORDER
)
688 gspklBaseLayout
= pKl
;
690 if (pKl
!= pti
->KeyboardLayout
)
692 /* Activate layout for current thread */
693 pKl
= co_UserActivateKbl(pti
, pKl
, Flags
);
695 /* Send shell message */
696 if (!(Flags
& KLF_NOTELLSHELL
))
697 co_IntShellHookNotify(HSHELL_LANGUAGE
, (LPARAM
)hkl
);
706 * NtUserUnloadKeyboardLayout
708 * Unloads keyboard layout with specified hkl value
712 NtUserUnloadKeyboardLayout(
718 UserEnterExclusive();
720 pKl
= UserHklToKbl(hKl
);
722 bRet
= UserUnloadKbl(pKl
);
724 ERR("Invalid HKL %x!\n", hKl
);