input: Don't send leave events to destroyed views
authorEmilio Pozuelo Monfort <pochu27@gmail.com>
Tue, 19 Nov 2013 10:37:15 +0000 (11:37 +0100)
committerKristian Høgsberg <krh@bitplanet.net>
Tue, 19 Nov 2013 19:49:40 +0000 (11:49 -0800)
If a view which has focus is destroyed, we would send a leave
event while changing focus, causing a segfault. Prevent this
by listening to the view's destroy signal and removing it from
the pointer focus.

Signed-off-by: Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
src/compositor.h
src/input.c

index a8504af..b84289a 100644 (file)
@@ -308,6 +308,7 @@ struct weston_pointer {
        struct wl_list focus_resource_list;
        struct weston_view *focus;
        uint32_t focus_serial;
+       struct wl_listener focus_listener;
        struct wl_signal focus_signal;
        struct wl_signal motion_signal;
 
index fc93f88..7480374 100644 (file)
@@ -395,8 +395,9 @@ weston_pointer_create(struct weston_seat *seat)
                                        seat->compositor->default_pointer_grab);
        pointer->default_grab.pointer = pointer;
        pointer->grab = &pointer->default_grab;
-       wl_signal_init(&pointer->focus_signal);
        wl_signal_init(&pointer->motion_signal);
+       wl_signal_init(&pointer->focus_signal);
+       wl_list_init(&pointer->focus_listener.link);
 
        pointer->sprite_destroy_listener.notify = pointer_handle_sprite_destroy;
 
@@ -503,6 +504,23 @@ seat_send_updated_caps(struct weston_seat *seat)
        }
 }
 
+static void
+destroy_pointer_focus(struct wl_listener *listener, void *data)
+{
+       struct weston_pointer *pointer;
+
+       pointer = container_of(listener, struct weston_pointer,
+                              focus_listener);
+
+       pointer->focus = NULL;
+       move_resources(&pointer->resource_list, &pointer->focus_resource_list);
+
+       wl_list_remove(&pointer->focus_listener.link);
+       wl_list_init(&pointer->focus_listener.link);
+
+       wl_signal_emit(&pointer->focus_signal, pointer);
+}
+
 WL_EXPORT void
 weston_pointer_set_focus(struct weston_pointer *pointer,
                         struct weston_view *view,
@@ -559,7 +577,14 @@ weston_pointer_set_focus(struct weston_pointer *pointer,
                pointer->focus_serial = serial;
        }
 
+       if (!wl_list_empty(&pointer->focus_listener.link)) {
+               wl_list_remove(&pointer->focus_listener.link);
+               wl_list_init(&pointer->focus_listener.link);
+       }
        pointer->focus = view;
+       pointer->focus_listener.notify = destroy_pointer_focus;
+       if (view)
+               wl_signal_add(&view->destroy_signal, &pointer->focus_listener);
        wl_signal_emit(&pointer->focus_signal, pointer);
 }