diff --git a/js/ui/panel.js b/js/ui/panel.js index e01510999..5c9330ef1 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -381,6 +381,14 @@ Panel.prototype = { statusmenu.toggle(e); return false; }); + // This depends on connection ordering: since we are after the Button's + // ::button-release-event handler, calling button.release() will properly + // unset the 'active' flag for this stays-pressed button + statusbutton.button.connect('button-release-event', function (b, e) { + if (!statusmenu.is_active()) + statusbutton.release(); + return false; + }); this._rightBox.append(statusbutton.button, Big.BoxPackFlags.NONE); // We get a deactivated event when the popup disappears this._statusmenu.connect('deactivated', function (sm) { diff --git a/src/shell-global.c b/src/shell-global.c index 6193e0eb6..f8eff4097 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -512,6 +512,30 @@ shell_global_end_modal (ShellGlobal *global, mutter_plugin_end_modal (global->plugin, timestamp); } +/** + * shell_global_display_is_grabbed + * @global: a #ShellGlobal + * + * Determines whether Mutter currently has a grab (keyboard or mouse or + * both) on the display. This could be the result of a current window + * management operation like a window move, or could be from + * shell_global_begin_modal(). + * + * This function is useful to for ad-hoc checks to avoid over-grabbing + * the Mutter grab a grab from GTK+. Longer-term we might instead want a + * mechanism to make Mutter use GDK grabs instead of raw XGrabPointer(). + * + * Return value: %TRUE if Mutter has a grab on the display + */ +gboolean +shell_global_display_is_grabbed (ShellGlobal *global) +{ + MetaScreen *screen = mutter_plugin_get_screen (global->plugin); + MetaDisplay *display = meta_screen_get_display (screen); + + return meta_display_get_grab_op (display) != META_GRAB_OP_NONE; +} + /* Code to close all file descriptors before we exec; copied from gspawn.c in GLib. * * Authors: Padraig O'Briain, Matthias Clasen, Lennart Poettering diff --git a/src/shell-global.h b/src/shell-global.h index a3ad26397..3cda07b56 100644 --- a/src/shell-global.h +++ b/src/shell-global.h @@ -69,6 +69,8 @@ gboolean shell_global_begin_modal (ShellGlobal *global, void shell_global_end_modal (ShellGlobal *global, guint32 timestamp); +gboolean shell_global_display_is_grabbed (ShellGlobal *global); + void shell_global_reexec_self (ShellGlobal *global); void shell_global_format_time_relative_pretty (ShellGlobal *global, guint delta, char **text, guint *update_time); diff --git a/src/shell-status-menu.c b/src/shell-status-menu.c index a5b1ccd78..4018faf3c 100644 --- a/src/shell-status-menu.c +++ b/src/shell-status-menu.c @@ -660,23 +660,53 @@ position_menu (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user *y = (gint)(0.5 + src_y + height); } +/** + * shell_status_menu_toggle: + * @menu: a #ShellStatusMenu + * + * If the menu is not currently up, pops it up. Otherwise, hides it. + * Popping up may fail if another grab is already active; check with + * shell_status_menu_is_active(). + */ void shell_status_menu_toggle (ShellStatusMenu *status, ClutterEvent *event) { ShellStatusMenuPrivate *priv = status->priv; - if (GTK_WIDGET_VISIBLE (GTK_WIDGET (priv->menu))) + if (GTK_WIDGET_VISIBLE (priv->menu)) { - gtk_widget_hide (GTK_WIDGET (priv->menu)); + gtk_menu_popdown (GTK_MENU (priv->menu)); } else { - gtk_widget_show (GTK_WIDGET (priv->menu)); + /* We don't want to overgrab a Mutter grab with the grab that GTK+ + * uses on menus. + */ + ShellGlobal *global = shell_global_get (); + if (shell_global_display_is_grabbed (global)) + return; + gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL, position_menu, - status, 1, event->button.time); + status, 1, event->button.time); } } +/** + * shell_status_menu_is_active: + * @menu: a #ShellStatusMenu + * + * Gets whether the menu is currently popped up + * + * Return value: %TRUE if the menu is currently popped up + */ +gboolean +shell_status_menu_is_active (ShellStatusMenu *status) +{ + ShellStatusMenuPrivate *priv = status->priv; + + return GTK_WIDGET_VISIBLE (priv->menu); +} + /** * shell_status_menu_get_name: * @menu: a #ShellStatusMenu diff --git a/src/shell-status-menu.h b/src/shell-status-menu.h index 983174187..9a7c2969f 100644 --- a/src/shell-status-menu.h +++ b/src/shell-status-menu.h @@ -36,7 +36,7 @@ struct _ShellStatusMenuClass GType shell_status_menu_get_type (void); void shell_status_menu_toggle (ShellStatusMenu *menu, ClutterEvent *event); - +gboolean shell_status_menu_is_active (ShellStatusMenu *menu); ClutterText *shell_status_menu_get_name (ShellStatusMenu *menu); ClutterTexture *shell_status_menu_get_icon (ShellStatusMenu *menu);