-\r
-/*\r
- * PROJECT: ReactOS Kernel\r
- * LICENSE: GPL - See COPYING in the top level directory\r
- * FILE: subsystems/win32/win32k/ntuser/kbdlayout.c\r
- * PURPOSE: Keyboard layout management\r
- * COPYRIGHT: Copyright 2007 Saveliy Tretiakov\r
- * Copyright 2008 Colin Finck\r
- *\r
- */\r
-\r
-\r
-/* INCLUDES ******************************************************************/\r
-\r
-#include <w32k.h>\r
-\r
-#define NDEBUG\r
-#include <debug.h>\r
-\r
-PKBL KBLList = NULL; // Keyboard layout list.\r
-\r
-typedef PVOID (*KbdLayerDescriptor)(VOID);\r
-NTSTATUS APIENTRY LdrGetProcedureAddress(PVOID module,\r
- PANSI_STRING import_name,\r
- DWORD flags,\r
- PVOID *func_addr);\r
-\r
-\r
-\r
-/* PRIVATE FUNCTIONS ******************************************************/\r
-\r
-\r
-/*\r
- * Utility function to read a value from the registry more easily.\r
- *\r
- * IN PUNICODE_STRING KeyName -> Name of key to open\r
- * IN PUNICODE_STRING ValueName -> Name of value to open\r
- * OUT PUNICODE_STRING ReturnedValue -> String contained in registry\r
- *\r
- * Returns NTSTATUS\r
- */\r
-\r
-static NTSTATUS APIENTRY ReadRegistryValue( PUNICODE_STRING KeyName,\r
- PUNICODE_STRING ValueName,\r
- PUNICODE_STRING ReturnedValue )\r
-{\r
- NTSTATUS Status;\r
- HANDLE KeyHandle;\r
- OBJECT_ATTRIBUTES KeyAttributes;\r
- PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;\r
- ULONG Length = 0;\r
- ULONG ResLength = 0;\r
- PWCHAR ReturnBuffer;\r
-\r
- InitializeObjectAttributes(&KeyAttributes, KeyName, OBJ_CASE_INSENSITIVE,\r
- NULL, NULL);\r
- Status = ZwOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);\r
- if( !NT_SUCCESS(Status) )\r
- {\r
- return Status;\r
- }\r
-\r
- Status = ZwQueryValueKey(KeyHandle, ValueName, KeyValuePartialInformation,\r
- 0,\r
- 0,\r
- &ResLength);\r
-\r
- if( Status != STATUS_BUFFER_TOO_SMALL )\r
- {\r
- NtClose(KeyHandle);\r
- return Status;\r
- }\r
-\r
- ResLength += sizeof( *KeyValuePartialInfo );\r
- KeyValuePartialInfo =\r
- ExAllocatePoolWithTag(PagedPool, ResLength, TAG_STRING);\r
- Length = ResLength;\r
-\r
- if( !KeyValuePartialInfo )\r
- {\r
- NtClose(KeyHandle);\r
- return STATUS_NO_MEMORY;\r
- }\r
-\r
- Status = ZwQueryValueKey(KeyHandle,\r
- ValueName,\r
- KeyValuePartialInformation,\r
- (PVOID)KeyValuePartialInfo,\r
- Length,\r
- &ResLength);\r
-\r
- if( !NT_SUCCESS(Status) )\r
- {\r
- NtClose(KeyHandle);\r
- ExFreePoolWithTag(KeyValuePartialInfo, TAG_STRING);\r
- return Status;\r
- }\r
-\r
- /* At this point, KeyValuePartialInfo->Data contains the key data */\r
- ReturnBuffer = ExAllocatePoolWithTag(PagedPool,\r
- KeyValuePartialInfo->DataLength,\r
- TAG_STRING);\r
-\r
- if(!ReturnBuffer)\r
- {\r
- NtClose(KeyHandle);\r
- ExFreePoolWithTag(KeyValuePartialInfo, TAG_STRING);\r
- return STATUS_NO_MEMORY;\r
- }\r
-\r
- RtlCopyMemory(ReturnBuffer,\r
- KeyValuePartialInfo->Data,\r
- KeyValuePartialInfo->DataLength);\r
- RtlInitUnicodeString(ReturnedValue, ReturnBuffer);\r
-\r
- ExFreePoolWithTag(KeyValuePartialInfo, TAG_STRING);\r
- NtClose(KeyHandle);\r
-\r
- return Status;\r
-}\r
-\r
-static BOOL UserLoadKbdDll(WCHAR *wsKLID,\r
- HANDLE *phModule,\r
- PKBDTABLES *pKbdTables)\r
-{\r
- NTSTATUS Status;\r
- KbdLayerDescriptor layerDescGetFn;\r
- ANSI_STRING kbdProcedureName;\r
- UNICODE_STRING LayoutKeyName;\r
- UNICODE_STRING LayoutValueName;\r
- UNICODE_STRING LayoutFile;\r
- UNICODE_STRING FullLayoutPath;\r
- UNICODE_STRING klid;\r
- WCHAR LayoutPathBuffer[MAX_PATH] = L"\\SystemRoot\\System32\\";\r
- WCHAR KeyNameBuffer[MAX_PATH] = L"\\REGISTRY\\Machine\\SYSTEM\\"\r
- L"CurrentControlSet\\Control\\Keyboard Layouts\\";\r
-\r
- RtlInitUnicodeString(&klid, wsKLID);\r
- RtlInitUnicodeString(&LayoutValueName,L"Layout File");\r
- RtlInitUnicodeString(&LayoutKeyName, KeyNameBuffer);\r
- LayoutKeyName.MaximumLength = sizeof(KeyNameBuffer);\r
-\r
- RtlAppendUnicodeStringToString(&LayoutKeyName, &klid);\r
- Status = ReadRegistryValue(&LayoutKeyName, &LayoutValueName, &LayoutFile);\r
-\r
- if(!NT_SUCCESS(Status))\r
- {\r
- DPRINT("Can't get layout filename for %wZ. (%08lx)\n", klid, Status);\r
- return FALSE;\r
- }\r
-\r
- DPRINT("Read registry and got %wZ\n", &LayoutFile);\r
- RtlInitUnicodeString(&FullLayoutPath, LayoutPathBuffer);\r
- FullLayoutPath.MaximumLength = sizeof(LayoutPathBuffer);\r
- RtlAppendUnicodeStringToString(&FullLayoutPath, &LayoutFile);\r
- DPRINT("Loading Keyboard DLL %wZ\n", &FullLayoutPath);\r
- ExFreePoolWithTag(LayoutFile.Buffer, TAG_STRING);\r
-\r
- *phModule = EngLoadImage(FullLayoutPath.Buffer);\r
-\r
- if(*phModule)\r
- {\r
- DPRINT("Loaded %wZ\n", &FullLayoutPath);\r
-\r
- RtlInitAnsiString( &kbdProcedureName, "KbdLayerDescriptor" );\r
- LdrGetProcedureAddress((PVOID)*phModule,\r
- &kbdProcedureName,\r
- 0,\r
- (PVOID*)&layerDescGetFn);\r
-\r
- if(layerDescGetFn)\r
- {\r
- *pKbdTables = layerDescGetFn();\r
- }\r
- else\r
- {\r
- DPRINT1("Error: %wZ has no KbdLayerDescriptor()\n", &FullLayoutPath);\r
- }\r
-\r
- if(!layerDescGetFn || !*pKbdTables)\r
- {\r
- DPRINT1("Failed to load the keyboard layout.\n");\r
- EngUnloadImage(*phModule);\r
- return FALSE;\r
- }\r
- }\r
- else\r
- {\r
- DPRINT1("Failed to load dll %wZ\n", &FullLayoutPath);\r
- return FALSE;\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-static PKBL UserLoadDllAndCreateKbl(DWORD LocaleId)\r
-{\r
- PKBL NewKbl;\r
- ULONG hKl;\r
- LANGID langid;\r
-\r
- NewKbl = ExAllocatePool(PagedPool, sizeof(KBL));\r
-\r
- if(!NewKbl)\r
- {\r
- DPRINT1("%s: Can't allocate memory!\n", __FUNCTION__);\r
- return NULL;\r
- }\r
-\r
- swprintf(NewKbl->Name, L"%08lx", LocaleId);\r
-\r
- if(!UserLoadKbdDll(NewKbl->Name, &NewKbl->hModule, &NewKbl->KBTables))\r
- {\r
- DPRINT("%s: failed to load %x dll!\n", __FUNCTION__, LocaleId);\r
- ExFreePool(NewKbl);\r
- return NULL;\r
- }\r
-\r
- /* Microsoft Office expects this value to be something specific\r
- * for Japanese and Korean Windows with an IME the value is 0xe001\r
- * We should probably check to see if an IME exists and if so then\r
- * set this word properly.\r
- */\r
- langid = PRIMARYLANGID(LANGIDFROMLCID(LocaleId));\r
- hKl = LocaleId;\r
-\r
- if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)\r
- hKl |= 0xe001 << 16; /* FIXME */\r
- else hKl |= hKl << 16;\r
-\r
- NewKbl->hkl = (HKL)(ULONG_PTR) hKl;\r
- NewKbl->klid = LocaleId;\r
- NewKbl->Flags = 0;\r
- NewKbl->RefCount = 0;\r
-\r
- return NewKbl;\r
-}\r
-\r
-BOOL UserInitDefaultKeyboardLayout()\r
-{\r
- LCID LocaleId;\r
- NTSTATUS Status;\r
-\r
- Status = ZwQueryDefaultLocale(FALSE, &LocaleId);\r
- if (!NT_SUCCESS(Status))\r
- {\r
- DPRINT1("Could not get default locale (%08lx).\n", Status);\r
- }\r
- else\r
- {\r
- DPRINT("DefaultLocale = %08lx\n", LocaleId);\r
- }\r
-\r
- if(!NT_SUCCESS(Status) || !(KBLList = UserLoadDllAndCreateKbl(LocaleId)))\r
- {\r
- DPRINT1("Trying to load US Keyboard Layout.\n");\r
- LocaleId = 0x409;\r
-\r
- if(!(KBLList = UserLoadDllAndCreateKbl(LocaleId)))\r
- {\r
- DPRINT1("Failed to load any Keyboard Layout\n");\r
- return FALSE;\r
- }\r
- }\r
-\r
- KBLList->Flags |= KBL_PRELOAD;\r
-\r
- InitializeListHead(&KBLList->List);\r
- return TRUE;\r
-}\r
-\r
-PKBL W32kGetDefaultKeyLayout(VOID)\r
-{\r
- const WCHAR szKeyboardLayoutPath[] = L"\\Keyboard Layout\\Preload";\r
- const WCHAR szDefaultUserPath[] = L"\\REGISTRY\\USER\\.DEFAULT";\r
-\r
- HANDLE KeyHandle;\r
- LCID LayoutLocaleId = 0;\r
- NTSTATUS Status;\r
- OBJECT_ATTRIBUTES KeyAttributes;\r
- PKBL pKbl;\r
- UNICODE_STRING CurrentUserPath;\r
- UNICODE_STRING FullKeyboardLayoutPath;\r
- UNICODE_STRING LayoutValueName;\r
- UNICODE_STRING LayoutLocaleIdString;\r
- WCHAR wszBuffer[MAX_PATH];\r
-\r
- // Get the path to HKEY_CURRENT_USER\r
- Status = RtlFormatCurrentUserKeyPath(&CurrentUserPath);\r
-\r
- if( NT_SUCCESS(Status) )\r
- {\r
- // FIXME: Is this 100% correct?\r
- // We're called very early, so HKEY_CURRENT_USER might not be available yet. Check this first.\r
- InitializeObjectAttributes(&KeyAttributes, &CurrentUserPath, OBJ_CASE_INSENSITIVE, NULL, NULL);\r
- Status = ZwOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);\r
-\r
- if(Status == STATUS_OBJECT_NAME_NOT_FOUND)\r
- {\r
- // It is not available, so read it from HKEY_USERS\.DEFAULT\r
- RtlCopyMemory(wszBuffer, szDefaultUserPath, sizeof(szDefaultUserPath));\r
- }\r
- else\r
- {\r
- // The path is available\r
- ZwClose(KeyHandle);\r
- RtlCopyMemory(wszBuffer, CurrentUserPath.Buffer, CurrentUserPath.MaximumLength);\r
- }\r
-\r
- // Build the full path\r
- RtlInitUnicodeString(&FullKeyboardLayoutPath, wszBuffer);\r
- FullKeyboardLayoutPath.MaximumLength = MAX_PATH;\r
-\r
- Status = RtlAppendUnicodeToString(&FullKeyboardLayoutPath, szKeyboardLayoutPath);\r
-\r
- if( NT_SUCCESS(Status) )\r
- {\r
- // Return the first keyboard layout listed there\r
- RtlInitUnicodeString(&LayoutValueName, L"1");\r
-\r
- Status = ReadRegistryValue(&FullKeyboardLayoutPath, &LayoutValueName, &LayoutLocaleIdString);\r
-\r
- if( NT_SUCCESS(Status) )\r
- {\r
- RtlUnicodeStringToInteger(&LayoutLocaleIdString, 16, &LayoutLocaleId);\r
- ExFreePoolWithTag(LayoutLocaleIdString.Buffer, TAG_STRING);\r
- }\r
- else\r
- DPRINT1("ReadRegistryValue failed! (%08lx).\n", Status);\r
- }\r
- else\r
- DPRINT1("RtlAppendUnicodeToString failed! (%08lx)\n", Status);\r
-\r
- RtlFreeUnicodeString(&CurrentUserPath);\r
- }\r
- else\r
- DPRINT1("RtlFormatCurrentUserKeyPath failed! (%08lx)\n", Status);\r
-\r
- if(!LayoutLocaleId)\r
- {\r
- // This block is only reached in case of a failure, so use DPRINT1 here\r
- DPRINT1("Assuming default locale for the keyboard layout (0x409 - US)\n");\r
- LayoutLocaleId = 0x409;\r
- }\r
-\r
- pKbl = KBLList;\r
- do\r
- {\r
- if(pKbl->klid == LayoutLocaleId)\r
- {\r
- return pKbl;\r
- }\r
-\r
- pKbl = (PKBL) pKbl->List.Flink;\r
- } while(pKbl != KBLList);\r
-\r
- DPRINT("Loading new default keyboard layout.\n");\r
- pKbl = UserLoadDllAndCreateKbl(LayoutLocaleId);\r
-\r
- if(!pKbl)\r
- {\r
- DPRINT("Failed to load %x!!! Returning any availableKL.\n", LayoutLocaleId);\r
- return KBLList;\r
- }\r
-\r
- InsertTailList(&KBLList->List, &pKbl->List);\r
- return pKbl;\r
-}\r
-\r
-PKBL UserHklToKbl(HKL hKl)\r
-{\r
- PKBL pKbl = KBLList;\r
- do\r
- {\r
- if(pKbl->hkl == hKl) return pKbl;\r
- pKbl = (PKBL) pKbl->List.Flink;\r
- } while(pKbl != KBLList);\r
-\r
- return NULL;\r
-}\r
-\r
-BOOL UserUnloadKbl(PKBL pKbl)\r
-{\r
- /* According to msdn, UnloadKeyboardLayout can fail\r
- if the keyboard layout identifier was preloaded. */\r
-\r
- if(pKbl->Flags & KBL_PRELOAD)\r
- {\r
- DPRINT1("Attempted to unload preloaded keyboard layout.\n");\r
- return FALSE;\r
- }\r
-\r
- if(pKbl->RefCount > 0)\r
- {\r
- /* Layout is used by other threads.\r
- Mark it as unloaded and don't do anything else. */\r
- pKbl->Flags |= KBL_UNLOAD;\r
- }\r
- else\r
- {\r
- //Unload the layout\r
- EngUnloadImage(pKbl->hModule);\r
- RemoveEntryList(&pKbl->List);\r
- ExFreePool(pKbl);\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-static PKBL co_UserActivateKbl(PTHREADINFO w32Thread, PKBL pKbl, UINT Flags)\r
-{\r
- PKBL Prev;\r
-\r
- Prev = w32Thread->KeyboardLayout;\r
- Prev->RefCount--;\r
- w32Thread->KeyboardLayout = pKbl;\r
- pKbl->RefCount++;\r
-\r
- if(Flags & KLF_SETFORPROCESS)\r
- {\r
- //FIXME\r
-\r
- }\r
-\r
- if(Prev->Flags & KBL_UNLOAD && Prev->RefCount == 0)\r
- {\r
- UserUnloadKbl(Prev);\r
- }\r
-\r
- // Send WM_INPUTLANGCHANGE to thread's focus window\r
- co_IntSendMessage(w32Thread->MessageQueue->FocusWindow,\r
- WM_INPUTLANGCHANGE,\r
- 0, // FIXME: put charset here (what is this?)\r
- (LPARAM)pKbl->hkl); //klid\r
-\r
- return Prev;\r
-}\r
-\r
-/* EXPORTS *******************************************************************/\r
-\r
-HKL FASTCALL\r
-UserGetKeyboardLayout(\r
- DWORD dwThreadId)\r
-{\r
- NTSTATUS Status;\r
- PETHREAD Thread;\r
- PTHREADINFO W32Thread;\r
- HKL Ret;\r
-\r
- if(!dwThreadId)\r
- {\r
- W32Thread = PsGetCurrentThreadWin32Thread();\r
- return W32Thread->KeyboardLayout->hkl;\r
- }\r
-\r
- Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);\r
- if(!NT_SUCCESS(Status))\r
- {\r
- SetLastWin32Error(ERROR_INVALID_PARAMETER);\r
- return NULL;\r
- }\r
-\r
- W32Thread = PsGetThreadWin32Thread(Thread);\r
- Ret = W32Thread->KeyboardLayout->hkl;\r
- ObDereferenceObject(Thread);\r
- return Ret;\r
-}\r
-\r
-UINT\r
-APIENTRY\r
-NtUserGetKeyboardLayoutList(\r
- INT nItems,\r
- HKL* pHklBuff)\r
-{\r
- UINT Ret = 0;\r
- PKBL pKbl;\r
-\r
- UserEnterShared();\r
- pKbl = KBLList;\r
-\r
- if(nItems == 0)\r
- {\r
- do\r
- {\r
- Ret++;\r
- pKbl = (PKBL) pKbl->List.Flink;\r
- } while(pKbl != KBLList);\r
- }\r
- else\r
- {\r
- _SEH2_TRY\r
- {\r
- ProbeForWrite(pHklBuff, nItems*sizeof(HKL), 4);\r
-\r
- while(Ret < nItems)\r
- {\r
- if(!(pKbl->Flags & KBL_UNLOAD))\r
- {\r
- pHklBuff[Ret] = pKbl->hkl;\r
- Ret++;\r
- pKbl = (PKBL) pKbl->List.Flink;\r
- if(pKbl == KBLList) break;\r
- }\r
- }\r
-\r
- }\r
- _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)\r
- {\r
- SetLastNtError(_SEH2_GetExceptionCode());\r
- Ret = 0;\r
- }\r
- _SEH2_END;\r
- }\r
-\r
- UserLeave();\r
- return Ret;\r
-}\r
-\r
-BOOL\r
-APIENTRY\r
-NtUserGetKeyboardLayoutName(\r
- LPWSTR lpszName)\r
-{\r
- BOOL ret = FALSE;\r
- PKBL pKbl;\r
- PTHREADINFO pti;\r
-\r
- UserEnterShared();\r
-\r
- _SEH2_TRY\r
- {\r
- ProbeForWrite(lpszName, KL_NAMELENGTH*sizeof(WCHAR), 1);\r
- pti = PsGetCurrentThreadWin32Thread();\r
- pKbl = pti->KeyboardLayout;\r
- RtlCopyMemory(lpszName, pKbl->Name, KL_NAMELENGTH*sizeof(WCHAR));\r
- ret = TRUE;\r
- }\r
- _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)\r
- {\r
- SetLastNtError(_SEH2_GetExceptionCode());\r
- ret = FALSE;\r
- }\r
- _SEH2_END;\r
-\r
- UserLeave();\r
- return ret;\r
-}\r
-\r
-\r
-HKL\r
-APIENTRY\r
-NtUserLoadKeyboardLayoutEx(\r
- IN HANDLE Handle,\r
- IN DWORD offTable,\r
- IN PUNICODE_STRING puszKeyboardName,\r
- IN HKL hKL,\r
- IN PUNICODE_STRING puszKLID,\r
- IN DWORD dwKLID,\r
- IN UINT Flags)\r
-{\r
- HKL Ret = NULL;\r
- PKBL pKbl = NULL, Cur;\r
-\r
- UserEnterExclusive();\r
-\r
- //Let's see if layout was already loaded.\r
- Cur = KBLList;\r
- do\r
- {\r
- if(Cur->klid == dwKLID)\r
- {\r
- pKbl = Cur;\r
- pKbl->Flags &= ~KBL_UNLOAD;\r
- break;\r
- }\r
-\r
- Cur = (PKBL) Cur->List.Flink;\r
- } while(Cur != KBLList);\r
-\r
- //It wasn't, so load it.\r
- if(!pKbl)\r
- {\r
- pKbl = UserLoadDllAndCreateKbl(dwKLID);\r
-\r
- if(!pKbl)\r
- {\r
- goto the_end;\r
- }\r
-\r
- InsertTailList(&KBLList->List, &pKbl->List);\r
- }\r
-\r
- if(Flags & KLF_REORDER) KBLList = pKbl;\r
-\r
- if(Flags & KLF_ACTIVATE)\r
- {\r
- co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKbl, Flags);\r
- }\r
-\r
- Ret = pKbl->hkl;\r
-\r
- //FIXME: KLF_NOTELLSHELL\r
- // KLF_REPLACELANG\r
- // KLF_SUBSTITUTE_OK\r
-\r
-the_end:\r
- UserLeave();\r
- return Ret;\r
-}\r
-\r
-HKL\r
-APIENTRY\r
-NtUserActivateKeyboardLayout(\r
- HKL hKl,\r
- ULONG Flags)\r
-{\r
- PKBL pKbl;\r
- HKL Ret = NULL;\r
- PTHREADINFO pWThread;\r
-\r
- UserEnterExclusive();\r
-\r
- pWThread = PsGetCurrentThreadWin32Thread();\r
-\r
- if(pWThread->KeyboardLayout->hkl == hKl)\r
- {\r
- Ret = hKl;\r
- goto the_end;\r
- }\r
-\r
- if(hKl == (HKL)HKL_NEXT)\r
- {\r
- pKbl = (PKBL)pWThread->KeyboardLayout->List.Flink;\r
- }\r
- else if(hKl == (HKL)HKL_PREV)\r
- {\r
- pKbl = (PKBL)pWThread->KeyboardLayout->List.Blink;\r
- }\r
- else pKbl = UserHklToKbl(hKl);\r
-\r
- //FIXME: KLF_RESET, KLF_SHIFTLOCK\r
-\r
- if(pKbl)\r
- {\r
- if(Flags & KLF_REORDER)\r
- KBLList = pKbl;\r
-\r
- if(pKbl == pWThread->KeyboardLayout)\r
- {\r
- Ret = pKbl->hkl;\r
- }\r
- else\r
- {\r
- pKbl = co_UserActivateKbl(pWThread, pKbl, Flags);\r
- Ret = pKbl->hkl;\r
- }\r
- }\r
- else\r
- {\r
- DPRINT1("%s: Invalid HKL %x!\n", __FUNCTION__, hKl);\r
- }\r
-\r
-the_end:\r
- UserLeave();\r
- return Ret;\r
-}\r
-\r
-BOOL\r
-APIENTRY\r
-NtUserUnloadKeyboardLayout(\r
- HKL hKl)\r
-{\r
- PKBL pKbl;\r
- BOOL Ret = FALSE;\r
-\r
- UserEnterExclusive();\r
-\r
- if((pKbl = UserHklToKbl(hKl)))\r
- {\r
- Ret = UserUnloadKbl(pKbl);\r
- }\r
- else\r
- {\r
- DPRINT1("%s: Invalid HKL %x!\n", __FUNCTION__, hKl);\r
- }\r
-\r
- UserLeave();\r
- return Ret;\r
-}\r
-\r
-/* EOF */\r
+
+/*
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: subsystems/win32/win32k/ntuser/kbdlayout.c
+ * PURPOSE: Keyboard layout management
+ * COPYRIGHT: Copyright 2007 Saveliy Tretiakov
+ * Copyright 2008 Colin Finck
+ *
+ */
+
+
+/* INCLUDES ******************************************************************/
+
+#include <win32k.h>
+
+#define NDEBUG
+#include <debug.h>
+
+PKBL KBLList = NULL; // Keyboard layout list.
+
+typedef PVOID (*KbdLayerDescriptor)(VOID);
+NTSTATUS APIENTRY LdrGetProcedureAddress(PVOID module,
+ PANSI_STRING import_name,
+ DWORD flags,
+ PVOID *func_addr);
+
+
+
+/* PRIVATE FUNCTIONS ******************************************************/
+
+
+/*
+ * Utility function to read a value from the registry more easily.
+ *
+ * IN PUNICODE_STRING KeyName -> Name of key to open
+ * IN PUNICODE_STRING ValueName -> Name of value to open
+ * OUT PUNICODE_STRING ReturnedValue -> String contained in registry
+ *
+ * Returns NTSTATUS
+ */
+
+static NTSTATUS APIENTRY ReadRegistryValue( PUNICODE_STRING KeyName,
+ PUNICODE_STRING ValueName,
+ PUNICODE_STRING ReturnedValue )
+{
+ NTSTATUS Status;
+ HANDLE KeyHandle;
+ OBJECT_ATTRIBUTES KeyAttributes;
+ PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
+ ULONG Length = 0;
+ ULONG ResLength = 0;
+ PWCHAR ReturnBuffer;
+
+ InitializeObjectAttributes(&KeyAttributes, KeyName, OBJ_CASE_INSENSITIVE,
+ NULL, NULL);
+ Status = ZwOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
+ if( !NT_SUCCESS(Status) )
+ {
+ return Status;
+ }
+
+ Status = ZwQueryValueKey(KeyHandle, ValueName, KeyValuePartialInformation,
+ 0,
+ 0,
+ &ResLength);
+
+ if( Status != STATUS_BUFFER_TOO_SMALL )
+ {
+ NtClose(KeyHandle);
+ return Status;
+ }
+
+ ResLength += sizeof( *KeyValuePartialInfo );
+ KeyValuePartialInfo =
+ ExAllocatePoolWithTag(PagedPool, ResLength, TAG_STRING);
+ Length = ResLength;
+
+ if( !KeyValuePartialInfo )
+ {
+ NtClose(KeyHandle);
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = ZwQueryValueKey(KeyHandle,
+ ValueName,
+ KeyValuePartialInformation,
+ (PVOID)KeyValuePartialInfo,
+ Length,
+ &ResLength);
+
+ if( !NT_SUCCESS(Status) )
+ {
+ NtClose(KeyHandle);
+ ExFreePoolWithTag(KeyValuePartialInfo, TAG_STRING);
+ return Status;
+ }
+
+ /* At this point, KeyValuePartialInfo->Data contains the key data */
+ ReturnBuffer = ExAllocatePoolWithTag(PagedPool,
+ KeyValuePartialInfo->DataLength,
+ TAG_STRING);
+
+ if(!ReturnBuffer)
+ {
+ NtClose(KeyHandle);
+ ExFreePoolWithTag(KeyValuePartialInfo, TAG_STRING);
+ return STATUS_NO_MEMORY;
+ }
+
+ RtlCopyMemory(ReturnBuffer,
+ KeyValuePartialInfo->Data,
+ KeyValuePartialInfo->DataLength);
+ RtlInitUnicodeString(ReturnedValue, ReturnBuffer);
+
+ ExFreePoolWithTag(KeyValuePartialInfo, TAG_STRING);
+ NtClose(KeyHandle);
+
+ return Status;
+}
+
+static BOOL UserLoadKbdDll(WCHAR *wsKLID,
+ HANDLE *phModule,
+ PKBDTABLES *pKbdTables)
+{
+ NTSTATUS Status;
+ KbdLayerDescriptor layerDescGetFn;
+ ANSI_STRING kbdProcedureName;
+ UNICODE_STRING LayoutKeyName;
+ UNICODE_STRING LayoutValueName;
+ UNICODE_STRING LayoutFile;
+ UNICODE_STRING FullLayoutPath;
+ UNICODE_STRING klid;
+ WCHAR LayoutPathBuffer[MAX_PATH] = L"\\SystemRoot\\System32\\";
+ WCHAR KeyNameBuffer[MAX_PATH] = L"\\REGISTRY\\Machine\\SYSTEM\\"
+ L"CurrentControlSet\\Control\\Keyboard Layouts\\";
+
+ RtlInitUnicodeString(&klid, wsKLID);
+ RtlInitUnicodeString(&LayoutValueName,L"Layout File");
+ RtlInitUnicodeString(&LayoutKeyName, KeyNameBuffer);
+ LayoutKeyName.MaximumLength = sizeof(KeyNameBuffer);
+
+ RtlAppendUnicodeStringToString(&LayoutKeyName, &klid);
+ Status = ReadRegistryValue(&LayoutKeyName, &LayoutValueName, &LayoutFile);
+
+ if(!NT_SUCCESS(Status))
+ {
+ DPRINT("Can't get layout filename for %wZ. (%08lx)\n", klid, Status);
+ return FALSE;
+ }
+
+ DPRINT("Read registry and got %wZ\n", &LayoutFile);
+ RtlInitUnicodeString(&FullLayoutPath, LayoutPathBuffer);
+ FullLayoutPath.MaximumLength = sizeof(LayoutPathBuffer);
+ RtlAppendUnicodeStringToString(&FullLayoutPath, &LayoutFile);
+ DPRINT("Loading Keyboard DLL %wZ\n", &FullLayoutPath);
+ ExFreePoolWithTag(LayoutFile.Buffer, TAG_STRING);
+
+ *phModule = EngLoadImage(FullLayoutPath.Buffer);
+
+ if(*phModule)
+ {
+ DPRINT("Loaded %wZ\n", &FullLayoutPath);
+
+ RtlInitAnsiString( &kbdProcedureName, "KbdLayerDescriptor" );
+ layerDescGetFn = EngFindImageProcAddress(*phModule, "KbdLayerDescriptor");
+
+ if(layerDescGetFn)
+ {
+ *pKbdTables = layerDescGetFn();
+ }
+ else
+ {
+ DPRINT1("Error: %wZ has no KbdLayerDescriptor()\n", &FullLayoutPath);
+ }
+
+ if(!layerDescGetFn || !*pKbdTables)
+ {
+ DPRINT1("Failed to load the keyboard layout.\n");
+ EngUnloadImage(*phModule);
+ return FALSE;
+ }
+ }
+ else
+ {
+ DPRINT1("Failed to load dll %wZ\n", &FullLayoutPath);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static PKBL UserLoadDllAndCreateKbl(DWORD LocaleId)
+{
+ PKBL NewKbl;
+ ULONG hKl;
+ LANGID langid;
+
+ NewKbl = ExAllocatePoolWithTag(PagedPool, sizeof(KBL), TAG_KEYBOARD);
+
+ if(!NewKbl)
+ {
+ DPRINT1("%s: Can't allocate memory!\n", __FUNCTION__);
+ return NULL;
+ }
+
+ swprintf(NewKbl->Name, L"%08lx", LocaleId);
+
+ if(!UserLoadKbdDll(NewKbl->Name, &NewKbl->hModule, &NewKbl->KBTables))
+ {
+ DPRINT("%s: failed to load %x dll!\n", __FUNCTION__, LocaleId);
+ ExFreePoolWithTag(NewKbl, TAG_KEYBOARD);
+ return NULL;
+ }
+
+ /* Microsoft Office expects this value to be something specific
+ * for Japanese and Korean Windows with an IME the value is 0xe001
+ * We should probably check to see if an IME exists and if so then
+ * set this word properly.
+ */
+ langid = PRIMARYLANGID(LANGIDFROMLCID(LocaleId));
+ hKl = LocaleId;
+
+ if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
+ hKl |= 0xe001 << 16; /* FIXME */
+ else hKl |= hKl << 16;
+
+ NewKbl->hkl = (HKL)(ULONG_PTR) hKl;
+ NewKbl->klid = LocaleId;
+ NewKbl->Flags = 0;
+ NewKbl->RefCount = 0;
+
+ return NewKbl;
+}
+
+BOOL UserInitDefaultKeyboardLayout()
+{
+ LCID LocaleId;
+ NTSTATUS Status;
+
+ Status = ZwQueryDefaultLocale(FALSE, &LocaleId);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Could not get default locale (%08lx).\n", Status);
+ }
+ else
+ {
+ DPRINT("DefaultLocale = %08lx\n", LocaleId);
+ }
+
+ if(!NT_SUCCESS(Status) || !(KBLList = UserLoadDllAndCreateKbl(LocaleId)))
+ {
+ DPRINT1("Trying to load US Keyboard Layout.\n");
+ LocaleId = 0x409;
+
+ if(!(KBLList = UserLoadDllAndCreateKbl(LocaleId)))
+ {
+ DPRINT1("Failed to load any Keyboard Layout\n");
+ return FALSE;
+ }
+ }
+
+ KBLList->Flags |= KBL_PRELOAD;
+
+ InitializeListHead(&KBLList->List);
+ return TRUE;
+}
+
+PKBL W32kGetDefaultKeyLayout(VOID)
+{
+ const WCHAR szKeyboardLayoutPath[] = L"\\Keyboard Layout\\Preload";
+ const WCHAR szDefaultUserPath[] = L"\\REGISTRY\\USER\\.DEFAULT";
+
+ HANDLE KeyHandle;
+ LCID LayoutLocaleId = 0;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES KeyAttributes;
+ PKBL pKbl;
+ UNICODE_STRING CurrentUserPath;
+ UNICODE_STRING FullKeyboardLayoutPath;
+ UNICODE_STRING LayoutValueName;
+ UNICODE_STRING LayoutLocaleIdString;
+ WCHAR wszBuffer[MAX_PATH];
+
+ // Get the path to HKEY_CURRENT_USER
+ Status = RtlFormatCurrentUserKeyPath(&CurrentUserPath);
+
+ if( NT_SUCCESS(Status) )
+ {
+ // FIXME: Is this 100% correct?
+ // We're called very early, so HKEY_CURRENT_USER might not be available yet. Check this first.
+ InitializeObjectAttributes(&KeyAttributes, &CurrentUserPath, OBJ_CASE_INSENSITIVE, NULL, NULL);
+ Status = ZwOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
+
+ if(Status == STATUS_OBJECT_NAME_NOT_FOUND)
+ {
+ // It is not available, so read it from HKEY_USERS\.DEFAULT
+ RtlCopyMemory(wszBuffer, szDefaultUserPath, sizeof(szDefaultUserPath));
+ }
+ else
+ {
+ // The path is available
+ ZwClose(KeyHandle);
+ RtlCopyMemory(wszBuffer, CurrentUserPath.Buffer, CurrentUserPath.MaximumLength);
+ }
+
+ // Build the full path
+ RtlInitUnicodeString(&FullKeyboardLayoutPath, wszBuffer);
+ FullKeyboardLayoutPath.MaximumLength = MAX_PATH;
+
+ Status = RtlAppendUnicodeToString(&FullKeyboardLayoutPath, szKeyboardLayoutPath);
+
+ if( NT_SUCCESS(Status) )
+ {
+ // Return the first keyboard layout listed there
+ RtlInitUnicodeString(&LayoutValueName, L"1");
+
+ Status = ReadRegistryValue(&FullKeyboardLayoutPath, &LayoutValueName, &LayoutLocaleIdString);
+
+ if( NT_SUCCESS(Status) )
+ {
+ RtlUnicodeStringToInteger(&LayoutLocaleIdString, 16, &LayoutLocaleId);
+ ExFreePoolWithTag(LayoutLocaleIdString.Buffer, TAG_STRING);
+ }
+ else
+ DPRINT1("ReadRegistryValue failed! (%08lx).\n", Status);
+ }
+ else
+ DPRINT1("RtlAppendUnicodeToString failed! (%08lx)\n", Status);
+
+ RtlFreeUnicodeString(&CurrentUserPath);
+ }
+ else
+ DPRINT1("RtlFormatCurrentUserKeyPath failed! (%08lx)\n", Status);
+
+ if(!LayoutLocaleId)
+ {
+ // This block is only reached in case of a failure, so use DPRINT1 here
+ DPRINT1("Assuming default locale for the keyboard layout (0x409 - US)\n");
+ LayoutLocaleId = 0x409;
+ }
+
+ pKbl = KBLList;
+ do
+ {
+ if(pKbl->klid == LayoutLocaleId)
+ {
+ return pKbl;
+ }
+
+ pKbl = (PKBL) pKbl->List.Flink;
+ } while(pKbl != KBLList);
+
+ DPRINT("Loading new default keyboard layout.\n");
+ pKbl = UserLoadDllAndCreateKbl(LayoutLocaleId);
+
+ if(!pKbl)
+ {
+ DPRINT("Failed to load %x!!! Returning any availableKL.\n", LayoutLocaleId);
+ return KBLList;
+ }
+
+ InsertTailList(&KBLList->List, &pKbl->List);
+ return pKbl;
+}
+
+PKBL UserHklToKbl(HKL hKl)
+{
+ PKBL pKbl = KBLList;
+ do
+ {
+ if(pKbl->hkl == hKl) return pKbl;
+ pKbl = (PKBL) pKbl->List.Flink;
+ } while(pKbl != KBLList);
+
+ return NULL;
+}
+
+BOOL UserUnloadKbl(PKBL pKbl)
+{
+ /* According to msdn, UnloadKeyboardLayout can fail
+ if the keyboard layout identifier was preloaded. */
+
+ if(pKbl->Flags & KBL_PRELOAD)
+ {
+ DPRINT1("Attempted to unload preloaded keyboard layout.\n");
+ return FALSE;
+ }
+
+ if(pKbl->RefCount > 0)
+ {
+ /* Layout is used by other threads.
+ Mark it as unloaded and don't do anything else. */
+ pKbl->Flags |= KBL_UNLOAD;
+ }
+ else
+ {
+ //Unload the layout
+ EngUnloadImage(pKbl->hModule);
+ RemoveEntryList(&pKbl->List);
+ ExFreePoolWithTag(pKbl, TAG_KEYBOARD);
+ }
+
+ return TRUE;
+}
+
+static PKBL co_UserActivateKbl(PTHREADINFO w32Thread, PKBL pKbl, UINT Flags)
+{
+ PKBL Prev;
+
+ Prev = w32Thread->KeyboardLayout;
+ Prev->RefCount--;
+ w32Thread->KeyboardLayout = pKbl;
+ pKbl->RefCount++;
+
+ if(Flags & KLF_SETFORPROCESS)
+ {
+ //FIXME
+
+ }
+
+ if(Prev->Flags & KBL_UNLOAD && Prev->RefCount == 0)
+ {
+ UserUnloadKbl(Prev);
+ }
+
+ // Send WM_INPUTLANGCHANGE to thread's focus window
+ co_IntSendMessage(w32Thread->MessageQueue->FocusWindow,
+ WM_INPUTLANGCHANGE,
+ 0, // FIXME: put charset here (what is this?)
+ (LPARAM)pKbl->hkl); //klid
+
+ return Prev;
+}
+
+/* EXPORTS *******************************************************************/
+
+HKL FASTCALL
+UserGetKeyboardLayout(
+ DWORD dwThreadId)
+{
+ NTSTATUS Status;
+ PETHREAD Thread;
+ PTHREADINFO W32Thread;
+ HKL Ret;
+
+ if(!dwThreadId)
+ {
+ W32Thread = PsGetCurrentThreadWin32Thread();
+ return W32Thread->KeyboardLayout->hkl;
+ }
+
+ Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
+
+ W32Thread = PsGetThreadWin32Thread(Thread);
+ Ret = W32Thread->KeyboardLayout->hkl;
+ ObDereferenceObject(Thread);
+ return Ret;
+}
+
+UINT
+APIENTRY
+NtUserGetKeyboardLayoutList(
+ INT nItems,
+ HKL* pHklBuff)
+{
+ UINT Ret = 0;
+ PKBL pKbl;
+
+ UserEnterShared();
+ pKbl = KBLList;
+
+ if(nItems == 0)
+ {
+ do
+ {
+ Ret++;
+ pKbl = (PKBL) pKbl->List.Flink;
+ } while(pKbl != KBLList);
+ }
+ else
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite(pHklBuff, nItems*sizeof(HKL), 4);
+
+ while(Ret < nItems)
+ {
+ if(!(pKbl->Flags & KBL_UNLOAD))
+ {
+ pHklBuff[Ret] = pKbl->hkl;
+ Ret++;
+ pKbl = (PKBL) pKbl->List.Flink;
+ if(pKbl == KBLList) break;
+ }
+ }
+
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ Ret = 0;
+ }
+ _SEH2_END;
+ }
+
+ UserLeave();
+ return Ret;
+}
+
+BOOL
+APIENTRY
+NtUserGetKeyboardLayoutName(
+ LPWSTR lpszName)
+{
+ BOOL ret = FALSE;
+ PKBL pKbl;
+ PTHREADINFO pti;
+
+ UserEnterShared();
+
+ _SEH2_TRY
+ {
+ ProbeForWrite(lpszName, KL_NAMELENGTH*sizeof(WCHAR), 1);
+ pti = PsGetCurrentThreadWin32Thread();
+ pKbl = pti->KeyboardLayout;
+ RtlCopyMemory(lpszName, pKbl->Name, KL_NAMELENGTH*sizeof(WCHAR));
+ ret = TRUE;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ ret = FALSE;
+ }
+ _SEH2_END;
+
+ UserLeave();
+ return ret;
+}
+
+
+HKL
+APIENTRY
+NtUserLoadKeyboardLayoutEx(
+ IN HANDLE Handle,
+ IN DWORD offTable,
+ IN PUNICODE_STRING puszKeyboardName,
+ IN HKL hKL,
+ IN PUNICODE_STRING puszKLID,
+ IN DWORD dwKLID,
+ IN UINT Flags)
+{
+ HKL Ret = NULL;
+ PKBL pKbl = NULL, Cur;
+
+ UserEnterExclusive();
+
+ //Let's see if layout was already loaded.
+ Cur = KBLList;
+ do
+ {
+ if(Cur->klid == dwKLID)
+ {
+ pKbl = Cur;
+ pKbl->Flags &= ~KBL_UNLOAD;
+ break;
+ }
+
+ Cur = (PKBL) Cur->List.Flink;
+ } while(Cur != KBLList);
+
+ //It wasn't, so load it.
+ if(!pKbl)
+ {
+ pKbl = UserLoadDllAndCreateKbl(dwKLID);
+
+ if(!pKbl)
+ {
+ goto the_end;
+ }
+
+ InsertTailList(&KBLList->List, &pKbl->List);
+ }
+
+ if(Flags & KLF_REORDER) KBLList = pKbl;
+
+ if(Flags & KLF_ACTIVATE)
+ {
+ co_UserActivateKbl(PsGetCurrentThreadWin32Thread(), pKbl, Flags);
+ }
+
+ Ret = pKbl->hkl;
+
+ //FIXME: KLF_NOTELLSHELL
+ // KLF_REPLACELANG
+ // KLF_SUBSTITUTE_OK
+
+the_end:
+ UserLeave();
+ return Ret;
+}
+
+HKL
+APIENTRY
+NtUserActivateKeyboardLayout(
+ HKL hKl,
+ ULONG Flags)
+{
+ PKBL pKbl;
+ HKL Ret = NULL;
+ PTHREADINFO pWThread;
+
+ UserEnterExclusive();
+
+ pWThread = PsGetCurrentThreadWin32Thread();
+
+ if(pWThread->KeyboardLayout->hkl == hKl)
+ {
+ Ret = hKl;
+ goto the_end;
+ }
+
+ if(hKl == (HKL)HKL_NEXT)
+ {
+ pKbl = (PKBL)pWThread->KeyboardLayout->List.Flink;
+ }
+ else if(hKl == (HKL)HKL_PREV)
+ {
+ pKbl = (PKBL)pWThread->KeyboardLayout->List.Blink;
+ }
+ else pKbl = UserHklToKbl(hKl);
+
+ //FIXME: KLF_RESET, KLF_SHIFTLOCK
+
+ if(pKbl)
+ {
+ if(Flags & KLF_REORDER)
+ KBLList = pKbl;
+
+ if(pKbl == pWThread->KeyboardLayout)
+ {
+ Ret = pKbl->hkl;
+ }
+ else
+ {
+ pKbl = co_UserActivateKbl(pWThread, pKbl, Flags);
+ Ret = pKbl->hkl;
+ }
+ }
+ else
+ {
+ DPRINT1("%s: Invalid HKL %x!\n", __FUNCTION__, hKl);
+ }
+
+the_end:
+ UserLeave();
+ return Ret;
+}
+
+BOOL
+APIENTRY
+NtUserUnloadKeyboardLayout(
+ HKL hKl)
+{
+ PKBL pKbl;
+ BOOL Ret = FALSE;
+
+ UserEnterExclusive();
+
+ if((pKbl = UserHklToKbl(hKl)))
+ {
+ Ret = UserUnloadKbl(pKbl);
+ }
+ else
+ {
+ DPRINT1("%s: Invalid HKL %x!\n", __FUNCTION__, hKl);
+ }
+
+ UserLeave();
+ return Ret;
+}
+
+/* EOF */