diff options
author | Tony Crisci <tony@dubstepdish.com> | 2017-11-22 08:10:06 -0500 |
---|---|---|
committer | Tony Crisci <tony@dubstepdish.com> | 2017-11-22 08:10:06 -0500 |
commit | b0683874e98a2dab7cc73734355bb8e0ad656113 (patch) | |
tree | 810f987a840d25b302d235129aa37185b5283522 /xwayland/selection.c | |
parent | ea6f77b4840f8a282f645f8ceb3fdd7b72d6af65 (diff) |
xwm: send selection data
Diffstat (limited to 'xwayland/selection.c')
-rw-r--r-- | xwayland/selection.c | 285 |
1 files changed, 261 insertions, 24 deletions
diff --git a/xwayland/selection.c b/xwayland/selection.c index acc93728..3d57f394 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -2,38 +2,236 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> +#include <assert.h> #include <xcb/xfixes.h> #include <fcntl.h> #include "wlr/util/log.h" #include "wlr/types/wlr_data_device.h" #include "xwm.h" -static int xwm_handle_selection_property_notify(struct wlr_xwm *xwm, - xcb_generic_event_t *event) { - xcb_property_notify_event_t *property_notify = - (xcb_property_notify_event_t *) event; - - if (property_notify->window == xwm->selection_window) { - if (property_notify->state == XCB_PROPERTY_NEW_VALUE && - property_notify->atom == xwm->atoms[WL_SELECTION] && - xwm->incr) - wlr_log(L_DEBUG, "TODO: get selection"); - return 1; - } else if (property_notify->window == xwm->selection_request.requestor) { - if (property_notify->state == XCB_PROPERTY_DELETE && - property_notify->atom == xwm->selection_request.property && - xwm->incr) - wlr_log(L_DEBUG, "TODO: send selection"); - return 1; +static const size_t incr_chunk_size = 64 * 1024; + +static void xwm_send_selection_notify(struct wlr_xwm *xwm, + xcb_atom_t property) { + xcb_selection_notify_event_t selection_notify; + + memset(&selection_notify, 0, sizeof selection_notify); + selection_notify.response_type = XCB_SELECTION_NOTIFY; + selection_notify.sequence = 0; + selection_notify.time = xwm->selection_request.time; + selection_notify.requestor = xwm->selection_request.requestor; + selection_notify.selection = xwm->selection_request.selection; + selection_notify.target = xwm->selection_request.target; + selection_notify.property = property; + + xcb_send_event(xwm->xcb_conn, 0, // propagate + xwm->selection_request.requestor, + XCB_EVENT_MASK_NO_EVENT, (char *)&selection_notify); +} + +static int xwm_flush_source_data(struct wlr_xwm *xwm) +{ + xcb_change_property(xwm->xcb_conn, + XCB_PROP_MODE_REPLACE, + xwm->selection_request.requestor, + xwm->selection_request.property, + xwm->selection_target, + 8, // format + xwm->source_data.size, + xwm->source_data.data); + xwm->selection_property_set = 1; + int length = xwm->source_data.size; + xwm->source_data.size = 0; + + return length; +} + +static int xwm_read_data_source(int fd, uint32_t mask, void *data) { + struct wlr_xwm *xwm = data; + void *p; + + int current = xwm->source_data.size; + if (xwm->source_data.size < incr_chunk_size) { + p = wl_array_add(&xwm->source_data, incr_chunk_size); + } else { + p = (char *) xwm->source_data.data + xwm->source_data.size; } - return 0; + int available = xwm->source_data.alloc - current; + + int len = read(fd, p, available); + if (len == -1) { + wlr_log(L_ERROR, "read error from data source: %m\n"); + xwm_send_selection_notify(xwm, XCB_ATOM_NONE); + wl_event_source_remove(xwm->property_source); + xwm->property_source = NULL; + close(fd); + wl_array_release(&xwm->source_data); + } + + wlr_log(L_DEBUG, "read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n", + len, available, mask, len, (char *) p); + + xwm->source_data.size = current + len; + if (xwm->source_data.size >= incr_chunk_size) { + if (!xwm->incr) { + wlr_log(L_DEBUG, "got %zu bytes, starting incr\n", + xwm->source_data.size); + xwm->incr = 1; + xcb_change_property(xwm->xcb_conn, + XCB_PROP_MODE_REPLACE, + xwm->selection_request.requestor, + xwm->selection_request.property, + xwm->atoms[INCR], + 32, /* format */ + 1, &incr_chunk_size); + xwm->selection_property_set = 1; + xwm->flush_property_on_delete = 1; + wl_event_source_remove(xwm->property_source); + xwm->property_source = NULL; + xwm_send_selection_notify(xwm, xwm->selection_request.property); + } else if (xwm->selection_property_set) { + wlr_log(L_DEBUG, "got %zu bytes, waiting for " + "property delete\n", xwm->source_data.size); + + xwm->flush_property_on_delete = 1; + wl_event_source_remove(xwm->property_source); + xwm->property_source = NULL; + } else { + wlr_log(L_DEBUG, "got %zu bytes, " + "property deleted, setting new property\n", + xwm->source_data.size); + xwm_flush_source_data(xwm); + } + } else if (len == 0 && !xwm->incr) { + wlr_log(L_DEBUG, "non-incr transfer complete\n"); + /* Non-incr transfer all done. */ + xwm_flush_source_data(xwm); + xwm_send_selection_notify(xwm, xwm->selection_request.property); + xcb_flush(xwm->xcb_conn); + wl_event_source_remove(xwm->property_source); + xwm->property_source = NULL; + close(fd); + wl_array_release(&xwm->source_data); + xwm->selection_request.requestor = XCB_NONE; + } else if (len == 0 && xwm->incr) { + wlr_log(L_DEBUG, "incr transfer complete\n"); + + xwm->flush_property_on_delete = 1; + if (xwm->selection_property_set) { + wlr_log(L_DEBUG, "got %zu bytes, waiting for " + "property delete\n", xwm->source_data.size); + } else { + wlr_log(L_DEBUG, "got %zu bytes, " + "property deleted, setting new property\n", + xwm->source_data.size); + xwm_flush_source_data(xwm); + } + xcb_flush(xwm->xcb_conn); + wl_event_source_remove(xwm->property_source); + xwm->property_source = NULL; + close(xwm->data_source_fd); + xwm->data_source_fd = -1; + close(fd); + } else { + wlr_log(L_DEBUG, "nothing happened, buffered the bytes\n"); + } + + return 1; +} + +static void xwm_send_data(struct wlr_xwm *xwm, xcb_atom_t target, + const char *mime_type) { + struct wlr_data_source *source; + int p[2]; + + if (pipe(p) == -1) { + wlr_log(L_ERROR, "pipe failed: %m\n"); + xwm_send_selection_notify(xwm, XCB_ATOM_NONE); + return; + } + + fcntl(p[0], F_SETFD, FD_CLOEXEC); + fcntl(p[0], F_SETFL, O_NONBLOCK); + fcntl(p[1], F_SETFD, FD_CLOEXEC); + fcntl(p[1], F_SETFL, O_NONBLOCK); + + wl_array_init(&xwm->source_data); + xwm->selection_target = target; + xwm->data_source_fd = p[0]; + struct wl_event_loop *loop = + wl_display_get_event_loop(xwm->xwayland->wl_display); + xwm->property_source = wl_event_loop_add_fd(loop, + xwm->data_source_fd, + WL_EVENT_READABLE, + xwm_read_data_source, + xwm); + + source = xwm->seat->selection_source; + source->send(source, mime_type, p[1]); + close(p[1]); +} + +static void xwm_send_timestamp(struct wlr_xwm *xwm) { + xcb_change_property(xwm->xcb_conn, + XCB_PROP_MODE_REPLACE, + xwm->selection_request.requestor, + xwm->selection_request.property, + XCB_ATOM_INTEGER, + 32, // format + 1, &xwm->selection_timestamp); + + xwm_send_selection_notify(xwm, xwm->selection_request.property); +} + +static void xwm_send_targets(struct wlr_xwm *xwm) { + xcb_atom_t targets[] = { + xwm->atoms[TIMESTAMP], + xwm->atoms[TARGETS], + xwm->atoms[UTF8_STRING], + xwm->atoms[TEXT], + }; + + xcb_change_property(xwm->xcb_conn, + XCB_PROP_MODE_REPLACE, + xwm->selection_request.requestor, + xwm->selection_request.property, + XCB_ATOM_ATOM, + 32, // format + sizeof(targets) / sizeof(targets[0]), targets); + + xwm_send_selection_notify(xwm, xwm->selection_request.property); } static void xwm_handle_selection_request(struct wlr_xwm *xwm, xcb_generic_event_t *event) { - wlr_log(L_DEBUG, "TODO: SELECTION REQUEST"); - return; + xcb_selection_request_event_t *selection_request = + (xcb_selection_request_event_t *) event; + + xwm->selection_request = *selection_request; + xwm->incr = 0; + xwm->flush_property_on_delete = 0; + + if (selection_request->selection == xwm->atoms[CLIPBOARD_MANAGER]) { + // The wlroots clipboard should already have grabbed + // the first target, so just send selection notify + // now. This isn't synchronized with the clipboard + // finishing getting the data, so there's a race here. + xwm_send_selection_notify(xwm, xwm->selection_request.property); + return; + } + + if (selection_request->target == xwm->atoms[TARGETS]) { + xwm_send_targets(xwm); + } else if (selection_request->target == xwm->atoms[TIMESTAMP]) { + xwm_send_timestamp(xwm); + } else if (selection_request->target == xwm->atoms[UTF8_STRING] || + selection_request->target == xwm->atoms[TEXT]) { + xwm_send_data(xwm, xwm->atoms[UTF8_STRING], "text/plain;charset=utf-8"); + } else { + wlr_log(L_DEBUG, "can only handle UTF8_STRING targets\n"); + xwm_send_selection_notify(xwm, XCB_ATOM_NONE); + } } static int writable_callback(int fd, uint32_t mask, void *data) { @@ -205,7 +403,7 @@ static void xwm_get_selection_targets(struct wlr_xwm *xwm) { } } - wlr_seat_set_selection(xwm->xwayland->seat, &source->base, + wlr_seat_set_selection(xwm->seat, &source->base, wl_display_next_serial(xwm->xwayland->wl_display)); free(reply); @@ -271,7 +469,7 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event) { - if (!xwm->xwayland->seat) { + if (!xwm->seat) { wlr_log(L_DEBUG, "not handling selection events:" "no seat assigned to xwayland"); return 0; @@ -281,8 +479,6 @@ int xwm_handle_selection_event(struct wlr_xwm *xwm, case XCB_SELECTION_NOTIFY: xwm_handle_selection_notify(xwm, event); return 1; - case XCB_PROPERTY_NOTIFY: - return xwm_handle_selection_property_notify(xwm, event); case XCB_SELECTION_REQUEST: xwm_handle_selection_request(xwm, event); return 1; @@ -327,3 +523,44 @@ void xwm_selection_init(struct wlr_xwm *xwm) { xcb_xfixes_select_selection_input(xwm->xcb_conn, xwm->selection_window, xwm->atoms[CLIPBOARD], mask); } + +static void handle_seat_set_selection(struct wl_listener *listener, + void *data) { + struct wlr_seat *seat = data; + struct wlr_xwm *xwm = + wl_container_of(listener, xwm, seat_selection_change); + struct wlr_data_source *source = seat->selection_source; + + if (source == NULL) { + if (xwm->selection_owner == xwm->selection_window) { + xcb_set_selection_owner(xwm->xcb_conn, + XCB_ATOM_NONE, + xwm->atoms[CLIPBOARD], + xwm->selection_timestamp); + } + + return; + } + + if (source->send == data_source_send) + return; + + xcb_set_selection_owner(xwm->xcb_conn, + xwm->selection_window, + xwm->atoms[CLIPBOARD], + XCB_TIME_CURRENT_TIME); +} + +void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat) { + assert(xwm); + assert(seat); + if (xwm->seat) { + wl_list_remove(&xwm->seat_selection_change.link); + xwm->seat = NULL; + } + + wl_signal_add(&seat->events.selection, &xwm->seat_selection_change); + xwm->seat_selection_change.notify = handle_seat_set_selection; + xwm->seat = seat; + handle_seat_set_selection(&xwm->seat_selection_change, seat); +} |