[gtk+] GDK W32: Cache multiple keyboard layouts simultaneously



commit 52c7e07948f0d82ade7c730e99c53dea3e13ca67
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Wed Jul 13 11:41:35 2016 +0000

    GDK W32: Cache multiple keyboard layouts simultaneously
    
    This changes the group/level semantic.
    Previously W32 backend used "group 0/1" to denote "AltGr OFF/ON"
    and "level 0/1" to denote "Shift is OFF/ON".
    Now "group" means "keyboard layout" and there can be up to 255 groups,
    while AltGr and Shift are combined into a single level enum that
    takes values between 0 and 4.
    Unlike X, W32 doesn't do effective group overriding, meaning that
    it will never tell the caller that a different group was actually
    used (even for universal keys, such as Enter), because key symbol
    table is completely fabricated and there's no point in trying to
    save a few of kilobytes of RAM by not duplicating universal key
    records for all groups.
    
    Also contains many whitespace changes (tab elimination, fixed
    indentation) and cleanup (axed a few global variables, these are
    now accessed via the default keymap).
    
    https://bugzilla.gnome.org/show_bug.cgi?id=768722

 gdk/gdkkeyuni.c              |    8 +-
 gdk/win32/gdkevents-win32.c  |   12 +-
 gdk/win32/gdkkeys-win32.c    |  983 ++++++++++++++++++++++++++++--------------
 gdk/win32/gdkmain-win32.c    |    1 +
 gdk/win32/gdkprivate-win32.h |   12 +-
 5 files changed, 679 insertions(+), 337 deletions(-)
---
diff --git a/gdk/gdkkeyuni.c b/gdk/gdkkeyuni.c
index 4b4431f..4bf77b2 100644
--- a/gdk/gdkkeyuni.c
+++ b/gdk/gdkkeyuni.c
@@ -904,10 +904,12 @@ gdk_keyval_to_unicode (guint keyval)
     return keyval & 0x00ffffff;
 
 #if defined(GDK_WINDOWING_WIN32)
-  if (GDK_IS_WIN32_DISPLAY (gdk_display_get_default ()))
+  if (GDK_IS_WIN32_DISPLAY (gdk_display_get_default ()) &&
+      keyval == 0xffae)
     {
-      if (keyval == 0xffae)
-        return (guint32) _gdk_win32_keymap_get_decimal_mark ();
+      GdkWin32Keymap *keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
+
+      return (guint32) _gdk_win32_keymap_get_decimal_mark (keymap);
     }
 #endif
 
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index 7f050a4..2f397f9 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -505,6 +505,8 @@ static void
 build_key_event_state (GdkEvent *event,
                       BYTE     *key_state)
 {
+  GdkWin32Keymap *keymap;
+
   event->key.state = 0;
 
   if (key_state[VK_SHIFT] & 0x80)
@@ -524,11 +526,13 @@ build_key_event_state (GdkEvent *event,
   if (key_state[VK_XBUTTON2] & 0x80)
     event->key.state |= GDK_BUTTON5_MASK;
 
-  if (_gdk_keyboard_has_altgr &&
+  keymap = GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display));
+  event->key.group = _gdk_win32_keymap_get_active_group (keymap);
+
+  if (_gdk_win32_keymap_has_altgr (keymap) &&
       (key_state[VK_LCONTROL] & 0x80) &&
       (key_state[VK_RMENU] & 0x80))
     {
-      event->key.group = 1;
       event->key.state |= GDK_MOD2_MASK;
       if (key_state[VK_RCONTROL] & 0x80)
        event->key.state |= GDK_CONTROL_MASK;
@@ -537,7 +541,6 @@ build_key_event_state (GdkEvent *event,
     }
   else
     {
-      event->key.group = 0;
       if (key_state[VK_CONTROL] & 0x80)
        event->key.state |= GDK_CONTROL_MASK;
       if (key_state[VK_MENU] & 0x80)
@@ -2224,6 +2227,7 @@ gdk_event_translate (MSG  *msg,
     {
     case WM_INPUTLANGCHANGE:
       _gdk_input_locale = (HKL) msg->lParam;
+      _gdk_win32_keymap_set_active_layout (GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display)), 
_gdk_input_locale);
       _gdk_input_locale_is_ime = ImmIsIME (_gdk_input_locale);
       GetLocaleInfo (MAKELCID (LOWORD (_gdk_input_locale), SORT_DEFAULT),
                     LOCALE_IDEFAULTANSICODEPAGE,
@@ -2391,7 +2395,7 @@ gdk_event_translate (MSG  *msg,
            }
        }
       else if (msg->wParam == VK_SHIFT &&
-              LOBYTE (HIWORD (msg->lParam)) == _scancode_rshift)
+              LOBYTE (HIWORD (msg->lParam)) == _gdk_win32_keymap_get_rshift_scancode (GDK_WIN32_KEYMAP 
(_gdk_win32_display_get_keymap (_gdk_display))))
        event->key.hardware_keycode = VK_RSHIFT;
 
       /* g_print ("ctrl:%02x lctrl:%02x rctrl:%02x alt:%02x lalt:%02x ralt:%02x\n", key_state[VK_CONTROL], 
key_state[VK_LCONTROL], key_state[VK_RCONTROL], key_state[VK_MENU], key_state[VK_LMENU], 
key_state[VK_RMENU]); */
diff --git a/gdk/win32/gdkkeys-win32.c b/gdk/win32/gdkkeys-win32.c
index e1ad6cf..f250187 100644
--- a/gdk/win32/gdkkeys-win32.c
+++ b/gdk/win32/gdkkeys-win32.c
@@ -38,6 +38,25 @@
 #include "gdkkeysprivate.h"
 #include "gdkwin32keys.h"
 
+struct _GdkWin32KeyGroupOptions
+{
+  /* character that should be used as the decimal separator */
+  wchar_t         decimal_mark;
+
+  /* the bits that indicate level shift when set (usually Shift, sometimes also CapsLock) */
+  GdkModifierType shift_modifiers;
+
+  /* Scancode for the VK_RSHIFT */
+  guint           scancode_rshift;
+
+  /* TRUE if Ctrl+Alt emulates AltGr */
+  gboolean        has_altgr;
+
+  gboolean        capslock_tested;
+};
+
+typedef struct _GdkWin32KeyGroupOptions GdkWin32KeyGroupOptions;
+
 struct _GdkWin32KeymapClass
 {
   GdkKeymapClass parent_class;
@@ -46,50 +65,107 @@ struct _GdkWin32KeymapClass
 struct _GdkWin32Keymap
 {
   GdkKeymap parent_instance;
+
+  /* length = what GetKeyboardLayoutList() returns, type = HKL.
+   * When it changes, recreate the keymap and repopulate the options.
+   */
+  GArray *layout_handles;
+
+  /* VirtualKeyCode -> gdk_keyval table
+   * length = 256 * length(layout_handles) * 2 * 2
+   * 256 is the number of virtual key codes,
+   * 2x2 is the number of Shift/AltGr 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;
+
+  /* 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;
+};
+
+enum _GdkWin32KeyLevelState
+{
+  GDK_WIN32_LEVEL_NONE = 0,
+  GDK_WIN32_LEVEL_SHIFT,
+  GDK_WIN32_LEVEL_ALTGR,
+  GDK_WIN32_LEVEL_SHIFT_ALTGR,
+  GDK_WIN32_LEVEL_COUNT
 };
 
+typedef enum _GdkWin32KeyLevelState GdkWin32KeyLevelState;
+
 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
 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));
+  keymap->keysym_tab = NULL;
+  keymap->active_layout = 0;
+  update_keymap (GDK_KEYMAP (keymap));
 }
 
-guint _gdk_keymap_serial = 0;
-gboolean _gdk_keyboard_has_altgr = FALSE;
-guint _scancode_rshift = 0;
-
-static GdkModifierType gdk_shift_modifiers = GDK_SHIFT_MASK;
-
-static GdkKeymap *default_keymap = NULL;
+static void
+gdk_win32_keymap_finalize (GObject *object)
+{
+  GdkWin32Keymap *keymap = GDK_WIN32_KEYMAP (object);
 
-static guint *keysym_tab = NULL;
-static wchar_t decimal_mark = 0;
+  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);
 
-#define KEY_STATE_SIZE 256
+  G_OBJECT_CLASS (gdk_win32_keymap_parent_class)->finalize (object);
+}
 
 #ifdef G_ENABLE_DEBUG
 static void
-print_keysym_tab (void)
+print_keysym_tab (GdkWin32Keymap *keymap)
 {
-  gint vk;
+  gint                      li;
+  GdkWin32KeyGroupOptions  *options;
+  gint                      vk;
+  GdkWin32KeyLevelState     level;
+  gint                      group_size = keymap->layout_handles->len;
 
-  g_print ("keymap:%s%s\n",
-          _gdk_keyboard_has_altgr ? " (uses AltGr)" : "",
-          (gdk_shift_modifiers & GDK_LOCK_MASK) ? " (has ShiftLock)" : "");
-  for (vk = 0; vk < KEY_STATE_SIZE; vk++)
+  for (li = 0; li < group_size; li++)
     {
-      gint state;
-
-      g_print ("%#.02x: ", vk);
-      for (state = 0; state < 4; state++)
-       {
-         gchar *name = gdk_keyval_name (keysym_tab[vk*4 + state]);
-         if (name == NULL)
-           name = "(none)";
-         g_print ("%s ", name);
-       }
-      g_print ("\n");
+      options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, li);
+      g_print ("keymap %d (0x%p):%s%s\n",
+               li, g_array_index (keymap->layout_handles, HKL, li),
+               options->has_altgr ? " (uses AltGr)" : "",
+               (options->shift_modifiers & GDK_LOCK_MASK) ? " (has ShiftLock)" : "");
+
+      for (vk = 0; vk < KEY_STATE_SIZE; vk++)
+        {
+          g_print ("%#.02x: ", vk);
+
+          for (level = 0; level < GDK_WIN32_LEVEL_COUNT; level++)
+            {
+              gchar *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
@@ -259,34 +335,38 @@ handle_special (guint  vk,
 }
 
 static void
-set_shift_vks (guchar *key_state,
-              gint    shift)
+set_level_vks (guchar               *key_state,
+              GdkWin32KeyLevelState level)
 {
-  switch (shift)
+  switch (level)
     {
-    case 0:
+    case GDK_WIN32_LEVEL_NONE:
       key_state[VK_SHIFT] = 0;
       key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
       break;
-    case 1:
+    case GDK_WIN32_LEVEL_SHIFT:
       key_state[VK_SHIFT] = 0x80;
       key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
       break;
-    case 2:
+    case GDK_WIN32_LEVEL_ALTGR:
       key_state[VK_SHIFT] = 0;
       key_state[VK_CONTROL] = key_state[VK_MENU] = 0x80;
       break;
-    case 3:
+    case GDK_WIN32_LEVEL_SHIFT_ALTGR:
       key_state[VK_SHIFT] = 0x80;
       key_state[VK_CONTROL] = key_state[VK_MENU] = 0x80;
       break;
+    case GDK_WIN32_LEVEL_COUNT:
+      g_assert_not_reached ();
+      break;
     }
 }
 
 static void
-reset_after_dead (guchar key_state[KEY_STATE_SIZE])
+reset_after_dead (guchar key_state[KEY_STATE_SIZE],
+                  HKL    handle)
 {
-  guchar temp_key_state[KEY_STATE_SIZE];
+  guchar  temp_key_state[KEY_STATE_SIZE];
   wchar_t wcs[2];
 
   memmove (temp_key_state, key_state, KEY_STATE_SIZE);
@@ -297,7 +377,7 @@ reset_after_dead (guchar key_state[KEY_STATE_SIZE])
 
   ToUnicodeEx (VK_SPACE, MapVirtualKey (VK_SPACE, 0),
               temp_key_state, wcs, G_N_ELEMENTS (wcs),
-              0, _gdk_input_locale);
+              0, handle);
 }
 
 static void
@@ -353,180 +433,360 @@ handle_dead (guint  keysym,
  * return current decimal mark as unicode character
  */
 guint32
-_gdk_win32_keymap_get_decimal_mark (void)
+_gdk_win32_keymap_get_decimal_mark (GdkWin32Keymap *keymap)
+{
+  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) '.';
+}
+
+static gboolean
+layouts_are_the_same (GArray *array, HKL *hkls, gint hkls_len)
 {
-  if (decimal_mark)
-    return (decimal_mark);
+  gint i;
+
+  if (hkls_len != array->len)
+    return FALSE;
 
-  return ((guint32) '.');
+  for (i = 0; i < hkls_len; i++)
+    if (hkls[i] != g_array_index (array, HKL, i))
+      return FALSE;
+
+  return TRUE;
 }
 
 static void
-update_keymap (void)
+check_that_active_layout_is_in_sync (GdkWin32Keymap *keymap)
 {
-  static guint current_serial = 0;
-  guchar key_state[KEY_STATE_SIZE];
-  guint scancode;
-  guint vk;
-  gboolean capslock_tested = FALSE;
+  HKL     hkl;
+  HKL     cached_hkl;
+  wchar_t hkl_name[KL_NAMELENGTH];
 
-  if (keysym_tab != NULL && current_serial == _gdk_keymap_serial)
+  if (keymap->layout_handles->len <= 0)
     return;
 
-  current_serial = _gdk_keymap_serial;
+  hkl = GetKeyboardLayout (0);
+  cached_hkl = g_array_index (keymap->layout_handles, HKL, keymap->active_layout);
+
+  if (hkl != cached_hkl)
+    {
+      if (!GetKeyboardLayoutNameW (hkl_name))
+        wcscpy_s (hkl_name, KL_NAMELENGTH, L"(NULL)");
 
-  if (keysym_tab == NULL)
-    keysym_tab = g_new (guint, 4*KEY_STATE_SIZE);
+      g_warning ("Cached active layout #%d (0x%p) does not match actual layout %S, 0x%p",
+                 keymap->active_layout, cached_hkl, hkl_name, hkl);
+    }
+}
 
-  memset (key_state, 0, sizeof (key_state));
+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;
+  gint                     i, group;
+  GdkWin32KeyLevelState    level;
+  GdkWin32KeyGroupOptions *options;
+  GdkWin32Keymap          *keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+  gint                     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;
 
-  _gdk_keyboard_has_altgr = FALSE;
-  gdk_shift_modifiers = GDK_SHIFT_MASK;
+  no_list = FALSE;
+  hkls_len = GetKeyboardLayoutList (0, NULL);
 
-  for (vk = 0; vk < KEY_STATE_SIZE; vk++)
+  if (hkls_len <= 0)
     {
-      if ((scancode = MapVirtualKey (vk, 0)) == 0 &&
-         vk != VK_DIVIDE)
-       keysym_tab[vk*4+0] =
-         keysym_tab[vk*4+1] =
-         keysym_tab[vk*4+2] =
-         keysym_tab[vk*4+3] = GDK_KEY_VoidSymbol;
-      else
-       {
-         gint shift;
+      hkls_len = 1;
+      no_list = TRUE;
+    }
+  else if (hkls_len > 255)
+    {
+      hkls_len = 255;
+    }
 
-         if (vk == VK_RSHIFT)
-           _scancode_rshift = scancode;
+  if (hkls_size < hkls_len)
+    {
+      hkls = g_renew (HKL, hkls, hkls_len);
+      hkls_size = hkls_len;
+    }
 
-         key_state[vk] = 0x80;
-         for (shift = 0; shift < 4; shift++)
-           {
-             guint *ksymp = keysym_tab + vk*4 + shift;
+  if (hkls_len != GetKeyboardLayoutList (hkls_len, hkls))
+    {
+      if (!no_list)
+        return;
+
+      hkls[0] = GetKeyboardLayout (0);
+      hkls_len = 1;
+    }
+
+  if (layouts_are_the_same (keymap->layout_handles, hkls, hkls_len))
+    {
+      check_that_active_layout_is_in_sync (keymap);
+      current_serial = _gdk_keymap_serial;
+
+      return;
+    }
+
+  GDK_NOTE (EVENTS, g_print ("\nHave %d keyboard layouts:", hkls_len));
+
+  for (i = 0; i < hkls_len; i++)
+    {
+      GDK_NOTE (EVENTS, g_print (" 0x%p", hkls[i]));
+
+      if (GetKeyboardLayout (0) == hkls[i])
+        {
+          wchar_t hkl_name[KL_NAMELENGTH];
 
-             set_shift_vks (key_state, shift);
+          if (!GetKeyboardLayoutNameW (hkl_name))
+            wcscpy_s (hkl_name, KL_NAMELENGTH, L"(NULL)");
 
-             *ksymp = 0;
+          GDK_NOTE (EVENTS, g_print ("(active, %S)", hkl_name));
+        }
+    }
 
-             /* First, handle those virtual keys that we always want
-              * as special GDK_* keysyms, even if ToAsciiEx might
-              * turn some them into a ASCII character (like TAB and
-              * ESC).
-              */
-             handle_special (vk, ksymp, shift);
+  GDK_NOTE (EVENTS, g_print ("\n"));
 
-             if ((*ksymp == 0) || ((vk == VK_DECIMAL) && (shift == 0)))
-               {
-                 wchar_t wcs[10];
-                 gint k;
+  keysym_tab_size = hkls_len * 256 * 2 * 2;
 
-                 wcs[0] = wcs[1] = 0;
-                 k = ToUnicodeEx (vk, scancode, key_state,
-                                  wcs, G_N_ELEMENTS (wcs),
-                                  0, _gdk_input_locale);
+  if (hkls_len != keymap->layout_handles->len)
+    keymap->keysym_tab = g_renew (guint, keymap->keysym_tab, keysym_tab_size);
+
+  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);
+
+  for (i = 0; i < hkls_len; i++)
+    {
+      options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, i);
+
+      options->decimal_mark = 0;
+      options->shift_modifiers = GDK_SHIFT_MASK;
+      options->scancode_rshift = 0;
+      options->has_altgr = FALSE;
+      options->capslock_tested = FALSE;
+
+      g_array_index (keymap->layout_handles, HKL, i) = hkls[i];
+
+      if (hkls[i] == _gdk_input_locale)
+        keymap->active_layout = i;
+    }
+
+  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];
+
+          if (scancode == 0 &&
+              vk != VK_DIVIDE)
+            {
+              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;
+
+          for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
+            {
+              guint *ksymp = &keygroup[level];
+
+              set_level_vks (key_state, level);
+
+              *ksymp = 0;
+
+              /* First, handle those virtual keys that we always want
+               * as special GDK_* keysyms, even if ToAsciiEx might
+               * turn some them into a ASCII character (like TAB and
+               * ESC).
+               */
+              handle_special (vk, ksymp, level);
+
+              if ((*ksymp == 0) ||
+                  ((vk == VK_DECIMAL) && (level == GDK_WIN32_LEVEL_NONE)))
+                {
+                  wchar_t wcs[10];
+                  gint    k;
+                  guint   keysym;
+
+                  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, shift, k,
-                          wcs[0], wcs[1]);
+                  g_print ("ToUnicodeEx(%#02x, %d: %d): %d, %04x %04x\n",
+                           vk, scancode, level, k,
+                           wcs[0], wcs[1]);
 #endif
-                 if ((vk == VK_DECIMAL) && (shift == 0))
-                   {
-                     if (k == 1)
-                       decimal_mark = wcs[0];
-                   }
-                 else if (k == 1)
-                   *ksymp = gdk_unicode_to_keyval (wcs[0]);
-                 else if (k == -1)
-                   {
-                     guint 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);
-
-                     /* Use dead keysyms instead of "undead" ones */
-                     handle_dead (keysym, ksymp);
-                   }
-                 else if (k == 0)
-                   {
-                     /* Seems to be necessary to "reset" the keyboard layout
-                      * in this case, too. Otherwise problems on NT4.
-                      */
-                     reset_after_dead (key_state);
-                   }
-                 else
-                   {
+                  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);
+                      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" : "")));
+                      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
-                   }
-               }
-             if (*ksymp == 0)
-               *ksymp = GDK_KEY_VoidSymbol;
-           }
-         key_state[vk] = 0;
-
-         /* Check if keyboard has an AltGr key by checking if
-          * the mapping with Control+Alt is different.
-          */
-         if (!_gdk_keyboard_has_altgr)
-           if ((keysym_tab[vk*4 + 2] != GDK_KEY_VoidSymbol &&
-                keysym_tab[vk*4] != keysym_tab[vk*4 + 2]) ||
-               (keysym_tab[vk*4 + 3] != GDK_KEY_VoidSymbol &&
-                keysym_tab[vk*4 + 1] != keysym_tab[vk*4 + 3]))
-             _gdk_keyboard_has_altgr = TRUE;
-
-         if (!capslock_tested)
-           {
-             /* Can we use this virtual key to determine the CapsLock
-              * key behaviour: CapsLock or ShiftLock? If it generates
-              * keysyms for printable characters and has a shifted
-              * keysym that isn't just the upperacase of the
-              * unshifted keysym, check the behaviour of VK_CAPITAL.
-              */
-             if (g_unichar_isgraph (gdk_keyval_to_unicode (keysym_tab[vk*4 + 0])) &&
-                 keysym_tab[vk*4 + 1] != keysym_tab[vk*4 + 0] &&
-                 g_unichar_isgraph (gdk_keyval_to_unicode (keysym_tab[vk*4 + 1])) &&
-                 keysym_tab[vk*4 + 1] != gdk_keyval_to_upper (keysym_tab[vk*4 + 0]))
-               {
-                 guchar chars[2];
-
-                 capslock_tested = TRUE;
-
-                 key_state[VK_SHIFT] = 0;
-                 key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
-                 key_state[VK_CAPITAL] = 1;
-
-                 if (ToAsciiEx (vk, scancode, key_state,
-                                (LPWORD) chars, 0, _gdk_input_locale) == 1)
-                   {
-                     if (chars[0] >= GDK_KEY_space &&
-                         chars[0] <= GDK_KEY_asciitilde &&
-                         chars[0] == keysym_tab[vk*4 + 1])
-                       {
-                         /* CapsLock acts as ShiftLock */
-                         gdk_shift_modifiers |= GDK_LOCK_MASK;
-                       }
-                   }
-                 key_state[VK_CAPITAL] = 0;
-               }
-           }
-       }
+                      break;
+                    }
+                }
+
+              if (*ksymp == 0)
+                *ksymp = GDK_KEY_VoidSymbol;
+            }
+
+          key_state[vk] = 0;
+
+          /* Check if keyboard has an AltGr key by checking if
+           * the mapping with Control+Alt is different.
+           */
+          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;
+
+          if (!options->capslock_tested)
+            {
+              /* Can we use this virtual key to determine the CapsLock
+               * key behaviour: CapsLock or ShiftLock? If it generates
+               * keysyms for printable characters and has a shifted
+               * keysym that isn't just the upperacase of the
+               * unshifted keysym, check the behaviour of VK_CAPITAL.
+               */
+              if (g_unichar_isgraph (gdk_keyval_to_unicode (keygroup[GDK_WIN32_LEVEL_NONE])) &&
+                  keygroup[GDK_WIN32_LEVEL_SHIFT] != keygroup[GDK_WIN32_LEVEL_NONE] &&
+                  g_unichar_isgraph (gdk_keyval_to_unicode (keygroup[GDK_WIN32_LEVEL_SHIFT])) &&
+                  keygroup[GDK_WIN32_LEVEL_SHIFT] != gdk_keyval_to_upper (keygroup[GDK_WIN32_LEVEL_NONE]))
+                {
+                  guchar chars[2];
+
+                  options->capslock_tested = TRUE;
+
+                  key_state[VK_SHIFT] = 0;
+                  key_state[VK_CONTROL] = key_state[VK_MENU] = 0;
+                  key_state[VK_CAPITAL] = 1;
+
+                  if (ToAsciiEx (vk, scancode, key_state,
+                                 (LPWORD) chars, 0, hkls[group]) == 1)
+                    {
+                      if (chars[0] >= GDK_KEY_space &&
+                          chars[0] <= GDK_KEY_asciitilde &&
+                          chars[0] == keygroup[GDK_WIN32_LEVEL_SHIFT])
+                        {
+                          /* CapsLock acts as ShiftLock */
+                          options->shift_modifiers |= GDK_LOCK_MASK;
+                        }
+                    }
+
+                  key_state[VK_CAPITAL] = 0;
+                }
+            }
+        }
     }
-  GDK_NOTE (EVENTS, print_keysym_tab ());
+
+  GDK_NOTE (EVENTS, print_keysym_tab (keymap));
+
+  check_that_active_layout_is_in_sync (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;
+}
+
+void
+_gdk_win32_keymap_set_active_layout (GdkWin32Keymap *keymap,
+                                     HKL             hkl)
+{
+  if (keymap != NULL &&
+      keymap->layout_handles->len > 0)
+    {
+      gint group;
+
+      for (group = 0; group < keymap->layout_handles->len; group++)
+        if (g_array_index (keymap->layout_handles, HKL, group) == hkl)
+          keymap->active_layout = group;
+    }
+}
+
+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;
+
+  return FALSE;
+}
+
+guint8
+_gdk_win32_keymap_get_active_group (GdkWin32Keymap *keymap)
+{
+  if (keymap != NULL &&
+      keymap->layout_handles->len > 0)
+    return keymap->active_layout;
+
+  return 0;
 }
 
 GdkKeymap*
@@ -541,11 +801,9 @@ _gdk_win32_display_get_keymap (GdkDisplay *display)
 }
 
 static PangoDirection
-gdk_win32_keymap_get_direction (GdkKeymap *keymap)
+get_hkl_direction (HKL hkl)
 {
-  update_keymap ();
-
-  switch (PRIMARYLANGID (LOWORD ((DWORD) (gintptr) _gdk_input_locale)))
+  switch (PRIMARYLANGID (LOWORD ((DWORD) (gintptr) hkl)))
     {
     case LANG_HEBREW:
     case LANG_ARABIC:
@@ -561,43 +819,86 @@ gdk_win32_keymap_get_direction (GdkKeymap *keymap)
     }
 }
 
+static PangoDirection
+gdk_win32_keymap_get_direction (GdkKeymap *gdk_keymap)
+{
+  HKL active_hkl;
+  GdkWin32Keymap *keymap;
+
+  if (gdk_keymap == NULL || gdk_keymap != gdk_keymap_get_default ())
+    keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
+  else
+    keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+
+  update_keymap (GDK_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);
+
+  return get_hkl_direction (active_hkl);
+}
+
 static gboolean
-gdk_win32_keymap_have_bidi_layouts (GdkKeymap *keymap)
+gdk_win32_keymap_have_bidi_layouts (GdkKeymap *gdk_keymap)
 {
-  /* Should we check if the kayboard layouts switchable at the moment
-   * cover both directionalities? What does the doc comment in
-   * ../x11/gdkkeys-x11.c exactly mean?
-   */
-  return FALSE;
+  GdkWin32Keymap *keymap;
+  gboolean        have_rtl = FALSE;
+  gboolean        have_ltr = FALSE;
+  gint            group;
+
+  if (gdk_keymap == NULL || gdk_keymap != gdk_keymap_get_default ())
+    keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
+  else
+    keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+
+  update_keymap (GDK_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)
+        have_rtl = TRUE;
+      else
+        have_ltr = TRUE;
+    }
+
+  return have_ltr && have_rtl;
 }
 
 static gboolean
 gdk_win32_keymap_get_caps_lock_state (GdkKeymap *keymap)
 {
+  (void) keymap;
+
   return ((GetKeyState (VK_CAPITAL) & 1) != 0);
 }
 
 static gboolean
 gdk_win32_keymap_get_num_lock_state (GdkKeymap *keymap)
 {
+  (void) keymap;
+
   return ((GetKeyState (VK_NUMLOCK) & 1) != 0);
 }
 
 static gboolean
 gdk_win32_keymap_get_scroll_lock_state (GdkKeymap *keymap)
 {
+  (void) keymap;
+
   return ((GetKeyState (VK_SCROLL) & 1) != 0);
 }
 
 static gboolean
-gdk_win32_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
-                                   guint          keyval,
-                                   GdkKeymapKey **keys,
-                                   gint          *n_keys)
+gdk_win32_keymap_get_entries_for_keyval (GdkKeymap     *gdk_keymap,
+                                         guint          keyval,
+                                         GdkKeymapKey **keys,
+                                         gint          *n_keys)
 {
   GArray *retval;
 
-  g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
+  g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), FALSE);
   g_return_val_if_fail (keys != NULL, FALSE);
   g_return_val_if_fail (n_keys != NULL, FALSE);
   g_return_val_if_fail (keyval != 0, FALSE);
@@ -605,32 +906,44 @@ gdk_win32_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
   retval = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
 
   /* Accept only the default keymap */
-  if (keymap == NULL || keymap == gdk_keymap_get_default ())
+  if (gdk_keymap == NULL || gdk_keymap == gdk_keymap_get_default ())
     {
       gint vk;
+      GdkWin32Keymap *keymap;
 
-      update_keymap ();
+      if (gdk_keymap == NULL)
+        keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
+      else
+        keymap = GDK_WIN32_KEYMAP (gdk_keymap);
+
+      update_keymap (gdk_keymap);
 
       for (vk = 0; vk < KEY_STATE_SIZE; vk++)
-       {
-         gint i;
+        {
+          gint group;
+
+          for (group = 0; group < keymap->layout_handles->len; group++)
+            {
+              GdkWin32KeyLevelState    level;
 
-         for (i = 0; i < 4; i++)
-           {
-             if (keysym_tab[vk*4+i] == keyval)
-               {
-                 GdkKeymapKey key;
+              for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
+                {
+                  guint *keygroup;
 
-                 key.keycode = vk;
+                  keygroup = &keymap->keysym_tab[(vk * keymap->layout_handles->len + group) * 
GDK_WIN32_LEVEL_COUNT];
 
-                 /* 2 levels (normal, shift), two groups (normal, AltGr) */
-                 key.group = i / 2;
-                 key.level = i % 2;
+                  if (keygroup[level] == keyval)
+                    {
+                      GdkKeymapKey key;
 
-                 g_array_append_val (retval, key);
-               }
-           }
-       }
+                      key.keycode = vk;
+                      key.group = group;
+                      key.level = level;
+                      g_array_append_val (retval, key);
+                    }
+                }
+            }
+        }
     }
 
 #ifdef G_ENABLE_DEBUG
@@ -639,12 +952,12 @@ gdk_win32_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
       guint i;
 
       g_print ("gdk_keymap_get_entries_for_keyval: %#.04x (%s):",
-              keyval, gdk_keyval_name (keyval));
+               keyval, gdk_keyval_name (keyval));
       for (i = 0; i < retval->len; i++)
-       {
-         GdkKeymapKey *entry = (GdkKeymapKey *) retval->data + i;
-         g_print ("  %#.02x %d %d", entry->keycode, entry->group, entry->level);
-       }
+        {
+          GdkKeymapKey *entry = (GdkKeymapKey *) retval->data + i;
+          g_print ("  %#.02x %d %d", entry->keycode, entry->group, entry->level);
+        }
       g_print ("\n");
     }
 #endif
@@ -666,21 +979,26 @@ gdk_win32_keymap_get_entries_for_keyval (GdkKeymap     *keymap,
 }
 
 static gboolean
-gdk_win32_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
-                                    guint          hardware_keycode,
-                                    GdkKeymapKey **keys,
-                                    guint        **keyvals,
-                                    gint          *n_entries)
+gdk_win32_keymap_get_entries_for_keycode (GdkKeymap     *gdk_keymap,
+                                          guint          hardware_keycode,
+                                          GdkKeymapKey **keys,
+                                          guint        **keyvals,
+                                          gint          *n_entries)
 {
-  GArray *key_array;
-  GArray *keyval_array;
+  GArray         *key_array;
+  GArray         *keyval_array;
+  gint            group;
+  GdkWin32Keymap *keymap;
 
-  g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
+  g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), FALSE);
   g_return_val_if_fail (n_entries != NULL, FALSE);
 
   if (hardware_keycode <= 0 ||
-      hardware_keycode >= KEY_STATE_SIZE)
+      hardware_keycode >= KEY_STATE_SIZE ||
+      (keys == NULL && keyvals == NULL) ||
+      (gdk_keymap != NULL && gdk_keymap != gdk_keymap_get_default ()))
     {
+      /* Wrong keycode or NULL output arrays or wrong keymap */
       if (keys)
         *keys = NULL;
       if (keyvals)
@@ -700,32 +1018,36 @@ gdk_win32_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
   else
     keyval_array = NULL;
 
-  /* Accept only the default keymap */
-  if (keymap == NULL || keymap == gdk_keymap_get_default ())
-    {
-      gint i;
+  keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
+  update_keymap (GDK_KEYMAP (keymap));
 
-      update_keymap ();
+  for (group = 0; group < keymap->layout_handles->len; group++)
+    {
+      GdkWin32KeyLevelState    level;
 
-      for (i = 0; i < 4; i++)
-       {
-         if (key_array)
+      for (level = GDK_WIN32_LEVEL_NONE; level < GDK_WIN32_LEVEL_COUNT; level++)
+        {
+          if (key_array)
             {
               GdkKeymapKey key;
 
               key.keycode = hardware_keycode;
-
-              key.group = i / 2;
-              key.level = i % 2;
-
+              key.group = group;
+              key.level = level;
               g_array_append_val (key_array, key);
             }
 
           if (keyval_array)
-            g_array_append_val (keyval_array, keysym_tab[hardware_keycode*4+i]);
-       }
+            {
+              guint keyval = keymap->keysym_tab[(hardware_keycode * keymap->layout_handles->len + group) * 
GDK_WIN32_LEVEL_COUNT + level];
+
+              g_array_append_val (keyval_array, keyval);
+            }
+        }
     }
 
+  *n_entries = group * GDK_WIN32_LEVEL_COUNT;
+
   if ((key_array && key_array->len > 0) ||
       (keyval_array && keyval_array->len > 0))
     {
@@ -734,11 +1056,6 @@ gdk_win32_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
 
       if (keyvals)
         *keyvals = (guint*) keyval_array->data;
-
-      if (key_array)
-        *n_entries = key_array->len;
-      else
-        *n_entries = keyval_array->len;
     }
   else
     {
@@ -747,8 +1064,6 @@ gdk_win32_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
 
       if (keyvals)
         *keyvals = NULL;
-
-      *n_entries = 0;
     }
 
   if (key_array)
@@ -760,27 +1075,29 @@ gdk_win32_keymap_get_entries_for_keycode (GdkKeymap     *keymap,
 }
 
 static guint
-gdk_win32_keymap_lookup_key (GdkKeymap          *keymap,
-                       const GdkKeymapKey *key)
+gdk_win32_keymap_lookup_key (GdkKeymap          *gdk_keymap,
+                             const GdkKeymapKey *key)
 {
   guint sym;
+  GdkWin32Keymap *keymap;
 
-  g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), 0);
+  g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), 0);
   g_return_val_if_fail (key != NULL, 0);
   g_return_val_if_fail (key->group < 4, 0);
 
   /* Accept only the default keymap */
-  if (keymap != NULL && keymap != gdk_keymap_get_default ())
+  if (gdk_keymap != NULL && gdk_keymap != gdk_keymap_get_default ())
     return 0;
 
-  update_keymap ();
+  keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
+  update_keymap (GDK_KEYMAP (keymap));
 
   if (key->keycode >= KEY_STATE_SIZE ||
-      key->group < 0 || key->group >= 2 ||
-      key->level < 0 || key->level >= 2)
+      key->group < 0 || key->group >= keymap->layout_handles->len ||
+      key->level < 0 || key->level >= GDK_WIN32_LEVEL_COUNT)
     return 0;
 
-  sym = keysym_tab[key->keycode*4 + key->group*2 + key->level];
+  sym = keymap->keysym_tab[(key->keycode * keymap->layout_handles->len + key->group) * GDK_WIN32_LEVEL_COUNT 
+ key->level];
 
   if (sym == GDK_KEY_VoidSymbol)
     return 0;
@@ -789,22 +1106,23 @@ gdk_win32_keymap_lookup_key (GdkKeymap          *keymap,
 }
 
 static gboolean
-gdk_win32_keymap_translate_keyboard_state (GdkKeymap       *keymap,
-                                     guint            hardware_keycode,
-                                     GdkModifierType  state,
-                                     gint             group,
-                                     guint           *keyval,
-                                     gint            *effective_group,
-                                     gint            *level,
-                                     GdkModifierType *consumed_modifiers)
+gdk_win32_keymap_translate_keyboard_state (GdkKeymap       *gdk_keymap,
+                                           guint            hardware_keycode,
+                                           GdkModifierType  state,
+                                           gint             group,
+                                           guint           *keyval,
+                                           gint            *effective_group,
+                                           gint            *level,
+                                           GdkModifierType *consumed_modifiers)
 {
+  GdkWin32Keymap *keymap;
   guint tmp_keyval;
-  guint *keyvals;
-  gint shift_level;
-  gboolean ignore_shift = FALSE;
-  gboolean ignore_group = FALSE;
+  guint *keygroup;
+  GdkWin32KeyLevelState shift_level;
+  GdkWin32KeyGroupOptions  *options;
+  GdkModifierType modifiers = GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD2_MASK;
 
-  g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE);
+  g_return_val_if_fail (gdk_keymap == NULL || GDK_IS_KEYMAP (gdk_keymap), FALSE);
   g_return_val_if_fail (group < 4, FALSE);
 
 #if 0
@@ -821,83 +1139,103 @@ gdk_win32_keymap_translate_keyboard_state (GdkKeymap       *keymap,
     *consumed_modifiers = 0;
 
   /* Accept only the default keymap */
-  if (keymap != NULL && keymap != gdk_keymap_get_default ())
+  if (gdk_keymap != NULL && gdk_keymap != gdk_keymap_get_default ())
     return FALSE;
 
   if (hardware_keycode >= KEY_STATE_SIZE)
     return FALSE;
 
-  if (group < 0 || group >= 2)
-    return FALSE;
+  keymap = GDK_WIN32_KEYMAP (gdk_keymap_get_default ());
+  update_keymap (GDK_KEYMAP (keymap));
 
-  update_keymap ();
+  if (group < 0 || group >= keymap->layout_handles->len)
+    return FALSE;
 
-  keyvals = keysym_tab + hardware_keycode*4;
+  options = &g_array_index (keymap->options, GdkWin32KeyGroupOptions, group);
+  keygroup = &keymap->keysym_tab[(hardware_keycode * keymap->layout_handles->len + group) * 
GDK_WIN32_LEVEL_COUNT];
 
   if ((state & GDK_LOCK_MASK) &&
       (state & GDK_SHIFT_MASK) &&
-      ((gdk_shift_modifiers & GDK_LOCK_MASK) ||
-       (keyvals[group*2 + 1] == gdk_keyval_to_upper (keyvals[group*2 + 0]))))
+      ((options->shift_modifiers & GDK_LOCK_MASK) ||
+       (keygroup[GDK_WIN32_LEVEL_SHIFT] == gdk_keyval_to_upper (keygroup[GDK_WIN32_LEVEL_NONE]))))
     /* Shift always disables ShiftLock. Shift disables CapsLock for
      * keys with lowercase/uppercase letter pairs.
      */
-    shift_level = 0;
-  else if (state & gdk_shift_modifiers)
-    shift_level = 1;
+    shift_level = GDK_WIN32_LEVEL_NONE;
+  else if (state & options->shift_modifiers)
+    shift_level = GDK_WIN32_LEVEL_SHIFT;
   else
-    shift_level = 0;
+    shift_level = GDK_WIN32_LEVEL_NONE;
 
-  /* Drop group and shift if there are no keysymbols on
-   * the key for those.
-   */
-  if (shift_level == 1 &&
-      keyvals[group*2 + 1] == GDK_KEY_VoidSymbol &&
-      keyvals[group*2] != GDK_KEY_VoidSymbol)
+  if (shift_level == GDK_WIN32_LEVEL_NONE)
     {
-      shift_level = 0;
-      ignore_shift = TRUE;
+      if (state & GDK_MOD2_MASK)
+        shift_level = GDK_WIN32_LEVEL_ALTGR;
     }
-
-  if (group == 1 &&
-      keyvals[2 + shift_level] == GDK_KEY_VoidSymbol &&
-      keyvals[0 + shift_level] != GDK_KEY_VoidSymbol)
+  else
     {
-      group = 0;
-      ignore_group = TRUE;
+      if (state & GDK_MOD2_MASK)
+        shift_level = GDK_WIN32_LEVEL_SHIFT_ALTGR;
     }
 
-  if (keyvals[group *2 + shift_level] == GDK_KEY_VoidSymbol &&
-      keyvals[0 + 0] != GDK_KEY_VoidSymbol)
+  /* Drop altgr and shift if there are no keysymbols on
+   * the key for those.
+   */
+  if (keygroup[shift_level] == GDK_KEY_VoidSymbol)
     {
-      shift_level = 0;
-      group = 0;
-      ignore_group = TRUE;
-      ignore_shift = TRUE;
+      switch (shift_level)
+        {
+         case GDK_WIN32_LEVEL_NONE:
+         case GDK_WIN32_LEVEL_ALTGR:
+           if (keygroup[GDK_WIN32_LEVEL_NONE] != GDK_KEY_VoidSymbol)
+             shift_level = GDK_WIN32_LEVEL_NONE;
+           break;
+         case GDK_WIN32_LEVEL_SHIFT:
+           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_COUNT:
+           g_assert_not_reached ();
+        }
     }
 
   /* See whether the group and shift level actually mattered
    * to know what to put in consumed_modifiers
    */
-  if (keyvals[group*2 + 1] == GDK_KEY_VoidSymbol ||
-      keyvals[group*2 + 0] == keyvals[group*2 + 1])
-    ignore_shift = TRUE;
+  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]))
+      modifiers &= ~(GDK_SHIFT_MASK | GDK_LOCK_MASK);
 
-  if (keyvals[2 + shift_level] == GDK_KEY_VoidSymbol ||
-      keyvals[0 + shift_level] == keyvals[2 + shift_level])
-    ignore_group = TRUE;
+  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]))
+      modifiers &= ~GDK_MOD2_MASK;
 
-  tmp_keyval = keyvals[group*2 + shift_level];
+  tmp_keyval = keygroup[shift_level];
 
   /* If a true CapsLock is toggled, and Shift is not down,
    * and the shifted keysym is the uppercase of the unshifted,
    * use it.
    */
-  if (!(gdk_shift_modifiers & GDK_LOCK_MASK) &&
+  if (!(options->shift_modifiers & GDK_LOCK_MASK) &&
       !(state & GDK_SHIFT_MASK) &&
-      (state & GDK_LOCK_MASK))
+      (state & GDK_LOCK_MASK) &&
+      (shift_level < GDK_WIN32_LEVEL_SHIFT_ALTGR))
     {
       guint upper = gdk_keyval_to_upper (tmp_keyval);
-      if (upper == keyvals[group*2 + 1])
+
+      if (upper == keygroup[shift_level + 1])
        tmp_keyval = upper;
     }
 
@@ -911,15 +1249,11 @@ gdk_win32_keymap_translate_keyboard_state (GdkKeymap       *keymap,
     *level = shift_level;
 
   if (consumed_modifiers)
-    {
-      *consumed_modifiers =
-       (ignore_group ? 0 : GDK_MOD2_MASK) |
-       (ignore_shift ? 0 : (GDK_SHIFT_MASK|GDK_LOCK_MASK));
-    }
+    *consumed_modifiers = modifiers;
 
 #if 0
   GDK_NOTE (EVENTS, g_print ("... group=%d level=%d cmods=%#x keyval=%s\n",
-                            group, shift_level, tmp_modifiers, gdk_keyval_name (tmp_keyval)));
+                            group, shift_level, modifiers, gdk_keyval_name (tmp_keyval)));
 #endif
 
   return tmp_keyval != GDK_KEY_VoidSymbol;
@@ -927,24 +1261,19 @@ gdk_win32_keymap_translate_keyboard_state (GdkKeymap       *keymap,
 
 static void
 gdk_win32_keymap_add_virtual_modifiers (GdkKeymap       *keymap,
-                                  GdkModifierType *state)
+                                        GdkModifierType *state)
 {
 }
 
 static gboolean
 gdk_win32_keymap_map_virtual_modifiers (GdkKeymap       *keymap,
-                                  GdkModifierType *state)
+                                        GdkModifierType *state)
 {
   /* FIXME: Is this the right thing to do? */
   return TRUE;
 }
 
 static void
-gdk_win32_keymap_finalize (GObject *object)
-{
-}
-
-static void
 gdk_win32_keymap_class_init (GdkWin32KeymapClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
diff --git a/gdk/win32/gdkmain-win32.c b/gdk/win32/gdkmain-win32.c
index b170612..de2d1f8 100644
--- a/gdk/win32/gdkmain-win32.c
+++ b/gdk/win32/gdkmain-win32.c
@@ -89,6 +89,7 @@ _gdk_win32_windowing_init (void)
   _gdk_app_hmodule = GetModuleHandle (NULL);
   _gdk_display_hdc = CreateDC ("DISPLAY", NULL, NULL, NULL);
   _gdk_input_locale = GetKeyboardLayout (0);
+  _gdk_win32_keymap_set_active_layout (GDK_WIN32_KEYMAP (_gdk_win32_display_get_keymap (_gdk_display)), 
_gdk_input_locale);
   _gdk_input_locale_is_ime = ImmIsIME (_gdk_input_locale);
   GetLocaleInfo (MAKELCID (LOWORD (_gdk_input_locale), SORT_DEFAULT),
                 LOCALE_IDEFAULTANSICODEPAGE,
diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h
index dac65f4..c8491cb 100644
--- a/gdk/win32/gdkprivate-win32.h
+++ b/gdk/win32/gdkprivate-win32.h
@@ -39,6 +39,7 @@
 #include <gdk/win32/gdkwindow-win32.h>
 #include <gdk/win32/gdkwin32display.h>
 #include <gdk/win32/gdkwin32screen.h>
+#include <gdk/win32/gdkwin32keys.h>
 
 #include "gdkinternals.h"
 
@@ -290,8 +291,6 @@ extern gboolean              _gdk_input_locale_is_ime;
 extern UINT             _gdk_input_codepage;
 
 extern guint            _gdk_keymap_serial;
-extern gboolean                 _gdk_keyboard_has_altgr;
-extern guint            _scancode_rshift;
 
 /* GdkAtoms: properties, targets and types */
 extern GdkAtom          _gdk_selection;
@@ -474,6 +473,13 @@ gint      _gdk_win32_display_text_property_to_utf8_list (GdkDisplay    *display,
                                                         gchar       ***list);
 gchar     *_gdk_win32_display_utf8_to_string_target (GdkDisplay *display, const gchar *str);
 
+gboolean   _gdk_win32_keymap_has_altgr           (GdkWin32Keymap *keymap);
+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);
+
+
 GdkKeymap *_gdk_win32_display_get_keymap (GdkDisplay *display);
 
 void       _gdk_win32_display_create_window_impl   (GdkDisplay    *display,
@@ -534,7 +540,7 @@ void _gdk_win32_append_event (GdkEvent *event);
 void _gdk_win32_emit_configure_event (GdkWindow *window);
 
 
-guint32 _gdk_win32_keymap_get_decimal_mark (void);
+guint32 _gdk_win32_keymap_get_decimal_mark (GdkWin32Keymap *keymap);
 
 void     _gdk_win32_window_handle_aerosnap      (GdkWindow            *window,
                                                  GdkWin32AeroSnapCombo combo);


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