Accumulate DnD image data

This commit is contained in:
Kovid Goyal 2026-04-03 23:18:07 +05:30
parent 950497ac0c
commit 0fcbd265b6
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
5 changed files with 55 additions and 16 deletions

View file

@ -265,8 +265,9 @@ operation, it indicates the drag should be started by sending ``t=P:x=-1``. At
this time if the user has already cancelled the drag or the terminal determines
the drag operation is not allowed, it must respond with ``t=R ; EPERM``. If any
other error occurs starting the drag operation, it must respond with the appropriate
POSIX error code. If the drag operation is successfully started, it must respond with
``t=R ; OK``.
POSIX error code. If it determines that the image data after conversion to
display format is too large, it must respond with ``t=R ; EFBIG``. If the drag
operation is successfully started, it must respond with ``t=R ; OK``.
Multiplexers
-----------------

View file

@ -18,6 +18,7 @@
#include <errno.h>
static const size_t MIME_LIST_SIZE_CAP = 1024 * 1024;
static const size_t PRESENT_DATA_CAP = 64 * 1024 * 1024;
// In test mode, this callable is invoked instead of schedule_write_to_child_if_possible.
// It receives (window_id: int, data: bytes) and its return value is ignored.
@ -869,8 +870,13 @@ drag_free_offer(Window *w) {
for (size_t i=0; i < ds.num_mimes; i++) free(ds.items[i].optional_data);
free(ds.items);
}
for (size_t i = 0; i < arraysz(ds.images); i++) {
if (ds.images[i].data) free(ds.images[i].data);
zero_at_ptr(ds.images + i);
}
ds.num_mimes = 0;
ds.pre_sent_total_sz = 0;
ds.images_sent_total_sz = 0;
}
void
@ -912,22 +918,49 @@ drag_add_mimes(Window *w, int allowed_operations, const char *data, size_t sz, b
void
drag_add_pre_sent_data(Window *w, unsigned idx, const uint8_t *payload, size_t sz) {
if (!ds.offer_being_built || idx >= ds.num_mimes) abrt(EINVAL);
if (sz + ds.pre_sent_total_sz > 64 * 1024 * 1024) abrt(EFBIG);
if (sz + ds.pre_sent_total_sz > PRESENT_DATA_CAP) abrt(EFBIG);
ds.pre_sent_total_sz += sz;
DragSourceItem *item = ds.items + idx;
if (!item->data_decode_initialized) {
item->data_decode_initialized = true;
base64_init_stream_decoder(&item->base64_state);
#define item ds.items[idx]
if (!item.data_decode_initialized) {
item.data_decode_initialized = true;
base64_init_stream_decoder(&item.base64_state);
}
if (item->data_capacity < sz + item->data_size) {
size_t newcap = MAX(item->data_size * 2, sz + item->data_size);
item->optional_data = realloc(item->optional_data, newcap);
if (!item->optional_data) abrt(ENOMEM);
item->data_capacity = newcap;
if (item.data_capacity < sz + item.data_size) {
size_t newcap = MAX(item.data_size * 2, sz + item.data_size);
item.optional_data = realloc(item.optional_data, newcap);
if (!item.optional_data) abrt(ENOMEM);
item.data_capacity = newcap;
}
size_t outlen = item->data_capacity - item->data_size;
if (!base64_decode_stream(&item->base64_state, payload, sz, item->optional_data + item->data_size, &outlen)) abrt(EINVAL);
item->data_size += outlen;
size_t outlen = item.data_capacity - item.data_size;
if (!base64_decode_stream(&item.base64_state, payload, sz, item.optional_data + item.data_size, &outlen)) abrt(EINVAL);
item.data_size += outlen;
#undef item
}
void
drag_add_image(Window *w, unsigned idx, int fmt, int width, int height, const uint8_t *payload, size_t sz) {
if (idx + 1 >= arraysz(ds.images)) abrt(EFBIG);
if (ds.images_sent_total_sz + sz > PRESENT_DATA_CAP) abrt(EFBIG);
ds.images_sent_total_sz += sz;
#define img ds.images[idx]
if (!img.started) {
if (fmt != 24 && fmt != 32 && fmt != 100) abrt(EINVAL);
if (width < 1 || height < 1) abrt(EINVAL);
img.started = true;
img.width = width; img.height = height;
img.fmt = fmt;
base64_init_stream_decoder(&img.base64_state);
}
if (img.capacity < sz + img.sz) {
size_t newcap = MAX(img.sz * 2, sz + img.sz);
img.data = realloc(img.data, newcap);
if (!img.data) abrt(ENOMEM);
img.capacity = newcap;
}
size_t outlen = img.capacity - img.sz;
if (!base64_decode_stream(&img.base64_state, payload, sz, img.data + img.sz, &outlen)) abrt(EINVAL);
img.sz += outlen;
#undef img
}
#undef abrt

View file

@ -26,3 +26,4 @@ void dnd_set_test_write_func(PyObject *func);
void drag_free_offer(Window *w);
void drag_add_mimes(Window *w, int allowed_operations, const char *data, size_t sz, bool has_more);
void drag_add_pre_sent_data(Window *w, unsigned idx, const uint8_t *payload, size_t sz);
void drag_add_image(Window *w, unsigned idx_, int fmt, int width, int height, const uint8_t *payload, size_t sz);

View file

@ -1550,6 +1550,7 @@ screen_handle_dnd_command(Screen *self, const DnDCommand *cmd, const uint8_t *pa
} break;
case 'p': {
if (cmd->cell_x >= 0) drag_add_pre_sent_data(w, cmd->cell_x, payload, cmd->payload_sz);
else drag_add_image(w, -cmd->cell_x, cmd->cell_y, cmd->pixel_x, cmd->pixel_y, payload, cmd->payload_sz);
} break;
}
}

View file

@ -299,7 +299,10 @@ typedef struct Window {
struct { double x, y; monotonic_t at; } initial_left_press;
char *mimes_buf; size_t num_mimes, bufsz;
DragSourceItem *items;
size_t pre_sent_total_sz;
struct {
int width, height, fmt; uint8_t *data; size_t sz, capacity; bool started; base64_state base64_state;
} images[16];
size_t pre_sent_total_sz, images_sent_total_sz;
int allowed_operations;
bool offer_being_built;
} drag_source;