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 ******************************************************************/
17 DBG_DEFAULT_CHANNEL(UserKbdLayout
);
19 PKBL KBLList
= NULL
; // Keyboard layout list.
21 typedef PVOID (*KbdLayerDescriptor
)(VOID
);
24 /* PRIVATE FUNCTIONS ******************************************************/
28 * Utility function to read a value from the registry more easily.
30 * IN PUNICODE_STRING KeyName -> Name of key to open
31 * IN PUNICODE_STRING ValueName -> Name of value to open
32 * OUT PUNICODE_STRING ReturnedValue -> String contained in registry
37 static NTSTATUS APIENTRY
ReadRegistryValue( PUNICODE_STRING KeyName
,
38 PUNICODE_STRING ValueName
,
39 PUNICODE_STRING ReturnedValue
)
43 OBJECT_ATTRIBUTES KeyAttributes
;
44 PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo
;
49 InitializeObjectAttributes(&KeyAttributes
, KeyName
, OBJ_CASE_INSENSITIVE
,
51 Status
= ZwOpenKey(&KeyHandle
, KEY_READ
, &KeyAttributes
);
52 if( !NT_SUCCESS(Status
) )
57 Status
= ZwQueryValueKey(KeyHandle
, ValueName
, KeyValuePartialInformation
,
62 if( Status
!= STATUS_BUFFER_TOO_SMALL
)
68 ResLength
+= sizeof( *KeyValuePartialInfo
);
70 ExAllocatePoolWithTag(PagedPool
, ResLength
, TAG_STRING
);
73 if( !KeyValuePartialInfo
)
76 return STATUS_NO_MEMORY
;
79 Status
= ZwQueryValueKey(KeyHandle
,
81 KeyValuePartialInformation
,
82 (PVOID
)KeyValuePartialInfo
,
86 if( !NT_SUCCESS(Status
) )
89 ExFreePoolWithTag(KeyValuePartialInfo
, TAG_STRING
);
93 /* At this point, KeyValuePartialInfo->Data contains the key data */
94 ReturnBuffer
= ExAllocatePoolWithTag(PagedPool
,
95 KeyValuePartialInfo
->DataLength
,
101 ExFreePoolWithTag(KeyValuePartialInfo
, TAG_STRING
);
102 return STATUS_NO_MEMORY
;
105 RtlCopyMemory(ReturnBuffer
,
106 KeyValuePartialInfo
->Data
,
107 KeyValuePartialInfo
->DataLength
);
108 RtlInitUnicodeString(ReturnedValue
, ReturnBuffer
);
110 ExFreePoolWithTag(KeyValuePartialInfo
, TAG_STRING
);
116 static BOOL
UserLoadKbdDll(WCHAR
*wsKLID
,
118 PKBDTABLES
*pKbdTables
)
121 KbdLayerDescriptor layerDescGetFn
;
122 ANSI_STRING kbdProcedureName
;
123 UNICODE_STRING LayoutKeyName
;
124 UNICODE_STRING LayoutValueName
;
125 UNICODE_STRING LayoutFile
;
126 UNICODE_STRING FullLayoutPath
;
128 WCHAR LayoutPathBuffer
[MAX_PATH
] = L
"\\SystemRoot\\System32\\";
129 WCHAR KeyNameBuffer
[MAX_PATH
] = L
"\\REGISTRY\\Machine\\SYSTEM\\"
130 L
"CurrentControlSet\\Control\\Keyboard Layouts\\";
132 RtlInitUnicodeString(&klid
, wsKLID
);
133 RtlInitUnicodeString(&LayoutValueName
,L
"Layout File");
134 RtlInitUnicodeString(&LayoutKeyName
, KeyNameBuffer
);
135 LayoutKeyName
.MaximumLength
= sizeof(KeyNameBuffer
);
137 RtlAppendUnicodeStringToString(&LayoutKeyName
, &klid
);
138 Status
= ReadRegistryValue(&LayoutKeyName
, &LayoutValueName
, &LayoutFile
);
140 if(!NT_SUCCESS(Status
))
142 TRACE("Can't get layout filename for %wZ. (%08lx)\n", klid
, Status
);
146 TRACE("Read registry and got %wZ\n", &LayoutFile
);
147 RtlInitUnicodeString(&FullLayoutPath
, LayoutPathBuffer
);
148 FullLayoutPath
.MaximumLength
= sizeof(LayoutPathBuffer
);
149 RtlAppendUnicodeStringToString(&FullLayoutPath
, &LayoutFile
);
150 TRACE("Loading Keyboard DLL %wZ\n", &FullLayoutPath
);
151 ExFreePoolWithTag(LayoutFile
.Buffer
, TAG_STRING
);
153 *phModule
= EngLoadImage(FullLayoutPath
.Buffer
);
157 TRACE("Loaded %wZ\n", &FullLayoutPath
);
159 RtlInitAnsiString( &kbdProcedureName
, "KbdLayerDescriptor" );
160 layerDescGetFn
= EngFindImageProcAddress(*phModule
, "KbdLayerDescriptor");
164 *pKbdTables
= layerDescGetFn();
168 ERR("Error: %wZ has no KbdLayerDescriptor()\n", &FullLayoutPath
);
171 if(!layerDescGetFn
|| !*pKbdTables
)
173 ERR("Failed to load the keyboard layout.\n");
174 EngUnloadImage(*phModule
);
180 ERR("Failed to load dll %wZ\n", &FullLayoutPath
);
187 static PKBL
UserLoadDllAndCreateKbl(DWORD LocaleId
)
193 NewKbl
= ExAllocatePoolWithTag(PagedPool
, sizeof(KBL
), USERTAG_KBDLAYOUT
);
197 ERR("%s: Can't allocate memory!\n", __FUNCTION__
);
201 swprintf(NewKbl
->Name
, L
"%08lx", LocaleId
);
203 if(!UserLoadKbdDll(NewKbl
->Name
, &NewKbl
->hModule
, &NewKbl
->KBTables
))
205 TRACE("%s: failed to load %x dll!\n", __FUNCTION__
, LocaleId
);
206 ExFreePoolWithTag(NewKbl
, USERTAG_KBDLAYOUT
);
210 /* Microsoft Office expects this value to be something specific
211 * for Japanese and Korean Windows with an IME the value is 0xe001
212 * We should probably check to see if an IME exists and if so then
213 * set this word properly.
215 langid
= PRIMARYLANGID(LANGIDFROMLCID(LocaleId
));
218 if (langid
== LANG_CHINESE
|| langid
== LANG_JAPANESE
|| langid
== LANG_KOREAN
)
219 hKl
|= 0xe001 << 16; /* FIXME */
220 else hKl
|= hKl
<< 16;
222 NewKbl
->hkl
= (HKL
)(ULONG_PTR
) hKl
;
223 NewKbl
->klid
= LocaleId
;
225 NewKbl
->RefCount
= 0;
230 BOOL
UserInitDefaultKeyboardLayout()
235 Status
= ZwQueryDefaultLocale(FALSE
, &LocaleId
);
236 if (!NT_SUCCESS(Status
))
238 ERR("Could not get default locale (%08lx).\n", Status
);
242 TRACE("DefaultLocale = %08lx\n", LocaleId
);
245 if(!NT_SUCCESS(Status
) || !(KBLList
= UserLoadDllAndCreateKbl(LocaleId
)))
247 ERR("Trying to load US Keyboard Layout.\n");
250 if(!(KBLList
= UserLoadDllAndCreateKbl(LocaleId
)))
252 ERR("Failed to load any Keyboard Layout\n");
257 KBLList
->Flags
|= KBL_PRELOAD
;
259 InitializeListHead(&KBLList
->List
);
263 PKBL
W32kGetDefaultKeyLayout(VOID
)
265 const WCHAR szKeyboardLayoutPath
[] = L
"\\Keyboard Layout\\Preload";
266 const WCHAR szDefaultUserPath
[] = L
"\\REGISTRY\\USER\\.DEFAULT";
269 LCID LayoutLocaleId
= 0;
271 OBJECT_ATTRIBUTES KeyAttributes
;
273 UNICODE_STRING CurrentUserPath
;
274 UNICODE_STRING FullKeyboardLayoutPath
;
275 UNICODE_STRING LayoutValueName
;
276 UNICODE_STRING LayoutLocaleIdString
;
277 WCHAR wszBuffer
[MAX_PATH
];
279 // Get the path to HKEY_CURRENT_USER
280 Status
= RtlFormatCurrentUserKeyPath(&CurrentUserPath
);
282 if( NT_SUCCESS(Status
) )
284 FullKeyboardLayoutPath
.Buffer
= wszBuffer
;
285 FullKeyboardLayoutPath
.MaximumLength
= sizeof(wszBuffer
);
287 // FIXME: Is this 100% correct?
288 // We're called very early, so HKEY_CURRENT_USER might not be available yet. Check this first.
289 InitializeObjectAttributes(&KeyAttributes
, &CurrentUserPath
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
290 Status
= ZwOpenKey(&KeyHandle
, KEY_READ
, &KeyAttributes
);
292 if(Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
294 // It is not available, so read it from HKEY_USERS\.DEFAULT
295 FullKeyboardLayoutPath
.Length
= sizeof(szDefaultUserPath
) - sizeof(UNICODE_NULL
);
296 RtlCopyMemory(wszBuffer
, szDefaultUserPath
, sizeof(szDefaultUserPath
));
300 // The path is available
302 RtlCopyUnicodeString(&FullKeyboardLayoutPath
, &CurrentUserPath
);
305 // Free CurrentUserPath - we dont need it anymore
306 RtlFreeUnicodeString(&CurrentUserPath
);
308 Status
= RtlAppendUnicodeToString(&FullKeyboardLayoutPath
, szKeyboardLayoutPath
);
310 if( NT_SUCCESS(Status
) )
312 // Return the first keyboard layout listed there
313 RtlInitUnicodeString(&LayoutValueName
, L
"1");
315 Status
= ReadRegistryValue(&FullKeyboardLayoutPath
, &LayoutValueName
, &LayoutLocaleIdString
);
317 if( NT_SUCCESS(Status
) )
319 RtlUnicodeStringToInteger(&LayoutLocaleIdString
, 16, &LayoutLocaleId
);
320 ExFreePoolWithTag(LayoutLocaleIdString
.Buffer
, TAG_STRING
);
323 ERR("ReadRegistryValue failed! (%08lx).\n", Status
);
326 ERR("RtlAppendUnicodeToString failed! (%08lx)\n", Status
);
329 ERR("RtlFormatCurrentUserKeyPath failed! (%08lx)\n", Status
);
333 ERR("Assuming default locale for the keyboard layout (0x409 - US)\n");
334 LayoutLocaleId
= 0x409;
340 if(pKbl
->klid
== LayoutLocaleId
)
345 pKbl
= (PKBL
) pKbl
->List
.Flink
;
346 } while(pKbl
!= KBLList
);
348 TRACE("Loading new default keyboard layout.\n");
349 pKbl
= UserLoadDllAndCreateKbl(LayoutLocaleId
);
353 TRACE("Failed to load %x!!! Returning any availableKL.\n", LayoutLocaleId
);
357 InsertTailList(&KBLList
->List
, &pKbl
->List
);
361 PKBL
UserHklToKbl(HKL hKl
)
366 if(pKbl
->hkl
== hKl
) return pKbl
;
367 pKbl
= (PKBL
) pKbl
->List
.Flink
;
368 } while(pKbl
!= KBLList
);
373 BOOL
UserUnloadKbl(PKBL pKbl
)
375 /* According to msdn, UnloadKeyboardLayout can fail
376 if the keyboard layout identifier was preloaded. */
378 if(pKbl
->Flags
& KBL_PRELOAD
)
380 ERR("Attempted to unload preloaded keyboard layout.\n");
384 if(pKbl
->RefCount
> 0)
386 /* Layout is used by other threads.
387 Mark it as unloaded and don't do anything else. */
388 pKbl
->Flags
|= KBL_UNLOAD
;
393 EngUnloadImage(pKbl
->hModule
);
394 RemoveEntryList(&pKbl
->List
);
395 ExFreePoolWithTag(pKbl
, USERTAG_KBDLAYOUT
);
401 static PKBL
co_UserActivateKbl(PTHREADINFO w32Thread
, PKBL pKbl
, UINT Flags
)
405 Prev
= w32Thread
->KeyboardLayout
;
407 w32Thread
->KeyboardLayout
= pKbl
;
410 if(Flags
& KLF_SETFORPROCESS
)
416 if(Prev
->Flags
& KBL_UNLOAD
&& Prev
->RefCount
== 0)
421 // Send WM_INPUTLANGCHANGE to thread's focus window
422 co_IntSendMessage(w32Thread
->MessageQueue
->FocusWindow
,
424 0, // FIXME: put charset here (what is this?)
425 (LPARAM
)pKbl
->hkl
); //klid
430 /* EXPORTS *******************************************************************/
433 UserGetKeyboardLayout(
438 PTHREADINFO W32Thread
;
443 W32Thread
= PsGetCurrentThreadWin32Thread();
444 return W32Thread
->KeyboardLayout
->hkl
;
447 Status
= PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
)dwThreadId
, &Thread
);
448 if(!NT_SUCCESS(Status
))
450 EngSetLastError(ERROR_INVALID_PARAMETER
);
454 W32Thread
= PsGetThreadWin32Thread(Thread
);
455 Ret
= W32Thread
->KeyboardLayout
->hkl
;
456 ObDereferenceObject(Thread
);
462 NtUserGetKeyboardLayoutList(
477 pKbl
= (PKBL
) pKbl
->List
.Flink
;
478 } while(pKbl
!= KBLList
);
484 ProbeForWrite(pHklBuff
, nItems
*sizeof(HKL
), 4);
488 if(!(pKbl
->Flags
& KBL_UNLOAD
))
490 pHklBuff
[Ret
] = pKbl
->hkl
;
492 pKbl
= (PKBL
) pKbl
->List
.Flink
;
493 if(pKbl
== KBLList
) break;
498 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
500 SetLastNtError(_SEH2_GetExceptionCode());
512 NtUserGetKeyboardLayoutName(
523 ProbeForWrite(lpszName
, KL_NAMELENGTH
*sizeof(WCHAR
), 1);
524 pti
= PsGetCurrentThreadWin32Thread();
525 pKbl
= pti
->KeyboardLayout
;
526 RtlCopyMemory(lpszName
, pKbl
->Name
, KL_NAMELENGTH
*sizeof(WCHAR
));
529 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
531 SetLastNtError(_SEH2_GetExceptionCode());
543 NtUserLoadKeyboardLayoutEx(
546 IN PUNICODE_STRING puszKeyboardName
,
548 IN PUNICODE_STRING puszKLID
,
553 PKBL pKbl
= NULL
, Cur
;
555 UserEnterExclusive();
557 //Let's see if layout was already loaded.
561 if(Cur
->klid
== dwKLID
)
564 pKbl
->Flags
&= ~KBL_UNLOAD
;
568 Cur
= (PKBL
) Cur
->List
.Flink
;
569 } while(Cur
!= KBLList
);
571 //It wasn't, so load it.
574 pKbl
= UserLoadDllAndCreateKbl(dwKLID
);
581 InsertTailList(&KBLList
->List
, &pKbl
->List
);
584 if(Flags
& KLF_REORDER
) KBLList
= pKbl
;
586 if(Flags
& KLF_ACTIVATE
)
588 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKbl
, Flags
);
593 //FIXME: KLF_NOTELLSHELL
604 NtUserActivateKeyboardLayout(
610 PTHREADINFO pWThread
;
612 UserEnterExclusive();
614 pWThread
= PsGetCurrentThreadWin32Thread();
616 if(pWThread
->KeyboardLayout
->hkl
== hKl
)
622 if(hKl
== (HKL
)HKL_NEXT
)
624 pKbl
= (PKBL
)pWThread
->KeyboardLayout
->List
.Flink
;
626 else if(hKl
== (HKL
)HKL_PREV
)
628 pKbl
= (PKBL
)pWThread
->KeyboardLayout
->List
.Blink
;
630 else pKbl
= UserHklToKbl(hKl
);
632 //FIXME: KLF_RESET, KLF_SHIFTLOCK
636 if(Flags
& KLF_REORDER
)
639 if(pKbl
== pWThread
->KeyboardLayout
)
645 pKbl
= co_UserActivateKbl(pWThread
, pKbl
, Flags
);
651 ERR("%s: Invalid HKL %x!\n", __FUNCTION__
, hKl
);
661 NtUserUnloadKeyboardLayout(
667 UserEnterExclusive();
669 if((pKbl
= UserHklToKbl(hKl
)))
671 Ret
= UserUnloadKbl(pKbl
);
675 ERR("%s: Invalid HKL %x!\n", __FUNCTION__
, hKl
);