--- gtk+/gdk/x11/gdkkeys-x11.c.orig	2004-03-09 01:23:54.219631680 -0500
+++ gtk+/gdk/x11/gdkkeys-x11.c	2004-03-09 04:47:34.317894368 -0500
@@ -58,6 +58,12 @@
 #define GDK_KEYMAP_X11(object)       (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_KEYMAP_X11, GdkKeymapX11))
 #define GDK_IS_KEYMAP_X11(object)    (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_KEYMAP_X11))
 
+struct _DirectionCacheEntry {
+  guint serial;
+  gchar *group_name;
+  PangoDirection direction;
+};
+
 struct _GdkKeymapX11
 {
   GdkKeymap     parent_instance;
@@ -77,6 +83,13 @@
   
 #ifdef HAVE_XKB
   XkbDescPtr xkb_desc;
+  /* We cache the directions */
+  char *current_group_name;
+  /* A cache of size three should be more than enough, people usually
+     have two groups around.
+  */
+  struct _DirectionCacheEntry group_direction_cache[3];
+
 #endif
 };
 
@@ -380,32 +393,121 @@
 
 #if HAVE_XKB
 static PangoDirection
-get_direction (GdkKeymapX11 *keymap_x11)
+get_direction (XkbDescRec *xkb, unsigned char group)
+{
+  KeyCode code = xkb->min_key_code;
+
+  /* We use an infinite loop here instead of 'for' since max_key_code
+   * is often the maximum value of KeyCode (=CARD8), and a for loop
+   *  ends up overflowing and running endlessly.
+   */
+  for (;;)
+    {
+      int width = XkbKeyGroupsWidth(xkb, code);
+      int w;
+      for (w = 0; w < width; ++w)
+	{
+	  KeySym sym = XkbKeySymEntry(xkb, code, w, group);
+
+	  if (pango_unichar_direction(
+              gdk_keyval_to_unicode(sym)) == PANGO_DIRECTION_RTL)
+	  {
+	    return PANGO_DIRECTION_RTL;
+	  }
+	}
+      if (code == xkb->max_key_code)
+	break;
+      ++code;
+    }
+    
+  return PANGO_DIRECTION_LTR;
+}
+
+#define GROUP_CACHE_SIZE(keymap_x11) (G_N_ELEMENTS(keymap_x11->group_direction_cache))
+
+static void
+find_direction (GdkKeymapX11 *keymap_x11)
 {
   XkbDescRec *xkb = get_xkb (keymap_x11);
-  const char *name;
   XkbStateRec state_rec;
-  PangoDirection result;
-
   GdkDisplay *display = GDK_KEYMAP (keymap_x11)->display;
+  unsigned char group;
+  gchar *group_name;
 
   XkbGetState (GDK_DISPLAY_XDISPLAY (display), XkbUseCoreKbd, &state_rec);
-  
-  if (xkb->names->groups[state_rec.locked_group] == None)
-    result = PANGO_DIRECTION_LTR;
-  else
-    {
-      name = gdk_x11_get_xatom_name_for_display (display, xkb->names->groups[state_rec.locked_group]);
+  group = XkbGroupLock(&state_rec);
+  group_name = (gchar *)gdk_x11_get_xatom_name_for_display (display, xkb->names->groups[group]);
 
-      if (g_ascii_strcasecmp (name, "arabic") == 0 ||
-	  g_ascii_strcasecmp (name, "hebrew") == 0 ||
-	  g_ascii_strcasecmp (name, "israelian") == 0)
-	result = PANGO_DIRECTION_RTL;
-      else
-	result = PANGO_DIRECTION_LTR;
+  /* a group change? */
+  if (!keymap_x11->have_direction ||
+	  0 != strcmp(keymap_x11->current_group_name, group_name))
+    {
+      /* cache lookup status: 0=not initialized, 1=miss, 2=hit */
+      int cache_state = 0;
+      struct _DirectionCacheEntry *cache = keymap_x11->group_direction_cache;
+      PangoDirection direction = PANGO_DIRECTION_NEUTRAL;
+
+      if (keymap_x11->have_direction)
+	{
+	  /* lookup in cache */
+          int i;
+
+	  cache_state = 1/*hit*/;
+
+	  for (i = 0; i < GROUP_CACHE_SIZE(keymap_x11); i++)
+	    if (0 == strcmp(cache[i].group_name, group_name))
+	      {
+		cache_state = 2;
+		cache[i].serial = keymap_x11->current_serial;
+		direction = cache[i].direction;
+		group_name = cache[i].group_name;
+		break;
+	      }
+
+	}
+
+      if (cache_state == 0/*not initialized*/)
+	{
+	  int i;
+
+	  for (i = 0; i < G_N_ELEMENTS(keymap_x11->group_direction_cache); i++)
+	    {
+	      cache[i].group_name = "";
+	      cache[i].serial = keymap_x11->current_serial;
+	    }
+	}
+
+      if (cache_state != 2/*not hit*/)
+	{
+	  int i;
+	  int oldest = 0;
+
+	  /* g_message("finding keymap direction for %s\n", group_name); */
+	  direction = get_direction(xkb, group);
+	  group_name = g_strdup(group_name);
+
+	  /* insert in cache by removing the oldest entry */
+	  for (i = 1; i < G_N_ELEMENTS(keymap_x11->group_direction_cache); i++)
+	    if (cache[oldest].serial > cache[i].serial)
+	      oldest = i;
+	  
+	  if (*(cache[oldest].group_name))
+	    g_free(cache[oldest].group_name);
+
+	  cache[oldest].group_name = group_name;
+	  cache[oldest].direction = direction;
+	  cache[oldest].serial = keymap_x11->current_serial;
+	}
+
+      /* notify and voila */
+      if (!keymap_x11->have_direction || direction != keymap_x11->current_direction)
+	{
+          keymap_x11->have_direction = TRUE;
+          keymap_x11->current_direction = direction;
+          keymap_x11->current_group_name = group_name;
+	  g_signal_emit_by_name (keymap_x11, "direction_changed");
+	}
     }
-    
-  return result;
 }
 
 void
@@ -417,14 +519,7 @@
     {
       GdkKeymapX11 *keymap_x11 = GDK_KEYMAP_X11 (display_x11->keymap);
       
-      PangoDirection new_direction = get_direction (keymap_x11);
-      
-      if (!keymap_x11->have_direction || new_direction != keymap_x11->current_direction)
-	{
-	  keymap_x11->have_direction = TRUE;
-	  keymap_x11->current_direction = new_direction;
-	  g_signal_emit_by_name (keymap_x11, "direction_changed");
-	}
+      find_direction (keymap_x11);
     }
 }
 
@@ -458,16 +553,13 @@
       GdkKeymapX11 *keymap_x11 = GDK_KEYMAP_X11 (keymap);
       
       if (!keymap_x11->have_direction)
-	{
-	  keymap_x11->current_direction = get_direction (keymap_x11);
-	  keymap_x11->have_direction = TRUE;
-	}
+	find_direction(keymap_x11);
   
       return keymap_x11->current_direction;
     }
   else
 #endif /* HAVE_XKB */
-    return PANGO_DIRECTION_LTR;
+    return PANGO_DIRECTION_NEUTRAL;
 }
 
 /**

