Start and stop accepting drops

This commit is contained in:
Kovid Goyal 2026-03-05 20:47:22 +05:30
parent 2898324047
commit 5a8132d241
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
6 changed files with 82 additions and 7 deletions

54
docs/dnd-protocol.rst Normal file
View file

@ -0,0 +1,54 @@
The Drag and Drop protocol
==============================================
.. versionadded:: 0.47.0
This protocol enables drag and drop functionality for terminal programs
that is as good as the drag and drop functionality available for GUI
programs.
There is one central escape code used for this protocol, which is of the form::
OSC _dnd_code ; metadata ; base64 encoded payload ST
Here, ``OSC`` is the bytes ``ESC ] (0x1b 0x5b)``. The ``metadata`` is a colon
separated list of ``key=value`` pairs. The final part of the escape code is the
:rfc:`base64 <4648>` encoded payload data, whose meaning depends on the
metadata. The payload must be no more than 4096 bytes *before base64 encoding*.
Accepting drops
-----------------
In order to inform the terminal emulator that the program accepts drops, it
must, send the following escape code::
OSC _dnd_code ; t=a ; payload ST
The payload here is a space separated list of MIME types the program accepts.
The list of MIME types is optional, it is needed if the program wants to accept
exotic or private use MIME types on platforms such as macOS, where the system
does not deliver drop events unless the MIME type is registered.
When the program is done accepting drops, or at exit, it should send the escape
code::
OSC _dnd_code ; t=A ST
to inform the terminal that it no longer wants drops.
Metadata reference
---------------------------
The table below shows all the metadata keys as well as what values they can
take, and the default value they take when missing. All integers are 32-bit.
======= ==================== ========= =================
Key Value Default Description
======= ==================== ========= =================
``t`` Single character. ``a`` The overall action this graphics command is performing.
``(a, A, ``t`` - transmit data, ``T`` - transmit data and display image,
)`` ``q`` - query terminal, ``p`` - put (display) previous transmitted image,
``d`` - delete image, ``f`` - transmit data for animation frames,
``a`` - control animation, ``c`` - compose animation frames

View file

@ -331,7 +331,7 @@ def parsers() -> None:
write_header(text, 'kitty/parse-multicell-command.h')
keymap = {
't': ('type', flag('ae')),
't': ('type', flag('aA')),
'm': ('more', 'uint'),
}
text = generate(

View file

@ -765,6 +765,29 @@ read_drop_data(GLFWwindow *window, GLFWDropEvent *ev) {
#undef finish
}
void
register_drop_window(id_type window_id, const uint8_t *payload, size_t payload_sz, bool on) {
Window *w = window_for_window_id(window_id);
OSWindow *osw = os_window_for_kitty_window(window_id);
if (w && osw && osw->handle) {
w->accepts_drops = on;
if (on && payload && payload_sz) {
#ifdef __APPLE__
RAII_ALLOC(char, copy, malloc(payload_sz + 1)); if (!copy) return;
RAII_ALLOC(const char*, mimes, calloc(payload_sz, sizeof(char*))); if (!mimes) return;
memcpy(copy, payload, payload_sz); copy[payload_sz] = 0;
size_t num = 0;
char* token = strtok(copy, " ");
while (token != NULL) {
mimes[num++] = token;
token = strtok(NULL, " ");
}
glfwCocoaRegisterMIMETypes(osw->handle, mimes, num);
#endif
}
}
}
static void
on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
if (!set_callback_window(window)) return;

View file

@ -1511,11 +1511,8 @@ void
screen_handle_dnd_command(Screen *self, const DnDCommand *cmd, const uint8_t *payload) {
if (!self->window_id) return;
switch(cmd->type) {
case 'a':
(void)payload;
break;
case 'e':
break;
case 'a': register_drop_window(self->window_id, payload, cmd->payload_sz, true); break;
case 'A': register_drop_window(self->window_id, NULL, 0, false); break;
}
}

View file

@ -323,6 +323,7 @@ bool screen_pause_rendering(Screen *self, bool pause, int for_in_ms);
void screen_check_pause_rendering(Screen *self, monotonic_t now);
void screen_designate_charset(Screen *self, uint32_t which, uint32_t as);
void screen_multi_cursor(Screen *self, int queried_shape, int *params, unsigned num_params);
void register_drop_window(id_type window_id, const uint8_t *payload, size_t payload_sz, bool on);
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);
DECLARE_CH_SCREEN_HANDLER(bell)
DECLARE_CH_SCREEN_HANDLER(backspace)

View file

@ -206,7 +206,7 @@ typedef struct WindowBarData {
typedef struct Window {
id_type id;
bool visible;
bool visible, accepts_drops;
PyObject *title;
WindowRenderData render_data;
WindowRenderData window_title_render_data;