diff --git a/TODO b/TODO index 8f8486ae..fe9b015b 100644 --- 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 diff --git a/clients/dnd.c b/clients/dnd.c index a53caa28..df4d8c57 100644 --- a/clients/dnd.c +++ b/clients/dnd.c @@ -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); diff --git a/clients/window.c b/clients/window.c index b6074e57..5a2f2475 100644 --- a/clients/window.c +++ b/clients/window.c @@ -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) @@ -1099,71 +1102,12 @@ display_add_input(struct display *d, uint32_t id) wl_input_device_set_user_data(input->input_device, input); } -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; +} diff --git a/clients/window.h b/clients/window.h index f9da8051..1b4285ff 100644 --- a/clients/window.h +++ b/clients/window.h @@ -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); diff --git a/compositor.c b/compositor.c index 9c8d5b5b..32f9d11b 100644 --- a/compositor.c +++ b/compositor.c @@ -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; } @@ -695,8 +735,6 @@ pick_surface(struct wlsc_input_device *device, int32_t *sx, int32_t *sy) return NULL; } -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, @@ -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; diff --git a/compositor.h b/compositor.h index 44fc1a8d..097cdc64 100644 --- a/compositor.h +++ b/compositor.h @@ -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; }; diff --git a/protocol.xml b/protocol.xml index 390fb232..0f2bd98d 100644 --- a/protocol.xml +++ b/protocol.xml @@ -66,7 +66,7 @@ - + @@ -84,6 +84,10 @@ + + + + @@ -97,46 +101,65 @@ - - - - - - - + + + + + - - + + + + + + + + + + + + + - + + + + - + - - - + + + - + @@ -146,12 +169,6 @@ - - - - - @@ -162,34 +179,15 @@ - - - - - - + - - - - - - - + diff --git a/scanner.c b/scanner.c index b4d76681..613e12b9 100644 --- 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" diff --git a/wayland-client.c b/wayland-client.c index 47df184d..dd0500f5 100644 --- a/wayland-client.c +++ b/wayland-client.c @@ -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) diff --git a/wayland-server.h b/wayland-server.h index acfe7914..4292be05 100644 --- a/wayland-server.h +++ b/wayland-server.h @@ -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