mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
selection: do mime-type based decoding before passing data to callback
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.
This commit is contained in:
parent
0271e377c0
commit
43f293f22e
1 changed files with 104 additions and 119 deletions
223
selection.c
223
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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue