diff options
author | Matthias Clasen <mclasen@redhat.com> | 2004-11-29 14:25:22 +0000 |
---|---|---|
committer | Matthias Clasen <matthiasc@src.gnome.org> | 2004-11-29 14:25:22 +0000 |
commit | c61b0e4a07d4a4147ba9d57fe0b04d95c8bea84c (patch) | |
tree | 8216368a7f44e9692d241bef175105bda1401e8e /gdk | |
parent | e08caa5bae054c3b1d528e61b390a6f1b129f791 (diff) | |
download | gdk-pixbuf-c61b0e4a07d4a4147ba9d57fe0b04d95c8bea84c.tar.gz |
Determine the direction of XKB groups from their content, not by looking
2004-11-29 Matthias Clasen <mclasen@redhat.com>
Determine the direction of XKB groups from their content,
not by looking for hardcoded keymap names. (#116626, patch by
Behdad Esfahbod, based on an earlier patch by Ilya Konstantinov)
* gdk/x11/gdkkeys-x11.c (struct _GdkKeymapX11): Cache directions
for XKB groups.
(get_direction): Determine direction of group by looking at
directions of keysyms.
(update_direction): Maintain the cache of group directions.
(gdk_keymap_get_direction): Use update_direction().
Diffstat (limited to 'gdk')
-rw-r--r-- | gdk/x11/gdkkeys-x11.c | 187 |
1 files changed, 149 insertions, 38 deletions
diff --git a/gdk/x11/gdkkeys-x11.c b/gdk/x11/gdkkeys-x11.c index 29571ec03..957d0bf2f 100644 --- a/gdk/x11/gdkkeys-x11.c +++ b/gdk/x11/gdkkeys-x11.c @@ -59,10 +59,19 @@ typedef struct _GdkKeymapX11 GdkKeymapX11; #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)) +typedef struct _DirectionCacheEntry DirectionCacheEntry; + +struct _DirectionCacheEntry +{ + guint serial; + Atom group_atom; + PangoDirection direction; +}; + struct _GdkKeymapX11 { GdkKeymap parent_instance; - + gint min_keycode; gint max_keycode; KeySym* keymap; @@ -75,9 +84,18 @@ struct _GdkKeymapX11 PangoDirection current_direction; gboolean have_direction; guint current_serial; - + #ifdef HAVE_XKB XkbDescPtr xkb_desc; + /* We cache the directions */ + Atom current_group_atom; + guint current_cache_serial; + /* A cache of size four should be more than enough, people usually + * have two groups around, and the xkb limit is four. It still + * works correct for more than four groups. It's just the + * cache. + */ + DirectionCacheEntry group_direction_cache[4]; #endif }; @@ -130,12 +148,14 @@ gdk_keymap_x11_init (GdkKeymapX11 *keymap) keymap->group_switch_mask = 0; keymap->lock_keysym = GDK_Caps_Lock; keymap->have_direction = FALSE; - + keymap->current_serial = 0; + #ifdef HAVE_XKB keymap->xkb_desc = NULL; + keymap->current_group_atom = 0; + keymap->current_cache_serial = 0; #endif - keymap->current_serial = 0; } static inline void @@ -211,14 +231,17 @@ gdk_keymap_get_for_display (GdkDisplay *display) * otherwise we lose a whole group of keys */ #define KEYSYM_INDEX(keymap_impl, group, level) \ - (2 * ((group) % (int)((keymap_impl->keysyms_per_keycode + 1) / 2)) + (level)) + (2 * ((group) % (gint)((keymap_impl->keysyms_per_keycode + 1) / 2)) + (level)) #define KEYSYM_IS_KEYPAD(s) (((s) >= 0xff80 && (s) <= 0xffbd) || \ ((s) >= 0x11000000 && (s) <= 0x1100ffff)) -static int -get_symbol (const KeySym *syms, GdkKeymapX11 *keymap_x11, int group, int level) +static gint +get_symbol (const KeySym *syms, + GdkKeymapX11 *keymap_x11, + gint group, + gint level) { - int index; + gint index; index = KEYSYM_INDEX(keymap_x11, group, level); if (index > keymap_x11->keysyms_per_keycode) @@ -397,7 +420,7 @@ get_keymap (GdkKeymapX11 *keymap_x11) static GdkKeymap * get_effective_keymap (GdkKeymap *keymap, - const char *function) + const char *function) { if (!keymap) { @@ -412,34 +435,132 @@ get_effective_keymap (GdkKeymap *keymap, #if HAVE_XKB static PangoDirection -get_direction (GdkKeymapX11 *keymap_x11) +get_direction (XkbDescRec *xkb, + gint group) +{ + gint code; + + gint rtl_minus_ltr = 0; /* total number of RTL keysyms minus LTR ones */ + + for (code = xkb->min_key_code; code <= xkb->max_key_code; code++) + { + gint width = XkbKeyGroupWidth (xkb, code, group); + gint level; + for (level = 0; level < width; level++) + { + KeySym sym = XkbKeySymEntry (xkb, code, level, group); + PangoDirection dir = pango_unichar_direction (gdk_keyval_to_unicode (sym)); + + switch (dir) + { + case PANGO_DIRECTION_RTL: + rtl_minus_ltr++; + break; + case PANGO_DIRECTION_LTR: + rtl_minus_ltr--; + break; + default: + break; + } + } + } + + if (rtl_minus_ltr > 0) + return PANGO_DIRECTION_RTL; + else + return PANGO_DIRECTION_LTR; +} + +static void +update_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; + gint group; + Atom group_atom; XkbGetState (GDK_DISPLAY_XDISPLAY (display), XkbUseCoreKbd, &state_rec); - - if (xkb->names->groups[state_rec.locked_group] == None) - result = PANGO_DIRECTION_LTR; - else + group = XkbGroupLock (&state_rec); + group_atom = xkb->names->groups[group]; + + /* a group change? */ + if (!keymap_x11->have_direction || keymap_x11->current_group_atom != group_atom) { - name = gdk_x11_get_xatom_name_for_display (display, xkb->names->groups[state_rec.locked_group]); + gboolean cache_hit = FALSE; + DirectionCacheEntry *cache = keymap_x11->group_direction_cache; - if (g_ascii_strcasecmp (name, "arabic") == 0 || - g_ascii_strcasecmp (name, "hebrew") == 0 || - g_ascii_strcasecmp (name, "israelian") == 0) - result = PANGO_DIRECTION_RTL; + PangoDirection direction = PANGO_DIRECTION_NEUTRAL; + gint i; + + if (keymap_x11->have_direction) + { + /* lookup in cache */ + for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++) + { + if (cache[i].group_atom == group_atom) + { + cache_hit = TRUE; + cache[i].serial = keymap_x11->current_cache_serial++; /* freshen */ + direction = cache[i].direction; + group_atom = cache[i].group_atom; + break; + } + } + } else - result = PANGO_DIRECTION_LTR; + { + /* initialize cache */ + for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++) + { + cache[i].group_atom = 0; + cache[i].serial = keymap_x11->current_cache_serial; + } + keymap_x11->current_cache_serial++; + } + + /* insert in cache */ + if (!cache_hit) + { + gint oldest = 0; + + direction = get_direction (xkb, group); + + /* remove the oldest entry */ + for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++) + { + if (cache[i].serial < cache[oldest].serial) + oldest = i; + } + + cache[oldest].group_atom = group_atom; + cache[oldest].direction = direction; + cache[oldest].serial = keymap_x11->current_cache_serial++; + } + + keymap_x11->current_group_atom = group_atom; + + keymap_x11->have_direction = TRUE; + keymap_x11->current_direction = direction; } - - return result; } +static void +_gdk_keymap_direction_changed (GdkKeymapX11 *keymap_x11) +{ + gboolean had_direction; + PangoDirection direction; + + had_direction = keymap_x11->have_direction; + direction = keymap_x11->current_direction; + + update_direction (keymap_x11); + + if (!had_direction || direction != keymap_x11->current_direction) + g_signal_emit_by_name (keymap_x11, "direction_changed"); +} + + void _gdk_keymap_state_changed (GdkDisplay *display) { @@ -449,14 +570,7 @@ _gdk_keymap_state_changed (GdkDisplay *display) { 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"); - } + _gdk_keymap_direction_changed (keymap_x11); } } @@ -484,16 +598,13 @@ gdk_keymap_get_direction (GdkKeymap *keymap) 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; - } + update_direction (keymap_x11); return keymap_x11->current_direction; } else #endif /* HAVE_XKB */ - return PANGO_DIRECTION_LTR; + return PANGO_DIRECTION_NEUTRAL; } /** |