diff --git a/kittens/dnd/main.go b/kittens/dnd/main.go index 39acb9b44..02b9a1678 100644 --- a/kittens/dnd/main.go +++ b/kittens/dnd/main.go @@ -323,10 +323,11 @@ func run_loop(opts *Options, drop_dests map[string]*drop_dest, drag_sources map[ if drop_status.remote_phase_started { return on_remote_drop_data(cmd) } - if cmd.X < 0 || cmd.X > len(drop_status.accepted_mimes) { + idx := cmd.X - 1 + if idx < 0 || idx > len(drop_status.accepted_mimes) { return fmt.Errorf("terminal sent drop data for a index outside the list of accepted MIMEs") } - mime := drop_status.accepted_mimes[cmd.X] + mime := drop_status.accepted_mimes[idx] dest := drop_dests[mime] if cmd.Xp == 1 && mime == "text/uri-list" { drop_status.is_remote_client = true @@ -428,6 +429,8 @@ func run_loop(opts *Options, drop_dests map[string]*drop_dest, drag_sources map[ } else { send_test_response("") } + case "DROP_IS_REMOTE": + send_test_response(utils.IfElse(drop_status.is_remote_client, "True", "False")) default: send_test_response("UNKNOWN TEST COMMAND: " + string(cmd.Payload)) } diff --git a/kitty/dnd.c b/kitty/dnd.c index 9c349b3c6..47fa02cf7 100644 --- a/kitty/dnd.c +++ b/kitty/dnd.c @@ -2093,15 +2093,15 @@ dnd_test_fake_drop_data(PyObject *self UNUSED, PyObject *args) { unsigned long long window_id; const char *mime; RAII_PY_BUFFER(data); - int error_code = 0; - if (!PyArg_ParseTuple(args, "Ksy*|i", &window_id, &mime, &data, &error_code)) return NULL; + int error_code = 0, no_eod = 0; + if (!PyArg_ParseTuple(args, "Ksy*|ii", &window_id, &mime, &data, &error_code, &no_eod)) return NULL; Window *w = window_for_window_id((id_type)window_id); if (!w) { PyErr_SetString(PyExc_ValueError, "Window not found"); return NULL; } if (error_code > 0) { drop_dispatch_data(w, mime, NULL, -(ssize_t)error_code); } else if (data.len > 0) { drop_dispatch_data(w, mime, (const char*)data.buf, (ssize_t)data.len); - drop_dispatch_data(w, mime, NULL, 0); // mandatory end-of-data signal + if (!no_eod) drop_dispatch_data(w, mime, NULL, 0); // mandatory end-of-data signal } else { // Empty data: just the end-of-data signal (sz=0 is the sentinel for "no more data"). drop_dispatch_data(w, mime, NULL, 0); @@ -2216,6 +2216,9 @@ dnd_test_probe_state(PyObject *self UNUSED, PyObject *args) { } return ans; } + if (strcmp(q, "drop_getting_data_for_mime") == 0) { + return PyUnicode_FromString(w->drop.getting_data_for_mime ? w->drop.getting_data_for_mime : ""); + } Py_RETURN_NONE; } diff --git a/kitty_tests/dnd_kitten.py b/kitty_tests/dnd_kitten.py index 985a6a2a5..b589931bb 100644 --- a/kitty_tests/dnd_kitten.py +++ b/kitty_tests/dnd_kitten.py @@ -2,8 +2,10 @@ # License: GPLv3 Copyright: 2026, Kovid Goyal import os +import random import tempfile from base64 import standard_b64encode +from functools import partial from kitty.constants import kitten_exe from kitty.fast_data_types import ( @@ -13,9 +15,11 @@ from kitty.fast_data_types import ( dnd_set_test_write_func, dnd_test_cleanup_fake_window, dnd_test_create_fake_window, + dnd_test_fake_drop_data, dnd_test_fake_drop_event, dnd_test_probe_state, ) +from kitty.utils import as_file_url from . import PTY, BaseTest from .dnd import WriteCapture @@ -27,6 +31,27 @@ class Capture(WriteCapture): self.pty.write_to_child(data) +def create_fs(base): + join = partial(os.path.join, base) + def w(sz, *path): + if sz == 0: + sz = random.randint(5713, 9879) + with open(join(*path), 'wb') as f: + f.write(os.urandom(sz)) + os.makedirs(join('d1', 'sd', 'ssd')) + os.mkdir(join('d2')) + os.symlink('/does-not-exist', join('s1')) + os.symlink('d1', join('sd')) + os.symlink('/', join('sr')) + os.symlink('../d1', join('d1', 'sr')) + w(4096 * 3 + 113, 'some-image.png') + w(0, 'd1', 'f1') + w(0, 'd1', 'f2') + w(0, 'd1', 'sd', 'f1') + w(0, 'd1', 'sd', 'ssd', 'f1') + os.symlink('../moose', join('d1', 'sd', 'ssd', 's1')) + + class TestDnDKitten(BaseTest): def setUp(self): @@ -164,3 +189,13 @@ class TestDnDKitten(BaseTest): self.send_dnd_command_to_kitten('DROP_MIMES') self.wait_for_responses(all_mimes) self.wait_for_state('drop_data_requests', ((1,0,0), (2,0,0))) + self.assertEqual('text/uri-list', self.probe_state('drop_getting_data_for_mime')) + create_fs(self.src_data_dir) + uri_list = [] + for x in os.listdir(self.src_data_dir): + uri_list.append(as_file_url(self.src_data_dir, x)) + uri_list = ['moose://cow', 'frog:march'] + uri_list + uri_list.insert(3, 'ignore://me') + dnd_test_fake_drop_data(self.capture.window_id, 'text/uri-list', '\r\n'.join(uri_list).encode()) + self.send_dnd_command_to_kitten('DROP_IS_REMOTE') + self.wait_for_responses(str(remote_client))