From 43f293f22ed1566939364f6edeb4bd28283ecab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 1 Nov 2020 11:52:11 +0100 Subject: [PATCH] selection: do mime-type based decoding before passing data to callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intention here is to make *all* text_from_clipboard() and text_from_primary() callers benefit from mime-type base decoding (e.g. URI list decoding). Previously, the decoding was done *in* the callback, which meant only the “default” clipboard/primary selection paste functions, and DnD, recognized URI lists. --- selection.c | 223 ++++++++++++++++++++++++---------------------------- 1 file changed, 104 insertions(+), 119 deletions(-) diff --git a/selection.c b/selection.c index 6767f5fe..46fd5f1e 100644 --- a/selection.c +++ b/selection.c @@ -1131,6 +1131,16 @@ struct clipboard_receive { int timeout_fd; struct itimerspec timeout; + void (*decoder)(struct clipboard_receive *ctx, char *data, size_t size); + + /* URI state */ + bool add_space; + struct { + char *data; + size_t sz; + size_t idx; + } buf; + /* Callback data */ void (*cb)(char *data, size_t size, void *user); void (*done)(void *user); @@ -1143,6 +1153,7 @@ clipboard_receive_done(struct fdm *fdm, struct clipboard_receive *ctx) fdm_del(fdm, ctx->timeout_fd); fdm_del(fdm, ctx->read_fd); ctx->done(ctx->user); + free(ctx->buf.data); free(ctx); } @@ -1172,6 +1183,65 @@ fdm_receive_timeout(struct fdm *fdm, int fd, int events, void *data) return true; } +static void +fdm_receive_decoder_plain(struct clipboard_receive *ctx, char *data, size_t size) +{ + ctx->cb(data, size, ctx->user); +} + +static void +fdm_receive_decoder_uri(struct clipboard_receive *ctx, char *data, size_t size) +{ + while (ctx->buf.idx + size > ctx->buf.sz) { + size_t new_sz = ctx->buf.sz == 0 ? size : 2 * ctx->buf.sz; + ctx->buf.data = xrealloc(ctx->buf.data, new_sz); + ctx->buf.sz = new_sz; + } + + memcpy(&ctx->buf.data[ctx->buf.idx], data, size); + ctx->buf.idx += size; + + char *start = ctx->buf.data; + char *end = NULL; + + while ((end = memchr(start, '\n', ctx->buf.idx - (start - ctx->buf.data))) != NULL) { + const size_t len = end - start; + + LOG_DBG("URI: \"%.*s\"", (int)len, start); + + char *scheme, *host, *path; + if (!uri_parse(start, len, &scheme, NULL, NULL, &host, NULL, &path, NULL, NULL)) { + LOG_ERR("drag-and-drop: invalid URI: %.*s", (int)len, start); + start = end + 1; + continue; + } + + if (ctx->add_space) + ctx->cb(" ", 1, ctx->user); + ctx->add_space = true; + + + if (strcmp(scheme, "file") == 0 && hostname_is_localhost(host)) { + ctx->cb("'", 1, ctx->user); + ctx->cb(path, strlen(path), ctx->user); + ctx->cb("'", 1, ctx->user); + } else + ctx->cb(start, len, ctx->user); + + start = end + 1; + + free(scheme); + free(host); + free(path); + } + + const size_t ofs = start - ctx->buf.data; + const size_t left = ctx->buf.idx - ofs; + + memmove(&ctx->buf.data[0], &ctx->buf.data[ofs], left); + ctx->buf.idx = left; +} + static bool fdm_receive(struct fdm *fdm, int fd, int events, void *data) { @@ -1208,7 +1278,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) again: for (size_t i = 0; i < left - 1; i++) { if (p[i] == '\r' && p[i + 1] == '\n') { - ctx->cb(p, i, ctx->user); + ctx->decoder(ctx, p, i); assert(i + 1 <= left); p += i + 1; @@ -1217,7 +1287,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) } } - ctx->cb(p, left, ctx->user); + ctx->decoder(ctx, p, left); left = 0; } @@ -1228,6 +1298,7 @@ done: static void begin_receive_clipboard(struct terminal *term, int read_fd, + enum data_offer_mime_type mime_type, void (*cb)(char *data, size_t size, void *user), void (*done)(void *user), void *user) { @@ -1259,6 +1330,9 @@ begin_receive_clipboard(struct terminal *term, int read_fd, .read_fd = read_fd, .timeout_fd = timeout_fd, .timeout = timeout, + .decoder = (mime_type == DATA_OFFER_MIME_URI_LIST + ? &fdm_receive_decoder_uri + : &fdm_receive_decoder_plain), .cb = cb, .done = done, .user = user, @@ -1308,97 +1382,20 @@ text_from_clipboard(struct seat *seat, struct terminal *term, /* Don't keep our copy of the write-end open (or we'll never get EOF) */ close(write_fd); - begin_receive_clipboard(term, read_fd, cb, done, user); -} - -struct receive_offer_context { - struct terminal *term; - - /* URI state */ - bool add_space; - struct { - char *data; - size_t sz; - size_t idx; - } buf; - - union { - struct wl_data_offer *data_offer; - struct zwp_primary_selection_offer_v1 *primary_offer; - }; -}; - -static void -receive_offer_text(char *data, size_t size, void *user) -{ - struct receive_offer_context *ctx = user; - assert(ctx->term->is_sending_paste_data); - term_paste_data_to_slave(ctx->term, data, size); + begin_receive_clipboard(term, read_fd, clipboard->mime_type, cb, done, user); } static void -receive_offer_uri(char *data, size_t size, void *user) +receive_offer(char *data, size_t size, void *user) { - struct receive_offer_context *ctx = user; - - while (ctx->buf.idx + size > ctx->buf.sz) { - size_t new_sz = ctx->buf.sz == 0 ? size : 2 * ctx->buf.sz; - ctx->buf.data = xrealloc(ctx->buf.data, new_sz); - ctx->buf.sz = new_sz; - } - - memcpy(&ctx->buf.data[ctx->buf.idx], data, size); - ctx->buf.idx += size; - - char *start = ctx->buf.data; - char *end = NULL; - - while ((end = memchr(start, '\n', ctx->buf.idx - (start - ctx->buf.data))) != NULL) { - const size_t len = end - start; - - LOG_DBG("URI: \"%.*s\"", (int)len, start); - - char *scheme, *host, *path; - if (!uri_parse(start, len, &scheme, NULL, NULL, &host, NULL, &path, NULL, NULL)) { - LOG_ERR("drag-and-drop: invalid URI: %.*s", (int)len, start); - start = end + 1; - continue; - } - - if (ctx->add_space) - receive_offer_text(" ", 1, ctx); - ctx->add_space = true; - - receive_offer_text("'", 1, ctx); - - if (strcmp(scheme, "file") == 0 && hostname_is_localhost(host)) - receive_offer_text(path, strlen(path), ctx); - else - receive_offer_text(start, len, ctx); - - receive_offer_text("'", 1, ctx); - start = end + 1; - - free(scheme); - free(host); - free(path); - } - - const size_t ofs = start - ctx->buf.data; - const size_t left = ctx->buf.idx - ofs; - - memmove(&ctx->buf.data[0], &ctx->buf.data[ofs], left); - ctx->buf.idx = left; + struct terminal *term = user; + assert(term->is_sending_paste_data); + term_paste_data_to_slave(term, data, size); } - static void receive_offer_done(void *user) { - struct receive_offer_context *ctx = user; - struct terminal *term = ctx->term; - - free(ctx->buf.data); - free(ctx); + struct terminal *term = user; if (term->bracketed_paste) term_paste_data_to_slave(term, "\033[201~", 6); @@ -1427,18 +1424,7 @@ selection_from_clipboard(struct seat *seat, struct terminal *term, uint32_t seri if (term->bracketed_paste) term_paste_data_to_slave(term, "\033[200~", 6); - struct receive_offer_context *ctx = xmalloc(sizeof(*ctx)); - *ctx = (struct receive_offer_context) { - .term = term, - .data_offer = clipboard->data_offer, - }; - - text_from_clipboard( - seat, term, - (clipboard->mime_type == DATA_OFFER_MIME_URI_LIST - ? &receive_offer_uri - : &receive_offer_text), - &receive_offer_done, ctx); + text_from_clipboard(seat, term, &receive_offer, &receive_offer_done, term); } bool @@ -1533,7 +1519,7 @@ text_from_primary( /* Don't keep our copy of the write-end open (or we'll never get EOF) */ close(write_fd); - begin_receive_clipboard(term, read_fd, cb, done, user); + begin_receive_clipboard(term, read_fd, primary->mime_type, cb, done, user); } void @@ -1555,18 +1541,7 @@ selection_from_primary(struct seat *seat, struct terminal *term) if (term->bracketed_paste) term_paste_data_to_slave(term, "\033[200~", 6); - struct receive_offer_context *ctx = xmalloc(sizeof(*ctx)); - *ctx = (struct receive_offer_context){ - .term = term, - .primary_offer = primary->data_offer, - }; - - text_from_primary( - seat, term, - (primary->mime_type == DATA_OFFER_MIME_URI_LIST - ? &receive_offer_uri - : &receive_offer_text), - &receive_offer_done, ctx); + text_from_primary(seat, term, &receive_offer, &receive_offer_done, term); } static void @@ -1771,14 +1746,27 @@ motion(void *data, struct wl_data_device *wl_data_device, uint32_t time, { } +struct dnd_context { + struct terminal *term; + struct wl_data_offer *data_offer; +}; + +static void +receive_dnd(char *data, size_t size, void *user) +{ + struct dnd_context *ctx = user; + receive_offer(data, size, ctx->term); +} + static void receive_dnd_done(void *user) { - struct receive_offer_context *ctx = user; + struct dnd_context *ctx = user; wl_data_offer_finish(ctx->data_offer); wl_data_offer_destroy(ctx->data_offer); - receive_offer_done(user); + receive_offer_done(ctx->term); + free(ctx); } static void @@ -1791,8 +1779,8 @@ drop(void *data, struct wl_data_device *wl_data_device) struct wl_clipboard *clipboard = &seat->clipboard; - struct receive_offer_context *ctx = xmalloc(sizeof(*ctx)); - *ctx = (struct receive_offer_context){ + struct dnd_context *ctx = xmalloc(sizeof(*ctx)); + *ctx = (struct dnd_context){ .term = term, .data_offer = clipboard->data_offer, }; @@ -1823,11 +1811,8 @@ drop(void *data, struct wl_data_device *wl_data_device) term_paste_data_to_slave(term, "\033[200~", 6); begin_receive_clipboard( - term, read_fd, - (clipboard->mime_type == DATA_OFFER_MIME_URI_LIST - ? &receive_offer_uri - : &receive_offer_text), - &receive_dnd_done, ctx); + term, read_fd, clipboard->mime_type, + &receive_dnd, &receive_dnd_done, ctx); /* data offer is now “owned” by the receive context */ clipboard->data_offer = NULL;