From f0f4c5e1217eefd46edf9f98633fb32967c67461 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Thu, 30 Aug 2018 18:11:29 +0200 Subject: menu-bar: transfer focus correctly on alt-F1 The background is in https://gitlab.gnome.org/GNOME/gtk/issues/85 . One of the conclusions, in https://gitlab.gnome.org/GNOME/gtk/issues/85#note_264804 , is that mate-panel needs to properly transfer focus on alt-F1 keyboard shortcut. It used to work only by luck before, only because gtk used to deactivate itself during a keyboard grab. But as discussed in https://gitlab.gnome.org/GNOME/gtk/issues/85 that behavior poses accessibility feedback issues, is not coherent, and keyboard grab feedback will not be available in wayland anyway. Thus @ebassi saying in https://gitlab.gnome.org/GNOME/gtk/issues/85#note_264804 that not transferring focus properly is the actual bug. This change explictly switches to the menu bar after saving which X Window had the focus, and on menu bar deactivation restores focus to that X Window. Fixes #851 --- mate-panel/panel-menu-bar.c | 22 ++++++++++ mate-panel/panel-menu-button.c | 22 ++++++++++ mate-panel/panel-util.c | 92 ++++++++++++++++++++++++++++++++++++++++++ mate-panel/panel-util.h | 5 +++ 4 files changed, 141 insertions(+) diff --git a/mate-panel/panel-menu-bar.c b/mate-panel/panel-menu-bar.c index 955e9c4b..06fca819 100644 --- a/mate-panel/panel-menu-bar.c +++ b/mate-panel/panel-menu-bar.c @@ -28,6 +28,9 @@ #include "panel-menu-bar.h" +#include +#include + #include #include @@ -65,6 +68,8 @@ struct _PanelMenuBarPrivate { GSettings* settings; PanelOrientation orientation; + + Window interrupted_window; }; enum { @@ -83,6 +88,15 @@ static gboolean panel_menu_bar_reinit_tooltip(GtkWidget* widget, PanelMenuBar* m return FALSE; } +static gboolean panel_menu_bar_deactivate (GtkWidget* widget, PanelMenuBar* menubar) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (widget); + panel_util_set_current_active_window (toplevel, menubar->priv->interrupted_window); + menubar->priv->interrupted_window = None; + + return FALSE; +} + static gboolean panel_menu_bar_hide_tooltip_and_focus(GtkWidget* widget, PanelMenuBar* menubar) { /* remove focus that would be drawn on the currently focused child of @@ -108,6 +122,7 @@ static void panel_menu_bar_setup_tooltip(PanelMenuBar* menubar) /* Reset tooltip when the menu bar is not used */ g_signal_connect(GTK_MENU_SHELL (menubar), "deactivate", G_CALLBACK (panel_menu_bar_reinit_tooltip), menubar); + g_signal_connect(GTK_MENU_SHELL (menubar), "deactivate", G_CALLBACK (panel_menu_bar_deactivate), menubar); } static void panel_menu_bar_update_visibility (GSettings* settings, gchar* key, PanelMenuBar* menubar) @@ -411,11 +426,18 @@ void panel_menu_bar_popup_menu(PanelMenuBar* menubar, guint32 activate_time) { GtkMenu* menu; GtkMenuShell* menu_shell; + GtkWidget* toplevel; + GdkWindow* window; g_return_if_fail(PANEL_IS_MENU_BAR(menubar)); menu = GTK_MENU(menubar->priv->applications_menu); + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menubar)); + menubar->priv->interrupted_window = panel_util_get_current_active_window (toplevel); + window = gtk_widget_get_window (toplevel); + panel_util_set_current_active_window (toplevel, GDK_WINDOW_XID(window)); + /* * We need to call _gtk_menu_shell_activate() here as is done in * window_key_press_handler in gtkmenubar.c which pops up menu diff --git a/mate-panel/panel-menu-button.c b/mate-panel/panel-menu-button.c index ae6db424..56969cc8 100644 --- a/mate-panel/panel-menu-button.c +++ b/mate-panel/panel-menu-button.c @@ -26,9 +26,12 @@ #include "panel-menu-button.h" +#include + #include #include #include +#include #include @@ -92,6 +95,8 @@ struct _PanelMenuButtonPrivate { char *custom_icon; char *tooltip; + Window interrupted_window; + MenuPathRoot path_root; guint use_menu_path : 1; guint use_custom_icon : 1; @@ -437,12 +442,23 @@ panel_menu_button_recreate_menu (PanelMenuButton *button) button->priv->menu = NULL; } +static gboolean panel_menu_button_menu_deactivate (GtkWidget* widget, PanelMenuButton* button) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel(widget); + panel_util_set_current_active_window(toplevel, button->priv->interrupted_window); + button->priv->interrupted_window = None; + + return FALSE; +} + void panel_menu_button_popup_menu (PanelMenuButton *button, guint n_button, guint32 activate_time) { GdkScreen *screen; + GtkWidget *toplevel; + GdkWindow *window; g_return_if_fail (PANEL_IS_MENU_BUTTON (button)); @@ -460,6 +476,12 @@ panel_menu_button_popup_menu (PanelMenuButton *button, GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL); + + g_signal_connect(GTK_MENU_SHELL (button->priv->menu), "deactivate", G_CALLBACK (panel_menu_button_menu_deactivate), button); + toplevel = gtk_widget_get_toplevel(GTK_WIDGET(button->priv->toplevel)); + button->priv->interrupted_window = panel_util_get_current_active_window (toplevel); + window = gtk_widget_get_window (toplevel); + panel_util_set_current_active_window (toplevel, GDK_WINDOW_XID(window)); } static void diff --git a/mate-panel/panel-util.c b/mate-panel/panel-util.c index 1106f034..6183cca8 100644 --- a/mate-panel/panel-util.c +++ b/mate-panel/panel-util.c @@ -23,11 +23,15 @@ #include #include +#include +#include + #include #include #include #include #include +#include #define MATE_DESKTOP_USE_UNSTABLE_API #include @@ -47,6 +51,8 @@ #include "panel-icon-names.h" #include "panel-lockdown.h" +static Atom _net_active_window = None; + char * panel_util_make_exec_uri_for_desktop (const char *exec) { @@ -1241,3 +1247,89 @@ panel_util_get_file_optional_homedir (const char *location) return file; } + +static void panel_menu_bar_get_net_active_window(Display *xdisplay) +{ + if (_net_active_window == None) + _net_active_window = XInternAtom (xdisplay, + "_NET_ACTIVE_WINDOW", + False); +} + +Window panel_util_get_current_active_window (GtkWidget *toplevel) +{ + GdkScreen *screen; + GdkDisplay *display; + GdkWindow *root; + Display *xdisplay; + Window xroot; + + Window res = None; + + Atom return_type; + int return_format; + unsigned long n; + unsigned long bytes; + unsigned char *prop = NULL; + + screen = gtk_window_get_screen (GTK_WINDOW(toplevel)); + display = gdk_screen_get_display (screen); + root = gdk_screen_get_root_window (screen); + + xdisplay = GDK_DISPLAY_XDISPLAY (display); + xroot = GDK_WINDOW_XID (root); + + panel_menu_bar_get_net_active_window (xdisplay); + if (_net_active_window != None + && XGetWindowProperty (xdisplay, xroot, _net_active_window, 0, 1, + False, XA_WINDOW, &return_type, &return_format, + &n, &bytes, &prop) == Success) + { + if ((return_type == XA_WINDOW) && (return_format == 32) && + (n == 1) && (prop)) { + res = *(Window *)prop; + } + + if (prop) + XFree (prop); + + } + return res; +} + +void panel_util_set_current_active_window (GtkWidget *toplevel, Window window) +{ + GdkScreen *screen; + GdkDisplay *display; + GdkWindow *root; + Display *xdisplay; + Window xroot; + XEvent xev; + + screen = gtk_window_get_screen (GTK_WINDOW(toplevel)); + display = gdk_screen_get_display (screen); + root = gdk_screen_get_root_window (screen); + + xdisplay = GDK_DISPLAY_XDISPLAY (display); + xroot = GDK_WINDOW_XID (root); + + panel_menu_bar_get_net_active_window (xdisplay); + if (_net_active_window == None) + return; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = window; + xev.xclient.message_type = _net_active_window; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 2; /* requestor type; we're not an app */ + xev.xclient.data.l[1] = CurrentTime; + xev.xclient.data.l[2] = None; /* our currently active window */ + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + XSendEvent (xdisplay, xroot, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); +} diff --git a/mate-panel/panel-util.h b/mate-panel/panel-util.h index 8d9fb2ef..fb1303a3 100644 --- a/mate-panel/panel-util.h +++ b/mate-panel/panel-util.h @@ -3,6 +3,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -66,6 +67,10 @@ void panel_util_set_tooltip_text (GtkWidget *widget, GFile *panel_util_get_file_optional_homedir (const char *location); +Window panel_util_get_current_active_window (GtkWidget *toplevel); + +void panel_util_set_current_active_window (GtkWidget *toplevel, + Window window); #ifdef __cplusplus } #endif -- cgit v1.2.1