mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 14:18:26 +02:00
More work on DnD protocol implementation
This commit is contained in:
@@ -242,19 +242,20 @@ the ``t=o`` key indicating the offer if the data is available.
|
||||
|
||||
To associate one or more images with the drag operation, the terminal program
|
||||
must transmit the data for the image with the ``idx`` value above being a
|
||||
negative number starting with ``-1`` for the first image and so on. When
|
||||
transmitting images, the image data format is specified using the ``y`` key.
|
||||
A value of ``y=24`` mean 24bit RGB data and ``y=32`` means 32bit RGBA data.
|
||||
Colors in the RGB/A data must be in the sRGB color space.
|
||||
Using ``y=100`` means the data is a PNG image. Additionally, the ``X`` and
|
||||
``Y`` keys must be used to specify the width and height of the image data in
|
||||
pixels. If the size of the transmitted data does not match the image dimensions
|
||||
the terminal must replay with ``t=R ; EINVAL``. Terminals are free to impose a
|
||||
limit on the amount of image data, too avoid Denial-of-service attacks. If the
|
||||
image data is too much or the image is too large they must reply with ``t=R ;
|
||||
EFBIG`` and abort the drag. By default, the drag will be started using the
|
||||
first image, if any. During the drag, the terminal program can change the
|
||||
image by sending::
|
||||
negative number starting with ``-1`` for the first image and so on. Clients
|
||||
**must** transmit all images consecutively in order, starting with the frist,
|
||||
then the second and so on. When transmitting images, the image data format is
|
||||
specified using the ``y`` key. A value of ``y=24`` mean 24bit RGB data and
|
||||
``y=32`` means 32bit RGBA data. Colors in the RGB/A data must be in the sRGB
|
||||
color space. Using ``y=100`` means the data is a PNG image. Additionally, the
|
||||
``X`` and ``Y`` keys must be used to specify the width and height of the image
|
||||
data in pixels. If the size of the transmitted data does not match the image
|
||||
dimensions the terminal must replay with ``t=R ; EINVAL``. Terminals are free
|
||||
to impose a limit on the amount of image data, too avoid Denial-of-service
|
||||
attacks. If the image data is too much or the image is too large they must
|
||||
reply with ``t=R ; EFBIG`` and abort the drag. By default, the drag will be
|
||||
started using the first image, if any. During the drag, the terminal program
|
||||
can change the image by sending::
|
||||
|
||||
OSC _dnd_code ; t=P:x=idx ST
|
||||
|
||||
|
||||
22
kitty/dnd.c
22
kitty/dnd.c
@@ -862,11 +862,8 @@ drop_left_child(Window *w) {
|
||||
|
||||
#define ds w->drag_source
|
||||
|
||||
void
|
||||
drag_free_offer(Window *w) {
|
||||
free(ds.mimes_buf); ds.mimes_buf = NULL;
|
||||
ds.allowed_operations = 0;
|
||||
ds.state = DRAG_SOURCE_NONE;
|
||||
static void
|
||||
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);
|
||||
free(ds.items);
|
||||
@@ -875,6 +872,14 @@ drag_free_offer(Window *w) {
|
||||
if (ds.images[i].data) free(ds.images[i].data);
|
||||
zero_at_ptr(ds.images + i);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
drag_free_offer(Window *w) {
|
||||
free(ds.mimes_buf); ds.mimes_buf = NULL;
|
||||
drag_free_built_data(w);
|
||||
ds.allowed_operations = 0;
|
||||
ds.state = DRAG_SOURCE_NONE;
|
||||
ds.num_mimes = 0;
|
||||
ds.pre_sent_total_sz = 0;
|
||||
ds.images_sent_total_sz = 0;
|
||||
@@ -1028,6 +1033,13 @@ drag_start(Window *w) {
|
||||
if (img.sz != (size_t)img.width * (size_t)img.height * 4u) abrt(EINVAL);
|
||||
}
|
||||
}
|
||||
int err = start_window_drag(w);
|
||||
if (err != 0) {
|
||||
abrt(err);
|
||||
} else {
|
||||
drag_free_built_data(w);
|
||||
ds.state = DRAG_SOURCE_STARTED;
|
||||
}
|
||||
}
|
||||
|
||||
#undef img
|
||||
|
||||
39
kitty/glfw.c
39
kitty/glfw.c
@@ -3147,6 +3147,44 @@ change_drag_thumbnail(PyObject *self UNUSED, PyObject *args) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
start_window_drag(Window *w) {
|
||||
OSWindow *osw = os_window_for_kitty_window(w->id);
|
||||
if (!osw || !osw->handle) return EINVAL;
|
||||
RAII_ALLOC(GLFWDragSourceItem, items, calloc(w->drag_source.num_mimes, sizeof(GLFWDragSourceItem)));
|
||||
if (!items) return ENOMEM;
|
||||
for (size_t i = 0; i < w->drag_source.num_mimes; i++) {
|
||||
items[i].mime_type = w->drag_source.items[i].mime_type;
|
||||
items[i].optional_data = (char*)w->drag_source.items[i].optional_data;
|
||||
items[i].data_size = w->drag_source.items[i].data_size;
|
||||
}
|
||||
size_t num_images = 0;
|
||||
for (size_t i = 0; i < arraysz(w->drag_source.images); i++) if (w->drag_source.images[i].data) num_images++;
|
||||
RAII_PyObject(images, PyTuple_New(num_images));
|
||||
if (!images) { PyErr_Clear(); return ENOMEM; }
|
||||
for (size_t i = 0, n = 0; i < arraysz(w->drag_source.images); i++) {
|
||||
if (w->drag_source.images[i].data) {
|
||||
PyObject *t = Py_BuildValue(
|
||||
"y#ii", w->drag_source.images[i].data, w->drag_source.images[i].sz, w->drag_source.images[i].width, w->drag_source.images[i].height);
|
||||
if (!t) { PyErr_Clear(); return ENOMEM; }
|
||||
PyTuple_SET_ITEM(images, n, t); n++;
|
||||
}
|
||||
}
|
||||
GLFWimage thumbnail = {0};
|
||||
if (w->drag_source.img_idx < num_images && !get_thumbnail(images, &thumbnail, w->drag_source.img_idx)) return ENOMEM;
|
||||
free_drag_source();
|
||||
global_state.drag_source.thumbnails = Py_NewRef(images);
|
||||
global_state.drag_source.is_active = true;
|
||||
global_state.drag_source.needs_toplevel_on_wayland = true;
|
||||
global_state.drag_source.from_window = w->id;
|
||||
global_state.drag_source.from_os_window = osw->id;
|
||||
global_state.drag_source.thumbnail_idx = w->drag_source.img_idx < num_images ? (int)w->drag_source.img_idx : -1;
|
||||
int ret = glfwStartDrag(osw->handle, items, w->drag_source.num_mimes, thumbnail.pixels ? &thumbnail : NULL, w->drag_source.allowed_operations, true);
|
||||
if (ret != 0) free_drag_source();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
start_drag_with_data(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
||||
static const char* kwlist[] = {"os_window_id", "data_map", "thumbnails", "operations", NULL};
|
||||
@@ -3185,6 +3223,7 @@ start_drag_with_data(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
||||
errno = glfwStartDrag(w->handle, items, num, thumbnail.pixels ? &thumbnail : NULL, operations, needs_toplevel_on_wayland);
|
||||
if (errno != 0) {
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
free_drag_source();
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
|
||||
@@ -594,3 +594,4 @@ 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);
|
||||
int start_window_drag(Window *w);
|
||||
|
||||
Reference in New Issue
Block a user