mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-05-13 16:37:27 +00:00
More work on DnD protocol implementation
This commit is contained in:
parent
9666cf83ad
commit
ce041fab84
6 changed files with 106 additions and 16 deletions
|
|
@ -259,6 +259,7 @@ image by sending::
|
|||
OSC _dnd_code ; t=P:x=idx ST
|
||||
|
||||
Where ``idx`` is now a zero based index with zero being the first image and so on.
|
||||
Sending an ``idx`` out of bounds means the drag image should be removed.
|
||||
|
||||
Once the terminal program has sent all data and images for the drag
|
||||
operation, it indicates the drag should be started by sending ``t=P:x=-1``. At
|
||||
|
|
|
|||
82
kitty/dnd.c
82
kitty/dnd.c
|
|
@ -10,6 +10,7 @@
|
|||
#include "control-codes.h"
|
||||
#include "safe-wrappers.h"
|
||||
#include "iqsort.h"
|
||||
#include "png-reader.h"
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
|
@ -865,7 +866,7 @@ void
|
|||
drag_free_offer(Window *w) {
|
||||
free(ds.mimes_buf); ds.mimes_buf = NULL;
|
||||
ds.allowed_operations = 0;
|
||||
ds.offer_being_built = false;
|
||||
ds.state = DRAG_SOURCE_NONE;
|
||||
if (ds.items) {
|
||||
for (size_t i=0; i < ds.num_mimes; i++) free(ds.items[i].optional_data);
|
||||
free(ds.items);
|
||||
|
|
@ -879,13 +880,20 @@ drag_free_offer(Window *w) {
|
|||
ds.images_sent_total_sz = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cancel_drag(Window *w, int error_code) {
|
||||
if (error_code) drop_send_error(w, error_code);
|
||||
if (global_state.drag_source.is_active && global_state.drag_source.from_window == w->id) cancel_current_drag_source();
|
||||
drag_free_offer(w);
|
||||
}
|
||||
|
||||
void
|
||||
drag_add_mimes(Window *w, int allowed_operations, const char *data, size_t sz, bool has_more) {
|
||||
#define abrt(code) { drop_send_error(w, code); drag_free_offer(w); return; }
|
||||
if (allowed_operations && ds.offer_being_built) drag_free_offer(w);
|
||||
#define abrt(code) { cancel_drag(w, code); return; }
|
||||
if (allowed_operations && ds.state != DRAG_SOURCE_NONE) cancel_drag(w, 0);
|
||||
if (allowed_operations && !ds.allowed_operations) ds.allowed_operations = allowed_operations;
|
||||
if (!ds.allowed_operations) { abrt(EINVAL); }
|
||||
ds.offer_being_built = true;
|
||||
ds.state = DRAG_SOURCE_BEING_BUILT;
|
||||
size_t new_sz = ds.bufsz + sz;
|
||||
if (new_sz > MIME_LIST_SIZE_CAP) abrt(EFBIG);
|
||||
ds.mimes_buf = realloc(ds.mimes_buf, ds.bufsz + sz + 1);
|
||||
|
|
@ -917,7 +925,7 @@ 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 (ds.state != DRAG_SOURCE_BEING_BUILT || idx >= ds.num_mimes) abrt(EINVAL);
|
||||
if (sz + ds.pre_sent_total_sz > PRESENT_DATA_CAP) abrt(EFBIG);
|
||||
ds.pre_sent_total_sz += sz;
|
||||
#define item ds.items[idx]
|
||||
|
|
@ -937,12 +945,14 @@ drag_add_pre_sent_data(Window *w, unsigned idx, const uint8_t *payload, size_t s
|
|||
#undef item
|
||||
}
|
||||
|
||||
#define img ds.images[idx]
|
||||
|
||||
void
|
||||
drag_add_image(Window *w, unsigned idx, int fmt, int width, int height, const uint8_t *payload, size_t sz) {
|
||||
if (ds.state != DRAG_SOURCE_BEING_BUILT) abrt(EINVAL);
|
||||
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);
|
||||
|
|
@ -960,8 +970,66 @@ drag_add_image(Window *w, unsigned idx, int fmt, int width, int height, const ui
|
|||
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
|
||||
}
|
||||
|
||||
void
|
||||
drag_change_image(Window *w, unsigned idx) {
|
||||
ds.img_idx = idx;
|
||||
if (ds.state == DRAG_SOURCE_STARTED) change_drag_image(idx);
|
||||
}
|
||||
|
||||
static bool
|
||||
expand_rgb_data(Window *w, size_t idx) {
|
||||
#define fail(code) { cancel_drag(w, code); return false; }
|
||||
if (img.sz != (size_t)img.width * (size_t)img.height * 3) fail(EINVAL);
|
||||
const size_t sz = img.width * img.height * 4;
|
||||
RAII_ALLOC(uint8_t, expanded, malloc(sz));
|
||||
if (!expanded) fail(ENOMEM);
|
||||
memset(expanded, 0xff, sz);
|
||||
for (int r = 0; r < img.height; r++) {
|
||||
uint8_t *src_row = img.data + r * img.width * 3, *dest_row = expanded + r * img.width * 4;
|
||||
for (int c = 0; c < img.width; c++) memcpy(dest_row + c * 4, src_row + c * 3, 3);
|
||||
}
|
||||
SWAP(img.data, expanded); img.sz = sz; img.fmt = 32;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
expand_png_data(Window *w, size_t idx) {
|
||||
png_read_data d = {0};
|
||||
inflate_png_inner(&d, img.data, img.sz, 2000);
|
||||
if (d.ok) {
|
||||
free(img.data);
|
||||
img.data = d.decompressed;
|
||||
img.sz = d.sz;
|
||||
img.width = d.width; img.height = d.height;
|
||||
} else free(d.decompressed);
|
||||
free(d.row_pointers);
|
||||
return d.ok;
|
||||
}
|
||||
#undef fail
|
||||
|
||||
void
|
||||
drag_start(Window *w) {
|
||||
if (ds.state != DRAG_SOURCE_BEING_BUILT) abrt(EINVAL);
|
||||
size_t total_size = 0;
|
||||
for (size_t idx = 0; idx < arraysz(ds.images); idx++) {
|
||||
if (img.sz) {
|
||||
switch (img.fmt) {
|
||||
case 24:
|
||||
if (!expand_rgb_data(w, idx)) return;
|
||||
break;
|
||||
case 100:
|
||||
if (!expand_png_data(w, idx)) return;
|
||||
break;
|
||||
}
|
||||
total_size += img.sz;
|
||||
if (total_size > 2 * PRESENT_DATA_CAP) abrt(EFBIG);
|
||||
if (img.sz != (size_t)img.width * (size_t)img.height * 4u) abrt(EINVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef img
|
||||
#undef abrt
|
||||
#undef ds
|
||||
|
|
|
|||
|
|
@ -27,3 +27,5 @@ 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);
|
||||
void drag_change_image(Window *w, unsigned idx);
|
||||
void drag_start(Window *w);
|
||||
|
|
|
|||
15
kitty/glfw.c
15
kitty/glfw.c
|
|
@ -3110,6 +3110,21 @@ get_thumbnail(PyObject *thumbnails, GLFWimage *thumbnail, int idx) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
change_drag_image(int idx) {
|
||||
if (!global_state.drag_source.from_os_window) return false;
|
||||
OSWindow *w = os_window_for_id(global_state.drag_source.from_os_window);
|
||||
if (!w || !w->handle) { errno = EINVAL; return false; }
|
||||
if (global_state.drag_source.thumbnail_idx == idx) return true;
|
||||
GLFWimage thumbnail = {0};
|
||||
if (idx >=0 && global_state.drag_source.thumbnails && idx < PySequence_Size(global_state.drag_source.thumbnails)) {
|
||||
if (!get_thumbnail(global_state.drag_source.thumbnails, &thumbnail, idx)) { errno = ENOMEM; PyErr_Clear(); return NULL; }
|
||||
global_state.drag_source.thumbnail_idx = idx;
|
||||
} else global_state.drag_source.thumbnail_idx = -1;
|
||||
errno = glfwStartDrag(w->handle, NULL, 0, thumbnail.pixels ? &thumbnail : NULL, -2, false);
|
||||
return errno == 0;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
change_drag_thumbnail(PyObject *self UNUSED, PyObject *args) {
|
||||
unsigned long long os_window_id; int idx = -1;
|
||||
|
|
|
|||
|
|
@ -1552,6 +1552,11 @@ screen_handle_dnd_command(Screen *self, const DnDCommand *cmd, const uint8_t *pa
|
|||
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;
|
||||
case 'P': {
|
||||
if (cmd->cell_x >= 0) drag_change_image(w, cmd->cell_x);
|
||||
else drag_start(w);
|
||||
} break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -234,13 +234,7 @@ typedef struct DirHandle {
|
|||
uint32_t id; /* handle id, 1-based; 0 = invalid */
|
||||
} DirHandle;
|
||||
|
||||
typedef struct DragSourceItem {
|
||||
const char *mime_type;
|
||||
uint8_t *optional_data;
|
||||
size_t data_size, data_capacity;
|
||||
base64_state base64_state;
|
||||
bool data_decode_initialized;
|
||||
} DragSourceItem;
|
||||
typedef enum { DRAG_SOURCE_NONE, DRAG_SOURCE_BEING_BUILT, DRAG_SOURCE_STARTED, DRAG_SOURCE_DROPPED } DragSourceState;
|
||||
|
||||
typedef struct Window {
|
||||
id_type id;
|
||||
|
|
@ -298,13 +292,17 @@ typedef struct Window {
|
|||
bool can_offer;
|
||||
struct { double x, y; monotonic_t at; } initial_left_press;
|
||||
char *mimes_buf; size_t num_mimes, bufsz;
|
||||
DragSourceItem *items;
|
||||
struct {
|
||||
const char *mime_type; uint8_t *optional_data; size_t data_size, data_capacity; base64_state base64_state;
|
||||
bool data_decode_initialized;
|
||||
} *items;
|
||||
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;
|
||||
unsigned img_idx;
|
||||
int allowed_operations;
|
||||
bool offer_being_built;
|
||||
DragSourceState state;
|
||||
} drag_source;
|
||||
} Window;
|
||||
|
||||
|
|
@ -595,3 +593,4 @@ void request_drop_status_update(OSWindow *osw);
|
|||
void register_mimes_for_drop(OSWindow *w, const char **mimes, size_t sz);
|
||||
void request_drop_data(OSWindow *w, id_type wid, const char* mime);
|
||||
void cancel_current_drag_source(void);
|
||||
bool change_drag_image(int idx);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue