summaryrefslogtreecommitdiff
path: root/compositor
diff options
context:
space:
mode:
authorKristian Høgsberg <krh@bitplanet.net>2011-12-28 22:29:23 -0500
committerKristian Høgsberg <krh@bitplanet.net>2011-12-28 22:42:09 -0500
commit33f8670ee2448d5602a30c0b936af294b24fb358 (patch)
tree04a77599b188478de396ff441e61fa9c42cffb2f /compositor
parent727bacdddfa7632bb349b5c79f30a5a73eb79a1f (diff)
x11: Bridge Wayland selections to X11 CLIPBOARD selection
This is the other direction. The selection bridge will grab the X11 CLIPBOARD selection on behalf of the Wayland client when it sets the Wayland selection. Right now only UTF-8 text is supported, but the data types offered will be taken from the Wayland data source.
Diffstat (limited to 'compositor')
-rw-r--r--compositor/compositor.h2
-rw-r--r--compositor/data-device.c2
-rw-r--r--compositor/xserver-launcher.c329
3 files changed, 327 insertions, 6 deletions
diff --git a/compositor/compositor.h b/compositor/compositor.h
index 359c8880..4c011f01 100644
--- a/compositor/compositor.h
+++ b/compositor/compositor.h
@@ -457,6 +457,8 @@ void
wlsc_xserver_destroy(struct wlsc_compositor *compositor);
void
wlsc_xserver_surface_activate(struct wlsc_surface *surface);
+void
+wlsc_xserver_set_selection(struct wlsc_input_device *device);
struct wlsc_zoom;
typedef void (*wlsc_zoom_done_func_t)(struct wlsc_zoom *zoom, void *data);
diff --git a/compositor/data-device.c b/compositor/data-device.c
index 6410608c..c772246c 100644
--- a/compositor/data-device.c
+++ b/compositor/data-device.c
@@ -339,6 +339,8 @@ wlsc_input_device_set_selection(struct wlsc_input_device *device,
}
}
+ wlsc_xserver_set_selection(device);
+
device->selection_data_source_listener.func =
destroy_selection_data_source;
wl_list_insert(source->resource.destroy_listener_list.prev,
diff --git a/compositor/xserver-launcher.c b/compositor/xserver-launcher.c
index 3a4cc347..7d2d134c 100644
--- a/compositor/xserver-launcher.c
+++ b/compositor/xserver-launcher.c
@@ -75,6 +75,12 @@ struct wlsc_wm {
struct wl_event_source *property_source;
xcb_get_property_reply_t *property_reply;
int property_start;
+ struct wl_array source_data;
+ xcb_selection_request_event_t selection_request;
+ xcb_atom_t selection_target;
+ xcb_timestamp_t selection_timestamp;
+ int selection_property_set;
+ int flush_property_on_delete;
struct {
xcb_atom_t wm_protocols;
@@ -469,6 +475,39 @@ wlsc_wm_get_incr_chunk(struct wlsc_wm *wm)
}
}
+void
+wlsc_xserver_set_selection(struct wlsc_input_device *device)
+{
+ struct wlsc_xserver *wxs = device->compositor->wxs;
+ struct wlsc_wm *wm = wxs->wm;
+ struct wlsc_data_source *source;
+ const char **p, **end;
+ int has_text_plain = 0;
+
+ fprintf(stderr, "set selection\n");
+
+ source = device->selection_data_source;
+ p = source->mime_types.data;
+ end = (const char **)
+ ((char *) source->mime_types.data + source->mime_types.size);
+
+ while (p < end) {
+ fprintf(stderr, " %s\n", *p);
+ if (strcmp(*p, "text/plain") == 0 ||
+ strcmp(*p, "text/plain;charset=utf-8") == 0)
+ has_text_plain = 1;
+ p++;
+ }
+
+ if (wm && has_text_plain &&
+ source->create_offer != data_source_create_offer) {
+ xcb_set_selection_owner(wm->conn,
+ wm->selection_window,
+ wm->atom.clipboard,
+ XCB_TIME_CURRENT_TIME);
+ }
+}
+
static void
wlsc_wm_handle_configure_request(struct wlsc_wm *wm, xcb_generic_event_t *event)
{
@@ -555,14 +594,9 @@ wlsc_wm_handle_map_request(struct wlsc_wm *wm, xcb_generic_event_t *event)
{
xcb_map_request_event_t *map_request =
(xcb_map_request_event_t *) event;
- uint32_t values[1];
fprintf(stderr, "XCB_MAP_REQUEST (window %d)\n", map_request->window);
- values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
- xcb_change_window_attributes(wm->conn, map_request->window,
- XCB_CW_EVENT_MASK, values);
-
xcb_map_window(wm->conn, map_request->window);
}
@@ -648,6 +682,265 @@ wlsc_wm_handle_map_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
wlsc_wm_activate(wm, window, XCB_TIME_CURRENT_TIME);
}
+static const int incr_chunk_size = 64 * 1024;
+
+static void
+wlsc_wm_send_selection_notify(struct wlsc_wm *wm, 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 = wm->selection_request.time;
+ selection_notify.requestor = wm->selection_request.requestor;
+ selection_notify.selection = wm->selection_request.selection;
+ selection_notify.target = wm->selection_request.target;
+ selection_notify.property = property;
+
+ xcb_send_event(wm->conn, 0, /* propagate */
+ wm->selection_request.requestor,
+ XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
+}
+
+static void
+wlsc_wm_send_targets(struct wlsc_wm *wm)
+{
+ xcb_atom_t targets[] = {
+ wm->atom.timestamp,
+ wm->atom.targets,
+ wm->atom.utf8_string,
+ /* wm->atom.compound_text, */
+ wm->atom.text,
+ /* wm->atom.string */
+ };
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ XCB_ATOM_ATOM,
+ 32, /* format */
+ ARRAY_LENGTH(targets), targets);
+
+ wlsc_wm_send_selection_notify(wm, wm->selection_request.property);
+}
+
+static void
+wlsc_wm_send_timestamp(struct wlsc_wm *wm)
+{
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ XCB_ATOM_INTEGER,
+ 32, /* format */
+ 1, &wm->selection_timestamp);
+
+ wlsc_wm_send_selection_notify(wm, wm->selection_request.property);
+}
+
+static int
+wlsc_wm_flush_source_data(struct wlsc_wm *wm)
+{
+ int length;
+
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ wm->selection_target,
+ 8, /* format */
+ wm->source_data.size,
+ wm->source_data.data);
+ wm->selection_property_set = 1;
+ length = wm->source_data.size;
+ wm->source_data.size = 0;
+
+ return length;
+}
+
+static int
+wlsc_wm_read_data_source(int fd, uint32_t mask, void *data)
+{
+ struct wlsc_wm *wm = data;
+ int len, current, available;
+ void *p;
+
+ current = wm->source_data.size;
+ if (wm->source_data.size < incr_chunk_size)
+ p = wl_array_add(&wm->source_data, incr_chunk_size);
+ else
+ p = (char *) wm->source_data.data + wm->source_data.size;
+ available = wm->source_data.alloc - current;
+
+ len = read(fd, p, available);
+ if (len == -1) {
+ fprintf(stderr, "read error from data source: %m\n");
+ wlsc_wm_send_selection_notify(wm, XCB_ATOM_NONE);
+ wl_event_source_remove(wm->property_source);
+ close(fd);
+ wl_array_release(&wm->source_data);
+ }
+
+ fprintf(stderr, "read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
+ len, available, mask, len, (char *) p);
+
+ wm->source_data.size = current + len;
+ if (wm->source_data.size >= incr_chunk_size) {
+ if (!wm->incr) {
+ fprintf(stderr, "got %d bytes, starting incr\n",
+ wm->source_data.size);
+ wm->incr = 1;
+ xcb_change_property(wm->conn,
+ XCB_PROP_MODE_REPLACE,
+ wm->selection_request.requestor,
+ wm->selection_request.property,
+ wm->atom.incr,
+ 32, /* format */
+ 1, &incr_chunk_size);
+ wm->selection_property_set = 1;
+ wm->flush_property_on_delete = 1;
+ wl_event_source_remove(wm->property_source);
+ wlsc_wm_send_selection_notify(wm, wm->selection_request.property);
+ } else if (wm->selection_property_set) {
+ fprintf(stderr, "got %d bytes, waiting for "
+ "property delete\n", wm->source_data.size);
+
+ wm->flush_property_on_delete = 1;
+ wl_event_source_remove(wm->property_source);
+ } else {
+ fprintf(stderr, "got %d bytes, "
+ "property deleted, seting new property\n",
+ wm->source_data.size);
+ wlsc_wm_flush_source_data(wm);
+ }
+ } else if (len == 0 && !wm->incr) {
+ fprintf(stderr, "non-incr transfer complete\n");
+ /* Non-incr transfer all done. */
+ wlsc_wm_flush_source_data(wm);
+ wlsc_wm_send_selection_notify(wm, wm->selection_request.property);
+ xcb_flush(wm->conn);
+ wl_event_source_remove(wm->property_source);
+ close(fd);
+ wl_array_release(&wm->source_data);
+ wm->selection_request.requestor = XCB_NONE;
+ } else if (len == 0 && wm->incr) {
+ fprintf(stderr, "incr transfer complete\n");
+
+ wm->flush_property_on_delete = 1;
+ if (wm->selection_property_set) {
+ fprintf(stderr, "got %d bytes, waiting for "
+ "property delete\n", wm->source_data.size);
+ } else {
+ fprintf(stderr, "got %d bytes, "
+ "property deleted, seting new property\n",
+ wm->source_data.size);
+ wlsc_wm_flush_source_data(wm);
+ }
+ xcb_flush(wm->conn);
+ wl_event_source_remove(wm->property_source);
+ wm->data_source_fd = -1;
+ close(fd);
+ } else {
+ fprintf(stderr, "nothing happened, buffered the bytes\n");
+ }
+
+ return 1;
+}
+
+static void
+wlsc_wm_send_data(struct wlsc_wm *wm, xcb_atom_t target, const char *mime_type)
+{
+ struct wlsc_input_device *device = (struct wlsc_input_device *)
+ wm->server->compositor->input_device;
+ int p[2];
+
+ if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
+ fprintf(stderr, "pipe2 failed: %m\n");
+ wlsc_wm_send_selection_notify(wm, XCB_ATOM_NONE);
+ return;
+ }
+
+ wl_array_init(&wm->source_data);
+ wm->selection_target = target;
+ wm->data_source_fd = p[0];
+ wm->property_source = wl_event_loop_add_fd(wm->server->loop,
+ wm->data_source_fd,
+ WL_EVENT_READABLE,
+ wlsc_wm_read_data_source,
+ wm);
+
+ wl_resource_post_event(&device->selection_data_source->resource,
+ WL_DATA_SOURCE_SEND, mime_type, p[1]);
+ close(p[1]);
+}
+
+static void
+wlsc_wm_send_incr_chunk(struct wlsc_wm *wm)
+{
+ fprintf(stderr, "property deleted\n");
+ int length;
+
+ wm->selection_property_set = 0;
+ if (wm->flush_property_on_delete) {
+ fprintf(stderr, "setting new property, %d bytes\n",
+ wm->source_data.size);
+ wm->flush_property_on_delete = 0;
+ length = wlsc_wm_flush_source_data(wm);
+
+ if (wm->data_source_fd >= 0) {
+ wm->property_source =
+ wl_event_loop_add_fd(wm->server->loop,
+ wm->data_source_fd,
+ WL_EVENT_READABLE,
+ wlsc_wm_read_data_source,
+ wm);
+ } else if (length > 0) {
+ /* Transfer is all done, but queue a flush for
+ * the delete of the last chunk so we can set
+ * the 0 sized propert to signal the end of
+ * the transfer. */
+ wm->flush_property_on_delete = 1;
+ wl_array_release(&wm->source_data);
+ } else {
+ wm->selection_request.requestor = XCB_NONE;
+ }
+ }
+}
+
+static void
+wlsc_wm_handle_selection_request(struct wlsc_wm *wm,
+ xcb_generic_event_t *event)
+{
+ xcb_selection_request_event_t *selection_request =
+ (xcb_selection_request_event_t *) event;
+
+ fprintf(stderr, "selection request, %s, ",
+ get_atom_name(wm->conn, selection_request->selection));
+ fprintf(stderr, "target %s, ",
+ get_atom_name(wm->conn, selection_request->target));
+ fprintf(stderr, "property %s\n",
+ get_atom_name(wm->conn, selection_request->property));
+
+ wm->selection_request = *selection_request;
+ wm->incr = 0;
+ wm->flush_property_on_delete = 0;
+
+ if (selection_request->target == wm->atom.targets) {
+ wlsc_wm_send_targets(wm);
+ } else if (selection_request->target == wm->atom.timestamp) {
+ wlsc_wm_send_timestamp(wm);
+ } else if (selection_request->target == wm->atom.utf8_string ||
+ selection_request->target == wm->atom.text) {
+ wlsc_wm_send_data(wm, wm->atom.utf8_string,
+ "text/plain;charset=utf-8");
+ } else {
+ fprintf(stderr, "can only handle UTF8_STRING targets...\n");
+ wlsc_wm_send_selection_notify(wm, XCB_ATOM_NONE);
+ }
+}
+
static void
wlsc_wm_handle_property_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
{
@@ -659,6 +952,11 @@ wlsc_wm_handle_property_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
property_notify->atom == wm->atom.wl_selection &&
wm->incr)
wlsc_wm_get_incr_chunk(wm);
+ } else if (property_notify->window == wm->selection_request.requestor) {
+ if (property_notify->state == XCB_PROPERTY_DELETE &&
+ property_notify->atom == wm->selection_request.property &&
+ wm->incr)
+ wlsc_wm_send_incr_chunk(wm);
} else if (property_notify->atom == XCB_ATOM_WM_CLASS) {
fprintf(stderr, "wm_class changed\n");
} else if (property_notify->atom == XCB_ATOM_WM_TRANSIENT_FOR) {
@@ -688,6 +986,7 @@ wlsc_wm_handle_create_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
xcb_create_notify_event_t *create_notify =
(xcb_create_notify_event_t *) event;
struct wlsc_wm_window *window;
+ uint32_t values[1];
fprintf(stderr, "XCB_CREATE_NOTIFY (window %d)\n",
create_notify->window);
@@ -698,6 +997,10 @@ wlsc_wm_handle_create_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
return;
}
+ values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
+ xcb_change_window_attributes(wm->conn, create_notify->window,
+ XCB_CW_EVENT_MASK, values);
+
memset(window, 0, sizeof *window);
window->id = create_notify->window;
hash_table_insert(wm->window_hash, window->id, window);
@@ -753,11 +1056,20 @@ wlsc_wm_handle_xfixes_selection_notify(struct wlsc_wm *wm,
printf("xfixes selection notify event: owner %d\n",
xfixes_selection_notify->owner);
+ /* We have to use XCB_TIME_CURRENT_TIME when we claim the
+ * selection, so grab the actual timestamp here so we can
+ * answer TIMESTAMP conversion requests correctly. */
+ if (xfixes_selection_notify->owner == wm->selection_window) {
+ wm->selection_timestamp = xfixes_selection_notify->timestamp;
+ fprintf(stderr, "our window, skipping\n");
+ return;
+ }
+
xcb_convert_selection(wm->conn, wm->selection_window,
wm->atom.clipboard,
wm->atom.targets,
wm->atom.wl_selection,
- XCB_TIME_CURRENT_TIME);
+ xfixes_selection_notify->timestamp);
xcb_flush(wm->conn);
}
@@ -801,6 +1113,9 @@ wlsc_wm_handle_event(int fd, uint32_t mask, void *data)
case XCB_SELECTION_NOTIFY:
wlsc_wm_handle_selection_notify(wm, event);
break;
+ case XCB_SELECTION_REQUEST:
+ wlsc_wm_handle_selection_request(wm, event);
+ break;
}
switch (event->response_type - wm->xfixes->first_event) {
@@ -948,6 +1263,8 @@ wlsc_wm_create(struct wlsc_xserver *wxs)
xcb_change_window_attributes(wm->conn, wm->screen->root,
XCB_CW_EVENT_MASK, values);
+ wm->selection_request.requestor = XCB_NONE;
+
wm->selection_window = xcb_generate_id(wm->conn);
xcb_create_window(wm->conn,
XCB_COPY_FROM_PARENT,