From d3885e92a7db130d9686592f58386c9080f6eed9 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Thu, 19 Apr 2018 14:22:04 +0200 Subject: [PATCH] wayland: check native surface 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 surface, so if the grab is issued on a child gdk surface, those won't match and we would emit more focus_out events than focus_in. This means that a widget such as spice-gtk which issues a keyboard grab whenever the pointer enters the surface and releases the grab when it leaves the surface 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 surfaces 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 Closes https://gitlab.gnome.org/GNOME/gtk/issues/792 --- gdk/wayland/gdkdevice-wayland.c | 116 +++++++++++++++----------------- 1 file changed, 54 insertions(+), 62 deletions(-) diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c index edeb32f283..64551c46a3 100644 --- a/gdk/wayland/gdkdevice-wayland.c +++ b/gdk/wayland/gdkdevice-wayland.c @@ -712,6 +712,43 @@ gdk_wayland_device_get_focus (GdkDevice *device) return NULL; } +static void +device_maybe_emit_grab_crossing (GdkDevice *device, + GdkSurface *window, + guint32 time) +{ + GdkSurface *native = gdk_wayland_device_get_focus (device); + GdkSurface *focus = gdk_surface_get_toplevel (window); + + if (focus != native) + device_emit_grab_crossing (device, focus, window, GDK_CROSSING_GRAB, time); +} + +static GdkSurface* +device_maybe_emit_ungrab_crossing (GdkDevice *device, + guint32 time) +{ + GdkDeviceGrabInfo *grab; + GdkSurface *focus = NULL; + GdkSurface *native = NULL; + GdkSurface *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->surface; + native = grab->native_surface; + } + + 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, GdkSurface *surface, @@ -722,7 +759,6 @@ gdk_wayland_device_grab (GdkDevice *device, guint32 time_) { GdkWaylandSeat *wayland_seat = GDK_WAYLAND_SEAT (gdk_device_get_seat (device)); - GdkSurface *prev_focus = gdk_wayland_device_get_focus (device); GdkWaylandPointerData *pointer = GDK_WAYLAND_DEVICE (device)->pointer; if (gdk_surface_get_surface_type (surface) == GDK_SURFACE_TEMP && @@ -734,8 +770,7 @@ gdk_wayland_device_grab (GdkDevice *device, surface); } - if (prev_focus != surface) - device_emit_grab_crossing (device, prev_focus, surface, GDK_CROSSING_GRAB, time_); + device_maybe_emit_grab_crossing (device, surface, time_); if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) { @@ -776,24 +811,9 @@ gdk_wayland_device_ungrab (GdkDevice *device, guint32 time_) { GdkWaylandPointerData *pointer = GDK_WAYLAND_DEVICE (device)->pointer; - GdkDisplay *display; - GdkDeviceGrabInfo *grab; - GdkSurface *focus, *prev_focus = NULL; + GdkSurface *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->surface; - } - - 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) { @@ -4665,11 +4685,8 @@ gdk_wayland_seat_grab (GdkSeat *seat, if (wayland_seat->master_pointer && capabilities & GDK_SEAT_CAPABILITY_POINTER) { - GdkSurface *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, @@ -4690,11 +4707,8 @@ gdk_wayland_seat_grab (GdkSeat *seat, if (wayland_seat->touch_master && capabilities & GDK_SEAT_CAPABILITY_TOUCH) { - GdkSurface *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, @@ -4711,11 +4725,8 @@ gdk_wayland_seat_grab (GdkSeat *seat, if (wayland_seat->master_keyboard && capabilities & GDK_SEAT_CAPABILITY_KEYBOARD) { - GdkSurface *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, @@ -4739,11 +4750,8 @@ gdk_wayland_seat_grab (GdkSeat *seat, for (l = wayland_seat->tablets; l; l = l->next) { GdkWaylandTabletData *tablet = l->data; - GdkSurface *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, @@ -4777,36 +4785,20 @@ gdk_wayland_seat_ungrab (GdkSeat *seat) if (wayland_seat->master_pointer) { - GdkSurface *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->surface; - } - - 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_surface_cursor (wayland_seat->master_pointer); } if (wayland_seat->master_keyboard) { - grab = _gdk_display_get_last_device_grab (display, wayland_seat->master_keyboard); + GdkSurface *prev_focus; - if (grab) - { - grab->serial_end = grab->serial_start; - if (grab->surface) - gdk_wayland_surface_restore_shortcuts (grab->surface, seat); - } + prev_focus = device_maybe_emit_ungrab_crossing (wayland_seat->master_keyboard, + GDK_CURRENT_TIME); + if (prev_focus) + gdk_wayland_surface_restore_shortcuts (prev_focus, seat); } if (wayland_seat->touch_master)