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.
This commit is contained in:
parent
727bacdddf
commit
33f8670ee2
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user