diff --git a/src/core/keybindings-private.h b/src/core/keybindings-private.h index 5a17c991f..a91e47e02 100644 --- a/src/core/keybindings-private.h +++ b/src/core/keybindings-private.h @@ -44,7 +44,8 @@ struct _MetaKeyHandler }; typedef struct _MetaResolvedKeyCombo { - xkb_keycode_t keycode; + xkb_keycode_t *keycodes; + int len; xkb_mod_mask_t mask; } MetaResolvedKeyCombo; @@ -102,7 +103,7 @@ typedef struct MetaKeyCombo overlay_key_combo; MetaResolvedKeyCombo overlay_resolved_key_combo; gboolean overlay_key_only_pressed; - MetaResolvedKeyCombo *iso_next_group_combos; + MetaResolvedKeyCombo iso_next_group_combo[2]; int n_iso_next_group_combos; xkb_level_index_t keymap_num_levels; diff --git a/src/core/keybindings.c b/src/core/keybindings.c index 19b48ee98..f2aafdd4e 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -64,16 +64,63 @@ static gboolean add_builtin_keybinding (MetaDisplay *display, MetaKeyHandlerFunc handler, int handler_arg); +static void +resolved_key_combo_reset (MetaResolvedKeyCombo *resolved_combo) +{ + g_free (resolved_combo->keycodes); + resolved_combo->len = 0; + resolved_combo->keycodes = NULL; +} + +static void +resolved_key_combo_copy (MetaResolvedKeyCombo *from, + MetaResolvedKeyCombo *to) +{ + to->len = from->len; + to->keycodes = g_memdup (from->keycodes, + from->len * sizeof (xkb_keycode_t)); +} + +static gboolean +resolved_key_combo_has_keycode (MetaResolvedKeyCombo *resolved_combo, + int keycode) +{ + int i; + + for (i = 0; i < resolved_combo->len; i++) + if ((int) resolved_combo->keycodes[i] == keycode) + return TRUE; + + return FALSE; +} + +static gboolean +resolved_key_combo_intersect (MetaResolvedKeyCombo *a, + MetaResolvedKeyCombo *b) +{ + int i; + + for (i = 0; i < a->len; i++) + if (resolved_key_combo_has_keycode (b, a->keycodes[i])) + return TRUE; + + return FALSE; +} + static void meta_key_binding_free (MetaKeyBinding *binding) { + resolved_key_combo_reset (&binding->resolved_combo); g_slice_free (MetaKeyBinding, binding); } static MetaKeyBinding * meta_key_binding_copy (MetaKeyBinding *binding) { - return g_slice_dup (MetaKeyBinding, binding); + MetaKeyBinding *clone = g_slice_dup (MetaKeyBinding, binding); + resolved_key_combo_copy (&binding->resolved_combo, + &clone->resolved_combo); + return clone; } G_DEFINE_BOXED_TYPE(MetaKeyBinding, @@ -163,7 +210,8 @@ meta_key_grab_free (MetaKeyGrab *grab) } static guint32 -key_combo_key (MetaResolvedKeyCombo *resolved_combo) +key_combo_key (MetaResolvedKeyCombo *resolved_combo, + int i) { /* On X, keycodes are only 8 bits while libxkbcommon supports 32 bit keycodes, but since we're using the same XKB keymaps that X uses, @@ -173,7 +221,7 @@ key_combo_key (MetaResolvedKeyCombo *resolved_combo) can use a 32 bit integer to safely concatenate both keycode and mask and thus making it easy to use them as an index in a GHashTable. */ - guint32 key = resolved_combo->keycode & 0xffff; + guint32 key = resolved_combo->keycodes[i] & 0xffff; return (key << 16) | (resolved_combo->mask & 0xffff); } @@ -274,21 +322,34 @@ get_keycodes_for_keysym_iter (struct xkb_keymap *keymap, xkb_level_index_t level = search_data->level; if (is_keycode_for_keysym (keymap, layout, level, keycode, keysym)) - g_array_append_val (keycodes, keycode); + { + guint i; + gboolean missing = TRUE; + + /* duplicate keycode detection */ + for (i = 0; i < keycodes->len; i++) + if (g_array_index (keycodes, xkb_keysym_t, i) == keycode) + { + missing = FALSE; + break; + } + + if (missing) + g_array_append_val (keycodes, keycode); + } } /* Original code from gdk_x11_keymap_get_entries_for_keyval() in * gdkkeys-x11.c */ -static int +static void get_keycodes_for_keysym (MetaKeyBindingManager *keys, int keysym, - int **keycodes) + MetaResolvedKeyCombo *resolved_combo) { GArray *retval; - int n_keycodes; int keycode; - retval = g_array_new (FALSE, FALSE, sizeof (int)); + retval = g_array_new (FALSE, FALSE, sizeof (xkb_keysym_t)); /* Special-case: Fake mutter keysym */ if (keysym == META_KEY_ABOVE_TAB) @@ -313,28 +374,8 @@ get_keycodes_for_keysym (MetaKeyBindingManager *keys, } out: - n_keycodes = retval->len; - *keycodes = (int*) g_array_free (retval, n_keycodes == 0 ? TRUE : FALSE); - return n_keycodes; -} - -static guint -get_first_keycode_for_keysym (MetaKeyBindingManager *keys, - guint keysym) -{ - int *keycodes; - int n_keycodes; - int keycode; - - n_keycodes = get_keycodes_for_keysym (keys, keysym, &keycodes); - - if (n_keycodes > 0) - keycode = keycodes[0]; - else - keycode = 0; - - g_free (keycodes); - return keycode; + resolved_combo->len = retval->len; + resolved_combo->keycodes = (xkb_keycode_t *) g_array_free (retval, retval->len == 0 ? TRUE : FALSE); } static void @@ -367,20 +408,23 @@ static void reload_iso_next_group_combos (MetaKeyBindingManager *keys) { const char *iso_next_group_option; - MetaResolvedKeyCombo *combos; - int *keycodes; - int n_keycodes; - int n_combos; int i; - g_clear_pointer (&keys->iso_next_group_combos, g_free); + for (i = 0; i < keys->n_iso_next_group_combos; i++) + resolved_key_combo_reset (&keys->iso_next_group_combo[i]); + keys->n_iso_next_group_combos = 0; iso_next_group_option = meta_prefs_get_iso_next_group_option (); if (iso_next_group_option == NULL) return; - n_keycodes = get_keycodes_for_keysym (keys, XKB_KEY_ISO_Next_Group, &keycodes); + get_keycodes_for_keysym (keys, XKB_KEY_ISO_Next_Group, keys->iso_next_group_combo); + + if (keys->iso_next_group_combo[0].len == 0) + return; + + keys->n_iso_next_group_combos = 1; if (g_str_equal (iso_next_group_option, "toggle") || g_str_equal (iso_next_group_option, "lalt_toggle") || @@ -394,94 +438,53 @@ reload_iso_next_group_combos (MetaKeyBindingManager *keys) g_str_equal (iso_next_group_option, "menu_toggle") || g_str_equal (iso_next_group_option, "caps_toggle")) { - n_combos = n_keycodes; - combos = g_new (MetaResolvedKeyCombo, n_combos); - - for (i = 0; i < n_keycodes; ++i) - { - combos[i].keycode = keycodes[i]; - combos[i].mask = 0; - } + keys->iso_next_group_combo[0].mask = 0; } else if (g_str_equal (iso_next_group_option, "shift_caps_toggle") || g_str_equal (iso_next_group_option, "shifts_toggle")) { - n_combos = n_keycodes; - combos = g_new (MetaResolvedKeyCombo, n_combos); - - for (i = 0; i < n_keycodes; ++i) - { - combos[i].keycode = keycodes[i]; - combos[i].mask = ShiftMask; - } + keys->iso_next_group_combo[0].mask = ShiftMask; } else if (g_str_equal (iso_next_group_option, "alt_caps_toggle") || g_str_equal (iso_next_group_option, "alt_space_toggle")) { - n_combos = n_keycodes; - combos = g_new (MetaResolvedKeyCombo, n_combos); - - for (i = 0; i < n_keycodes; ++i) - { - combos[i].keycode = keycodes[i]; - combos[i].mask = Mod1Mask; - } + keys->iso_next_group_combo[0].mask = Mod1Mask; } else if (g_str_equal (iso_next_group_option, "ctrl_shift_toggle") || g_str_equal (iso_next_group_option, "lctrl_lshift_toggle") || g_str_equal (iso_next_group_option, "rctrl_rshift_toggle")) { - n_combos = n_keycodes * 2; - combos = g_new (MetaResolvedKeyCombo, n_combos); + resolved_key_combo_copy (&keys->iso_next_group_combo[0], + &keys->iso_next_group_combo[1]); - for (i = 0; i < n_keycodes; ++i) - { - combos[i].keycode = keycodes[i]; - combos[i].mask = ShiftMask; - - combos[i + n_keycodes].keycode = keycodes[i]; - combos[i + n_keycodes].mask = ControlMask; - } + keys->iso_next_group_combo[0].mask = ShiftMask; + keys->iso_next_group_combo[1].mask = ControlMask; + keys->n_iso_next_group_combos = 2; } else if (g_str_equal (iso_next_group_option, "ctrl_alt_toggle")) { - n_combos = n_keycodes * 2; - combos = g_new (MetaResolvedKeyCombo, n_combos); + resolved_key_combo_copy (&keys->iso_next_group_combo[0], + &keys->iso_next_group_combo[1]); - for (i = 0; i < n_keycodes; ++i) - { - combos[i].keycode = keycodes[i]; - combos[i].mask = Mod1Mask; - - combos[i + n_keycodes].keycode = keycodes[i]; - combos[i + n_keycodes].mask = ControlMask; - } + keys->iso_next_group_combo[0].mask = Mod1Mask; + keys->iso_next_group_combo[1].mask = ControlMask; + keys->n_iso_next_group_combos = 2; } else if (g_str_equal (iso_next_group_option, "alt_shift_toggle") || g_str_equal (iso_next_group_option, "lalt_lshift_toggle")) { - n_combos = n_keycodes * 2; - combos = g_new (MetaResolvedKeyCombo, n_combos); + resolved_key_combo_copy (&keys->iso_next_group_combo[0], + &keys->iso_next_group_combo[1]); - for (i = 0; i < n_keycodes; ++i) - { - combos[i].keycode = keycodes[i]; - combos[i].mask = Mod1Mask; - - combos[i + n_keycodes].keycode = keycodes[i]; - combos[i + n_keycodes].mask = ShiftMask; - } + keys->iso_next_group_combo[0].mask = Mod1Mask; + keys->iso_next_group_combo[1].mask = ShiftMask; + keys->n_iso_next_group_combos = 2; } else { - n_combos = 0; - combos = NULL; + resolved_key_combo_reset (keys->iso_next_group_combo); + keys->n_iso_next_group_combos = 0; } - - g_free (keycodes); - - keys->n_iso_next_group_combos = n_combos; - keys->iso_next_group_combos = combos; } static void @@ -517,11 +520,35 @@ static void index_binding (MetaKeyBindingManager *keys, MetaKeyBinding *binding) { - guint32 index_key; + int i; - index_key = key_combo_key (&binding->resolved_combo); - g_hash_table_replace (keys->key_bindings_index, - GINT_TO_POINTER (index_key), binding); + for (i = 0; i < binding->resolved_combo.len; i++) + { + MetaKeyBinding *existing; + guint32 index_key; + + index_key = key_combo_key (&binding->resolved_combo, i); + + existing = g_hash_table_lookup (keys->key_bindings_index, + GINT_TO_POINTER (index_key)); + if (existing != NULL) + { + /* Overwrite already indexed keycodes only for the first + * keycode, i.e. we give those primary keycodes precedence + * over non-first ones. */ + if (i > 0) + continue; + + meta_warning ("Overwriting existing binding of keysym %x" + " with keysym %x (keycode %x).\n", + binding->combo.keysym, + existing->combo.keysym, + binding->resolved_combo.keycodes[i]); + } + + g_hash_table_replace (keys->key_bindings_index, + GINT_TO_POINTER (index_key), binding); + } } static void @@ -529,10 +556,19 @@ resolve_key_combo (MetaKeyBindingManager *keys, MetaKeyCombo *combo, MetaResolvedKeyCombo *resolved_combo) { + + resolved_key_combo_reset (resolved_combo); + if (combo->keysym != 0) - resolved_combo->keycode = get_first_keycode_for_keysym (keys, combo->keysym); - else - resolved_combo->keycode = combo->keycode; + { + get_keycodes_for_keysym (keys, combo->keysym, resolved_combo); + } + else if (combo->keycode != 0) + { + resolved_combo->keycodes = g_new0 (xkb_keycode_t, 1); + resolved_combo->keycodes[0] = combo->keycode; + resolved_combo->len = 1; + } devirtualize_modifiers (keys, combo->modifiers, &resolved_combo->mask); } @@ -691,9 +727,22 @@ static MetaKeyBinding * get_keybinding (MetaKeyBindingManager *keys, MetaResolvedKeyCombo *resolved_combo) { - guint32 key; - key = key_combo_key (resolved_combo); - return g_hash_table_lookup (keys->key_bindings_index, GINT_TO_POINTER (key)); + MetaKeyBinding *binding = NULL; + int i; + + for (i = 0; i < resolved_combo->len; i++) + { + guint32 key; + + key = key_combo_key (resolved_combo, i); + binding = g_hash_table_lookup (keys->key_bindings_index, + GINT_TO_POINTER (key)); + + if (binding != NULL) + break; + } + + return binding; } static guint @@ -823,7 +872,8 @@ get_keybinding_action (MetaKeyBindingManager *keys, * of mutter keybindings while holding a grab, the overlay-key-only-pressed * tracking is left to the plugin here. */ - if (resolved_combo->keycode == (unsigned int)keys->overlay_resolved_key_combo.keycode) + if (resolved_key_combo_intersect (resolved_combo, + &keys->overlay_resolved_key_combo)) return META_KEYBINDING_ACTION_OVERLAY_KEY; binding = get_keybinding (keys, resolved_combo); @@ -841,14 +891,11 @@ get_keybinding_action (MetaKeyBindingManager *keys, } } -static void -resolved_combo_from_event_params (MetaResolvedKeyCombo *resolved_combo, - MetaKeyBindingManager *keys, - unsigned int keycode, - unsigned long mask) +static xkb_mod_mask_t +mask_from_event_params (MetaKeyBindingManager *keys, + unsigned long mask) { - resolved_combo->keycode = keycode; - resolved_combo->mask = mask & 0xff & ~keys->ignored_modifier_mask; + return mask & 0xff & ~keys->ignored_modifier_mask; } /** @@ -871,8 +918,10 @@ meta_display_get_keybinding_action (MetaDisplay *display, unsigned long mask) { MetaKeyBindingManager *keys = &display->key_binding_manager; - MetaResolvedKeyCombo resolved_combo; - resolved_combo_from_event_params (&resolved_combo, keys, keycode, mask); + xkb_keycode_t code = (xkb_keycode_t) keycode; + MetaResolvedKeyCombo resolved_combo = { &code, 1 }; + + resolved_combo.mask = mask_from_event_params (keys, mask); return get_keybinding_action (keys, &resolved_combo); } @@ -1165,30 +1214,36 @@ meta_change_keygrab (MetaKeyBindingManager *keys, MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); Display *xdisplay = meta_backend_x11_get_xdisplay (backend); GArray *mods; + int i; /* Grab keycode/modmask, together with * all combinations of ignored modifiers. * X provides no better way to do this. */ - meta_topic (META_DEBUG_KEYBINDINGS, - "%s keybinding keycode %d mask 0x%x on 0x%lx\n", - grab ? "Grabbing" : "Ungrabbing", - resolved_combo->keycode, resolved_combo->mask, xwindow); - mods = calc_grab_modifiers (keys, resolved_combo->mask); - if (grab) - XIGrabKeycode (xdisplay, - META_VIRTUAL_CORE_KEYBOARD_ID, - resolved_combo->keycode, xwindow, - XIGrabModeSync, XIGrabModeAsync, - False, &mask, mods->len, (XIGrabModifiers *)mods->data); - else - XIUngrabKeycode (xdisplay, - META_VIRTUAL_CORE_KEYBOARD_ID, - resolved_combo->keycode, xwindow, - mods->len, (XIGrabModifiers *)mods->data); + for (i = 0; i < resolved_combo->len; i++) + { + xkb_keycode_t keycode = resolved_combo->keycodes[i]; + + meta_topic (META_DEBUG_KEYBINDINGS, + "%s keybinding keycode %d mask 0x%x on 0x%lx\n", + grab ? "Grabbing" : "Ungrabbing", + keycode, resolved_combo->mask, xwindow); + + if (grab) + XIGrabKeycode (xdisplay, + META_VIRTUAL_CORE_KEYBOARD_ID, + keycode, xwindow, + XIGrabModeSync, XIGrabModeAsync, + False, &mask, mods->len, (XIGrabModifiers *)mods->data); + else + XIUngrabKeycode (xdisplay, + META_VIRTUAL_CORE_KEYBOARD_ID, + keycode, xwindow, + mods->len, (XIGrabModifiers *)mods->data); + } g_array_free (mods, TRUE); } @@ -1213,7 +1268,7 @@ change_keygrab_foreach (gpointer key, if (data->only_per_window != binding_is_per_window) return; - if (binding->resolved_combo.keycode == 0) + if (binding->resolved_combo.len == 0) return; meta_change_keygrab (data->keys, data->xwindow, data->grab, &binding->resolved_combo); @@ -1241,21 +1296,13 @@ meta_screen_change_keygrabs (MetaScreen *screen, { MetaDisplay *display = screen->display; MetaKeyBindingManager *keys = &display->key_binding_manager; + int i; - if (keys->overlay_resolved_key_combo.keycode != 0) + if (keys->overlay_resolved_key_combo.len != 0) meta_change_keygrab (keys, screen->xroot, grab, &keys->overlay_resolved_key_combo); - if (keys->iso_next_group_combos) - { - int i = 0; - while (i < keys->n_iso_next_group_combos) - { - if (keys->iso_next_group_combos[i].keycode != 0) - meta_change_keygrab (keys, screen->xroot, grab, &keys->iso_next_group_combos[i]); - - ++i; - } - } + for (i = 0; i < keys->n_iso_next_group_combos; i++) + meta_change_keygrab (keys, screen->xroot, grab, &keys->iso_next_group_combo[i]); change_binding_keygrabs (keys, screen->xroot, FALSE, grab); } @@ -1367,7 +1414,7 @@ meta_display_grab_accelerator (MetaDisplay *display, MetaKeyBinding *binding; MetaKeyGrab *grab; MetaKeyCombo combo = { 0 }; - MetaResolvedKeyCombo resolved_combo = { 0 }; + MetaResolvedKeyCombo resolved_combo = { NULL, 0 }; if (!meta_parse_accelerator (accelerator, &combo)) { @@ -1380,11 +1427,14 @@ meta_display_grab_accelerator (MetaDisplay *display, resolve_key_combo (keys, &combo, &resolved_combo); - if (resolved_combo.keycode == 0) + if (resolved_combo.len == 0) return META_KEYBINDING_ACTION_NONE; if (get_keybinding (keys, &resolved_combo)) - return META_KEYBINDING_ACTION_NONE; + { + resolved_key_combo_reset (&resolved_combo); + return META_KEYBINDING_ACTION_NONE; + } meta_change_keygrab (keys, display->screen->xroot, TRUE, &resolved_combo); @@ -1415,7 +1465,7 @@ meta_display_ungrab_accelerator (MetaDisplay *display, MetaKeyBinding *binding; MetaKeyGrab *grab; char *key; - MetaResolvedKeyCombo resolved_combo; + MetaResolvedKeyCombo resolved_combo = { NULL, 0 }; g_return_val_if_fail (action != META_KEYBINDING_ACTION_NONE, FALSE); @@ -1428,18 +1478,22 @@ meta_display_ungrab_accelerator (MetaDisplay *display, binding = get_keybinding (keys, &resolved_combo); if (binding) { - guint32 index_key; + int i; meta_change_keygrab (keys, display->screen->xroot, FALSE, &binding->resolved_combo); - index_key = key_combo_key (&binding->resolved_combo); - g_hash_table_remove (keys->key_bindings_index, GINT_TO_POINTER (index_key)); + for (i = 0; i < binding->resolved_combo.len; i++) + { + guint32 index_key = key_combo_key (&binding->resolved_combo, i); + g_hash_table_remove (keys->key_bindings_index, GINT_TO_POINTER (index_key)); + } g_hash_table_remove (keys->key_bindings, binding); } g_hash_table_remove (external_grabs, key); g_free (key); + resolved_key_combo_reset (&resolved_combo); return TRUE; } @@ -1645,16 +1699,15 @@ process_event (MetaDisplay *display, ClutterKeyEvent *event) { MetaKeyBindingManager *keys = &display->key_binding_manager; - MetaResolvedKeyCombo resolved_combo; + xkb_keycode_t keycode = (xkb_keycode_t) event->hardware_keycode; + MetaResolvedKeyCombo resolved_combo = { &keycode, 1 }; MetaKeyBinding *binding; /* we used to have release-based bindings but no longer. */ if (event->type == CLUTTER_KEY_RELEASE) return FALSE; - resolved_combo_from_event_params (&resolved_combo, keys, - event->hardware_keycode, - event->modifier_state); + resolved_combo.mask = mask_from_event_params (keys, event->modifier_state); binding = get_keybinding (keys, &resolved_combo); @@ -1708,7 +1761,8 @@ process_overlay_key (MetaDisplay *display, if (keys->overlay_key_only_pressed) { - if (event->hardware_keycode != (int)keys->overlay_resolved_key_combo.keycode) + if (! resolved_key_combo_has_keycode (&keys->overlay_resolved_key_combo, + event->hardware_keycode)) { keys->overlay_key_only_pressed = FALSE; @@ -1790,7 +1844,8 @@ process_overlay_key (MetaDisplay *display, return TRUE; } else if (event->type == CLUTTER_KEY_PRESS && - event->hardware_keycode == (int)keys->overlay_resolved_key_combo.keycode) + resolved_key_combo_has_keycode (&keys->overlay_resolved_key_combo, + event->hardware_keycode)) { keys->overlay_key_only_pressed = TRUE; /* We keep the keyboard frozen - this allows us to use ReplayKeyboard @@ -1813,30 +1868,31 @@ process_iso_next_group (MetaDisplay *display, { MetaKeyBindingManager *keys = &display->key_binding_manager; gboolean activate; - MetaResolvedKeyCombo resolved_combo; - int i; + xkb_keycode_t keycode = (xkb_keycode_t) event->hardware_keycode; + xkb_mod_mask_t mask; + int i, j; if (event->type == CLUTTER_KEY_RELEASE) return FALSE; activate = FALSE; - - resolved_combo_from_event_params (&resolved_combo, keys, - event->hardware_keycode, - event->modifier_state); + mask = mask_from_event_params (keys, event->modifier_state); for (i = 0; i < keys->n_iso_next_group_combos; ++i) { - if (resolved_combo.keycode == keys->iso_next_group_combos[i].keycode && - resolved_combo.mask == keys->iso_next_group_combos[i].mask) + for (j = 0; j < keys->iso_next_group_combo[i].len; ++j) { - /* If the signal handler returns TRUE the keyboard will - remain frozen. It's the signal handler's responsibility - to unfreeze it. */ - if (!meta_display_modifiers_accelerator_activate (display)) - meta_display_unfreeze_keyboard (display, event->time); - activate = TRUE; - break; + if (keycode == keys->iso_next_group_combo[i].keycodes[j] && + mask == keys->iso_next_group_combo[i].mask) + { + /* If the signal handler returns TRUE the keyboard will + remain frozen. It's the signal handler's responsibility + to unfreeze it. */ + if (!meta_display_modifiers_accelerator_activate (display)) + meta_display_unfreeze_keyboard (display, event->time); + activate = TRUE; + break; + } } }