wayland: check native window for crossing events

gdk_wayland_*_grab()/ungrab() would emit crossing events which translate
as focus_in/focus_out events for keyboard.

However, the ungrab() functions compare the native toplevel as this is
what gets the Wayland pointer enter/leave events with the grab window,
so if the grab is issued on a child gdk window, those won't match and we
would emit more focus_out events than focus_in events.

This means that a widget such as spice-gtk which issues a keyboard grab
whenever the pointer enters the window and releases the grab when it
leaves the window would get uneven numbers of focus_in/focus_out events.

Also, gdk_wayland_seat_ungrab() would not emit crossing events for
keyboard devices, whereas gdk_wayland_device_ungrab() does, which adds
even more potential discrepancies between focus_in/focus_out events.

To solve this problem, introduce two new helper functions which check
the relevant native windows to emit crossing events when needed that get
called evenly from both gdk_wayland_seat_grab()/ungrab() and gdk_Wayland
_device_grab()/ungrab() APIs.

Fixes: https://bugzilla.gnome.org/show_bug.cgi?id=780422
Fixes: https://gitlab.gnome.org/GNOME/gtk/issues/792
This commit is contained in:
Olivier Fourdan
2018-04-19 14:22:04 +02:00
committed by Daniel Boles
parent a7cd20823d
commit cc4dd0d343

View File

@ -741,6 +741,43 @@ gdk_wayland_device_get_focus (GdkDevice *device)
return NULL;
}
static void
device_maybe_emit_grab_crossing (GdkDevice *device,
GdkWindow *window,
guint32 time)
{
GdkWindow *native = gdk_wayland_device_get_focus (device);
GdkWindow *focus = gdk_window_get_toplevel (window);
if (focus != native)
device_emit_grab_crossing (device, focus, window, GDK_CROSSING_GRAB, time);
}
static GdkWindow*
device_maybe_emit_ungrab_crossing (GdkDevice *device,
guint32 time)
{
GdkDeviceGrabInfo *grab;
GdkWindow *focus = NULL;
GdkWindow *native = NULL;
GdkWindow *prev_focus = NULL;
focus = gdk_wayland_device_get_focus (device);
grab = _gdk_display_get_last_device_grab (gdk_device_get_display (device), device);
if (grab)
{
grab->serial_end = grab->serial_start;
prev_focus = grab->window;
native = grab->native_window;
}
if (focus != native)
device_emit_grab_crossing (device, prev_focus, focus, GDK_CROSSING_UNGRAB, time);
return prev_focus;
}
static GdkGrabStatus
gdk_wayland_device_grab (GdkDevice *device,
GdkWindow *window,
@ -751,7 +788,6 @@ gdk_wayland_device_grab (GdkDevice *device,
guint32 time_)
{
GdkWaylandSeat *wayland_seat = GDK_WAYLAND_SEAT (gdk_device_get_seat (device));
GdkWindow *prev_focus = gdk_wayland_device_get_focus (device);
GdkWaylandPointerData *pointer = GDK_WAYLAND_DEVICE (device)->pointer;
if (gdk_window_get_window_type (window) == GDK_WINDOW_TEMP &&
@ -763,8 +799,7 @@ gdk_wayland_device_grab (GdkDevice *device,
window);
}
if (prev_focus != window)
device_emit_grab_crossing (device, prev_focus, window, GDK_CROSSING_GRAB, time_);
device_maybe_emit_grab_crossing (device, window, time_);
if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
{
@ -809,24 +844,9 @@ gdk_wayland_device_ungrab (GdkDevice *device,
guint32 time_)
{
GdkWaylandPointerData *pointer = GDK_WAYLAND_DEVICE (device)->pointer;
GdkDisplay *display;
GdkDeviceGrabInfo *grab;
GdkWindow *focus, *prev_focus = NULL;
GdkWindow *prev_focus;
display = gdk_device_get_display (device);
grab = _gdk_display_get_last_device_grab (display, device);
if (grab)
{
grab->serial_end = grab->serial_start;
prev_focus = grab->window;
}
focus = gdk_wayland_device_get_focus (device);
if (focus != prev_focus)
device_emit_grab_crossing (device, prev_focus, focus, GDK_CROSSING_UNGRAB, time_);
prev_focus = device_maybe_emit_ungrab_crossing (device, time_);
if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
{
@ -4743,11 +4763,8 @@ gdk_wayland_seat_grab (GdkSeat *seat,
if (wayland_seat->master_pointer &&
capabilities & GDK_SEAT_CAPABILITY_POINTER)
{
GdkWindow *prev_focus = gdk_wayland_device_get_focus (wayland_seat->master_pointer);
if (prev_focus != native)
device_emit_grab_crossing (wayland_seat->master_pointer, prev_focus,
native, GDK_CROSSING_GRAB, evtime);
device_maybe_emit_grab_crossing (wayland_seat->master_pointer,
native, evtime);
_gdk_display_add_device_grab (display,
wayland_seat->master_pointer,
@ -4768,11 +4785,8 @@ gdk_wayland_seat_grab (GdkSeat *seat,
if (wayland_seat->touch_master &&
capabilities & GDK_SEAT_CAPABILITY_TOUCH)
{
GdkWindow *prev_focus = gdk_wayland_device_get_focus (wayland_seat->touch_master);
if (prev_focus != native)
device_emit_grab_crossing (wayland_seat->touch_master, prev_focus,
native, GDK_CROSSING_GRAB, evtime);
device_maybe_emit_grab_crossing (wayland_seat->touch_master,
native, evtime);
_gdk_display_add_device_grab (display,
wayland_seat->touch_master,
@ -4789,11 +4803,8 @@ gdk_wayland_seat_grab (GdkSeat *seat,
if (wayland_seat->master_keyboard &&
capabilities & GDK_SEAT_CAPABILITY_KEYBOARD)
{
GdkWindow *prev_focus = gdk_wayland_device_get_focus (wayland_seat->master_keyboard);
if (prev_focus != native)
device_emit_grab_crossing (wayland_seat->master_keyboard, prev_focus,
native, GDK_CROSSING_GRAB, evtime);
device_maybe_emit_grab_crossing (wayland_seat->master_keyboard,
native, evtime);
_gdk_display_add_device_grab (display,
wayland_seat->master_keyboard,
@ -4818,11 +4829,8 @@ gdk_wayland_seat_grab (GdkSeat *seat,
for (l = wayland_seat->tablets; l; l = l->next)
{
GdkWaylandTabletData *tablet = l->data;
GdkWindow *prev_focus = gdk_wayland_device_get_focus (tablet->master);
if (prev_focus != native)
device_emit_grab_crossing (tablet->master, prev_focus,
native, GDK_CROSSING_GRAB, evtime);
device_maybe_emit_grab_crossing (tablet->master, native, evtime);
_gdk_display_add_device_grab (display,
tablet->master,
@ -4856,36 +4864,20 @@ gdk_wayland_seat_ungrab (GdkSeat *seat)
if (wayland_seat->master_pointer)
{
GdkWindow *focus, *prev_focus = NULL;
grab = _gdk_display_get_last_device_grab (display, wayland_seat->master_pointer);
if (grab)
{
grab->serial_end = grab->serial_start;
prev_focus = grab->window;
}
focus = gdk_wayland_device_get_focus (wayland_seat->master_pointer);
if (focus != prev_focus)
device_emit_grab_crossing (wayland_seat->master_pointer, prev_focus,
focus, GDK_CROSSING_UNGRAB,
GDK_CURRENT_TIME);
device_maybe_emit_ungrab_crossing (wayland_seat->master_pointer,
GDK_CURRENT_TIME);
gdk_wayland_device_update_window_cursor (wayland_seat->master_pointer);
}
if (wayland_seat->master_keyboard)
{
grab = _gdk_display_get_last_device_grab (display, wayland_seat->master_keyboard);
GdkWindow *prev_focus;
if (grab)
{
grab->serial_end = grab->serial_start;
if (grab->window)
gdk_wayland_window_restore_shortcuts (grab->window, seat);
}
prev_focus = device_maybe_emit_ungrab_crossing (wayland_seat->master_keyboard,
GDK_CURRENT_TIME);
if (prev_focus)
gdk_wayland_window_restore_shortcuts (prev_focus, seat);
}
if (wayland_seat->touch_master)