From 12f71c9795e21cefd0faf1d98327d2139172ae71 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Fri, 8 Jul 2011 20:48:01 -0400 Subject: [PATCH] Improve handling of key combinations When we get a press of the overlay key, and then another key is pressed, first try to handle the combination as a global keybinding. If that fails, call XAllowEvents(..., ReplayKeyboard, ...) to let it be handled by our per-window keybindings or by the application. This requires restructuring things to call XAllowEvents a bit later so we can pass the right mode. https://bugzilla.gnome.org/show_bug.cgi?id=624869 --- src/core/keybindings.c | 123 +++++++++++++++++++++++++++++------------ 1 file changed, 89 insertions(+), 34 deletions(-) diff --git a/src/core/keybindings.c b/src/core/keybindings.c index 79f1e42db..0e7d355fc 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -1250,31 +1250,6 @@ primary_modifier_still_pressed (MetaDisplay *display, return TRUE; } -static gboolean -process_overlay_key (MetaDisplay *display, - MetaScreen *Screen, - XEvent *event, - KeySym keysym) -{ - if (event->xkey.keycode != display->overlay_key_combo.keycode) - { - display->overlay_key_only_pressed = FALSE; - return FALSE; - } - - if (event->xkey.type == KeyPress) - { - display->overlay_key_only_pressed = TRUE; - } - else if (event->xkey.type == KeyRelease && display->overlay_key_only_pressed) - { - display->overlay_key_only_pressed = FALSE; - meta_display_overlay_key_activate (display); - } - - return TRUE; -} - static void invoke_handler (MetaDisplay *display, MetaScreen *screen, @@ -1380,6 +1355,73 @@ process_event (MetaKeyBinding *bindings, return FALSE; } +static gboolean +process_overlay_key (MetaDisplay *display, + MetaScreen *screen, + XEvent *event, + KeySym keysym) +{ + if (display->overlay_key_only_pressed) + { + if (event->xkey.keycode != display->overlay_key_combo.keycode) + { + display->overlay_key_only_pressed = FALSE; + + /* OK, the user hit modifier+key rather than pressing and + * releasing the ovelay key. We want to handle the key + * sequence "normally". Unfortunately, using + * XAllowEvents(..., ReplayKeyboard, ...) doesn't quite + * work, since global keybindings won't be activated ("this + * time, however, the function ignores any passive grabs at + * above (toward the root of) the grab_window of the grab + * just released.") So, we first explicitly check for one of + * our global keybindings, and if not found, we then replay + * the event. Other clients with global grabs will be out of + * luck. + */ + if (process_event (display->key_bindings, + display->n_key_bindings, + display, screen, NULL, event, keysym, + FALSE)) + { + /* As normally, after we've handled a global key + * binding, we unfreeze the keyboard but keep the grab + * (this is important for something like cycling + * windows */ + XAllowEvents (display->xdisplay, AsyncKeyboard, event->xkey.time); + } + else + { + /* Replay the event so it gets delivered to our + * per-window key bindings or to the application */ + XAllowEvents (display->xdisplay, ReplayKeyboard, event->xkey.time); + } + } + else if (event->xkey.type == KeyRelease) + { + display->overlay_key_only_pressed = FALSE; + /* We want to unfreeze events, but keep the grab so that if the user + * starts typing into the overlay we get all the keys */ + XAllowEvents (display->xdisplay, AsyncKeyboard, event->xkey.time); + meta_display_overlay_key_activate (display); + } + + return TRUE; + } + else if (event->xkey.type == KeyPress && + event->xkey.keycode == display->overlay_key_combo.keycode) + { + display->overlay_key_only_pressed = TRUE; + /* We keep the keyboard frozen - this allows us to use ReplayKeyboard + * on the next event if it's not the release of the overlay key */ + XAllowEvents (display->xdisplay, SyncKeyboard, event->xkey.time); + + return TRUE; + } + else + return FALSE; +} + /* 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 @@ -1406,11 +1448,19 @@ meta_display_process_key_event (MetaDisplay *display, const char *str; MetaScreen *screen; - XAllowEvents (display->xdisplay, - all_bindings_disabled ? ReplayKeyboard : AsyncKeyboard, - event->xkey.time); if (all_bindings_disabled) - return FALSE; + { + /* In this mode, we try to pretend we don't have grabs, so we + * immediately replay events and drop the grab. (This still + * messes up global passive grabs from other clients.) The + * FALSE return here is a little suspect, but we don't really + * know if we'll see the event again or not, and it's pretty + * poorly defined how this mode is supposed to interact with + * plugins. + */ + XAllowEvents (display->xdisplay, ReplayKeyboard, event->xkey.time); + return FALSE; + } /* if key event was on root window, we have a shortcut */ screen = meta_display_screen_for_root (display, event->xkey.window); @@ -1440,8 +1490,17 @@ meta_display_process_key_event (MetaDisplay *display, str ? str : "none", event->xkey.state, window ? window->desc : "(no window)"); - keep_grab = TRUE; all_keys_grabbed = window ? window->all_keys_grabbed : screen->all_keys_grabbed; + if (!all_keys_grabbed) + { + handled = process_overlay_key (display, screen, event, keysym); + if (handled) + return TRUE; + } + + XAllowEvents (display->xdisplay, AsyncKeyboard, event->xkey.time); + + keep_grab = TRUE; if (all_keys_grabbed) { if (display->grab_op == META_GRAB_OP_NONE) @@ -1527,10 +1586,6 @@ meta_display_process_key_event (MetaDisplay *display, return TRUE; } - handled = process_overlay_key (display, screen, event, keysym); - if (handled) - return TRUE; - /* Do the normal keybindings */ return process_event (display->key_bindings, display->n_key_bindings,