aboutsummaryrefslogtreecommitdiff
path: root/xwayland/selection.c
diff options
context:
space:
mode:
authorTony Crisci <tony@dubstepdish.com>2017-11-22 08:10:06 -0500
committerTony Crisci <tony@dubstepdish.com>2017-11-22 08:10:06 -0500
commitb0683874e98a2dab7cc73734355bb8e0ad656113 (patch)
tree810f987a840d25b302d235129aa37185b5283522 /xwayland/selection.c
parentea6f77b4840f8a282f645f8ceb3fdd7b72d6af65 (diff)
xwm: send selection data
Diffstat (limited to 'xwayland/selection.c')
-rw-r--r--xwayland/selection.c285
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);
+}