Get drop status updates working
This commit is contained in:
parent
111b35b6a7
commit
3a30c47942
11 changed files with 125 additions and 9 deletions
|
|
@ -68,6 +68,21 @@ 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.
|
||||
|
||||
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
|
||||
accepts. Until the client does so the terminal will indicate to the OS that
|
||||
the drop is not accepted. To do so, the client sends an escape code of the
|
||||
form::
|
||||
|
||||
OSC _dnd_code ; t=m:o=O ; MIME list ST
|
||||
|
||||
Here the ``o`` key is the operation the client intends to perform if a drop
|
||||
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
|
||||
indication of the first MIME type in the list.
|
||||
|
||||
Metadata reference
|
||||
---------------------------
|
||||
|
|
@ -89,6 +104,11 @@ Key Value Default Description
|
|||
When it is set, all responses from
|
||||
the terminal in that session will
|
||||
have it set to the same value.
|
||||
|
||||
``o`` Positive integer ``0`` What drop operation to perform. ``0``
|
||||
means rejected, ``1`` means copy and
|
||||
``2`` means move.
|
||||
|
||||
**Keys for location**
|
||||
-----------------------------------------------------------
|
||||
``x`` Integer ``0`` Cell x-coordinate origin is 0, 0 at top left of screen
|
||||
|
|
|
|||
|
|
@ -334,6 +334,11 @@ def parsers() -> None:
|
|||
't': ('type', flag('aA')),
|
||||
'm': ('more', 'uint'),
|
||||
'i': ('client_id', 'uint'),
|
||||
'o': ('operation', 'uint'),
|
||||
'x': ('cell_x', 'int'),
|
||||
'y': ('cell_y', 'int'),
|
||||
'X': ('pixel_x', 'int'),
|
||||
'Y': ('pixel_y', 'int'),
|
||||
}
|
||||
text = generate(
|
||||
'parse_dnd_code', 'screen_handle_dnd_command', 'dnd_command', keymap, 'DnDCommand',
|
||||
|
|
|
|||
3
glfw/glfw3.h
vendored
3
glfw/glfw3.h
vendored
|
|
@ -4976,7 +4976,8 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun ca
|
|||
GLFWAPI GLFWliveresizefun glfwSetLiveResizeCallback(GLFWwindow* window, GLFWliveresizefun callback);
|
||||
|
||||
GLFWAPI GLFWdropeventfun glfwSetDropEventCallback(GLFWwindow *window, GLFWdropeventfun callback);
|
||||
GLFWAPI void glfwRequestDropUpdate(GLFWwindow *window); // ask for update before GLFW_DROP_DROP happens
|
||||
// ask for update before GLFW_DROP_DROP happens
|
||||
GLFWAPI void glfwRequestDropUpdate(GLFWwindow *window);
|
||||
GLFWAPI int glfwRequestDropData(GLFWwindow *window, const char *mime);
|
||||
GLFWAPI void glfwEndDrop(GLFWwindow *window, GLFWDragOperationType op);
|
||||
GLFWAPI GLFWdragsourcefun glfwSetDragSourceCallback(GLFWwindow* window, GLFWdragsourcefun callback);
|
||||
|
|
|
|||
60
kitty/dnd.c
60
kitty/dnd.c
|
|
@ -8,6 +8,7 @@
|
|||
#include "dnd.h"
|
||||
#include "base64.h"
|
||||
#include "control-codes.h"
|
||||
#include "iqsort.h"
|
||||
|
||||
static void
|
||||
drop_free_offered_mimes(Window *w) {
|
||||
|
|
@ -18,6 +19,12 @@ drop_free_offered_mimes(Window *w) {
|
|||
w->drop.num_offerred_mimes = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
drop_free_accepted_mimes(Window *w) {
|
||||
free(w->drop.accepted_mimes); w->drop.accepted_mimes = NULL;
|
||||
w->drop.accepted_mimes_sz = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
free_pending(PendingData *pending) {
|
||||
if (pending->items) {
|
||||
|
|
@ -30,6 +37,7 @@ free_pending(PendingData *pending) {
|
|||
void
|
||||
drop_free_data(Window *w) {
|
||||
drop_free_offered_mimes(w);
|
||||
drop_free_accepted_mimes(w);
|
||||
free_pending(&w->drop.pending);
|
||||
}
|
||||
|
||||
|
|
@ -154,11 +162,61 @@ drop_move_on_child(Window *w, const char** mimes, size_t num_mimes) {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
drop_set_status(Window *w, int operation, const char *payload, size_t payload_sz, bool more) {
|
||||
if (!w->drop.accept_in_progress) {
|
||||
drop_free_accepted_mimes(w); w->drop.accept_in_progress = true; w->drop.accepted_operation = 0;
|
||||
switch(operation) {
|
||||
case 1: w->drop.accepted_operation = GLFW_DRAG_OPERATION_COPY; break;
|
||||
case 2: w->drop.accepted_operation = GLFW_DRAG_OPERATION_MOVE; break;
|
||||
default: w->drop.accepted_operation = GLFW_DRAG_OPERATION_NONE; break;
|
||||
}
|
||||
}
|
||||
w->drop.accepted_mimes = realloc(w->drop.accepted_mimes, w->drop.accepted_mimes_sz + payload_sz + 2);
|
||||
if (w->drop.accepted_mimes) {
|
||||
memcpy(w->drop.accepted_mimes + w->drop.accepted_mimes_sz, payload, payload_sz);
|
||||
w->drop.accepted_mimes_sz += payload_sz;
|
||||
}
|
||||
if (!more) {
|
||||
w->drop.accept_in_progress = false;
|
||||
if (w->drop.accepted_mimes) {
|
||||
for (size_t i = 0; i < w->drop.accepted_mimes_sz; i++)
|
||||
if (w->drop.accepted_mimes[i] == ' ') w->drop.accepted_mimes[i] = 0;
|
||||
w->drop.accepted_mimes[w->drop.accepted_mimes_sz++] = 0;
|
||||
}
|
||||
OSWindow *osw = os_window_for_kitty_window(w->id);
|
||||
if (osw) request_drop_status_update(osw);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
drop_update_mimes(Window *w, const char **allowed_mimes, size_t allowed_mimes_count) {
|
||||
if (w->drop.accept_in_progress) return allowed_mimes_count;
|
||||
if (w->drop.accepted_operation == GLFW_DRAG_OPERATION_NONE) return 0;
|
||||
typedef struct mime_sorter { const char *m; ssize_t key; } mime_sorter;
|
||||
if (!w->drop.accepted_mimes) return allowed_mimes_count;
|
||||
RAII_ALLOC(mime_sorter, ms, malloc(sizeof(mime_sorter) * allowed_mimes_count));
|
||||
if (!ms) return allowed_mimes_count;
|
||||
const ssize_t sentinel = allowed_mimes_count;
|
||||
for (size_t i = 0; i < allowed_mimes_count; i++) {
|
||||
ms[i].m = allowed_mimes[i];
|
||||
const char *p = strstr(w->drop.accepted_mimes, ms[i].m);
|
||||
ms[i].key = p ? p - w->drop.accepted_mimes : sentinel;
|
||||
}
|
||||
#define mimes_lt(a, b) ((a)->key < (b)->key)
|
||||
QSORT(mime_sorter, ms, allowed_mimes_count, mimes_lt);
|
||||
#undef mimes_lt
|
||||
while(allowed_mimes_count && ms[allowed_mimes_count-1].key == sentinel) allowed_mimes_count--;
|
||||
for (size_t i = 0; i < allowed_mimes_count; i++) allowed_mimes[i] = ms[i].m;
|
||||
return allowed_mimes_count;
|
||||
}
|
||||
|
||||
void
|
||||
drop_left_child(Window *w) {
|
||||
w->drop.hovered = false;
|
||||
drop_free_offered_mimes(w);
|
||||
if (w->drop.allowed) {
|
||||
if (w->drop.wanted) {
|
||||
char buf[128];
|
||||
int header_size = snprintf(buf, sizeof(buf), "\x1b]%d;i=%u:t=m:x=-1:y=-1", DND_CODE, w->drop.client_id);
|
||||
queue_payload_to_child(w->id, &w->drop.pending, buf, header_size, NULL, 0);
|
||||
|
|
|
|||
|
|
@ -11,3 +11,5 @@
|
|||
void drop_move_on_child(Window *w, const char **mimes, size_t num_mimes);
|
||||
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);
|
||||
size_t drop_update_mimes(Window *w, const char **allowed_mimes, size_t allowed_mimes_count);
|
||||
|
|
|
|||
3
kitty/glfw-wrapper.c
generated
3
kitty/glfw-wrapper.c
generated
|
|
@ -359,6 +359,9 @@ load_glfw(const char* path) {
|
|||
*(void **) (&glfwSetDropEventCallback_impl) = dlsym(handle, "glfwSetDropEventCallback");
|
||||
if (glfwSetDropEventCallback_impl == NULL) fail("Failed to load glfw function glfwSetDropEventCallback with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwRequestDropUpdate_impl) = dlsym(handle, "glfwRequestDropUpdate");
|
||||
if (glfwRequestDropUpdate_impl == NULL) fail("Failed to load glfw function glfwRequestDropUpdate with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwRequestDropData_impl) = dlsym(handle, "glfwRequestDropData");
|
||||
if (glfwRequestDropData_impl == NULL) fail("Failed to load glfw function glfwRequestDropData with error: %s", dlerror());
|
||||
|
||||
|
|
|
|||
4
kitty/glfw-wrapper.h
generated
4
kitty/glfw-wrapper.h
generated
|
|
@ -2290,6 +2290,10 @@ typedef GLFWdropeventfun (*glfwSetDropEventCallback_func)(GLFWwindow*, GLFWdrope
|
|||
GFW_EXTERN glfwSetDropEventCallback_func glfwSetDropEventCallback_impl;
|
||||
#define glfwSetDropEventCallback glfwSetDropEventCallback_impl
|
||||
|
||||
typedef void (*glfwRequestDropUpdate_func)(GLFWwindow*);
|
||||
GFW_EXTERN glfwRequestDropUpdate_func glfwRequestDropUpdate_impl;
|
||||
#define glfwRequestDropUpdate glfwRequestDropUpdate_impl
|
||||
|
||||
typedef int (*glfwRequestDropData_func)(GLFWwindow*, const char*);
|
||||
GFW_EXTERN glfwRequestDropData_func glfwRequestDropData_impl;
|
||||
#define glfwRequestDropData glfwRequestDropData_impl
|
||||
|
|
|
|||
19
kitty/glfw.c
19
kitty/glfw.c
|
|
@ -771,7 +771,7 @@ register_drop_window(id_type window_id, const uint8_t *payload, size_t payload_s
|
|||
Window *w = window_for_window_id(window_id);
|
||||
OSWindow *osw = os_window_for_kitty_window(window_id);
|
||||
if (w && osw && osw->handle) {
|
||||
w->drop.allowed = on;
|
||||
w->drop.wanted = on;
|
||||
w->drop.client_id = client_id;
|
||||
if (!on) { drop_free_data(w); zero_at_ptr(&w->drop); }
|
||||
else if (payload && payload_sz) {
|
||||
|
|
@ -795,7 +795,7 @@ static void
|
|||
on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
|
||||
if (!set_callback_window(window)) return;
|
||||
OSWindow *os_window = global_state.callback_os_window;
|
||||
Window *w = NULL;
|
||||
Window *w = NULL; id_type wid = global_state.mouse_hover_in_window;
|
||||
bool is_kitty_ui_drag = false;
|
||||
for (size_t i = 0; i < ev->num_mimes; i++) {
|
||||
if (is_droppable_mime(ev->mimes[i]) >= TAB_DRAG_MIME_NUMBER) { is_kitty_ui_drag = true; break;}
|
||||
|
|
@ -803,11 +803,13 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
|
|||
switch (ev->type) {
|
||||
case GLFW_DROP_ENTER:
|
||||
case GLFW_DROP_MOVE:
|
||||
global_state.drop_dest.drop_has_happened = false;
|
||||
os_window->last_drag_event.x = (int)(ev->xpos * os_window->viewport_x_ratio);
|
||||
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 && global_state.mouse_hover_in_window && (w = window_for_window_id(global_state.mouse_hover_in_window)) && w->drop.allowed) {
|
||||
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);
|
||||
ev->num_mimes = drop_update_mimes(w, ev->mimes, ev->num_mimes);
|
||||
return;
|
||||
}
|
||||
call_boss(on_drop_move, "KiiOO",
|
||||
|
|
@ -816,8 +818,8 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
|
|||
/* fallthrough */
|
||||
case GLFW_DROP_STATUS_UPDATE:
|
||||
if (is_kitty_ui_drag) update_allowed_mimes_for_drop(ev);
|
||||
else {
|
||||
}
|
||||
else if (wid && (w = window_for_window_id(wid)) && w->drop.wanted && w->drop.hovered)
|
||||
ev->num_mimes = drop_update_mimes(w, ev->mimes, ev->num_mimes);
|
||||
break;
|
||||
case GLFW_DROP_LEAVE:
|
||||
for (size_t tc = 0; tc < os_window->num_tabs; tc++) {
|
||||
|
|
@ -833,6 +835,7 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
|
|||
break;
|
||||
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;
|
||||
|
|
@ -856,6 +859,12 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
request_drop_status_update(OSWindow *osw) {
|
||||
if (osw && osw->handle && !global_state.drop_dest.drop_has_happened && global_state.drop_dest.os_window_id == osw->id)
|
||||
glfwRequestDropUpdate(osw->handle);
|
||||
}
|
||||
|
||||
static void
|
||||
application_close_requested_callback(int flags) {
|
||||
if (flags) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "data-types.h"
|
||||
#include "control-codes.h"
|
||||
#include "screen.h"
|
||||
#include "dnd.h"
|
||||
#include "state.h"
|
||||
#include "iqsort.h"
|
||||
#include "fonts.h"
|
||||
|
|
@ -1513,6 +1514,10 @@ screen_handle_dnd_command(Screen *self, const DnDCommand *cmd, const uint8_t *pa
|
|||
switch(cmd->type) {
|
||||
case 'a': register_drop_window(self->window_id, payload, cmd->payload_sz, true, cmd->client_id); break;
|
||||
case 'A': register_drop_window(self->window_id, NULL, 0, false, cmd->client_id); break;
|
||||
case 'm': {
|
||||
Window *w = window_for_window_id(self->window_id);
|
||||
if (w) drop_set_status(w, cmd->operation, (const char*)payload, cmd->payload_sz, cmd->more);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ typedef struct DnDCommand {
|
|||
unsigned more;
|
||||
uint32_t client_id;
|
||||
size_t payload_sz;
|
||||
int32_t cell_x, cell_y, pixel_x, pixel_y;
|
||||
uint32_t operation;
|
||||
} DnDCommand;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
|||
|
|
@ -246,10 +246,15 @@ typedef struct Window {
|
|||
bool is_hovering;
|
||||
} scrollbar;
|
||||
struct {
|
||||
bool allowed, hovered;
|
||||
bool wanted, hovered;
|
||||
uint32_t client_id;
|
||||
const char **offerred_mimes; size_t num_offerred_mimes;
|
||||
PendingData pending;
|
||||
|
||||
const char **offerred_mimes; size_t num_offerred_mimes;
|
||||
|
||||
char *accepted_mimes; size_t accepted_mimes_sz;
|
||||
int accepted_operation; bool accept_in_progress;
|
||||
|
||||
} drop;
|
||||
} Window;
|
||||
|
||||
|
|
@ -402,6 +407,7 @@ typedef struct GlobalState {
|
|||
id_type os_window_id;
|
||||
double x, y;
|
||||
size_t num_left;
|
||||
bool drop_has_happened;
|
||||
} drop_dest;
|
||||
|
||||
struct {
|
||||
|
|
@ -529,3 +535,4 @@ void setup_os_window_for_rendering(OSWindow*, Tab*, Window*, bool);
|
|||
void swap_window_buffers(OSWindow *w);
|
||||
void take_screenshot_of_rectangular_region(OSWindow *os_window, Region region, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h);
|
||||
bool current_framebuffer_is_ok(void);
|
||||
void request_drop_status_update(OSWindow *osw);
|
||||
|
|
|
|||
Loading…
Reference in a new issue