Skip to content

Commit f0f4c5e

Browse files
sthibaullukefromdc
authored andcommitted
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
1 parent 4c30c52 commit f0f4c5e

File tree

4 files changed

+141
-0
lines changed

4 files changed

+141
-0
lines changed

‎mate-panel/panel-menu-bar.c‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828

2929
#include "panel-menu-bar.h"
3030

31+
#include <X11/Xlib.h>
32+
#include <gdk/gdkx.h>
33+
3134
#include <string.h>
3235
#include <glib/gi18n.h>
3336

@@ -65,6 +68,8 @@ struct _PanelMenuBarPrivate {
6568
GSettings* settings;
6669

6770
PanelOrientation orientation;
71+
72+
Window interrupted_window;
6873
};
6974

7075
enum {
@@ -83,6 +88,15 @@ static gboolean panel_menu_bar_reinit_tooltip(GtkWidget* widget, PanelMenuBar* m
8388
return FALSE;
8489
}
8590

91+
static gboolean panel_menu_bar_deactivate (GtkWidget* widget, PanelMenuBar* menubar)
92+
{
93+
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
94+
panel_util_set_current_active_window (toplevel, menubar->priv->interrupted_window);
95+
menubar->priv->interrupted_window = None;
96+
97+
return FALSE;
98+
}
99+
86100
static gboolean panel_menu_bar_hide_tooltip_and_focus(GtkWidget* widget, PanelMenuBar* menubar)
87101
{
88102
/* 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)
108122

109123
/* Reset tooltip when the menu bar is not used */
110124
g_signal_connect(GTK_MENU_SHELL (menubar), "deactivate", G_CALLBACK (panel_menu_bar_reinit_tooltip), menubar);
125+
g_signal_connect(GTK_MENU_SHELL (menubar), "deactivate", G_CALLBACK (panel_menu_bar_deactivate), menubar);
111126
}
112127

113128
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)
411426
{
412427
GtkMenu* menu;
413428
GtkMenuShell* menu_shell;
429+
GtkWidget* toplevel;
430+
GdkWindow* window;
414431

415432
g_return_if_fail(PANEL_IS_MENU_BAR(menubar));
416433

417434
menu = GTK_MENU(menubar->priv->applications_menu);
418435

436+
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menubar));
437+
menubar->priv->interrupted_window = panel_util_get_current_active_window (toplevel);
438+
window = gtk_widget_get_window (toplevel);
439+
panel_util_set_current_active_window (toplevel, GDK_WINDOW_XID(window));
440+
419441
/*
420442
* We need to call _gtk_menu_shell_activate() here as is done in
421443
* window_key_press_handler in gtkmenubar.c which pops up menu

‎mate-panel/panel-menu-button.c‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@
2626

2727
#include "panel-menu-button.h"
2828

29+
#include <X11/Xlib.h>
30+
2931
#include <string.h>
3032
#include <glib/gi18n.h>
3133
#include <gio/gio.h>
34+
#include <gdk/gdkx.h>
3235

3336
#include <matemenu-tree.h>
3437

@@ -92,6 +95,8 @@ struct _PanelMenuButtonPrivate {
9295
char *custom_icon;
9396
char *tooltip;
9497

98+
Window interrupted_window;
99+
95100
MenuPathRoot path_root;
96101
guint use_menu_path : 1;
97102
guint use_custom_icon : 1;
@@ -437,12 +442,23 @@ panel_menu_button_recreate_menu (PanelMenuButton *button)
437442
button->priv->menu = NULL;
438443
}
439444

445+
static gboolean panel_menu_button_menu_deactivate (GtkWidget* widget, PanelMenuButton* button)
446+
{
447+
GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
448+
panel_util_set_current_active_window(toplevel, button->priv->interrupted_window);
449+
button->priv->interrupted_window = None;
450+
451+
return FALSE;
452+
}
453+
440454
void
441455
panel_menu_button_popup_menu (PanelMenuButton *button,
442456
guint n_button,
443457
guint32 activate_time)
444458
{
445459
GdkScreen *screen;
460+
GtkWidget *toplevel;
461+
GdkWindow *window;
446462

447463
g_return_if_fail (PANEL_IS_MENU_BUTTON (button));
448464

@@ -460,6 +476,12 @@ panel_menu_button_popup_menu (PanelMenuButton *button,
460476
GDK_GRAVITY_SOUTH_WEST,
461477
GDK_GRAVITY_NORTH_WEST,
462478
NULL);
479+
480+
g_signal_connect(GTK_MENU_SHELL (button->priv->menu), "deactivate", G_CALLBACK (panel_menu_button_menu_deactivate), button);
481+
toplevel = gtk_widget_get_toplevel(GTK_WIDGET(button->priv->toplevel));
482+
button->priv->interrupted_window = panel_util_get_current_active_window (toplevel);
483+
window = gtk_widget_get_window (toplevel);
484+
panel_util_set_current_active_window (toplevel, GDK_WINDOW_XID(window));
463485
}
464486

465487
static void

‎mate-panel/panel-util.c‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@
2323
#include <unistd.h>
2424
#include <sys/types.h>
2525

26+
#include <X11/Xlib.h>
27+
#include <X11/Xatom.h>
28+
2629
#include <glib.h>
2730
#include <glib/gi18n.h>
2831
#include <glib/gstdio.h>
2932
#include <gio/gio.h>
3033
#include <gdk-pixbuf/gdk-pixbuf.h>
34+
#include <gdk/gdkx.h>
3135

3236
#define MATE_DESKTOP_USE_UNSTABLE_API
3337
#include <libmate-desktop/mate-desktop-utils.h>
@@ -47,6 +51,8 @@
4751
#include "panel-icon-names.h"
4852
#include "panel-lockdown.h"
4953

54+
static Atom _net_active_window = None;
55+
5056
char *
5157
panel_util_make_exec_uri_for_desktop (const char *exec)
5258
{
@@ -1241,3 +1247,89 @@ panel_util_get_file_optional_homedir (const char *location)
12411247

12421248
return file;
12431249
}
1250+
1251+
static void panel_menu_bar_get_net_active_window(Display *xdisplay)
1252+
{
1253+
if (_net_active_window == None)
1254+
_net_active_window = XInternAtom (xdisplay,
1255+
"_NET_ACTIVE_WINDOW",
1256+
False);
1257+
}
1258+
1259+
Window panel_util_get_current_active_window (GtkWidget *toplevel)
1260+
{
1261+
GdkScreen *screen;
1262+
GdkDisplay *display;
1263+
GdkWindow *root;
1264+
Display *xdisplay;
1265+
Window xroot;
1266+
1267+
Window res = None;
1268+
1269+
Atom return_type;
1270+
int return_format;
1271+
unsigned long n;
1272+
unsigned long bytes;
1273+
unsigned char *prop = NULL;
1274+
1275+
screen = gtk_window_get_screen (GTK_WINDOW(toplevel));
1276+
display = gdk_screen_get_display (screen);
1277+
root = gdk_screen_get_root_window (screen);
1278+
1279+
xdisplay = GDK_DISPLAY_XDISPLAY (display);
1280+
xroot = GDK_WINDOW_XID (root);
1281+
1282+
panel_menu_bar_get_net_active_window (xdisplay);
1283+
if (_net_active_window != None
1284+
&& XGetWindowProperty (xdisplay, xroot, _net_active_window, 0, 1,
1285+
False, XA_WINDOW, &return_type, &return_format,
1286+
&n, &bytes, &prop) == Success)
1287+
{
1288+
if ((return_type == XA_WINDOW) && (return_format == 32) &&
1289+
(n == 1) && (prop)) {
1290+
res = *(Window *)prop;
1291+
}
1292+
1293+
if (prop)
1294+
XFree (prop);
1295+
1296+
}
1297+
return res;
1298+
}
1299+
1300+
void panel_util_set_current_active_window (GtkWidget *toplevel, Window window)
1301+
{
1302+
GdkScreen *screen;
1303+
GdkDisplay *display;
1304+
GdkWindow *root;
1305+
Display *xdisplay;
1306+
Window xroot;
1307+
XEvent xev;
1308+
1309+
screen = gtk_window_get_screen (GTK_WINDOW(toplevel));
1310+
display = gdk_screen_get_display (screen);
1311+
root = gdk_screen_get_root_window (screen);
1312+
1313+
xdisplay = GDK_DISPLAY_XDISPLAY (display);
1314+
xroot = GDK_WINDOW_XID (root);
1315+
1316+
panel_menu_bar_get_net_active_window (xdisplay);
1317+
if (_net_active_window == None)
1318+
return;
1319+
1320+
xev.xclient.type = ClientMessage;
1321+
xev.xclient.serial = 0;
1322+
xev.xclient.send_event = True;
1323+
xev.xclient.window = window;
1324+
xev.xclient.message_type = _net_active_window;
1325+
xev.xclient.format = 32;
1326+
xev.xclient.data.l[0] = 2; /* requestor type; we're not an app */
1327+
xev.xclient.data.l[1] = CurrentTime;
1328+
xev.xclient.data.l[2] = None; /* our currently active window */
1329+
xev.xclient.data.l[3] = 0;
1330+
xev.xclient.data.l[4] = 0;
1331+
1332+
XSendEvent (xdisplay, xroot, False,
1333+
SubstructureRedirectMask | SubstructureNotifyMask,
1334+
&xev);
1335+
}

‎mate-panel/panel-util.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <gio/gio.h>
55
#include <gtk/gtk.h>
6+
#include <X11/Xlib.h>
67

78
#ifdef __cplusplus
89
extern "C" {
@@ -66,6 +67,10 @@ void panel_util_set_tooltip_text (GtkWidget *widget,
6667

6768
GFile *panel_util_get_file_optional_homedir (const char *location);
6869

70+
Window panel_util_get_current_active_window (GtkWidget *toplevel);
71+
72+
void panel_util_set_current_active_window (GtkWidget *toplevel,
73+
Window window);
6974
#ifdef __cplusplus
7075
}
7176
#endif

0 commit comments

Comments
 (0)