Implement drag source data functions: drag_free_data, drag_get_data, drag_process_item_data
Agent-Logs-Url: https://github.com/kovidgoyal/kitty/sessions/39fe0e9c-1286-442c-8d7d-7cea5905a178 Co-authored-by: kovidgoyal <1308621+kovidgoyal@users.noreply.github.com>
This commit is contained in:
parent
78dfc1f940
commit
054ea80e00
1 changed files with 133 additions and 8 deletions
141
kitty/dnd.c
141
kitty/dnd.c
|
|
@ -866,7 +866,7 @@ drag_free_built_data(Window *w) {
|
|||
if (ds.items) {
|
||||
for (size_t i=0; i < ds.num_mimes; i++) {
|
||||
free(ds.items[i].optional_data);
|
||||
if (ds.items[i].fd_plus_one) safe_close(ds.items[i].fd_plus_one, __FILE__, __LINE__);
|
||||
if (ds.items[i].fd_plus_one > 0) safe_close(ds.items[i].fd_plus_one - 1, __FILE__, __LINE__);
|
||||
}
|
||||
free(ds.items);
|
||||
}
|
||||
|
|
@ -1049,7 +1049,19 @@ drag_start(Window *w) {
|
|||
if (err != 0) {
|
||||
abrt(err);
|
||||
} else {
|
||||
drag_free_built_data(w);
|
||||
// Free images and optional_data but keep the items array for later
|
||||
// data requests from the drop target
|
||||
for (size_t i = 0; i < ds.num_mimes; i++) {
|
||||
free(ds.items[i].optional_data);
|
||||
ds.items[i].optional_data = NULL;
|
||||
ds.items[i].data_size = 0;
|
||||
ds.items[i].data_capacity = 0;
|
||||
ds.items[i].data_decode_initialized = false;
|
||||
}
|
||||
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.state = DRAG_SOURCE_STARTED;
|
||||
drag_send_error(w, 0); // send OK
|
||||
}
|
||||
|
|
@ -1082,18 +1094,54 @@ drag_notify(Window *w, DragNotifyType type) {
|
|||
|
||||
int
|
||||
drag_free_data(Window *w, const char *mime_type, const char* data, size_t sz) {
|
||||
(void)w; (void)mime_type; (void)data; (void)sz;
|
||||
(void)w; (void)mime_type; (void)sz;
|
||||
free((void*)data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char*
|
||||
drag_get_data(Window *w, const char *mime_type, size_t *sz, int *err_code) {
|
||||
*err_code = ENOENT; *sz = 0;
|
||||
if (!ds.items) return NULL;
|
||||
for (size_t i = 0; i < ds.num_mimes; i++) {
|
||||
if (strcmp(ds.items[i].mime_type, mime_type) == 0) {
|
||||
if (ds.items[i].fd_plus_one < 0) {
|
||||
// Error was stored by drag_process_item_data
|
||||
*err_code = -ds.items[i].fd_plus_one;
|
||||
ds.items[i].fd_plus_one = 0;
|
||||
return NULL;
|
||||
}
|
||||
if (ds.items[i].fd_plus_one > 0) {
|
||||
// Data is available in the temp file, read it
|
||||
int fd = ds.items[i].fd_plus_one - 1;
|
||||
off_t end = lseek(fd, 0, SEEK_END);
|
||||
if (end < 0) { *err_code = EIO; return NULL; }
|
||||
if (lseek(fd, 0, SEEK_SET) < 0) { *err_code = EIO; return NULL; }
|
||||
char *data = malloc(end ? (size_t)end : 1);
|
||||
if (!data) { *err_code = ENOMEM; return NULL; }
|
||||
size_t total = 0;
|
||||
while (total < (size_t)end) {
|
||||
ssize_t n = read(fd, data + total, (size_t)end - total);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
free(data);
|
||||
*err_code = EIO;
|
||||
return NULL;
|
||||
}
|
||||
if (n == 0) break;
|
||||
total += (size_t)n;
|
||||
}
|
||||
// Close and reset the fd after reading
|
||||
safe_close(fd, __FILE__, __LINE__);
|
||||
ds.items[i].fd_plus_one = 0;
|
||||
*sz = total;
|
||||
*err_code = 0;
|
||||
return data;
|
||||
}
|
||||
// No fd yet, request data from the client
|
||||
char buf[128];
|
||||
size_t sz = snprintf(buf, sizeof(buf), "t=e:x=%d:y=%zu", DRAG_NOTIFY_FINISHED + 2, i);
|
||||
queue_payload_to_child(w->id, w->drag_source.client_id, &w->drag_source.pending, buf, sz, NULL, 0, false);
|
||||
int header_sz = snprintf(buf, sizeof(buf), "\x1b]%d;t=e:x=%d:y=%zu", DND_CODE, DRAG_NOTIFY_FINISHED + 2, i);
|
||||
queue_payload_to_child(w->id, w->drag_source.client_id, &w->drag_source.pending, buf, header_sz, NULL, 0, false);
|
||||
*err_code = EAGAIN;
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1101,11 +1149,88 @@ drag_get_data(Window *w, const char *mime_type, size_t *sz, int *err_code) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_errno_name(const uint8_t *data, size_t sz) {
|
||||
if (sz >= 6 && memcmp(data, "ENOENT", 6) == 0) return ENOENT;
|
||||
if (sz >= 5 && memcmp(data, "EPERM", 5) == 0) return EPERM;
|
||||
if (sz >= 6 && memcmp(data, "EINVAL", 6) == 0) return EINVAL;
|
||||
if (sz >= 6 && memcmp(data, "ENOMEM", 6) == 0) return ENOMEM;
|
||||
if (sz >= 5 && memcmp(data, "EFBIG", 5) == 0) return EFBIG;
|
||||
if (sz >= 3 && memcmp(data, "EIO", 3) == 0) return EIO;
|
||||
return EIO;
|
||||
}
|
||||
|
||||
static int
|
||||
open_item_tmpfile(void) {
|
||||
int fd = -1;
|
||||
#ifdef O_TMPFILE
|
||||
fd = safe_open("/tmp", O_TMPFILE | O_CLOEXEC | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
#endif
|
||||
if (fd < 0) {
|
||||
char name[] = "/tmp/kitty-dnd-XXXXXXXXXXXX";
|
||||
fd = safe_mkstemp(name);
|
||||
if (fd >= 0) unlink(name);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
void
|
||||
drag_process_item_data(Window *w, size_t idx, int has_more, const uint8_t *payload, size_t payload_sz) {
|
||||
(void)w; (void)idx; (void)has_more; (void)payload; (void)payload_sz;
|
||||
// call notify_drag_data_ready() after writing payload to file and
|
||||
// dont forget to check the return value of notify_drag_data_ready()
|
||||
if ((ds.state != DRAG_SOURCE_STARTED && ds.state != DRAG_SOURCE_DROPPED) || idx >= ds.num_mimes || !ds.items) return;
|
||||
|
||||
if (has_more < 0) {
|
||||
// Error from the client program
|
||||
if (ds.items[idx].fd_plus_one > 0) {
|
||||
safe_close(ds.items[idx].fd_plus_one - 1, __FILE__, __LINE__);
|
||||
}
|
||||
int err = parse_errno_name(payload, payload_sz);
|
||||
ds.items[idx].fd_plus_one = -err;
|
||||
ds.items[idx].data_decode_initialized = false;
|
||||
int ret = notify_drag_data_ready(global_state.drag_source.from_os_window, ds.items[idx].mime_type);
|
||||
if (ret) cancel_drag(w, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
// Open temp file if not yet open
|
||||
if (!ds.items[idx].fd_plus_one) {
|
||||
int fd = open_item_tmpfile();
|
||||
if (fd < 0) { cancel_drag(w, ENOMEM); return; }
|
||||
ds.items[idx].fd_plus_one = fd + 1;
|
||||
ds.items[idx].data_decode_initialized = true;
|
||||
base64_init_stream_decoder(&ds.items[idx].base64_state);
|
||||
}
|
||||
|
||||
// Decode and write payload data
|
||||
if (payload_sz > 0) {
|
||||
RAII_ALLOC(uint8_t, decoded, malloc(payload_sz));
|
||||
if (!decoded) { cancel_drag(w, ENOMEM); return; }
|
||||
size_t outlen = payload_sz;
|
||||
if (!base64_decode_stream(&ds.items[idx].base64_state, payload, payload_sz, decoded, &outlen)) {
|
||||
cancel_drag(w, EINVAL);
|
||||
return;
|
||||
}
|
||||
size_t written = 0;
|
||||
while (written < outlen) {
|
||||
ssize_t n = write(ds.items[idx].fd_plus_one - 1, decoded + written, outlen - written);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
cancel_drag(w, EIO);
|
||||
return;
|
||||
}
|
||||
written += (size_t)n;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_more == 0) {
|
||||
// All data received, seek to beginning and notify
|
||||
if (lseek(ds.items[idx].fd_plus_one - 1, 0, SEEK_SET) < 0) {
|
||||
cancel_drag(w, EIO);
|
||||
return;
|
||||
}
|
||||
ds.items[idx].data_decode_initialized = false;
|
||||
int ret = notify_drag_data_ready(global_state.drag_source.from_os_window, ds.items[idx].mime_type);
|
||||
if (ret) cancel_drag(w, ret);
|
||||
}
|
||||
}
|
||||
#undef img
|
||||
#undef abrt
|
||||
|
|
|
|||
Loading…
Reference in a new issue