3 * PROJECT: ReactOS Kernel
4 * LICENSE: GPL - See COPYING in the top level directory
5 * FILE: subsystems/win32/win32k/ntuser/kbdlayout.c
6 * PURPOSE: Keyboard layout management
7 * COPYRIGHT: Copyright 2007 Saveliy Tretiakov
8 * Copyright 2008 Colin Finck
13 /* INCLUDES ******************************************************************/
20 PKBL KBLList
= NULL
; // Keyboard layout list.
22 typedef PVOID (*KbdLayerDescriptor
)(VOID
);
23 NTSTATUS APIENTRY
LdrGetProcedureAddress(PVOID module
,
24 PANSI_STRING import_name
,
30 /* PRIVATE FUNCTIONS ******************************************************/
34 * Utility function to read a value from the registry more easily.
36 * IN PUNICODE_STRING KeyName -> Name of key to open
37 * IN PUNICODE_STRING ValueName -> Name of value to open
38 * OUT PUNICODE_STRING ReturnedValue -> String contained in registry
43 static NTSTATUS APIENTRY
ReadRegistryValue( PUNICODE_STRING KeyName
,
44 PUNICODE_STRING ValueName
,
45 PUNICODE_STRING ReturnedValue
)
49 OBJECT_ATTRIBUTES KeyAttributes
;
50 PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo
;
55 InitializeObjectAttributes(&KeyAttributes
, KeyName
, OBJ_CASE_INSENSITIVE
,
57 Status
= ZwOpenKey(&KeyHandle
, KEY_READ
, &KeyAttributes
);
58 if( !NT_SUCCESS(Status
) )
63 Status
= ZwQueryValueKey(KeyHandle
, ValueName
, KeyValuePartialInformation
,
68 if( Status
!= STATUS_BUFFER_TOO_SMALL
)
74 ResLength
+= sizeof( *KeyValuePartialInfo
);
76 ExAllocatePoolWithTag(PagedPool
, ResLength
, TAG_STRING
);
79 if( !KeyValuePartialInfo
)
82 return STATUS_NO_MEMORY
;
85 Status
= ZwQueryValueKey(KeyHandle
,
87 KeyValuePartialInformation
,
88 (PVOID
)KeyValuePartialInfo
,
92 if( !NT_SUCCESS(Status
) )
95 ExFreePoolWithTag(KeyValuePartialInfo
, TAG_STRING
);
99 /* At this point, KeyValuePartialInfo->Data contains the key data */
100 ReturnBuffer
= ExAllocatePoolWithTag(PagedPool
,
101 KeyValuePartialInfo
->DataLength
,
107 ExFreePoolWithTag(KeyValuePartialInfo
, TAG_STRING
);
108 return STATUS_NO_MEMORY
;
111 RtlCopyMemory(ReturnBuffer
,
112 KeyValuePartialInfo
->Data
,
113 KeyValuePartialInfo
->DataLength
);
114 RtlInitUnicodeString(ReturnedValue
, ReturnBuffer
);
116 ExFreePoolWithTag(KeyValuePartialInfo
, TAG_STRING
);
122 static BOOL
UserLoadKbdDll(WCHAR
*wsKLID
,
124 PKBDTABLES
*pKbdTables
)
127 KbdLayerDescriptor layerDescGetFn
;
128 ANSI_STRING kbdProcedureName
;
129 UNICODE_STRING LayoutKeyName
;
130 UNICODE_STRING LayoutValueName
;
131 UNICODE_STRING LayoutFile
;
132 UNICODE_STRING FullLayoutPath
;
134 WCHAR LayoutPathBuffer
[MAX_PATH
] = L
"\\SystemRoot\\System32\\";
135 WCHAR KeyNameBuffer
[MAX_PATH
] = L
"\\REGISTRY\\Machine\\SYSTEM\\"
136 L
"CurrentControlSet\\Control\\Keyboard Layouts\\";
138 RtlInitUnicodeString(&klid
, wsKLID
);
139 RtlInitUnicodeString(&LayoutValueName
,L
"Layout File");
140 RtlInitUnicodeString(&LayoutKeyName
, KeyNameBuffer
);
141 LayoutKeyName
.MaximumLength
= sizeof(KeyNameBuffer
);
143 RtlAppendUnicodeStringToString(&LayoutKeyName
, &klid
);
144 Status
= ReadRegistryValue(&LayoutKeyName
, &LayoutValueName
, &LayoutFile
);
146 if(!NT_SUCCESS(Status
))
148 DPRINT("Can't get layout filename for %wZ. (%08lx)\n", klid
, Status
);
152 DPRINT("Read registry and got %wZ\n", &LayoutFile
);
153 RtlInitUnicodeString(&FullLayoutPath
, LayoutPathBuffer
);
154 FullLayoutPath
.MaximumLength
= sizeof(LayoutPathBuffer
);
155 RtlAppendUnicodeStringToString(&FullLayoutPath
, &LayoutFile
);
156 DPRINT("Loading Keyboard DLL %wZ\n", &FullLayoutPath
);
157 ExFreePoolWithTag(LayoutFile
.Buffer
, TAG_STRING
);
159 *phModule
= EngLoadImage(FullLayoutPath
.Buffer
);
163 DPRINT("Loaded %wZ\n", &FullLayoutPath
);
165 RtlInitAnsiString( &kbdProcedureName
, "KbdLayerDescriptor" );
166 LdrGetProcedureAddress((*(PDRIVERS
*)phModule
)->BaseAddress
,
169 (PVOID
*)&layerDescGetFn
);
173 *pKbdTables
= layerDescGetFn();
177 DPRINT1("Error: %wZ has no KbdLayerDescriptor()\n", &FullLayoutPath
);
180 if(!layerDescGetFn
|| !*pKbdTables
)
182 DPRINT1("Failed to load the keyboard layout.\n");
183 EngUnloadImage(*phModule
);
189 DPRINT1("Failed to load dll %wZ\n", &FullLayoutPath
);
196 static PKBL
UserLoadDllAndCreateKbl(DWORD LocaleId
)
202 NewKbl
= ExAllocatePool(PagedPool
, sizeof(KBL
));
206 DPRINT1("%s: Can't allocate memory!\n", __FUNCTION__
);
210 swprintf(NewKbl
->Name
, L
"%08lx", LocaleId
);
212 if(!UserLoadKbdDll(NewKbl
->Name
, &NewKbl
->hModule
, &NewKbl
->KBTables
))
214 DPRINT("%s: failed to load %x dll!\n", __FUNCTION__
, LocaleId
);
219 /* Microsoft Office expects this value to be something specific
220 * for Japanese and Korean Windows with an IME the value is 0xe001
221 * We should probably check to see if an IME exists and if so then
222 * set this word properly.
224 langid
= PRIMARYLANGID(LANGIDFROMLCID(LocaleId
));
227 if (langid
== LANG_CHINESE
|| langid
== LANG_JAPANESE
|| langid
== LANG_KOREAN
)
228 hKl
|= 0xe001 << 16; /* FIXME */
229 else hKl
|= hKl
<< 16;
231 NewKbl
->hkl
= (HKL
)(ULONG_PTR
) hKl
;
232 NewKbl
->klid
= LocaleId
;
234 NewKbl
->RefCount
= 0;
239 BOOL
UserInitDefaultKeyboardLayout()
244 Status
= ZwQueryDefaultLocale(FALSE
, &LocaleId
);
245 if (!NT_SUCCESS(Status
))
247 DPRINT1("Could not get default locale (%08lx).\n", Status
);
251 DPRINT("DefaultLocale = %08lx\n", LocaleId
);
254 if(!NT_SUCCESS(Status
) || !(KBLList
= UserLoadDllAndCreateKbl(LocaleId
)))
256 DPRINT1("Trying to load US Keyboard Layout.\n");
259 if(!(KBLList
= UserLoadDllAndCreateKbl(LocaleId
)))
261 DPRINT1("Failed to load any Keyboard Layout\n");
266 KBLList
->Flags
|= KBL_PRELOAD
;
268 InitializeListHead(&KBLList
->List
);
272 PKBL
W32kGetDefaultKeyLayout(VOID
)
274 const WCHAR szKeyboardLayoutPath
[] = L
"\\Keyboard Layout\\Preload";
275 const WCHAR szDefaultUserPath
[] = L
"\\REGISTRY\\USER\\.DEFAULT";
278 LCID LayoutLocaleId
= 0;
280 OBJECT_ATTRIBUTES KeyAttributes
;
282 UNICODE_STRING CurrentUserPath
;
283 UNICODE_STRING FullKeyboardLayoutPath
;
284 UNICODE_STRING LayoutValueName
;
285 UNICODE_STRING LayoutLocaleIdString
;
286 WCHAR wszBuffer
[MAX_PATH
];
288 // Get the path to HKEY_CURRENT_USER
289 Status
= RtlFormatCurrentUserKeyPath(&CurrentUserPath
);
291 if( NT_SUCCESS(Status
) )
293 // FIXME: Is this 100% correct?
294 // We're called very early, so HKEY_CURRENT_USER might not be available yet. Check this first.
295 InitializeObjectAttributes(&KeyAttributes
, &CurrentUserPath
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
296 Status
= ZwOpenKey(&KeyHandle
, KEY_READ
, &KeyAttributes
);
298 if(Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
300 // It is not available, so read it from HKEY_USERS\.DEFAULT
301 RtlCopyMemory(wszBuffer
, szDefaultUserPath
, sizeof(szDefaultUserPath
));
305 // The path is available
307 RtlCopyMemory(wszBuffer
, CurrentUserPath
.Buffer
, CurrentUserPath
.MaximumLength
);
310 // Build the full path
311 RtlInitUnicodeString(&FullKeyboardLayoutPath
, wszBuffer
);
312 FullKeyboardLayoutPath
.MaximumLength
= MAX_PATH
;
314 Status
= RtlAppendUnicodeToString(&FullKeyboardLayoutPath
, szKeyboardLayoutPath
);
316 if( NT_SUCCESS(Status
) )
318 // Return the first keyboard layout listed there
319 RtlInitUnicodeString(&LayoutValueName
, L
"1");
321 Status
= ReadRegistryValue(&FullKeyboardLayoutPath
, &LayoutValueName
, &LayoutLocaleIdString
);
323 if( NT_SUCCESS(Status
) )
325 RtlUnicodeStringToInteger(&LayoutLocaleIdString
, 16, &LayoutLocaleId
);
326 ExFreePoolWithTag(LayoutLocaleIdString
.Buffer
, TAG_STRING
);
329 DPRINT1("ReadRegistryValue failed! (%08lx).\n", Status
);
332 DPRINT1("RtlAppendUnicodeToString failed! (%08lx)\n", Status
);
334 RtlFreeUnicodeString(&CurrentUserPath
);
337 DPRINT1("RtlFormatCurrentUserKeyPath failed! (%08lx)\n", Status
);
341 // This block is only reached in case of a failure, so use DPRINT1 here
342 DPRINT1("Assuming default locale for the keyboard layout (0x409 - US)\n");
343 LayoutLocaleId
= 0x409;
349 if(pKbl
->klid
== LayoutLocaleId
)
354 pKbl
= (PKBL
) pKbl
->List
.Flink
;
355 } while(pKbl
!= KBLList
);
357 DPRINT("Loading new default keyboard layout.\n");
358 pKbl
= UserLoadDllAndCreateKbl(LayoutLocaleId
);
362 DPRINT("Failed to load %x!!! Returning any availableKL.\n", LayoutLocaleId
);
366 InsertTailList(&KBLList
->List
, &pKbl
->List
);
370 PKBL
UserHklToKbl(HKL hKl
)
375 if(pKbl
->hkl
== hKl
) return pKbl
;
376 pKbl
= (PKBL
) pKbl
->List
.Flink
;
377 } while(pKbl
!= KBLList
);
382 BOOL
UserUnloadKbl(PKBL pKbl
)
384 /* According to msdn, UnloadKeyboardLayout can fail
385 if the keyboard layout identifier was preloaded. */
387 if(pKbl
->Flags
& KBL_PRELOAD
)
389 DPRINT1("Attempted to unload preloaded keyboard layout.\n");
393 if(pKbl
->RefCount
> 0)
395 /* Layout is used by other threads.
396 Mark it as unloaded and don't do anything else. */
397 pKbl
->Flags
|= KBL_UNLOAD
;
402 EngUnloadImage(pKbl
->hModule
);
403 RemoveEntryList(&pKbl
->List
);
410 static PKBL
co_UserActivateKbl(PTHREADINFO w32Thread
, PKBL pKbl
, UINT Flags
)
414 Prev
= w32Thread
->KeyboardLayout
;
416 w32Thread
->KeyboardLayout
= pKbl
;
419 if(Flags
& KLF_SETFORPROCESS
)
425 if(Prev
->Flags
& KBL_UNLOAD
&& Prev
->RefCount
== 0)
430 // Send WM_INPUTLANGCHANGE to thread's focus window
431 co_IntSendMessage(w32Thread
->MessageQueue
->FocusWindow
,
433 0, // FIXME: put charset here (what is this?)
434 (LPARAM
)pKbl
->hkl
); //klid
439 /* EXPORTS *******************************************************************/
442 UserGetKeyboardLayout(
447 PTHREADINFO W32Thread
;
452 W32Thread
= PsGetCurrentThreadWin32Thread();
453 return W32Thread
->KeyboardLayout
->hkl
;
456 Status
= PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
)dwThreadId
, &Thread
);
457 if(!NT_SUCCESS(Status
))
459 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
463 W32Thread
= PsGetThreadWin32Thread(Thread
);
464 Ret
= W32Thread
->KeyboardLayout
->hkl
;
465 ObDereferenceObject(Thread
);
471 NtUserGetKeyboardLayoutList(
486 pKbl
= (PKBL
) pKbl
->List
.Flink
;
487 } while(pKbl
!= KBLList
);
493 ProbeForWrite(pHklBuff
, nItems
*sizeof(HKL
), 4);
497 if(!(pKbl
->Flags
& KBL_UNLOAD
))
499 pHklBuff
[Ret
] = pKbl
->hkl
;
501 pKbl
= (PKBL
) pKbl
->List
.Flink
;
502 if(pKbl
== KBLList
) break;
507 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
509 SetLastNtError(_SEH2_GetExceptionCode());
521 NtUserGetKeyboardLayoutName(
532 ProbeForWrite(lpszName
, KL_NAMELENGTH
*sizeof(WCHAR
), 1);
533 pti
= PsGetCurrentThreadWin32Thread();
534 pKbl
= pti
->KeyboardLayout
;
535 RtlCopyMemory(lpszName
, pKbl
->Name
, KL_NAMELENGTH
*sizeof(WCHAR
));
538 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
540 SetLastNtError(_SEH2_GetExceptionCode());
552 NtUserLoadKeyboardLayoutEx(
555 IN PUNICODE_STRING puszKeyboardName
,
557 IN PUNICODE_STRING puszKLID
,
562 PKBL pKbl
= NULL
, Cur
;
564 UserEnterExclusive();
566 //Let's see if layout was already loaded.
570 if(Cur
->klid
== dwKLID
)
573 pKbl
->Flags
&= ~KBL_UNLOAD
;
577 Cur
= (PKBL
) Cur
->List
.Flink
;
578 } while(Cur
!= KBLList
);
580 //It wasn't, so load it.
583 pKbl
= UserLoadDllAndCreateKbl(dwKLID
);
590 InsertTailList(&KBLList
->List
, &pKbl
->List
);
593 if(Flags
& KLF_REORDER
) KBLList
= pKbl
;
595 if(Flags
& KLF_ACTIVATE
)
597 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKbl
, Flags
);
602 //FIXME: KLF_NOTELLSHELL
613 NtUserActivateKeyboardLayout(
619 PTHREADINFO pWThread
;
621 UserEnterExclusive();
623 pWThread
= PsGetCurrentThreadWin32Thread();
625 if(pWThread
->KeyboardLayout
->hkl
== hKl
)
631 if(hKl
== (HKL
)HKL_NEXT
)
633 pKbl
= (PKBL
)pWThread
->KeyboardLayout
->List
.Flink
;
635 else if(hKl
== (HKL
)HKL_PREV
)
637 pKbl
= (PKBL
)pWThread
->KeyboardLayout
->List
.Blink
;
639 else pKbl
= UserHklToKbl(hKl
);
641 //FIXME: KLF_RESET, KLF_SHIFTLOCK
645 if(Flags
& KLF_REORDER
)
648 if(pKbl
== pWThread
->KeyboardLayout
)
654 pKbl
= co_UserActivateKbl(pWThread
, pKbl
, Flags
);
660 DPRINT1("%s: Invalid HKL %x!\n", __FUNCTION__
, hKl
);
670 NtUserUnloadKeyboardLayout(
676 UserEnterExclusive();
678 if((pKbl
= UserHklToKbl(hKl
)))
680 Ret
= UserUnloadKbl(pKbl
);
684 DPRINT1("%s: Invalid HKL %x!\n", __FUNCTION__
, hKl
);