gboolean match_xi2_key (Key *key, XIDeviceEvent *event) { guint keyval; GdkModifierType consumed; gint group; guint keycode, state; if (key == NULL) return FALSE; setup_modifiers (); state = device_xi2_translate_state (&event->mods, &event->group); if (have_xkb (event->display)) group = XkbGroupForCoreState (state); else group = (state & GDK_KEY_Mode_switch) ? 1 : 0; keycode = event->detail; /* Check if we find a keysym that matches our current state */ if (gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (), keycode, state, group, &keyval, NULL, NULL, &consumed)) { guint lower, upper; guint mask; /* The Key structure contains virtual modifiers, whereas * the XEvent will be using the real modifier, so translate those */ mask = key->state; gdk_keymap_map_virtual_modifiers (gdk_keymap_get_default (), &mask); gdk_keyval_convert_case (keyval, &lower, &upper); /* If we are checking against the lower version of the * keysym, we might need the Shift state for matching, * so remove it from the consumed modifiers */ if (lower == key->keysym) consumed &= ~GDK_SHIFT_MASK; return ((lower == key->keysym || upper == key->keysym) && (state & ~consumed & gsd_used_mods) == mask); } /* The key we passed doesn't have a keysym, so try with just the keycode */ return (key != NULL && key->state == (state & gsd_used_mods) && key_uses_keycode (key, keycode)); }
static gboolean do_ungrab_key (struct Binding *binding) { GdkKeymap *keymap = gdk_keymap_get_default (); GdkWindow *rootwin = gdk_get_default_root_window (); GdkModifierType modifiers; if (keymap == NULL || rootwin == NULL) return FALSE; TRACE (g_print ("Ungrabbing keyval: %d, vmodifiers: 0x%x, name: %s\n", binding->keyval, binding->modifiers, binding->keystring)); /* Map virtual modifiers to non-virtual modifiers */ modifiers = binding->modifiers; gdk_keymap_map_virtual_modifiers(keymap, &modifiers); grab_ungrab (rootwin, binding->keyval, modifiers, FALSE /* ungrab */); return TRUE; }
static gboolean do_grab_key (struct Binding *binding) { gboolean success; GdkWindow *rootwin = gdk_get_default_root_window (); GdkKeymap *keymap = gdk_keymap_get_default (); GdkModifierType modifiers; guint keysym = 0; if (keymap == NULL || rootwin == NULL) return FALSE; gtk_accelerator_parse(binding->keystring, &keysym, &modifiers); if (keysym == 0) return FALSE; binding->keyval = keysym; binding->modifiers = modifiers; TRACE (g_print ("Grabbing keyval: %d, vmodifiers: 0x%x, name: %s\n", keysym, modifiers, binding->keystring)); /* Map virtual modifiers to non-virtual modifiers */ gdk_keymap_map_virtual_modifiers(keymap, &modifiers); if (modifiers == binding->modifiers && (GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK) & modifiers) { g_warning ("Failed to map virtual modifiers"); return FALSE; } success = grab_ungrab (rootwin, keysym, modifiers, TRUE /* grab */); /* if (!success) { g_warning ("Binding '%s' failed!", binding->keystring); } */ return success; }
void grab_key_unsafe (Key *key, gboolean grab, GSList *screens) { int indexes[N_BITS]; /* indexes of bits we need to flip */ int i; int bit; int bits_set_cnt; int uppervalue; guint mask, modifiers; GArray *all_mods; GSList *l; setup_modifiers (); mask = gsd_ignored_mods & ~key->state & GDK_MODIFIER_MASK; /* XGrabKey requires real modifiers, not virtual ones */ modifiers = key->state; gdk_keymap_map_virtual_modifiers (gdk_keymap_get_default (), &modifiers); /* If key doesn't have a usable modifier, we don't want * to grab it, since the user might lose a useful key. * * The exception is the XFree86 keys and the Function keys * (which are useful to grab without a modifier). */ if ((modifiers & gsd_used_mods) == 0 && !IN_RANGE(key->keysym, XF86KEYS_RANGE_MIN, XF86KEYS_RANGE_MAX) && !IN_RANGE(key->keysym, FKEYS_RANGE_MIN, FKEYS_RANGE_MAX) && key->keysym != GDK_KEY_Pause && key->keysym != GDK_KEY_Print) { GString *keycodes; keycodes = g_string_new (""); if (key->keycodes != NULL) { guint *c; for (c = key->keycodes; *c; ++c) { g_string_printf (keycodes, " %u", *c); } } g_warning ("Key 0x%x (keycodes: %s) with state 0x%x (resolved to 0x%x) " " has no usable modifiers (usable modifiers are 0x%x)", key->keysym, keycodes->str, key->state, modifiers, gsd_used_mods); g_string_free (keycodes, TRUE); return; } bit = 0; /* store the indexes of all set bits in mask in the array */ for (i = 0; mask; ++i, mask >>= 1) { if (mask & 0x1) { indexes[bit++] = i; } } bits_set_cnt = bit; all_mods = g_array_new (FALSE, TRUE, sizeof(XIGrabModifiers)); uppervalue = 1 << bits_set_cnt; /* store all possible modifier combinations for our mask into all_mods */ for (i = 0; i < uppervalue; ++i) { int j; int result = 0; XIGrabModifiers *mod; /* map bits in the counter to those in the mask */ for (j = 0; j < bits_set_cnt; ++j) { if (i & (1 << j)) { result |= (1 << indexes[j]); } } /* Grow the array by one, to fit our new XIGrabModifiers item */ g_array_set_size (all_mods, all_mods->len + 1); mod = &g_array_index (all_mods, XIGrabModifiers, all_mods->len - 1); mod->modifiers = result | modifiers; } /* Capture the actual keycodes with the modifier array */ for (l = screens; l; l = l->next) { GdkScreen *screen = l->data; guint *code; for (code = key->keycodes; *code; ++code) { grab_key_real (*code, gdk_screen_get_root_window (screen), grab, (XIGrabModifiers *) all_mods->data, all_mods->len); } } g_array_free (all_mods, TRUE); }
/** * _gtk_key_hash_lookup: * @key_hash: a #GtkKeyHash * @hardware_keycode: hardware keycode field from a #GdkEventKey * @state: state field from a #GdkEventKey * @mask: mask of modifiers to consider when matching against the * modifiers in entries. * @group: group field from a #GdkEventKey * * Looks up the best matching entry or entries in the hash table for * a given event. The results are sorted so that entries with less * modifiers come before entries with more modifiers. * * The matches returned by this function can be exact (i.e. keycode, level * and group all match) or fuzzy (i.e. keycode and level match, but group * does not). As long there are any exact matches, only exact matches * are returned. If there are no exact matches, fuzzy matches will be * returned, as long as they are not shadowing a possible exact match. * This means that fuzzy matches won’t be considered if their keyval is * present in the current group. * * Return value: A newly-allocated #GSList of matching entries. * Free with g_slist_free() when no longer needed. */ GSList * _gtk_key_hash_lookup (GtkKeyHash *key_hash, guint16 hardware_keycode, GdkModifierType state, GdkModifierType mask, gint group) { GHashTable *keycode_hash = key_hash_get_keycode_hash (key_hash); GSList *keys = g_hash_table_lookup (keycode_hash, GUINT_TO_POINTER ((guint)hardware_keycode)); GSList *results = NULL; GSList *l; gboolean have_exact = FALSE; guint keyval; gint effective_group; gint level; GdkModifierType modifiers; GdkModifierType consumed_modifiers; GdkModifierType shift_group_mask; gboolean group_mod_is_accel_mod = FALSE; const GdkModifierType xmods = GDK_MOD2_MASK|GDK_MOD3_MASK|GDK_MOD4_MASK|GDK_MOD5_MASK; const GdkModifierType vmods = GDK_SUPER_MASK|GDK_HYPER_MASK|GDK_META_MASK; /* We don't want Caps_Lock to affect keybinding lookups. */ state &= ~GDK_LOCK_MASK; _gtk_translate_keyboard_accel_state (key_hash->keymap, hardware_keycode, state, mask, group, &keyval, &effective_group, &level, &consumed_modifiers); /* if the group-toggling modifier is part of the default accel mod * mask, and it is active, disable it for matching */ shift_group_mask = gdk_keymap_get_modifier_mask (key_hash->keymap, GDK_MODIFIER_INTENT_SHIFT_GROUP); if (mask & shift_group_mask) group_mod_is_accel_mod = TRUE; gdk_keymap_map_virtual_modifiers (key_hash->keymap, &mask); gdk_keymap_add_virtual_modifiers (key_hash->keymap, &state); GTK_NOTE (KEYBINDINGS, g_message ("Looking up keycode = %u, modifiers = 0x%04x,\n" " keyval = %u, group = %d, level = %d, consumed_modifiers = 0x%04x", hardware_keycode, state, keyval, effective_group, level, consumed_modifiers)); if (keys) { GSList *tmp_list = keys; while (tmp_list) { GtkKeyHashEntry *entry = tmp_list->data; /* If the virtual Super, Hyper or Meta modifiers are present, * they will also be mapped to some of the Mod2 - Mod5 modifiers, * so we compare them twice, ignoring either set. * We accept combinations involving virtual modifiers only if they * are mapped to separate modifiers; i.e. if Super and Hyper are * both mapped to Mod4, then pressing a key that is mapped to Mod4 * will not match a Super+Hyper entry. */ modifiers = entry->modifiers; if (gdk_keymap_map_virtual_modifiers (key_hash->keymap, &modifiers) && ((modifiers & ~consumed_modifiers & mask & ~vmods) == (state & ~consumed_modifiers & mask & ~vmods) || (modifiers & ~consumed_modifiers & mask & ~xmods) == (state & ~consumed_modifiers & mask & ~xmods))) { gint i; if (keyval == entry->keyval && /* Exact match */ /* but also match for group if it is an accel mod, because * otherwise we can get multiple exact matches, some being * bogus */ (!group_mod_is_accel_mod || (state & shift_group_mask) == (entry->modifiers & shift_group_mask))) { GTK_NOTE (KEYBINDINGS, g_message (" found exact match, keyval = %u, modifiers = 0x%04x", entry->keyval, entry->modifiers)); if (!have_exact) { g_slist_free (results); results = NULL; } have_exact = TRUE; results = g_slist_prepend (results, entry); } if (!have_exact) { for (i = 0; i < entry->n_keys; i++) { if (entry->keys[i].keycode == hardware_keycode && entry->keys[i].level == level && /* Only match for group if it's an accel mod */ (!group_mod_is_accel_mod || entry->keys[i].group == effective_group)) { GTK_NOTE (KEYBINDINGS, g_message (" found group = %d, level = %d", entry->keys[i].group, entry->keys[i].level)); results = g_slist_prepend (results, entry); break; } } } } tmp_list = tmp_list->next; } } if (!have_exact && results) { /* If there are fuzzy matches, check that the current group doesn't also * define these keyvals; if yes, discard results because a widget up in * the stack may have an exact match and we don't want to 'steal' it. */ guint oldkeyval = 0; GtkKeyHashEntry *keyhashentry; results = sort_lookup_results_by_keyval (results); for (l = results; l; l = l->next) { keyhashentry = l->data; if (l == results || oldkeyval != keyhashentry->keyval) { oldkeyval = keyhashentry->keyval; if (keyval_in_group (key_hash->keymap, oldkeyval, group)) { g_slist_free (results); return NULL; } } } } results = sort_lookup_results (results); for (l = results; l; l = l->next) l->data = ((GtkKeyHashEntry *)l->data)->value; return results; }