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
12 /* INCLUDES ******************************************************************/
19 PKBL KBLList
= NULL
; // Keyboard layout list.
21 typedef PVOID (*KbdLayerDescriptor
)(VOID
);
22 NTSTATUS STDCALL
LdrGetProcedureAddress(PVOID module
,
23 PANSI_STRING import_name
,
29 /* PRIVATE FUNCTIONS ******************************************************/
33 * Utility function to read a value from the registry more easily.
35 * IN PUNICODE_STRING KeyName -> Name of key to open
36 * IN PUNICODE_STRING ValueName -> Name of value to open
37 * OUT PUNICODE_STRING ReturnedValue -> String contained in registry
42 static NTSTATUS NTAPI
ReadRegistryValue( PUNICODE_STRING KeyName
,
43 PUNICODE_STRING ValueName
,
44 PUNICODE_STRING ReturnedValue
)
48 OBJECT_ATTRIBUTES KeyAttributes
;
49 PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo
;
54 InitializeObjectAttributes(&KeyAttributes
, KeyName
, OBJ_CASE_INSENSITIVE
,
56 Status
= ZwOpenKey(&KeyHandle
, KEY_ALL_ACCESS
, &KeyAttributes
);
57 if( !NT_SUCCESS(Status
) )
62 Status
= ZwQueryValueKey(KeyHandle
, ValueName
, KeyValuePartialInformation
,
67 if( Status
!= STATUS_BUFFER_TOO_SMALL
)
73 ResLength
+= sizeof( *KeyValuePartialInfo
);
75 ExAllocatePoolWithTag(PagedPool
, ResLength
, TAG_STRING
);
78 if( !KeyValuePartialInfo
)
81 return STATUS_NO_MEMORY
;
84 Status
= ZwQueryValueKey(KeyHandle
,
86 KeyValuePartialInformation
,
87 (PVOID
)KeyValuePartialInfo
,
91 if( !NT_SUCCESS(Status
) )
94 ExFreePool(KeyValuePartialInfo
);
98 /* At this point, KeyValuePartialInfo->Data contains the key data */
99 ReturnBuffer
= ExAllocatePoolWithTag(PagedPool
,
100 KeyValuePartialInfo
->DataLength
,
106 ExFreePool(KeyValuePartialInfo
);
107 return STATUS_NO_MEMORY
;
110 RtlCopyMemory(ReturnBuffer
,
111 KeyValuePartialInfo
->Data
,
112 KeyValuePartialInfo
->DataLength
);
113 RtlInitUnicodeString(ReturnedValue
, ReturnBuffer
);
115 ExFreePool(KeyValuePartialInfo
);
121 static BOOL
UserLoadKbdDll(WCHAR
*wsKLID
,
123 PKBDTABLES
*pKbdTables
)
126 KbdLayerDescriptor layerDescGetFn
;
127 ANSI_STRING kbdProcedureName
;
128 UNICODE_STRING LayoutKeyName
;
129 UNICODE_STRING LayoutValueName
;
130 UNICODE_STRING LayoutFile
;
131 UNICODE_STRING FullLayoutPath
;
133 WCHAR LayoutPathBuffer
[MAX_PATH
] = L
"\\SystemRoot\\System32\\";
134 WCHAR KeyNameBuffer
[MAX_PATH
] = L
"\\REGISTRY\\Machine\\SYSTEM\\"
135 L
"CurrentControlSet\\Control\\KeyboardLayouts\\";
137 RtlInitUnicodeString(&klid
, wsKLID
);
138 RtlInitUnicodeString(&LayoutValueName
,L
"Layout File");
139 RtlInitUnicodeString(&LayoutKeyName
, KeyNameBuffer
);
140 LayoutKeyName
.MaximumLength
= sizeof(KeyNameBuffer
);
142 RtlAppendUnicodeStringToString(&LayoutKeyName
, &klid
);
143 Status
= ReadRegistryValue(&LayoutKeyName
, &LayoutValueName
, &LayoutFile
);
145 if(!NT_SUCCESS(Status
))
147 DPRINT1("Can't get layout filename for %wZ. (%08lx)\n", klid
, Status
);
151 DPRINT("Read registry and got %wZ\n", &LayoutFile
);
152 RtlInitUnicodeString(&FullLayoutPath
, LayoutPathBuffer
);
153 FullLayoutPath
.MaximumLength
= sizeof(LayoutPathBuffer
);
154 RtlAppendUnicodeStringToString(&FullLayoutPath
, &LayoutFile
);
155 DPRINT("Loading Keyboard DLL %wZ\n", &FullLayoutPath
);
156 RtlFreeUnicodeString(&LayoutFile
);
158 *phModule
= EngLoadImage(FullLayoutPath
.Buffer
);
162 DPRINT("Loaded %wZ\n", &FullLayoutPath
);
164 RtlInitAnsiString( &kbdProcedureName
, "KbdLayerDescriptor" );
165 LdrGetProcedureAddress((PVOID
)*phModule
,
168 (PVOID
*)&layerDescGetFn
);
172 *pKbdTables
= layerDescGetFn();
176 DPRINT1("Error: %wZ has no KbdLayerDescriptor()\n", &FullLayoutPath
);
179 if(!layerDescGetFn
|| !*pKbdTables
)
181 DPRINT1("Failed to load the keyboard layout.\n");
182 EngUnloadImage(*phModule
);
188 DPRINT1("Failed to load dll %wZ\n", &FullLayoutPath
);
195 static PKBL
UserLoadDllAndCreateKbl(DWORD LocaleId
)
201 NewKbl
= ExAllocatePool(PagedPool
, sizeof(KBL
));
205 DPRINT1("%s: Can't allocate memory!\n", __FUNCTION__
);
209 swprintf(NewKbl
->Name
, L
"%08lx", LocaleId
);
211 if(!UserLoadKbdDll(NewKbl
->Name
, &NewKbl
->hModule
, &NewKbl
->KBTables
))
213 DPRINT1("%s: failed to load %x dll!\n", __FUNCTION__
, LocaleId
);
218 /* Microsoft Office expects this value to be something specific
219 * for Japanese and Korean Windows with an IME the value is 0xe001
220 * We should probably check to see if an IME exists and if so then
221 * set this word properly.
223 langid
= PRIMARYLANGID(LANGIDFROMLCID(LocaleId
));
226 if (langid
== LANG_CHINESE
|| langid
== LANG_JAPANESE
|| langid
== LANG_KOREAN
)
227 hKl
|= 0xe001 << 16; /* FIXME */
228 else hKl
|= hKl
<< 16;
230 NewKbl
->hkl
= (HKL
) hKl
;
231 NewKbl
->klid
= LocaleId
;
233 NewKbl
->RefCount
= 0;
238 BOOL
UserInitDefaultKeyboardLayout()
243 Status
= ZwQueryDefaultLocale(FALSE
, &LocaleId
);
244 if (!NT_SUCCESS(Status
))
246 DPRINT1("Could not get default locale (%08lx).\n", Status
);
250 DPRINT("DefaultLocale = %08lx\n", LocaleId
);
253 if(!NT_SUCCESS(Status
) || !(KBLList
= UserLoadDllAndCreateKbl(LocaleId
)))
255 DPRINT1("Trying to load US Keyboard Layout.\n");
258 if(!(KBLList
= UserLoadDllAndCreateKbl(LocaleId
)))
260 DPRINT1("Failed to load any Keyboard Layout\n");
265 InitializeListHead(&KBLList
->List
);
269 PKBL
W32kGetDefaultKeyLayout(VOID
)
275 // This is probably wrong...
276 // I need to do more research.
277 Status
= ZwQueryDefaultLocale(FALSE
, &LocaleId
);
278 if (!NT_SUCCESS(Status
))
280 DPRINT1("Could not get default locale (%08lx).\n", Status
);
281 DPRINT1("Assuming default locale = 0x409 (US).\n");
288 if(pKbl
->klid
== LocaleId
)
293 pKbl
= (PKBL
) pKbl
->List
.Flink
;
294 } while(pKbl
!= KBLList
);
296 DPRINT("Loading new default keyboard layout.\n");
297 pKbl
= UserLoadDllAndCreateKbl(LocaleId
);
301 DPRINT1("Failed to load %x!!! Returning any availableKL.\n", LocaleId
);
305 InsertTailList(&KBLList
->List
, &pKbl
->List
);
309 static PKBL
UserHklToKbl(HKL hKl
)
314 if(pKbl
->hkl
== hKl
) return pKbl
;
315 pKbl
= (PKBL
) pKbl
->List
.Flink
;
316 } while(pKbl
!= KBLList
);
321 static PKBL
co_UserActivateKbl(PW32THREAD Thread
, PKBL pKbl
)
325 Prev
= Thread
->KeyboardLayout
;
327 Thread
->KeyboardLayout
= pKbl
;
330 // Send WM_INPUTLANGCHANGE to thread's focus window
331 co_IntSendMessage(Thread
->MessageQueue
->FocusWindow
,
333 0, // FIXME: put charset here (what is this?)
334 (LPARAM
)pKbl
->hkl
); //klid
339 /* EXPORTS *******************************************************************/
342 UserGetKeyboardLayout(
347 PW32THREAD W32Thread
;
352 W32Thread
= PsGetCurrentThreadWin32Thread();
353 return W32Thread
->KeyboardLayout
->hkl
;
356 Status
= PsLookupThreadByThreadId((HANDLE
)dwThreadId
, &Thread
);
357 if(!NT_SUCCESS(Status
))
359 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
363 W32Thread
= PsGetThreadWin32Thread(Thread
);
364 Ret
= W32Thread
->KeyboardLayout
->hkl
;
365 ObDereferenceObject(Thread
);
371 NtUserGetKeyboardLayoutList(
386 pKbl
= (PKBL
) pKbl
->List
.Flink
;
387 } while(pKbl
!= KBLList
);
393 ProbeForWrite(pHklBuff
, nItems
*sizeof(HKL
), 4);
397 pHklBuff
[Ret
] = pKbl
->hkl
;
399 pKbl
= (PKBL
) pKbl
->List
.Flink
;
400 if(pKbl
== KBLList
) break;
406 SetLastNtError(_SEH_GetExceptionCode());
418 NtUserGetKeyboardLayoutName(
428 ProbeForWrite(lpszName
, KL_NAMELENGTH
*sizeof(WCHAR
), 1);
429 pKbl
= PsGetCurrentThreadWin32Thread()->KeyboardLayout
;
430 RtlCopyMemory(lpszName
, pKbl
->Name
, KL_NAMELENGTH
*sizeof(WCHAR
));
435 SetLastNtError(_SEH_GetExceptionCode());
447 NtUserLoadKeyboardLayoutEx(
456 PKBL pKbl
= NULL
, Cur
;
458 UserEnterExclusive();
463 if(Cur
->klid
== dwKLID
)
469 Cur
= (PKBL
) Cur
->List
.Flink
;
470 } while(Cur
!= KBLList
);
474 pKbl
= UserLoadDllAndCreateKbl(dwKLID
);
481 InsertTailList(&KBLList
->List
, &pKbl
->List
);
484 if(Flags
& KLF_REORDER
) KBLList
= pKbl
;
486 if(Flags
& KLF_ACTIVATE
)
488 co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKbl
);
493 //FIXME: Respect Flags!
494 // KLF_NOTELLSHELL KLF_SETFORPROCESS
495 // KLF_REPLACELANG KLF_SUBSTITUTE_OK
504 NtUserActivateKeyboardLayout(
512 UserEnterExclusive();
514 pWThread
= PsGetCurrentThreadWin32Thread();
516 if(pWThread
->KeyboardLayout
->hkl
== hKl
)
522 if(hKl
== (HKL
)HKL_NEXT
)
524 pKbl
= (PKBL
)pWThread
->KeyboardLayout
->List
.Flink
;
526 else if(hKl
== (HKL
)HKL_PREV
)
528 pKbl
= (PKBL
)pWThread
->KeyboardLayout
->List
.Blink
;
530 else pKbl
= UserHklToKbl(hKl
);
532 //FIXME: KLF_RESET, KLF_SHIFTLOCK
533 //FIXME: KLF_SETFORPROCESS
537 if(Flags
& KLF_REORDER
)
540 if(pKbl
== pWThread
->KeyboardLayout
)
546 pKbl
= co_UserActivateKbl(pWThread
, pKbl
);