Fix custom-alt-tabs for single-handling of key events

The changes to enforce single handling of all key events were breaking
custom-alt-tab keypress handlers, since that code was assuming that
key event would get to process_tab_grab(), and then maybe to
process_event() and then to the plugin's xevent_filter to detect a
key release.

We centeralize all of this handling into process_tab_grab() and either

 - Invoke a custom handler for the key press
 - Select the current window on modifier release by calling a  new
   pseudo-binding "tab_popup_select"
 - Cancel the grab on an unbound key by calling a new pseudo-binding
   "tab_popup_cancel"

http://bugzilla.gnome.org/show_bug.cgi?id=590754
This commit is contained in:
Owen W. Taylor 2009-08-25 16:54:54 -04:00
parent 67682a2683
commit 7b0ba87b24
2 changed files with 145 additions and 70 deletions

View File

@ -475,8 +475,8 @@ regrab_key_bindings (MetaDisplay *display)
g_slist_free (windows); g_slist_free (windows);
} }
static MetaKeyBindingAction static MetaKeyBinding *
display_get_keybinding_action (MetaDisplay *display, display_get_keybinding (MetaDisplay *display,
unsigned int keysym, unsigned int keysym,
unsigned int keycode, unsigned int keycode,
unsigned long mask) unsigned long mask)
@ -490,12 +490,28 @@ display_get_keybinding_action (MetaDisplay *display,
display->key_bindings[i].keycode == keycode && display->key_bindings[i].keycode == keycode &&
display->key_bindings[i].mask == mask) display->key_bindings[i].mask == mask)
{ {
return meta_prefs_get_keybinding_action (display->key_bindings[i].name); return &display->key_bindings[i];
} }
--i; --i;
} }
return NULL;
}
static MetaKeyBindingAction
display_get_keybinding_action (MetaDisplay *display,
unsigned int keysym,
unsigned int keycode,
unsigned long mask)
{
MetaKeyBinding *binding;
binding = display_get_keybinding (display, keysym, keycode, mask);
if (binding)
return meta_prefs_get_keybinding_action (binding->name);
else
return META_KEYBINDING_ACTION_NONE; return META_KEYBINDING_ACTION_NONE;
} }
@ -1197,6 +1213,45 @@ process_overlay_key (MetaDisplay *display,
return TRUE; return TRUE;
} }
static void
invoke_handler (MetaDisplay *display,
MetaScreen *screen,
MetaKeyHandler *handler,
MetaWindow *window,
XEvent *event,
MetaKeyBinding *binding)
{
if (handler->func)
(* handler->func) (display, screen,
handler->flags & BINDING_PER_WINDOW ?
window : NULL,
event,
binding,
handler->user_data);
else
(* handler->default_func) (display, screen,
handler->flags & BINDING_PER_WINDOW ?
window: NULL,
event,
binding,
NULL);
}
static void
invoke_handler_by_name (MetaDisplay *display,
MetaScreen *screen,
const char *handler_name,
MetaWindow *window,
XEvent *event)
{
MetaKeyHandler *handler;
handler = find_handler (key_handlers, handler_name);
if (handler)
invoke_handler (display, screen, handler, window, event, NULL);
}
/* now called from only one place, may be worth merging */ /* now called from only one place, may be worth merging */
static gboolean static gboolean
process_event (MetaKeyBinding *bindings, process_event (MetaKeyBinding *bindings,
@ -1253,20 +1308,7 @@ process_event (MetaKeyBinding *bindings,
*/ */
display->allow_terminal_deactivation = TRUE; display->allow_terminal_deactivation = TRUE;
if (handler->func) invoke_handler (display, screen, handler, window, event, &bindings[i]);
(* handler->func) (display, screen,
bindings[i].handler->flags & BINDING_PER_WINDOW ?
window: NULL,
event,
&bindings[i],
handler->user_data);
else
(* handler->default_func) (display, screen,
bindings[i].handler->flags & BINDING_PER_WINDOW ?
window: NULL,
event,
&bindings[i],
NULL);
return TRUE; return TRUE;
} }
@ -1985,6 +2027,7 @@ process_tab_grab (MetaDisplay *display,
XEvent *event, XEvent *event,
KeySym keysym) KeySym keysym)
{ {
MetaKeyBinding *binding;
MetaKeyBindingAction action; MetaKeyBindingAction action;
gboolean popup_not_showing; gboolean popup_not_showing;
gboolean backward; gboolean backward;
@ -1994,79 +2037,78 @@ process_tab_grab (MetaDisplay *display,
if (screen != display->grab_screen) if (screen != display->grab_screen)
return FALSE; return FALSE;
action = display_get_keybinding_action (display, binding = display_get_keybinding (display,
keysym, keysym,
event->xkey.keycode, event->xkey.keycode,
display->grab_mask); display->grab_mask);
if (binding)
action = meta_prefs_get_keybinding_action (binding->name);
else
action = META_KEYBINDING_ACTION_NONE;
/* /*
* If there is no tab_pop up object, i.e., there is some custom handler * There are currently two different ways of customizing Alt-Tab, you can either
* implementing Alt+Tab & Co., we call this custom handler; we do not * provide a replacement AltTabHandler object, or you can hook into the keybindings
* mess about with the grab, as that is up to the handler to deal with. * meta_keybindings_set_custom_handler() and call meta_display_begin_grab_op()
* yourself with one of the "tabbing" grab ops META_GRAB_OP_KEYBOARD_TABBING_NORMAL,
* etc. See meta_display_process_key_event() for the complete list. If screen->tab_handler
* is NULL, the latter mechanism is being used. We skip most of our normal
* processing and just make sure that the right custom handlers get called.
*/ */
if (!screen->tab_handler) if (!screen->tab_handler)
{ {
MetaKeyHandler *handler = NULL; if (event->type == KeyRelease)
const gchar *handler_name = NULL; {
if (end_keyboard_grab (display, event->xkey.keycode))
{
invoke_handler_by_name (display, screen, "tab_popup_select", NULL, event);
/* We return FALSE to end the grab; if the handler ended the grab itself
* that will be a noop. If the handler didn't end the grab, then it's a
* safety measure to prevent a stuck grab.
*/
return FALSE;
}
return TRUE;
}
switch (action) switch (action)
{ {
case META_KEYBINDING_ACTION_CYCLE_PANELS: case META_KEYBINDING_ACTION_CYCLE_PANELS:
handler_name = "cycle_group";
break;
case META_KEYBINDING_ACTION_CYCLE_WINDOWS: case META_KEYBINDING_ACTION_CYCLE_WINDOWS:
handler_name = "cycle_windows";
break;
case META_KEYBINDING_ACTION_CYCLE_PANELS_BACKWARD: case META_KEYBINDING_ACTION_CYCLE_PANELS_BACKWARD:
handler_name = "cycle_panels_backward";
break;
case META_KEYBINDING_ACTION_CYCLE_WINDOWS_BACKWARD: case META_KEYBINDING_ACTION_CYCLE_WINDOWS_BACKWARD:
handler_name = "cycle_windows_backward";
break;
case META_KEYBINDING_ACTION_SWITCH_PANELS: case META_KEYBINDING_ACTION_SWITCH_PANELS:
handler_name = "switch_panels";
break;
case META_KEYBINDING_ACTION_SWITCH_WINDOWS: case META_KEYBINDING_ACTION_SWITCH_WINDOWS:
handler_name = "switch_windows";
break;
case META_KEYBINDING_ACTION_SWITCH_PANELS_BACKWARD: case META_KEYBINDING_ACTION_SWITCH_PANELS_BACKWARD:
handler_name = "switch_panels_backward";
break;
case META_KEYBINDING_ACTION_SWITCH_WINDOWS_BACKWARD: case META_KEYBINDING_ACTION_SWITCH_WINDOWS_BACKWARD:
handler_name = "switch_windows_backward";
break;
case META_KEYBINDING_ACTION_CYCLE_GROUP: case META_KEYBINDING_ACTION_CYCLE_GROUP:
handler_name = "cycle_group";
break;
case META_KEYBINDING_ACTION_CYCLE_GROUP_BACKWARD: case META_KEYBINDING_ACTION_CYCLE_GROUP_BACKWARD:
handler_name = "cycle_group_backward";
break;
case META_KEYBINDING_ACTION_SWITCH_GROUP: case META_KEYBINDING_ACTION_SWITCH_GROUP:
handler_name = "switch_group";
break;
case META_KEYBINDING_ACTION_SWITCH_GROUP_BACKWARD: case META_KEYBINDING_ACTION_SWITCH_GROUP_BACKWARD:
handler_name = "switch_group_backward"; /* These are the tab-popup bindings. If a custom Alt-Tab implementation
break; * is in effect, we expect it to want to handle all of these as a group
default: *
/* * If there are some of them that the custom implementation didn't
* This is the case when the Alt key is released; we preserve * handle, we treat them as "unbound" for the duration - running the
* the grab, as it is up to the custom implementaiton to free it * normal handlers could get us into trouble.
* (a plugin can catch this in their xevent_filter function).
*/ */
if (binding->handler &&
binding->handler->func &&
binding->handler->func != binding->handler->default_func)
{
invoke_handler (display, screen, binding->handler, NULL, event, binding);
return TRUE; return TRUE;
} }
break;
default:
break;
}
/* /* Some unhandled key press */
* We do not want to actually call the handler, we just want to ensure invoke_handler_by_name (display, screen, "tab_popup_cancel", NULL, event);
* that if a custom handler is installed, we do not release the grab here.
* The handler will get called as normal in the process_event() function.
*/
handler = find_handler (key_handlers, handler_name);
if (!handler || !handler->func || handler->func == handler->default_func)
return FALSE; return FALSE;
return TRUE;
} }
if (event->type == KeyRelease && if (event->type == KeyRelease &&
@ -3048,6 +3090,27 @@ handle_cycle (MetaDisplay *display,
backwards, FALSE); backwards, FALSE);
} }
static void
handle_tab_popup_select (MetaDisplay *display,
MetaScreen *screen,
MetaWindow *window,
XEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
/* Stub for custom handlers; no default implementation */
}
static void
handle_tab_popup_cancel (MetaDisplay *display,
MetaScreen *screen,
MetaWindow *window,
XEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
/* Stub for custom handlers; no default implementation */
}
static void static void
handle_toggle_fullscreen (MetaDisplay *display, handle_toggle_fullscreen (MetaDisplay *display,

View File

@ -187,6 +187,18 @@ keybind (cycle_panels_backward, handle_cycle, META_TAB_LIST_DOCKS,
/***********************************/ /***********************************/
/* These two are special pseudo-bindings that are provided for allowing
* custom handlers, but will never be bound to a key. While a tab
* grab is in effect, they are invoked for releasing the primary modifier
* or pressing some unbound key, respectively.
*/
keybind (tab_popup_select, handle_tab_popup_select, 0, 0, NULL,
"Select window from tab popup")
keybind (tab_popup_cancel, handle_tab_popup_cancel, 0, 0, NULL,
"Cancel tab popup")
/***********************************/
keybind (show_desktop, handle_show_desktop, 0, 0, "<Control><Alt>d", keybind (show_desktop, handle_show_desktop, 0, 0, "<Control><Alt>d",
_("Hide all normal windows and set focus to the desktop")) _("Hide all normal windows and set focus to the desktop"))
keybind (panel_main_menu, handle_panel, keybind (panel_main_menu, handle_panel,