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