aboutsummaryrefslogtreecommitdiff
path: root/xwayland
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
parentea6f77b4840f8a282f645f8ceb3fdd7b72d6af65 (diff)
xwm: send selection data
Diffstat (limited to 'xwayland')
-rw-r--r--xwayland/selection.c285
-rw-r--r--xwayland/xwayland.c5
-rw-r--r--xwayland/xwm.c2
-rw-r--r--xwayland/xwm.h10
4 files changed, 278 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);
+}
diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c
index ecec785c..bd43fefb 100644
--- a/xwayland/xwayland.c
+++ b/xwayland/xwayland.c
@@ -341,3 +341,8 @@ void wlr_xwayland_set_cursor(struct wlr_xwayland *wlr_xwayland,
wlr_xwayland->cursor->hotspot_x = hotspot_x;
wlr_xwayland->cursor->hotspot_y = hotspot_y;
}
+
+void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland,
+ struct wlr_seat *seat) {
+ xwm_set_seat(xwayland->xwm, seat);
+}
diff --git a/xwayland/xwm.c b/xwayland/xwm.c
index af429a5c..407ee362 100644
--- a/xwayland/xwm.c
+++ b/xwayland/xwm.c
@@ -48,6 +48,8 @@ const char *atom_map[ATOM_LAST] = {
"TARGETS",
"CLIPBOARD_MANAGER",
"INCR",
+ "TEXT",
+ "TIMESTAMP",
};
/* General helpers */
diff --git a/xwayland/xwm.h b/xwayland/xwm.h
index a7bfd623..47e674c0 100644
--- a/xwayland/xwm.h
+++ b/xwayland/xwm.h
@@ -36,6 +36,8 @@ enum atom_name {
TARGETS,
CLIPBOARD_MANAGER,
INCR,
+ TEXT,
+ TIMESTAMP,
ATOM_LAST,
};
@@ -50,6 +52,7 @@ enum net_wm_state_action {
struct wlr_xwm {
struct wlr_xwayland *xwayland;
struct wl_event_source *event_source;
+ struct wlr_seat *seat;
xcb_atom_t atoms[ATOM_LAST];
xcb_connection_t *xcb_conn;
@@ -70,6 +73,10 @@ struct wlr_xwm {
int property_start;
xcb_get_property_reply_t *property_reply;
struct wl_event_source *property_source;
+ int flush_property_on_delete;
+ struct wl_array source_data;
+ xcb_atom_t selection_target;
+ bool selection_property_set;
struct wlr_xwayland_surface *focus_surface;
@@ -79,6 +86,7 @@ struct wlr_xwm {
const xcb_query_extension_reply_t *xfixes;
struct wl_listener compositor_surface_create;
+ struct wl_listener seat_selection_change;
};
struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland);
@@ -92,4 +100,6 @@ int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event);
void xwm_selection_init(struct wlr_xwm *xwm);
+void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat);
+
#endif