X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gdk%2Fx11%2Fgdkkeys-x11.c;h=831c69f1d4f22ee0a98ae2b18e0c6ac686edecfc;hb=c3851df92d9c4bff0bb99855faa8c8110d248318;hp=36a05ec907ff80487e0a0bd7d8498ad6c3c6e9a6;hpb=aeaaae04ac7b17964764fdeb4a51197022879cfd;p=~andy%2Fgtk diff --git a/gdk/x11/gdkkeys-x11.c b/gdk/x11/gdkkeys-x11.c index 36a05ec90..831c69f1d 100644 --- a/gdk/x11/gdkkeys-x11.c +++ b/gdk/x11/gdkkeys-x11.c @@ -24,6 +24,8 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#include "config.h" + #include #include #include @@ -39,8 +41,6 @@ #include "gdkdisplay-x11.h" #include "gdkkeysyms.h" -#include "config.h" - #ifdef HAVE_XKB #include @@ -52,36 +52,65 @@ # endif #endif /* HAVE_XKB */ -typedef struct _GdkKeymapX11 GdkKeymapX11; +typedef struct _GdkKeymapX11 GdkKeymapX11; +typedef struct _GdkKeymapClass GdkKeymapX11Class; #define GDK_TYPE_KEYMAP_X11 (gdk_keymap_x11_get_type ()) #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; gint keysyms_per_keycode; XModifierKeymap* mod_keymap; + guint lock_keysym; GdkModifierType group_switch_mask; + GdkModifierType num_lock_mask; + GdkModifierType modmap[8]; PangoDirection current_direction; - gboolean have_direction; + guint sun_keypad : 1; + guint have_direction : 1; + guint caps_lock_state : 1; + guint num_lock_state : 1; 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 }; #define KEYMAP_USE_XKB(keymap) GDK_DISPLAY_X11 ((keymap)->display)->use_xkb #define KEYMAP_XDISPLAY(keymap) GDK_DISPLAY_XDISPLAY ((keymap)->display) -static GType gdk_keymap_x11_get_type (void); -static void gdk_keymap_x11_init (GdkKeymapX11 *keymap); +static GType gdk_keymap_x11_get_type (void); +static void gdk_keymap_x11_class_init (GdkKeymapX11Class *klass); +static void gdk_keymap_x11_init (GdkKeymapX11 *keymap); +static void gdk_keymap_x11_finalize (GObject *object); + +static GdkKeymapClass *parent_class = NULL; static GType gdk_keymap_x11_get_type (void) @@ -90,12 +119,12 @@ gdk_keymap_x11_get_type (void) if (!object_type) { - static const GTypeInfo object_info = + const GTypeInfo object_info = { sizeof (GdkKeymapClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, - (GClassInitFunc) NULL, + (GClassInitFunc) gdk_keymap_x11_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GdkKeymapX11), @@ -104,13 +133,23 @@ gdk_keymap_x11_get_type (void) }; object_type = g_type_register_static (GDK_TYPE_KEYMAP, - "GdkKeymapX11", + g_intern_static_string ("GdkKeymapX11"), &object_info, 0); } return object_type; } +static void +gdk_keymap_x11_class_init (GdkKeymapX11Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_keymap_x11_finalize; +} + static void gdk_keymap_x11_init (GdkKeymapX11 *keymap) { @@ -121,11 +160,38 @@ gdk_keymap_x11_init (GdkKeymapX11 *keymap) keymap->keysyms_per_keycode = 0; keymap->mod_keymap = NULL; + keymap->num_lock_mask = 0; + keymap->sun_keypad = FALSE; keymap->group_switch_mask = 0; + keymap->lock_keysym = GDK_KEY_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 void +gdk_keymap_x11_finalize (GObject *object) +{ + GdkKeymapX11 *keymap_x11 = GDK_KEYMAP_X11 (object); + + if (keymap_x11->keymap) + XFree (keymap_x11->keymap); + + if (keymap_x11->mod_keymap) + XFreeModifiermap (keymap_x11->mod_keymap); + +#ifdef HAVE_XKB + if (keymap_x11->xkb_desc) + XkbFreeKeyboard (keymap_x11->xkb_desc, XkbAllComponentsMask, True); +#endif + + G_OBJECT_CLASS (parent_class)->finalize (object); } static inline void @@ -138,6 +204,46 @@ update_keyrange (GdkKeymapX11 *keymap_x11) #ifdef HAVE_XKB +static void +update_modmap (Display *display, + GdkKeymapX11 *keymap_x11) +{ + static struct { + const gchar *name; + Atom atom; + GdkModifierType mask; + } vmods[] = { + { "Meta", 0, GDK_META_MASK }, + { "Super", 0, GDK_SUPER_MASK }, + { "Hyper", 0, GDK_HYPER_MASK }, + { NULL, 0, 0 } + }; + + gint i, j, k; + + if (!vmods[0].atom) + for (i = 0; vmods[i].name; i++) + vmods[i].atom = XInternAtom (display, vmods[i].name, FALSE); + + for (i = 0; i < 8; i++) + keymap_x11->modmap[i] = 1 << i; + + for (i = 0; i < XkbNumVirtualMods; i++) + { + for (j = 0; vmods[j].atom; j++) + { + if (keymap_x11->xkb_desc->names->vmods[i] == vmods[j].atom) + { + for (k = 0; k < 8; k++) + { + if (keymap_x11->xkb_desc->server->vmods[i] & (1 << k)) + keymap_x11->modmap[k] |= vmods[j].mask; + } + } + } + } +} + static XkbDescPtr get_xkb (GdkKeymapX11 *keymap_x11) { @@ -148,21 +254,31 @@ get_xkb (GdkKeymapX11 *keymap_x11) if (keymap_x11->xkb_desc == NULL) { - keymap_x11->xkb_desc = XkbGetMap (xdisplay, XkbKeySymsMask | XkbKeyTypesMask, XkbUseCoreKbd); + keymap_x11->xkb_desc = XkbGetMap (xdisplay, XkbKeySymsMask | XkbKeyTypesMask | XkbModifierMapMask | XkbVirtualModsMask, XkbUseCoreKbd); if (keymap_x11->xkb_desc == NULL) - g_error ("Failed to get keymap"); + { + g_error ("Failed to get keymap"); + return NULL; + } + + XkbGetNames (xdisplay, XkbGroupNamesMask | XkbVirtualModNamesMask, keymap_x11->xkb_desc); - XkbGetNames (xdisplay, XkbGroupNamesMask, keymap_x11->xkb_desc); + update_modmap (xdisplay, keymap_x11); } else if (keymap_x11->current_serial != display_x11->keymap_serial) { - XkbGetUpdatedMap (xdisplay, XkbKeySymsMask | XkbKeyTypesMask, + XkbGetUpdatedMap (xdisplay, XkbKeySymsMask | XkbKeyTypesMask | XkbModifierMapMask | XkbVirtualModsMask, keymap_x11->xkb_desc); - XkbGetNames (xdisplay, XkbGroupNamesMask, keymap_x11->xkb_desc); + XkbGetNames (xdisplay, XkbGroupNamesMask | XkbVirtualModNamesMask, keymap_x11->xkb_desc); + + update_modmap (xdisplay, keymap_x11); } keymap_x11->current_serial = display_x11->keymap_serial; + if (keymap_x11->num_lock_mask == 0) + keymap_x11->num_lock_mask = XkbKeysymToModifiers (KEYMAP_XDISPLAY (GDK_KEYMAP (keymap_x11)), XK_Num_Lock); + return keymap_x11->xkb_desc; } #endif /* HAVE_XKB */ @@ -178,6 +294,8 @@ get_xkb (GdkKeymapX11 *keymap_x11) * @returns: the #GdkKeymap attached to @display. * * Returns the #GdkKeymap attached to @display. + * + * Since: 2.2 **/ GdkKeymap* gdk_keymap_get_for_display (GdkDisplay *display) @@ -195,9 +313,44 @@ gdk_keymap_get_for_display (GdkDisplay *display) } /* Find the index of the group/level pair within the keysyms for a key. + * We round up the number of keysyms per keycode to the next even number, + * otherwise we lose a whole group of keys */ #define KEYSYM_INDEX(keymap_impl, group, level) \ - (2 * ((group) % (keymap_impl->keysyms_per_keycode / 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 gint +get_symbol (const KeySym *syms, + GdkKeymapX11 *keymap_x11, + gint group, + gint level) +{ + gint index; + + index = KEYSYM_INDEX(keymap_x11, group, level); + if (index >= keymap_x11->keysyms_per_keycode) + return NoSymbol; + + return syms[index]; +} + +static void +set_symbol (KeySym *syms, + GdkKeymapX11 *keymap_x11, + gint group, + gint level, + KeySym sym) +{ + gint index; + + index = KEYSYM_INDEX(keymap_x11, group, level); + if (index >= keymap_x11->keysyms_per_keycode) + return; + + syms[index] = sym; +} static void update_keymaps (GdkKeymapX11 *keymap_x11) @@ -227,40 +380,40 @@ update_keymaps (GdkKeymapX11 *keymap_x11) XFreeModifiermap (keymap_x11->mod_keymap); keymap_x11->keymap = XGetKeyboardMapping (xdisplay, keymap_x11->min_keycode, - keymap_x11->max_keycode - keymap_x11->min_keycode, + keymap_x11->max_keycode - keymap_x11->min_keycode + 1, &keymap_x11->keysyms_per_keycode); - /* GDK_ISO_Left_Tab, as usually configured through XKB, really messes + /* GDK_KEY_ISO_Left_Tab, as usually configured through XKB, really messes * up the whole idea of "consumed modifiers" because shift is consumed. - * However, Tab is not usually GDK_ISO_Left_Tab without XKB, + * However, Tab is not usually GDK_KEY_ISO_Left_Tab without XKB, * we we fudge the map here. */ keycode = keymap_x11->min_keycode; - while (keycode < keymap_x11->max_keycode) + while (keycode <= keymap_x11->max_keycode) { KeySym *syms = keymap_x11->keymap + (keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode; /* Check both groups */ for (i = 0 ; i < 2 ; i++) { - if (syms[KEYSYM_INDEX (keymap_x11, i, 0)] == GDK_Tab) - syms[KEYSYM_INDEX (keymap_x11, i, 1)] = GDK_ISO_Left_Tab; + if (get_symbol (syms, keymap_x11, i, 0) == GDK_KEY_Tab) + set_symbol (syms, keymap_x11, i, 1, GDK_KEY_ISO_Left_Tab); } /* * If there is one keysym and the key symbol has upper and lower * case variants fudge the keymap */ - if (syms[KEYSYM_INDEX (keymap_x11, 0, 1)] == 0) + if (get_symbol (syms, keymap_x11, 0, 1) == 0) { guint lower; guint upper; - gdk_keyval_convert_case (syms[KEYSYM_INDEX (keymap_x11, 0, 0)], &lower, &upper); + gdk_keyval_convert_case (get_symbol (syms, keymap_x11, 0, 0), &lower, &upper); if (lower != upper) { - syms[KEYSYM_INDEX (keymap_x11, 0, 0)] = lower; - syms[KEYSYM_INDEX (keymap_x11, 0, 1)] = upper; + set_symbol (syms, keymap_x11, 0, 0, lower); + set_symbol (syms, keymap_x11, 0, 1, upper); } } @@ -270,49 +423,109 @@ update_keymaps (GdkKeymapX11 *keymap_x11) keymap_x11->mod_keymap = XGetModifierMapping (xdisplay); - + keymap_x11->lock_keysym = GDK_KEY_VoidSymbol; keymap_x11->group_switch_mask = 0; + keymap_x11->num_lock_mask = 0; - /* there are 8 modifiers, and the first 3 are shift, shift lock, - * and control + for (i = 0; i < 8; i++) + keymap_x11->modmap[i] = 1 << i; + + /* There are 8 sets of modifiers, with each set containing + * max_keypermod keycodes. */ map_size = 8 * keymap_x11->mod_keymap->max_keypermod; - i = 3 * keymap_x11->mod_keymap->max_keypermod; - while (i < map_size) + for (i = 0; i < map_size; i++) { - /* get the key code at this point in the map, - * see if its keysym is GDK_Mode_switch, if so - * we have the mode key - */ + /* Get the key code at this point in the map. */ gint keycode = keymap_x11->mod_keymap->modifiermap[i]; - - if (keycode >= keymap_x11->min_keycode && - keycode <= keymap_x11->max_keycode) + gint j; + KeySym *syms; + guint mask; + + /* Ignore invalid keycodes. */ + if (keycode < keymap_x11->min_keycode || + keycode > keymap_x11->max_keycode) + continue; + + syms = keymap_x11->keymap + (keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode; + + mask = 0; + for (j = 0; j < keymap_x11->keysyms_per_keycode; j++) + { + if (syms[j] == GDK_KEY_Meta_L || + syms[j] == GDK_KEY_Meta_R) + mask |= GDK_META_MASK; + else if (syms[j] == GDK_KEY_Hyper_L || + syms[j] == GDK_KEY_Hyper_R) + mask |= GDK_HYPER_MASK; + else if (syms[j] == GDK_KEY_Super_L || + syms[j] == GDK_KEY_Super_R) + mask |= GDK_SUPER_MASK; + } + + keymap_x11->modmap[i/keymap_x11->mod_keymap->max_keypermod] |= mask; + + /* The fourth modifier, GDK_MOD1_MASK is 1 << 3. + * Each group of max_keypermod entries refers to the same modifier. + */ + mask = 1 << (i / keymap_x11->mod_keymap->max_keypermod); + + switch (mask) { - gint j = 0; - KeySym *syms = keymap_x11->keymap + (keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode; - - while (j < keymap_x11->keysyms_per_keycode) + case GDK_LOCK_MASK: + /* Get the Lock keysym. If any keysym bound to the Lock modifier + * is Caps_Lock, we will interpret the modifier as Caps_Lock; + * otherwise, if any is bound to Shift_Lock, we will interpret + * the modifier as Shift_Lock. Otherwise, the lock modifier + * has no effect. + */ + for (j = 0; j < keymap_x11->keysyms_per_keycode; j++) + { + if (syms[j] == GDK_KEY_Caps_Lock) + keymap_x11->lock_keysym = GDK_KEY_Caps_Lock; + else if (syms[j] == GDK_KEY_Shift_Lock && + keymap_x11->lock_keysym == GDK_KEY_VoidSymbol) + keymap_x11->lock_keysym = GDK_KEY_Shift_Lock; + } + break; + + case GDK_CONTROL_MASK: + case GDK_SHIFT_MASK: + case GDK_MOD1_MASK: + /* Some keyboard maps are known to map Mode_Switch as an + * extra Mod1 key. In circumstances like that, it won't be + * used to switch groups. + */ + break; + + default: + /* Find the Mode_Switch and Num_Lock modifiers. */ + for (j = 0; j < keymap_x11->keysyms_per_keycode; j++) { - if (syms[j] == GDK_Mode_switch) + if (syms[j] == GDK_KEY_Mode_switch) { /* This modifier swaps groups */ - - /* GDK_MOD1_MASK is 1 << 3 for example, i.e. the - * fourth modifier, i / keyspermod is the modifier - * index - */ - - keymap_x11->group_switch_mask |= (1 << ( i / keymap_x11->mod_keymap->max_keypermod)); - break; + keymap_x11->group_switch_mask |= mask; + } + else if (syms[j] == GDK_KEY_Num_Lock) + { + /* This modifier is used for Num_Lock */ + keymap_x11->num_lock_mask |= mask; } - - ++j; } + break; } - - ++i; } + + /* Hack: The Sun X server puts the keysym to use when the Num Lock + * modifier is on in the third element of the keysym array, instead + * of the second. + */ + if ((strcmp (ServerVendor (xdisplay), "Sun Microsystems, Inc.") == 0) && + (keymap_x11->keysyms_per_keycode > 2)) + keymap_x11->sun_keypad = TRUE; + else + keymap_x11->sun_keypad = FALSE; } } @@ -324,93 +537,336 @@ get_keymap (GdkKeymapX11 *keymap_x11) return keymap_x11->keymap; } +#define GET_EFFECTIVE_KEYMAP(keymap) get_effective_keymap ((keymap), G_STRFUNC) + +static GdkKeymap * +get_effective_keymap (GdkKeymap *keymap, + const char *function) +{ + if (!keymap) + { + GDK_NOTE (MULTIHEAD, + g_message ("reverting to default display keymap in %s", + function)); + return gdk_keymap_get_default (); + } + + return keymap; +} + #if HAVE_XKB static PangoDirection -get_direction (GdkKeymapX11 *keymap_x11) +get_direction (XkbDescRec *xkb, + gint group) { - XkbDescRec *xkb = get_xkb (keymap_x11); - const char *name; - XkbStateRec state_rec; - PangoDirection result; + gint code; - GdkDisplay *display = GDK_KEYMAP (keymap_x11)->display; + gint rtl_minus_ltr = 0; /* total number of RTL keysyms minus LTR ones */ - XkbGetState (GDK_DISPLAY_XDISPLAY (display), XkbUseCoreKbd, &state_rec); - - if (xkb->names->groups[state_rec.locked_group] == None) - result = PANGO_DIRECTION_LTR; + for (code = xkb->min_key_code; code <= xkb->max_key_code; code++) + { + gint level = 0; + 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 PangoDirection +get_direction_from_cache (GdkKeymapX11 *keymap_x11, + XkbDescPtr xkb, + gint group) +{ + Atom group_atom = xkb->names->groups[group]; + + gboolean cache_hit = FALSE; + DirectionCacheEntry *cache = keymap_x11->group_direction_cache; + + PangoDirection direction = PANGO_DIRECTION_NEUTRAL; + gint i; + + if (keymap_x11->have_direction) { - name = gdk_x11_get_xatom_name_for_display (display, xkb->names->groups[state_rec.locked_group]); + /* 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 + { + /* 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++; + } - if (g_strcasecmp (name, "arabic") == 0 || - g_strcasecmp (name, "hebrew") == 0 || - g_strcasecmp (name, "israelian") == 0) - result = PANGO_DIRECTION_RTL; - else - result = PANGO_DIRECTION_LTR; + /* 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++; } - - return result; + + return direction; +} + +static int +get_num_groups (GdkKeymap *keymap, + XkbDescPtr xkb) +{ + Display *display = KEYMAP_XDISPLAY (keymap); + XkbGetControls(display, XkbSlowKeysMask, xkb); + XkbGetUpdatedMap (display, XkbKeySymsMask | XkbKeyTypesMask | + XkbModifierMapMask | XkbVirtualModsMask, xkb); + return xkb->ctrls->num_groups; +} + +static gboolean +update_direction (GdkKeymapX11 *keymap_x11, + gint group) +{ + XkbDescPtr xkb = get_xkb (keymap_x11); + Atom group_atom; + gboolean had_direction; + PangoDirection old_direction; + + had_direction = keymap_x11->have_direction; + old_direction = keymap_x11->current_direction; + + group_atom = xkb->names->groups[group]; + + /* a group change? */ + if (!keymap_x11->have_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->have_direction = TRUE; + } + + return !had_direction || old_direction != keymap_x11->current_direction; } +static gboolean +update_lock_state (GdkKeymapX11 *keymap_x11, + gint locked_mods) +{ + gboolean caps_lock_state; + gboolean num_lock_state; + + caps_lock_state = keymap_x11->caps_lock_state; + num_lock_state = keymap_x11->num_lock_state; + + keymap_x11->caps_lock_state = (locked_mods & GDK_LOCK_MASK) != 0; + keymap_x11->num_lock_state = (locked_mods & keymap_x11->num_lock_mask) != 0; + + return (caps_lock_state != keymap_x11->caps_lock_state) + || (num_lock_state != keymap_x11->num_lock_state); +} + +/* keep this in sync with the XkbSelectEventDetails() call + * in gdk_display_open() + */ void -_gdk_keymap_state_changed (GdkDisplay *display) +_gdk_keymap_state_changed (GdkDisplay *display, + XEvent *xevent) { GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display); + XkbEvent *xkb_event = (XkbEvent *)xevent; if (display_x11->keymap) { 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 (G_OBJECT (keymap_x11), "direction_changed"); - } + if (update_direction (keymap_x11, XkbStateGroup (&xkb_event->state))) + g_signal_emit_by_name (keymap_x11, "direction-changed"); + + if (update_lock_state (keymap_x11, xkb_event->state.locked_mods)) + g_signal_emit_by_name (keymap_x11, "state-changed"); } } + #endif /* HAVE_XKB */ + +void +_gdk_keymap_keys_changed (GdkDisplay *display) +{ + GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display); + ++display_x11->keymap_serial; + + if (display_x11->keymap) + g_signal_emit_by_name (display_x11->keymap, "keys_changed", 0); +} + +/** + * gdk_keymap_get_direction: + * @keymap: a #GdkKeymap or %NULL to use the default keymap + * + * Returns the direction of effective layout of the keymap. + * + * Returns: %PANGO_DIRECTION_LTR or %PANGO_DIRECTION_RTL + * if it can determine the direction. %PANGO_DIRECTION_NEUTRAL + * otherwise. + **/ PangoDirection gdk_keymap_get_direction (GdkKeymap *keymap) { - if (!keymap) - { - keymap = gdk_keymap_get_for_display (gdk_get_default_display ()); - GDK_NOTE (MULTIHEAD, - g_message ("_multihead : reverting to default display keymap " - "in gdk_keymap_get_direction")); - } + keymap = GET_EFFECTIVE_KEYMAP (keymap); #if HAVE_XKB if (KEYMAP_USE_XKB (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; + GdkDisplay *display = GDK_KEYMAP (keymap_x11)->display; + XkbStateRec state_rec; + + XkbGetState (GDK_DISPLAY_XDISPLAY (display), XkbUseCoreKbd, + &state_rec); + update_direction (keymap_x11, XkbStateGroup (&state_rec)); } return keymap_x11->current_direction; } else #endif /* HAVE_XKB */ - return PANGO_DIRECTION_LTR; + return PANGO_DIRECTION_NEUTRAL; +} + +/** + * gdk_keymap_have_bidi_layouts: + * @keymap: a #GdkKeymap or %NULL to use the default keymap + * + * Determines if keyboard layouts for both right-to-left and left-to-right + * languages are in use. + * + * Returns: %TRUE if there are layouts in both directions, %FALSE otherwise + * + * Since: 2.12 + **/ +gboolean +gdk_keymap_have_bidi_layouts (GdkKeymap *keymap) +{ + keymap = GET_EFFECTIVE_KEYMAP (keymap); + +#if HAVE_XKB + if (KEYMAP_USE_XKB (keymap)) + { + GdkKeymapX11 *keymap_x11 = GDK_KEYMAP_X11 (keymap); + XkbDescPtr xkb = get_xkb (keymap_x11); + int num_groups = get_num_groups (keymap, xkb); + + int i; + gboolean have_ltr_keyboard = FALSE; + gboolean have_rtl_keyboard = FALSE; + + for (i = 0; i < num_groups; i++) + { + if (get_direction_from_cache (keymap_x11, xkb, i) == PANGO_DIRECTION_RTL) + have_rtl_keyboard = TRUE; + else + have_ltr_keyboard = TRUE; + } + + return have_ltr_keyboard && have_rtl_keyboard; + } + else +#endif /* HAVE_XKB */ + return FALSE; +} + +/** + * gdk_keymap_get_caps_lock_state: + * @keymap: a #GdkKeymap + * + * Returns whether the Caps Lock modifer is locked. + * + * Returns: %TRUE if Caps Lock is on + * + * Since: 2.16 + */ +gboolean +gdk_keymap_get_caps_lock_state (GdkKeymap *keymap) +{ + GdkKeymapX11 *keymap_x11; + + keymap = GET_EFFECTIVE_KEYMAP (keymap); + + keymap_x11 = GDK_KEYMAP_X11 (keymap); + + return keymap_x11->caps_lock_state; +} + +/** + * gdk_keymap_get_num_lock_state: + * @keymap: a #GdkKeymap + * + * Returns whether the Num Lock modifer is locked. + * + * Returns: %TRUE if Num Lock is on + * + * Since: 3.0 + */ +gboolean +gdk_keymap_get_num_lock_state (GdkKeymap *keymap) +{ + GdkKeymapX11 *keymap_x11; + + keymap = GET_EFFECTIVE_KEYMAP (keymap); + + keymap_x11 = GDK_KEYMAP_X11 (keymap); + + return keymap_x11->num_lock_state; } /** * gdk_keymap_get_entries_for_keyval: - * @keymap: a #GdkKeymap, or %NULL to use the default keymap + * @keymap: (allow-none): a #GdkKeymap, or %NULL to use the default keymap * @keyval: a keyval, such as %GDK_a, %GDK_Up, %GDK_Return, etc. - * @keys: return location for an array of #GdkKeymapKey - * @n_keys: return location for number of elements in returned array - * + * @keys: (out): return location for an array of #GdkKeymapKey + * @n_keys: (out): return location for number of elements in returned array + * * Obtains a list of keycode/group/level combinations that will * generate @keyval. Groups and levels are two kinds of keyboard mode; * in general, the level determines whether the top or bottom symbol @@ -439,14 +895,7 @@ gdk_keymap_get_entries_for_keyval (GdkKeymap *keymap, g_return_val_if_fail (n_keys != NULL, FALSE); g_return_val_if_fail (keyval != 0, FALSE); - if (!keymap) - { - keymap = gdk_keymap_get_for_display (gdk_get_default_display ()); - GDK_NOTE (MULTIHEAD, - g_message ("_multihead : reverting to default display keymap " - "in gdk_keymap_get_entries_for_keyval\n")); - } - + keymap = GET_EFFECTIVE_KEYMAP (keymap); keymap_x11 = GDK_KEYMAP_X11 (keymap); retval = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey)); @@ -517,7 +966,7 @@ gdk_keymap_get_entries_for_keyval (GdkKeymap *keymap, gint keycode; keycode = keymap_x11->min_keycode; - while (keycode < keymap_x11->max_keycode) + while (keycode <= keymap_x11->max_keycode) { const KeySym *syms = map + (keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode; gint i = 0; @@ -563,10 +1012,10 @@ gdk_keymap_get_entries_for_keyval (GdkKeymap *keymap, /** * gdk_keymap_get_entries_for_keycode: - * @keymap: a #GdkKeymap or %NULL to use the default keymap + * @keymap: (allow-none): a #GdkKeymap or %NULL to use the default keymap * @hardware_keycode: a keycode - * @keys: return location for array of #GdkKeymapKey, or NULL - * @keyvals: return location for array of keyvals, or NULL + * @keys: (out): return location for array of #GdkKeymapKey, or %NULL + * @keyvals: (out): return location for array of keyvals, or %NULL * @n_entries: length of @keys and @keyvals * * Returns the keyvals bound to @hardware_keycode. @@ -593,14 +1042,7 @@ gdk_keymap_get_entries_for_keycode (GdkKeymap *keymap, g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE); g_return_val_if_fail (n_entries != NULL, FALSE); - if (!keymap) - { - keymap = gdk_keymap_get_for_display (gdk_get_default_display ()); - GDK_NOTE (MULTIHEAD, - g_message ("_multihead : reverting to default display keymap " - "in gdk_keymap_get_entries_for_keycode\n")); - } - + keymap = GET_EFFECTIVE_KEYMAP (keymap); keymap_x11 = GDK_KEYMAP_X11 (keymap); update_keyrange (keymap_x11); @@ -709,37 +1151,21 @@ gdk_keymap_get_entries_for_keycode (GdkKeymap *keymap, ++i; } } - - if ((key_array && key_array->len > 0) || - (keyval_array && keyval_array->len > 0)) - { - if (keys) - *keys = (GdkKeymapKey*) key_array->data; - if (keyvals) - *keyvals = (guint*) keyval_array->data; + *n_entries = 0; - if (key_array) - *n_entries = key_array->len; - else - *n_entries = keyval_array->len; + if (keys) + { + *n_entries = key_array->len; + *keys = (GdkKeymapKey*) g_array_free (key_array, FALSE); } - else + + if (keyvals) { - if (keys) - *keys = NULL; - - if (keyvals) - *keyvals = NULL; - - *n_entries = 0; + *n_entries = keyval_array->len; + *keyvals = (guint*) g_array_free (keyval_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); - return *n_entries > 0; } @@ -766,15 +1192,8 @@ gdk_keymap_lookup_key (GdkKeymap *keymap, g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), 0); g_return_val_if_fail (key != NULL, 0); g_return_val_if_fail (key->group < 4, 0); - - if (!keymap) - { - keymap = gdk_keymap_get_for_display (gdk_get_default_display ()); - GDK_NOTE (MULTIHEAD, - g_message ("_multihead : reverting to default display keymap " - "in gdk_keymap_lookup_key\n")); - } + keymap = GET_EFFECTIVE_KEYMAP (keymap); keymap_x11 = GDK_KEYMAP_X11 (keymap); #ifdef HAVE_XKB @@ -789,14 +1208,17 @@ gdk_keymap_lookup_key (GdkKeymap *keymap, { const KeySym *map = get_keymap (keymap_x11); const KeySym *syms = map + (key->keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode; - return syms [KEYSYM_INDEX (keymap_x11, key->group, key->level)]; + return get_symbol (syms, keymap_x11, key->group, key->level); } } #ifdef HAVE_XKB -/* This is copied straight from XFree86 Xlib, because I needed to - * add the group and level return. It's unchanged for ease of - * diff against the Xlib sources; don't reformat it. +/* This is copied straight from XFree86 Xlib, to: + * - add the group and level return. + * - change the interpretation of mods_rtrn as described + * in the docs for gdk_keymap_translate_keyboard_state() + * It's unchanged for ease of diff against the Xlib sources; don't + * reformat it. */ static Bool MyEnhancedXkbTranslateKeyCode(register XkbDescPtr xkb, @@ -804,8 +1226,8 @@ MyEnhancedXkbTranslateKeyCode(register XkbDescPtr xkb, register unsigned int mods, unsigned int * mods_rtrn, KeySym * keysym_rtrn, - unsigned int * group_rtrn, - unsigned int * level_rtrn) + int * group_rtrn, + int * level_rtrn) { XkbKeyTypeRec *type; int col,nKeyGroups; @@ -850,29 +1272,48 @@ MyEnhancedXkbTranslateKeyCode(register XkbDescPtr xkb, if (type->map) { /* find the column (shift level) within the group */ register int i; register XkbKTMapEntryPtr entry; + /* ---- Begin section modified for GDK ---- */ + int found = 0; + for (i=0,entry=type->map;imap_count;i++,entry++) { - if ((entry->active)&&((mods&type->mods.mask)==entry->mods.mask)) { + if (mods_rtrn) { + int bits = 0; + unsigned long tmp = entry->mods.mask; + while (tmp) { + if ((tmp & 1) == 1) + bits++; + tmp >>= 1; + } + /* We always add one-modifiers levels to mods_rtrn since + * they can't wipe out bits in the state unless the + * level would be triggered. But return other modifiers + * + */ + if (bits == 1 || (mods&type->mods.mask)==entry->mods.mask) + *mods_rtrn |= entry->mods.mask; + } + + if (!found&&entry->active&&((mods&type->mods.mask)==entry->mods.mask)) { col+= entry->level; if (type->preserve) preserve= type->preserve[i].mask; - /* ---- Begin stuff GDK adds to the original Xlib version ---- */ - if (level_rtrn) *level_rtrn = entry->level; - /* ---- End stuff GDK adds to the original Xlib version ---- */ - - break; + found = 1; } } + /* ---- End section modified for GDK ---- */ } if (keysym_rtrn!=NULL) *keysym_rtrn= syms[col]; if (mods_rtrn) { - *mods_rtrn= type->mods.mask&(~preserve); - + /* ---- Begin section modified for GDK ---- */ + *mods_rtrn &= ~preserve; + /* ---- End section modified for GDK ---- */ + /* ---- Begin stuff GDK comments out of the original Xlib version ---- */ /* This is commented out because xkb_info is a private struct */ @@ -899,31 +1340,165 @@ MyEnhancedXkbTranslateKeyCode(register XkbDescPtr xkb, /* ---- End stuff GDK adds to the original Xlib version ---- */ - return (syms[col]!=NoSymbol); + return (syms[col] != NoSymbol); } #endif /* HAVE_XKB */ +/* Translates from keycode/state to keysymbol using the traditional interpretation + * of the keyboard map. See section 12.7 of the Xlib reference manual + */ +static guint +translate_keysym (GdkKeymapX11 *keymap_x11, + guint hardware_keycode, + gint group, + GdkModifierType state, + gint *effective_group, + gint *effective_level) +{ + const KeySym *map = get_keymap (keymap_x11); + const KeySym *syms = map + (hardware_keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode; + +#define SYM(k,g,l) get_symbol (syms, k,g,l) + + GdkModifierType shift_modifiers; + gint shift_level; + guint tmp_keyval; + gint num_lock_index; + + shift_modifiers = GDK_SHIFT_MASK; + if (keymap_x11->lock_keysym == GDK_KEY_Shift_Lock) + shift_modifiers |= GDK_LOCK_MASK; + + /* Fall back to the first group if the passed in group is empty + */ + if (!(SYM (keymap_x11, group, 0) || SYM (keymap_x11, group, 1)) && + (SYM (keymap_x11, 0, 0) || SYM (keymap_x11, 0, 1))) + group = 0; + + /* Hack: On Sun, the Num Lock modifier uses the third element in the + * keysym array, and Mode_Switch does not apply for a keypad key. + */ + if (keymap_x11->sun_keypad) + { + num_lock_index = 2; + + if (group != 0) + { + gint i; + + for (i = 0; i < keymap_x11->keysyms_per_keycode; i++) + if (KEYSYM_IS_KEYPAD (SYM (keymap_x11, 0, i))) + group = 0; + } + } + else + num_lock_index = 1; + + if ((state & keymap_x11->num_lock_mask) && + KEYSYM_IS_KEYPAD (SYM (keymap_x11, group, num_lock_index))) + { + /* Shift, Shift_Lock cancel Num_Lock + */ + shift_level = (state & shift_modifiers) ? 0 : num_lock_index; + if (!SYM (keymap_x11, group, shift_level) && SYM (keymap_x11, group, 0)) + shift_level = 0; + + tmp_keyval = SYM (keymap_x11, group, shift_level); + } + else + { + /* Fall back to the first level if no symbol for the level + * we were passed. + */ + shift_level = (state & shift_modifiers) ? 1 : 0; + if (!SYM (keymap_x11, group, shift_level) && SYM (keymap_x11, group, 0)) + shift_level = 0; + + tmp_keyval = SYM (keymap_x11, group, shift_level); + + if (keymap_x11->lock_keysym == GDK_KEY_Caps_Lock && (state & GDK_LOCK_MASK) != 0) + { + guint upper = gdk_keyval_to_upper (tmp_keyval); + if (upper != tmp_keyval) + tmp_keyval = upper; + } + } + + if (effective_group) + *effective_group = group; + + if (effective_level) + *effective_level = shift_level; + + return tmp_keyval; + +#undef SYM +} + /** * gdk_keymap_translate_keyboard_state: - * @keymap: a #GdkKeymap, or %NULL to use the default + * @keymap: (allow-none): a #GdkKeymap, or %NULL to use the default * @hardware_keycode: a keycode - * @state: a modifier state + * @state: a modifier state * @group: active keyboard group - * @keyval: return location for keyval - * @effective_group: return location for effective group - * @level: return location for level - * @consumed_modifiers: return location for modifiers that were used to determine the group or level - * + * @keyval: (out) (allow-none): return location for keyval, or %NULL + * @effective_group: (out) (allow-none): return location for effective group, or %NULL + * @level: (out) (allow-none): return location for level, or %NULL + * @consumed_modifiers: (out) (allow-none): return location for modifiers that were used to + * determine the group or level, or %NULL * * Translates the contents of a #GdkEventKey into a keyval, effective * group, and level. Modifiers that affected the translation and * are thus unavailable for application use are returned in - * @consumed_modifiers. See gdk_keyval_get_keys() for an explanation of + * @consumed_modifiers. See for an explanation of * groups and levels. The @effective_group is the group that was * actually used for the translation; some keys such as Enter are not * affected by the active keyboard group. The @level is derived from * @state. For convenience, #GdkEventKey already contains the translated * keyval, so this function isn't as useful as you might think. + * + * + * @consumed_modifiers gives modifiers that should be masked out + * from @state when comparing this key press to a hot key. For + * instance, on a US keyboard, the plus + * symbol is shifted, so when comparing a key press to a + * <Control>plus accelerator <Shift> should + * be masked out. + * + * + * /* We want to ignore irrelevant modifiers like ScrollLock */ + * #define ALL_ACCELS_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK) + * gdk_keymap_translate_keyboard_state (keymap, event->hardware_keycode, + * event->state, event->group, + * &keyval, NULL, NULL, &consumed); + * if (keyval == GDK_PLUS && + * (event->state & ~consumed & ALL_ACCELS_MASK) == GDK_CONTROL_MASK) + * /* Control was pressed */ + * + * + * An older interpretation @consumed_modifiers was that it contained + * all modifiers that might affect the translation of the key; + * this allowed accelerators to be stored with irrelevant consumed + * modifiers, by doing: + * + * /* XXX Don't do this XXX */ + * if (keyval == accel_keyval && + * (event->state & ~consumed & ALL_ACCELS_MASK) == (accel_mods & ~consumed)) + * /* Accelerator was pressed */ + * + * + * However, this did not work if multi-modifier combinations were + * used in the keymap, since, for instance, <Control> + * would be masked out even if only <Control><Alt> + * was used in the keymap. To support this usage as well as well as + * possible, all single modifier combinations + * that could affect the key for any combination of modifiers will + * be returned in @consumed_modifiers; multi-modifier combinations + * are returned only when actually found in @state. When you store + * accelerators, you should always store them with consumed modifiers + * removed. Store <Control>plus, + * not <Control><Shift>plus, + * * * Return value: %TRUE if there was a keyval bound to the keycode/state/group **/ @@ -943,7 +1518,8 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, g_return_val_if_fail (keymap == NULL || GDK_IS_KEYMAP (keymap), FALSE); g_return_val_if_fail (group < 4, FALSE); - + + keymap = GET_EFFECTIVE_KEYMAP (keymap); keymap_x11 = GDK_KEYMAP_X11 (keymap); if (keyval) @@ -960,7 +1536,7 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, if (hardware_keycode < keymap_x11->min_keycode || hardware_keycode > keymap_x11->max_keycode) return FALSE; - + #ifdef HAVE_XKB if (KEYMAP_USE_XKB (keymap)) { @@ -989,68 +1565,36 @@ gdk_keymap_translate_keyboard_state (GdkKeymap *keymap, else #endif { - const KeySym *map = get_keymap (keymap_x11); - const KeySym *syms; - gint shift_level; - gboolean ignore_shift = FALSE; - gboolean ignore_group = FALSE; + GdkModifierType bit; - if ((state & GDK_SHIFT_MASK) && - (state & GDK_LOCK_MASK)) - shift_level = 0; /* shift disables shift lock */ - else if ((state & GDK_SHIFT_MASK) || - (state & GDK_LOCK_MASK)) - shift_level = 1; - else - shift_level = 0; + tmp_modifiers = 0; - syms = map + (hardware_keycode - keymap_x11->min_keycode) * keymap_x11->keysyms_per_keycode; - -#define SYM(k,g,l) syms[KEYSYM_INDEX (k,g,l)] - - /* Drop group and shift if there are no keysymbols on - * the specified key. + /* We see what modifiers matter by trying the translation with + * and without each possible modifier */ - if (!SYM (keymap_x11, group, shift_level) && SYM (keymap_x11, group, 0)) + for (bit = GDK_SHIFT_MASK; bit < GDK_BUTTON1_MASK; bit <<= 1) { - shift_level = 0; - ignore_shift = TRUE; + /* Handling of the group here is a bit funky; a traditional + * X keyboard map can have more than two groups, but no way + * of accessing the extra groups is defined. We allow a + * caller to pass in any group to this function, but we + * only can represent switching between group 0 and 1 in + * consumed modifiers. + */ + if (translate_keysym (keymap_x11, hardware_keycode, + (bit == keymap_x11->group_switch_mask) ? 0 : group, + state & ~bit, + NULL, NULL) != + translate_keysym (keymap_x11, hardware_keycode, + (bit == keymap_x11->group_switch_mask) ? 1 : group, + state | bit, + NULL, NULL)) + tmp_modifiers |= bit; } - if (!SYM (keymap_x11, group, shift_level) && SYM (keymap_x11, 0, shift_level)) - { - group = 0; - ignore_group = TRUE; - } - if (!SYM (keymap_x11, group, shift_level) && SYM (keymap_x11, 0, 0)) - { - shift_level = 0; - group = 0; - ignore_group = TRUE; - ignore_shift = TRUE; - } - - /* See whether the group and shift level actually mattered - * to know what to put in consumed_modifiers - */ - if (!SYM (keymap_x11, group, 1) || - SYM (keymap_x11, group, 0) == SYM (keymap_x11, group, 1)) - ignore_shift = TRUE; - - if (!SYM (keymap_x11, 1, shift_level) || - SYM (keymap_x11, 0, shift_level) == SYM (keymap_x11, 1, shift_level)) - ignore_group = TRUE; - - tmp_keyval = SYM (keymap_x11, group, shift_level); - - tmp_modifiers = ignore_group ? 0 : keymap_x11->group_switch_mask; - tmp_modifiers |= ignore_shift ? 0 : (GDK_SHIFT_MASK | GDK_LOCK_MASK); - - if (effective_group) - *effective_group = group; - - if (level) - *level = shift_level; -#undef SYM + + tmp_keyval = translate_keysym (keymap_x11, hardware_keycode, + group, state, + level, effective_group); } if (consumed_modifiers) @@ -1070,13 +1614,13 @@ gdk_keyval_name (guint keyval) { switch (keyval) { - case GDK_Page_Up: + case GDK_KEY_Page_Up: return "Page_Up"; - case GDK_Page_Down: + case GDK_KEY_Page_Down: return "Page_Down"; - case GDK_KP_Page_Up: + case GDK_KEY_KP_Page_Up: return "KP_Page_Up"; - case GDK_KP_Page_Down: + case GDK_KEY_KP_Page_Down: return "KP_Page_Down"; } @@ -1139,3 +1683,167 @@ _gdk_x11_get_group_for_state (GdkDisplay *display, return (state & keymap_impl->group_switch_mask) ? 1 : 0; } } + +void +_gdk_keymap_add_virtual_modifiers_compat (GdkKeymap *keymap, + GdkModifierType *modifiers) +{ + GdkKeymapX11 *keymap_x11; + int i; + + keymap = GET_EFFECTIVE_KEYMAP (keymap); + keymap_x11 = GDK_KEYMAP_X11 (keymap); + + for (i = 3; i < 8; i++) + { + if ((1 << i) & *modifiers) + { + if (keymap_x11->modmap[i] & GDK_MOD1_MASK) + *modifiers |= GDK_MOD1_MASK; + else if (keymap_x11->modmap[i] & GDK_SUPER_MASK) + *modifiers |= GDK_SUPER_MASK; + else if (keymap_x11->modmap[i] & GDK_HYPER_MASK) + *modifiers |= GDK_HYPER_MASK; + else if (keymap_x11->modmap[i] & GDK_META_MASK) + *modifiers |= GDK_META_MASK; + } + } +} + +/** + * gdk_keymap_add_virtual_modifiers: + * @keymap: a #GdkKeymap + * @state: pointer to the modifier mask to change + * + * Adds virtual modifiers (i.e. Super, Hyper and Meta) which correspond + * to the real modifiers (i.e Mod2, Mod3, ...) in @modifiers. + * are set in @state to their non-virtual counterparts (i.e. Mod2, + * Mod3,...) and set the corresponding bits in @state. + * + * GDK already does this before delivering key events, but for + * compatibility reasons, it only sets the first virtual modifier + * it finds, whereas this function sets all matching virtual modifiers. + * + * This function is useful when matching key events against + * accelerators. + * + * Since: 2.20 + */ +void +gdk_keymap_add_virtual_modifiers (GdkKeymap *keymap, + GdkModifierType *state) +{ + GdkKeymapX11 *keymap_x11; + int i; + + keymap = GET_EFFECTIVE_KEYMAP (keymap); + keymap_x11 = GDK_KEYMAP_X11 (keymap); + + for (i = 3; i < 8; i++) + { + if ((1 << i) & *state) + { + if (keymap_x11->modmap[i] & GDK_MOD1_MASK) + *state |= GDK_MOD1_MASK; + if (keymap_x11->modmap[i] & GDK_SUPER_MASK) + *state |= GDK_SUPER_MASK; + if (keymap_x11->modmap[i] & GDK_HYPER_MASK) + *state |= GDK_HYPER_MASK; + if (keymap_x11->modmap[i] & GDK_META_MASK) + *state |= GDK_META_MASK; + } + } +} + +gboolean +_gdk_keymap_key_is_modifier (GdkKeymap *keymap, + guint keycode) +{ + GdkKeymapX11 *keymap_x11; + gint i; + + keymap = GET_EFFECTIVE_KEYMAP (keymap); + keymap_x11 = GDK_KEYMAP_X11 (keymap); + + if (keycode < keymap_x11->min_keycode || + keycode > keymap_x11->max_keycode) + return FALSE; + +#ifdef HAVE_XKB + if (KEYMAP_USE_XKB (keymap)) + { + XkbDescRec *xkb = get_xkb (keymap_x11); + + if (xkb->map->modmap && xkb->map->modmap[keycode] != 0) + return TRUE; + } + else +#endif + { + for (i = 0; i < 8 * keymap_x11->mod_keymap->max_keypermod; i++) + { + if (keycode == keymap_x11->mod_keymap->modifiermap[i]) + return TRUE; + } + } + + return FALSE; +} + +/** + * gdk_keymap_map_virtual_modifiers: + * @keymap: a #GdkKeymap + * @state: pointer to the modifier state to map + * + * Maps the virtual modifiers (i.e. Super, Hyper and Meta) which + * are set in @state to their non-virtual counterparts (i.e. Mod2, + * Mod3,...) and set the corresponding bits in @state. + * + * This function is useful when matching key events against + * accelerators. + * + * Returns: %TRUE if no virtual modifiers were mapped to the + * same non-virtual modifier. Note that %FALSE is also returned + * if a virtual modifier is mapped to a non-virtual modifier that + * was already set in @state. + * + * Since: 2.20 + */ +gboolean +gdk_keymap_map_virtual_modifiers (GdkKeymap *keymap, + GdkModifierType *state) +{ + GdkKeymapX11 *keymap_x11; + const guint vmods[] = { + GDK_SUPER_MASK, GDK_HYPER_MASK, GDK_META_MASK + }; + int i, j; + gboolean retval; + + keymap = GET_EFFECTIVE_KEYMAP (keymap); + keymap_x11 = GDK_KEYMAP_X11 (keymap); + + if (KEYMAP_USE_XKB (keymap)) + get_xkb (keymap_x11); + + retval = TRUE; + + for (j = 0; j < 3; j++) + { + if (*state & vmods[j]) + { + for (i = 3; i < 8; i++) + { + if (keymap_x11->modmap[i] & vmods[j]) + { + if (*state & (1 << i)) + retval = FALSE; + else + *state |= 1 << i; + } + } + } + } + + return retval; +}