1
0
Fork 0

x11: Add keymap direction query

We should use the Xkb API to query the direction of the key map,
depending on the group. To get a valid result we need to go over
the Unicode equivalents of the key symbols for each group, so we
should cache the result.

The code used to query and cache the key map direction is taken
from GDK.

https://bugzilla.gnome.org/show_bug.cgi?id=705779
This commit is contained in:
Emmanuele Bassi 2014-03-03 23:23:12 +00:00
parent a000349978
commit 3209129d6b
3 changed files with 179 additions and 1 deletions

View file

@ -774,6 +774,17 @@ clutter_backend_x11_create_stage (ClutterBackend *backend,
return stage; return stage;
} }
static PangoDirection
clutter_backend_x11_get_keymap_direction (ClutterBackend *backend)
{
ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend);
if (G_UNLIKELY (backend_x11->keymap == NULL))
return PANGO_DIRECTION_NEUTRAL;
return _clutter_keymap_x11_get_direction (backend_x11->keymap);
}
static void static void
clutter_backend_x11_class_init (ClutterBackendX11Class *klass) clutter_backend_x11_class_init (ClutterBackendX11Class *klass)
{ {
@ -797,6 +808,8 @@ clutter_backend_x11_class_init (ClutterBackendX11Class *klass)
backend_class->get_renderer = clutter_backend_x11_get_renderer; backend_class->get_renderer = clutter_backend_x11_get_renderer;
backend_class->get_display = clutter_backend_x11_get_display; backend_class->get_display = clutter_backend_x11_get_display;
backend_class->create_stage = clutter_backend_x11_create_stage; backend_class->create_stage = clutter_backend_x11_create_stage;
backend_class->get_keymap_direction = clutter_backend_x11_get_keymap_direction;
} }
static void static void

View file

@ -37,6 +37,14 @@
#endif #endif
typedef struct _ClutterKeymapX11Class ClutterKeymapX11Class; typedef struct _ClutterKeymapX11Class ClutterKeymapX11Class;
typedef struct _DirectionCacheEntry DirectionCacheEntry;
struct _DirectionCacheEntry
{
guint serial;
Atom group_atom;
PangoDirection direction;
};
struct _ClutterKeymapX11 struct _ClutterKeymapX11
{ {
@ -52,14 +60,20 @@ struct _ClutterKeymapX11
ClutterModifierType num_lock_mask; ClutterModifierType num_lock_mask;
ClutterModifierType scroll_lock_mask; ClutterModifierType scroll_lock_mask;
PangoDirection current_direction;
#ifdef HAVE_XKB #ifdef HAVE_XKB
XkbDescPtr xkb_desc; XkbDescPtr xkb_desc;
int xkb_event_base; int xkb_event_base;
guint xkb_map_serial; guint xkb_map_serial;
Atom current_group_atom;
guint current_cache_serial;
DirectionCacheEntry group_direction_cache[4];
#endif #endif
guint caps_lock_state : 1; guint caps_lock_state : 1;
guint num_lock_state : 1; guint num_lock_state : 1;
guint has_direction : 1;
}; };
struct _ClutterKeymapX11Class struct _ClutterKeymapX11Class
@ -217,6 +231,128 @@ update_locked_mods (ClutterKeymapX11 *keymap_x11,
} }
#endif /* HAVE_XKB */ #endif /* HAVE_XKB */
#ifdef HAVE_XKB
/* the code to retrieve the keymap direction and cache it
* is taken from GDK:
* gdk/x11/gdkkeys-x11.c
*/
static PangoDirection
get_direction (XkbDescPtr xkb,
int group)
{
int rtl_minus_ltr = 0; /* total number of RTL keysyms minus LTR ones */
int code;
for (code = xkb->min_key_code;
code <= xkb->max_key_code;
code += 1)
{
int level = 0;
KeySym sym = XkbKeySymEntry (xkb, code, level, group);
PangoDirection dir = pango_unichar_direction (clutter_keysym_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;
return PANGO_DIRECTION_LTR;
}
static PangoDirection
get_direction_from_cache (ClutterKeymapX11 *keymap_x11,
XkbDescPtr xkb,
int group)
{
Atom group_atom = xkb->names->groups[group];
gboolean cache_hit = FALSE;
DirectionCacheEntry *cache = keymap_x11->group_direction_cache;
PangoDirection direction = PANGO_DIRECTION_NEUTRAL;
int i;
if (keymap_x11->has_direction)
{
/* look up in the 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++;
direction = cache[i].direction;
group_atom = cache[i].group_atom;
break;
}
}
}
else
{
/* initialize the cache */
for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++)
{
cache[i].group_atom = 0;
cache[i].direction = PANGO_DIRECTION_NEUTRAL;
cache[i].serial = keymap_x11->current_cache_serial;
}
keymap_x11->current_cache_serial += 1;
}
/* insert the new entry in the cache */
if (!cache_hit)
{
int oldest = 0;
direction = get_direction (xkb, group);
/* replace 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++;
}
return direction;
}
#endif /* HAVE_XKB */
static void
update_direction (ClutterKeymapX11 *keymap_x11,
int group)
{
#ifdef HAVE_XKB
XkbDescPtr xkb = get_xkb (keymap_x11);
Atom group_atom;
group_atom = xkb->names->groups[group];
if (!keymap_x11->has_direction || keymap_x11->current_group_atom != group_atom)
{
keymap_x11->current_direction = get_direction_from_cache (keymap_x11, xkb, group);
keymap_x11->current_group_atom = group_atom;
keymap_x11->has_direction = TRUE;
}
#endif /* HAVE_XKB */
}
static void static void
clutter_keymap_x11_constructed (GObject *gobject) clutter_keymap_x11_constructed (GObject *gobject)
{ {
@ -332,6 +468,7 @@ clutter_keymap_x11_class_init (ClutterKeymapX11Class *klass)
static void static void
clutter_keymap_x11_init (ClutterKeymapX11 *keymap) clutter_keymap_x11_init (ClutterKeymapX11 *keymap)
{ {
keymap->current_direction = PANGO_DIRECTION_NEUTRAL;
} }
static ClutterTranslateReturn static ClutterTranslateReturn
@ -360,7 +497,8 @@ clutter_keymap_x11_translate_event (ClutterEventTranslator *translator,
switch (xkb_event->any.xkb_type) switch (xkb_event->any.xkb_type)
{ {
case XkbStateNotify: case XkbStateNotify:
CLUTTER_NOTE (EVENT, "Updating locked modifiers"); CLUTTER_NOTE (EVENT, "Updating keyboard state");
update_direction (keymap_x11, XkbStateGroup (&xkb_event->state));
update_locked_mods (keymap_x11, xkb_event->state.locked_mods); update_locked_mods (keymap_x11, xkb_event->state.locked_mods);
retval = CLUTTER_TRANSLATE_REMOVE; retval = CLUTTER_TRANSLATE_REMOVE;
break; break;
@ -503,3 +641,27 @@ _clutter_keymap_x11_get_is_modifier (ClutterKeymapX11 *keymap,
return FALSE; return FALSE;
} }
PangoDirection
_clutter_keymap_x11_get_direction (ClutterKeymapX11 *keymap)
{
g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), PANGO_DIRECTION_NEUTRAL);
#ifdef HAVE_XKB
if (CLUTTER_BACKEND_X11 (keymap->backend)->use_xkb)
{
if (!keymap->has_direction)
{
Display *xdisplay = CLUTTER_BACKEND_X11 (keymap->backend)->xdpy;
XkbStateRec state_rec;
XkbGetState (xdisplay, XkbUseCoreKbd, &state_rec);
update_direction (keymap, XkbStateGroup (&state_rec));
}
return keymap->current_direction;
}
else
#endif
return PANGO_DIRECTION_NEUTRAL;
}

View file

@ -25,6 +25,7 @@
#define __CLUTTER_KEYMAP_X11_H__ #define __CLUTTER_KEYMAP_X11_H__
#include <glib-object.h> #include <glib-object.h>
#include <pango/pango.h>
#include <clutter/clutter-event.h> #include <clutter/clutter-event.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -48,6 +49,8 @@ gint _clutter_keymap_x11_translate_key_state (ClutterKeymapX11 *keymap,
gboolean _clutter_keymap_x11_get_is_modifier (ClutterKeymapX11 *keymap, gboolean _clutter_keymap_x11_get_is_modifier (ClutterKeymapX11 *keymap,
gint keycode); gint keycode);
PangoDirection _clutter_keymap_x11_get_direction (ClutterKeymapX11 *keymap);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_KEYMAP_X11_H__ */ #endif /* __CLUTTER_KEYMAP_X11_H__ */