From 60c3ff8737165b572ba9ff66f68eff02652a0400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Nov 2019 13:11:15 +0100 Subject: [PATCH] selection: send primary/clipboard data asynchronously TODO: try writing synchronously first --- selection.c | 118 ++++++++++++++++++++++++++++++++++++---------------- terminal.c | 1 + 2 files changed, 83 insertions(+), 36 deletions(-) diff --git a/selection.c b/selection.c index 6490fb06..30f9b42a 100644 --- a/selection.c +++ b/selection.c @@ -7,6 +7,8 @@ #include #include +#include + #define LOG_MODULE "selection" #define LOG_ENABLE_DBG 0 #include "log.h" @@ -315,32 +317,76 @@ target(void *data, struct wl_data_source *wl_data_source, const char *mime_type) LOG_WARN("TARGET: mime-type=%s", mime_type); } +typedef bool (*fdm_handler_t)(struct fdm *fdm, int fd, int events, void *data); + +struct clipboard_send { + char *data; + size_t len; + size_t idx; +}; + +static bool +fdm_send(struct fdm *fdm, int fd, int events, void *data) +{ + struct clipboard_send *ctx = data; + size_t left = ctx->len - ctx->idx; + + while (left > 0) { + ssize_t count = write(fd, &ctx->data[ctx->idx], left); + if (count < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + return true; + + /* + * Log error, but handle this as if it completed + * successfully - we don't want to terminate just because + * the clipboard receiver cancelled + */ + LOG_ERRNO("failed to write to FD=%d", fd); + goto out; + } + + LOG_DBG("async sent %zd bytes (of %zu, %zu left)", + count, left, left - count); + + ctx->idx += count; + left -= count; + } + +out: + fdm_del(fdm, fd); + free(ctx->data); + free(ctx); + return true; +} + static void send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, int32_t fd) { - const struct wl_clipboard *clipboard - = wl_data_source_get_user_data(wl_data_source); + struct wayland *wayl = data; + const struct wl_clipboard *clipboard = &wayl->clipboard; assert(clipboard != NULL); assert(clipboard->text != NULL); - size_t left = strlen(clipboard->text); - size_t idx = 0; - - while (left > 0) { - ssize_t ret = write(fd, &clipboard->text[idx], left); - - if (ret == -1 && errno != EINTR) { - LOG_ERRNO("failed to write to clipboard"); - break; - } - - left -= ret; - idx += ret; + int flags; + if ((flags = fcntl(fd, F_GETFL)) < 0 || + fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) + { + LOG_ERRNO("failed to set O_NONBLOCK"); + return; } - close(fd); + struct clipboard_send *ctx = malloc(sizeof(*ctx)); + *ctx = (struct clipboard_send) { + .data = strdup(clipboard->text), + .len = strlen(clipboard->text), + .idx = 0, + }; + + if (!fdm_add(wayl->fdm, fd, EPOLLOUT, &fdm_send, ctx)) + free(ctx); } static void @@ -386,28 +432,29 @@ primary_send(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1, const char *mime_type, int32_t fd) { - const struct wl_primary *primary - = zwp_primary_selection_source_v1_get_user_data(zwp_primary_selection_source_v1); + struct wayland *wayl = data; + const struct wl_primary *primary = &wayl->primary; assert(primary != NULL); assert(primary->text != NULL); - size_t left = strlen(primary->text); - size_t idx = 0; - - while (left > 0) { - ssize_t ret = write(fd, &primary->text[idx], left); - - if (ret == -1 && errno != EINTR) { - LOG_ERRNO("failed to write to clipboard"); - break; - } - - left -= ret; - idx += ret; + int flags; + if ((flags = fcntl(fd, F_GETFL)) < 0 || + fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) + { + LOG_ERRNO("failed to set O_NONBLOCK"); + return; } - close(fd); + struct clipboard_send *ctx = malloc(sizeof(*ctx)); + *ctx = (struct clipboard_send) { + .data = strdup(primary->text), + .len = strlen(primary->text), + .idx = 0, + }; + + if (!fdm_add(wayl->fdm, fd, EPOLLOUT, &fdm_send, ctx)) + free(ctx); } static void @@ -458,10 +505,10 @@ text_to_clipboard(struct terminal *term, char *text, uint32_t serial) clipboard->text = text; /* Configure source */ + LOG_INFO("registering listener, term=%p", term); wl_data_source_offer(clipboard->data_source, "text/plain;charset=utf-8"); - wl_data_source_add_listener(clipboard->data_source, &data_source_listener, term); + wl_data_source_add_listener(clipboard->data_source, &data_source_listener, term->wl); wl_data_device_set_selection(term->wl->data_device, clipboard->data_source, serial); - wl_data_source_set_user_data(clipboard->data_source, clipboard); /* Needed when sending the selection to other client */ clipboard->serial = serial; @@ -588,9 +635,8 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial) /* Configure source */ zwp_primary_selection_source_v1_offer(primary->data_source, "text/plain;charset=utf-8"); - zwp_primary_selection_source_v1_add_listener(primary->data_source, &primary_selection_source_listener, term); + zwp_primary_selection_source_v1_add_listener(primary->data_source, &primary_selection_source_listener, term->wl); zwp_primary_selection_device_v1_set_selection(term->wl->primary_selection_device, primary->data_source, serial); - zwp_primary_selection_source_v1_set_user_data(primary->data_source, primary); /* Needed when sending the selection to other client */ primary->serial = serial; diff --git a/terminal.c b/terminal.c index 38682104..fafd942d 100644 --- a/terminal.c +++ b/terminal.c @@ -273,6 +273,7 @@ fdm_blink(struct fdm *fdm, int fd, int events, void *data) LOG_DBG("blink timer expired %llu times", (unsigned long long)expiration_count); + /* Invert blink state */ term->blink.state = term->blink.state == BLINK_ON ? BLINK_OFF : BLINK_ON;