diff --git a/src/core/display-private.h b/src/core/display-private.h index 1f73fc3b2..3423fb176 100644 --- a/src/core/display-private.h +++ b/src/core/display-private.h @@ -239,6 +239,8 @@ struct _MetaDisplay unsigned int meta_mask; MetaKeyCombo overlay_key_combo; gboolean overlay_key_only_pressed; + MetaKeyCombo *iso_next_group_combos; + int n_iso_next_group_combos; /* Monitor cache */ unsigned int monitor_cache_invalidated : 1; @@ -458,6 +460,7 @@ void meta_display_overlay_key_activate (MetaDisplay *display); void meta_display_accelerator_activate (MetaDisplay *display, guint action, guint deviceid); +gboolean meta_display_modifiers_accelerator_activate (MetaDisplay *display); /* In above-tab-keycode.c */ guint meta_display_get_above_tab_keycode (MetaDisplay *display); diff --git a/src/core/display.c b/src/core/display.c index f4f7e6325..5555a3d91 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -139,6 +139,7 @@ enum { OVERLAY_KEY, ACCELERATOR_ACTIVATED, + MODIFIERS_ACCELERATOR_ACTIVATED, FOCUS_WINDOW, WINDOW_CREATED, WINDOW_DEMANDS_ATTENTION, @@ -255,6 +256,25 @@ meta_display_class_init (MetaDisplayClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); + /** + * MetaDisplay::modifiers-accelerator-activated: + * @display: the #MetaDisplay instance + * + * The ::modifiers-accelerator-activated signal will be emitted when + * a special modifiers-only keybinding is activated. + * + * Returns: %TRUE means that the keyboard device should remain + * frozen and %FALSE for the default behavior of unfreezing the + * keyboard. + */ + display_signals[MODIFIERS_ACCELERATOR_ACTIVATED] = + g_signal_new ("modifiers-accelerator-activated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + g_signal_accumulator_first_wins, NULL, NULL, + G_TYPE_BOOLEAN, 0); + display_signals[WINDOW_CREATED] = g_signal_new ("window-created", G_TYPE_FROM_CLASS (klass), @@ -5681,6 +5701,16 @@ meta_display_accelerator_activate (MetaDisplay *display, 0, action, deviceid); } +gboolean +meta_display_modifiers_accelerator_activate (MetaDisplay *display) +{ + gboolean freeze; + + g_signal_emit (display, display_signals[MODIFIERS_ACCELERATOR_ACTIVATED], 0, &freeze); + + return freeze; +} + void meta_display_get_compositor_version (MetaDisplay *display, int *major, diff --git a/src/core/keybindings.c b/src/core/keybindings.c index 94f86dc58..baceb6d69 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -302,6 +302,172 @@ reload_modmap (MetaDisplay *display) display->meta_mask); } +/* Original code from gdk_x11_keymap_get_entries_for_keyval() in + * gdkkeys-x11.c */ +static int +get_keycodes_for_keysym (MetaDisplay *display, + int keysym, + int **keycodes) +{ + GArray *retval; + int n_keycodes; + int keycode; + + retval = g_array_new (FALSE, FALSE, sizeof (int)); + + keycode = display->min_keycode; + while (keycode <= display->max_keycode) + { + const KeySym *syms = display->keymap + (keycode - display->min_keycode) * display->keysyms_per_keycode; + int i = 0; + + while (i < display->keysyms_per_keycode) + { + if (syms[i] == (unsigned int)keysym) + g_array_append_val (retval, keycode); + + ++i; + } + + ++keycode; + } + + n_keycodes = retval->len; + *keycodes = (int*) g_array_free (retval, n_keycodes == 0 ? TRUE : FALSE); + + return n_keycodes; +} + +static void +reload_iso_next_group_combos (MetaDisplay *display) +{ + const char *iso_next_group_option; + MetaKeyCombo *combos; + int *keycodes; + int n_keycodes; + int n_combos; + int i; + + g_clear_pointer (&display->iso_next_group_combos, g_free); + display->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 (display, XK_ISO_Next_Group, &keycodes); + + if (g_str_equal (iso_next_group_option, "toggle") || + g_str_equal (iso_next_group_option, "lalt_toggle") || + g_str_equal (iso_next_group_option, "lwin_toggle") || + g_str_equal (iso_next_group_option, "rwin_toggle") || + g_str_equal (iso_next_group_option, "lshift_toggle") || + g_str_equal (iso_next_group_option, "rshift_toggle") || + g_str_equal (iso_next_group_option, "lctrl_toggle") || + g_str_equal (iso_next_group_option, "rctrl_toggle") || + g_str_equal (iso_next_group_option, "sclk_toggle") || + 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 (MetaKeyCombo, n_combos); + + for (i = 0; i < n_keycodes; ++i) + { + combos[i].keysym = XK_ISO_Next_Group; + combos[i].keycode = keycodes[i]; + combos[i].modifiers = 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 (MetaKeyCombo, n_combos); + + for (i = 0; i < n_keycodes; ++i) + { + combos[i].keysym = XK_ISO_Next_Group; + combos[i].keycode = keycodes[i]; + combos[i].modifiers = 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 (MetaKeyCombo, n_combos); + + for (i = 0; i < n_keycodes; ++i) + { + combos[i].keysym = XK_ISO_Next_Group; + combos[i].keycode = keycodes[i]; + combos[i].modifiers = 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 (MetaKeyCombo, n_combos); + + for (i = 0; i < n_keycodes; ++i) + { + combos[i].keysym = XK_ISO_Next_Group; + combos[i].keycode = keycodes[i]; + combos[i].modifiers = ShiftMask; + + combos[i + n_keycodes].keysym = XK_ISO_Next_Group; + combos[i + n_keycodes].keycode = keycodes[i]; + combos[i + n_keycodes].modifiers = ControlMask; + } + } + else if (g_str_equal (iso_next_group_option, "ctrl_alt_toggle")) + { + n_combos = n_keycodes * 2; + combos = g_new (MetaKeyCombo, n_combos); + + for (i = 0; i < n_keycodes; ++i) + { + combos[i].keysym = XK_ISO_Next_Group; + combos[i].keycode = keycodes[i]; + combos[i].modifiers = Mod1Mask; + + combos[i + n_keycodes].keysym = XK_ISO_Next_Group; + combos[i + n_keycodes].keycode = keycodes[i]; + combos[i + n_keycodes].modifiers = ControlMask; + } + } + 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 (MetaKeyCombo, n_combos); + + for (i = 0; i < n_keycodes; ++i) + { + combos[i].keysym = XK_ISO_Next_Group; + combos[i].keycode = keycodes[i]; + combos[i].modifiers = Mod1Mask; + + combos[i + n_keycodes].keysym = XK_ISO_Next_Group; + combos[i + n_keycodes].keycode = keycodes[i]; + combos[i + n_keycodes].modifiers = ShiftMask; + } + } + else + { + n_combos = 0; + combos = NULL; + } + + g_free (keycodes); + + display->n_iso_next_group_combos = n_combos; + display->iso_next_group_combos = combos; +} + static guint keysym_to_keycode (MetaDisplay *display, guint keysym) @@ -328,6 +494,8 @@ reload_keycodes (MetaDisplay *display) display->overlay_key_combo.keycode = 0; } + reload_iso_next_group_combos (display); + if (display->key_bindings) { int i; @@ -1026,6 +1194,22 @@ meta_screen_change_keygrabs (MetaScreen *screen, display->overlay_key_combo.keycode, display->overlay_key_combo.modifiers); + if (display->iso_next_group_combos) + { + int i = 0; + while (i < display->n_iso_next_group_combos) + { + if (display->iso_next_group_combos[i].keycode != 0) + { + meta_change_keygrab (display, screen->xroot, grab, + display->iso_next_group_combos[i].keysym, + display->iso_next_group_combos[i].keycode, + display->iso_next_group_combos[i].modifiers); + } + ++i; + } + } + change_binding_keygrabs (screen->display->key_bindings, screen->display->n_key_bindings, screen->display, screen->xroot, @@ -1801,6 +1985,41 @@ process_overlay_key (MetaDisplay *display, return FALSE; } +static gboolean +process_iso_next_group (MetaDisplay *display, + MetaScreen *screen, + XIDeviceEvent *event, + KeySym keysym) +{ + gboolean activate; + unsigned int mods; + int i; + + if (event->evtype != XI_KeyPress) + return FALSE; + + activate = FALSE; + mods = (event->mods.effective & 0xff & ~(display->ignored_modifier_mask)); + + for (i = 0; i < display->n_iso_next_group_combos; ++i) + { + if (event->detail == (int)display->iso_next_group_combos[i].keycode && + mods == display->iso_next_group_combos[i].modifiers) + { + /* 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)) + XIAllowEvents (display->xdisplay, event->deviceid, + XIAsyncDevice, event->time); + activate = TRUE; + break; + } + } + + return activate; +} + /* Handle a key event. May be called recursively: some key events cause * grabs to be ended and then need to be processed again in their own * right. This cannot cause infinite recursion because we never call @@ -1875,6 +2094,10 @@ meta_display_process_key_event (MetaDisplay *display, handled = process_overlay_key (display, screen, event, keysym); if (handled) return TRUE; + + handled = process_iso_next_group (display, screen, event, keysym); + if (handled) + return TRUE; } XIAllowEvents (display->xdisplay, event->deviceid, @@ -4568,6 +4791,12 @@ meta_display_init_keys (MetaDisplay *display) g_hash_table_insert (key_handlers, g_strdup ("overlay-key"), handler); + handler = g_new0 (MetaKeyHandler, 1); + handler->name = g_strdup ("iso-next-group"); + handler->flags = META_KEY_BINDING_BUILTIN; + + g_hash_table_insert (key_handlers, g_strdup ("iso-next-group"), handler); + handler = g_new0 (MetaKeyHandler, 1); handler->name = g_strdup ("external-grab"); handler->func = handle_external_grab;