Send dropped message
This commit is contained in:
parent
3a30c47942
commit
4f404f8c85
6 changed files with 73 additions and 27 deletions
|
|
@ -66,7 +66,8 @@ only if the list changes.
|
|||
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
|
||||
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 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
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
---------------------------
|
||||
|
||||
|
|
@ -97,6 +112,7 @@ Key Value Default Description
|
|||
``(a, A, ``a`` - start accepting drops
|
||||
)`` ``A`` - stop accepting drops
|
||||
``m`` - a drop move event
|
||||
``M`` - a drop dropped event
|
||||
|
||||
``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('aA')),
|
||||
't': ('type', flag('aAmM')),
|
||||
'm': ('more', 'uint'),
|
||||
'i': ('client_id', '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);
|
||||
}
|
||||
|
||||
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
|
||||
string_arrays_cmp(const char **a, size_t an, const char **b, size_t bn) {
|
||||
if (an != bn) return (int)an - (int)bn;
|
||||
|
|
@ -102,6 +113,18 @@ flush_pending(id_type id, PendingData *pending) {
|
|||
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
|
||||
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;
|
||||
|
|
@ -114,14 +137,16 @@ queue_payload_to_child(id_type id, PendingData *pending, const char *header, siz
|
|||
PendingEntry *e = &pending->items[pending->count++];
|
||||
e->buf = buf; e->header_sz = header_sz; e->data_sz = data_sz - offset;
|
||||
}
|
||||
if (pending->count) check_for_pending_writes();
|
||||
}
|
||||
|
||||
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) {
|
||||
drop_free_offered_mimes(w);
|
||||
reset_drop(w);
|
||||
w->drop.hovered = true;
|
||||
}
|
||||
if (is_drop) { w->drop.dropped = true; w->drop.hovered = false; }
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
// 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];
|
||||
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,
|
||||
w->mouse_pos.cell_x, w->mouse_pos.cell_y, (int)w->mouse_pos.global_x, (int)w->mouse_pos.global_y);
|
||||
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,
|
||||
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) {
|
||||
mimes_total_size += 1;
|
||||
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 {
|
||||
buf[header_size++] = 0x1b; buf[header_size++] = '\\';
|
||||
bool found, too_much_data;
|
||||
schedule_write_to_child_if_possible(w->id, buf, header_size, &found, &too_much_data);
|
||||
queue_payload_to_child(w->id, &w->drop.pending, buf, header_size, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,6 +240,7 @@ drop_update_mimes(Window *w, const char **allowed_mimes, size_t allowed_mimes_co
|
|||
void
|
||||
drop_left_child(Window *w) {
|
||||
w->drop.hovered = false;
|
||||
w->drop.dropped = false;
|
||||
drop_free_offered_mimes(w);
|
||||
if (w->drop.wanted) {
|
||||
char buf[128];
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#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_free_data(Window *w);
|
||||
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);
|
||||
on_mouse_position_update(ev->xpos, ev->ypos);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
|
@ -836,20 +836,24 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
|
|||
case GLFW_DROP_DROP:
|
||||
Py_CLEAR(global_state.drop_dest.data);
|
||||
global_state.drop_dest.drop_has_happened = true;
|
||||
if (ev->from_self) {
|
||||
if (global_state.drag_source.drag_data) {
|
||||
global_state.drag_source.was_dropped = true;
|
||||
WINDOW_CALLBACK(on_drop, "OOii", global_state.drag_source.drag_data, Py_True,
|
||||
global_state.callback_os_window->last_drag_event.x, global_state.callback_os_window->last_drag_event.y);
|
||||
} else log_error("Got a drop from self but drag_source.drag_data is NULL");
|
||||
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);
|
||||
global_state.drop_dest.num_left = ev->num_mimes;
|
||||
if (!global_state.drop_dest.num_left || !(global_state.drop_dest.data = PyDict_New())) {
|
||||
ev->finish_drop(window, GLFW_DRAG_OPERATION_GENERIC);
|
||||
if (is_kitty_ui_drag) {
|
||||
if (ev->from_self) {
|
||||
if (global_state.drag_source.drag_data) {
|
||||
global_state.drag_source.was_dropped = true;
|
||||
WINDOW_CALLBACK(on_drop, "OOii", global_state.drag_source.drag_data, Py_True,
|
||||
global_state.callback_os_window->last_drag_event.x, global_state.callback_os_window->last_drag_event.y);
|
||||
} else log_error("Got a drop from self but drag_source.drag_data is NULL");
|
||||
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);
|
||||
global_state.drop_dest.num_left = ev->num_mimes;
|
||||
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;
|
||||
case GLFW_DROP_DATA_AVAILABLE:
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ typedef struct Window {
|
|||
bool is_hovering;
|
||||
} scrollbar;
|
||||
struct {
|
||||
bool wanted, hovered;
|
||||
bool wanted, hovered, dropped;
|
||||
uint32_t client_id;
|
||||
PendingData pending;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue