From 506e20eed96e39737a402358e43aa3d5249d49a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Thu, 19 Aug 2010 17:26:02 -0400 Subject: [PATCH] More work on dnd --- TODO | 13 ++++++++ clients/dnd.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ clients/window.c | 23 +++++++++----- clients/window.h | 5 ++++ compositor.c | 32 +++++++++++++++++++- 5 files changed, 142 insertions(+), 9 deletions(-) diff --git a/TODO b/TODO index 5cb6ab4b..601a97e2 100644 --- a/TODO +++ b/TODO @@ -50,6 +50,19 @@ Core wayland protocol 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? + + How to handle surfaces from clients that don't know about dnd or + don't care? Maybe the dnd object should have a + dnd.register_surface() method so clients can opt-in the surfaces + 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. + - copy-n-paste, store data in server (only one mime-type available) or do X style (content mime-type negotiation, but data goes away when client quits). diff --git a/clients/dnd.c b/clients/dnd.c index 5772e8e2..0557ed9b 100644 --- a/clients/dnd.c +++ b/clients/dnd.c @@ -206,6 +206,82 @@ dnd_get_item(struct dnd *dnd, int32_t x, int32_t y) return NULL; } +static void +drag_handle_device(void *data, + struct wl_drag *drag, struct wl_input_device *device) +{ +} + +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) +{ + fprintf(stderr, "drag pointer focus %p\n", surface); + + wl_drag_accept(drag, "text/plain"); +} + +static void +drag_offer(void *data, + struct wl_drag *drag, const char *type) +{ + fprintf(stderr, "drag offer %s\n", 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) +{ + fprintf(stderr, "drag motion %d,%d\n", surface_x, surface_y); + + /* 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. */ + wl_drag_accept(drag, "text/plain"); +} + +static void +drag_target(void *data, + struct wl_drag *drag, const char *mime_type) +{ + fprintf(stderr, "target %s\n", mime_type); +} + +static void +drag_finish(void *data, struct wl_drag *drag) +{ + fprintf(stderr, "drag finish\n"); + struct wl_array a; + char text[] = "[drop data]"; + + a.data = text; + a.size = sizeof text; + + wl_drag_send(drag, &a); +} + +static void +drag_data(void *data, + struct wl_drag *drag, struct wl_array *contents) +{ + fprintf(stderr, "drag drop, data %s\n", contents->data); +} + +static const struct wl_drag_listener drag_listener = { + drag_handle_device, + drag_pointer_focus, + drag_offer, + drag_motion, + drag_target, + drag_finish, + drag_data +}; + static void dnd_button_handler(struct window *window, struct input *input, uint32_t time, @@ -352,6 +428,8 @@ main(int argc, char *argv[]) d = display_create(&argc, &argv, option_entries); + display_add_drag_listener(d, &drag_listener, d); + dnd = dnd_create (d); display_run(d); diff --git a/clients/window.c b/clients/window.c index 994c8a8f..a56bcfae 100644 --- a/clients/window.c +++ b/clients/window.c @@ -57,7 +57,6 @@ struct display { struct wl_shell *shell; struct wl_drm *drm; struct wl_output *output; - struct wl_drag *drag; struct rectangle screen_allocation; int authenticated; EGLDisplay dpy; @@ -718,6 +717,17 @@ input_get_position(struct input *input, int32_t *x, int32_t *y) *y = input->sy; } +void +display_add_drag_listener(struct display *display, + const struct wl_drag_listener *drag_listener, + void *data) +{ + struct input *input; + + wl_list_for_each(input, &display->input_list, link) + wl_drag_add_listener(input->drag, drag_listener, data); +} + void window_start_drag(struct window *window, struct input *input, uint32_t time, struct wl_buffer *buffer, int32_t x, int32_t y) @@ -1072,7 +1082,7 @@ drag_handle_device(void *data, input = wl_input_device_get_user_data(device); input->drag = drag; - + wl_drag_set_user_data(drag, input); } static void @@ -1081,14 +1091,12 @@ drag_pointer_focus(void *data, uint32_t time, struct wl_surface *surface, int32_t x, int32_t y, int32_t surface_x, int32_t surface_y) { - fprintf(stderr, "drag pointer focus %p\n", surface); } static void drag_offer(void *data, struct wl_drag *drag, const char *type) { - fprintf(stderr, "drag offer %s\n", type); } static void @@ -1097,7 +1105,6 @@ drag_motion(void *data, uint32_t time, int32_t x, int32_t y, int32_t surface_x, int32_t surface_y) { - fprintf(stderr, "drag motion %d,%d\n", surface_x, surface_y); } static void @@ -1109,7 +1116,6 @@ drag_target(void *data, static void drag_finish(void *data, struct wl_drag *drag) { - fprintf(stderr, "drag finish\n"); } static void @@ -1133,6 +1139,7 @@ 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; if (strcmp(interface, "compositor") == 0) { d->compositor = wl_compositor_create(display, id); @@ -1150,8 +1157,8 @@ display_handle_global(struct wl_display *display, uint32_t id, d->drm = wl_drm_create(display, id); wl_drm_add_listener(d->drm, &drm_listener, d); } else if (strcmp(interface, "drag") == 0) { - d->drag = wl_drag_create(display, id); - wl_drag_add_listener(d->drag, &drag_listener, d); + drag = wl_drag_create(display, id); + wl_drag_add_listener(drag, &drag_listener, NULL); } } diff --git a/clients/window.h b/clients/window.h index ab17417e..2c0b49b6 100644 --- a/clients/window.h +++ b/clients/window.h @@ -59,6 +59,11 @@ display_get_pointer_surface(struct display *display, int pointer, int *width, int *height, int *hotspot_x, int *hotspot_y); +void +display_add_drag_listener(struct display *display, + const struct wl_drag_listener *drag_listener, + void *data); + void display_run(struct display *d); diff --git a/compositor.c b/compositor.c index 93aad964..9c5ee9b7 100644 --- a/compositor.c +++ b/compositor.c @@ -1000,6 +1000,8 @@ 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) { + char **p, **end; + if (drag->pointer_focus == &surface->base) return; @@ -1009,13 +1011,21 @@ wl_drag_set_pointer_focus(struct wl_drag *drag, &drag->base, WL_DRAG_POINTER_FOCUS, time, NULL, 0, 0, 0, 0); - if (surface) + if (surface) { wl_surface_post_event(&surface->base, &drag->base, WL_DRAG_POINTER_FOCUS, time, &surface->base, x, y, sx, sy); + 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->pointer_focus = &surface->base; } @@ -1133,6 +1143,26 @@ drag_accept(struct wl_client *client, { 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;