[YAROTOWS] Reintegrate the branch. For a brighter future.
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / kbdlayout.c
index b56b85e..a6ac6e6 100644 (file)
-\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 */