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;