From 8f241998c03938b3c159f54c9a87c928808f19f9 Mon Sep 17 00:00:00 2001 From: Alexander Preisinger Date: Sat, 4 Jan 2014 13:48:50 +0100 Subject: wayland: implement dropping files on the VO window Needs some proper testing. --- video/out/wayland_common.c | 187 ++++++++++++++++++++++++++++++++++++++++++++- video/out/wayland_common.h | 5 ++ 2 files changed, 190 insertions(+), 2 deletions(-) (limited to 'video') diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index 1a4414da3d..ae2b480b4a 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -465,6 +465,99 @@ static const struct wl_seat_listener seat_listener = { seat_handle_capabilities, }; +static void data_offer_handle_offer(void *data, + struct wl_data_offer *offer, + const char *mime_type) +{ + struct vo_wayland_state *wl = data; + if (strcmp(mime_type, "text/uri-list") != 0) + MP_VERBOSE(wl, "unsupported mime type for drag and drop: %s\n", mime_type); +} + +static const struct wl_data_offer_listener data_offer_listener = { + data_offer_handle_offer, +}; + +static void data_device_handle_data_offer(void *data, + struct wl_data_device *wl_data_device, + struct wl_data_offer *id) +{ + struct vo_wayland_state *wl = data; + if (wl->input.offer) { + MP_ERR(wl, "There is already a dnd entry point.\n"); + wl_data_offer_destroy(wl->input.offer); + } + + wl->input.offer = id; + wl_data_offer_add_listener(id, &data_offer_listener, wl); +} + +static void data_device_handle_enter(void *data, + struct wl_data_device *wl_data_device, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t x, + wl_fixed_t y, + struct wl_data_offer *id) +{ + struct vo_wayland_state *wl = data; + if (wl->input.offer != id) + MP_FATAL(wl, "Fatal dnd error (Please report this issue)\n"); + + wl_data_offer_accept(id, serial, "text/uri-list"); +} + +static void data_device_handle_leave(void *data, + struct wl_data_device *wl_data_device) +{ + struct vo_wayland_state *wl = data; + if (wl->input.offer) { + wl_data_offer_destroy(wl->input.offer); + wl->input.offer = NULL; + } + // dnd fd is closed on POLLHUP +} + +static void data_device_handle_motion(void *data, + struct wl_data_device *wl_data_device, + uint32_t time, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void data_device_handle_drop(void *data, + struct wl_data_device *wl_data_device) +{ + struct vo_wayland_state *wl = data; + + int pipefd[2]; + + if (pipe(pipefd) == -1) { + MP_FATAL(wl, "can't create pipe for dnd communication\n"); + return; + } + + wl->input.dnd_fd = pipefd[0]; + wl_data_offer_receive(wl->input.offer, "text/uri-list", pipefd[1]); + close(pipefd[1]); +} + +static void data_device_handle_selection(void *data, + struct wl_data_device *wl_data_device, + struct wl_data_offer *id) +{ +} + +static const struct wl_data_device_listener data_device_listener = { + data_device_handle_data_offer, + data_device_handle_enter, + data_device_handle_leave, + data_device_handle_motion, + data_device_handle_drop, + data_device_handle_selection +}; + static void registry_handle_global (void *data, struct wl_registry *registry, uint32_t id, @@ -502,11 +595,22 @@ static void registry_handle_global (void *data, wl_list_insert(&wl->display.output_list, &output->link); } + else if (strcmp(interface, "wl_data_device_manager") == 0) { + + wl->input.devman = wl_registry_bind(reg, + id, + &wl_data_device_manager_interface, + 1); + } + else if (strcmp(interface, "wl_seat") == 0) { wl->input.seat = wl_registry_bind(reg, id, &wl_seat_interface, 1); - wl_seat_add_listener(wl->input.seat, &seat_listener, wl); + + wl->input.datadev = wl_data_device_manager_get_data_device( + wl->input.devman, wl->input.seat); + wl_data_device_add_listener(wl->input.datadev, &data_device_listener, wl); } } @@ -629,6 +733,29 @@ static void shedule_resize(struct vo_wayland_state *wl, wl->vo->dheight = height; } +// stolen from x11 code +// The data is in the form of the mimetype text/uri-list. +static bool dnd_handle_drop_data(struct vo *vo, bstr data) +{ + void *tmp = talloc_new(NULL); + int num_files = 0; + char **files = NULL; + + while (data.len) { + bstr line = bstr_getline(data, &data); + line = bstr_strip_linebreaks(line); + if (bstr_startswith0(line, "#")) + continue; + + char *s = bstrto0(tmp, line); + MP_TARRAY_APPEND(tmp, files, num_files, s); + + } + vo_drop_files(vo, num_files, files); + + talloc_free(tmp); + return num_files > 0; +} static bool create_display (struct vo_wayland_state *wl) { @@ -731,6 +858,8 @@ static bool create_input (struct vo_wayland_state *wl) return false; } + wl->input.dnd_fd = -1; + return true; } @@ -746,9 +875,14 @@ static void destroy_input (struct vo_wayland_state *wl) if (wl->input.pointer) wl_pointer_destroy(wl->input.pointer); + if (wl->input.datadev) + wl_data_device_destroy(wl->input.datadev); + + if (wl->input.devman) + wl_data_device_manager_destroy(wl->input.devman); + if (wl->input.seat) wl_seat_destroy(wl->input.seat); - } /*** mplayer2 interface ***/ @@ -863,6 +997,55 @@ static int vo_wayland_check_events (struct vo *vo) wl_display_flush(dp); } + /* If drag & drop was ended poll the file descriptor from the offer if + * there is data to read. + * We only accept the mime type text/uri-list. + */ + if (wl->input.dnd_fd != -1) { + fd.fd = wl->input.dnd_fd; + fd.events = POLLIN | POLLHUP | POLLERR; + + if (poll(&fd, 1, 0) > 0) { + if (fd.revents & POLLERR) { + MP_ERR(wl, "error occured on the drag&drop fd\n"); + close(wl->input.dnd_fd); + wl->input.dnd_fd = -1; + } + + if (fd.revents & POLLIN) { + int const to_read = 2048; + char *buffer = malloc(to_read); + size_t buffer_len = to_read; + size_t str_len = 0; + int has_read = 0; + + while (0 < (has_read = read(fd.fd, buffer, to_read))) { + if (buffer_len + to_read < buffer_len) { + MP_ERR(wl, "Integer overflow while reading from fd\n"); + free(buffer); + break; + } + + str_len += has_read; + buffer_len += to_read; + buffer = realloc(buffer, buffer_len); + + if (has_read < to_read) { + buffer[str_len] = 0; + struct bstr file_list = bstr0(buffer); + dnd_handle_drop_data(wl->vo, file_list); + break; + } + } + } + + if (fd.revents & POLLHUP) { + close(wl->input.dnd_fd); + wl->input.dnd_fd = -1; + } + } + } + // window events are reset by the resizing code return wl->window.events; } diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h index 30fc9b72d2..01ff09fa43 100644 --- a/video/out/wayland_common.h +++ b/video/out/wayland_common.h @@ -100,6 +100,11 @@ struct vo_wayland_state { struct xkb_keymap *keymap; struct xkb_state *state; } xkb; + + struct wl_data_device_manager *devman; + struct wl_data_device *datadev; + struct wl_data_offer *offer; + int dnd_fd; } input; }; -- cgit v1.2.3