Use a transient object for the dnd session
authorKristian Høgsberg <krh@bitplanet.net>
Fri, 3 Sep 2010 00:22:42 +0000 (20:22 -0400)
committerKristian Høgsberg <krh@bitplanet.net>
Fri, 3 Sep 2010 00:22:42 +0000 (20:22 -0400)
TODO
clients/dnd.c
clients/window.c
clients/window.h
compositor.c
compositor.h
protocol.xml
scanner.c
wayland-client.c
wayland-server.h

diff --git a/TODO b/TODO
index 8f8486a..fe9b015 100644 (file)
--- a/TODO
+++ b/TODO
@@ -27,18 +27,6 @@ Core wayland protocol
       send a NULL type or x-wayland/root-something type if the source
       offers that.
 
-   Races between pointer motion, ending the drag, the target sending
-   accept request and the source receiving the target event.
-
-    - We've sent a drag focus or motion event to the source, but
-      haven't received an accept request corresponding to that event
-      and now the button is release.  The compositor could wait for
-      the source to reply to outstanding focus/motion events before
-      sending the finish event to the source.  Or we could send the
-      finish event through the source so that it needs to reply to the
-      finish event too.  Either way, the state of the drag blocks on
-      the client.  What if we drag to a client that doesn't doo dnd?
-
    How do we animate the drag icon back to the drag origin in case of
    a failed drag?
 
@@ -48,18 +36,6 @@ Core wayland protocol
    that will participate in dnd.  Or just assume client is not
    participating until we receive an accept request.
 
-   May need to look at all offer events before we can decide which one
-   to go with.  Problem is, we don't know when we've seen that last
-   offer event.
-
-   Create transient dnd object when a client starts a drag.  Announce
-   the dnd object to clients first time the drag enters one of its
-   surfaces.  Track if we've already announced the object by comparing
-   the drag start timestamp/serial with the clients last-event
-   timestamp/serial?  Wont work if we send other events to a client
-   after creating the drag object.  Maybe just keep the transient
-   object on the initiator side?
-
  - Pointer image issue:
 
     - A touch input device doesn't have a pointer; indicate that
index a53caa2..df4d8c5 100644 (file)
@@ -44,12 +44,21 @@ struct dnd {
        struct display *display;
        uint32_t key;
        struct item *items[16];
+};
 
-       struct wl_buffer *translucent_buffer;
-       struct wl_buffer *opaque_buffer;
+struct dnd_drag {
+       cairo_surface_t *translucent;
+       cairo_surface_t *opaque;
        int hotspot_x, hotspot_y;
-       uint32_t tag;
+       struct dnd *dnd;
+       struct input *input;
+};
+
+struct dnd_offer {
+       struct dnd *dnd;
+       struct wl_array types;
        const char *drag_type;
+       uint32_t tag;
 };
 
 struct item {
@@ -108,7 +117,7 @@ item_create(struct display *display, int x, int y)
                cairo_curve_to(cr,
                               x1 - y1 * u, y1 + x1 * u,
                               x2 + y2 * v, y2 - x2 * v,
-                              x2, y2);                        
+                              x2, y2);
 
                cairo_curve_to(cr,
                               x2 - y2 * v, y2 + x2 * v,
@@ -219,90 +228,130 @@ dnd_get_item(struct dnd *dnd, int32_t x, int32_t y)
 }
 
 static void
-drag_handle_device(void *data,
-                  struct wl_drag *drag, struct wl_input_device *device)
+drag_target(void *data,
+           struct wl_drag *drag, const char *mime_type)
 {
+       struct dnd_drag *dnd_drag = data;
+       struct dnd *dnd = dnd_drag->dnd;
+       struct wl_input_device *device;
+       cairo_surface_t *surface;
+       struct wl_buffer *buffer;
+
+       fprintf(stderr, "target %s\n", mime_type);
+       device = input_get_input_device(dnd_drag->input);
+       if (mime_type)
+               surface = dnd_drag->opaque;
+       else
+               surface = dnd_drag->translucent;
+
+       buffer = display_get_buffer_for_surface(dnd->display, surface);
+       wl_input_device_attach(device, buffer,
+                              dnd_drag->hotspot_x, dnd_drag->hotspot_y);
 }
 
 static void
-drag_pointer_focus(void *data,
-                  struct wl_drag *drag,
-                  uint32_t time, struct wl_surface *surface,
-                  int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
+drag_finish(void *data, struct wl_drag *drag, int fd)
 {
-       struct dnd *dnd = data;
-
-       /* FIXME: We need the offered types before we get the
-        * pointer_focus event so we know which one we want and can
-        * send the accept request back. */
+       struct dnd_drag *dnd_drag = data;
+       char text[] = "[drop data]";
 
-       fprintf(stderr, "drag pointer focus %p\n", surface);
+       fprintf(stderr, "got 'finish', fd %d, sending message\n", fd);
 
-       if (!surface) {
-               dnd->drag_type = NULL;
-               return;
-       }
+       write(fd, text, sizeof text);
+       close(fd);
 
-       if (!dnd_get_item(dnd, surface_x, surface_y)) {
-               wl_drag_accept(drag, "text/plain");
-               dnd->drag_type = "text/plain";
-       } else {
-               wl_drag_accept(drag, NULL);
-               dnd->drag_type = NULL;
-       }
+       /* The 'finish' event marks the end of the session on the drag
+        * source side and we need to clean up the drag object created
+        * and the local state. */
+       wl_drag_destroy(drag);
+       cairo_surface_destroy(dnd_drag->translucent);
+       cairo_surface_destroy(dnd_drag->opaque);
+       free(dnd_drag);
 }
 
+static const struct wl_drag_listener drag_listener = {
+       drag_target,
+       drag_finish
+};
+
 static void
-drag_offer(void *data,
-          struct wl_drag *drag, const char *type)
+drag_offer_offer(void *data,
+                struct wl_drag_offer *offer, const char *type)
 {
-       fprintf(stderr, "drag offer %s\n", type);
+       struct dnd_offer *dnd_offer = data;
+       char **p;
+
+       p = wl_array_add(&dnd_offer->types, sizeof *p);
+       if (p)
+               *p = strdup(type);
 }
 
 static void
-drag_motion(void *data,
-           struct wl_drag *drag,
-           uint32_t time,
-           int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
+drag_offer_pointer_focus(void *data,
+                        struct wl_drag_offer *offer,
+                        uint32_t time, struct wl_surface *surface,
+                        int32_t x, int32_t y,
+                        int32_t surface_x, int32_t surface_y)
 {
-       struct dnd *dnd = data;
+       struct dnd_offer *dnd_offer = data;
+       struct window *window;
+       char **p, **end;
 
-       /* FIXME: Need to correlate this with the offer event.
-        * Problem is, we don't know when we've seen that last offer
-        * event, and we might need to look at all of them before we
-        * can decide which one to go with. */
-       if (!dnd_get_item(dnd, surface_x, surface_y)) {
-               wl_drag_accept(drag, "text/plain");
-               dnd->drag_type = "text/plain";
+       /* The last event in a dnd session is pointer_focus with a
+        * NULL surface, whether or not we get the drop event.  We
+        * need to clean up the dnd_offer proxy and whatever state we
+        * allocated. */
+       if (!surface) {
+               fprintf(stderr, "pointer focus NULL, session over\n");
+               wl_array_release(&dnd_offer->types);
+               free(dnd_offer);
+               wl_drag_offer_destroy(offer);
+               return;
+       }
+
+       fprintf(stderr, "drag pointer focus %p\n", surface);
+       fprintf(stderr, "offered types:\n");
+       end = dnd_offer->types.data + dnd_offer->types.size;
+       for (p = dnd_offer->types.data; p < end; p++)
+               fprintf(stderr, "\%s\n", *p);
+
+       window = wl_surface_get_user_data(surface);
+       dnd_offer->dnd = window_get_user_data(window);
+
+       if (!dnd_get_item(dnd_offer->dnd, surface_x, surface_y)) {
+               wl_drag_offer_accept(offer, time, "text/plain");
+               dnd_offer->drag_type = "text/plain";
        } else {
-               wl_drag_accept(drag, NULL);
-               dnd->drag_type = NULL;
+               wl_drag_offer_accept(offer, time, NULL);
+               dnd_offer->drag_type = NULL;
        }
 }
 
 static void
-drag_target(void *data,
-           struct wl_drag *drag, const char *mime_type)
+drag_offer_motion(void *data,
+                 struct wl_drag_offer *offer, uint32_t time,
+                 int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
 {
-       struct dnd *dnd = data;
-       struct input *input;
-       struct wl_input_device *device;
+       struct dnd_offer *dnd_offer = data;
+       struct dnd *dnd = dnd_offer->dnd;
 
-       fprintf(stderr, "target %s\n", mime_type);
-       input = wl_drag_get_user_data(drag);
-       device = input_get_input_device(input);
-       if (mime_type)
-               wl_input_device_attach(device, dnd->opaque_buffer,
-                                      dnd->hotspot_x, dnd->hotspot_y);
-       else
-               wl_input_device_attach(device, dnd->translucent_buffer,
-                                      dnd->hotspot_x, dnd->hotspot_y);
+       if (!dnd_get_item(dnd, surface_x, surface_y)) {
+               fprintf(stderr, "drag offer motion %d, %d, accepting\n",
+                       surface_x, surface_y);
+               wl_drag_offer_accept(offer, time, "text/plain");
+               dnd_offer->drag_type = "text/plain";
+       } else {
+               fprintf(stderr, "drag offer motion %d, %d, declining\n",
+                       surface_x, surface_y);
+               wl_drag_offer_accept(offer, time, NULL);
+               dnd_offer->drag_type = NULL;
+       }
 }
 
 static gboolean
 drop_io_func(GIOChannel *source, GIOCondition condition, gpointer data)
 {
-       struct dnd *dnd = data;
+       struct dnd_offer *dnd_offer = data;
        char buffer[256];
        int fd;
        unsigned int len;
@@ -312,7 +361,7 @@ drop_io_func(GIOChannel *source, GIOCondition condition, gpointer data)
        fprintf(stderr, "read %d bytes: %s\n", len, buffer);
        fd = g_io_channel_unix_get_fd(source);
        close(fd);
-       g_source_remove(dnd->tag);
+       g_source_remove(dnd_offer->tag);
 
        g_io_channel_unref(source);
 
@@ -320,13 +369,13 @@ drop_io_func(GIOChannel *source, GIOCondition condition, gpointer data)
 }
 
 static void
-drag_drop(void *data, struct wl_drag *drag)
+drag_offer_drop(void *data, struct wl_drag_offer *offer)
 {
-       struct dnd *dnd = data;
-       int p[2];
+       struct dnd_offer *dnd_offer = data;
        GIOChannel *channel;
+       int p[2];
 
-       if (!dnd->drag_type) {
+       if (!dnd_offer->drag_type) {
                fprintf(stderr, "got 'drop', but no target\n");
                /* FIXME: Should send response so compositor and
                 * source knows it's over. Can't send -1 to indicate
@@ -338,38 +387,39 @@ drag_drop(void *data, struct wl_drag *drag)
        fprintf(stderr, "got 'drop', sending write end of pipe\n");
 
        pipe(p);
-       wl_drag_receive(drag, p[1]);
+       wl_drag_offer_receive(offer, p[1]);
        close(p[1]);
 
        channel = g_io_channel_unix_new(p[0]);
-       dnd->tag = g_io_add_watch(channel, G_IO_IN, drop_io_func, dnd);
+       dnd_offer->tag = g_io_add_watch(channel, G_IO_IN,
+                                       drop_io_func, dnd_offer);
 }
 
+static const struct wl_drag_offer_listener drag_offer_listener = {
+       drag_offer_offer,
+       drag_offer_pointer_focus,
+       drag_offer_motion,
+       drag_offer_drop,
+};
+
 static void
-drag_finish(void *data, struct wl_drag *drag, int fd)
+drag_offer_handler(struct wl_drag_offer *offer, struct display *display)
 {
-       char text[] = "[drop data]";
+       struct dnd_offer *dnd_offer;
 
-       fprintf(stderr, "got 'finish', fd %d, sending message\n", fd);
+       dnd_offer = malloc(sizeof *dnd_offer);
+       if (dnd_offer == NULL)
+               return;
 
-       write(fd, text, sizeof text);
-       close(fd);
+       wl_drag_offer_add_listener(offer, &drag_offer_listener, dnd_offer);
+       wl_array_init(&dnd_offer->types);
 }
 
-static const struct wl_drag_listener drag_listener = {
-       drag_handle_device,
-       drag_pointer_focus,
-       drag_offer,
-       drag_motion,
-       drag_target,
-       drag_drop,
-       drag_finish
-};
-
 static cairo_surface_t *
-create_drag_cursor(struct dnd *dnd, struct item *item, int32_t x, int32_t y,
-                  double opacity)
+create_drag_cursor(struct dnd_drag *dnd_drag,
+                  struct item *item, int32_t x, int32_t y, double opacity)
 {
+       struct dnd *dnd = dnd_drag->dnd;
        cairo_surface_t *surface, *pointer;
        int32_t pointer_width, pointer_height, hotspot_x, hotspot_y;
        struct rectangle rectangle;
@@ -409,8 +459,8 @@ create_drag_cursor(struct dnd *dnd, struct item *item, int32_t x, int32_t y,
        display_flush_cairo_device(dnd->display);
        cairo_destroy(cr);
 
-       dnd->hotspot_x = pointer_width + x - item->x;
-       dnd->hotspot_y = pointer_height + y - item->y;
+       dnd_drag->hotspot_x = pointer_width + x - item->x;
+       dnd_drag->hotspot_y = pointer_height + y - item->y;
 
        return surface;
 }
@@ -423,8 +473,8 @@ dnd_button_handler(struct window *window,
        struct dnd *dnd = data;
        int32_t x, y;
        struct item *item;
-       cairo_surface_t *surface;
        struct rectangle rectangle;
+       struct dnd_drag *dnd_drag;
 
        window_get_child_rectangle(dnd->window, &rectangle);
        input_get_position(input, &x, &y);
@@ -435,18 +485,17 @@ dnd_button_handler(struct window *window,
        if (item && state == 1) {
                fprintf(stderr, "start drag, item %p\n", item);
 
-               surface = create_drag_cursor(dnd, item, x, y, 1);
-               dnd->opaque_buffer =
-                       display_get_buffer_for_surface(dnd->display, surface);
-
-               surface = create_drag_cursor(dnd, item, x, y, 0.2);
-               dnd->translucent_buffer =
-                       display_get_buffer_for_surface(dnd->display, surface);
+               dnd_drag = malloc(sizeof *dnd_drag);
+               dnd_drag->dnd = dnd;
+               dnd_drag->input = input;
 
-               window_start_drag(window, input, time);
+               dnd_drag->opaque =
+                       create_drag_cursor(dnd_drag, item, x, y, 1);
+               dnd_drag->translucent =
+                       create_drag_cursor(dnd_drag, item, x, y, 0.2);
 
-               /* FIXME: We leak the surface because we can't free it
-                * until the server has referenced it. */
+               window_start_drag(window, input, time,
+                                 &drag_listener, dnd_drag);
        }
 }
 
@@ -509,8 +558,6 @@ dnd_create(struct display *display)
        rectangle.height = 4 * (item_height + item_padding) + item_padding;
        window_set_child_size(dnd->window, &rectangle);
 
-       display_add_drag_listener(display, &drag_listener, dnd);
-
        dnd_draw(dnd);
 
        return dnd;
@@ -532,6 +579,8 @@ main(int argc, char *argv[])
 
        d = display_create(&argc, &argv, option_entries);
 
+       display_set_drag_offer_handler(d, drag_offer_handler);
+
        dnd = dnd_create (d);
 
        display_run(d);
index b6074e5..5a2f247 100644 (file)
@@ -71,6 +71,8 @@ struct display {
        cairo_surface_t *active_frame, *inactive_frame, *shadow;
        struct xkb_desc *xkb;
        cairo_surface_t **pointer_surfaces;
+
+       display_drag_offer_handler_t drag_offer_handler;
 };
 
 struct window {
@@ -107,7 +109,6 @@ struct window {
 struct input {
        struct display *display;
        struct wl_input_device *input_device;
-       struct wl_drag *drag;
        struct window *pointer_focus;
        struct window *keyboard_focus;
        uint32_t modifiers;
@@ -744,25 +745,21 @@ input_get_input_device(struct input *input)
        return input->input_device;
 }
 
-void
-display_add_drag_listener(struct display *display,
-                         const struct wl_drag_listener *drag_listener,
-                         void *data)
+struct wl_drag *
+window_start_drag(struct window *window, struct input *input, uint32_t time,
+                 const struct wl_drag_listener *listener, void *data)
 {
-       struct input *input;
-
-       wl_list_for_each(input, &display->input_list, link)
-               wl_drag_add_listener(input->drag, drag_listener, data);
-}
+       struct wl_drag *drag;
 
-void
-window_start_drag(struct window *window, struct input *input, uint32_t time)
-{
        cairo_device_flush (window->display->device);
 
-       wl_drag_prepare(input->drag, window->surface, time);
-       wl_drag_offer(input->drag, "text/plain");
-       wl_drag_activate(input->drag);
+       drag = wl_shell_create_drag(window->display->shell);
+       wl_drag_offer(drag, "text/plain");
+       wl_drag_offer(drag, "text/html");
+       wl_drag_activate(drag, window->surface, input->input_device, time);
+       wl_drag_add_listener(drag, listener, data);
+
+       return drag;
 }
 
 static void
@@ -902,6 +899,12 @@ window_set_user_data(struct window *window, void *data)
        window->user_data = data;
 }
 
+void *
+window_get_user_data(struct window *window)
+{
+       return window->user_data;
+}
+
 void
 window_set_resize_handler(struct window *window,
                          window_resize_handler_t handler)
@@ -1100,70 +1103,11 @@ display_add_input(struct display *d, uint32_t id)
 }
 
 static void
-drag_handle_device(void *data,
-                  struct wl_drag *drag, struct wl_input_device *device)
-{
-       struct input *input;
-
-       input = wl_input_device_get_user_data(device);
-       input->drag = drag;
-       wl_drag_set_user_data(drag, input);
-}
-
-static void
-drag_pointer_focus(void *data,
-                  struct wl_drag *drag,
-                  uint32_t time, struct wl_surface *surface,
-                  int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
-{
-}
-
-static void
-drag_offer(void *data,
-          struct wl_drag *drag, const char *type)
-{
-}
-
-static void
-drag_motion(void *data,
-           struct wl_drag *drag,
-           uint32_t time,
-           int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
-{
-}
-
-static void
-drag_target(void *data,
-           struct wl_drag *drag, const char *mime_type)
-{
-}
-
-static void
-drag_drop(void *data, struct wl_drag *drag)
-{
-}
-
-static void
-drag_finish(void *data, struct wl_drag *drag, int fd)
-{
-}
-
-static const struct wl_drag_listener drag_listener = {
-       drag_handle_device,
-       drag_pointer_focus,
-       drag_offer,
-       drag_motion,
-       drag_target,
-       drag_drop,
-       drag_finish
-};
-
-static void
 display_handle_global(struct wl_display *display, uint32_t id,
                      const char *interface, uint32_t version, void *data)
 {
        struct display *d = data;
-       struct wl_drag *drag;
+       struct wl_drag_offer *offer;
 
        if (strcmp(interface, "compositor") == 0) {
                d->compositor = wl_compositor_create(display, id);
@@ -1180,9 +1124,9 @@ display_handle_global(struct wl_display *display, uint32_t id,
        } else if (strcmp(interface, "drm") == 0) {
                d->drm = wl_drm_create(display, id);
                wl_drm_add_listener(d->drm, &drm_listener, d);
-       } else if (strcmp(interface, "drag") == 0) {
-               drag = wl_drag_create(display, id);
-               wl_drag_add_listener(drag, &drag_listener, NULL);
+       } else if (strcmp(interface, "drag_offer") == 0) {
+               offer = wl_drag_offer_create(display, id);
+               d->drag_offer_handler(offer, d);
        }
 }
 
@@ -1363,3 +1307,10 @@ display_run(struct display *d)
 {
        g_main_loop_run(d->loop);
 }
+
+void
+display_set_drag_offer_handler(struct display *display,
+                              display_drag_offer_handler_t handler)
+{
+       display->drag_offer_handler = handler;
+}
index f9da805..1b4285f 100644 (file)
@@ -111,6 +111,9 @@ typedef int (*window_motion_handler_t)(struct window *window,
                                       int32_t x, int32_t y,
                                       int32_t sx, int32_t sy, void *data);
 
+typedef void (*display_drag_offer_handler_t)(struct wl_drag_offer *offer,
+                                            struct display *display);
+
 struct window *
 window_create(struct display *display, const char *title,
              int32_t x, int32_t y, int32_t width, int32_t height);
@@ -149,6 +152,9 @@ window_set_fullscreen(struct window *window, int fullscreen);
 void
 window_set_user_data(struct window *window, void *data);
 
+void *
+window_get_user_data(struct window *window);
+
 void
 window_set_redraw_handler(struct window *window,
                          window_redraw_handler_t handler);
@@ -191,7 +197,13 @@ window_set_frame_handler(struct window *window,
                         window_frame_handler_t handler);
 
 void
-window_start_drag(struct window *window, struct input *input, uint32_t time);
+display_set_drag_offer_handler(struct display *display,
+                              display_drag_offer_handler_t handler);
+
+struct wl_drag *
+window_start_drag(struct window *window, struct input *input, uint32_t time,
+                 const struct wl_drag_listener *listener, void *data);
+
 
 void
 input_get_position(struct input *input, int32_t *x, int32_t *y);
index 9c8d5b5..32f9d11 100644 (file)
@@ -573,9 +573,49 @@ shell_resize(struct wl_client *client, struct wl_shell *shell,
        wlsc_input_device_set_pointer_image(wd, pointer);
 }
 
+static void
+destroy_drag(struct wl_resource *resource, struct wl_client *client)
+{
+       struct wl_drag *drag =
+               container_of(resource, struct wl_drag, resource);
+
+       /* FIXME: More stuff */
+
+       free(drag);
+}
+
+const static struct wl_drag_interface drag_interface;
+
+static void
+shell_create_drag(struct wl_client *client,
+                 struct wl_shell *shell, uint32_t id)
+{
+       struct wl_display *display = wl_client_get_display(client);
+       struct wl_drag *drag;
+
+       drag = malloc(sizeof *drag);
+       if (drag == NULL) {
+               wl_client_post_event(client,
+                                    (struct wl_object *) display,
+                                    WL_DISPLAY_NO_MEMORY);
+               return;
+       }
+
+       memset(drag, 0, sizeof *drag);
+       drag->resource.base.id = id;
+       drag->resource.base.interface = &wl_drag_interface;
+       drag->resource.base.implementation =
+               (void (**)(void)) &drag_interface;
+
+       drag->resource.destroy = destroy_drag;
+
+       wl_client_add_resource(client, &drag->resource);
+}
+
 const static struct wl_shell_interface shell_interface = {
        shell_move,
-       shell_resize
+       shell_resize,
+       shell_create_drag
 };
 
 static void
@@ -589,7 +629,7 @@ compositor_create_surface(struct wl_client *client,
        if (surface == NULL) {
                wl_client_post_event(client,
                                     (struct wl_object *) ec->wl_display,
-                                    WL_DISPLAY_NO_MEMORY, 0);
+                                    WL_DISPLAY_NO_MEMORY);
                return;
        }
 
@@ -696,8 +736,6 @@ pick_surface(struct wlsc_input_device *device, int32_t *sx, int32_t *sy)
 }
 
 static void
-wl_drag_reset(struct wl_drag *drag);
-static void
 wl_drag_set_pointer_focus(struct wl_drag *drag,
                          struct wlsc_surface *surface, uint32_t time,
                          int32_t x, int32_t y, int32_t sx, int32_t sy);
@@ -797,11 +835,12 @@ notify_motion(struct wlsc_input_device *device, uint32_t time, int x, int y)
 
        case WLSC_DEVICE_GRAB_DRAG:
                es = pick_surface(device, &sx, &sy);
-               wl_drag_set_pointer_focus(&device->drag,
+               wl_drag_set_pointer_focus(device->drag,
                                          es, time, x, y, sx, sy);
                if (es)
-                       wl_surface_post_event(&es->base, &device->drag.base,
-                                             WL_DRAG_MOTION,
+                       wl_surface_post_event(&es->base,
+                                             &device->drag->drag_offer.base,
+                                             WL_DRAG_OFFER_MOTION,
                                              time, x, y, sx, sy);
                break;
 
@@ -817,7 +856,7 @@ notify_motion(struct wlsc_input_device *device, uint32_t time, int x, int y)
 static void
 wlsc_input_device_end_grab(struct wlsc_input_device *device, uint32_t time)
 {
-       struct wl_drag *drag = &device->drag;
+       struct wl_drag *drag = device->drag;
        struct wlsc_surface *es;
        int32_t sx, sy;
 
@@ -825,9 +864,10 @@ wlsc_input_device_end_grab(struct wlsc_input_device *device, uint32_t time)
        case WLSC_DEVICE_GRAB_DRAG:
                if (drag->target)
                        wl_client_post_event(drag->target,
-                                            &drag->base, WL_DRAG_DROP);
+                                            &drag->drag_offer.base,
+                                            WL_DRAG_OFFER_DROP);
                wl_drag_set_pointer_focus(drag, NULL, time, 0, 0, 0, 0);
-               wl_drag_reset(drag);
+               device->drag = NULL;
                break;
        default:
                break;
@@ -1012,80 +1052,89 @@ wl_drag_set_pointer_focus(struct wl_drag *drag,
        if (drag->pointer_focus &&
            (!surface || drag->pointer_focus->client != surface->base.client))
                wl_surface_post_event(drag->pointer_focus,
-                                     &drag->base,
-                                     WL_DRAG_POINTER_FOCUS,
+                                     &drag->drag_offer.base,
+                                     WL_DRAG_OFFER_POINTER_FOCUS,
                                      time, NULL, 0, 0, 0, 0);
-       if (surface) {
+
+       if (surface &&
+           (!drag->pointer_focus ||
+            drag->pointer_focus->client != surface->base.client)) {
                wl_surface_post_event(&surface->base,
-                                     &drag->base,
-                                     WL_DRAG_POINTER_FOCUS,
-                                     time, &surface->base,
-                                     x, y, sx, sy);
+                                     (struct wl_object *) surface->compositor->wl_display,
+                                     WL_DISPLAY_GLOBAL,
+                                     &drag->drag_offer.base,
+                                     drag->drag_offer.base.interface->name,
+                                     drag->drag_offer.base.interface->version);
 
                end = drag->types.data + drag->types.size;
                for (p = drag->types.data; p < end; p++)
                        wl_surface_post_event(&surface->base,
-                                             &drag->base,
-                                             WL_DRAG_OFFER, *p);
+                                             &drag->drag_offer.base,
+                                             WL_DRAG_OFFER_OFFER, *p);
        }
 
+       if (surface) {
+               wl_surface_post_event(&surface->base,
+                                     &drag->drag_offer.base,
+                                     WL_DRAG_OFFER_POINTER_FOCUS,
+                                     time, &surface->base,
+                                     x, y, sx, sy);
+
+       }
 
        drag->pointer_focus = &surface->base;
+       drag->pointer_focus_time = time;
        drag->target = NULL;
 }
 
 static void
-wl_drag_reset(struct wl_drag *drag)
+drag_offer_accept(struct wl_client *client,
+                 struct wl_drag_offer *offer, uint32_t time, const char *type)
 {
+       struct wl_drag *drag = container_of(offer, struct wl_drag, drag_offer);
        char **p, **end;
 
+       /* If the client responds to drag pointer_focus or motion
+        * events after the pointer has left the surface, we just
+        * discard the accept requests.  The drag source just won't
+        * get the corresponding 'target' events and eventually the
+        * next surface/root will start sending events. */
+       if (time < drag->pointer_focus_time)
+               return;
+
+       drag->target = client;
+       drag->type = NULL;
        end = drag->types.data + drag->types.size;
        for (p = drag->types.data; p < end; p++)
-               free(*p);
-       wl_array_release(&drag->types);
-       wl_array_init(&drag->types);
-
-       /* FIXME: We need to reset drag->target too, but can't right
-        * now because we need it for send/drop.
-        *
-        * drag->target = NULL;
-        * drag->source = NULL;
-        */
-       drag->time = 0;
-       drag->pointer_focus = NULL;
+               if (type && strcmp(*p, type) == 0)
+                       drag->type = *p;
+
+       wl_surface_post_event(drag->source, &drag->resource.base,
+                             WL_DRAG_TARGET, drag->type);
 }
 
 static void
-drag_prepare(struct wl_client *client,
-            struct wl_drag *drag, struct wl_surface *surface, uint32_t time)
+drag_offer_receive(struct wl_client *client,
+                  struct wl_drag_offer *offer, int fd)
 {
-       struct wlsc_input_device *device =
-               (struct wlsc_input_device *) drag->input_device;
-
-       if (&device->pointer_focus->base != surface ||
-           device->grab_time != time)
-               return;
+       struct wl_drag *drag = container_of(offer, struct wl_drag, drag_offer);
 
-       wl_drag_reset(drag);
-       drag->source = surface;
-       drag->time = time;
+       wl_surface_post_event(drag->source, &drag->resource.base,
+                             WL_DRAG_FINISH, fd);
+       close(fd);
 }
 
+static const struct wl_drag_offer_interface drag_offer_interface = {
+       drag_offer_accept,
+       drag_offer_receive
+};
+
 static void
 drag_offer(struct wl_client *client, struct wl_drag *drag, const char *type)
 {
        struct wl_display *display = wl_client_get_display (client);
-       struct wlsc_input_device *device =
-               (struct wlsc_input_device *) drag->input_device;
        char **p;
 
-       if (drag->source == NULL ||
-           drag->source->client != client ||
-           device->grab != WLSC_DEVICE_GRAB_MOTION ||
-           &device->pointer_focus->base != drag->source ||
-           device->grab_time != drag->time)
-               return;
-
        p = wl_array_add(&drag->types, sizeof *p);
        if (p)
                *p = strdup(type);
@@ -1097,25 +1146,36 @@ drag_offer(struct wl_client *client, struct wl_drag *drag, const char *type)
 
 static void
 drag_activate(struct wl_client *client,
-             struct wl_drag *drag)
+             struct wl_drag *drag,
+             struct wl_surface *surface,
+             struct wl_input_device *input_device, uint32_t time)
 {
+       struct wl_display *display = wl_client_get_display (client);
        struct wlsc_input_device *device =
-               (struct wlsc_input_device *) drag->input_device;
-       struct wlsc_surface *surface;
+               (struct wlsc_input_device *) input_device;
+       struct wlsc_surface *target;
        int32_t sx, sy;
 
-       if (drag->source == NULL ||
-           drag->source->client != client ||
-           device->grab != WLSC_DEVICE_GRAB_MOTION ||
-           &device->pointer_focus->base != drag->source ||
-           device->grab_time != drag->time)
+       if (device->grab != WLSC_DEVICE_GRAB_MOTION ||
+           &device->pointer_focus->base != surface ||
+           device->grab_time != time)
                return;
 
-       wlsc_input_device_start_grab(device, drag->time,
+       drag->source = surface;
+       drag->input_device = input_device;
+
+       drag->drag_offer.base.interface = &wl_drag_offer_interface;
+       drag->drag_offer.base.implementation =
+               (void (**)(void)) &drag_offer_interface;
+
+       wl_display_add_object(display, &drag->drag_offer.base);
+
+       wlsc_input_device_start_grab(device, time,
                                     WLSC_DEVICE_GRAB_DRAG);
 
-       surface = pick_surface(device, &sx, &sy);
-       wl_drag_set_pointer_focus(&device->drag, surface, drag->time,
+       device->drag = drag;
+       target = pick_surface(device, &sx, &sy);
+       wl_drag_set_pointer_focus(device->drag, target, time,
                                  device->x, device->y, sx, sy);
 }
 
@@ -1125,102 +1185,19 @@ drag_cancel(struct wl_client *client, struct wl_drag *drag)
        struct wlsc_input_device *device =
                (struct wlsc_input_device *) drag->input_device;
 
-       if (drag->source == NULL ||
-           drag->source->client != client ||
-           device->grab != WLSC_DEVICE_GRAB_DRAG)
+       if (drag->source == NULL || drag->source->client != client)
                return;
 
        wlsc_input_device_end_grab(device, get_time());
-}
-
-static void
-drag_accept(struct wl_client *client,
-           struct wl_drag *drag, const char *type)
-{
-       char **p, **end;
-
-       /* If the client responds to drag motion events after the
-        * pointer has left the surface, we just discard the accept
-        * requests.  The drag source just won't get the corresponding
-        * 'target' events and the next surface/root will start
-        * sending events. */
-       if (drag->pointer_focus == NULL)
-               return;
-
-       /* The accept request may arrive after the pointer has left
-        * the surface that received the drag motion events that
-        * triggered the request.  But if the pointer re-enters the
-        * surface (or another surface from the same client) and we
-        * receive the 'accept' requests from the first visit to the
-        * surface later, this check will pass.  Then we end up
-        * sending the 'target' event for the old 'accept' requests
-        * events *after* potentially sending 'target' from the root
-        * surface, out of order.  So we need to track the time of
-        * pointer_focus and this request needs a time stamp so we can
-        * compare and reject 'accept' requests that are older than
-        * the pointer_focus in timestamp. */
-       if (drag->pointer_focus->client != client)
-               return;
-
-       /* FIXME: We need a serial number here to correlate the accept
-        * request with a pointer_focus/motion event. */
-       drag->target = client;
-
-       if (type == NULL) {
-               drag->type = NULL;
-       } else {
-               end = drag->types.data + drag->types.size;
-               for (p = drag->types.data; p < end; p++)
-                       if (strcmp(*p, type) == 0)
-                               drag->type = *p;
-       }
-
-       wl_surface_post_event(drag->source, &drag->base,
-                             WL_DRAG_TARGET, drag->type);
-}
-
-static void
-drag_receive(struct wl_client *client,
-            struct wl_drag *drag, int fd)
-{
-       wl_surface_post_event(drag->source, &drag->base, WL_DRAG_FINISH, fd);
-       close(fd);
+       device->drag = NULL;
 }
 
 static const struct wl_drag_interface drag_interface = {
-       drag_prepare,
        drag_offer,
        drag_activate,
        drag_cancel,
-       drag_accept,
-       drag_receive
 };
 
-static void
-wl_drag_post_device(struct wl_client *client, struct wl_object *global)
-{
-       struct wl_drag *drag = container_of(global, struct wl_drag, base);
-
-       wl_client_post_event(client, global,
-                            WL_DRAG_DEVICE, drag->input_device);
-}
-
-static void
-wl_drag_init(struct wl_drag *drag,
-            struct wl_display *display, struct wl_input_device *input_device)
-{
-       drag->base.interface = &wl_drag_interface;
-       drag->base.implementation = (void (**)(void))
-               &drag_interface;
-
-       wl_display_add_object(display, &drag->base);
-       wl_display_add_global(display, &drag->base, wl_drag_post_device);
-
-       drag->source = NULL;
-       wl_array_init(&drag->types);
-       drag->input_device = input_device;
-}
-
 void
 wlsc_input_device_init(struct wlsc_input_device *device,
                       struct wlsc_compositor *ec)
@@ -1231,8 +1208,6 @@ wlsc_input_device_init(struct wlsc_input_device *device,
        wl_display_add_object(ec->wl_display, &device->base.base);
        wl_display_add_global(ec->wl_display, &device->base.base, NULL);
 
-       wl_drag_init(&device->drag, ec->wl_display, &device->base);
-
        device->x = 100;
        device->y = 100;
        device->ec = ec;
index 44fc1a8..097cdc6 100644 (file)
@@ -107,7 +107,7 @@ struct wlsc_input_device {
        int32_t grab_width, grab_height;
        int32_t grab_dx, grab_dy;
        uint32_t grab_button;
-       struct wl_drag drag;
+       struct wl_drag *drag;
 
        struct wlsc_listener listener;
 };
index 390fb23..0f2bd98 100644 (file)
@@ -66,7 +66,7 @@
   </interface>
 
   <interface name="buffer" version="1">
-    <request name="destroy"/>
+    <request name="destroy" type="destructor"/>
   </interface>
 
   <interface name="shell" version="1">
       <arg name="edges" type="uint"/>
     </request>
 
+    <request name="create_drag">
+      <arg name="id" type="new_id" interface="drag"/>
+    </request>
+
     <event name="configure">
       <arg name="time" type="uint"/>
       <!-- Same as edges except also move (16) -->
   </interface>
 
   <interface name="drag" version="1">
-    <request name="prepare">
-      <!-- Start a drag action from given surface and device for the
-          grab started by the button click at time -->
-      <arg name="surface" type="object" interface="surface"/>
-      <arg name="time" type="uint"/>
-    </request>
-
     <!-- Add an offered mime type.  Can be called several times to
          offer multiple types, but must be called before 'activate'. -->
     <request name="offer">
       <arg name="type" type="string"/>
     </request>
 
-    <request name="activate"/>
+    <request name="activate">
+      <arg name="surface" type="object" interface="surface"/>
+      <arg name="input_device" type="object" interface="input_device"/>
+      <arg name="time" type="uint"/>
+    </request>
 
-    <!-- Cancel the drag. -->
-    <request name="cancel"/>
+    <!-- Destroy the drag and cancel the session. -->
+    <request name="destroy" type="destructor"/>
+
+    <!-- Sent when a target accepts pointer_focus or motion events.
+         If a target does not accept any of the offered types, type is
+         NULL -->
+    <event name="target">
+      <arg name="mime_type" type="string"/>
+    </event>
+
+    <!-- Sent when the drag is finished.  The final mime type is that
+         of the last target event.  If that was NULL, no drag target
+         was valid when the drag finished, fd is undefined and the
+         source should not send data.  The event is also sent in case
+         a drag source tries to activate a drag after the grab was
+         released, in which case mime_type will also be NULL. -->
+    <event name="finish">
+      <arg name="fd" type="fd"/>
+    </event>
+  </interface>
 
-    <!-- Called by the drag target to accept the offer of the given
-         type -->
+
+  <interface name="drag_offer" version="1">
+    <!-- Call to accept the offer of the given type -->
     <request name="accept">
+      <arg name="time" type="uint"/>
       <arg name="type" type="string"/>
     </request>
 
-    <!-- Called by the drag target to initiate the drag finish
-         sequence.  Send the pipe fd to the compositor, which forwards
-         it to the source in the 'finish' event -->
+    <!-- Called to initiate the drag finish sequence.  Sends the pipe
+         fd to the compositor, which forwards it to the source in the
+         'finish' event -->
     <request name="receive">
       <arg name="fd" type="fd"/>
     </request>
 
-    <!-- Sent at connect time to announce the association -->
-    <event name="device">
-      <arg name="device" type="object" interface="input_device"/>
+    <!-- Sent before the pointer_focus event to announce the types
+         offered.  One event per offered mime type. -->
+    <event name="offer">
+      <arg name="type" type="string"/>
     </event>
 
-    <!-- Similar to device::pointer_focus.  Sent to potential
-        target surfaces to offer drag data.  If the device
-        leaves the window, the drag stops or the originator cancels
-        the drag, this event is sent with the NULL surface. -->
+    <!-- Similar to device::pointer_focus.  Sent to potential target
+         surfaces to offer drag data.  If the device leaves the
+         window, the drag stops or the originator cancels the drag,
+         this event is sent with the NULL surface, at which point the
+         drag object may no longer be valid. -->
     <event name="pointer_focus">
       <arg name="time" type="uint"/>
       <arg name="surface" type="object" interface="surface"/>
       <arg name="surface_y" type="int"/>
     </event>
 
-    <!-- Sent after the pointer_focus event to announce the types
-        offered.  One event per offered mime type. -->
-    <event name="offer">
-      <arg name="type" type="string"/>
-    </event>
-
     <!-- Similar to device::motion.  Sent to potential target surfaces
         as the drag pointer moves around in the surface. -->
     <event name="motion">
       <arg name="surface_y" type="int"/>
     </event>
 
-    <!-- Sent to drag originator in response to pointer_focus and
-         motion events.  If a target does not accept any of the
-         offered types, type is NULL -->
-    <event name="target">
-      <arg name="mime_type" type="string"/>
-    </event>
-
-    <!-- Sent to target, to indicate that the drag is finishing.  The
-         last motion/pointer_focus event gives the location of the
-         drop.  Target must respond with the 'receive' request, which
-         sends an fd to the source for writing the drag data. -->
+    <!-- Sent to indicate that the drag is finishing.  The last
+         motion/pointer_focus event gives the location of the drop.
+         Target must respond with the 'receive' request, which sends
+         an fd to the source for writing the drag data. -->
     <event name="drop"/>
-
-    <!-- Sent to drag source when the drag is finished.  The final
-         mime type is that of the last target event.  If that was
-         NULL, no drag target was valid when the drag finished, fd is
-         undefined and the source should not send data.  The event is
-         also sent in case a drag source tries to activate a drag
-         after the grab was released, in which case mime_type will
-         also be NULL. -->
-    <event name="finish">
-      <arg name="fd" type="fd"/>
-    </event>
-
   </interface>
 
   <interface name="surface" version="1">
-    <request name="destroy"/>
+    <request name="destroy" type="destructor"/>
 
     <request name="attach">
       <arg name="buffer" type="object" interface="buffer"/>
index b4d7668..613e12b 100644 (file)
--- a/scanner.c
+++ b/scanner.c
@@ -74,6 +74,7 @@ struct message {
        char *uppercase_name;
        struct wl_list arg_list;
        struct wl_list link;
+       int destructor;
 };
 
 enum arg_type {
@@ -177,6 +178,11 @@ start_element(void *data, const char *element_name, const char **atts)
                        wl_list_insert(ctx->interface->event_list.prev,
                                       &message->link);
 
+               if (type != NULL && strcmp(type, "destructor") == 0)
+                       message->destructor = 1;
+               else
+                       message->destructor = 0;
+
                ctx->message = message;
        } else if (strcmp(element_name, "arg") == 0) {
                arg = malloc(sizeof *arg);
@@ -263,6 +269,7 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
 {
        struct message *m;
        struct arg *a, *ret;
+       int has_destructor, has_destroy;
 
        /* We provide a hand written constructor for the display object */
        if (strcmp(interface->name, "display") != 0)
@@ -293,6 +300,33 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
               interface->name, interface->name, interface->name,
               interface->name);
 
+       has_destructor = 0;
+       has_destroy = 0;
+       wl_list_for_each(m, message_list, link) {
+               if (m->destructor)
+                       has_destructor = 1;
+               if (strcmp(m->name, "destroy)") == 0)
+                       has_destroy = 1;
+       }
+
+       if (!has_destructor && has_destroy) {
+               fprintf(stderr,
+                       "interface %s has method named destroy but"
+                       "no destructor", interface->name);
+               exit(EXIT_FAILURE);
+       }
+
+       /* And we have a hand-written display destructor */
+       if (!has_destructor && strcmp(interface->name, "display") != 0)
+               printf("static inline void\n"
+                      "wl_%s_destroy(struct wl_%s *%s)\n"
+                      "{\n"
+                      "\twl_proxy_destroy("
+                      "(struct wl_proxy *) %s);\n"
+                      "}\n\n",
+                      interface->name, interface->name, interface->name,
+                      interface->name);
+
        if (wl_list_empty(message_list))
                return;
 
@@ -347,6 +381,11 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
                }
                printf(");\n");
 
+               if (m->destructor)
+                       printf("\n\twl_proxy_destroy("
+                              "(struct wl_proxy *) %s);\n",
+                              interface->name);
+
                if (ret)
                        printf("\n\treturn (struct wl_%s *) %s;\n",
                               ret->interface_name, ret->name);
@@ -442,6 +481,9 @@ static const char client_prototypes[] =
        "wl_proxy_create_for_id(struct wl_display *display,\n"
        "\t\t       const struct wl_interface *interface, uint32_t id);\n"
 
+       "extern void\n"
+       "wl_proxy_destroy(struct wl_proxy *proxy);\n\n"
+
        "extern int\n"
        "wl_proxy_add_listener(struct wl_proxy *proxy,\n"
        "\t\t      void (**implementation)(void), void *data);\n\n"
index 47df184..dd0500f 100644 (file)
@@ -146,6 +146,18 @@ wl_proxy_create(struct wl_proxy *factory,
                                      wl_display_allocate_id(factory->display));
 }
 
+WL_EXPORT void
+wl_proxy_destroy(struct wl_proxy *proxy)
+{
+       struct wl_listener *listener, *next;
+
+       wl_list_for_each_safe(listener, next, &proxy->listener_list, link)
+               free(listener);
+
+       wl_hash_table_remove(proxy->display->objects, proxy->base.id);
+       free(proxy);
+}
+
 WL_EXPORT int
 wl_proxy_add_listener(struct wl_proxy *proxy,
                      void (**implementation)(void), void *data)
index acfe791..4292be0 100644 (file)
@@ -124,8 +124,13 @@ struct wl_visual {
        struct wl_object base;
 };
 
-struct wl_drag {
+struct wl_drag_offer {
        struct wl_object base;
+};
+
+struct wl_drag {
+       struct wl_resource resource;
+       struct wl_drag_offer drag_offer;
        struct wl_surface *source;
        struct wl_surface *pointer_focus;
        struct wl_client *target;
@@ -133,7 +138,7 @@ struct wl_drag {
        struct wl_input_device *input_device;
        struct wl_array types;
        const char *type;
-       uint32_t time;
+       uint32_t pointer_focus_time;
 };
 
 void