Failed grab of GDK_SEAT_CAPABILITY_KEYBOARD hides window as a side effect
Steps to reproduce
- Compile the attached grab.c against a GTK+ version new enough to have
gdk_seat_grab
. - Run 'grab' without arguments, using the X11 GDK backend.
- (Should the program succeed, it will grab the keyboard, and keep it until its window is closed. But the observed behaviour is that it fails – see below – so that shouldn't be necessary.)
Description of test case
The program tries to grab the keyboard, using gdk_seat_grab
with parameter GDK_SEAT_CAPABILITY_KEYBOARD
. It's common (at least in X11) for another application's keyboard grab to be momentarily active, if the program trying to grab the keyboard is launched by a window-manager hot key, because handling those hot keys involves a temporary keyboard grab of its own. Anticipating this problem, the program detects a failed grab and retries a few times at 1/8 second intervals before giving up completely.
Current behavior
The initial grab seems to fail with GDK_GRAB_NOT_VIEWABLE
, which I guess is because I haven't used the right technique to wait for the X window to actually appear before attempting it. (I am also curious about how I should be doing that, but it's not my primary issue here.)
attempt #1: gdk_seat_grab returned 3
The program's window is then not visible. The next three attempts print
(grab:3272): Gdk-CRITICAL **: 13:15:35.998: Window 0x564aa95654b0 has not been made visible in GdkSeatGrabPrepareFunc
attempt #2: gdk_seat_grab returned 3
and the same with attempts 3 and 4. Then the program terminates when it runs out of attempts.
Expected outcome
After the initial grab failed because I tried it too early, I expected that the window would finish setting itself up, and the next attempt 1/8 second later would succeed.
Version information
I observed this on Ubuntu 18.04's package of libgtk-3-dev
, version 3.22.30-1ubuntu1.
Additional information
Source-diving in the GTK+ git repository, I find that in gdk_seat_default_grab()
there is a mysterious call to gdk_window_hide
/ gdk_surface_hide
(depending on version) which occurs specifically in the case of a failed grab of GDK_SEAT_CAPABILITY_KEYBOARD
:
if (status == GDK_GRAB_SUCCESS &&
capabilities & GDK_SEAT_CAPABILITY_KEYBOARD)
{
status = gdk_device_grab (priv->master_keyboard, surface,
GDK_OWNERSHIP_NONE, owner_events,
KEYBOARD_EVENTS, cursor,
evtime);
if (status != GDK_GRAB_SUCCESS)
{
if (capabilities & ~GDK_SEAT_CAPABILITY_KEYBOARD)
gdk_device_ungrab (priv->master_pointer, evtime);
gdk_surface_hide (surface);
}
}
Finding that gave me the idea of working around the issue by calling gdk_window_show
immediately after the grab fails. (The test program provides a --workaround
option to demonstrate this.) Then the second grab succeeds.
I suppose it's possible that this hiding behaviour is intentional for some reason I haven't thought of. But there's no comment in the code explaining it; I couldn't see anything in the documentation for gdk_seat_grab
that mentions any window-hiding behaviour or any special feature of GDK_SEAT_CAPABILITY_KEYBOARD
in particular; and I certainly can't think of a rationale for this oddity by myself.