[gtk: 1/2] Rewrite GdkWin32Keymap (load table directly from layout DLL)




commit ea65abc7e2cd056a2c562073664b05faddaf4fad
Author: Philip Zander <philip zander gmail com>
Date:   Wed Jan 5 15:44:49 2022 +0100

    Rewrite GdkWin32Keymap (load table directly from layout DLL)
    
    The old code used repeated calls to `ToUnicodeEx` to populate
    the translation table, which is slow and buggy. The new code
    directly loads the layout driver DLLs from Windows.
    
    See https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4338

 gdk/win32/gdkdisplay-win32.c         |   51 +-
 gdk/win32/gdkdisplay-win32.h         |    8 -
 gdk/win32/gdkevents-win32.c          |   28 +-
 gdk/win32/gdkkeys-win32-impl-wow64.c |   26 +
 gdk/win32/gdkkeys-win32-impl.c       |  548 ++++++++++
 gdk/win32/gdkkeys-win32.c            | 1828 +++++++++++-----------------------
 gdk/win32/gdkkeys-win32.h            |  168 ++++
 gdk/win32/gdkprivate-win32.h         |   10 +-
 gdk/win32/meson.build                |    2 +
 9 files changed, 1378 insertions(+), 1291 deletions(-)
---
diff --git a/gdk/win32/gdkdisplay-win32.c b/gdk/win32/gdkdisplay-win32.c
index d4a275ec7f..675e1af5e2 100644
--- a/gdk/win32/gdkdisplay-win32.c
+++ b/gdk/win32/gdkdisplay-win32.c
@@ -945,38 +945,71 @@ _gdk_win32_enable_hidpi (GdkWin32Display *display)
     }
 }
 
-static void
-_gdk_win32_check_on_arm64 (GdkWin32Display *display)
+gboolean
+_gdk_win32_check_processor (GdkWin32ProcessorCheckType check_type)
 {
   static gsize checked = 0;
+  static gboolean is_arm64 = FALSE;
+  static gboolean is_wow64 = FALSE;
 
   if (g_once_init_enter (&checked))
     {
+      gboolean fallback_wow64_check = FALSE;
       HMODULE kernel32 = LoadLibraryW (L"kernel32.dll");
 
       if (kernel32 != NULL)
         {
-          display->cpu_funcs.isWow64Process2 =
+          typedef BOOL (WINAPI *funcIsWow64Process2) (HANDLE, USHORT *, USHORT *);
+
+          funcIsWow64Process2 isWow64Process2 =
             (funcIsWow64Process2) GetProcAddress (kernel32, "IsWow64Process2");
 
-          if (display->cpu_funcs.isWow64Process2 != NULL)
+          if (isWow64Process2 != NULL)
             {
               USHORT proc_cpu = 0;
               USHORT native_cpu = 0;
 
-              display->cpu_funcs.isWow64Process2 (GetCurrentProcess (),
-                                                  &proc_cpu,
-                                                  &native_cpu);
+              isWow64Process2 (GetCurrentProcess (), &proc_cpu, &native_cpu);
 
               if (native_cpu == IMAGE_FILE_MACHINE_ARM64)
-                display->running_on_arm64 = TRUE;
+                is_arm64 = TRUE;
+
+              if (proc_cpu != IMAGE_FILE_MACHINE_UNKNOWN)
+                is_wow64 = TRUE;
+            }
+          else
+            {
+              fallback_wow64_check = TRUE;
             }
 
           FreeLibrary (kernel32);
         }
+      else
+        {
+          fallback_wow64_check = TRUE;
+        }
+
+      if (fallback_wow64_check)
+        IsWow64Process (GetCurrentProcess (), &is_wow64);
 
       g_once_init_leave (&checked, 1);
     }
+
+  switch (check_type)
+    {
+      case GDK_WIN32_ARM64:
+        return is_arm64;
+        break;
+
+      case GDK_WIN32_WOW64:
+        return is_wow64;
+        break;
+
+      default:
+        g_warning ("unknown CPU check type");
+        return FALSE;
+        break;
+    }
 }
 
 static void
@@ -987,7 +1020,7 @@ gdk_win32_display_init (GdkWin32Display *display_win32)
   display_win32->monitors = G_LIST_MODEL (g_list_store_new (GDK_TYPE_MONITOR));
 
   _gdk_win32_enable_hidpi (display_win32);
-  _gdk_win32_check_on_arm64 (display_win32);
+  display_win32->running_on_arm64 = _gdk_win32_check_processor (GDK_WIN32_ARM64);
 
   /* if we have DPI awareness, set up fixed scale if set */
   if (display_win32->dpi_aware_type != PROCESS_DPI_UNAWARE &&
diff --git a/gdk/win32/gdkdisplay-win32.h b/gdk/win32/gdkdisplay-win32.h
index 22532cf39a..3b746bbe1b 100644
--- a/gdk/win32/gdkdisplay-win32.h
+++ b/gdk/win32/gdkdisplay-win32.h
@@ -105,13 +105,6 @@ typedef enum {
   GDK_WIN32_TABLET_INPUT_API_WINPOINTER
 } GdkWin32TabletInputAPI;
 
-/* Detect running architecture */
-typedef BOOL (WINAPI *funcIsWow64Process2) (HANDLE, USHORT *, USHORT *);
-typedef struct _GdkWin32KernelCPUFuncs
-{
-  funcIsWow64Process2 isWow64Process2;
-} GdkWin32KernelCPUFuncs;
-
 typedef struct
 {
   HDC hdc;
@@ -177,7 +170,6 @@ struct _GdkWin32Display
 
   /* Running CPU items */
   guint running_on_arm64 : 1;
-  GdkWin32KernelCPUFuncs cpu_funcs;
 };
 
 struct _GdkWin32DisplayClass
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index bc7590aa13..9e752d12f5 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -19,7 +19,7 @@
  */
 
 /*
- * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
+ * Modified by the GTK+ Team and others 1997-2020.  See the AUTHORS
  * file for a list of people on the GTK+ Team.  See the ChangeLog
  * files for a list of changes.  These files are distributed with
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
@@ -644,11 +644,9 @@ build_key_event_state (BYTE *key_state)
 {
   GdkModifierType state;
   GdkWin32Keymap *keymap;
+  keymap = GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display));
 
-  state = 0;
-
-  if (key_state[VK_SHIFT] & 0x80)
-    state |= GDK_SHIFT_MASK;
+  state = _gdk_win32_keymap_get_mod_mask (keymap);
 
   if (key_state[VK_CAPITAL] & 0x01)
     state |= GDK_LOCK_MASK;
@@ -664,26 +662,6 @@ build_key_event_state (BYTE *key_state)
   if (key_state[VK_XBUTTON2] & 0x80)
     state |= GDK_BUTTON5_MASK;
 
-  keymap = GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display));
-
-  if (_gdk_win32_keymap_has_altgr (keymap) &&
-      (key_state[VK_LCONTROL] & 0x80) &&
-      (key_state[VK_RMENU] & 0x80))
-    {
-      state |= GDK_MOD2_MASK;
-      if (key_state[VK_RCONTROL] & 0x80)
-       state |= GDK_CONTROL_MASK;
-      if (key_state[VK_LMENU] & 0x80)
-       state |= GDK_ALT_MASK;
-    }
-  else
-    {
-      if (key_state[VK_CONTROL] & 0x80)
-       state |= GDK_CONTROL_MASK;
-      if (key_state[VK_MENU] & 0x80)
-       state |= GDK_ALT_MASK;
-    }
-
   return state;
 }
 
diff --git a/gdk/win32/gdkkeys-win32-impl-wow64.c b/gdk/win32/gdkkeys-win32-impl-wow64.c
new file mode 100644
index 0000000000..ada1211d87
--- /dev/null
+++ b/gdk/win32/gdkkeys-win32-impl-wow64.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2021 Philip Zander
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _WIN64
+#define GDK_WIN32_COMPILE_FOR_WOW64 1
+#include "gdkkeys-win32-impl.c"
+#endif
diff --git a/gdk/win32/gdkkeys-win32-impl.c b/gdk/win32/gdkkeys-win32-impl.c
new file mode 100644
index 0000000000..f792c4e24b
--- /dev/null
+++ b/gdk/win32/gdkkeys-win32-impl.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 2021 Philip Zander
+ * Copyright (c) 2018 Microsoft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* NOTE: When compiling the 32-bit version of the library, in addition to being
+ * compiled as a regular source file, this file is also included by
+ * gdkkeys-win32-impl-wow64.c to generate an alternate version of the code
+ * intended for running on a 64-bit kernel. Because of the way keyboard layout
+ * DLLs work on Windows, we have to generate two versions and decide at runtime
+ * which code path to execute. You can read more about the specifics below, in
+ * the section about KBD_LONG_POINTER. */
+
+#include "gdkkeys-win32.h"
+
+#ifndef GDK_WIN32_COMPILE_FOR_WOW64
+  #define GDK_WIN32_COMPILE_FOR_WOW64 0
+#endif
+ 
+/* This is our equivalent of the KBD_LONG_POINTER macro in Microsoft's kbd.h.
+ *
+ * A KBD_LONG_POINTER represents a pointer native to the *host*.
+ * I.e. 32 bits on 32-bit Windows and 64 bits on 64-bit Windows.
+ *
+ * This is *not* the same as the the bitness of the application, since it is
+ * possible to execute 32-bit binaries on either a 32-bit *or* a 64-bit host.
+ * On a 64-bit host, KBD_LONG_PTR will be 64-bits, even if the application 
+ * itself is 32-bit. (Whereas on a 32-bit host, it will be 32-bit.)
+ *
+ * For clarity, here is an overview of the bit-size of KBD_LONG_POINTER on all
+ * possible host & app combinations:
+ *
+ *      Host  32  64
+ *  App +-----------
+ *  32  |     32  64
+ *  64  |     -   64
+ *
+ * In the official MS headers, KBD_LONG_POINTER is implemented via a macro
+ * which expands to the attribute `__ptr64` if the keyboard driver is 
+ * compiled for a 64 bit host. Unfortunately, `__ptr64` is only 
+ * supported by MSVC. We use a union here as a workaround.
+ *
+ * For all KBD_LONG_POINTERs, we define an alias starting with "KLP".
+ * Our naming schema (inspired by the Windows headers) is thus the following:
+ * - FOO:    The type FOO itself
+ * - PFOO:   Regular pointer to the type FOO
+ * - KLPFOO: Keyboard Long Pointer to the type FOO
+ */
+
+#if GDK_WIN32_COMPILE_FOR_WOW64
+  #define DEFINE_KBD_LONG_POINTER(type)          \
+    typedef union {                              \
+      P##type ptr;                               \
+      UINT64  _align;                            \
+    } KLP##type
+#else
+  #define DEFINE_KBD_LONG_POINTER(type)          \
+    typedef union {                              \
+      P##type ptr;                               \
+    } KLP##type
+#endif
+
+DEFINE_KBD_LONG_POINTER (USHORT);
+DEFINE_KBD_LONG_POINTER (VOID);
+
+/* Driver definitions
+ * See
+ *   
https://github.com/microsoft/windows-rs/blob/0.28.0/crates/deps/sys/src/Windows/Win32/UI/Input/KeyboardAndMouse/mod.rs
+ *
+ * For more information on how these structures work, see also:
+ *   
https://github.com/microsoft/Windows-driver-samples/tree/f0adcda012820b1cd44a8b3a1953baf478029738/input/layout
+ */
+
+typedef struct
+{
+  BYTE                        Vk;
+  BYTE                        ModBits;
+} VK_TO_BIT, *PVK_TO_BIT;
+
+DEFINE_KBD_LONG_POINTER (VK_TO_BIT);
+
+typedef struct
+{
+  KLPVK_TO_BIT                pVkToBit;
+  WORD                        wMaxModBits;
+  BYTE                        ModNumber[1];
+} MODIFIERS, *PMODIFIERS;
+
+DEFINE_KBD_LONG_POINTER (MODIFIERS);
+
+typedef struct
+{
+  BYTE                        Vsc;
+  USHORT                      Vk;
+} VSC_VK, *PVSC_VK;
+
+DEFINE_KBD_LONG_POINTER (VSC_VK);
+
+typedef struct
+{
+  BYTE                        Vk;
+  BYTE                        Vsc;
+} VK_VSC, *PVK_VSC;
+
+DEFINE_KBD_LONG_POINTER (VK_VSC);
+
+typedef struct
+{
+  BYTE                        VirtualKey;
+  BYTE                        Attributes;
+  WCHAR                       wch[1];
+} VK_TO_WCHARS, *PVK_TO_WCHARS;
+
+DEFINE_KBD_LONG_POINTER (VK_TO_WCHARS);
+
+typedef struct
+{
+  KLPVK_TO_WCHARS             pVkToWchars;
+  BYTE                        nModifications;
+  BYTE                        cbSize;
+} VK_TO_WCHAR_TABLE, *PVK_TO_WCHAR_TABLE;
+
+DEFINE_KBD_LONG_POINTER (VK_TO_WCHAR_TABLE);
+
+typedef struct
+{
+  DWORD                       dwBoth;
+  WCHAR                       wchComposed;
+  USHORT                      uFlags;
+} DEADKEY, *PDEADKEY;
+
+DEFINE_KBD_LONG_POINTER (DEADKEY);
+
+typedef struct
+{
+  KLPMODIFIERS                pCharModifiers;
+  KLPVK_TO_WCHAR_TABLE        pVkToWcharTable;
+  KLPDEADKEY                  pDeadKey;
+  KLPVOID                     pKeyNames;
+  KLPVOID                     pKeyNamesExt;
+  KLPVOID                     pKeyNamesDead;
+  KLPUSHORT                   pusVSCtoVK;
+  BYTE                        bMaxVSCtoVK;
+  KLPVSC_VK                   pVSCtoVK_E0;
+  KLPVSC_VK                   pVSCtoVK_E1;
+  DWORD                       fLocaleFlags;
+  BYTE                        nLgMaxd;
+  BYTE                        cbLgEntry;
+  KLPVOID                     pLigature;
+} KBDTABLES, *PKBDTABLES;
+
+DEFINE_KBD_LONG_POINTER (KBDTABLES);
+
+/* End of declarations */
+
+static BYTE
+keystate_to_modbits (GdkWin32KeymapLayoutInfo *info,
+                     const BYTE                keystate[256])
+{
+  PKBDTABLES tables = (PKBDTABLES) info->tables;
+  PVK_TO_BIT vk_to_bit;
+  BYTE       result = 0;
+  int        i;
+
+  g_return_val_if_fail (tables != NULL, 0);
+
+  vk_to_bit = tables->pCharModifiers.ptr->pVkToBit.ptr;
+
+  for (i = 0; vk_to_bit[i].Vk != 0; ++i)
+    if (keystate[vk_to_bit[i].Vk] & 0x80)
+      result |= vk_to_bit[i].ModBits;
+
+  return result;
+}
+
+static BYTE
+modbits_to_level (GdkWin32KeymapLayoutInfo *info,
+                  BYTE                      modbits)
+{
+  PKBDTABLES tables = (PKBDTABLES) info->tables;
+  PMODIFIERS modifiers;
+
+  g_return_val_if_fail (tables != NULL, 0);
+
+  modifiers = tables->pCharModifiers.ptr;
+  if (modbits > modifiers->wMaxModBits)
+    return 0;
+
+  return modifiers->ModNumber[modbits];
+}
+
+#define POPCOUNT(b) (!!(b & 0x01) + !!(b & 0x02) + !!(b & 0x04) + !!(b & 0x08) + \
+                     !!(b & 0x10) + !!(b & 0x20) + !!(b & 0x40) + !!(b & 0x80))
+
+/*
+ * vk_to_char_fuzzy:
+ *
+ * For a given key and keystate, return the best-fit character and the
+ * modifiers used to produce it. Note that not all modifiers need to be used,
+ * because some modifier combination aren't actually mapped in the keyboard
+ * layout (for example the Ctrl key typically has no effect, unless used in
+ * combination with Alt). Such modifiers will not be consumed.
+ *
+ * 'Best-fit' means 'consume as many modifiers as possibe'.
+ *
+ * For example (assuming a neutral keystate):
+ *
+ * - Shift + a        -> 'A', consumed_mod_bits: [Shift]
+ * - Ctrl + a         -> 'a', consumed_mod_bits: []
+ * - Ctrl + Shift + a -> 'A', consumed_mod_bits: [Shift]
+ *
+ * If capslock is active, the result could be:
+ *
+ * - Shift + a        -> 'a', consumed_mod_bits: [Shift]
+ *
+ * The caller can supply additional modifiers to be added to the
+ * keystate in `extra_mod_bits`.
+ *
+ * If the key combination results in a dead key, `is_dead` will be set to TRUE,
+ * otherwise it will be set to FALSE.
+ */
+static WCHAR
+vk_to_char_fuzzy (GdkWin32KeymapLayoutInfo *info,
+                  const BYTE                keystate[256],
+                  BYTE                      extra_mod_bits,
+                  BYTE                     *consumed_mod_bits,
+                  gboolean                 *is_dead,
+                  BYTE                      vk)
+{
+  PKBDTABLES         tables = (PKBDTABLES) info->tables;
+
+  PVK_TO_WCHAR_TABLE wch_tables;
+  PVK_TO_WCHAR_TABLE wch_table;
+  PVK_TO_WCHARS      entry;
+
+  int                table_index;
+  int                entry_index;
+  int                n_levels;
+  int                entry_size;
+
+  /* Initialize with defaults */
+  if (consumed_mod_bits)
+    *consumed_mod_bits = 0;
+  if (is_dead)
+    *is_dead = FALSE;
+
+  g_return_val_if_fail (tables != NULL, WCH_NONE);
+
+  wch_tables = tables->pVkToWcharTable.ptr;
+
+  table_index = info->vk_lookup_table[vk].table;
+  entry_index = info->vk_lookup_table[vk].index;
+
+  if (table_index == -1 || entry_index == -1)
+    return WCH_NONE;
+
+  wch_table = &wch_tables[table_index];
+
+  n_levels = wch_table->nModifications;
+  entry_size = wch_table->cbSize;
+
+  entry = (PVK_TO_WCHARS) ((PBYTE) wch_table->pVkToWchars.ptr
+                           + entry_size*entry_index);
+
+  if (entry->VirtualKey == vk)
+    {
+      BYTE     modbits;
+      WCHAR    best_char      = WCH_NONE;
+      BYTE     best_modifiers = 0;
+      int      best_score     = -1;
+      gboolean best_is_dead   = FALSE;
+      int      level;
+
+      /* Add modbits of currently pressed keys. */
+      modbits  = keystate_to_modbits (info, keystate);
+      /* Add modbits supplied by caller. */
+      modbits |= extra_mod_bits;
+
+      /* Take toggled keys into account. For example, capslock normally inverts the
+       * state of KBDSHIFT (with some exceptions). */
+
+      /* Key supporting capslock */
+      if ((entry->Attributes & CAPLOK) &&
+          /* Ignore capslock if any modifiers other than shift are pressed.
+           * E.g. on the German layout, CapsLock + AltGr + q is the same as
+           * AltGr + q ('@'), but NOT the same as Shift + AltGr + q (not mapped). */
+          !(modbits & ~KBDSHIFT) &&
+         (keystate[VK_CAPITAL] & 0x01))
+        modbits ^= KBDSHIFT;
+
+      /* Key supporting combination of capslock + altgr */
+      if ((entry->Attributes & CAPLOKALTGR) &&
+          (modbits & KBDALTGR) &&
+          (keystate[VK_CAPITAL] & 0x01))
+        modbits ^= KBDSHIFT;
+
+      /* In the Swiss German layout, CapsLock + key is different from Shift + key
+       * for some keys. For such keys, Capslock toggles the KBDCTRL bit. */
+      if ((entry->Attributes & SGCAPS) &&
+          (keystate[VK_CAPITAL] & 0x01))
+        modbits ^= KBDCTRL;
+
+      /* I'm not totally sure how kanalok behaves, for now I assume that there
+       * aren't any special cases. */
+      if ((entry->Attributes & KANALOK) &&
+          (keystate[VK_KANA] & 0x01))
+        modbits ^= KBDKANA;
+
+      /* We try to find the entry with the most matching modifiers */
+      for (level = 0; level < n_levels; ++level)
+        {
+          BYTE     candidate_modbits = info->level_to_modbits[level];
+          gboolean candidate_is_dead = FALSE;
+          WCHAR    c;
+          int      score;
+
+          if (candidate_modbits & ~modbits)
+              continue;
+
+          c = entry->wch[level];
+          if (c == WCH_DEAD)
+            {
+              /* Next entry contains the undead keys */
+              PVK_TO_WCHARS next_entry;
+              next_entry = (PVK_TO_WCHARS) ((PBYTE) wch_table->pVkToWchars.ptr
+                                            + entry_size * (entry_index + 1));
+              c = next_entry->wch[level];
+              candidate_is_dead = TRUE;
+            }
+
+          if (c == WCH_DEAD || c == WCH_LGTR || c == WCH_NONE)
+            continue;
+
+          score = POPCOUNT (candidate_modbits & modbits);
+          if (score > best_score)
+            {
+              best_score = score;
+              best_char = c;
+              best_modifiers = candidate_modbits;
+              best_is_dead = candidate_is_dead;
+            }
+        }
+
+      if (consumed_mod_bits)
+        *consumed_mod_bits = best_modifiers;
+
+      if (is_dead)
+        *is_dead = best_is_dead;
+
+      return best_char;
+    }
+
+  return WCH_NONE;
+}
+
+static void
+init_vk_lookup_table (GdkWin32KeymapLayoutInfo *info)
+{
+  PKBDTABLES         tables = (PKBDTABLES) info->tables;
+  PVK_TO_WCHAR_TABLE wch_tables;
+  PMODIFIERS         modifiers;
+  int                i, vk, table_idx;
+
+  g_return_if_fail (tables != NULL);
+
+  wch_tables = tables->pVkToWcharTable.ptr;
+
+  /* Initialize empty table */
+  memset (info->vk_lookup_table, -1, sizeof (info->vk_lookup_table));
+
+  /* Initialize level -> modbits lookup table */
+  memset (info->level_to_modbits, 0, sizeof(info->level_to_modbits));
+  info->max_level = 0;
+
+  modifiers = tables->pCharModifiers.ptr;
+  for (i = 0; i <= modifiers->wMaxModBits; ++i)
+    {
+      if (modifiers->ModNumber[i] != SHFT_INVALID &&
+          modifiers->ModNumber[i] != 0 /* Workaround for buggy layouts*/)
+        {
+          if (modifiers->ModNumber[i] > info->max_level)
+              info->max_level = modifiers->ModNumber[i];
+          info->level_to_modbits[modifiers->ModNumber[i]] = i;
+        }
+    }
+
+  info->max_modbit_value = modifiers->wMaxModBits;
+
+  /* For convenience, we add 256 identity-mapped entries corresponding to the VKs.
+   * This allows us to return a pointer to them from the `gdk_keysym_to_key_entry`
+   * function.
+   */
+
+  for (vk = 0; vk < 256; ++vk)
+    {
+      GdkWin32KeymapKeyEntry key_entry = {0};
+      key_entry.vk       = vk;
+      key_entry.mod_bits = 0;
+      key_entry.next     = -1;
+      g_array_append_val (info->key_entries, key_entry);
+    }
+
+  /* Special entry for ISO_Left_Tab */
+  {
+    GdkWin32KeymapKeyEntry key_entry = {0};
+    key_entry.vk       = VK_TAB;
+    key_entry.mod_bits = KBDSHIFT;
+    key_entry.next     = -1;
+    g_array_append_val (info->key_entries, key_entry);
+  }
+
+  /* Initialize generic vk <-> char tables */
+
+  for (table_idx = 0; ; ++table_idx)
+    {
+      PVK_TO_WCHAR_TABLE wch_table = &wch_tables[table_idx];
+      int entry_size;
+      int n_levels;
+      int entry_idx;
+
+      if (wch_table->pVkToWchars.ptr == NULL)
+        break;
+
+      entry_size = wch_table->cbSize;
+      n_levels   = wch_table->nModifications;
+
+      for (entry_idx = 0; ; ++entry_idx)
+        {
+          PVK_TO_WCHARS entry;
+          int           level;
+
+          entry = (PVK_TO_WCHARS) ((PBYTE) wch_table->pVkToWchars.ptr
+                                  + entry_size * entry_idx);
+
+          if (entry->VirtualKey == 0)
+            break;
+
+          /* Lookup table to find entry for a VK in O(1). */
+
+          info->vk_lookup_table[entry->VirtualKey].table = table_idx;
+          info->vk_lookup_table[entry->VirtualKey].index = entry_idx;
+
+          /* Create reverse lookup entries to find a VK+modifier combinations
+           * that results in a given character. */
+          for (level = 0; level < n_levels; ++level)
+            {
+              GdkWin32KeymapKeyEntry key_entry = {0};
+              WCHAR                  c         = entry->wch[level];
+              int                    inserted_idx;
+              gintptr                next_idx;
+
+              key_entry.vk       = entry->VirtualKey;
+              key_entry.mod_bits = info->level_to_modbits[level];
+
+              /* There can be multiple combinations that produce the same character.
+               * We store all of them in a linked list.
+               * Check if we already have an entry for the character, so we can chain
+               * them together. */
+              if (g_hash_table_lookup_extended (info->reverse_lookup_table,
+                                                GINT_TO_POINTER (c),
+                                                NULL, (gpointer*)&next_idx))
+                {
+                  key_entry.next = next_idx;
+                }
+              else
+                {
+                  key_entry.next = -1;
+                }
+
+              /* We store the KeyEntry in an array. In the hash table we only store
+               * the index. */
+
+              g_array_append_val (info->key_entries, key_entry);
+              inserted_idx = info->key_entries->len - 1;
+
+              g_hash_table_insert (info->reverse_lookup_table,
+                                   GINT_TO_POINTER (c),
+                                   GINT_TO_POINTER (inserted_idx));
+          }
+        }
+    }
+}
+
+static gboolean
+load_layout_dll (const char               *dll,
+                 GdkWin32KeymapLayoutInfo *info)
+{
+    typedef KLPKBDTABLES (*KbdLayerDescriptor)(VOID);
+
+    HMODULE             lib;
+    KbdLayerDescriptor  func;
+    KLPKBDTABLES        tables;
+
+    g_return_val_if_fail (dll != NULL, FALSE);
+
+    lib = LoadLibraryA (dll);
+    if (lib == NULL)
+      goto fail1;
+
+    func = (KbdLayerDescriptor) GetProcAddress (lib, "KbdLayerDescriptor");
+    if (func == NULL)
+      goto fail2;
+
+    tables = func();
+
+    info->lib = lib;
+    info->tables = (PKBDTABLES) tables.ptr;
+
+    return TRUE;
+
+fail2:
+    FreeLibrary (lib);
+fail1:
+    return FALSE;
+}
+
+#if GDK_WIN32_COMPILE_FOR_WOW64
+  #define GDK_WIN32_KEYMAP_IMPL_NAME gdkwin32_keymap_impl_wow64
+#else
+  #define GDK_WIN32_KEYMAP_IMPL_NAME gdkwin32_keymap_impl
+#endif
+
+const GdkWin32KeymapImpl GDK_WIN32_KEYMAP_IMPL_NAME =
+  {
+    load_layout_dll,
+    init_vk_lookup_table,
+    keystate_to_modbits,
+    modbits_to_level,
+    vk_to_char_fuzzy
+  };
diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c
index 70d65e97fb..a8f976e2ab 100644
--- a/gdk/win32/gdkkeys-win32.c
+++ b/gdk/win32/gdkkeys-win32.c
@@ -32,6 +32,7 @@
 #include "gdkdisplayprivate.h"
 #include "gdkkeysyms.h"
 #include "gdkkeysprivate.h"
+#include "gdkkeys-win32.h"
 
 #include <ctype.h>
 #include <stdio.h>
@@ -42,106 +43,7 @@
 
 #define GDK_MOD2_MASK (1 << 4)
 
-enum _GdkWin32KeyLevelState
-{
-  GDK_WIN32_LEVEL_NONE = 0,
-  GDK_WIN32_LEVEL_SHIFT,
-  GDK_WIN32_LEVEL_CAPSLOCK,
-  GDK_WIN32_LEVEL_SHIFT_CAPSLOCK,
-  GDK_WIN32_LEVEL_ALTGR,
-  GDK_WIN32_LEVEL_SHIFT_ALTGR,
-  GDK_WIN32_LEVEL_CAPSLOCK_ALTGR,
-  GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR,
-  GDK_WIN32_LEVEL_COUNT
-};
-
-typedef enum _GdkWin32KeyLevelState GdkWin32KeyLevelState;
-
-struct _GdkWin32KeyNode
-{
-  /* Non-spacing version of the dead key */
-  guint                  undead_gdk_keycode;
-
-  /* Virtual key code */
-  guint8                 vk;
-
-  /* Level for which this virtual key code produces this gdk_keycode */
-  GdkWin32KeyLevelState  level;
-
-  /* GDK (X11) code for this key */
-  guint                  gdk_keycode;
-
-  /* Array of GdkWin32KeyNode should be sorted by gdk_keycode, then by level */
-  GArray                *combinations;
-};
-
-typedef struct _GdkWin32KeyNode GdkWin32KeyNode;
-
-/*
-Example:
-  GdkWin32KeyNode
-  {
-    undead_gdk_keycode = 0x0b4 GDK_KEY_acute (')
-    vk = 0xde VK_OEM_7
-    level = GDK_WIN32_LEVEL_NONE
-    gdk_keycode = 0xfe51 GDK_KEY_dead_acute
-    combinations = 
-    {
-      GdkWin32KeyNode
-      {
-        undead_gdk_keycode = 0x061 GDK_KEY_a (a)
-        level = GDK_WIN32_LEVEL_NONE
-        vk = 0x41 VK_A
-        gdk_keycode = 0xe1 GDK_KEY_aacute á
-        combinations = NULL
-      },
-      GdkWin32KeyNode
-      {
-        unicode_char = 0x041 GDK_KEY_A (A)
-        level = GDK_WIN32_LEVEL_SHIFT
-        vk = 0x41 VK_A
-        gdk_keycode = 0x0c1 GDK_KEY_Aacute Á
-        combinations = NULL
-      },
-      { ... }
-    }
-  }
-
-Thus:
-
-GDK_KEY_dead_acute + GDK_KEY_a
-= GDK_KEY_aacute
-
-GDK_KEY_dead_acute + GDK_KEY_A
-= GDK_KEY_Aacute
-
-GDK_KEY_dead_acute + GDK_KEY_s
-matches partially
-(GDK_KEY_dead_acute is a known dead key, but does not combine with GDK_KEY_s)
-and resolves into:
-GDK_KEY_acute + GDK_KEY_s
-
-GDK_KEY_dead_somethingelse + GDK_KEY_anything
-does not match at all
-(W32 API did not provide any deadkey info for GDK_KEY_dead_somethingelse)
-and the caller will try other matching mechanisms for compose_buffer
-*/
-
-struct _GdkWin32KeyGroupOptions
-{
-  /* character that should be used as the decimal separator */
-  wchar_t         decimal_mark;
-
-  /* Scancode for the VK_RSHIFT */
-  guint           scancode_rshift;
-
-  /* TRUE if Ctrl+Alt emulates AltGr */
-  gboolean        has_altgr;
-
-  GArray         *dead_keys;
-};
-
-typedef struct _GdkWin32KeyGroupOptions GdkWin32KeyGroupOptions;
+/* GdkWin32Keymap */
 
 struct _GdkWin32KeymapClass
 {
@@ -152,63 +54,61 @@ struct _GdkWin32Keymap
 {
   GdkKeymap parent_instance;
 
-  /* length = what GetKeyboardLayoutList() returns, type = HKL.
-   * When it changes, recreate the keymap and repopulate the options.
-   */
+  /* Array of HKL */
   GArray *layout_handles;
 
-  /* VirtualKeyCode -> gdk_keyval table
-   * length = 256 * length(layout_handles) * 2 * 4
-   * 256 is the number of virtual key codes,
-   * 2x4 is the number of Shift/AltGr/CapsLock combinations (level),
-   * length(layout_handles) is the number of layout handles (group).
-   */
-  guint  *keysym_tab;
-
-  /* length = length(layout_handles), type =  GdkWin32KeyGroupOptions
-   * Kept separate from layout_handles because layout_handles is
-   * populated by W32 API.
-   */
-  GArray *options;
+  /* Array of GdkWin32KeymapLayoutInfo */
+  GArray *layout_infos;
 
   /* Index of a handle in layout_handles,
    * at any point it should be the same handle as GetKeyboardLayout(0) returns,
    * but GDK caches it to avoid calling GetKeyboardLayout (0) every time.
    */
   guint8 active_layout;
+
+  guint current_serial;
+
+  /* Pointer to the implementation to be used. See comment in gdkkeys-win32.h.
+   * (we will dynamically choose at runtime for 32-bit builds based on whether
+   * we are running under WOW64)
+   */
+  const GdkWin32KeymapImpl *gdkwin32_keymap_impl;
 };
 
 G_DEFINE_TYPE (GdkWin32Keymap, gdk_win32_keymap, GDK_TYPE_KEYMAP)
 
 guint _gdk_keymap_serial = 0;
-
 static GdkKeymap *default_keymap = NULL;
 
-#define KEY_STATE_SIZE 256
-
-static void update_keymap (GdkKeymap *gdk_keymap);
+static void update_keymap              (GdkWin32Keymap *gdk_keymap);
+static void clear_keyboard_layout_info (gpointer        data);
 
 static void
-gdk_win32_key_group_options_clear (GdkWin32KeyGroupOptions *options)
+gdk_win32_keymap_init (GdkWin32Keymap *keymap)
 {
-  g_clear_pointer (&options->dead_keys, g_array_unref);
-}
+  /* Regular implementation (32 bit & 64 bit) */
+  extern const GdkWin32KeymapImpl gdkwin32_keymap_impl;
+  /* Implementation for 32 bit applications running on a 64 bit host (WOW64). */
+#ifndef _WIN64
+  extern const GdkWin32KeymapImpl gdkwin32_keymap_impl_wow64;
+#endif
 
-static void
-gdk_win32_key_node_clear (GdkWin32KeyNode *node)
-{
-  g_clear_pointer (&node->combinations, g_array_unref);
-}
+  keymap->layout_infos = g_array_new (FALSE, TRUE,
+                                      sizeof (GdkWin32KeymapLayoutInfo));
+  g_array_set_clear_func (keymap->layout_infos,
+                          clear_keyboard_layout_info);
 
-static void
-gdk_win32_keymap_init (GdkWin32Keymap *keymap)
-{
-  keymap->layout_handles = g_array_new (FALSE, FALSE, sizeof (HKL));
-  keymap->options = g_array_new (FALSE, FALSE, sizeof (GdkWin32KeyGroupOptions));
-  g_array_set_clear_func (keymap->options, (GDestroyNotify) gdk_win32_key_group_options_clear);
-  keymap->keysym_tab = NULL;
+  keymap->layout_handles = g_array_new (FALSE, FALSE,
+                                        sizeof (GdkWin32KeymapLayoutInfo));
   keymap->active_layout = 0;
-  update_keymap (GDK_KEYMAP (keymap));
+
+  keymap->gdkwin32_keymap_impl = &gdkwin32_keymap_impl;
+#ifndef _WIN64
+  if (_gdk_win32_check_processor (GDK_WIN32_WOW64))
+    keymap->gdkwin32_keymap_impl = &gdkwin32_keymap_impl_wow64;
+#endif
+
+  update_keymap (keymap);
 }
 
 static void
@@ -216,933 +116,471 @@ gdk_win32_keymap_finalize (GObject *object)
 {
   GdkWin32Keymap *keymap = GDK_WIN32_KEYMAP (object);
 
-  g_clear_pointer (&keymap->keysym_tab, g_free);
   g_clear_pointer (&keymap->layout_handles, g_array_unref);
-  g_clear_pointer (&keymap->options, g_array_unref);
+  g_clear_pointer (&keymap->layout_infos, g_array_unref);
 
   G_OBJECT_CLASS (gdk_win32_keymap_parent_class)->finalize (object);
 }
 
-#ifdef G_ENABLE_DEBUG
-static void
-print_keysym_tab (GdkWin32Keymap *keymap)
-{
-  int                       li;
-  GdkWin32KeyGroupOptions  *options;
-  int                       vk;
-  GdkWin32KeyLevelState     level;
-  int                       group_size = keymap->layout_handles->len;
-
-  for (li = 0; li < group_size; li++)
-    {
-      options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, li);
-      g_print ("keymap %d (0x%p):%s\n",
-               li, g_array_index (keymap->layout_handles, HKL, li),
-               options->has_altgr ? " (uses AltGr)" : "");
-
-      for (vk = 0; vk < KEY_STATE_SIZE; vk++)
-        {
-          g_print ("%#.02x: ", vk);
-
-          for (level = 0; level < GDK_WIN32_LEVEL_COUNT; level++)
-            {
-              const char *name = gdk_keyval_name (keymap->keysym_tab[vk * group_size * GDK_WIN32_LEVEL_COUNT 
+ level]);
-
-              g_print ("%s ", name ? name : "(none)");
-            }
-
-          g_print ("\n");
-        }
-    }
-}
-#endif
-
-static void
-handle_special (guint  vk,
-               guint *ksymp,
-               int    shift)
+/* Convenience wrapper functions */
 
+static gboolean
+load_layout_dll (GdkWin32Keymap           *keymap,
+                 const char               *dll,
+                 GdkWin32KeymapLayoutInfo *info)
 {
-  switch (vk)
-    {
-    case VK_CANCEL:
-      *ksymp = GDK_KEY_Cancel; break;
-    case VK_BACK:
-      *ksymp = GDK_KEY_BackSpace; break;
-    case VK_TAB:
-      if (shift & 0x1)
-       *ksymp = GDK_KEY_ISO_Left_Tab;
-      else
-       *ksymp = GDK_KEY_Tab;
-      break;
-    case VK_CLEAR:
-      *ksymp = GDK_KEY_Clear; break;
-    case VK_RETURN:
-      *ksymp = GDK_KEY_Return; break;
-    case VK_SHIFT:
-    case VK_LSHIFT:
-      *ksymp = GDK_KEY_Shift_L; break;
-    case VK_CONTROL:
-    case VK_LCONTROL:
-      *ksymp = GDK_KEY_Control_L; break;
-    case VK_MENU:
-    case VK_LMENU:
-      *ksymp = GDK_KEY_Alt_L; break;
-    case VK_PAUSE:
-      *ksymp = GDK_KEY_Pause; break;
-    case VK_ESCAPE:
-      *ksymp = GDK_KEY_Escape; break;
-    case VK_PRIOR:
-      *ksymp = GDK_KEY_Prior; break;
-    case VK_NEXT:
-      *ksymp = GDK_KEY_Next; break;
-    case VK_END:
-      *ksymp = GDK_KEY_End; break;
-    case VK_HOME:
-      *ksymp = GDK_KEY_Home; break;
-    case VK_LEFT:
-      *ksymp = GDK_KEY_Left; break;
-    case VK_UP:
-      *ksymp = GDK_KEY_Up; break;
-    case VK_RIGHT:
-      *ksymp = GDK_KEY_Right; break;
-    case VK_DOWN:
-      *ksymp = GDK_KEY_Down; break;
-    case VK_SELECT:
-      *ksymp = GDK_KEY_Select; break;
-    case VK_PRINT:
-      *ksymp = GDK_KEY_Print; break;
-    case VK_SNAPSHOT:
-      *ksymp = GDK_KEY_Print; break;
-    case VK_EXECUTE:
-      *ksymp = GDK_KEY_Execute; break;
-    case VK_INSERT:
-      *ksymp = GDK_KEY_Insert; break;
-    case VK_DELETE:
-      *ksymp = GDK_KEY_Delete; break;
-    case VK_HELP:
-      *ksymp = GDK_KEY_Help; break;
-    case VK_LWIN:
-      *ksymp = GDK_KEY_Meta_L; break;
-    case VK_RWIN:
-      *ksymp = GDK_KEY_Meta_R; break;
-    case VK_APPS:
-      *ksymp = GDK_KEY_Menu; break;
-    case VK_DECIMAL:
-      *ksymp = GDK_KEY_KP_Decimal; break;
-    case VK_MULTIPLY:
-      *ksymp = GDK_KEY_KP_Multiply; break;
-    case VK_ADD:
-      *ksymp = GDK_KEY_KP_Add; break;
-    case VK_SEPARATOR:
-      *ksymp = GDK_KEY_KP_Separator; break;
-    case VK_SUBTRACT:
-      *ksymp = GDK_KEY_KP_Subtract; break;
-    case VK_DIVIDE:
-      *ksymp = GDK_KEY_KP_Divide; break;
-    case VK_NUMPAD0:
-      *ksymp = GDK_KEY_KP_0; break;
-    case VK_NUMPAD1:
-      *ksymp = GDK_KEY_KP_1; break;
-    case VK_NUMPAD2:
-      *ksymp = GDK_KEY_KP_2; break;
-    case VK_NUMPAD3:
-      *ksymp = GDK_KEY_KP_3; break;
-    case VK_NUMPAD4:
-      *ksymp = GDK_KEY_KP_4; break;
-    case VK_NUMPAD5:
-      *ksymp = GDK_KEY_KP_5; break;
-    case VK_NUMPAD6:
-      *ksymp = GDK_KEY_KP_6; break;
-    case VK_NUMPAD7:
-      *ksymp = GDK_KEY_KP_7; break;
-    case VK_NUMPAD8:
-      *ksymp = GDK_KEY_KP_8; break;
-    case VK_NUMPAD9:
-      *ksymp = GDK_KEY_KP_9; break;
-    case VK_F1:
-      *ksymp = GDK_KEY_F1; break;
-    case VK_F2:
-      *ksymp = GDK_KEY_F2; break;
-    case VK_F3:
-      *ksymp = GDK_KEY_F3; break;
-    case VK_F4:
-      *ksymp = GDK_KEY_F4; break;
-    case VK_F5:
-      *ksymp = GDK_KEY_F5; break;
-    case VK_F6:
-      *ksymp = GDK_KEY_F6; break;
-    case VK_F7:
-      *ksymp = GDK_KEY_F7; break;
-    case VK_F8:
-      *ksymp = GDK_KEY_F8; break;
-    case VK_F9:
-      *ksymp = GDK_KEY_F9; break;
-    case VK_F10:
-      *ksymp = GDK_KEY_F10; break;
-    case VK_F11:
-      *ksymp = GDK_KEY_F11; break;
-    case VK_F12:
-      *ksymp = GDK_KEY_F12; break;
-    case VK_F13:
-      *ksymp = GDK_KEY_F13; break;
-    case VK_F14:
-      *ksymp = GDK_KEY_F14; break;
-    case VK_F15:
-      *ksymp = GDK_KEY_F15; break;
-    case VK_F16:
-      *ksymp = GDK_KEY_F16; break;
-    case VK_F17:
-      *ksymp = GDK_KEY_F17; break;
-    case VK_F18:
-      *ksymp = GDK_KEY_F18; break;
-    case VK_F19:
-      *ksymp = GDK_KEY_F19; break;
-    case VK_F20:
-      *ksymp = GDK_KEY_F20; break;
-    case VK_F21:
-      *ksymp = GDK_KEY_F21; break;
-    case VK_F22:
-      *ksymp = GDK_KEY_F22; break;
-    case VK_F23:
-      *ksymp = GDK_KEY_F23; break;
-    case VK_F24:
-      *ksymp = GDK_KEY_F24; break;
-    case VK_NUMLOCK:
-      *ksymp = GDK_KEY_Num_Lock; break;
-    case VK_SCROLL:
-      *ksymp = GDK_KEY_Scroll_Lock; break;
-    case VK_RSHIFT:
-      *ksymp = GDK_KEY_Shift_R; break;
-    case VK_RCONTROL:
-      *ksymp = GDK_KEY_Control_R; break;
-    case VK_RMENU:
-      *ksymp = GDK_KEY_Alt_R; break;
-    }
+  return keymap->gdkwin32_keymap_impl->load_layout_dll (dll, info);
 }
 
 static void
-set_level_vks (guchar               *key_state,
-              GdkWin32KeyLevelState level)
+init_vk_lookup_table (GdkWin32Keymap           *keymap,
+                      GdkWin32KeymapLayoutInfo *info)
 {
-  switch (level)
-    {
-    case GDK_WIN32_LEVEL_NONE:
-      key_state[VK_SHIFT] = 0;
-      key_state[VK_CAPITAL] = 0;
-      key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
-      break;
-    case GDK_WIN32_LEVEL_SHIFT:
-      key_state[VK_SHIFT] = 0x80;
-      key_state[VK_CAPITAL] = 0;
-      key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
-      break;
-    case GDK_WIN32_LEVEL_CAPSLOCK:
-      key_state[VK_SHIFT] = 0;
-      key_state[VK_CAPITAL] = 0x01;
-      key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
-      break;
-    case GDK_WIN32_LEVEL_SHIFT_CAPSLOCK:
-      key_state[VK_SHIFT] = 0x80;
-      key_state[VK_CAPITAL] = 0x01;
-      key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
-      break;
-    case GDK_WIN32_LEVEL_ALTGR:
-      key_state[VK_SHIFT] = 0;
-      key_state[VK_CAPITAL] = 0;
-      key_state[VK_CONTROL] = key_state[VK_MENU] = 0x80;
-      break;
-    case GDK_WIN32_LEVEL_SHIFT_ALTGR:
-      key_state[VK_SHIFT] = 0x80;
-      key_state[VK_CAPITAL] = 0;
-      key_state[VK_CONTROL] = key_state[VK_MENU] = 0x80;
-      break;
-    case GDK_WIN32_LEVEL_CAPSLOCK_ALTGR:
-      key_state[VK_SHIFT] = 0;
-      key_state[VK_CAPITAL] = 0x01;
-      key_state[VK_CONTROL] = key_state[VK_MENU] = 0x80;
-      break;
-    case GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR:
-      key_state[VK_SHIFT] = 0x80;
-      key_state[VK_CAPITAL] = 0x01;
-      key_state[VK_CONTROL] = key_state[VK_MENU] = 0x80;
-      break;
-    case GDK_WIN32_LEVEL_COUNT:
-      g_assert_not_reached ();
-      break;
-    }
+  keymap->gdkwin32_keymap_impl->init_vk_lookup_table (info);
 }
 
-static void
-reset_after_dead (guchar key_state[KEY_STATE_SIZE],
-                  HKL    handle)
+static BYTE
+keystate_to_modbits (GdkWin32Keymap           *keymap,
+                     GdkWin32KeymapLayoutInfo *info,
+                     const BYTE                keystate[256])
 {
-  guchar  temp_key_state[KEY_STATE_SIZE];
-  wchar_t wcs[2];
-
-  memmove (temp_key_state, key_state, KEY_STATE_SIZE);
-
-  temp_key_state[VK_SHIFT] =
-    temp_key_state[VK_CONTROL] =
-    temp_key_state[VK_CAPITAL] =
-    temp_key_state[VK_MENU] = 0;
-
-  ToUnicodeEx (VK_SPACE, MapVirtualKey (VK_SPACE, 0),
-              temp_key_state, wcs, G_N_ELEMENTS (wcs),
-              0, handle);
+  return keymap->gdkwin32_keymap_impl->keystate_to_modbits (info, keystate);
 }
 
-static void
-handle_dead (guint  keysym,
-            guint *ksymp)
+static BYTE
+modbits_to_level (GdkWin32Keymap           *keymap,
+                  GdkWin32KeymapLayoutInfo *info,
+                  BYTE                      modbits)
 {
-  switch (keysym)
-    {
-    case '"': /* 0x022 */
-      *ksymp = GDK_KEY_dead_diaeresis; break;
-    case '\'': /* 0x027 */
-      *ksymp = GDK_KEY_dead_acute; break;
-    case GDK_KEY_asciicircum: /* 0x05e */
-      *ksymp = GDK_KEY_dead_circumflex; break;
-    case GDK_KEY_grave:        /* 0x060 */
-      *ksymp = GDK_KEY_dead_grave; break;
-    case GDK_KEY_asciitilde: /* 0x07e */
-      *ksymp = GDK_KEY_dead_tilde; break;
-    case GDK_KEY_diaeresis: /* 0x0a8 */
-      *ksymp = GDK_KEY_dead_diaeresis; break;
-    case GDK_KEY_degree: /* 0x0b0 */
-      *ksymp = GDK_KEY_dead_abovering; break;
-    case GDK_KEY_acute:        /* 0x0b4 */
-      *ksymp = GDK_KEY_dead_acute; break;
-    case GDK_KEY_periodcentered: /* 0x0b7 */
-      *ksymp = GDK_KEY_dead_abovedot; break;
-    case GDK_KEY_cedilla: /* 0x0b8 */
-      *ksymp = GDK_KEY_dead_cedilla; break;
-    case GDK_KEY_breve:        /* 0x1a2 */
-      *ksymp = GDK_KEY_dead_breve; break;
-    case GDK_KEY_ogonek: /* 0x1b2 */
-      *ksymp = GDK_KEY_dead_ogonek; break;
-    case GDK_KEY_caron:        /* 0x1b7 */
-      *ksymp = GDK_KEY_dead_caron; break;
-    case GDK_KEY_doubleacute: /* 0x1bd */
-      *ksymp = GDK_KEY_dead_doubleacute; break;
-    case GDK_KEY_abovedot: /* 0x1ff */
-      *ksymp = GDK_KEY_dead_abovedot; break;
-    case 0x1000384: /* Greek tonos */
-      *ksymp = GDK_KEY_dead_acute; break;
-    case GDK_KEY_Greek_accentdieresis: /* 0x7ae */
-      *ksymp = GDK_KEY_Greek_accentdieresis; break;
-    default:
-      /* By default use the keysym as such. This takes care of for
-       * instance the dead U+09CD (BENGALI VIRAMA) on the ekushey
-       * Bengali layout.
-       */
-      *ksymp = keysym; break;
-    }
+  return keymap->gdkwin32_keymap_impl->modbits_to_level (info, modbits);
 }
 
-/* keypad decimal mark depends on active keyboard layout
- * return current decimal mark as unicode character
- */
-guint32
-_gdk_win32_keymap_get_decimal_mark (GdkWin32Keymap *keymap)
+static WCHAR
+vk_to_char_fuzzy (GdkWin32Keymap           *keymap,
+                  GdkWin32KeymapLayoutInfo *info,
+                  const BYTE                keystate[256],
+                  BYTE                      extra_mod_bits,
+                  BYTE                     *consumed_mod_bits,
+                  gboolean                 *is_dead,
+                  BYTE                      vk)
 {
-  if (keymap != NULL &&
-      keymap->layout_handles->len > 0 &&
-      g_array_index (keymap->options, GdkWin32KeyGroupOptions, keymap->active_layout).decimal_mark)
-    return g_array_index (keymap->options, GdkWin32KeyGroupOptions, keymap->active_layout).decimal_mark;
-
-  return (guint32) '.';
+  return keymap->gdkwin32_keymap_impl->vk_to_char_fuzzy (info, keystate, extra_mod_bits,
+                                                         consumed_mod_bits, is_dead, vk);
 }
 
-static gboolean
-layouts_are_the_same (GArray *array, HKL *hkls, int hkls_len)
+/* 
+ * Get the file path of the keyboard layout dll.
+ * The result is heap-allocated and should be freed with g_free().
+ */
+static char*
+get_keyboard_layout_file (const char *layout_name)
 {
-  int i;
+  HKEY   hkey          = 0;
+  DWORD  var_type      = REG_SZ;
+  char  *result        = NULL;
+  DWORD  file_name_len = 0;
+  int    dir_len       = 0;
+  int    buf_len       = 0;
 
-  if (hkls_len != array->len)
-    return FALSE;
+  static const char prefix[] = "SYSTEM\\CurrentControlSet\\Control\\"
+                               "Keyboard Layouts\\";
+  char kbdKeyPath[sizeof (prefix) + KL_NAMELENGTH];
 
-  for (i = 0; i < hkls_len; i++)
-    if (hkls[i] != g_array_index (array, HKL, i))
-      return FALSE;
+  g_snprintf (kbdKeyPath, sizeof (prefix) + KL_NAMELENGTH, "%s%s", prefix,
+              layout_name);
 
-  return TRUE;
-}
+  if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, (LPCSTR) kbdKeyPath, 0,
+                     KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS)
+    goto fail1;
 
-static void
-check_that_active_layout_is_in_sync (GdkWin32Keymap *keymap)
-{
-  HKL     hkl;
-  HKL     cached_hkl;
-  wchar_t hkl_name[KL_NAMELENGTH];
+  /* Get sizes */
+  if (RegQueryValueExA (hkey, "Layout File", 0, &var_type, 0,
+                       &file_name_len) != ERROR_SUCCESS)
+    goto fail2;
 
-  if (keymap->layout_handles->len <= 0)
-    return;
+  dir_len = GetSystemDirectoryA (0, 0); /* includes \0 */
+  if (dir_len == 0)
+    goto fail2;
 
-  hkl = GetKeyboardLayout (0);
-  cached_hkl = g_array_index (keymap->layout_handles, HKL, keymap->active_layout);
+  /* Allocate buffer */
+  buf_len = dir_len + (int) strlen ("\\") + file_name_len;
+  result = (char*) g_malloc (buf_len);
 
-  if (hkl != cached_hkl)
-    {
-      if (!GetKeyboardLayoutNameW (hkl_name))
-        wcscpy_s (hkl_name, KL_NAMELENGTH, L"(NULL)");
+  /* Append system directory. The -1 is because dir_len includes \0 */
+  if (GetSystemDirectoryA (&result[0], dir_len) != dir_len - 1)
+    goto fail3;
 
-      g_warning ("Cached active layout #%d (0x%p) does not match actual layout %S, 0x%p",
-                 keymap->active_layout, cached_hkl, hkl_name, hkl);
-    }
-}
+  /* Append directory separator */
+  result[dir_len - 1] = '\\';
 
-static int
-sort_key_nodes_by_gdk_keyval (gconstpointer a,
-                              gconstpointer b)
-{
-  const GdkWin32KeyNode *one = a;
-  const GdkWin32KeyNode *two = b;
+  /* Append file name */
+  if (RegQueryValueExA (hkey, "Layout File", 0, &var_type,
+                       (LPBYTE) &result[dir_len], &file_name_len)
+      != ERROR_SUCCESS)
+    goto fail3;
 
-  if (one->gdk_keycode < two->gdk_keycode)
-    return -1;
-  else if (one->gdk_keycode > two->gdk_keycode)
-    return 1;
+  result[dir_len + file_name_len] = '\0';
 
-  if (one->level < two->level)
-    return -1;
-  else if (one->level > two->level)
-    return 1;
+  RegCloseKey (hkey);
+  return result;
 
-  return 0;
+fail3:
+  g_free (result);
+fail2:
+  RegCloseKey (hkey);
+fail1:
+  return NULL;
 }
 
 static void
-update_keymap (GdkKeymap *gdk_keymap)
-{
-  int                      hkls_len;
-  static int               hkls_size = 0;
-  static HKL              *hkls = NULL;
-  gboolean                 no_list;
-  static guint             current_serial = 0;
-  int                      i, group;
-  GdkWin32KeyLevelState    level;
-  GdkWin32KeyGroupOptions *options;
-  GdkWin32Keymap          *keymap = GDK_WIN32_KEYMAP (gdk_keymap);
-  int                      keysym_tab_size;
-
-  guchar                   key_state[KEY_STATE_SIZE];
-  guint                    scancode;
-  guint                    vk;
-  guint                   *keygroup;
-
-  if (keymap->keysym_tab != NULL &&
-      current_serial == _gdk_keymap_serial)
-    return;
-
-  no_list = FALSE;
-  hkls_len = GetKeyboardLayoutList (0, NULL);
-
-  if (hkls_len <= 0)
-    {
-      hkls_len = 1;
-      no_list = TRUE;
-    }
-  else if (hkls_len > 255)
-    {
-      hkls_len = 255;
-    }
+clear_keyboard_layout_info (gpointer data)
+{
+  GdkWin32KeymapLayoutInfo *layout_info = (GdkWin32KeymapLayoutInfo*) data;
+
+  g_free (layout_info->file);
+
+  if (layout_info->key_entries != NULL)
+    g_array_unref (layout_info->key_entries);
+
+  if (layout_info->reverse_lookup_table != NULL)
+    g_hash_table_destroy (layout_info->reverse_lookup_table);
+
+  if (layout_info->lib != NULL)
+    FreeLibrary (layout_info->lib);
+
+  memset (layout_info, 0, sizeof (GdkWin32KeymapLayoutInfo));
+}
+
+#define DEFINE_SPECIAL(map)                 \
+  map (VK_CANCEL,     GDK_KEY_Cancel)       \
+  map (VK_BACK,       GDK_KEY_BackSpace)    \
+  map (VK_CLEAR,      GDK_KEY_Clear)        \
+  map (VK_RETURN,     GDK_KEY_Return)       \
+  map (VK_LSHIFT,     GDK_KEY_Shift_L)      \
+  map (VK_LCONTROL,   GDK_KEY_Control_L)    \
+  map (VK_LMENU,      GDK_KEY_Alt_L)        \
+  map (VK_PAUSE,      GDK_KEY_Pause)        \
+  map (VK_ESCAPE,     GDK_KEY_Escape)       \
+  map (VK_PRIOR,      GDK_KEY_Prior)        \
+  map (VK_NEXT,       GDK_KEY_Next)         \
+  map (VK_END,        GDK_KEY_End)          \
+  map (VK_HOME,       GDK_KEY_Home)         \
+  map (VK_LEFT,       GDK_KEY_Left)         \
+  map (VK_UP,         GDK_KEY_Up)           \
+  map (VK_RIGHT,      GDK_KEY_Right)        \
+  map (VK_DOWN,       GDK_KEY_Down)         \
+  map (VK_SELECT,     GDK_KEY_Select)       \
+  map (VK_PRINT,      GDK_KEY_Print)        \
+  map (VK_EXECUTE,    GDK_KEY_Execute)      \
+  map (VK_INSERT,     GDK_KEY_Insert)       \
+  map (VK_DELETE,     GDK_KEY_Delete)       \
+  map (VK_HELP,       GDK_KEY_Help)         \
+  map (VK_LWIN,       GDK_KEY_Meta_L)       \
+  map (VK_RWIN,       GDK_KEY_Meta_R)       \
+  map (VK_APPS,       GDK_KEY_Menu)         \
+  map (VK_DECIMAL,    GDK_KEY_KP_Decimal)   \
+  map (VK_MULTIPLY,   GDK_KEY_KP_Multiply)  \
+  map (VK_ADD,        GDK_KEY_KP_Add)       \
+  map (VK_SEPARATOR,  GDK_KEY_KP_Separator) \
+  map (VK_SUBTRACT,   GDK_KEY_KP_Subtract)  \
+  map (VK_DIVIDE,     GDK_KEY_KP_Divide)    \
+  map (VK_NUMPAD0,    GDK_KEY_KP_0)         \
+  map (VK_NUMPAD1,    GDK_KEY_KP_1)         \
+  map (VK_NUMPAD2,    GDK_KEY_KP_2)         \
+  map (VK_NUMPAD3,    GDK_KEY_KP_3)         \
+  map (VK_NUMPAD4,    GDK_KEY_KP_4)         \
+  map (VK_NUMPAD5,    GDK_KEY_KP_5)         \
+  map (VK_NUMPAD6,    GDK_KEY_KP_6)         \
+  map (VK_NUMPAD7,    GDK_KEY_KP_7)         \
+  map (VK_NUMPAD8,    GDK_KEY_KP_8)         \
+  map (VK_NUMPAD9,    GDK_KEY_KP_9)         \
+  map (VK_F1,         GDK_KEY_F1)           \
+  map (VK_F2,         GDK_KEY_F2)           \
+  map (VK_F3,         GDK_KEY_F3)           \
+  map (VK_F4,         GDK_KEY_F4)           \
+  map (VK_F5,         GDK_KEY_F5)           \
+  map (VK_F6,         GDK_KEY_F6)           \
+  map (VK_F7,         GDK_KEY_F7)           \
+  map (VK_F8,         GDK_KEY_F8)           \
+  map (VK_F9,         GDK_KEY_F9)           \
+  map (VK_F10,        GDK_KEY_F10)          \
+  map (VK_F11,        GDK_KEY_F11)          \
+  map (VK_F12,        GDK_KEY_F12)          \
+  map (VK_F13,        GDK_KEY_F13)          \
+  map (VK_F14,        GDK_KEY_F14)          \
+  map (VK_F15,        GDK_KEY_F15)          \
+  map (VK_F16,        GDK_KEY_F16)          \
+  map (VK_F17,        GDK_KEY_F17)          \
+  map (VK_F18,        GDK_KEY_F18)          \
+  map (VK_F19,        GDK_KEY_F19)          \
+  map (VK_F20,        GDK_KEY_F20)          \
+  map (VK_F21,        GDK_KEY_F21)          \
+  map (VK_F22,        GDK_KEY_F22)          \
+  map (VK_F23,        GDK_KEY_F23)          \
+  map (VK_F24,        GDK_KEY_F24)          \
+  map (VK_NUMLOCK,    GDK_KEY_Num_Lock)     \
+  map (VK_SCROLL,     GDK_KEY_Scroll_Lock)  \
+  map (VK_RSHIFT,     GDK_KEY_Shift_R)      \
+  map (VK_RCONTROL,   GDK_KEY_Control_R)    \
+  map (VK_RMENU,      GDK_KEY_Alt_R)  
+
+
+#define DEFINE_DEAD(map)                                                      \
+  map ('"',                          /* 0x022 */ GDK_KEY_dead_diaeresis)      \
+  map ('\'',                         /* 0x027 */ GDK_KEY_dead_acute)          \
+  map (GDK_KEY_asciicircum,          /* 0x05e */ GDK_KEY_dead_circumflex)     \
+  map (GDK_KEY_grave,                /* 0x060 */ GDK_KEY_dead_grave)          \
+  map (GDK_KEY_asciitilde,           /* 0x07e */ GDK_KEY_dead_tilde)          \
+  map (GDK_KEY_diaeresis,            /* 0x0a8 */ GDK_KEY_dead_diaeresis)      \
+  map (GDK_KEY_degree,               /* 0x0b0 */ GDK_KEY_dead_abovering)      \
+  map (GDK_KEY_acute,                /* 0x0b4 */ GDK_KEY_dead_acute)          \
+  map (GDK_KEY_periodcentered,       /* 0x0b7 */ GDK_KEY_dead_abovedot)       \
+  map (GDK_KEY_cedilla,              /* 0x0b8 */ GDK_KEY_dead_cedilla)        \
+  map (GDK_KEY_breve,                /* 0x1a2 */ GDK_KEY_dead_breve)          \
+  map (GDK_KEY_ogonek,               /* 0x1b2 */ GDK_KEY_dead_ogonek)         \
+  map (GDK_KEY_caron,                /* 0x1b7 */ GDK_KEY_dead_caron)          \
+  map (GDK_KEY_doubleacute,          /* 0x1bd */ GDK_KEY_dead_doubleacute)    \
+  map (GDK_KEY_abovedot,             /* 0x1ff */ GDK_KEY_dead_abovedot)       \
+  map (0x1000384,              /* Greek tonos */ GDK_KEY_dead_acute)          \
+  map (GDK_KEY_Greek_accentdieresis, /* 0x7ae */ GDK_KEY_Greek_accentdieresis)
 
-  if (hkls_size < hkls_len)
-    {
-      hkls = g_renew (HKL, hkls, hkls_len);
-      hkls_size = hkls_len;
-    }
 
-  if (hkls_len != GetKeyboardLayoutList (hkls_len, hkls))
-    {
-      if (!no_list)
-        return;
+static guint
+vk_and_mod_bits_to_gdk_keysym (GdkWin32Keymap     *keymap,
+                               GdkWin32KeymapLayoutInfo *info,
+                               guint               vk,
+                               const BYTE          keystate[256],
+                               BYTE                mod_bits,
+                               BYTE               *consumed_mod_bits)
 
-      hkls[0] = GetKeyboardLayout (0);
-      hkls_len = 1;
-    }
+{
+  gboolean is_dead = FALSE;
+  gunichar c;
+  guint    sym;
 
-  if (layouts_are_the_same (keymap->layout_handles, hkls, hkls_len))
-    {
-      check_that_active_layout_is_in_sync (keymap);
-      current_serial = _gdk_keymap_serial;
+  if (consumed_mod_bits)
+    *consumed_mod_bits = 0;
 
-      return;
+  /* Handle special key: Tab */
+  if (vk == VK_TAB)
+    {
+      if (consumed_mod_bits)
+        *consumed_mod_bits = mod_bits & KBDSHIFT;
+      return (mod_bits & KBDSHIFT) ? GDK_KEY_ISO_Left_Tab : GDK_KEY_Tab;
     }
 
-  GDK_NOTE (EVENTS, g_print ("\nHave %d keyboard layouts:", hkls_len));
-
-  for (i = 0; i < hkls_len; i++)
+  /* Handle other special keys */
+  switch (vk)
     {
-      GDK_NOTE (EVENTS, g_print (" 0x%p", hkls[i]));
+      #define MAP(a_vk, a_gdk) case a_vk: return a_gdk;
 
-      if (GetKeyboardLayout (0) == hkls[i])
-        {
-          wchar_t hkl_name[KL_NAMELENGTH];
+      DEFINE_SPECIAL (MAP)
 
-          if (!GetKeyboardLayoutNameW (hkl_name))
-            wcscpy_s (hkl_name, KL_NAMELENGTH, L"(NULL)");
+      /* Non-bijective mappings: */
+      MAP (VK_SHIFT,    GDK_KEY_Shift_L)
+      MAP (VK_CONTROL,  GDK_KEY_Control_L)
+      MAP (VK_MENU,     GDK_KEY_Alt_L)
+      MAP (VK_SNAPSHOT, GDK_KEY_Print)
 
-          GDK_NOTE (EVENTS, g_print ("(active, %S)", hkl_name));
-        }
+      #undef MAP
     }
 
-  GDK_NOTE (EVENTS, g_print ("\n"));
-
-  keysym_tab_size = hkls_len * 256 * 2 * 4;
+  /* Handle regular keys (including dead keys) */
+  c = vk_to_char_fuzzy (keymap, info, keystate, mod_bits,
+                        consumed_mod_bits, &is_dead, vk);
 
-  if (hkls_len != keymap->layout_handles->len)
-    keymap->keysym_tab = g_renew (guint, keymap->keysym_tab, keysym_tab_size);
+  if (c == WCH_NONE)
+    return GDK_KEY_VoidSymbol;
 
-  memset (key_state, 0, sizeof(key_state));
-  memset (keymap->keysym_tab, 0, keysym_tab_size);
-  g_array_set_size (keymap->layout_handles, hkls_len);
-  g_array_set_size (keymap->options, hkls_len);
+  sym = gdk_unicode_to_keyval (c);
 
-  for (i = 0; i < hkls_len; i++)
+  if (is_dead)
     {
-      options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, i);
-
-      options->decimal_mark = 0;
-      options->scancode_rshift = 0;
-      options->has_altgr = FALSE;
-      options->dead_keys = g_array_new (FALSE, FALSE, sizeof (GdkWin32KeyNode));
-      g_array_set_clear_func (options->dead_keys, (GDestroyNotify) gdk_win32_key_node_clear);
-
-      g_array_index (keymap->layout_handles, HKL, i) = hkls[i];
-
-      if (hkls[i] == _gdk_input_locale)
-        keymap->active_layout = i;
+      switch (sym)
+       {
+         #define MAP(a_nondead, a_dead) case a_nondead: return a_dead;
+         DEFINE_DEAD (MAP)
+         #undef MAP
+       }
     }
 
-  for (vk = 0; vk < KEY_STATE_SIZE; vk++)
-    {
-      for (group = 0; group < hkls_len; group++)
-        {
-          options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, group);
-          scancode = MapVirtualKeyEx (vk, 0, hkls[group]);
-          keygroup = &keymap->keysym_tab[(vk * hkls_len + group) * GDK_WIN32_LEVEL_COUNT];
-
-          /* MapVirtualKeyEx() fails to produce a scancode for VK_DIVIDE and VK_PAUSE.
-           * Ignore that, handle_special() will figure out a Gdk keyval for these
-           * without needing a scancode.
-           */
-          if (scancode == 0 &&
-              vk != VK_DIVIDE &&
-              vk != VK_PAUSE)
-            {
-              for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
-                keygroup[level] = GDK_KEY_VoidSymbol;
-
-              continue;
-            }
-
-          if (vk == VK_RSHIFT)
-            options->scancode_rshift = scancode;
-
-          key_state[vk] = 0x80;
+  return sym;
+}
 
-          for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
-            {
-              guint *ksymp = &keygroup[level];
+static int
+gdk_keysym_to_key_entry_index (GdkWin32KeymapLayoutInfo *info,
+                               guint               sym)
+{
+  gunichar c;
+  gintptr  index;
 
-              set_level_vks (key_state, level);
+  /* Special cases */
+  if (sym == GDK_KEY_Tab)
+    return VK_TAB;
+  if (sym == GDK_KEY_ISO_Left_Tab)
+    return 256;
 
-              *ksymp = 0;
+  /* Generic non-printable keys */
+  switch (sym)
+    {
+      #define MAP(a_vk, a_gdk) case a_gdk: return a_vk;
+      DEFINE_SPECIAL (MAP)
+      #undef MAP
+    }
 
-              /* First, handle those virtual keys that we always want
-               * as special GDK_* keysyms, even if ToAsciiEx might
-               * turn some them into an ASCII character (like TAB and
-               * ESC).
-               */
-              handle_special (vk, ksymp, level);
+  /* Fix up dead keys */
+  #define MAP(a_nondead, a_dead) \
+    if (sym == a_dead)           \
+      sym = a_nondead;
+  DEFINE_DEAD (MAP)
+  #undef MAP
 
-              if ((*ksymp == 0) ||
-                  ((vk == VK_DECIMAL) && (level == GDK_WIN32_LEVEL_NONE)))
-                {
-                  wchar_t         wcs[10];
-                  int             k;
-                  guint           keysym;
-                  GdkWin32KeyNode dead_key;
-
-                  wcs[0] = wcs[1] = 0;
-                  k = ToUnicodeEx (vk, scancode, key_state,
-                                   wcs, G_N_ELEMENTS (wcs),
-                                   0, hkls[group]);
-#if 0
-                  g_print ("ToUnicodeEx(%#02x, %d: %d): %d, %04x %04x\n",
-                           vk, scancode, level, k,
-                           wcs[0], wcs[1]);
-#endif
-                  switch (k)
-                    {
-                    case 1:
-                      if ((vk == VK_DECIMAL) && (level == GDK_WIN32_LEVEL_NONE))
-                        options->decimal_mark = wcs[0];
-                      else
-                        *ksymp = gdk_unicode_to_keyval (wcs[0]);
-                      break;
-                    case -1:
-                      keysym = gdk_unicode_to_keyval (wcs[0]);
-
-                      /* It is a dead key, and it has been stored in
-                       * the keyboard layout's state by
-                       * ToAsciiEx()/ToUnicodeEx(). Yes, this is an
-                       * incredibly silly API! Make the keyboard
-                       * layout forget it by calling
-                       * ToAsciiEx()/ToUnicodeEx() once more, with the
-                       * virtual key code and scancode for the
-                       * spacebar, without shift or AltGr. Otherwise
-                       * the next call to ToAsciiEx() with a different
-                       * key would try to combine with the dead key.
-                       */
-                      reset_after_dead (key_state, hkls[group]);
-
-                      /* Use dead keysyms instead of "undead" ones */
-                      handle_dead (keysym, ksymp);
-
-                      dead_key.undead_gdk_keycode = keysym;
-                      dead_key.vk = vk;
-                      dead_key.level = level;
-                      dead_key.gdk_keycode = *ksymp;
-                      dead_key.combinations = NULL;
-                      g_array_append_val (options->dead_keys, dead_key);
-                      break;
-                    case 0:
-                      /* Seems to be necessary to "reset" the keyboard layout
-                       * in this case, too. Otherwise problems on NT4.
-                       */
-                      reset_after_dead (key_state, hkls[group]);
-                      break;
-                    default:
-#if 0
-                      GDK_NOTE (EVENTS,
-                                g_print ("ToUnicodeEx returns %d "
-                                         "for vk:%02x, sc:%02x%s%s\n",
-                                         k, vk, scancode,
-                                         (shift&0x1 ? " shift" : ""),
-                                         (shift&0x2 ? " altgr" : "")));
-#endif
-                      break;
-                    }
-                }
+  /* Try converting to Unicode and back */
+  c = gdk_keyval_to_unicode (sym);
 
-              if (*ksymp == 0)
-                *ksymp = GDK_KEY_VoidSymbol;
-            }
+  g_return_val_if_fail (info->reverse_lookup_table != NULL, -1);
 
-          key_state[vk] = 0;
-
-          /* Check if keyboard has an AltGr key by checking if
-           * the mapping with Control+Alt is different.
-           * Don't test CapsLock here, as it does not seem to affect
-           * dead keys themselves, only the results of dead key combinations.
-           */
-          if (!options->has_altgr)
-            if ((keygroup[GDK_WIN32_LEVEL_ALTGR] != GDK_KEY_VoidSymbol &&
-                 keygroup[GDK_WIN32_LEVEL_NONE] != keygroup[GDK_WIN32_LEVEL_ALTGR]) ||
-                (keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR] != GDK_KEY_VoidSymbol &&
-                 keygroup[GDK_WIN32_LEVEL_SHIFT] != keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR]))
-              options->has_altgr = TRUE;
-        }
+  index = -1;
+  if (g_hash_table_lookup_extended (info->reverse_lookup_table,
+                                    GINT_TO_POINTER (c),
+                                    NULL, (gpointer*) &index))
+    {
+      return index;
     }
-
-  scancode = 0x0;
-
-  for (group = 0; group < hkls_len; group++)
+  else
     {
-      options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, group);
-
-      for (i = 0; i < options->dead_keys->len; i++)
-        {
-          wchar_t          wcs[10];
-          int              k;
-          GdkWin32KeyNode *dead_key;
-          GdkWin32KeyNode  combo;
-
-          dead_key = &g_array_index (options->dead_keys, GdkWin32KeyNode, i);
-
-          for (vk = 0; vk < KEY_STATE_SIZE; vk++)
-            {
-              for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
-                {
-                  /* Prime the ToUnicodeEx() internal state */
-                  wcs[0] = wcs[1] = 0;
-                  set_level_vks (key_state, dead_key->level);
-                  k = ToUnicodeEx (dead_key->vk, scancode, key_state,
-                                   wcs, G_N_ELEMENTS (wcs),
-                                   0, hkls[group]);
-                  switch (k)
-                    {
-                    case -1:
-                      /* Okay */
-                      break;
-                    default:
-                      /* Expected a dead key, got something else */
-                      reset_after_dead (key_state, hkls[group]);
-                      continue;
-                    }
-
-                  /* Check how it combines with vk */
-                  wcs[0] = wcs[1] = 0;
-                  set_level_vks (key_state, level);
-                  k = ToUnicodeEx (vk, scancode, key_state,
-                                   wcs, G_N_ELEMENTS (wcs),
-                                   0, hkls[group]);
-
-                  if (k == 0)
-                    {
-                      reset_after_dead (key_state, hkls[group]);
-                    }
-                  else if (k == -1)
-                    {
-                      /* Dead key chaining? TODO: support this (deeper tree?) */
-                      reset_after_dead (key_state, hkls[group]);
-                    }
-                  else if (k == 1)
-                    {
-                      combo.vk = vk;
-                      combo.level = level;
-                      combo.gdk_keycode = gdk_unicode_to_keyval (wcs[0]);
-                      combo.undead_gdk_keycode = combo.gdk_keycode;
-                      combo.combinations = NULL;
-
-                      if (dead_key->combinations == NULL)
-                        {
-                          dead_key->combinations = g_array_new (FALSE, FALSE, sizeof (GdkWin32KeyNode));
-                          g_array_set_clear_func (dead_key->combinations, (GDestroyNotify) 
gdk_win32_key_node_clear);
-                        }
-
-#if 0
-                      {
-                        char *dead_key_undead_u8, *wcs_u8;
-                        wchar_t t = gdk_keyval_to_unicode (dead_key->undead_gdk_keycode);
-                        dead_key_undead_u8 = g_utf16_to_utf8 (&t, 1, NULL, NULL, NULL);
-                        wcs_u8 = g_utf16_to_utf8 (wcs, 1, NULL, NULL, NULL);
-                        g_fprintf (stdout, "%d %s%s%s0x%02x (%s) + %s%s%s0x%02x = 0x%04x (%s)\n", group,
-                                 (dead_key->level == GDK_WIN32_LEVEL_SHIFT ||
-                                  dead_key->level == GDK_WIN32_LEVEL_SHIFT_ALTGR ||
-                                  dead_key->level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK ||
-                                  dead_key->level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "SHIFT-" : "    
  ",
-                                 (dead_key->level == GDK_WIN32_LEVEL_CAPSLOCK ||
-                                  dead_key->level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK ||
-                                  dead_key->level == GDK_WIN32_LEVEL_CAPSLOCK_ALTGR ||
-                                  dead_key->level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "CAPSLOCK-" : " 
        ",
-                                 (dead_key->level == GDK_WIN32_LEVEL_ALTGR ||
-                                  dead_key->level == GDK_WIN32_LEVEL_SHIFT_ALTGR ||
-                                  dead_key->level == GDK_WIN32_LEVEL_CAPSLOCK_ALTGR ||
-                                  dead_key->level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "ALTGR-" : "    
  ",
-                                 dead_key->vk,
-                                 dead_key_undead_u8,
-                                 (combo.level == GDK_WIN32_LEVEL_SHIFT ||
-                                  combo.level == GDK_WIN32_LEVEL_SHIFT_ALTGR ||
-                                  combo.level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK ||
-                                  combo.level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "SHIFT-" : "      ",
-                                 (combo.level == GDK_WIN32_LEVEL_CAPSLOCK ||
-                                  combo.level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK ||
-                                  combo.level == GDK_WIN32_LEVEL_CAPSLOCK_ALTGR ||
-                                  combo.level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "CAPSLOCK-" : "     
    ",
-                                 (combo.level == GDK_WIN32_LEVEL_ALTGR ||
-                                  combo.level == GDK_WIN32_LEVEL_SHIFT_ALTGR ||
-                                  combo.level == GDK_WIN32_LEVEL_CAPSLOCK_ALTGR ||
-                                  combo.level == GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR) ? "ALTGR-" : "      ",
-                                 vk,
-                                 wcs[0],
-                                 wcs_u8);
-                        g_free (dead_key_undead_u8);
-                        g_free (wcs_u8);
-                      }
-#endif
-
-                      g_array_append_val (dead_key->combinations, combo);
-                    }
-                }
-            }
-        }
-
-       g_array_sort (options->dead_keys, (GCompareFunc) sort_key_nodes_by_gdk_keyval);
+      return -1;
     }
-
-  GDK_NOTE (EVENTS, print_keysym_tab (keymap));
-
-  check_that_active_layout_is_in_sync (keymap);
-  current_serial = _gdk_keymap_serial;
 }
 
-static gboolean
-find_deadkey_by_keyval (GArray *dead_keys, guint16 keyval, gsize *index)
+static GdkModifierType
+mod_bits_to_gdk_mod_mask (BYTE mod_bits)
 {
-  gsize deadkey_i;
-  gsize deadkey_i_max;
-
-  if (dead_keys->len == 0)
-    return FALSE;
+  GdkModifierType result = 0;
+  if (mod_bits & KBDSHIFT)
+    result |= GDK_SHIFT_MASK;
+  if (mod_bits & KBDCTRL)
+    result |= GDK_CONTROL_MASK;
+  if (mod_bits & KBDALT)
+    result |= GDK_ALT_MASK;
+  return result;
+}
 
-  deadkey_i = 0;
-  deadkey_i_max = dead_keys->len - 1;
+static BYTE
+gdk_mod_mask_to_mod_bits (GdkModifierType mod_mask)
+{
+  BYTE result = 0;
+  if (mod_mask & GDK_SHIFT_MASK)
+    result |= KBDSHIFT;
+  if (mod_mask & GDK_CONTROL_MASK)
+    result |= KBDCTRL;
+  if (mod_mask & GDK_ALT_MASK)
+    result |= KBDALT;
+  return result;
+}
 
-  while (deadkey_i != deadkey_i_max)
+static void
+get_lock_state (BYTE lock_state[256])
+{
+  static const guint mode_keys[] =
     {
-      GdkWin32KeyNode *dead_key;
-      gsize middle;
-
-      if (g_array_index (dead_keys, GdkWin32KeyNode, deadkey_i).gdk_keycode == keyval)
-        {
-          break;
-        }
-      else if (g_array_index (dead_keys, GdkWin32KeyNode, deadkey_i_max).gdk_keycode == keyval)
-        {
-          deadkey_i = deadkey_i_max;
-          break;
-        }
-      else if (deadkey_i + 1 == deadkey_i_max)
-        {
-          break;
-        }
+      VK_CAPITAL,
+      VK_KANA, VK_HANGUL, VK_JUNJA, VK_FINAL, VK_HANJA, VK_KANJI, /* Is this correct? */
+      VK_NUMLOCK, VK_SCROLL
+    };
 
-      middle = deadkey_i + (deadkey_i_max - deadkey_i) / 2;
-      dead_key = &g_array_index (dead_keys, GdkWin32KeyNode, middle);
+  BYTE keystate[256] = {0};
+  guint i;
 
-      if (dead_key->gdk_keycode < keyval)
-        deadkey_i = middle;
-      else if (dead_key->gdk_keycode > keyval)
-        deadkey_i_max = middle;
-      else
-        deadkey_i = deadkey_i_max = middle;
-    }
+  GetKeyboardState (keystate);
 
-  if (g_array_index (dead_keys, GdkWin32KeyNode, deadkey_i).gdk_keycode == keyval)
-    {
-      *index = deadkey_i;
+  /* Copy over some keystates like numlock and capslock */
+  for (i = 0; i < G_N_ELEMENTS(mode_keys); ++i)
+    lock_state[mode_keys[i]] = keystate[mode_keys[i]] & 0x1;
+}
 
-      return TRUE;
-    }
 
-  return FALSE;
+/* keypad decimal mark depends on active keyboard layout
+ * return current decimal mark as unicode character
+ */
+guint32
+_gdk_win32_keymap_get_decimal_mark (GdkWin32Keymap *keymap)
+{
+  guint32 c = MapVirtualKeyW (VK_DECIMAL, MAPVK_VK_TO_CHAR);
+  if (!c)
+    c = (guint32) '.';
+  return c;
 }
 
-GdkWin32KeymapMatch
-gdk_win32_keymap_check_compose (GdkWin32Keymap *keymap,
-                                guint16        *compose_buffer,
-                                gsize           compose_buffer_len,
-                                guint16        *output,
-                                gsize          *output_len)
+static void
+update_keymap (GdkWin32Keymap *keymap)
 {
-  int partial_match;
-  guint8 active_group;
-  gsize deadkey_i, node_i;
-  GdkWin32KeyNode *dead_key;
-  GdkWin32KeyGroupOptions *options;
-  GdkWin32KeymapMatch match;
-  gsize output_size;
-
-  g_return_val_if_fail (output != NULL && output_len != NULL, GDK_WIN32_KEYMAP_MATCH_NONE);
-
-  if (compose_buffer_len < 1)
-    return GDK_WIN32_KEYMAP_MATCH_NONE;
-
-  output_size = *output_len;
-
-  active_group = _gdk_win32_keymap_get_active_group (keymap);
-  options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, active_group);
+  HKL  current_layout;
+  BOOL changed = FALSE;
+  int  n_layouts;
+  int  i;
 
-  partial_match = -1;
-  match = GDK_WIN32_KEYMAP_MATCH_NONE;
-
-  if (find_deadkey_by_keyval (options->dead_keys, compose_buffer[0], &deadkey_i))
+  if (keymap->current_serial == _gdk_keymap_serial &&
+      keymap->layout_handles->len > 0)
     {
-      while (deadkey_i > 0 &&
-             g_array_index (options->dead_keys, GdkWin32KeyNode, deadkey_i - 1).gdk_keycode == 
compose_buffer[0])
-        deadkey_i--;
-
-      /* Hardcoded 2-tier tree here (dead key + non dead key = character).
-       * TODO: support trees with arbitrary depth for dead key chaining.
-       */
-      dead_key = &g_array_index (options->dead_keys, GdkWin32KeyNode, deadkey_i);
-
-      /* "Partial match" means "matched the whole sequence except the last key"
-       * (right now the sequence only has 2 keys, so this turns into "matched
-       * at least the first key").
-       * "last key" should be identified by having NULL further combinations.
-       * As a heuristic, convert the buffer contents into keyvals and use
-       * them as-is (normally there should be a separate unichar buffer for
-       * each combination, but we do not store these).
-       */
-      partial_match = deadkey_i;
-
-      if (compose_buffer_len < 2)
-        match = GDK_WIN32_KEYMAP_MATCH_INCOMPLETE;
-
-      for (node_i = 0;
-           match != GDK_WIN32_KEYMAP_MATCH_INCOMPLETE &&
-           node_i < dead_key->combinations->len;
-           node_i++)
-        {
-          GdkWin32KeyNode *node;
-
-          node = &g_array_index (dead_key->combinations, GdkWin32KeyNode, node_i);
+      return;
+    }
 
-          if (keymap->keysym_tab[(node->vk * keymap->layout_handles->len + active_group) * 
GDK_WIN32_LEVEL_COUNT + node->level] == compose_buffer[1])
-            {
-              match = GDK_WIN32_KEYMAP_MATCH_EXACT;
-              *output_len = 0;
+  n_layouts = GetKeyboardLayoutList (0, 0);
+  g_array_set_size (keymap->layout_handles, n_layouts);
+  n_layouts = GetKeyboardLayoutList (n_layouts,
+                                    &g_array_index(keymap->layout_handles,
+                                                   HKL, 0));
 
-              if (*output_len < output_size && node->gdk_keycode != 0)
-                output[(*output_len)++] = node->gdk_keycode;
+  g_array_set_size (keymap->layout_infos, n_layouts);
 
-              break;
-            }
-        }
-    }
+  current_layout = GetKeyboardLayout (0);
 
-  if (match == GDK_WIN32_KEYMAP_MATCH_EXACT ||
-      match == GDK_WIN32_KEYMAP_MATCH_INCOMPLETE)
+  for (i = 0; i < n_layouts; ++i)
     {
-      return match;
-    }
+      GdkWin32KeymapLayoutInfo *info = &g_array_index(keymap->layout_infos,
+                                                      GdkWin32KeymapLayoutInfo, i);
+      HKL hkl = g_array_index(keymap->layout_handles, HKL, i);
 
-  if (partial_match >= 0)
-    {
-      if (compose_buffer_len == 2)
+      if (info->handle != hkl)
         {
-          dead_key = &g_array_index (options->dead_keys, GdkWin32KeyNode, partial_match);
-          *output_len = 0;
+          changed = TRUE;
+
+          /* Free old data */
+          clear_keyboard_layout_info (info);
+
+          /* Load new data */
+          info->handle = hkl;
+          ActivateKeyboardLayout (hkl, 0);
+          GetKeyboardLayoutNameA (info->name);
 
-          if (output_size >= 1)
-            output[(*output_len)++] = dead_key->undead_gdk_keycode;
+          info->file = get_keyboard_layout_file (info->name);
 
-          if (output_size >= 2)
+          if (load_layout_dll (keymap, info->file, info))
             {
-              gsize second_deadkey_i;
-
-              /* Special case for "deadkey + deadkey = space-version-of-deadkey, space-version-of-deadkey" 
combinations.
-               * Normally the result is a sequence of 2 unichars, but we do not store this.
-               * For "deadkey + nondeadkey = space-version-of-deadkey, nondeadkey", we can use compose_buffer
-               * contents as-is, but space version of a dead key need to be looked up separately.
-               */
-              if (find_deadkey_by_keyval (options->dead_keys, compose_buffer[1], &second_deadkey_i))
-                output[(*output_len)++] = g_array_index (options->dead_keys, GdkWin32KeyNode, 
second_deadkey_i).undead_gdk_keycode;
-              else
-                output[(*output_len)++] = compose_buffer[1];
+              info->key_entries = g_array_new (FALSE, FALSE,
+                                               sizeof (GdkWin32KeymapKeyEntry));
+
+              info->reverse_lookup_table = g_hash_table_new (g_direct_hash,
+                                                             g_direct_equal);
+              init_vk_lookup_table (keymap, info);
             }
         }
 
-      return GDK_WIN32_KEYMAP_MATCH_PARTIAL;
+      if (info->handle == current_layout)
+        keymap->active_layout = i;
     }
 
-  return GDK_WIN32_KEYMAP_MATCH_NONE;
+  if (changed)
+    ActivateKeyboardLayout (current_layout, 0);
+
+  keymap->current_serial = _gdk_keymap_serial;
 }
 
 guint8
 _gdk_win32_keymap_get_rshift_scancode (GdkWin32Keymap *keymap)
 {
-  if (keymap != NULL &&
-      keymap->layout_handles->len > 0)
-    return g_array_index (keymap->options, GdkWin32KeyGroupOptions, keymap->active_layout).scancode_rshift;
-
-  return 0;
+  return MapVirtualKey (VK_RSHIFT, MAPVK_VK_TO_VSC);
 }
 
 void
@@ -1163,10 +601,11 @@ _gdk_win32_keymap_set_active_layout (GdkWin32Keymap *keymap,
 gboolean
 _gdk_win32_keymap_has_altgr (GdkWin32Keymap *keymap)
 {
-  if (keymap != NULL &&
-      keymap->layout_handles->len > 0)
-    return g_array_index (keymap->options, GdkWin32KeyGroupOptions, keymap->active_layout).has_altgr;
-
+  /* We just return FALSE, since it doesn't really matter because AltGr
+   * is the same as Ctrl + Alt. Hence, we will never get a GDK_MOD2_MASK,
+   * rather we will just get GDK_CONTROL_MASK | GDK_ALT_MASK. I don't
+   * think there is any clean way to distinguish <Ctrl + Alt> from
+   * <AltGr> on Windows. */
   return FALSE;
 }
 
@@ -1191,6 +630,25 @@ _gdk_win32_display_get_keymap (GdkDisplay *display)
   return default_keymap;
 }
 
+GdkModifierType
+_gdk_win32_keymap_get_mod_mask (GdkWin32Keymap *keymap)
+{
+  GdkWin32KeymapLayoutInfo *layout_info;
+  BYTE                      keystate[256] = {0};
+  BYTE                      mod_bits;
+    
+  update_keymap (keymap);
+
+  layout_info = &g_array_index (keymap->layout_infos, GdkWin32KeymapLayoutInfo,
+                                keymap->active_layout);
+    
+  GetKeyboardState (keystate);
+    
+  mod_bits = keystate_to_modbits (keymap, layout_info, keystate);
+
+  return mod_bits_to_gdk_mod_mask (mod_bits);
+}
+
 static PangoDirection
 get_hkl_direction (HKL hkl)
 {
@@ -1213,21 +671,20 @@ get_hkl_direction (HKL hkl)
 static PangoDirection
 gdk_win32_keymap_get_direction (GdkKeymap *gdk_keymap)
 {
-  HKL active_hkl;
   GdkWin32Keymap *keymap;
-  GdkKeymap *default_keymap = gdk_display_get_keymap (gdk_display_get_default ());
+  HKL             active_hkl;
+  
+  g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), PANGO_DIRECTION_LTR);
 
-  if (gdk_keymap == NULL || gdk_keymap != default_keymap)
-    keymap = GDK_WIN32_KEYMAP (default_keymap);
-  else
-    keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+  keymap = GDK_WIN32_KEYMAP (gdk_keymap);
 
-  update_keymap (GDK_KEYMAP (keymap));
+  update_keymap (keymap);
 
   if (keymap->layout_handles->len <= 0)
     active_hkl = GetKeyboardLayout (0);
   else
-    active_hkl = g_array_index (keymap->layout_handles, HKL, keymap->active_layout);
+    active_hkl = g_array_index (keymap->layout_handles, HKL,
+                                keymap->active_layout);
 
   return get_hkl_direction (active_hkl);
 }
@@ -1239,18 +696,17 @@ gdk_win32_keymap_have_bidi_layouts (GdkKeymap *gdk_keymap)
   gboolean        have_rtl = FALSE;
   gboolean        have_ltr = FALSE;
   int             group;
-  GdkKeymap      *default_keymap = gdk_display_get_keymap (gdk_display_get_default ());
 
-  if (gdk_keymap == NULL || gdk_keymap != default_keymap)
-    keymap = GDK_WIN32_KEYMAP (default_keymap);
-  else
-    keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+  g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
+
+  keymap = GDK_WIN32_KEYMAP (gdk_keymap);
 
-  update_keymap (GDK_KEYMAP (keymap));
+  update_keymap (keymap);
 
   for (group = 0; group < keymap->layout_handles->len; group++)
     {
-      if (get_hkl_direction (g_array_index (keymap->layout_handles, HKL, group)) == PANGO_DIRECTION_RTL)
+      if (get_hkl_direction (g_array_index (keymap->layout_handles, HKL,
+                             group)) == PANGO_DIRECTION_RTL)
         have_rtl = TRUE;
       else
         have_ltr = TRUE;
@@ -1288,70 +744,76 @@ gdk_win32_keymap_get_entries_for_keyval (GdkKeymap     *gdk_keymap,
                                          guint          keyval,
                                          GArray        *retval)
 {
-  GdkKeymap *default_keymap = gdk_display_get_keymap (gdk_display_get_default ());
-  guint len = retval->len;
+  GdkWin32Keymap *keymap;
+  BYTE            keystate[256] = {0};
+  int             group;
+  guint           len = retval->len;
 
-  g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), FALSE);
+  g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
   g_return_val_if_fail (keyval != 0, FALSE);
 
-  /* Accept only the default keymap */
-  if (gdk_keymap == NULL || gdk_keymap == default_keymap)
-    {
-      int vk;
-      GdkWin32Keymap *keymap;
+  keymap = GDK_WIN32_KEYMAP (gdk_keymap);
 
-      if (gdk_keymap == NULL)
-        keymap = GDK_WIN32_KEYMAP (default_keymap);
-      else
-        keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+  update_keymap (keymap);
 
-      update_keymap (gdk_keymap);
+  for (group = 0; group < keymap->layout_handles->len; group++)
+    {
+      GdkWin32KeymapLayoutInfo *info = &g_array_index (keymap->layout_infos,
+                                                       GdkWin32KeymapLayoutInfo,
+                                                       group);
+      int entry_index = gdk_keysym_to_key_entry_index (info, keyval);
 
-      for (vk = 0; vk < KEY_STATE_SIZE; vk++)
+      while (entry_index >= 0)
         {
-          int group;
-
-          for (group = 0; group < keymap->layout_handles->len; group++)
+          GdkWin32KeymapKeyEntry  *entry        = &g_array_index (info->key_entries,
+                                                                  GdkWin32KeymapKeyEntry,
+                                                                  entry_index);
+          BYTE                     base_modbits = entry->mod_bits;
+          BYTE                     extra_modbits;
+          GdkKeymapKey             gdk_key      = {0};
+
+          /* Add original key combination */
+          gdk_key.keycode = entry->vk;
+          gdk_key.level   = modbits_to_level (keymap, info, entry->mod_bits);
+          gdk_key.group   = group;
+
+          g_array_append_val (retval, gdk_key);
+
+          /* Add combinations with modifiers that do not affect the translation */
+          for (extra_modbits = 0;
+               extra_modbits <= info->max_modbit_value;
+               ++extra_modbits)
             {
-              GdkWin32KeyLevelState    level;
-
-              for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
+              BYTE  modbits;
+              guint sym;
+
+              /* We are only interested in masks which are orthogonal to the
+               * original mask. */
+              if ((extra_modbits | base_modbits) == base_modbits ||
+                  (extra_modbits & base_modbits) != 0)
+                continue;
+
+              modbits = base_modbits | extra_modbits;
+
+              /* Check if the additional modifiers change the semantics.
+               * If they do not, add them. */
+              sym = vk_and_mod_bits_to_gdk_keysym (keymap, info, entry->vk,
+                                                   keystate, modbits,
+                                                   NULL);
+              if (sym == keyval || sym == GDK_KEY_VoidSymbol)
                 {
-                  guint *keygroup;
-
-                  keygroup = &keymap->keysym_tab[(vk * keymap->layout_handles->len + group) * 
GDK_WIN32_LEVEL_COUNT];
-
-                  if (keygroup[level] == keyval)
-                    {
-                      GdkKeymapKey key;
-
-                      key.keycode = vk;
-                      key.group = group;
-                      key.level = level;
-                      g_array_append_val (retval, key);
-                    }
+                  gdk_key.keycode = entry->vk;
+                  gdk_key.level   = modbits_to_level (keymap, info, modbits);
+                  gdk_key.group   = group;
+                  g_array_append_val (retval, gdk_key);
                 }
             }
-        }
-    }
 
-#ifdef G_ENABLE_DEBUG
-  if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
-    {
-      guint i;
-
-      g_print ("gdk_keymap_get_entries_for_keyval: %#.04x (%s):",
-               keyval, gdk_keyval_name (keyval));
-      for (i = len; i < retval->len; i++)
-        {
-          GdkKeymapKey *entry = (GdkKeymapKey *) retval->data + i;
-          g_print ("  %#.02x %d %d", entry->keycode, entry->group, entry->level);
+          entry_index = entry->next;
         }
-      g_print ("\n");
-    }
-#endif
+   }
 
-  return len < retval->len;
+  return retval->len > len;
 }
 
 static gboolean
@@ -1361,92 +823,71 @@ gdk_win32_keymap_get_entries_for_keycode (GdkKeymap     *gdk_keymap,
                                           guint        **keyvals,
                                           int           *n_entries)
 {
+  GdkWin32Keymap *keymap;
   GArray         *key_array;
   GArray         *keyval_array;
   int             group;
-  GdkWin32Keymap *keymap;
-  GdkKeymap      *default_keymap = gdk_display_get_keymap (gdk_display_get_default ());
+  BYTE            keystate[256] = {0};
+  BYTE            vk;
 
-  g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), FALSE);
+  g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
   g_return_val_if_fail (n_entries != NULL, FALSE);
 
-  if (hardware_keycode <= 0 ||
-      hardware_keycode >= KEY_STATE_SIZE ||
-      (keys == NULL && keyvals == NULL) ||
-      (gdk_keymap != NULL && gdk_keymap != default_keymap))
-    {
-      /* Wrong keycode or NULL output arrays or wrong keymap */
-      if (keys)
-        *keys = NULL;
-      if (keyvals)
-        *keyvals = NULL;
-
-      *n_entries = 0;
-      return FALSE;
-    }
+  *n_entries = 0;
 
-  if (keys)
+  if (keys != NULL)
     key_array = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
   else
     key_array = NULL;
 
-  if (keyvals)
+  if (keyvals != NULL)
     keyval_array = g_array_new (FALSE, FALSE, sizeof (guint));
   else
     keyval_array = NULL;
 
-  keymap = GDK_WIN32_KEYMAP (default_keymap);
-  update_keymap (GDK_KEYMAP (keymap));
+  keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+  update_keymap (keymap);
+
+  vk = hardware_keycode;
 
   for (group = 0; group < keymap->layout_handles->len; group++)
     {
-      GdkWin32KeyLevelState    level;
+      GdkWin32KeymapLayoutInfo *info = &g_array_index (keymap->layout_infos,
+                                                       GdkWin32KeymapLayoutInfo,
+                                                       group);
+      int level;
 
-      for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
+      for (level = 0; level <= info->max_level; ++level)
         {
-          if (key_array)
-            {
-              GdkKeymapKey key;
+          BYTE         modbits          = info->level_to_modbits[level];
+          BYTE         consumed_modbits = 0;
+          GdkKeymapKey key              = {0};
+          guint        keyval;
 
-              key.keycode = hardware_keycode;
-              key.group = group;
-              key.level = level;
-              g_array_append_val (key_array, key);
-            }
+          keyval = vk_and_mod_bits_to_gdk_keysym (keymap, info, vk, keystate, modbits, &consumed_modbits);
 
-          if (keyval_array)
-            {
-              guint keyval = keymap->keysym_tab[(hardware_keycode * keymap->layout_handles->len + group) * 
GDK_WIN32_LEVEL_COUNT + level];
-
-              g_array_append_val (keyval_array, keyval);
-            }
-        }
-    }
+          if (keyval == GDK_KEY_VoidSymbol || consumed_modbits != modbits)
+            continue;
 
-  *n_entries = group * GDK_WIN32_LEVEL_COUNT;
+          key.keycode = vk;
+          key.group = group;
+          key.level = level;
 
-  if ((key_array && key_array->len > 0) ||
-      (keyval_array && keyval_array->len > 0))
-    {
-      if (keys)
-        *keys = (GdkKeymapKey*) key_array->data;
+          if (key_array)
+            g_array_append_val (key_array, key);
 
-      if (keyvals)
-        *keyvals = (guint*) keyval_array->data;
-    }
-  else
-    {
-      if (keys)
-        *keys = NULL;
+          if (keyval_array)
+            g_array_append_val (keyval_array, keyval);
 
-      if (keyvals)
-        *keyvals = NULL;
+          ++(*n_entries);
+        }
     }
+  
+  if (keys != NULL)
+    *keys = (GdkKeymapKey*) g_array_free (key_array, FALSE);
 
-  if (key_array)
-    g_array_free (key_array, key_array->len > 0 ? FALSE : TRUE);
-  if (keyval_array)
-    g_array_free (keyval_array, keyval_array->len > 0 ? FALSE : TRUE);
+  if (keyvals != NULL)
+    *keyvals = (guint*) g_array_free (keyval_array, FALSE);
 
   return *n_entries > 0;
 }
@@ -1455,26 +896,28 @@ static guint
 gdk_win32_keymap_lookup_key (GdkKeymap          *gdk_keymap,
                              const GdkKeymapKey *key)
 {
-  guint sym;
-  GdkWin32Keymap *keymap;
-  GdkKeymap      *default_keymap = gdk_display_get_keymap (gdk_display_get_default ());
+  GdkWin32Keymap           *keymap;
+  GdkWin32KeymapLayoutInfo *info;
 
-  g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), 0);
+  BYTE                      keystate[256] = {0};
+  BYTE                      modbits;
+  guint                     sym;
+
+  g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), 0);
   g_return_val_if_fail (key != NULL, 0);
 
-  /* Accept only the default keymap */
-  if (gdk_keymap != NULL && gdk_keymap != default_keymap)
-    return 0;
+  keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+  update_keymap (keymap);
 
-  keymap = GDK_WIN32_KEYMAP (default_keymap);
-  update_keymap (GDK_KEYMAP (keymap));
+  info = &g_array_index (keymap->layout_infos, GdkWin32KeymapLayoutInfo, key->group);
 
-  if (key->keycode >= KEY_STATE_SIZE ||
-      key->group < 0 || key->group >= keymap->layout_handles->len ||
-      key->level < 0 || key->level >= GDK_WIN32_LEVEL_COUNT)
+  if (key->group < 0 || key->group >= keymap->layout_handles->len)
     return 0;
-
-  sym = keymap->keysym_tab[(key->keycode * keymap->layout_handles->len + key->group) * GDK_WIN32_LEVEL_COUNT 
+ key->level];
+  if (key->level < 0 || key->level > info->max_level)
+    return 0;
+  
+  modbits = info->level_to_modbits[key->level];
+  sym = vk_and_mod_bits_to_gdk_keysym (keymap, info, key->keycode, keystate, modbits, NULL);
 
   if (sym == GDK_KEY_VoidSymbol)
     return 0;
@@ -1492,168 +935,57 @@ gdk_win32_keymap_translate_keyboard_state (GdkKeymap       *gdk_keymap,
                                            int             *level,
                                            GdkModifierType *consumed_modifiers)
 {
-  GdkWin32Keymap *keymap;
-  guint tmp_keyval;
-  guint *keygroup;
-  GdkWin32KeyLevelState shift_level;
-  GdkModifierType modifiers = GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD2_MASK;
-  GdkKeymap *default_keymap = gdk_display_get_keymap (gdk_display_get_default ());
-
-  g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), FALSE);
-
-#if 0
-  GDK_NOTE (EVENTS, g_print ("gdk_keymap_translate_keyboard_state: keycode=%#x state=%#x group=%d\n",
-                            hardware_keycode, state, group));
-#endif
-  if (keyval)
-    *keyval = 0;
-  if (effective_group)
-    *effective_group = 0;
-  if (level)
-    *level = 0;
-  if (consumed_modifiers)
-    *consumed_modifiers = 0;
+  GdkWin32Keymap           *keymap;
+  guint                     tmp_keyval;
+  int                       tmp_effective_group;
+  int                       tmp_level;
+  BYTE                      consumed_mod_bits;
 
-  /* Accept only the default keymap */
-  if (gdk_keymap != NULL && gdk_keymap != default_keymap)
-    return FALSE;
+  GdkWin32KeymapLayoutInfo *layout_info;
+  guint                     vk;
+  BYTE                      mod_bits;
+  BYTE                      keystate[256] = {0};
 
-  if (hardware_keycode >= KEY_STATE_SIZE)
-    return FALSE;
+  g_return_val_if_fail (GDK_IS_KEYMAP (gdk_keymap), FALSE);
 
-  keymap = GDK_WIN32_KEYMAP (default_keymap);
-  update_keymap (GDK_KEYMAP (keymap));
+  keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+  update_keymap (keymap);
 
-  if (group < 0 || group >= keymap->layout_handles->len)
-    return FALSE;
+  g_return_val_if_fail (group >= 0 && group < keymap->layout_infos->len, FALSE);
 
-  keygroup = &keymap->keysym_tab[(hardware_keycode * keymap->layout_handles->len + group) * 
GDK_WIN32_LEVEL_COUNT];
+  layout_info = &g_array_index (keymap->layout_infos, GdkWin32KeymapLayoutInfo,
+                                group);
 
-  if ((state & (GDK_SHIFT_MASK | GDK_LOCK_MASK)) == (GDK_SHIFT_MASK | GDK_LOCK_MASK))
-    shift_level = GDK_WIN32_LEVEL_SHIFT_CAPSLOCK;
-  else if (state & GDK_SHIFT_MASK)
-    shift_level = GDK_WIN32_LEVEL_SHIFT;
-  else if (state & GDK_LOCK_MASK)
-    shift_level = GDK_WIN32_LEVEL_CAPSLOCK;
-  else
-    shift_level = GDK_WIN32_LEVEL_NONE;
+  vk = hardware_keycode;
+  mod_bits = gdk_mod_mask_to_mod_bits (state);
 
-  if (state & GDK_MOD2_MASK)
-    {
-      if (shift_level == GDK_WIN32_LEVEL_NONE)
-        shift_level = GDK_WIN32_LEVEL_ALTGR;
-      else if (shift_level == GDK_WIN32_LEVEL_SHIFT)
-        shift_level = GDK_WIN32_LEVEL_SHIFT_ALTGR;
-      else if (shift_level == GDK_WIN32_LEVEL_CAPSLOCK)
-        shift_level = GDK_WIN32_LEVEL_CAPSLOCK_ALTGR;
-      else
-        shift_level = GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR;
-    }
+  if (vk == VK_SHIFT || vk == VK_LSHIFT || vk == VK_RSHIFT)
+    mod_bits &= ~KBDSHIFT;
+  if (vk == VK_CONTROL || vk == VK_LCONTROL || vk == VK_RCONTROL)
+    mod_bits &= ~KBDCTRL;
+  if (vk == VK_MENU || vk == VK_LMENU || vk == VK_RMENU)
+    mod_bits &= ~KBDALT;
+  if (vk == VK_RMENU)
+    mod_bits &= ~KBDALTGR;
 
-  /* Drop altgr, capslock and shift if there are no keysymbols on
-   * the key for those.
-   */
-  if (keygroup[shift_level] == GDK_KEY_VoidSymbol)
-    {
-      switch (shift_level)
-        {
-         case GDK_WIN32_LEVEL_NONE:
-         case GDK_WIN32_LEVEL_ALTGR:
-         case GDK_WIN32_LEVEL_SHIFT:
-         case GDK_WIN32_LEVEL_CAPSLOCK:
-           if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_NONE;
-           break;
-         case GDK_WIN32_LEVEL_SHIFT_CAPSLOCK:
-           if (keygroup[GDK_WIN32_LEVEL_CAPSLOCK] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_CAPSLOCK;
-           else if (keygroup[GDK_WIN32_LEVEL_SHIFT] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_SHIFT;
-           else if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_NONE;
-           break;
-         case GDK_WIN32_LEVEL_CAPSLOCK_ALTGR:
-           if (keygroup[GDK_WIN32_LEVEL_ALTGR] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_ALTGR;
-           else if (keygroup[GDK_WIN32_LEVEL_CAPSLOCK] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_CAPSLOCK;
-           else if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_NONE;
-           break;
-         case GDK_WIN32_LEVEL_SHIFT_ALTGR:
-           if (keygroup[GDK_WIN32_LEVEL_ALTGR] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_ALTGR;
-           else if (keygroup[GDK_WIN32_LEVEL_SHIFT] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_SHIFT;
-           else if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_NONE;
-           break;
-         case GDK_WIN32_LEVEL_SHIFT_CAPSLOCK_ALTGR:
-           if (keygroup[GDK_WIN32_LEVEL_CAPSLOCK_ALTGR] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_CAPSLOCK_ALTGR;
-           else if (keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_SHIFT_ALTGR;
-           else if (keygroup[GDK_WIN32_LEVEL_ALTGR] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_ALTGR;
-           else if (keygroup[GDK_WIN32_LEVEL_SHIFT_CAPSLOCK] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_SHIFT_CAPSLOCK;
-           else if (keygroup[GDK_WIN32_LEVEL_CAPSLOCK] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_CAPSLOCK;
-           else if (keygroup[GDK_WIN32_LEVEL_SHIFT] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_SHIFT;
-           else if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
-             shift_level = GDK_WIN32_LEVEL_NONE;
-           break;
-         case GDK_WIN32_LEVEL_COUNT:
-           g_assert_not_reached ();
-        }
-    }
+  /* We need to query the existing keyboard state for NumLock, CapsLock etc. */
+  get_lock_state (keystate);
 
-  /* See whether the shift level actually mattered
-   * to know what to put in consumed_modifiers
-   */
-  if ((keygroup[GDK_WIN32_LEVEL_SHIFT] == GDK_KEY_VoidSymbol ||
-       keygroup[GDK_WIN32_LEVEL_NONE] == keygroup[GDK_WIN32_LEVEL_SHIFT]) &&
-      (keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR] == GDK_KEY_VoidSymbol ||
-       keygroup[GDK_WIN32_LEVEL_ALTGR] == keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR]) &&
-      (keygroup[GDK_WIN32_LEVEL_SHIFT_CAPSLOCK] == GDK_KEY_VoidSymbol ||
-       keygroup[GDK_WIN32_LEVEL_CAPSLOCK] == keygroup[GDK_WIN32_LEVEL_SHIFT_CAPSLOCK]))
-      modifiers &= ~GDK_SHIFT_MASK;
-
-  if ((keygroup[GDK_WIN32_LEVEL_CAPSLOCK] == GDK_KEY_VoidSymbol ||
-       keygroup[GDK_WIN32_LEVEL_NONE] == keygroup[GDK_WIN32_LEVEL_CAPSLOCK]) &&
-      (keygroup[GDK_WIN32_LEVEL_CAPSLOCK_ALTGR] == GDK_KEY_VoidSymbol ||
-       keygroup[GDK_WIN32_LEVEL_ALTGR] == keygroup[GDK_WIN32_LEVEL_CAPSLOCK_ALTGR]) &&
-      (keygroup[GDK_WIN32_LEVEL_SHIFT_CAPSLOCK] == GDK_KEY_VoidSymbol ||
-       keygroup[GDK_WIN32_LEVEL_SHIFT] == keygroup[GDK_WIN32_LEVEL_SHIFT_CAPSLOCK]))
-      modifiers &= ~GDK_LOCK_MASK;
-
-  if ((keygroup[GDK_WIN32_LEVEL_ALTGR] == GDK_KEY_VoidSymbol ||
-       keygroup[GDK_WIN32_LEVEL_NONE] == keygroup[GDK_WIN32_LEVEL_ALTGR]) &&
-      (keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR] == GDK_KEY_VoidSymbol ||
-       keygroup[GDK_WIN32_LEVEL_SHIFT] == keygroup[GDK_WIN32_LEVEL_SHIFT_ALTGR]) &&
-      (keygroup[GDK_WIN32_LEVEL_CAPSLOCK_ALTGR] == GDK_KEY_VoidSymbol ||
-       keygroup[GDK_WIN32_LEVEL_CAPSLOCK] == keygroup[GDK_WIN32_LEVEL_CAPSLOCK_ALTGR]))
-      modifiers &= ~GDK_MOD2_MASK;
-
-  tmp_keyval = keygroup[shift_level];
+  tmp_keyval = vk_and_mod_bits_to_gdk_keysym (keymap, layout_info, vk, keystate,
+                                              mod_bits, &consumed_mod_bits);
+  tmp_effective_group = group;
+  tmp_level = modbits_to_level (keymap, layout_info, consumed_mod_bits);
 
+  /* Determine consumed modifiers */
+  
   if (keyval)
     *keyval = tmp_keyval;
-
   if (effective_group)
-    *effective_group = group;
-
+    *effective_group = tmp_effective_group;
   if (level)
-    *level = shift_level;
-
+    *level = tmp_level;
   if (consumed_modifiers)
-    *consumed_modifiers = modifiers;
-
-#if 0
-  GDK_NOTE (EVENTS, g_print ("... group=%d level=%d cmods=%#x keyval=%s\n",
-                            group, shift_level, modifiers, gdk_keyval_name (tmp_keyval)));
-#endif
+    *consumed_modifiers = mod_bits_to_gdk_mod_mask (consumed_mod_bits);
 
   return tmp_keyval != GDK_KEY_VoidSymbol;
 }
diff --git a/gdk/win32/gdkkeys-win32.h b/gdk/win32/gdkkeys-win32.h
new file mode 100644
index 0000000000..17b7d7125c
--- /dev/null
+++ b/gdk/win32/gdkkeys-win32.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2021 Philip Zander
+ * Copyright (c) 2018 Microsoft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <glib.h>
+#include <windows.h>
+
+/* For lookup table VK -> chars */
+typedef struct 
+{
+  int table;
+  int index;
+} GdkWin32KeymapTableAndIndex;
+
+/* For reverse lookup char -> VKs */
+typedef struct
+{
+  BYTE mod_bits;
+  BYTE vk;
+
+  /* Index of next KeyEntry. -1 if there is no next entry. */
+  int  next;
+} GdkWin32KeymapKeyEntry;
+
+typedef struct
+{
+  HKL            handle;
+
+  /* Keyboard layout identifier */
+  char           name[KL_NAMELENGTH];
+
+  /* Path of the layout DLL */
+  char          *file;
+
+  /* Handle of the layout DLL */
+  HINSTANCE      lib;
+
+  /* The actual conversion tables provided by the layout DLL.
+   *
+   * This is a pointer to a KBDTABLES structure. The exact definition
+   * of this structure depends on the kernel on which the executable
+   * run and can in general only be determined at runtime. That's why
+   * we have to use a generic gpointer instead of the actual type here.
+   *
+   * See comment on GdkWin32KeymapImpl below for more information. */
+  gpointer       tables;
+
+  /* VK -> chars lookup table so we don't have to do a linear scan
+   * every time we look up a key. */
+  GdkWin32KeymapTableAndIndex vk_lookup_table[256];
+
+  /* List of entries for reverse (char ->VKs) lookup. */
+  GArray        *key_entries;
+
+  /* Reverse lookup table (char -> VKs). Key: Unichar. Value: int.
+   * The value is used to index into the key_entries array. The key_entries
+   * array can contain multiple consecutive entries for a given char.
+   * The end of the list for the char is marked by a key entry that has
+   * mod_bits and vk set to 0xFF. */
+  GHashTable    *reverse_lookup_table;
+
+  /* Map level to modbits */
+  BYTE           level_to_modbits[256];
+
+  /* Max Number of levels */
+  BYTE           max_level;
+
+  /* Maximum possible value of a modbits bitset. */
+  BYTE           max_modbit_value;
+
+} GdkWin32KeymapLayoutInfo;
+
+/* Some keyboard driver constants
+ * See 
https://github.com/microsoft/windows-rs/blob/0.28.0/crates/deps/sys/src/Windows/Win32/UI/Input/KeyboardAndMouse/mod.rs
+ */
+
+/* Modifier bits */
+#define KBDBASE        0x00
+#define KBDSHIFT       0x01
+#define KBDCTRL        0x02
+#define KBDALT         0x04
+#define KBDKANA        0x08
+#define KBDROYA        0x10
+#define KBDLOYA        0x20
+#define KBDGRPSELTAP   0x80
+
+#define KBDALTGR (KBDCTRL| KBDALT)
+
+/* */
+#define SHFT_INVALID 0x0F
+
+/* Char table constants */
+#define WCH_NONE 0xF000
+#define WCH_DEAD 0xF001
+#define WCH_LGTR 0xF002
+
+/* Char table flags */
+#define CAPLOK      0x01
+#define SGCAPS      0x02
+#define CAPLOKALTGR 0x04
+#define KANALOK     0x08
+#define GRPSELTAP   0x80
+
+/* IMPORTANT:
+ *
+ * Keyboard layout DLLs are dependent on the host architecture.
+ *
+ * - 32 bit systems have just one 32 bit DLL in System32.
+ * - 64 bit systems contain two versions of each layout DLL: One in System32
+ *   for 64-bit applications, and one in SysWOW64 for 32-bit applications.
+ *
+ * Here comes the tricky part:
+ *
+ * The 32-bit DLL in SysWOW64 is *not* identical to the DLL you would find
+ * on a true 32 bit system, because all the pointers there are declared with
+ * the attribute `__ptr64` (which means they are 64 bits wide, but only the 
+ * lower 32 bits are used).
+ *
+ * This leads to the following problems:
+ *
+ *   (1) GCC does not support `__ptr64`
+ *   (2) When compiling the 32-bit library, we need two versions of the same code
+ *       and decide at run-time which one to execute, because we can't know at
+ *       compile time whether we will be running on a true 32-bit system, or on
+ *       WOW64.
+ *
+ * To solve this problem, we generate code for both cases (see
+ * gdkkeys-win32-impl.c + gdkkeys-win32-impl-wow64.c) and encapsulate
+ * the resulting functions in a struct of type GdkWin32KeymapImpl,
+ * allowing us to select the correct implementation at runtime.
+ *
+ */
+
+typedef struct
+{
+  gboolean  (*load_layout_dll)      (const char               *dll,
+                                     GdkWin32KeymapLayoutInfo *info);
+  void      (*init_vk_lookup_table) (GdkWin32KeymapLayoutInfo *info);
+  BYTE      (*keystate_to_modbits)  (GdkWin32KeymapLayoutInfo *info,
+                                     const BYTE                keystate[256]);
+  BYTE      (*modbits_to_level)     (GdkWin32KeymapLayoutInfo *info,
+                                     BYTE                      modbits);
+  WCHAR     (*vk_to_char_fuzzy)     (GdkWin32KeymapLayoutInfo *info,
+                                     const BYTE                keystate[256],
+                                     BYTE                      extra_mod_bits,
+                                     BYTE                     *consumed_mod_bits,
+                                     gboolean                 *is_dead,
+                                     BYTE                      vk);
+} GdkWin32KeymapImpl;
diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h
index 723e681c71..58185697a6 100644
--- a/gdk/win32/gdkprivate-win32.h
+++ b/gdk/win32/gdkprivate-win32.h
@@ -356,7 +356,7 @@ guint8     _gdk_win32_keymap_get_active_group    (GdkWin32Keymap *keymap);
 guint8     _gdk_win32_keymap_get_rshift_scancode (GdkWin32Keymap *keymap);
 void       _gdk_win32_keymap_set_active_layout   (GdkWin32Keymap *keymap,
                                                   HKL             hkl);
-
+GdkModifierType _gdk_win32_keymap_get_mod_mask   (GdkWin32Keymap *keymap);
 
 GdkKeymap *_gdk_win32_display_get_keymap (GdkDisplay *display);
 
@@ -425,4 +425,12 @@ void _gdk_win32_surfaceing_init (void);
 void _gdk_drag_init    (void);
 void _gdk_events_init (GdkDisplay *display);
 
+typedef enum _GdkWin32ProcessorCheckType
+{
+  GDK_WIN32_ARM64,
+  GDK_WIN32_WOW64,
+} GdkWin32ProcessorCheckType;
+
+gboolean _gdk_win32_check_processor (GdkWin32ProcessorCheckType check_type);
+
 #endif /* __GDK_PRIVATE_WIN32_H__ */
diff --git a/gdk/win32/meson.build b/gdk/win32/meson.build
index 0f40e73edc..d368648624 100644
--- a/gdk/win32/meson.build
+++ b/gdk/win32/meson.build
@@ -19,6 +19,8 @@ gdk_win32_sources = files([
   'gdkhdataoutputstream-win32.c',
   'gdkinput-winpointer.c',
   'gdkkeys-win32.c',
+  'gdkkeys-win32-impl.c',
+  'gdkkeys-win32-impl-wow64.c',
   'gdkwin32langnotification.c',
   'gdkmain-win32.c',
   'gdkmonitor-win32.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]