From 3880fb0a532d50d8efe1e080fc3e047d5d00bbfe Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 20 Nov 2017 07:23:17 -0500 Subject: [PATCH 01/21] xwm: selection stubs --- xwayland/meson.build | 1 + xwayland/selection.c | 47 ++++++++++++++++++++++++++++++++++++++++++++ xwayland/xwm.c | 6 ++++++ xwayland/xwm.h | 2 ++ 4 files changed, 56 insertions(+) create mode 100644 xwayland/selection.c diff --git a/xwayland/meson.build b/xwayland/meson.build index e92e042c..de05cfaa 100644 --- a/xwayland/meson.build +++ b/xwayland/meson.build @@ -1,6 +1,7 @@ lib_wlr_xwayland = static_library( 'wlr_xwayland', files( + 'selection.c', 'sockets.c', 'xwayland.c', 'xwm.c', diff --git a/xwayland/selection.c b/xwayland/selection.c new file mode 100644 index 00000000..a83e206c --- /dev/null +++ b/xwayland/selection.c @@ -0,0 +1,47 @@ +#include +#include "wlr/util/log.h" +#include "xwm.h" + +static void xwm_handle_selection_notify(struct wlr_xwm *xwm, xcb_generic_event_t + *event) { + wlr_log(L_DEBUG, "TODO: SELECTION NOTIFY"); +} + +static int xwm_handle_selection_property_notify(struct wlr_xwm *xwm, + xcb_generic_event_t *event) { + wlr_log(L_DEBUG, "TODO: SELECTION PROPERTY NOTIFY"); + return 1; +} + +static void xwm_handle_selection_request(struct wlr_xwm *xwm, xcb_generic_event_t + *event) { + wlr_log(L_DEBUG, "TODO: SELECTION REQUEST"); + return; +} + +static int weston_wm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, + xcb_generic_event_t *event) { + wlr_log(L_DEBUG, "TODO: XFIXES SELECTION NOTIFY"); + return 1; +} + + +int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event) { + switch (event->response_type & ~0x80) { + 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; + } + + switch (event->response_type - xwm->xfixes->first_event) { + case XCB_XFIXES_SELECTION_NOTIFY: + return weston_wm_handle_xfixes_selection_notify(xwm, event); + } + + return 0; +} diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 36722591..c10aab47 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -909,6 +909,12 @@ static int x11_event_handler(int fd, uint32_t mask, void *data) { while ((event = xcb_poll_for_event(xwm->xcb_conn))) { count++; + + if (xwm_handle_selection_event(xwm, event)) { + free(event); + continue; + } + switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { case XCB_CREATE_NOTIFY: xwm_handle_create_notify(xwm, (xcb_create_notify_event_t *)event); diff --git a/xwayland/xwm.h b/xwayland/xwm.h index c350b6e2..f6bec6c5 100644 --- a/xwayland/xwm.h +++ b/xwayland/xwm.h @@ -72,4 +72,6 @@ void xwm_destroy(struct wlr_xwm *xwm); void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y); +int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event); + #endif From 444257b6b17935be5dff11791e9ac9d52cf811b7 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 20 Nov 2017 08:51:40 -0500 Subject: [PATCH 02/21] xwm: selection init --- xwayland/selection.c | 58 ++++++++++++++++++++++++++++++++++++++++---- xwayland/xwm.c | 5 ++++ xwayland/xwm.h | 10 ++++++++ 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index a83e206c..c80e1db2 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -9,12 +9,28 @@ static void xwm_handle_selection_notify(struct wlr_xwm *xwm, xcb_generic_event_t static int xwm_handle_selection_property_notify(struct wlr_xwm *xwm, xcb_generic_event_t *event) { - wlr_log(L_DEBUG, "TODO: SELECTION PROPERTY NOTIFY"); - return 1; + 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; + } + + return 0; } -static void xwm_handle_selection_request(struct wlr_xwm *xwm, xcb_generic_event_t - *event) { +static void xwm_handle_selection_request(struct wlr_xwm *xwm, + xcb_generic_event_t *event) { wlr_log(L_DEBUG, "TODO: SELECTION REQUEST"); return; } @@ -26,7 +42,8 @@ static int weston_wm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, } -int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event) { +int xwm_handle_selection_event(struct wlr_xwm *xwm, + xcb_generic_event_t *event) { switch (event->response_type & ~0x80) { case XCB_SELECTION_NOTIFY: xwm_handle_selection_notify(xwm, event); @@ -45,3 +62,34 @@ int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event) return 0; } + +void xwm_selection_init(struct wlr_xwm *xwm) { + uint32_t values[1], mask; + + xwm->selection_request.requestor = XCB_NONE; + + values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE; + xwm->selection_window = xcb_generate_id(xwm->xcb_conn); + xcb_create_window(xwm->xcb_conn, + XCB_COPY_FROM_PARENT, + xwm->selection_window, + xwm->screen->root, + 0, 0, + 10, 10, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + xwm->screen->root_visual, + XCB_CW_EVENT_MASK, values); + + xcb_set_selection_owner(xwm->xcb_conn, + xwm->selection_window, + xwm->atoms[CLIPBOARD_MANAGER], + XCB_TIME_CURRENT_TIME); + + mask = + XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | + XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | + XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; + xcb_xfixes_select_selection_input(xwm->xcb_conn, xwm->selection_window, + xwm->atoms[CLIPBOARD], mask); +} diff --git a/xwayland/xwm.c b/xwayland/xwm.c index c10aab47..febe1ea2 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -43,6 +43,9 @@ const char *atom_map[ATOM_LAST] = { "_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_MAXIMIZED_HORZ", "WM_STATE", + "CLIPBOARD", + "_WL_SELECTION", + "CLIPBOARD_MANAGER", }; /* General helpers */ @@ -1339,6 +1342,8 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) { xwm_set_net_active_window(xwm, XCB_WINDOW_NONE); + xwm_selection_init(xwm); + xwm->compositor_surface_create.notify = handle_compositor_surface_create; wl_signal_add(&wlr_xwayland->compositor->events.create_surface, &xwm->compositor_surface_create); diff --git a/xwayland/xwm.h b/xwayland/xwm.h index f6bec6c5..2daa4eeb 100644 --- a/xwayland/xwm.h +++ b/xwayland/xwm.h @@ -31,6 +31,9 @@ enum atom_name { _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ, WM_STATE, + CLIPBOARD, + CLIPBOARD_MANAGER, + WL_SELECTION, ATOM_LAST, }; @@ -55,6 +58,11 @@ struct wlr_xwm { xcb_render_pictformat_t render_format_id; xcb_cursor_t cursor; + // selection properties + xcb_window_t selection_window; + xcb_selection_request_event_t selection_request; + int incr; + struct wlr_xwayland_surface *focus_surface; struct wl_list surfaces; // wlr_xwayland_surface::link @@ -74,4 +82,6 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event); +void xwm_selection_init(struct wlr_xwm *xwm); + #endif From 1f976a0f25d55e49e0c34cf47b89e28e693081d5 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 21 Nov 2017 09:25:05 -0500 Subject: [PATCH 03/21] xwm: xfixes selection notify --- xwayland/selection.c | 48 +++++++++++++++++++++++++++++++++++++++----- xwayland/xwm.c | 1 + xwayland/xwm.h | 3 +++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index c80e1db2..08091fe1 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -2,11 +2,6 @@ #include "wlr/util/log.h" #include "xwm.h" -static void xwm_handle_selection_notify(struct wlr_xwm *xwm, xcb_generic_event_t - *event) { - wlr_log(L_DEBUG, "TODO: SELECTION NOTIFY"); -} - static int xwm_handle_selection_property_notify(struct wlr_xwm *xwm, xcb_generic_event_t *event) { xcb_property_notify_event_t *property_notify = @@ -29,6 +24,11 @@ static int xwm_handle_selection_property_notify(struct wlr_xwm *xwm, return 0; } +static void xwm_handle_selection_notify(struct wlr_xwm *xwm, + xcb_generic_event_t *event) { + wlr_log(L_DEBUG, "TODO: SELECTION NOTIFY"); +} + static void xwm_handle_selection_request(struct wlr_xwm *xwm, xcb_generic_event_t *event) { wlr_log(L_DEBUG, "TODO: SELECTION REQUEST"); @@ -38,6 +38,43 @@ static void xwm_handle_selection_request(struct wlr_xwm *xwm, static int weston_wm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, xcb_generic_event_t *event) { wlr_log(L_DEBUG, "TODO: XFIXES SELECTION NOTIFY"); + + xcb_xfixes_selection_notify_event_t *xfixes_selection_notify = + (xcb_xfixes_selection_notify_event_t *) event; + + + if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) { + if (xwm->selection_owner != xwm->selection_window) { + // A real X client selection went away, not our + // proxy selection + // TODO: Clear the wayland selection (or not)? + } + + xwm->selection_owner = XCB_WINDOW_NONE; + + return 1; + } + + // 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 == xwm->selection_window) { + xwm->selection_timestamp = xfixes_selection_notify->timestamp; + return 1; + } + + xwm->selection_owner = xfixes_selection_notify->owner; + + xwm->incr = 0; + // doing this will give a selection notify where we actually handle the sync + xcb_convert_selection(xwm->xcb_conn, xwm->selection_window, + xwm->atoms[CLIPBOARD], + xwm->atoms[TARGETS], + xwm->atoms[WL_SELECTION], + xfixes_selection_notify->timestamp); + + xcb_flush(xwm->xcb_conn); + return 1; } @@ -57,6 +94,7 @@ int xwm_handle_selection_event(struct wlr_xwm *xwm, switch (event->response_type - xwm->xfixes->first_event) { case XCB_XFIXES_SELECTION_NOTIFY: + // an X11 window has copied something to the clipboard return weston_wm_handle_xfixes_selection_notify(xwm, event); } diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 39cd6362..5ce30f7b 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -45,6 +45,7 @@ const char *atom_map[ATOM_LAST] = { "WM_STATE", "CLIPBOARD", "_WL_SELECTION", + "TARGETS", "CLIPBOARD_MANAGER", }; diff --git a/xwayland/xwm.h b/xwayland/xwm.h index 2daa4eeb..59a26725 100644 --- a/xwayland/xwm.h +++ b/xwayland/xwm.h @@ -34,6 +34,7 @@ enum atom_name { CLIPBOARD, CLIPBOARD_MANAGER, WL_SELECTION, + TARGETS, ATOM_LAST, }; @@ -61,6 +62,8 @@ struct wlr_xwm { // selection properties xcb_window_t selection_window; xcb_selection_request_event_t selection_request; + xcb_window_t selection_owner; + xcb_timestamp_t selection_timestamp; int incr; struct wlr_xwayland_surface *focus_surface; From c67cf2e9e47976b2964a79df7c28d128e6e8e3fc Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 21 Nov 2017 10:18:44 -0500 Subject: [PATCH 04/21] xwm: handle selection stubs --- xwayland/selection.c | 34 +++++++++++++++++++++++++--------- xwayland/xwm.h | 2 +- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index 08091fe1..1c9fcf13 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -24,21 +24,36 @@ static int xwm_handle_selection_property_notify(struct wlr_xwm *xwm, return 0; } -static void xwm_handle_selection_notify(struct wlr_xwm *xwm, - xcb_generic_event_t *event) { - wlr_log(L_DEBUG, "TODO: SELECTION NOTIFY"); -} - static void xwm_handle_selection_request(struct wlr_xwm *xwm, xcb_generic_event_t *event) { wlr_log(L_DEBUG, "TODO: SELECTION REQUEST"); return; } -static int weston_wm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, - xcb_generic_event_t *event) { - wlr_log(L_DEBUG, "TODO: XFIXES SELECTION NOTIFY"); +static void xwm_get_selection_targets(struct wlr_xwm *xwm) { + wlr_log(L_DEBUG, "TODO: GET SELECTION TARGETS"); +} +static void xwm_get_selection_data(struct wlr_xwm *xwm) { + wlr_log(L_DEBUG, "TODO: GET SELECTION DATA"); +} + +static void xwm_handle_selection_notify(struct wlr_xwm *xwm, + xcb_generic_event_t *event) { + xcb_selection_notify_event_t *selection_notify = + (xcb_selection_notify_event_t *) event; + + if (selection_notify->property == XCB_ATOM_NONE) { + wlr_log(L_DEBUG, "TODO: convert selection failed"); + } else if (selection_notify->target == xwm->atoms[TARGETS]) { + xwm_get_selection_targets(xwm); + } else { + xwm_get_selection_data(xwm); + } +} + +static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, + xcb_generic_event_t *event) { xcb_xfixes_selection_notify_event_t *xfixes_selection_notify = (xcb_xfixes_selection_notify_event_t *) event; @@ -60,6 +75,7 @@ static int weston_wm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, // answer TIMESTAMP conversion requests correctly. if (xfixes_selection_notify->owner == xwm->selection_window) { xwm->selection_timestamp = xfixes_selection_notify->timestamp; + wlr_log(L_DEBUG, "TODO: our window"); return 1; } @@ -95,7 +111,7 @@ int xwm_handle_selection_event(struct wlr_xwm *xwm, switch (event->response_type - xwm->xfixes->first_event) { case XCB_XFIXES_SELECTION_NOTIFY: // an X11 window has copied something to the clipboard - return weston_wm_handle_xfixes_selection_notify(xwm, event); + return xwm_handle_xfixes_selection_notify(xwm, event); } return 0; diff --git a/xwayland/xwm.h b/xwayland/xwm.h index 59a26725..ea1edc65 100644 --- a/xwayland/xwm.h +++ b/xwayland/xwm.h @@ -32,9 +32,9 @@ enum atom_name { _NET_WM_STATE_MAXIMIZED_HORZ, WM_STATE, CLIPBOARD, - CLIPBOARD_MANAGER, WL_SELECTION, TARGETS, + CLIPBOARD_MANAGER, ATOM_LAST, }; From 1ada1b7f0bdf048ea3d5f01c6b8eb7badfd97eae Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 21 Nov 2017 11:19:55 -0500 Subject: [PATCH 05/21] xwm: x11 data source --- xwayland/selection.c | 46 +++++++++++++++++++++++++++++++++++++++----- xwayland/xwm.h | 1 + 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index 1c9fcf13..c458f867 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -1,5 +1,7 @@ #include +#include #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, @@ -30,21 +32,55 @@ static void xwm_handle_selection_request(struct wlr_xwm *xwm, return; } -static void xwm_get_selection_targets(struct wlr_xwm *xwm) { - wlr_log(L_DEBUG, "TODO: GET SELECTION TARGETS"); -} - static void xwm_get_selection_data(struct wlr_xwm *xwm) { wlr_log(L_DEBUG, "TODO: GET SELECTION DATA"); } +struct x11_data_source { + struct wlr_data_source base; + struct wlr_xwm *xwm; +}; + +static void data_source_accept(struct wlr_data_source *source, uint32_t time, + const char *mime_type) { +} + +static void data_source_send(struct wlr_data_source *base, + const char *mime_type, int32_t fd) { + struct x11_data_source *source = (struct x11_data_source *)base; + struct wlr_xwm *xwm = source->xwm; + + if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) { + // Get data for the utf8_string target + xcb_convert_selection(xwm->xcb_conn, + xwm->selection_window, + xwm->atoms[CLIPBOARD], + xwm->atoms[UTF8_STRING], + xwm->atoms[WL_SELECTION], + XCB_TIME_CURRENT_TIME); + + xcb_flush(xwm->xcb_conn); + + fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK); + xwm->data_source_fd = fd; + } +} + +static void data_source_cancel(struct wlr_data_source *source) { +} + +static void xwm_get_selection_targets(struct wlr_xwm *xwm) { + // set the wayland clipboard selection to the copied selection + wlr_log(L_DEBUG, "TODO: GET SELECTION TARGETS"); +} + static void xwm_handle_selection_notify(struct wlr_xwm *xwm, xcb_generic_event_t *event) { xcb_selection_notify_event_t *selection_notify = (xcb_selection_notify_event_t *) event; if (selection_notify->property == XCB_ATOM_NONE) { - wlr_log(L_DEBUG, "TODO: convert selection failed"); + wlr_log(L_ERROR, "convert selection failed"); } else if (selection_notify->target == xwm->atoms[TARGETS]) { xwm_get_selection_targets(xwm); } else { diff --git a/xwayland/xwm.h b/xwayland/xwm.h index ea1edc65..e1e42a54 100644 --- a/xwayland/xwm.h +++ b/xwayland/xwm.h @@ -65,6 +65,7 @@ struct wlr_xwm { xcb_window_t selection_owner; xcb_timestamp_t selection_timestamp; int incr; + int data_source_fd; struct wlr_xwayland_surface *focus_surface; From 39e2ea8420e78710906308a17a65b55e9d03dda8 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 21 Nov 2017 18:09:25 -0500 Subject: [PATCH 06/21] xwm: get selection targets --- include/wlr/xwayland.h | 1 + rootston/main.c | 6 ++++ xwayland/selection.c | 68 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h index 9b493d88..54558a7b 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -16,6 +16,7 @@ struct wlr_xwayland_cursor; struct wlr_xwayland { pid_t pid; int display; + struct wlr_seat *seat; int x_fd[2], wl_fd[2], wm_fd[2]; struct wl_client *client; struct wl_display *wl_display; diff --git a/rootston/main.c b/rootston/main.c index 46548094..74f9195c 100644 --- a/rootston/main.c +++ b/rootston/main.c @@ -38,6 +38,12 @@ int main(int argc, char **argv) { server.desktop = desktop_create(&server, server.config); server.input = input_create(&server, server.config); + struct roots_seat *default_seat = + roots_seat_create(server.input, ROOTS_CONFIG_DEFAULT_SEAT_NAME); + if (server.desktop->xwayland) { + server.desktop->xwayland->seat = default_seat->seat; + } + const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { wlr_log_errno(L_ERROR, "Unable to open wayland socket"); diff --git a/xwayland/selection.c b/xwayland/selection.c index c458f867..911a3627 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -1,3 +1,6 @@ +#define _XOPEN_SOURCE 700 +#include +#include #include #include #include "wlr/util/log.h" @@ -53,11 +56,11 @@ static void data_source_send(struct wlr_data_source *base, if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) { // Get data for the utf8_string target xcb_convert_selection(xwm->xcb_conn, - xwm->selection_window, - xwm->atoms[CLIPBOARD], - xwm->atoms[UTF8_STRING], - xwm->atoms[WL_SELECTION], - XCB_TIME_CURRENT_TIME); + xwm->selection_window, + xwm->atoms[CLIPBOARD], + xwm->atoms[UTF8_STRING], + xwm->atoms[WL_SELECTION], + XCB_TIME_CURRENT_TIME); xcb_flush(xwm->xcb_conn); @@ -71,7 +74,54 @@ static void data_source_cancel(struct wlr_data_source *source) { static void xwm_get_selection_targets(struct wlr_xwm *xwm) { // set the wayland clipboard selection to the copied selection - wlr_log(L_DEBUG, "TODO: GET SELECTION TARGETS"); + + xcb_get_property_cookie_t cookie = xcb_get_property(xwm->xcb_conn, + 1, // delete + xwm->selection_window, + xwm->atoms[WL_SELECTION], + XCB_GET_PROPERTY_TYPE_ANY, + 0, // offset + 4096 //length + ); + + xcb_get_property_reply_t *reply = + xcb_get_property_reply(xwm->xcb_conn, cookie, NULL); + if (reply == NULL) + return; + + if (reply->type != XCB_ATOM_ATOM) { + free(reply); + return; + } + + struct x11_data_source *source = calloc(1, sizeof(struct x11_data_source)); + if (source == NULL) { + free(reply); + return; + } + + wl_signal_init(&source->base.events.destroy); + source->base.accept = data_source_accept; + source->base.send = data_source_send; + source->base.cancel = data_source_cancel; + source->xwm = xwm; + + wl_array_init(&source->base.mime_types); + xcb_atom_t *value = xcb_get_property_value(reply); + for (uint32_t i = 0; i < reply->value_len; i++) { + if (value[i] == xwm->atoms[UTF8_STRING]) { + char **p = wl_array_add(&source->base.mime_types, sizeof *p); + if (p) { + *p = strdup("text/plain;charset=utf-8"); + } + } + } + + wlr_seat_set_selection(xwm->xwayland->seat, &source->base, + wl_display_next_serial(xwm->xwayland->wl_display)); + + free(reply); + } static void xwm_handle_selection_notify(struct wlr_xwm *xwm, @@ -133,6 +183,12 @@ 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) { + wlr_log(L_DEBUG, "not handling selection events:" + "no seat assigned to xwayland"); + return 0; + } + switch (event->response_type & ~0x80) { case XCB_SELECTION_NOTIFY: xwm_handle_selection_notify(xwm, event); From ea6f77b4840f8a282f645f8ceb3fdd7b72d6af65 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 21 Nov 2017 18:25:21 -0500 Subject: [PATCH 07/21] xwm: get selection data --- xwayland/selection.c | 90 +++++++++++++++++++++++++++++++++++++++++++- xwayland/xwm.c | 1 + xwayland/xwm.h | 4 ++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index 911a3627..acc93728 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -1,5 +1,6 @@ #define _XOPEN_SOURCE 700 #include +#include #include #include #include @@ -35,8 +36,95 @@ static void xwm_handle_selection_request(struct wlr_xwm *xwm, return; } +static int writable_callback(int fd, uint32_t mask, void *data) { + struct wlr_xwm *xwm = data; + + unsigned char *property = xcb_get_property_value(xwm->property_reply); + int remainder = xcb_get_property_value_length(xwm->property_reply) - + xwm->property_start; + + int len = write(fd, property + xwm->property_start, remainder); + if (len == -1) { + free(xwm->property_reply); + xwm->property_reply = NULL; + if (xwm->property_source) { + wl_event_source_remove(xwm->property_source); + } + xwm->property_source = NULL; + close(fd); + wlr_log(L_ERROR, "write error to target fd: %m\n"); + return 1; + } + + wlr_log(L_DEBUG, "wrote %d (chunk size %d) of %d bytes\n", + xwm->property_start + len, + len, xcb_get_property_value_length(xwm->property_reply)); + + xwm->property_start += len; + if (len == remainder) { + free(xwm->property_reply); + xwm->property_reply = NULL; + if (xwm->property_source) { + wl_event_source_remove(xwm->property_source); + } + xwm->property_source = NULL; + + if (xwm->incr) { + xcb_delete_property(xwm->xcb_conn, + xwm->selection_window, + xwm->atoms[WL_SELECTION]); + } else { + wlr_log(L_DEBUG, "transfer complete\n"); + close(fd); + } + } + + return 1; +} + +static void xwm_write_property(struct wlr_xwm *xwm, + xcb_get_property_reply_t *reply) { + xwm->property_start = 0; + xwm->property_reply = reply; + writable_callback(xwm->data_source_fd, WL_EVENT_WRITABLE, xwm); + + if (xwm->property_reply) { + 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_WRITABLE, + writable_callback, xwm); + } +} + static void xwm_get_selection_data(struct wlr_xwm *xwm) { - wlr_log(L_DEBUG, "TODO: GET SELECTION DATA"); + xcb_get_property_cookie_t cookie = + xcb_get_property(xwm->xcb_conn, + 1, // delete + xwm->selection_window, + xwm->atoms[WL_SELECTION], + XCB_GET_PROPERTY_TYPE_ANY, + 0, // offset + 0x1fffffff // length + ); + + xcb_get_property_reply_t *reply = + xcb_get_property_reply(xwm->xcb_conn, cookie, NULL); + + if (reply == NULL) { + return; + } else if (reply->type == xwm->atoms[INCR]) { + xwm->incr = 1; + free(reply); + } else { + xwm->incr = 0; + // reply's ownership is transferred to wm, which is responsible + // for freeing it + xwm_write_property(xwm, reply); + } + } struct x11_data_source { diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 5ce30f7b..af429a5c 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -47,6 +47,7 @@ const char *atom_map[ATOM_LAST] = { "_WL_SELECTION", "TARGETS", "CLIPBOARD_MANAGER", + "INCR", }; /* General helpers */ diff --git a/xwayland/xwm.h b/xwayland/xwm.h index e1e42a54..a7bfd623 100644 --- a/xwayland/xwm.h +++ b/xwayland/xwm.h @@ -35,6 +35,7 @@ enum atom_name { WL_SELECTION, TARGETS, CLIPBOARD_MANAGER, + INCR, ATOM_LAST, }; @@ -66,6 +67,9 @@ struct wlr_xwm { xcb_timestamp_t selection_timestamp; int incr; int data_source_fd; + int property_start; + xcb_get_property_reply_t *property_reply; + struct wl_event_source *property_source; struct wlr_xwayland_surface *focus_surface; From b0683874e98a2dab7cc73734355bb8e0ad656113 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Wed, 22 Nov 2017 08:10:06 -0500 Subject: [PATCH 08/21] xwm: send selection data --- include/rootston/input.h | 2 + include/wlr/xwayland.h | 5 +- rootston/input.c | 3 +- rootston/main.c | 19 ++- xwayland/selection.c | 283 +++++++++++++++++++++++++++++++++++---- xwayland/xwayland.c | 5 + xwayland/xwm.c | 2 + xwayland/xwm.h | 10 ++ 8 files changed, 298 insertions(+), 31 deletions(-) diff --git a/include/rootston/input.h b/include/rootston/input.h index 0af48577..726dda24 100644 --- a/include/rootston/input.h +++ b/include/rootston/input.h @@ -28,4 +28,6 @@ struct roots_seat *input_seat_from_wlr_seat(struct roots_input *input, bool input_view_has_focus(struct roots_input *input, struct roots_view *view); +struct roots_seat *input_get_seat(struct roots_input *input, char *name); + #endif diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h index 54558a7b..cef17f38 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #ifdef HAS_XCB_ICCCM @@ -16,7 +17,6 @@ struct wlr_xwayland_cursor; struct wlr_xwayland { pid_t pid; int display; - struct wlr_seat *seat; int x_fd[2], wl_fd[2], wm_fd[2]; struct wl_client *client; struct wl_display *wl_display; @@ -170,4 +170,7 @@ void wlr_xwayland_surface_set_maximized(struct wlr_xwayland *wlr_xwayland, void wlr_xwayland_surface_set_fullscreen(struct wlr_xwayland *wlr_xwayland, struct wlr_xwayland_surface *surface, bool fullscreen); +void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland, + struct wlr_seat *seat); + #endif diff --git a/rootston/input.c b/rootston/input.c index ce20e840..94524b5f 100644 --- a/rootston/input.c +++ b/rootston/input.c @@ -27,7 +27,7 @@ static const char *device_type(enum wlr_input_device_type type) { return NULL; } -static struct roots_seat *input_get_seat(struct roots_input *input, char *name) { +struct roots_seat *input_get_seat(struct roots_input *input, char *name) { struct roots_seat *seat = NULL; wl_list_for_each(seat, &input->seats, link) { if (strcmp(seat->seat->name, name) == 0) { @@ -86,6 +86,7 @@ struct roots_input *input_create(struct roots_server *server, input->server = server; wl_list_init(&input->seats); + roots_seat_create(input, ROOTS_CONFIG_DEFAULT_SEAT_NAME); input->input_add.notify = input_add_notify; wl_signal_add(&server->backend->events.input_add, &input->input_add); diff --git a/rootston/main.c b/rootston/main.c index 74f9195c..38bbec53 100644 --- a/rootston/main.c +++ b/rootston/main.c @@ -13,6 +13,19 @@ struct roots_server server = { 0 }; static void ready(struct wl_listener *listener, void *data) { + struct roots_desktop *desktop = + wl_container_of(listener, desktop, xwayland_ready); + +#ifdef HAS_XWAYLAND + struct wlr_xwayland *xwayland = data; + if (xwayland) { + struct roots_seat *seat = + input_get_seat(desktop->server->input, + ROOTS_CONFIG_DEFAULT_SEAT_NAME); + wlr_xwayland_set_seat(xwayland, seat->seat); + } +#endif + if (server.config->startup_cmd != NULL) { const char *cmd = server.config->startup_cmd; pid_t pid = fork(); @@ -38,12 +51,6 @@ int main(int argc, char **argv) { server.desktop = desktop_create(&server, server.config); server.input = input_create(&server, server.config); - struct roots_seat *default_seat = - roots_seat_create(server.input, ROOTS_CONFIG_DEFAULT_SEAT_NAME); - if (server.desktop->xwayland) { - server.desktop->xwayland->seat = default_seat->seat; - } - const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { wlr_log_errno(L_ERROR, "Unable to open wayland socket"); 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 #include #include +#include #include #include #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; +static const size_t incr_chunk_size = 64 * 1024; - 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 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 From fc17c74516c7c843f15426bca754a16f963ad348 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Wed, 22 Nov 2017 08:23:23 -0500 Subject: [PATCH 09/21] xwayland: clean up seat setting --- include/wlr/xwayland.h | 1 + rootston/input.c | 1 - rootston/main.c | 16 +++------------- xwayland/xwayland.c | 10 +++++++++- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h index cef17f38..351c6cd1 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -21,6 +21,7 @@ struct wlr_xwayland { struct wl_client *client; struct wl_display *wl_display; struct wlr_compositor *compositor; + struct wlr_seat *seat; time_t server_start; struct wl_event_source *sigusr1_source; diff --git a/rootston/input.c b/rootston/input.c index 94524b5f..a3904a61 100644 --- a/rootston/input.c +++ b/rootston/input.c @@ -86,7 +86,6 @@ struct roots_input *input_create(struct roots_server *server, input->server = server; wl_list_init(&input->seats); - roots_seat_create(input, ROOTS_CONFIG_DEFAULT_SEAT_NAME); input->input_add.notify = input_add_notify; wl_signal_add(&server->backend->events.input_add, &input->input_add); diff --git a/rootston/main.c b/rootston/main.c index 38bbec53..144da2c5 100644 --- a/rootston/main.c +++ b/rootston/main.c @@ -13,19 +13,6 @@ struct roots_server server = { 0 }; static void ready(struct wl_listener *listener, void *data) { - struct roots_desktop *desktop = - wl_container_of(listener, desktop, xwayland_ready); - -#ifdef HAS_XWAYLAND - struct wlr_xwayland *xwayland = data; - if (xwayland) { - struct roots_seat *seat = - input_get_seat(desktop->server->input, - ROOTS_CONFIG_DEFAULT_SEAT_NAME); - wlr_xwayland_set_seat(xwayland, seat->seat); - } -#endif - if (server.config->startup_cmd != NULL) { const char *cmd = server.config->startup_cmd; pid_t pid = fork(); @@ -72,6 +59,9 @@ int main(int argc, char **argv) { ready(NULL, NULL); #else if (server.desktop->xwayland != NULL) { + struct roots_seat *xwayland_seat = + input_get_seat(server.input, ROOTS_CONFIG_DEFAULT_SEAT_NAME); + wlr_xwayland_set_seat(server.desktop->xwayland, xwayland_seat->seat); wl_signal_add(&server.desktop->xwayland->events.ready, &server.desktop->xwayland_ready); server.desktop->xwayland_ready.notify = ready; diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index bd43fefb..4db30985 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -196,6 +196,10 @@ static int xserver_handle_ready(int signal_number, void *data) { return 1; } + if (wlr_xwayland->seat) { + xwm_set_seat(wlr_xwayland->xwm, wlr_xwayland->seat); + } + wl_event_source_remove(wlr_xwayland->sigusr1_source); wlr_xwayland->sigusr1_source = NULL; @@ -344,5 +348,9 @@ void wlr_xwayland_set_cursor(struct wlr_xwayland *wlr_xwayland, void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland, struct wlr_seat *seat) { - xwm_set_seat(xwayland->xwm, seat); + xwayland->seat = seat; + + if (xwayland->xwm) { + xwm_set_seat(xwayland->xwm, seat); + } } From e3542d879d50d6239ad4ca24b4d30520fef40a87 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Wed, 22 Nov 2017 14:58:36 -0500 Subject: [PATCH 10/21] xwm: only take clipboard if window is focused --- xwayland/selection.c | 23 +++++++++++++++++------ xwayland/xwm.c | 1 + xwayland/xwm.h | 2 ++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index 3d57f394..07d1d48f 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -466,7 +466,6 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, return 1; } - int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event) { if (!xwm->seat) { @@ -542,13 +541,25 @@ static void handle_seat_set_selection(struct wl_listener *listener, return; } - if (source->send == data_source_send) + if (source->send == data_source_send) { return; + } - xcb_set_selection_owner(xwm->xcb_conn, - xwm->selection_window, - xwm->atoms[CLIPBOARD], - XCB_TIME_CURRENT_TIME); + xwm_set_selection_owner(xwm); +} + +void xwm_set_selection_owner(struct wlr_xwm *xwm) { + if (xwm->focus_surface && xwm->seat->selection_source) { + xcb_set_selection_owner(xwm->xcb_conn, + xwm->selection_window, + xwm->atoms[CLIPBOARD], + XCB_TIME_CURRENT_TIME); + } else { + xcb_set_selection_owner(xwm->xcb_conn, + XCB_ATOM_NONE, + xwm->atoms[CLIPBOARD], + xwm->selection_timestamp); + } } void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat) { diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 407ee362..b0a24ff2 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -174,6 +174,7 @@ void xwm_surface_activate(struct wlr_xwm *xwm, xwm_send_focus_window(xwm, xsurface); xwm->focus_surface = xsurface; + xwm_set_selection_owner(xwm); xcb_flush(xwm->xcb_conn); } diff --git a/xwayland/xwm.h b/xwayland/xwm.h index 47e674c0..ad8bf9d5 100644 --- a/xwayland/xwm.h +++ b/xwayland/xwm.h @@ -98,6 +98,8 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event); +void xwm_set_selection_owner(struct wlr_xwm *xwm); + void xwm_selection_init(struct wlr_xwm *xwm); void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat); From 52cda5653b52ff33f26b27fb2444c9895192d87e Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 8 Dec 2017 09:23:26 -0500 Subject: [PATCH 11/21] Revert "xwm: only take clipboard if window is focused" This reverts commit e3542d879d50d6239ad4ca24b4d30520fef40a87. --- xwayland/selection.c | 23 ++++++----------------- xwayland/xwm.c | 1 - xwayland/xwm.h | 2 -- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index 07d1d48f..3d57f394 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -466,6 +466,7 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, return 1; } + int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event) { if (!xwm->seat) { @@ -541,25 +542,13 @@ static void handle_seat_set_selection(struct wl_listener *listener, return; } - if (source->send == data_source_send) { + if (source->send == data_source_send) return; - } - xwm_set_selection_owner(xwm); -} - -void xwm_set_selection_owner(struct wlr_xwm *xwm) { - if (xwm->focus_surface && xwm->seat->selection_source) { - xcb_set_selection_owner(xwm->xcb_conn, - xwm->selection_window, - xwm->atoms[CLIPBOARD], - XCB_TIME_CURRENT_TIME); - } else { - xcb_set_selection_owner(xwm->xcb_conn, - XCB_ATOM_NONE, - xwm->atoms[CLIPBOARD], - xwm->selection_timestamp); - } + 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) { diff --git a/xwayland/xwm.c b/xwayland/xwm.c index a93f3525..f1add638 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -175,7 +175,6 @@ void xwm_surface_activate(struct wlr_xwm *xwm, xwm_send_focus_window(xwm, xsurface); xwm->focus_surface = xsurface; - xwm_set_selection_owner(xwm); xcb_flush(xwm->xcb_conn); } diff --git a/xwayland/xwm.h b/xwayland/xwm.h index ad8bf9d5..47e674c0 100644 --- a/xwayland/xwm.h +++ b/xwayland/xwm.h @@ -98,8 +98,6 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event); -void xwm_set_selection_owner(struct wlr_xwm *xwm); - void xwm_selection_init(struct wlr_xwm *xwm); void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat); From a4bb0574c46ab2364abcee75b597adf7c52c076e Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 8 Dec 2017 09:46:50 -0500 Subject: [PATCH 12/21] selection fixes --- xwayland/selection.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index 3d57f394..fe4132ac 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -434,7 +434,8 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, if (xwm->selection_owner != xwm->selection_window) { // A real X client selection went away, not our // proxy selection - // TODO: Clear the wayland selection (or not)? + wlr_seat_set_selection(xwm->seat, NULL, + wl_display_next_serial(xwm->xwayland->wl_display)); } xwm->selection_owner = XCB_WINDOW_NONE; @@ -442,6 +443,8 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, return 1; } + xwm->selection_owner = 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. @@ -451,8 +454,6 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, return 1; } - xwm->selection_owner = xfixes_selection_notify->owner; - xwm->incr = 0; // doing this will give a selection notify where we actually handle the sync xcb_convert_selection(xwm->xcb_conn, xwm->selection_window, @@ -542,8 +543,9 @@ static void handle_seat_set_selection(struct wl_listener *listener, return; } - if (source->send == data_source_send) + if (source->send == data_source_send) { return; + } xcb_set_selection_owner(xwm->xcb_conn, xwm->selection_window, From 4da404818957d37fba4c0d6346a5f2f8a8e6db38 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sat, 9 Dec 2017 09:21:11 -0500 Subject: [PATCH 13/21] remove todo --- xwayland/selection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index fe4132ac..ba386341 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -450,7 +450,7 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, // answer TIMESTAMP conversion requests correctly. if (xfixes_selection_notify->owner == xwm->selection_window) { xwm->selection_timestamp = xfixes_selection_notify->timestamp; - wlr_log(L_DEBUG, "TODO: our window"); + wlr_log(L_DEBUG, "our window, skipping"); return 1; } From 62a16a356d187e9e38a4c67d32513b7396cf135f Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 14 Dec 2017 13:59:36 +0100 Subject: [PATCH 14/21] Support selection MIME types from xwayland to wayland --- xwayland/selection.c | 84 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index 220736d4..f7f8d440 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -328,6 +328,7 @@ static void xwm_get_selection_data(struct wlr_xwm *xwm) { struct x11_data_source { struct wlr_data_source base; struct wlr_xwm *xwm; + struct wl_array mime_types_atoms; }; static void data_source_accept(struct wlr_data_source *source, uint32_t time, @@ -335,24 +336,40 @@ static void data_source_accept(struct wlr_data_source *source, uint32_t time, } static void data_source_send(struct wlr_data_source *base, - const char *mime_type, int32_t fd) { + const char *requested_mime_type, int32_t fd) { struct x11_data_source *source = (struct x11_data_source *)base; struct wlr_xwm *xwm = source->xwm; - if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) { - // Get data for the utf8_string target - xcb_convert_selection(xwm->xcb_conn, - xwm->selection_window, - xwm->atoms[CLIPBOARD], - xwm->atoms[UTF8_STRING], - xwm->atoms[WL_SELECTION], - XCB_TIME_CURRENT_TIME); - - xcb_flush(xwm->xcb_conn); - - fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK); - xwm->data_source_fd = fd; + bool found = false; + xcb_atom_t mime_type_atom; + char **mime_type_ptr; + size_t i = 0; + wl_array_for_each(mime_type_ptr, &source->base.mime_types) { + char *mime_type = *mime_type_ptr; + if (strcmp(mime_type, requested_mime_type) == 0) { + found = true; + xcb_atom_t *atoms = source->mime_types_atoms.data; + mime_type_atom = atoms[i]; + break; + } + ++i; } + if (!found) { + return; + } + + // Get data for the utf8_string target + xcb_convert_selection(xwm->xcb_conn, + xwm->selection_window, + xwm->atoms[CLIPBOARD], + mime_type_atom, + xwm->atoms[WL_SELECTION], + XCB_TIME_CURRENT_TIME); + + xcb_flush(xwm->xcb_conn); + + fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK); + xwm->data_source_fd = fd; } static void data_source_cancel(struct wlr_data_source *source) { @@ -393,13 +410,45 @@ static void xwm_get_selection_targets(struct wlr_xwm *xwm) { source->xwm = xwm; wl_array_init(&source->base.mime_types); + wl_array_init(&source->mime_types_atoms); xcb_atom_t *value = xcb_get_property_value(reply); for (uint32_t i = 0; i < reply->value_len; i++) { + char *mime_type = NULL; + if (value[i] == xwm->atoms[UTF8_STRING]) { - char **p = wl_array_add(&source->base.mime_types, sizeof *p); - if (p) { - *p = strdup("text/plain;charset=utf-8"); + mime_type = strdup("text/plain;charset=utf-8"); + } else if (value[i] == xwm->atoms[TEXT]) { + mime_type = strdup("text/plain"); + } else if (value[i] != xwm->atoms[TARGETS] && + value[i] != xwm->atoms[TIMESTAMP]) { + xcb_get_atom_name_cookie_t name_cookie = + xcb_get_atom_name(xwm->xcb_conn, value[i]); + xcb_get_atom_name_reply_t *name_reply = + xcb_get_atom_name_reply(xwm->xcb_conn, name_cookie, NULL); + if (name_reply == NULL) { + continue; } + char *name = xcb_get_atom_name_name(name_reply); + if (strchr(name, '/') != NULL) { + mime_type = strdup(name); + } + free(name_reply); + } + + if (mime_type != NULL) { + char **mime_type_ptr = + wl_array_add(&source->base.mime_types, sizeof(*mime_type_ptr)); + if (mime_type_ptr == NULL) { + break; + } + *mime_type_ptr = mime_type; + + xcb_atom_t *atom_ptr = + wl_array_add(&source->mime_types_atoms, sizeof(*atom_ptr)); + if (atom_ptr == NULL) { + break; + } + *atom_ptr = value[i]; } } @@ -407,7 +456,6 @@ static void xwm_get_selection_targets(struct wlr_xwm *xwm) { wl_display_next_serial(xwm->xwayland->wl_display)); free(reply); - } static void xwm_handle_selection_notify(struct wlr_xwm *xwm, From a4094c82c79e4cfc8f423c09d6c3d41fbe2a333d Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 14 Dec 2017 16:24:12 +0100 Subject: [PATCH 15/21] Support selection MIME types from wayland to xwayland --- xwayland/selection.c | 131 +++++++++++++++++++++++++++++++------------ xwayland/xwm.c | 7 +-- 2 files changed, 96 insertions(+), 42 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index f7f8d440..07f9a68d 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -61,7 +61,7 @@ static int xwm_read_data_source(int fd, uint32_t mask, void *data) { int len = read(fd, p, available); if (len == -1) { - wlr_log(L_ERROR, "read error from data source: %m\n"); + wlr_log(L_ERROR, "read error from data source: %m"); xwm_send_selection_notify(xwm, XCB_ATOM_NONE); wl_event_source_remove(xwm->property_source); xwm->property_source = NULL; @@ -69,13 +69,13 @@ static int xwm_read_data_source(int fd, uint32_t mask, void *data) { wl_array_release(&xwm->source_data); } - wlr_log(L_DEBUG, "read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n", + wlr_log(L_DEBUG, "read %d (available %d, mask 0x%x) bytes: \"%.*s\"", 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", + wlr_log(L_DEBUG, "got %zu bytes, starting incr", xwm->source_data.size); xwm->incr = 1; xcb_change_property(xwm->xcb_conn, @@ -92,19 +92,19 @@ static int xwm_read_data_source(int fd, uint32_t mask, void *data) { 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); + "property delete", 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", + "property deleted, setting new property", xwm->source_data.size); xwm_flush_source_data(xwm); } } else if (len == 0 && !xwm->incr) { - wlr_log(L_DEBUG, "non-incr transfer complete\n"); + wlr_log(L_DEBUG, "non-incr transfer complete"); /* Non-incr transfer all done. */ xwm_flush_source_data(xwm); xwm_send_selection_notify(xwm, xwm->selection_request.property); @@ -115,15 +115,15 @@ static int xwm_read_data_source(int fd, uint32_t mask, void *data) { 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"); + wlr_log(L_DEBUG, "incr transfer complete"); 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); + "property delete", xwm->source_data.size); } else { wlr_log(L_DEBUG, "got %zu bytes, " - "property deleted, setting new property\n", + "property deleted, setting new property", xwm->source_data.size); xwm_flush_source_data(xwm); } @@ -134,7 +134,7 @@ static int xwm_read_data_source(int fd, uint32_t mask, void *data) { xwm->data_source_fd = -1; close(fd); } else { - wlr_log(L_DEBUG, "nothing happened, buffered the bytes\n"); + wlr_log(L_DEBUG, "nothing happened, buffered the bytes"); } return 1; @@ -146,7 +146,7 @@ static void xwm_send_data(struct wlr_xwm *xwm, xcb_atom_t target, int p[2]; if (pipe(p) == -1) { - wlr_log(L_ERROR, "pipe failed: %m\n"); + wlr_log(L_ERROR, "pipe failed: %m"); xwm_send_selection_notify(xwm, XCB_ATOM_NONE); return; } @@ -185,12 +185,39 @@ static void xwm_send_timestamp(struct wlr_xwm *xwm) { } 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], - }; + struct wlr_data_source *source = xwm->seat->selection_source; + size_t n = 2 + source->mime_types.size / sizeof(char *); + xcb_atom_t *targets = malloc(n * sizeof(xcb_atom_t)); + if (targets == NULL) { + return; + } + targets[0] = xwm->atoms[TIMESTAMP]; + targets[1] = xwm->atoms[TARGETS]; + + size_t i = 2; + char **mime_type_ptr; + wl_array_for_each(mime_type_ptr, &source->mime_types) { + char *mime_type = *mime_type_ptr; + xcb_atom_t atom; + if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) { + atom = xwm->atoms[UTF8_STRING]; + } else if (strcmp(mime_type, "text/plain") == 0) { + atom = xwm->atoms[TEXT]; + } else { + xcb_intern_atom_cookie_t cookie = + xcb_intern_atom(xwm->xcb_conn, 0, strlen(mime_type), mime_type); + xcb_intern_atom_reply_t *reply = + xcb_intern_atom_reply(xwm->xcb_conn, cookie, NULL); + if (reply == NULL) { + --n; + continue; + } + atom = reply->atom; + free(reply); + } + targets[i] = atom; + ++i; + } xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, @@ -198,9 +225,11 @@ static void xwm_send_targets(struct wlr_xwm *xwm) { xwm->selection_request.property, XCB_ATOM_ATOM, 32, // format - sizeof(targets) / sizeof(targets[0]), targets); + n, targets); xwm_send_selection_notify(xwm, xwm->selection_request.property); + + free(targets); } static void xwm_handle_selection_request(struct wlr_xwm *xwm, @@ -221,16 +250,41 @@ static void xwm_handle_selection_request(struct wlr_xwm *xwm, return; } + if (xwm->seat->selection_source == NULL) { + wlr_log(L_DEBUG, "not handling selection request:" + "no selection source assigned to xwayland seat"); + 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 if (selection_request->target == xwm->atoms[UTF8_STRING]) { + xwm_send_data(xwm, selection_request->target, "text/plain;charset=utf-8"); + } else if (selection_request->target == xwm->atoms[TEXT]) { + xwm_send_data(xwm, selection_request->target, "text/plain"); } else { - wlr_log(L_DEBUG, "can only handle UTF8_STRING targets\n"); - xwm_send_selection_notify(xwm, XCB_ATOM_NONE); + xcb_get_atom_name_cookie_t name_cookie = + xcb_get_atom_name(xwm->xcb_conn, selection_request->target); + xcb_get_atom_name_reply_t *name_reply = + xcb_get_atom_name_reply(xwm->xcb_conn, name_cookie, NULL); + if (name_reply == NULL) { + wlr_log(L_DEBUG, "not handling selection request: unknown atom"); + xwm_send_selection_notify(xwm, XCB_ATOM_NONE); + return; + } + size_t len = xcb_get_atom_name_name_length(name_reply); + char *mime_type = malloc((len + 1) * sizeof(char)); + if (mime_type == NULL) { + free(name_reply); + return; + } + memcpy(mime_type, xcb_get_atom_name_name(name_reply), len); + mime_type[len] = '\0'; + xwm_send_data(xwm, selection_request->target, mime_type); + free(mime_type); + free(name_reply); } } @@ -250,11 +304,11 @@ static int writable_callback(int fd, uint32_t mask, void *data) { } xwm->property_source = NULL; close(fd); - wlr_log(L_ERROR, "write error to target fd: %m\n"); + wlr_log(L_ERROR, "write error to target fd: %m"); return 1; } - wlr_log(L_DEBUG, "wrote %d (chunk size %d) of %d bytes\n", + wlr_log(L_DEBUG, "wrote %d (chunk size %d) of %d bytes", xwm->property_start + len, len, xcb_get_property_value_length(xwm->property_reply)); @@ -272,7 +326,7 @@ static int writable_callback(int fd, uint32_t mask, void *data) { xwm->selection_window, xwm->atoms[WL_SELECTION]); } else { - wlr_log(L_DEBUG, "transfer complete\n"); + wlr_log(L_DEBUG, "transfer complete"); close(fd); } } @@ -322,7 +376,6 @@ static void xwm_get_selection_data(struct wlr_xwm *xwm) { // for freeing it xwm_write_property(xwm, reply); } - } struct x11_data_source { @@ -358,7 +411,6 @@ static void data_source_send(struct wlr_data_source *base, return; } - // Get data for the utf8_string target xcb_convert_selection(xwm->xcb_conn, xwm->selection_window, xwm->atoms[CLIPBOARD], @@ -384,13 +436,14 @@ static void xwm_get_selection_targets(struct wlr_xwm *xwm) { xwm->atoms[WL_SELECTION], XCB_GET_PROPERTY_TYPE_ANY, 0, // offset - 4096 //length + 4096 // length ); xcb_get_property_reply_t *reply = xcb_get_property_reply(xwm->xcb_conn, cookie, NULL); - if (reply == NULL) + if (reply == NULL) { return; + } if (reply->type != XCB_ATOM_ATOM) { free(reply); @@ -428,9 +481,16 @@ static void xwm_get_selection_targets(struct wlr_xwm *xwm) { if (name_reply == NULL) { continue; } - char *name = xcb_get_atom_name_name(name_reply); - if (strchr(name, '/') != NULL) { - mime_type = strdup(name); + size_t len = xcb_get_atom_name_name_length(name_reply); + char *name = xcb_get_atom_name_name(name_reply); // not a C string + if (memchr(name, '/', len) != NULL) { + mime_type = malloc((len + 1) * sizeof(char)); + if (mime_type == NULL) { + free(name_reply); + continue; + } + memcpy(mime_type, name, len); + mime_type[len] = '\0'; } free(name_reply); } @@ -477,7 +537,6 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, xcb_xfixes_selection_notify_event_t *xfixes_selection_notify = (xcb_xfixes_selection_notify_event_t *) event; - if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) { if (xwm->selection_owner != xwm->selection_window) { // A real X client selection went away, not our @@ -487,7 +546,6 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, } xwm->selection_owner = XCB_WINDOW_NONE; - return 1; } @@ -515,10 +573,9 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, return 1; } - int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event) { - if (!xwm->seat) { + if (xwm->seat == NULL) { wlr_log(L_DEBUG, "not handling selection events:" "no seat assigned to xwayland"); return 0; diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 4027b77e..c3edebd2 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1118,15 +1118,12 @@ static void xwm_get_resources(struct wlr_xwm *xwm) { xcb_intern_atom(xwm->xcb_conn, 0, strlen(atom_map[i]), atom_map[i]); } for (i = 0; i < ATOM_LAST; i++) { - xcb_intern_atom_reply_t *reply; xcb_generic_error_t *error; - - reply = xcb_intern_atom_reply(xwm->xcb_conn, cookies[i], &error); - + xcb_intern_atom_reply_t *reply = + xcb_intern_atom_reply(xwm->xcb_conn, cookies[i], &error); if (reply && !error) { xwm->atoms[i] = reply->atom; } - free(reply); if (error) { From 343efd0c202ba7a2f872f117f6193175f831d97d Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 14 Dec 2017 16:44:30 +0100 Subject: [PATCH 16/21] Only reply to clipboard selection events --- xwayland/selection.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index 07f9a68d..d98f006f 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -237,6 +237,10 @@ static void xwm_handle_selection_request(struct wlr_xwm *xwm, xcb_selection_request_event_t *selection_request = (xcb_selection_request_event_t *) event; + if (selection_request->selection != xwm->atoms[CLIPBOARD]) { + return; + } + xwm->selection_request = *selection_request; xwm->incr = 0; xwm->flush_property_on_delete = 0; @@ -523,6 +527,10 @@ static void xwm_handle_selection_notify(struct wlr_xwm *xwm, xcb_selection_notify_event_t *selection_notify = (xcb_selection_notify_event_t *) event; + if (selection_notify->selection != xwm->atoms[CLIPBOARD]) { + return; + } + if (selection_notify->property == XCB_ATOM_NONE) { wlr_log(L_ERROR, "convert selection failed"); } else if (selection_notify->target == xwm->atoms[TARGETS]) { @@ -537,6 +545,10 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, xcb_xfixes_selection_notify_event_t *xfixes_selection_notify = (xcb_xfixes_selection_notify_event_t *) event; + if (xfixes_selection_notify->selection != xwm->atoms[CLIPBOARD]) { + return 0; + } + if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) { if (xwm->selection_owner != xwm->selection_window) { // A real X client selection went away, not our @@ -563,7 +575,7 @@ static int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, xwm->incr = 0; // doing this will give a selection notify where we actually handle the sync xcb_convert_selection(xwm->xcb_conn, xwm->selection_window, - xwm->atoms[CLIPBOARD], + xfixes_selection_notify->selection, xwm->atoms[TARGETS], xwm->atoms[WL_SELECTION], xfixes_selection_notify->timestamp); From b884025558e750268a06818dc63bc46716c75843 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 14 Dec 2017 23:57:44 +0100 Subject: [PATCH 17/21] Deny access to clipboard if no xwayland window is focused --- xwayland/selection.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index d98f006f..6b55f473 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -238,6 +238,7 @@ static void xwm_handle_selection_request(struct wlr_xwm *xwm, (xcb_selection_request_event_t *) event; if (selection_request->selection != xwm->atoms[CLIPBOARD]) { + xwm_send_selection_notify(xwm, XCB_ATOM_NONE); return; } @@ -255,11 +256,19 @@ static void xwm_handle_selection_request(struct wlr_xwm *xwm, } if (xwm->seat->selection_source == NULL) { - wlr_log(L_DEBUG, "not handling selection request:" + wlr_log(L_DEBUG, "not handling selection request: " "no selection source assigned to xwayland seat"); return; } + // No xwayland surface focused, deny access to clipboard + if (xwm->focus_surface == NULL) { + wlr_log(L_DEBUG, "denying read access to clipboard: " + "no xwayland surface focused"); + xwm_send_selection_notify(xwm, XCB_ATOM_NONE); + return; + } + if (selection_request->target == xwm->atoms[TARGETS]) { xwm_send_targets(xwm); } else if (selection_request->target == xwm->atoms[TIMESTAMP]) { @@ -531,6 +540,13 @@ static void xwm_handle_selection_notify(struct wlr_xwm *xwm, return; } + // No xwayland surface focused, deny access to clipboard + if (xwm->focus_surface == NULL) { + wlr_log(L_DEBUG, "denying write access to clipboard: " + "no xwayland surface focused"); + return; + } + if (selection_notify->property == XCB_ATOM_NONE) { wlr_log(L_ERROR, "convert selection failed"); } else if (selection_notify->target == xwm->atoms[TARGETS]) { From 5584f42c1ef1bbc4bf9f8184ddeeb7ac2d308aed Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 15 Dec 2017 00:05:05 +0100 Subject: [PATCH 18/21] Destroy xwm selection window on destroy --- xwayland/xwm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 2e859460..6cbe9bda 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1090,6 +1090,9 @@ void xwm_destroy(struct wlr_xwm *xwm) { if (!xwm) { return; } + if (xwm->selection_window) { + xcb_destroy_window(xwm->xcb_conn, xwm->selection_window); + } if (xwm->cursor) { xcb_free_cursor(xwm->xcb_conn, xwm->cursor); } From 76dcb694c142ca7ef288916076b446ae6f7baafa Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 15 Dec 2017 09:37:04 +0100 Subject: [PATCH 19/21] Send empty selection when clipboard is empty --- xwayland/selection.c | 1 + 1 file changed, 1 insertion(+) diff --git a/xwayland/selection.c b/xwayland/selection.c index 6b55f473..062fbf9c 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -258,6 +258,7 @@ static void xwm_handle_selection_request(struct wlr_xwm *xwm, if (xwm->seat->selection_source == NULL) { wlr_log(L_DEBUG, "not handling selection request: " "no selection source assigned to xwayland seat"); + xwm_send_selection_notify(xwm, XCB_ATOM_NONE); return; } From 810c4b119bcce2c55ddca8933cbc89381602674b Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 15 Dec 2017 09:42:42 +0100 Subject: [PATCH 20/21] Fix clipboard from xwayland to wayland --- xwayland/selection.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index 062fbf9c..25386b1a 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -541,16 +541,17 @@ static void xwm_handle_selection_notify(struct wlr_xwm *xwm, return; } - // No xwayland surface focused, deny access to clipboard - if (xwm->focus_surface == NULL) { - wlr_log(L_DEBUG, "denying write access to clipboard: " - "no xwayland surface focused"); - return; - } - if (selection_notify->property == XCB_ATOM_NONE) { wlr_log(L_ERROR, "convert selection failed"); } else if (selection_notify->target == xwm->atoms[TARGETS]) { + // No xwayland surface focused, deny access to clipboard + if (xwm->focus_surface == NULL) { + wlr_log(L_DEBUG, "denying write access to clipboard: " + "no xwayland surface focused"); + return; + } + + // This sets the Wayland clipboard (by calling wlr_seat_set_selection) xwm_get_selection_targets(xwm); } else { xwm_get_selection_data(xwm); From 804c7a73a6d7af18e202109d331d1208af7f57d4 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 18 Dec 2017 00:44:43 +0100 Subject: [PATCH 21/21] Allow clients to unset the selection --- types/wlr_data_device.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/wlr_data_device.c b/types/wlr_data_device.c index dacb2a45..b27009dc 100644 --- a/types/wlr_data_device.c +++ b/types/wlr_data_device.c @@ -335,11 +335,11 @@ void wlr_seat_set_selection(struct wlr_seat *seat, static void data_device_set_selection(struct wl_client *client, struct wl_resource *dd_resource, struct wl_resource *source_resource, uint32_t serial) { - if (!source_resource) { - return; + struct wlr_data_source *source = NULL; + if (source_resource != NULL) { + source = wl_resource_get_user_data(source_resource); } - struct wlr_data_source *source = wl_resource_get_user_data(source_resource); struct wlr_seat_client *seat_client = wl_resource_get_user_data(dd_resource);