mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-10-22 13:12:23 +00:00
More work on DnD protocol
This commit is contained in:
parent
390ae2efac
commit
348151c457
6 changed files with 122 additions and 18 deletions
|
|
@ -271,6 +271,40 @@ 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``.
|
||||
|
||||
As the drag progresses, status changes are reported using the ``t=e`` escape
|
||||
code. The variants are listed in the table below:
|
||||
|
||||
.. list-table:: Drag offer events
|
||||
|
||||
* - Code
|
||||
- Description
|
||||
* - ``t=e : x=1 : y=idx``
|
||||
- The drag has been accepted by a client. ``idx`` is a zero based index into the list of MIME types
|
||||
pointing to the MIME type the client is likely to want
|
||||
* - ``t=e : x=2 : o=O``
|
||||
- The action the client is likely to perform has changed to the value indicated by the ``o`` key
|
||||
* - ``t=e : x=3``
|
||||
- The drag offer has been dropped onto a client, there are likely to be requests for data in the near future
|
||||
* - ``t=e : x=4 : y=0 or 1``
|
||||
- The drag is finished. If ``y=1`` then the drag was canceled by the user.
|
||||
* - ``t=e : x=5 : y=idx``
|
||||
- Request data for the MIME type at the zero based index ``idx`` in the list of MIME types
|
||||
|
||||
The client program should respond to data requests with escape codes of the
|
||||
form::
|
||||
|
||||
OSC _dnd_code ; t=e:m=0 or 1 ; base64 encoded data ST
|
||||
|
||||
This, is the data for requested MIME type. The data should be chunkedusing the
|
||||
``m`` key. End of data is denoted by ``m=0`` and an rmpty payload. If an error
|
||||
occurs the client should send::
|
||||
|
||||
OSC _dnd_code ; t=E; ERR_CODE ST
|
||||
|
||||
Where ``ERR_CODE`` is a POSIX error code such as ``ENOENT`` if the MIME type is
|
||||
not found or ``EIO`` if an IO error occurred and so on.
|
||||
|
||||
|
||||
Multiplexers
|
||||
-----------------
|
||||
|
||||
|
|
@ -302,6 +336,7 @@ Key Value Default Description
|
|||
``O`` - stop offering drags
|
||||
``p`` - present data for drag offers
|
||||
``P`` - Change drag image or start drag
|
||||
``e`` - a drag offer event occurred
|
||||
|
||||
``m`` Chunking indicator ``0`` ``0`` or ``i``
|
||||
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ def parsers() -> None:
|
|||
write_header(text, 'kitty/parse-multicell-command.h')
|
||||
|
||||
keymap = {
|
||||
't': ('type', flag('aAmMrRsdoOpP')),
|
||||
't': ('type', flag('aAmMrRsdoOpPeE')),
|
||||
'm': ('more', 'uint'),
|
||||
'i': ('client_id', 'uint'),
|
||||
'o': ('operation', 'uint'),
|
||||
|
|
|
|||
46
kitty/dnd.c
46
kitty/dnd.c
|
|
@ -1052,6 +1052,52 @@ drag_start(Window *w) {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
drag_notify(Window *w, DragNotifyType type) {
|
||||
char buf[128];
|
||||
size_t sz = snprintf(buf, sizeof(buf), "t=e:x=%d", type + 1);
|
||||
switch(type) {
|
||||
case DRAG_NOTIFY_ACCEPTED:
|
||||
for (size_t i = 0; i < ds.num_mimes; i++) {
|
||||
if (strcmp(ds.items[i].mime_type, global_state.drag_source.accepted_mime_type) == 0) {
|
||||
sz += snprintf(buf + sz, sizeof(buf) - sz, "y=%zu", i); break;
|
||||
}
|
||||
}
|
||||
case DRAG_NOTIFY_ACTION_CHANGED:
|
||||
switch (global_state.drag_source.action) {
|
||||
case GLFW_DRAG_OPERATION_MOVE:
|
||||
sz += snprintf(buf + sz, sizeof(buf) - sz, "o=2"); break;
|
||||
default:
|
||||
sz += snprintf(buf + sz, sizeof(buf) - sz, "o=1"); break;
|
||||
}
|
||||
case DRAG_NOTIFY_DROPPED: break;
|
||||
case DRAG_NOTIFY_FINISHED:
|
||||
sz += snprintf(buf + sz, sizeof(buf) - sz, "y=%d", global_state.drag_source.was_canceled ? 1 : 0); break;
|
||||
}
|
||||
queue_payload_to_child(w->id, w->drag_source.client_id, &w->drag_source.pending, buf, sz, NULL, 0, false);
|
||||
}
|
||||
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char*
|
||||
drag_get_data(Window *w, const char *mime_type, size_t *sz, int *err_code) {
|
||||
*err_code = ENOENT; *sz = 0;
|
||||
for (size_t i = 0; i < ds.num_mimes; i++) {
|
||||
if (strcmp(ds.items[i].mime_type, mime_type) == 0) {
|
||||
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);
|
||||
*err_code = EAGAIN;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#undef img
|
||||
#undef abrt
|
||||
#undef ds
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "state.h"
|
||||
|
||||
|
||||
void drop_register_window(Window *w, const uint8_t *payload, size_t payload_sz, bool on, uint32_t client_id, bool more);
|
||||
void drop_move_on_child(Window *w, const char **mimes, size_t num_mimes, bool is_drop);
|
||||
void drop_left_child(Window *w);
|
||||
|
|
@ -23,9 +24,13 @@ void drop_finish(Window *w);
|
|||
void dnd_set_test_write_func(PyObject *func);
|
||||
|
||||
|
||||
typedef enum { DRAG_NOTIFY_ACCEPTED, DRAG_NOTIFY_ACTION_CHANGED, DRAG_NOTIFY_DROPPED, DRAG_NOTIFY_FINISHED } DragNotifyType;
|
||||
void drag_free_offer(Window *w);
|
||||
void drag_add_mimes(Window *w, int allowed_operations, uint32_t client_id, 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);
|
||||
void drag_notify(Window *w, DragNotifyType type);
|
||||
int drag_free_data(Window *w, const char *mime_type, const char* data, size_t sz);
|
||||
const char* drag_get_data(Window *w, const char *mime_type, size_t *sz, int *err_code);
|
||||
|
|
|
|||
45
kitty/glfw.c
45
kitty/glfw.c
|
|
@ -1084,42 +1084,59 @@ cancel_current_drag_source(void) {
|
|||
|
||||
static void
|
||||
drag_source_callback(GLFWwindow *window UNUSED, GLFWDragEvent *ev) {
|
||||
#define finish \
|
||||
#define finish { \
|
||||
call_boss(on_drag_source_finished, "OOsiOO", \
|
||||
ds.was_dropped && !global_state.drop_dest.os_window_id ? Py_True : Py_False, ds.was_canceled ? Py_True: Py_False, \
|
||||
ds.accepted_mime_type ? ds.accepted_mime_type : "", \
|
||||
ds.action, ds.drag_data ? ds.drag_data : Py_None, ds.needs_toplevel_on_wayland ? Py_True : Py_False); \
|
||||
free_drag_source();
|
||||
ds.was_dropped && !global_state.drop_dest.os_window_id ? Py_True : Py_False, ds.was_canceled ? Py_True: Py_False, \
|
||||
ds.accepted_mime_type ? ds.accepted_mime_type : "", \
|
||||
ds.action, ds.drag_data ? ds.drag_data : Py_None, ds.needs_toplevel_on_wayland ? Py_True : Py_False); \
|
||||
free_drag_source(); \
|
||||
}
|
||||
Window *w = NULL;
|
||||
bool is_client_drag = false;
|
||||
if (ds.from_window && (w = window_for_window_id(ds.from_window)) && w->drag_source.state) {
|
||||
is_client_drag = true;
|
||||
}
|
||||
|
||||
switch (ev->type) {
|
||||
case GLFW_DRAG_DATA_REQUEST: // we currently pre-provide all data so this should never happen
|
||||
if (ev->data_sz) {
|
||||
// previously returned data is consumed, free it
|
||||
} else {
|
||||
ev->err_num = ENOENT;
|
||||
case GLFW_DRAG_DATA_REQUEST:
|
||||
ev->err_num = ENOENT;
|
||||
if (is_client_drag) {
|
||||
ev->err_num = 0;
|
||||
if (ev->data_sz) {
|
||||
ev->err_num = drag_free_data(w, ev->mime_type, ev->data, ev->data_sz);
|
||||
} else {
|
||||
ev->data = drag_get_data(w, ev->mime_type, &ev->data_sz, &ev->err_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GLFW_DRAG_ACCEPTED:
|
||||
free(ds.accepted_mime_type);
|
||||
ds.accepted_mime_type = ev->mime_type ? strdup(ev->mime_type) : NULL;
|
||||
if (is_client_drag) drag_notify(w, DRAG_NOTIFY_ACCEPTED);
|
||||
break;
|
||||
case GLFW_DRAG_ACTION_CHANGED:
|
||||
ds.action = ev->action; break;
|
||||
if (is_client_drag) drag_notify(w, DRAG_NOTIFY_ACTION_CHANGED);
|
||||
case GLFW_DRAG_DROPPED:
|
||||
ds.was_dropped = true;
|
||||
// On Wayland, we cant trust the value of ev->action as it is zero
|
||||
// when dropping outside any application which is a case we want to
|
||||
// handle.
|
||||
if (global_state.drop_dest.os_window_id && ds.drag_data) {
|
||||
Py_CLEAR(global_state.drop_dest.self_drag_data);
|
||||
global_state.drop_dest.self_drag_data = Py_NewRef(ds.drag_data);
|
||||
if (is_client_drag) {
|
||||
drag_notify(w, DRAG_NOTIFY_DROPPED);
|
||||
} else {
|
||||
if (global_state.drop_dest.os_window_id && ds.drag_data) {
|
||||
Py_CLEAR(global_state.drop_dest.self_drag_data);
|
||||
global_state.drop_dest.self_drag_data = Py_NewRef(ds.drag_data);
|
||||
}
|
||||
finish;
|
||||
}
|
||||
finish;
|
||||
break;
|
||||
case GLFW_DRAG_CANCELLED:
|
||||
ds.was_canceled = true;
|
||||
/* fallthrough */
|
||||
case GLFW_DRAG_FINSHED:
|
||||
if (is_client_drag) drag_notify(w, DRAG_NOTIFY_FINISHED);
|
||||
finish
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,9 +86,10 @@ static inline void parse_dnd_code(PS *self, uint8_t *parser_buf,
|
|||
|
||||
case type: {
|
||||
g.type = parser_buf[pos++];
|
||||
if (g.type != 'A' && g.type != 'M' && g.type != 'O' && g.type != 'P' &&
|
||||
g.type != 'R' && g.type != 'a' && g.type != 'd' && g.type != 'm' &&
|
||||
g.type != 'o' && g.type != 'p' && g.type != 'r' && g.type != 's') {
|
||||
if (g.type != 'A' && g.type != 'E' && g.type != 'M' && g.type != 'O' &&
|
||||
g.type != 'P' && g.type != 'R' && g.type != 'a' && g.type != 'd' &&
|
||||
g.type != 'e' && g.type != 'm' && g.type != 'o' && g.type != 'p' &&
|
||||
g.type != 'r' && g.type != 's') {
|
||||
REPORT_ERROR("Malformed DnDCommand control block, unknown flag value "
|
||||
"for type: 0x%x",
|
||||
g.type);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue