This commit implements the selection proxy to connect XWayland copy-paste with the Wayland one, bringing back the last remaining feature we had before the xwm was split as a client (#2 selection support).
It connects the clipboard also for dealing with clients that go away. Worth to note that this only brings the needed implementation for porting selection to weston-xwm client and, that being said, I'm sure there are corn cases still to be solved. For testing, I'm opening one X gedit and one weston-terminal and then selecting text and performing copy-paste on both directions. Signed-off-by: Tiago Vignatti <[email protected]> --- clients/Makefile.am | 1 + clients/xwm-selection.c | 222 ++++++++++++++++------------------------- clients/xwm.c | 44 ++++---- clients/xwm.h | 25 ++++- protocol/xserver.xml | 18 ++++ src/xwayland/window-manager.c | 55 +++++++++- src/xwayland/xwayland.h | 1 + 7 files changed, 208 insertions(+), 158 deletions(-) diff --git a/clients/Makefile.am b/clients/Makefile.am index 022e791..ea03eed 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -174,6 +174,7 @@ weston_tablet_shell_LDADD = $(toolkit_libs) weston_xwm_SOURCES = \ xwm.h \ xwm.c \ + xwm-selection.c \ xserver-client-protocol.h \ xserver-protocol.c weston_xwm_LDADD = $(toolkit_libs) diff --git a/clients/xwm-selection.c b/clients/xwm-selection.c index f31afde..ab33b07 100644 --- a/clients/xwm-selection.c +++ b/clients/xwm-selection.c @@ -26,14 +26,17 @@ #include <string.h> #include <unistd.h> #include <fcntl.h> +#include <sys/epoll.h> -#include "xwayland.h" -#include "window-manager.h" +#include <wayland-client.h> +#include "xserver-client-protocol.h" +#include "xwm.h" -static int -xwm_write_property(int fd, uint32_t mask, void *data) +static void +xwm_write_property(struct task *task, uint32_t events) { - struct xwm_wm *wm = data; + struct xwm_wm *wm = + container_of(task, struct xwm_wm, selection_write_task); unsigned char *property; int len, remainder; @@ -41,13 +44,14 @@ xwm_write_property(int fd, uint32_t mask, void *data) remainder = xcb_get_property_value_length(wm->property_reply) - wm->property_start; - len = write(fd, property + wm->property_start, remainder); + len = write(wm->data_source_fd, property + wm->property_start, + remainder); if (len == -1) { free(wm->property_reply); - wl_event_source_remove(wm->property_source); - close(fd); + display_unwatch_fd(wm->display, wm->data_source_fd); + close(wm->data_source_fd); fprintf(stderr, "write error to target fd: %m\n"); - return 1; + return; } fprintf(stderr, "wrote %d (chunk size %d) of %d bytes\n", @@ -57,7 +61,7 @@ xwm_write_property(int fd, uint32_t mask, void *data) wm->property_start += len; if (len == remainder) { free(wm->property_reply); - wl_event_source_remove(wm->property_source); + display_unwatch_fd(wm->display, wm->data_source_fd); if (wm->incr) { xcb_delete_property(wm->conn, @@ -65,11 +69,9 @@ xwm_write_property(int fd, uint32_t mask, void *data) wm->atom.wl_selection); } else { fprintf(stderr, "transfer complete\n"); - close(fd); + close(wm->data_source_fd); } } - - return 1; } static void @@ -92,12 +94,9 @@ xwm_get_incr_chunk(struct xwm_wm *wm) if (xcb_get_property_value_length(reply) > 0) { wm->property_start = 0; - wm->property_source = - wl_event_loop_add_fd(wm->server->loop, - wm->data_source_fd, - WL_EVENT_WRITABLE, - xwm_write_property, - wm); + wm->selection_write_task.run = xwm_write_property; + display_watch_fd(wm->display, wm->data_source_fd, + EPOLLOUT, &wm->selection_write_task); wm->property_reply = reply; } else { fprintf(stderr, "transfer complete\n"); @@ -106,23 +105,17 @@ xwm_get_incr_chunk(struct xwm_wm *wm) } } -struct x11_data_source { - struct wl_data_source base; - struct xwm_wm *wm; -}; - static void -data_source_accept(struct wl_data_source *source, - uint32_t time, const char *mime_type) +data_source_accept(void *data, struct wl_data_source *source, + const char *mime_type) { } static void -data_source_send(struct wl_data_source *base, +data_source_send(void *data, struct wl_data_source *source, const char *mime_type, int32_t fd) { - struct x11_data_source *source = (struct x11_data_source *) base; - struct xwm_wm *wm = source->wm; + struct xwm_wm *wm = data; if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) { /* Get data for the utf8_string target */ @@ -141,21 +134,25 @@ data_source_send(struct wl_data_source *base, } static void -data_source_cancel(struct wl_data_source *source) +data_source_cancel(void *data, struct wl_data_source *source) { } +static const struct wl_data_source_listener data_source_listener = { + data_source_accept, + data_source_send, + data_source_cancel +}; + static void xwm_get_selection_targets(struct xwm_wm *wm) { - struct x11_data_source *source; - struct weston_compositor *compositor; - struct weston_seat *seat = xwm_pick_seat(wm); xcb_get_property_cookie_t cookie; xcb_get_property_reply_t *reply; xcb_atom_t *value; - char **p; uint32_t i; + int has_text_plain = 0; + struct xwm *xwm = wm->xwm; cookie = xcb_get_property(wm->conn, 1, /* delete */ @@ -173,33 +170,23 @@ xwm_get_selection_targets(struct xwm_wm *wm) free(reply); return; } - - source = malloc(sizeof *source); - if (source == NULL) - return; - - wl_signal_init(&source->base.resource.destroy_signal); - source->base.accept = data_source_accept; - source->base.send = data_source_send; - source->base.cancel = data_source_cancel; - source->base.resource.data = source; - source->wm = wm; - - wl_array_init(&source->base.mime_types); value = xcb_get_property_value(reply); for (i = 0; i < reply->value_len; i++) { - if (value[i] == wm->atom.utf8_string) { - p = wl_array_add(&source->base.mime_types, sizeof *p); - if (p) - *p = strdup("text/plain;charset=utf-8"); - } + if (value[i] == wm->atom.utf8_string) + has_text_plain = 1; } + free(reply); - compositor = wm->server->compositor; - wl_seat_set_selection(&seat->seat, &source->base, - wl_display_next_serial(compositor->wl_display)); + if (!has_text_plain) + return; - free(reply); + wm->selection = + wl_data_device_manager_create_data_source(xwm->data_device_manager); + wl_data_source_offer(wm->selection, "text/plain;charset=utf-8"); + wl_data_source_add_listener(wm->selection, + &data_source_listener, wm); + wl_data_device_set_selection(xwm->data_device, wm->selection, + display_get_serial(wm->display)); } static void @@ -226,16 +213,12 @@ xwm_get_selection_data(struct xwm_wm *wm) dump_property(wm, wm->atom.wl_selection, reply); wm->incr = 0; wm->property_start = 0; - wm->property_source = - wl_event_loop_add_fd(wm->server->loop, - wm->data_source_fd, - WL_EVENT_WRITABLE, - xwm_write_property, - wm); + wm->selection_write_task.run = xwm_write_property; + display_watch_fd(wm->display, wm->data_source_fd, + EPOLLOUT, &wm->selection_write_task); wm->property_reply = reply; } } - static void xwm_handle_selection_notify(struct xwm_wm *wm, xcb_generic_event_t *event) { @@ -329,10 +312,11 @@ xwm_flush_source_data(struct xwm_wm *wm) return length; } -static int -xwm_read_data_source(int fd, uint32_t mask, void *data) +static void +xwm_read_data_source(struct task *task, uint32_t events) { - struct xwm_wm *wm = data; + struct xwm_wm *wm = + container_of(task, struct xwm_wm, selection_read_task); int len, current, available; void *p; @@ -343,17 +327,17 @@ xwm_read_data_source(int fd, uint32_t mask, void *data) p = (char *) wm->source_data.data + wm->source_data.size; available = wm->source_data.alloc - current; - len = read(fd, p, available); + len = read(wm->data_source_fd, p, available); if (len == -1) { fprintf(stderr, "read error from data source: %m\n"); xwm_send_selection_notify(wm, XCB_ATOM_NONE); - wl_event_source_remove(wm->property_source); - close(fd); + display_unwatch_fd(wm->display, wm->data_source_fd); + close(wm->data_source_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); + fprintf(stderr, "read %d (available %d) bytes: \"%.*s\"\n", + len, available, len, (char *) p); wm->source_data.size = current + len; if (wm->source_data.size >= incr_chunk_size) { @@ -370,14 +354,14 @@ xwm_read_data_source(int fd, uint32_t mask, void *data) 1, &incr_chunk_size); wm->selection_property_set = 1; wm->flush_property_on_delete = 1; - wl_event_source_remove(wm->property_source); + display_unwatch_fd(wm->display, wm->data_source_fd); xwm_send_selection_notify(wm, wm->selection_request.property); } else if (wm->selection_property_set) { fprintf(stderr, "got %zu bytes, waiting for " "property delete\n", wm->source_data.size); wm->flush_property_on_delete = 1; - wl_event_source_remove(wm->property_source); + display_unwatch_fd(wm->display, wm->data_source_fd); } else { fprintf(stderr, "got %zu bytes, " "property deleted, seting new property\n", @@ -390,8 +374,8 @@ xwm_read_data_source(int fd, uint32_t mask, void *data) xwm_flush_source_data(wm); xwm_send_selection_notify(wm, wm->selection_request.property); xcb_flush(wm->conn); - wl_event_source_remove(wm->property_source); - close(fd); + display_unwatch_fd(wm->display, wm->data_source_fd); + close(wm->data_source_fd); wl_array_release(&wm->source_data); wm->selection_request.requestor = XCB_NONE; } else if (len == 0 && wm->incr) { @@ -408,41 +392,35 @@ xwm_read_data_source(int fd, uint32_t mask, void *data) xwm_flush_source_data(wm); } xcb_flush(wm->conn); - wl_event_source_remove(wm->property_source); + display_unwatch_fd(wm->display, wm->data_source_fd); close(wm->data_source_fd); wm->data_source_fd = -1; - close(fd); } else { fprintf(stderr, "nothing happened, buffered the bytes\n"); } - - return 1; } static void xwm_send_data(struct xwm_wm *wm, xcb_atom_t target, const char *mime_type) { - struct wl_data_source *source; - struct weston_seat *seat = xwm_pick_seat(wm); int p[2]; - if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) { + if (pipe2(p, O_CLOEXEC) == -1) { fprintf(stderr, "pipe2 failed: %m\n"); xwm_send_selection_notify(wm, XCB_ATOM_NONE); return; } + wm_set_selection(wm->xwm->wm, mime_type, p[1]); + close(p[1]); + 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, - xwm_read_data_source, - wm); - - source = seat->seat.selection_data_source; - source->send(source, mime_type, p[1]); + wm->selection_read_task.run = xwm_read_data_source; + display_watch_fd(wm->display, wm->data_source_fd, + EPOLLIN, &wm->selection_read_task); + } static void @@ -460,12 +438,9 @@ xwm_send_incr_chunk(struct xwm_wm *wm) length = xwm_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, - xwm_read_data_source, - wm); + wm->selection_read_task.run = xwm_read_data_source; + display_watch_fd(wm->display, wm->data_source_fd, + EPOLLIN, &wm->selection_read_task); } else if (length > 0) { /* Transfer is all done, but queue a flush for * the delete of the last chunk so we can set @@ -547,11 +522,10 @@ static void xwm_handle_xfixes_selection_notify(struct xwm_wm *wm, xcb_generic_event_t *event) { + struct xwm *xwm = wm->xwm; + xcb_xfixes_selection_notify_event_t *xfixes_selection_notify = (xcb_xfixes_selection_notify_event_t *) event; - struct weston_compositor *compositor; - struct weston_seat *seat = xwm_pick_seat(wm); - uint32_t serial; fprintf(stderr, "xfixes selection notify event: owner %d\n", xfixes_selection_notify->owner); @@ -560,9 +534,8 @@ xwm_handle_xfixes_selection_notify(struct xwm_wm *wm, if (wm->selection_owner != wm->selection_window) { /* A real X client selection went away, not our * proxy selection. Clear the wayland selection. */ - compositor = wm->server->compositor; - serial = wl_display_next_serial(compositor->wl_display); - wl_seat_set_selection(&seat->seat, NULL, serial); + wl_data_device_set_selection(xwm->data_device, + NULL, display_get_serial(wm->display)); } wm->selection_owner = XCB_WINDOW_NONE; @@ -614,37 +587,17 @@ xwm_handle_selection_event(struct xwm_wm *wm, xcb_generic_event_t *event) return 0; } -static void -xwm_set_selection(struct wl_listener *listener, void *data) +void +xwm_set_selection(struct xwm *xwm, int has_text_plain) { - struct wl_seat *seat = data; - struct xwm_wm *wm = - container_of(listener, struct xwm_wm, selection_listener); - struct wl_data_source *source = seat->selection_data_source; - const char **p, **end; - int has_text_plain = 0; - - if (source == NULL) { - if (wm->selection_owner == wm->selection_window) - xcb_set_selection_owner(wm->conn, - XCB_ATOM_NONE, - wm->atom.clipboard, - wm->selection_timestamp); - return; - } + struct xwm_wm *wm = xwm->xwm_wm; - if (source->send == data_source_send) + if (wm->selection_owner == wm->selection_window) { + xcb_set_selection_owner(wm->conn, + XCB_ATOM_NONE, + wm->atom.clipboard, + wm->selection_timestamp); return; - - 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 (has_text_plain) { @@ -663,7 +616,6 @@ xwm_set_selection(struct wl_listener *listener, void *data) void xwm_selection_init(struct xwm_wm *wm) { - struct weston_seat *seat; uint32_t values[1], mask; wm->selection_request.requestor = XCB_NONE; @@ -693,9 +645,7 @@ xwm_selection_init(struct xwm_wm *wm) xcb_xfixes_select_selection_input(wm->conn, wm->selection_window, wm->atom.clipboard, mask); - seat = xwm_pick_seat(wm); - wm->selection_listener.notify = xwm_set_selection; - wl_signal_add(&seat->seat.selection_signal, &wm->selection_listener); - - xwm_set_selection(&wm->selection_listener, seat); + xcb_set_selection_owner(wm->conn, XCB_ATOM_NONE, + wm->atom.clipboard, wm->selection_timestamp); + xcb_flush(wm->conn); } diff --git a/clients/xwm.c b/clients/xwm.c index 49e992b..80ebc3a 100644 --- a/clients/xwm.c +++ b/clients/xwm.c @@ -36,17 +36,6 @@ #include "../shared/cairo-util.h" #include "../shared/xwayland-hash.h" -struct xwm { - struct wl_registry *registry; - struct wl_display *wl_display; - struct wl_compositor *compositor; - struct wm *wm; - struct wm_xwin *wm_xwin; - struct display *display; - - struct xwm_wm *xwm_wm; -}; - struct xwm_window { struct xwm_wm *wm; xcb_window_t id; @@ -1232,13 +1221,12 @@ xwm_handle_event(int fd, uint32_t mask, void *data) int count = 0; while (event = xcb_poll_for_event(wm->conn), event != NULL) { -#if 0 if (xwm_handle_selection_event(wm, event)) { free(event); count++; continue; } -#endif + switch (event->response_type & ~0x80) { case XCB_BUTTON_PRESS: case XCB_BUTTON_RELEASE: @@ -1479,7 +1467,6 @@ wm_func(struct task *task, uint32_t events) static void xwm_create(struct xwm *xwm, int fd) { - struct display *display = xwm->display; struct xwm_wm *wm; xcb_screen_iterator_t s; uint32_t values[1]; @@ -1490,6 +1477,7 @@ xwm_create(struct xwm *xwm, int fd) return; memset(wm, 0, sizeof *wm); + wm->display = xwm->display; wm->window_hash = hash_table_create(); if (wm->window_hash == NULL) { free(wm); @@ -1507,7 +1495,7 @@ xwm_create(struct xwm *xwm, int fd) } wm->wm_task.run = wm_func; - display_watch_fd(display, fd, EPOLLIN, &wm->wm_task); + display_watch_fd(wm->display, fd, EPOLLIN, &wm->wm_task); s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn)); wm->screen = s.data; @@ -1532,9 +1520,9 @@ xwm_create(struct xwm *xwm, int fd) XCB_ATOM_ATOM, 32, /* format */ ARRAY_LENGTH(supported), supported); -#if 0 + xwm_selection_init(wm); -#endif + xcb_flush(wm->conn); xwm_create_cursors(wm); @@ -1574,9 +1562,18 @@ wm_handle_state(void *data, struct wm *w, uint32_t state) set_state_activate(xwm, NULL); } +static void +wm_handle_selection(void *data, struct wm *w, uint32_t has_text_plain) +{ + struct xwm *xwm = data; + + xwm_set_selection(xwm, has_text_plain); +} + static const struct wm_listener wm_listener = { wm_handle_xserver, - wm_handle_state + wm_handle_state, + wm_handle_selection }; static void @@ -1588,6 +1585,15 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, if (strcmp(interface, "wl_compositor") == 0) { xwm->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1); + } else if (strcmp(interface, "wl_seat") == 0) { + xwm->seat = wl_registry_bind(registry, id, + &wl_seat_interface, 1); + xwm->data_device = + wl_data_device_manager_get_data_device(xwm->data_device_manager, + xwm->seat); + } else if (strcmp(interface, "wl_data_device_manager") == 0) { + xwm->data_device_manager = wl_registry_bind(registry, id, + &wl_data_device_manager_interface, 1); } else if (!strcmp(interface, "wm")) { xwm->wm = wl_registry_bind(registry, id, &wm_interface, 1); @@ -1631,6 +1637,8 @@ main(int argc, char **argv) display_run(xwm.display); xwm_destroy(xwm.xwm_wm); + wl_data_device_manager_destroy(xwm.data_device_manager); + wl_data_device_destroy(xwm.data_device); display_destroy(xwm.display); return 0; diff --git a/clients/xwm.h b/clients/xwm.h index 14cccb4..bdd00aa 100644 --- a/clients/xwm.h +++ b/clients/xwm.h @@ -26,10 +26,25 @@ #include "window.h" +struct xwm { + struct wl_registry *registry; + struct wl_display *wl_display; + struct wl_compositor *compositor; + struct wl_seat *seat; + struct wl_data_device_manager *data_device_manager; + struct wl_data_device *data_device; + + struct wm *wm; + struct wm_xwin *wm_xwin; + struct xwm_wm *xwm_wm; + struct display *display; +}; + struct xwm_wm { struct xwm *xwm; struct task wm_task; xcb_connection_t *conn; + struct display *display; const xcb_query_extension_reply_t *xfixes; xcb_screen_t *screen; struct hash_table *window_hash; @@ -40,13 +55,17 @@ struct xwm_wm { int last_cursor; xcb_render_pictforminfo_t format_rgb, format_rgba; + /* copy and paste proxy */ + struct task selection_write_task; + struct task selection_read_task; xcb_window_t selection_window; xcb_window_t selection_owner; int incr; int data_source_fd; - struct wl_event_source *property_source; + struct wl_data_offer *selection_offer; xcb_get_property_reply_t *property_reply; int property_start; + struct wl_data_source *selection; struct wl_array source_data; xcb_selection_request_event_t selection_request; xcb_atom_t selection_target; @@ -114,5 +133,5 @@ void xwm_selection_init(struct xwm_wm *wm); int xwm_handle_selection_event(struct xwm_wm *wm, xcb_generic_event_t *event); -struct weston_seat * -xwm_pick_seat(struct xwm_wm *wm); +void +xwm_set_selection(struct xwm *xwm, int has_text_plain); diff --git a/protocol/xserver.xml b/protocol/xserver.xml index 05f771f..20d39e5 100644 --- a/protocol/xserver.xml +++ b/protocol/xserver.xml @@ -75,6 +75,16 @@ <arg name="xid" type="uint"/> </request> + <request name="set_selection"> + <description summary="send the selection data"> + Request for data from another client. Send the data as the specified + mime-type over the passed fd. + </description> + + <arg name="mime_type" type="string"/> + <arg name="fd" type="fd"/> + </request> + <event name="xserver"> <description summary="send X fd to window manager"> This is the other tip of the socketpair used for connecting X and @@ -98,6 +108,14 @@ <arg name="state" type="uint"/> </event> + + <event name="selection"> + <description summary="notifies wm whether a text plain was selected"> + The selection itself will happen via X then. + </description> + + <arg name="has_text_plain" type="uint"/> + </event> </interface> <interface name="wm_xwin" version="1"> diff --git a/src/xwayland/window-manager.c b/src/xwayland/window-manager.c index 00d6175..66fa52a 100644 --- a/src/xwayland/window-manager.c +++ b/src/xwayland/window-manager.c @@ -23,6 +23,8 @@ */ #include <stdlib.h> +#include <unistd.h> +#include <string.h> #include "xwayland.h" #include "../../shared/xwayland-hash.h" @@ -275,6 +277,38 @@ wm_activate_listener(struct wl_listener *listener, void *data) } static void +wm_set_selection_listener(struct wl_listener *listener, void *data) +{ + struct weston_xserver *wxs = container_of(listener, + struct weston_xserver, selection_listener); + struct wl_seat *seat = data; + struct weston_surface *weston_surface = + (struct weston_surface *) seat->keyboard->focus; + struct xserver_window *window = get_xserver_window(weston_surface); + struct wl_data_source *source = seat->selection_data_source; + const char **p, **end; + int has_text_plain = 0; + + /* just forward set_selection to WM when a Wayland client has + * initiated it */ + if (window) + return; + + p = source->mime_types.data; + end = (const char **) + ((char *) source->mime_types.data + source->mime_types.size); + while (p < end) { + weston_log(" %s\n", *p); + if (strcmp(*p, "text/plain") == 0 || + strcmp(*p, "text/plain;charset=utf-8") == 0) + has_text_plain = 1; + p++; + } + + wm_send_selection(wxs->wm_resource, has_text_plain); +} + +static void window_position_listener(struct wl_listener *listener, void *data) { struct weston_surface *surface = data; @@ -308,6 +342,7 @@ static void wm_handle_ready(struct wl_client *client, struct wl_resource *resource) { struct weston_xserver *wxs = resource->data; + struct weston_seat *seat = weston_wm_pick_seat(wxs); /* sending now the X display connection together with the actual first * X client that got connected */ @@ -319,6 +354,10 @@ wm_handle_ready(struct wl_client *client, struct wl_resource *resource) wxs->activate_listener.notify = wm_activate_listener; wl_signal_add(&wxs->compositor->activate_signal, &wxs->activate_listener); + + wxs->selection_listener.notify = wm_set_selection_listener; + wl_signal_add(&seat->seat.selection_signal, + &wxs->selection_listener); } static void @@ -358,8 +397,22 @@ wm_handle_create_xwindow(struct wl_client *client, wl_list_insert(&wxs->xserver_window_list, &window->link); } +static void +wm_handle_set_selection(struct wl_client *client, + struct wl_resource *resource, + const char *mime_type, int32_t fd) +{ + struct weston_xserver *wxs = resource->data; + struct weston_seat *seat = weston_wm_pick_seat(wxs); + struct wl_data_source *source; + + source = seat->seat.selection_data_source; + source->send(source, mime_type, fd); +} + const struct wm_interface wm_implementation = { wm_handle_ready, - wm_handle_create_xwindow + wm_handle_create_xwindow, + wm_handle_set_selection, }; diff --git a/src/xwayland/xwayland.h b/src/xwayland/xwayland.h index 65ecf15..1ba92b6 100644 --- a/src/xwayland/xwayland.h +++ b/src/xwayland/xwayland.h @@ -47,6 +47,7 @@ struct weston_xserver { struct wl_resource *wm_resource; struct wl_resource *wm_conn_resource; struct wl_listener activate_listener; + struct wl_listener selection_listener; struct wl_list xserver_window_list; }; -- 1.7.9.5 _______________________________________________ wayland-devel mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/wayland-devel
