wayland-server: Listen for pointer current surface destruction
authorRob Bradford <rob@linux.intel.com>
Wed, 3 Apr 2013 14:20:49 +0000 (15:20 +0100)
committerKristian Høgsberg <krh@bitplanet.net>
Wed, 3 Apr 2013 16:46:57 +0000 (12:46 -0400)
Add a destroy listener so that when the current surface associated with the
pointer is destroyed we can reset the pointer to the current surface. In order
to achieve this add a wl_pointer_set_current() which handles assigning the
surface and creating the listener.

This resolves a use-after-free error triggered with nested popup surfaces

Fixes: https://bugzilla.gnome.org/show_bug.cgi?id=696946

src/wayland-server.c
src/wayland-server.h

index 384b465..2109674 100644 (file)
@@ -946,6 +946,31 @@ wl_pointer_end_grab(struct wl_pointer *pointer)
                         pointer->current_x, pointer->current_y);
 }
 
+static void
+current_surface_destroy(struct wl_listener *listener, void *data)
+{
+       struct wl_pointer *pointer =
+               container_of(listener, struct wl_pointer, current_listener);
+
+       pointer->current = NULL;
+}
+
+WL_EXPORT void
+wl_pointer_set_current(struct wl_pointer *pointer, struct wl_surface *surface)
+{
+       if (pointer->current)
+               wl_list_remove(&pointer->current_listener.link);
+
+       pointer->current = surface;
+
+       if (!surface)
+               return;
+       
+       wl_signal_add(&surface->resource.destroy_signal,
+                     &pointer->current_listener);
+       pointer->current_listener.notify = current_surface_destroy;
+}
+
 WL_EXPORT void
 wl_touch_start_grab(struct wl_touch *touch, struct wl_touch_grab *grab)
 {
index e5a862a..af2be62 100644 (file)
@@ -312,6 +312,7 @@ struct wl_pointer {
 
        wl_fixed_t x, y;
        struct wl_surface *current;
+       struct wl_listener current_listener;
        wl_fixed_t current_x, current_y;
 
        uint32_t button_count;
@@ -450,6 +451,8 @@ wl_pointer_start_grab(struct wl_pointer *pointer,
                      struct wl_pointer_grab *grab);
 void
 wl_pointer_end_grab(struct wl_pointer *pointer);
+void
+wl_pointer_set_current(struct wl_pointer *pointer, struct wl_surface *surface);
 
 void
 wl_keyboard_init(struct wl_keyboard *keyboard);