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 layerDescGetFn
= EngFindImageProcAddress(*phModule
, "KbdLayerDescriptor");
170 *pKbdTables
= layerDescGetFn();
174 DPRINT1("Error: %wZ has no KbdLayerDescriptor()\n", &FullLayoutPath
);
177 if(!layerDescGetFn
|| !*pKbdTables
)
179 DPRINT1("Failed to load the keyboard layout.\n");
180 EngUnloadImage(*phModule
);
186 DPRINT1("Failed to load dll %wZ\n", &FullLayoutPath
);
193 static PKBL
UserLoadDllAndCreateKbl(DWORD LocaleId
)
199 NewKbl
= ExAllocatePoolWithTag(PagedPool
, sizeof(KBL
), TAG_KEYBOARD
);
203 DPRINT1("%s: Can't allocate memory!\n", __FUNCTION__
);
207 swprintf(NewKbl
->Name
, L
"%08lx", LocaleId
);
209 if(!UserLoadKbdDll(NewKbl
->Name
, &NewKbl
->hModule
, &NewKbl
->KBTables
))
211 DPRINT("%s: failed to load %x dll!\n", __FUNCTION__
, LocaleId
);
212 ExFreePoolWithTag(NewKbl
, TAG_KEYBOARD
);
216 /* Microsoft Office expects this value to be something specific
217 * for Japanese and Korean Windows with an IME the value is 0xe001
218 * We should probably check to see if an IME exists and if so then
219 * set this word properly.
221 langid
= PRIMARYLANGID(LANGIDFROMLCID(LocaleId
));
224 if (langid
== LANG_CHINESE
|| langid
== LANG_JAPANESE
|| langid
== LANG_KOREAN
)
225 hKl
|= 0xe001 << 16; /* FIXME */
226 else hKl
|= hKl
<< 16;
228 NewKbl
->hkl
= (HKL
)(ULONG_PTR
) hKl
;
229 NewKbl
->klid
= LocaleId
;
231 NewKbl
->RefCount
= 0;
236 BOOL
UserInitDefaultKeyboardLayout()
241 Status
= ZwQueryDefaultLocale(FALSE
, &LocaleId
);
242 if (!NT_SUCCESS(Status
))
244 DPRINT1("Could not get default locale (%08lx).\n", Status
);
248 DPRINT("DefaultLocale = %08lx\n", LocaleId
);
251 if(!NT_SUCCESS(Status
) || !(KBLList
= UserLoadDllAndCreateKbl(LocaleId
)))
253 DPRINT1("Trying to load US Keyboard Layout.\n");
256 if(!(KBLList
= UserLoadDllAndCreateKbl(LocaleId
)))
258 DPRINT1("Failed to load any Keyboard Layout\n");
263 KBLList
->Flags
|= KBL_PRELOAD
;
265 InitializeListHead(&KBLList
->List
);
269 PKBL
W32kGetDefaultKeyLayout(VOID
)
271 const WCHAR szKeyboardLayoutPath
[] = L
"\\Keyboard Layout\\Preload";
272 const WCHAR szDefaultUserPath
[] = L
"\\REGISTRY\\USER\\.DEFAULT";
275 LCID LayoutLocaleId
= 0;
277 OBJECT_ATTRIBUTES KeyAttributes
;
279 UNICODE_STRING CurrentUserPath
;
280 UNICODE_STRING FullKeyboardLayoutPath
;
281 UNICODE_STRING LayoutValueName
;
282 UNICODE_STRING LayoutLocaleIdString
;
283 WCHAR wszBuffer
[MAX_PATH
];
285 // Get the path to HKEY_CURRENT_USER
286 Status
= RtlFormatCurrentUserKeyPath(&CurrentUserPath
);
288 if( NT_SUCCESS(Status
) )
290 // FIXME: Is this 100% correct?
291 // We're called very early, so HKEY_CURRENT_USER might not be available yet. Check this first.
292 InitializeObjectAttributes(&KeyAttributes
, &CurrentUserPath
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
293 Status
= ZwOpenKey(&KeyHandle
, KEY_READ
, &KeyAttributes
);
295 if(Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
297 // It is not available, so read it from HKEY_USERS\.DEFAULT
298 RtlCopyMemory(wszBuffer
, szDefaultUserPath
, sizeof(szDefaultUserPath
));
302 // The path is available
304 RtlCopyMemory(wszBuffer
, CurrentUserPath
.Buffer
, CurrentUserPath
.MaximumLength
);
307 // Build the full path
308 RtlInitUnicodeString(&FullKeyboardLayoutPath
, wszBuffer
);
309 FullKeyboardLayoutPath
.MaximumLength
= MAX_PATH
;
311 Status
= RtlAppendUnicodeToString(&FullKeyboardLayoutPath
, szKeyboardLayoutPath
);
313 if( NT_SUCCESS(Status
) )
315 // Return the first keyboard layout listed there
316 RtlInitUnicodeString(&LayoutValueName
, L
"1");
318 Status
= ReadRegistryValue(&FullKeyboardLayoutPath
, &LayoutValueName
, &LayoutLocaleIdString
);
320 if( NT_SUCCESS(Status
) )
322 RtlUnicodeStringToInteger(&LayoutLocaleIdString
, 16, &LayoutLocaleId
);
323 ExFreePoolWithTag(LayoutLocaleIdString
.Buffer
, TAG_STRING
);
326 DPRINT1("ReadRegistryValue failed! (%08lx).\n", Status
);
329 DPRINT1("RtlAppendUnicodeToString failed! (%08lx)\n", Status
);
331 RtlFreeUnicodeString(&CurrentUserPath
);
334 DPRINT1("RtlFormatCurrentUserKeyPath failed! (%08lx)\n", Status
);
338 // This block is only reached in case of a failure, so use DPRINT1 here
339 DPRINT1("Assuming default locale for the keyboard layout (0x409 - US)\n");
340 LayoutLocaleId
= 0x409;
346 if(pKbl
->klid
== LayoutLocaleId
)
351 pKbl
= (PKBL
) pKbl
->List
.Flink
;
352 } while(pKbl
!= KBLList
);
354 DPRINT("Loading new default keyboard layout.\n");
355 pKbl
= UserLoadDllAndCreateKbl(LayoutLocaleId
);
359 DPRINT("Failed to load %x!!! Returning any availableKL.\n", LayoutLocaleId
);
363 InsertTailList(&KBLList
->List
, &pKbl
->List
);
367 PKBL
UserHklToKbl(HKL hKl
)
372 if(pKbl
->hkl
== hKl
) return pKbl
;
373 pKbl
= (PKBL
) pKbl
->List
.Flink
;
374 } while(pKbl
!= KBLList
);
379 BOOL
UserUnloadKbl(PKBL pKbl
)
381 /* According to msdn, UnloadKeyboardLayout can fail
382 if the keyboard layout identifier was preloaded. */
384 if(pKbl
->Flags
& KBL_PRELOAD
)
386 DPRINT1("Attempted to unload preloaded keyboard layout.\n");
390 if(pKbl
->RefCount
> 0)
392 /* Layout is used by other threads.
393 Mark it as unloaded and don't do anything else. */
394 pKbl
->Flags
|= KBL_UNLOAD
;
399 EngUnloadImage(pKbl
->hModule
);
400 RemoveEntryList(&pKbl
->List
);
401 ExFreePoolWithTag(pKbl
, TAG_KEYBOARD
);
407 static PKBL
co_UserActivateKbl(PTHREADINFO w32Thread
, PKBL pKbl
, UINT Flags
)
411 Prev
= w32Thread
->KeyboardLayout
;
413 w32Thread
->KeyboardLayout
= pKbl
;
416 if(Flags
& KLF_SETFORPROCESS
)
422 if(Prev
->Flags
& KBL_UNLOAD
&& Prev
->RefCount
== 0)
427 // Send WM_INPUTLANGCHANGE to thread's focus window
428 co_IntSendMessage(w32Thread
->MessageQueue
->FocusWindow
,
430 0, // FIXME: put charset here (what is this?)
431 (LPARAM
)pKbl
->hkl
); //klid
436 /* EXPORTS *******************************************************************/
439 UserGetKeyboardLayout(
444 PTHREADINFO W32Thread
;
449 W32Thread
= PsGetCurrentThreadWin32Thread();
450 return W32Thread
->KeyboardLayout
->hkl
;
453 Status
= PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
)dwThreadId
, &Thread
);
454 if(!NT_SUCCESS(Status
))
456 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
460 W32Thread
= PsGetThreadWin32Thread(Thread
);
461 Ret
= W32Thread
->KeyboardLayout
->hkl
;
462 ObDereferenceObject(Thread
);
468 NtUserGetKeyboardLayoutList(
483 pKbl
= (PKBL
) pKbl
->List
.Flink
;
484 } while(pKbl
!= KBLList
);
490 ProbeForWrite(pHklBuff
, nItems
*sizeof(HKL
), 4);
494 if(!(pKbl
->Flags
& KBL_UNLOAD
))
496 pHklBuff
[Ret
] = pKbl
->hkl
;
498 pKbl
= (PKBL
) pKbl
->List
.Flink
;
499 if(pKbl
== KBLList
) break;
504 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
506 SetLastNtError(_SEH2_GetExceptionCode());
518 NtUserGetKeyboardLayoutName(
529 ProbeForWrite(lpszName
, KL_NAMELENGTH
*sizeof(WCHAR
), 1);
530 pti
= PsGetCurrentThreadWin32Thread();
531 pKbl
= pti
->KeyboardLayout
;
532 RtlCopyMemory(lpszName
, pKbl
->Name
, KL_NAMELENGTH
*sizeof(WCHAR
));
535 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
537 SetLastNtError(_SEH2_GetExceptionCode());
549 NtUserLoadKeyboardLayoutEx(
552 IN PUNICODE_STRING puszKeyboardName
,
554 IN PUNICODE_STRING puszKLID
,
559 PKBL pKbl
= NULL
, Cur
;
561 UserEnterExclusive();
563 //Let's see if layout was already loaded.
567 if(Cur
->klid
== dwKLID
)
570 pKbl
->Flags
&= ~KBL_UNLOAD
;
574 Cur
= (PKBL
) Cur
->List
.Flink
;
575 } while(Cur
!= KBLList
);
577 //It wasn't, so load it.
580 pKbl
= UserLoadDllAndCreateKbl(dwKLID
);
587 InsertTailList(&KBLList
->List
, &pKbl
->List
);
590 if(Flags
& KLF_REORDER
) KBLList
= pKbl
;
592 if(Flags
& KLF_ACTIVATE
)
594 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKbl
, Flags
);
599 //FIXME: KLF_NOTELLSHELL
610 NtUserActivateKeyboardLayout(
616 PTHREADINFO pWThread
;
618 UserEnterExclusive();
620 pWThread
= PsGetCurrentThreadWin32Thread();
622 if(pWThread
->KeyboardLayout
->hkl
== hKl
)
628 if(hKl
== (HKL
)HKL_NEXT
)
630 pKbl
= (PKBL
)pWThread
->KeyboardLayout
->List
.Flink
;
632 else if(hKl
== (HKL
)HKL_PREV
)
634 pKbl
= (PKBL
)pWThread
->KeyboardLayout
->List
.Blink
;
636 else pKbl
= UserHklToKbl(hKl
);
638 //FIXME: KLF_RESET, KLF_SHIFTLOCK
642 if(Flags
& KLF_REORDER
)
645 if(pKbl
== pWThread
->KeyboardLayout
)
651 pKbl
= co_UserActivateKbl(pWThread
, pKbl
, Flags
);
657 DPRINT1("%s: Invalid HKL %x!\n", __FUNCTION__
, hKl
);
667 NtUserUnloadKeyboardLayout(
673 UserEnterExclusive();
675 if((pKbl
= UserHklToKbl(hKl
)))
677 Ret
= UserUnloadKbl(pKbl
);
681 DPRINT1("%s: Invalid HKL %x!\n", __FUNCTION__
, hKl
);