mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-27 10:41:58 +02:00
Send dropped message
This commit is contained in:
@@ -66,7 +66,8 @@ only if the list changes.
|
|||||||
When the drag leaves the window, the terminal will send the same event but
|
When the drag leaves the window, the terminal will send the same event but
|
||||||
with ``x, y = -1, -1`` to indicate that the drag has left the window. For such
|
with ``x, y = -1, -1`` to indicate that the drag has left the window. For such
|
||||||
events the list of MIME types must be empty. Note that the terminal must never
|
events the list of MIME types must be empty. Note that the terminal must never
|
||||||
send negative cell co-ordinates for any other reason.
|
send negative cell co-ordinates for any other reason. No more movement escape
|
||||||
|
codes ``t=m`` will be sent until this drop or another re-enters the window.
|
||||||
|
|
||||||
The client program must inform the terminal whether it will accept
|
The client program must inform the terminal whether it will accept
|
||||||
the potential drop and which MIME types of the set of offered MIME types it
|
the potential drop and which MIME types of the set of offered MIME types it
|
||||||
@@ -81,9 +82,23 @@ occurs which can be either ``1`` for copy or ``2`` for move or ``0`` for not
|
|||||||
accepted. The MIME list is the ordered list of MIME types from the offered list
|
accepted. The MIME list is the ordered list of MIME types from the offered list
|
||||||
that the client wants. If no MIME type list is present, it is equivalent to no
|
that the client wants. If no MIME type list is present, it is equivalent to no
|
||||||
change in the offered list of MIME types. The list should be ordered in order
|
change in the offered list of MIME types. The list should be ordered in order
|
||||||
of decreasing preference. Some platforms may assume show the user some
|
of decreasing preference. Some platforms may show the user some
|
||||||
indication of the first MIME type in the list.
|
indication of the first MIME type in the list.
|
||||||
|
|
||||||
|
When the user triggers a drop on the window, the terminal will send an escape
|
||||||
|
code of the form::
|
||||||
|
|
||||||
|
OSC _dnd_code ; t=M: ... ; MIME list ST
|
||||||
|
|
||||||
|
This is the same as the movement escape codes above, except that ``t=M``
|
||||||
|
(upper case M instead of lower case m), indicating this is a drop.
|
||||||
|
Once this escape code is received, no more movement escape codes ``t=m``
|
||||||
|
will be sent until a new drop enters the window. The MIME list here is
|
||||||
|
mandatory, terminals must send the full list of MIME types available in
|
||||||
|
the drop. The client program can now request data for the MIME types
|
||||||
|
it is interested in.
|
||||||
|
|
||||||
|
|
||||||
Metadata reference
|
Metadata reference
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
@@ -97,6 +112,7 @@ Key Value Default Description
|
|||||||
``(a, A, ``a`` - start accepting drops
|
``(a, A, ``a`` - start accepting drops
|
||||||
)`` ``A`` - stop accepting drops
|
)`` ``A`` - stop accepting drops
|
||||||
``m`` - a drop move event
|
``m`` - a drop move event
|
||||||
|
``M`` - a drop dropped event
|
||||||
|
|
||||||
``m`` Chunking indicator ``0`` ``0`` or ``i``
|
``m`` Chunking indicator ``0`` ``0`` or ``i``
|
||||||
|
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ def parsers() -> None:
|
|||||||
write_header(text, 'kitty/parse-multicell-command.h')
|
write_header(text, 'kitty/parse-multicell-command.h')
|
||||||
|
|
||||||
keymap = {
|
keymap = {
|
||||||
't': ('type', flag('aA')),
|
't': ('type', flag('aAmM')),
|
||||||
'm': ('more', 'uint'),
|
'm': ('more', 'uint'),
|
||||||
'i': ('client_id', 'uint'),
|
'i': ('client_id', 'uint'),
|
||||||
'o': ('operation', 'uint'),
|
'o': ('operation', 'uint'),
|
||||||
|
|||||||
40
kitty/dnd.c
40
kitty/dnd.c
@@ -41,6 +41,17 @@ drop_free_data(Window *w) {
|
|||||||
free_pending(&w->drop.pending);
|
free_pending(&w->drop.pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
reset_drop(Window *w) {
|
||||||
|
bool wanted = w->drop.wanted; uint32_t cid = w->drop.client_id;
|
||||||
|
drop_free_data(w);
|
||||||
|
zero_at_ptr(&w->drop);
|
||||||
|
if (wanted) {
|
||||||
|
w->drop.wanted = wanted;
|
||||||
|
w->drop.client_id = cid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
string_arrays_cmp(const char **a, size_t an, const char **b, size_t bn) {
|
string_arrays_cmp(const char **a, size_t an, const char **b, size_t bn) {
|
||||||
if (an != bn) return (int)an - (int)bn;
|
if (an != bn) return (int)an - (int)bn;
|
||||||
@@ -102,6 +113,18 @@ flush_pending(id_type id, PendingData *pending) {
|
|||||||
return pending->count > 0;
|
return pending->count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define check_for_pending_writes() \
|
||||||
|
add_main_loop_timer(ms_to_monotonic_t(20), false, flush_pending_payloads, (void*)(uintptr_t)id, NULL)
|
||||||
|
|
||||||
|
static void
|
||||||
|
flush_pending_payloads(id_type timer_id UNUSED, void *x) {
|
||||||
|
id_type id = (uintptr_t)x;
|
||||||
|
Window *w = window_for_window_id(id);
|
||||||
|
if (w && w->drop.wanted) {
|
||||||
|
if (!flush_pending(w->id, &w->drop.pending)) check_for_pending_writes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
queue_payload_to_child(id_type id, PendingData *pending, const char *header, size_t header_sz, const char *data, size_t data_sz) {
|
queue_payload_to_child(id_type id, PendingData *pending, const char *header, size_t header_sz, const char *data, size_t data_sz) {
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
@@ -114,14 +137,16 @@ queue_payload_to_child(id_type id, PendingData *pending, const char *header, siz
|
|||||||
PendingEntry *e = &pending->items[pending->count++];
|
PendingEntry *e = &pending->items[pending->count++];
|
||||||
e->buf = buf; e->header_sz = header_sz; e->data_sz = data_sz - offset;
|
e->buf = buf; e->header_sz = header_sz; e->data_sz = data_sz - offset;
|
||||||
}
|
}
|
||||||
|
if (pending->count) check_for_pending_writes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
drop_move_on_child(Window *w, const char** mimes, size_t num_mimes) {
|
drop_move_on_child(Window *w, const char** mimes, size_t num_mimes, bool is_drop) {
|
||||||
if (!w->drop.hovered) {
|
if (!w->drop.hovered) {
|
||||||
drop_free_offered_mimes(w);
|
reset_drop(w);
|
||||||
w->drop.hovered = true;
|
w->drop.hovered = true;
|
||||||
}
|
}
|
||||||
|
if (is_drop) { w->drop.dropped = true; w->drop.hovered = false; }
|
||||||
size_t mimes_total_size = 0;
|
size_t mimes_total_size = 0;
|
||||||
if (mimes && (w->drop.offerred_mimes == NULL || string_arrays_cmp(mimes, num_mimes, w->drop.offerred_mimes, w->drop.num_offerred_mimes) != 0)) {
|
if (mimes && (w->drop.offerred_mimes == NULL || string_arrays_cmp(mimes, num_mimes, w->drop.offerred_mimes, w->drop.num_offerred_mimes) != 0)) {
|
||||||
drop_free_offered_mimes(w);
|
drop_free_offered_mimes(w);
|
||||||
@@ -139,10 +164,11 @@ drop_move_on_child(Window *w, const char** mimes, size_t num_mimes) {
|
|||||||
w->drop.num_offerred_mimes = num_mimes;
|
w->drop.num_offerred_mimes = num_mimes;
|
||||||
}
|
}
|
||||||
// we simply drop this event if there is too much data being written to the child
|
// we simply drop this event if there is too much data being written to the child
|
||||||
if (w->drop.pending.count) return;
|
if (w->drop.pending.count && !is_drop) return;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
int header_size = snprintf(buf, sizeof(buf), "\x1b]%d;i=%u:t=m:x=%u:y=%u:X=%d:Y=%d", DND_CODE, w->drop.client_id,
|
int header_size = snprintf(buf, sizeof(buf), "\x1b]%d;i=%u:t=%c:x=%u:y=%u:X=%d:Y=%d", DND_CODE, w->drop.client_id,
|
||||||
w->mouse_pos.cell_x, w->mouse_pos.cell_y, (int)w->mouse_pos.global_x, (int)w->mouse_pos.global_y);
|
is_drop ? 'M' : 'm', w->mouse_pos.cell_x, w->mouse_pos.cell_y,
|
||||||
|
(int)w->mouse_pos.global_x, (int)w->mouse_pos.global_y);
|
||||||
if (mimes_total_size) {
|
if (mimes_total_size) {
|
||||||
mimes_total_size += 1;
|
mimes_total_size += 1;
|
||||||
RAII_ALLOC(char, mbuf, malloc(mimes_total_size));
|
RAII_ALLOC(char, mbuf, malloc(mimes_total_size));
|
||||||
@@ -157,8 +183,7 @@ drop_move_on_child(Window *w, const char** mimes, size_t num_mimes) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buf[header_size++] = 0x1b; buf[header_size++] = '\\';
|
buf[header_size++] = 0x1b; buf[header_size++] = '\\';
|
||||||
bool found, too_much_data;
|
queue_payload_to_child(w->id, &w->drop.pending, buf, header_size, NULL, 0);
|
||||||
schedule_write_to_child_if_possible(w->id, buf, header_size, &found, &too_much_data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,6 +240,7 @@ drop_update_mimes(Window *w, const char **allowed_mimes, size_t allowed_mimes_co
|
|||||||
void
|
void
|
||||||
drop_left_child(Window *w) {
|
drop_left_child(Window *w) {
|
||||||
w->drop.hovered = false;
|
w->drop.hovered = false;
|
||||||
|
w->drop.dropped = false;
|
||||||
drop_free_offered_mimes(w);
|
drop_free_offered_mimes(w);
|
||||||
if (w->drop.wanted) {
|
if (w->drop.wanted) {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
|
||||||
void drop_move_on_child(Window *w, const char **mimes, size_t num_mimes);
|
void drop_move_on_child(Window *w, const char **mimes, size_t num_mimes, bool is_drop);
|
||||||
void drop_left_child(Window *w);
|
void drop_left_child(Window *w);
|
||||||
void drop_free_data(Window *w);
|
void drop_free_data(Window *w);
|
||||||
void drop_set_status(Window *w, int operation, const char *payload, size_t payload_sz, bool more);
|
void drop_set_status(Window *w, int operation, const char *payload, size_t payload_sz, bool more);
|
||||||
|
|||||||
34
kitty/glfw.c
34
kitty/glfw.c
@@ -808,7 +808,7 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
|
|||||||
os_window->last_drag_event.y = (int)(ev->ypos * os_window->viewport_y_ratio);
|
os_window->last_drag_event.y = (int)(ev->ypos * os_window->viewport_y_ratio);
|
||||||
on_mouse_position_update(ev->xpos, ev->ypos);
|
on_mouse_position_update(ev->xpos, ev->ypos);
|
||||||
if (!is_kitty_ui_drag && wid && (w = window_for_window_id(wid)) && w->drop.wanted) {
|
if (!is_kitty_ui_drag && wid && (w = window_for_window_id(wid)) && w->drop.wanted) {
|
||||||
drop_move_on_child(w, ev->mimes, ev->num_mimes);
|
drop_move_on_child(w, ev->mimes, ev->num_mimes, false);
|
||||||
ev->num_mimes = drop_update_mimes(w, ev->mimes, ev->num_mimes);
|
ev->num_mimes = drop_update_mimes(w, ev->mimes, ev->num_mimes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -836,20 +836,24 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
|
|||||||
case GLFW_DROP_DROP:
|
case GLFW_DROP_DROP:
|
||||||
Py_CLEAR(global_state.drop_dest.data);
|
Py_CLEAR(global_state.drop_dest.data);
|
||||||
global_state.drop_dest.drop_has_happened = true;
|
global_state.drop_dest.drop_has_happened = true;
|
||||||
if (ev->from_self) {
|
if (is_kitty_ui_drag) {
|
||||||
if (global_state.drag_source.drag_data) {
|
if (ev->from_self) {
|
||||||
global_state.drag_source.was_dropped = true;
|
if (global_state.drag_source.drag_data) {
|
||||||
WINDOW_CALLBACK(on_drop, "OOii", global_state.drag_source.drag_data, Py_True,
|
global_state.drag_source.was_dropped = true;
|
||||||
global_state.callback_os_window->last_drag_event.x, global_state.callback_os_window->last_drag_event.y);
|
WINDOW_CALLBACK(on_drop, "OOii", global_state.drag_source.drag_data, Py_True,
|
||||||
} else log_error("Got a drop from self but drag_source.drag_data is NULL");
|
global_state.callback_os_window->last_drag_event.x, global_state.callback_os_window->last_drag_event.y);
|
||||||
ev->finish_drop(window, GLFW_DRAG_OPERATION_COPY);
|
} else log_error("Got a drop from self but drag_source.drag_data is NULL");
|
||||||
break;
|
ev->finish_drop(window, GLFW_DRAG_OPERATION_COPY);
|
||||||
}
|
break;
|
||||||
update_allowed_mimes_for_drop(ev);
|
}
|
||||||
ev->num_mimes = remove_duplicate_mimes(ev->mimes, ev->num_mimes);
|
update_allowed_mimes_for_drop(ev);
|
||||||
global_state.drop_dest.num_left = ev->num_mimes;
|
ev->num_mimes = remove_duplicate_mimes(ev->mimes, ev->num_mimes);
|
||||||
if (!global_state.drop_dest.num_left || !(global_state.drop_dest.data = PyDict_New())) {
|
global_state.drop_dest.num_left = ev->num_mimes;
|
||||||
ev->finish_drop(window, GLFW_DRAG_OPERATION_GENERIC);
|
if (!global_state.drop_dest.num_left || !(global_state.drop_dest.data = PyDict_New())) {
|
||||||
|
ev->finish_drop(window, GLFW_DRAG_OPERATION_GENERIC);
|
||||||
|
}
|
||||||
|
} else if (wid && (w = window_for_window_id(wid)) && w->drop.wanted && w->drop.hovered) {
|
||||||
|
drop_move_on_child(w, ev->mimes, ev->num_mimes, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GLFW_DROP_DATA_AVAILABLE:
|
case GLFW_DROP_DATA_AVAILABLE:
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ typedef struct Window {
|
|||||||
bool is_hovering;
|
bool is_hovering;
|
||||||
} scrollbar;
|
} scrollbar;
|
||||||
struct {
|
struct {
|
||||||
bool wanted, hovered;
|
bool wanted, hovered, dropped;
|
||||||
uint32_t client_id;
|
uint32_t client_id;
|
||||||
PendingData pending;
|
PendingData pending;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user