Implement the new dnd/selection protocol
The new protocol splits dnd/selection from wl_shell and allows us to move
the implementation out of shell.c.
diff --git a/clients/window.c b/clients/window.c
index 1e317c7..32c546d 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -20,6 +20,8 @@
* OF THIS SOFTWARE.
*/
+#define _GNU_SOURCE
+
#include "../config.h"
#include <stdint.h>
@@ -61,6 +63,7 @@
struct wl_shell *shell;
struct wl_shm *shm;
struct wl_output *output;
+ struct wl_data_device_manager *data_device_manager;
struct rectangle screen_allocation;
EGLDisplay dpy;
EGLConfig rgb_config;
@@ -127,6 +130,8 @@
window_enter_handler_t enter_handler;
window_leave_handler_t leave_handler;
window_item_focus_handler_t item_focus_handler;
+ window_data_handler_t data_handler;
+ window_drop_handler_t drop_handler;
struct wl_list item_list;
struct item *focus_item;
@@ -147,11 +152,14 @@
struct wl_input_device *input_device;
struct window *pointer_focus;
struct window *keyboard_focus;
- struct selection_offer *offer;
uint32_t current_pointer_image;
uint32_t modifiers;
int32_t x, y, sx, sy;
struct wl_list link;
+
+ struct wl_data_device *data_device;
+ struct data_offer *drag_offer;
+ struct data_offer *selection_offer;
};
enum {
@@ -695,7 +703,6 @@
return cairo_surface_reference(surface);
}
-
static void
window_attach_surface(struct window *window);
@@ -1132,8 +1139,8 @@
return location;
}
-static void
-set_pointer_image(struct input *input, uint32_t time, int pointer)
+void
+input_set_pointer_image(struct input *input, uint32_t time, int pointer)
{
struct display *display = input->display;
struct wl_buffer *buffer;
@@ -1233,7 +1240,7 @@
x, y, sx, sy,
window->user_data);
- set_pointer_image(input, time, pointer);
+ input_set_pointer_image(input, time, pointer);
}
static void
@@ -1364,7 +1371,7 @@
item = window_find_item(window, x, y);
window_set_focus_item(window, item);
- set_pointer_image(input, time, pointer);
+ input_set_pointer_image(input, time, pointer);
}
}
@@ -1435,13 +1442,238 @@
return input->modifiers;
}
-struct wl_drag *
-window_create_drag(struct window *window)
-{
- cairo_device_flush (window->display->rgb_device);
- cairo_device_flush (window->display->argb_device);
+struct data_offer {
+ struct wl_data_offer *offer;
+ struct input *input;
+ struct wl_array types;
+ int refcount;
- return wl_shell_create_drag(window->display->shell);
+ struct task io_task;
+ int fd;
+ data_func_t func;
+ int32_t x, y;
+ void *user_data;
+};
+
+static void
+data_offer_offer(void *data, struct wl_data_offer *wl_data_offer, const char *type)
+{
+ struct data_offer *offer = data;
+ char **p;
+
+ p = wl_array_add(&offer->types, sizeof *p);
+ *p = strdup(type);
+}
+
+static const struct wl_data_offer_listener data_offer_listener = {
+ data_offer_offer,
+};
+
+static void
+data_offer_destroy(struct data_offer *offer)
+{
+ char **p;
+
+ offer->refcount--;
+ if (offer->refcount == 0) {
+ wl_data_offer_destroy(offer->offer);
+ for (p = offer->types.data; *p; p++)
+ free(*p);
+ wl_array_release(&offer->types);
+ free(offer);
+ }
+}
+
+static void
+data_device_data_offer(void *data,
+ struct wl_data_device *data_device, uint32_t id)
+{
+ struct data_offer *offer;
+
+ offer = malloc(sizeof *offer);
+
+ wl_array_init(&offer->types);
+ offer->refcount = 1;
+ offer->input = data;
+
+ /* FIXME: Generate typesafe wrappers for this */
+ offer->offer = (struct wl_data_offer *)
+ wl_proxy_create_for_id((struct wl_proxy *) data_device,
+ id, &wl_data_offer_interface);
+
+ wl_data_offer_add_listener(offer->offer,
+ &data_offer_listener, offer);
+}
+
+static void
+data_device_enter(void *data, struct wl_data_device *data_device,
+ uint32_t time, struct wl_surface *surface,
+ int32_t x, int32_t y, struct wl_data_offer *offer)
+{
+ struct input *input = data;
+ struct window *window;
+ char **p;
+
+ input->drag_offer = wl_data_offer_get_user_data(offer);
+ window = wl_surface_get_user_data(surface);
+ input->pointer_focus = window;
+
+ p = wl_array_add(&input->drag_offer->types, sizeof *p);
+ *p = NULL;
+
+ window = input->pointer_focus;
+ if (window->data_handler)
+ window->data_handler(window, input, time, x, y,
+ input->drag_offer->types.data,
+ window->user_data);
+}
+
+static void
+data_device_leave(void *data, struct wl_data_device *data_device)
+{
+ struct input *input = data;
+
+ data_offer_destroy(input->drag_offer);
+ input->drag_offer = NULL;
+}
+
+static void
+data_device_motion(void *data, struct wl_data_device *data_device,
+ uint32_t time, int32_t x, int32_t y)
+{
+ struct input *input = data;
+ struct window *window = input->pointer_focus;
+
+ input->sx = x;
+ input->sy = y;
+
+ if (window->data_handler)
+ window->data_handler(window, input, time, x, y,
+ input->drag_offer->types.data,
+ window->user_data);
+}
+
+static void
+data_device_drop(void *data, struct wl_data_device *data_device)
+{
+ struct input *input = data;
+ struct window *window = input->pointer_focus;
+
+ if (window->drop_handler)
+ window->drop_handler(window, input,
+ input->sx, input->sy, window->user_data);
+}
+
+static void
+data_device_selection(void *data,
+ struct wl_data_device *wl_data_device,
+ struct wl_data_offer *offer)
+{
+ struct input *input = data;
+ char **p;
+
+ if (input->selection_offer)
+ data_offer_destroy(input->selection_offer);
+
+ input->selection_offer = wl_data_offer_get_user_data(offer);
+ p = wl_array_add(&input->selection_offer->types, sizeof *p);
+ *p = NULL;
+}
+
+static const struct wl_data_device_listener data_device_listener = {
+ data_device_data_offer,
+ data_device_enter,
+ data_device_leave,
+ data_device_motion,
+ data_device_drop,
+ data_device_selection
+};
+
+struct wl_data_device *
+input_get_data_device(struct input *input)
+{
+ return input->data_device;
+}
+
+void
+input_set_selection(struct input *input,
+ struct wl_data_source *source, uint32_t time)
+{
+ wl_data_device_set_selection(input->data_device, source, time);
+}
+
+void
+input_accept(struct input *input, uint32_t time, const char *type)
+{
+ wl_data_offer_accept(input->drag_offer->offer, time, type);
+}
+
+static void
+offer_io_func(struct task *task, uint32_t events)
+{
+ struct data_offer *offer =
+ container_of(task, struct data_offer, io_task);
+ unsigned int len;
+ char buffer[4096];
+
+ len = read(offer->fd, buffer, sizeof buffer);
+ offer->func(buffer, len,
+ offer->x, offer->y, offer->user_data);
+
+ if (len == 0) {
+ close(offer->fd);
+ data_offer_destroy(offer);
+ }
+}
+
+static void
+data_offer_receive_data(struct data_offer *offer, const char *mime_type,
+ data_func_t func, void *user_data)
+{
+ int p[2];
+
+ pipe2(p, O_CLOEXEC);
+ wl_data_offer_receive(offer->offer, mime_type, p[1]);
+ close(p[1]);
+
+ offer->io_task.run = offer_io_func;
+ offer->fd = p[0];
+ offer->func = func;
+ offer->refcount++;
+ offer->user_data = user_data;
+
+ display_watch_fd(offer->input->display,
+ offer->fd, EPOLLIN, &offer->io_task);
+}
+
+void
+input_receive_drag_data(struct input *input, const char *mime_type,
+ data_func_t func, void *data)
+{
+ data_offer_receive_data(input->drag_offer, mime_type, func, data);
+ input->drag_offer->x = input->sx;
+ input->drag_offer->y = input->sy;
+}
+
+int
+input_receive_selection_data(struct input *input, const char *mime_type,
+ data_func_t func, void *data)
+{
+ char **p;
+
+ if (input->selection_offer == NULL)
+ return -1;
+
+ for (p = input->selection_offer->types.data; *p; p++)
+ if (strcmp(mime_type, *p) == 0)
+ break;
+
+ if (*p == NULL)
+ return -1;
+
+ data_offer_receive_data(input->selection_offer,
+ mime_type, func, data);
+ return 0;
}
void
@@ -1452,13 +1684,6 @@
window->surface, input->input_device, time);
}
-void
-window_activate_drag(struct wl_drag *drag, struct window *window,
- struct input *input, uint32_t time)
-{
- wl_drag_activate(drag, window->surface, input->input_device, time);
-}
-
static void
handle_configure(void *data, struct wl_shell *shell,
uint32_t time, uint32_t edges,
@@ -1666,6 +1891,18 @@
}
void
+window_set_data_handler(struct window *window, window_data_handler_t handler)
+{
+ window->data_handler = handler;
+}
+
+void
+window_set_drop_handler(struct window *window, window_drop_handler_t handler)
+{
+ window->drop_handler = handler;
+}
+
+void
window_set_transparent(struct window *window, int transparent)
{
window->transparent = transparent;
@@ -1834,113 +2071,12 @@
wl_input_device_add_listener(input->input_device,
&input_device_listener, input);
wl_input_device_set_user_data(input->input_device, input);
-}
-struct selection_offer {
- struct display *display;
- struct wl_selection_offer *offer;
- struct wl_array types;
- struct input *input;
-};
-
-int
-input_offers_mime_type(struct input *input, const char *type)
-{
- struct selection_offer *offer = input->offer;
- char **p, **end;
-
- if (offer == NULL)
- return 0;
-
- end = offer->types.data + offer->types.size;
- for (p = offer->types.data; p < end; p++)
- if (strcmp(*p, type) == 0)
- return 1;
-
- return 0;
-}
-
-void
-input_receive_mime_type(struct input *input, const char *type, int fd)
-{
- struct selection_offer *offer = input->offer;
-
- /* FIXME: A number of things can go wrong here: the object may
- * not be the current selection offer any more (which could
- * still work, but the source may have gone away or just
- * destroyed its wl_selection) or the offer may not have the
- * requested type after all (programmer/client error,
- * typically) */
- wl_selection_offer_receive(offer->offer, type, fd);
-}
-
-static void
-selection_offer_offer(void *data,
- struct wl_selection_offer *selection_offer,
- const char *type)
-{
- struct selection_offer *offer = data;
-
- char **p;
-
- p = wl_array_add(&offer->types, sizeof *p);
- if (p)
- *p = strdup(type);
-};
-
-static void
-selection_offer_keyboard_focus(void *data,
- struct wl_selection_offer *selection_offer,
- struct wl_input_device *input_device)
-{
- struct selection_offer *offer = data;
- struct input *input;
- char **p, **end;
-
- if (input_device == NULL) {
- printf("selection offer retracted %p\n", selection_offer);
- input = offer->input;
- input->offer = NULL;
- wl_selection_offer_destroy(selection_offer);
- wl_array_release(&offer->types);
- free(offer);
- return;
- }
-
- input = wl_input_device_get_user_data(input_device);
- printf("new selection offer %p:", selection_offer);
-
- offer->input = input;
- input->offer = offer;
- end = offer->types.data + offer->types.size;
- for (p = offer->types.data; p < end; p++)
- printf(" %s", *p);
-
- printf("\n");
-}
-
-struct wl_selection_offer_listener selection_offer_listener = {
- selection_offer_offer,
- selection_offer_keyboard_focus
-};
-
-static void
-add_selection_offer(struct display *d, uint32_t id)
-{
- struct selection_offer *offer;
-
- offer = malloc(sizeof *offer);
- if (offer == NULL)
- return;
-
- offer->offer =
- wl_display_bind(d->display, id, &wl_selection_offer_interface);
- offer->display = d;
- wl_array_init(&offer->types);
- offer->input = NULL;
-
- wl_selection_offer_add_listener(offer->offer,
- &selection_offer_listener, offer);
+ input->data_device =
+ wl_data_device_manager_get_data_device(d->data_device_manager,
+ input->input_device);
+ wl_data_device_add_listener(input->data_device,
+ &data_device_listener, input);
}
static void
@@ -1962,8 +2098,10 @@
wl_shell_add_listener(d->shell, &shell_listener, d);
} else if (strcmp(interface, "wl_shm") == 0) {
d->shm = wl_display_bind(display, id, &wl_shm_interface);
- } else if (strcmp(interface, "wl_selection_offer") == 0) {
- add_selection_offer(d, id);
+ } else if (strcmp(interface, "wl_data_device_manager") == 0) {
+ d->data_device_manager =
+ wl_display_bind(display, id,
+ &wl_data_device_manager_interface);
}
}
@@ -2215,6 +2353,12 @@
return d->dpy;
}
+struct wl_data_source *
+display_create_data_source(struct display *display)
+{
+ return wl_data_device_manager_create_data_source(display->data_device_manager);
+}
+
EGLConfig
display_get_rgb_egl_config(struct display *d)
{