diff --git a/kittens/dnd/drag.go b/kittens/dnd/drag.go index 9f2b50d0e..1732dbc59 100644 --- a/kittens/dnd/drag.go +++ b/kittens/dnd/drag.go @@ -33,6 +33,7 @@ type remote_data_item struct { write_id loop.IdType base64 streaming_base64.StreamingBase64Encoder parent_dir_handle, idx_in_parent int + idx_in_uri_list int } type drag_status struct { @@ -220,21 +221,19 @@ func (dnd *dnd) on_data_request_finished(i int) (err error) { return } -func (dnd *dnd) send_remote_item_payload(parent_dir_handle, idx, item_type int, payload []byte) loop.IdType { - cmd := DC{Type: 'k', X: item_type} +func (dnd *dnd) send_remote_item_payload(parent_dir_handle, idx_in_parent, idx_in_uri_list, item_type int, payload []byte) loop.IdType { + cmd := DC{Type: 'k', Xp: item_type, X: idx_in_uri_list + 1} if len(payload) > 0 { cmd.Payload = utils.UnsafeStringToBytes(base64.RawStdEncoding.EncodeToString(payload)) } if parent_dir_handle != 0 { cmd.Yp = parent_dir_handle - cmd.Y = idx + 1 - } else { - cmd.X = idx + 1 + cmd.Y = idx_in_parent + 1 } return dnd.lp.QueueDnDData(cmd) } -func (dnd *dnd) send_remote_dir(path string, parent_dir_handle, idx int) (children []*remote_data_item, err error) { +func (dnd *dnd) send_remote_dir(path string, idx_in_uri_list, parent_dir_handle, idx int) (children []*remote_data_item, err error) { entries, err := os.ReadDir(path) if err != nil { dnd.finish_drag("EIO") @@ -249,24 +248,26 @@ func (dnd *dnd) send_remote_dir(path string, parent_dir_handle, idx int) (childr dnd.finish_drag("EIO") return nil, err } - x := remote_data_item{parent_dir_handle: handle, idx_in_parent: i, metadata: st, path: filepath.Join(path, entry.Name())} + x := remote_data_item{ + parent_dir_handle: handle, idx_in_parent: i, metadata: st, idx_in_uri_list: idx_in_uri_list, + path: filepath.Join(path, entry.Name())} children = append(children, &x) names = append(names, entry.Name()) } payload := utils.UnsafeStringToBytes(strings.Join(names, "\x00")) - dnd.send_remote_item_payload(parent_dir_handle, idx, handle, payload) - dnd.drag_status.remote_item_write_id = dnd.send_remote_item_payload(parent_dir_handle, idx, handle, nil) + dnd.send_remote_item_payload(parent_dir_handle, idx, idx_in_uri_list, handle, payload) + dnd.drag_status.remote_item_write_id = dnd.send_remote_item_payload(parent_dir_handle, idx, idx_in_uri_list, handle, nil) return } -func (dnd *dnd) send_remote_symlink(path string, parent_dir_handle, idx int) (err error) { +func (dnd *dnd) send_remote_symlink(path string, idx_in_uri_list, parent_dir_handle, idx int) (err error) { target, err := os.Readlink(path) if err != nil { dnd.finish_drag("EIO") return err } - dnd.send_remote_item_payload(parent_dir_handle, idx, 1, utils.UnsafeStringToBytes(target)) - dnd.drag_status.remote_item_write_id = dnd.send_remote_item_payload(parent_dir_handle, idx, 1, nil) + dnd.send_remote_item_payload(parent_dir_handle, idx, idx_in_uri_list, 1, utils.UnsafeStringToBytes(target)) + dnd.drag_status.remote_item_write_id = dnd.send_remote_item_payload(parent_dir_handle, idx, idx_in_uri_list, 1, nil) return } @@ -278,18 +279,17 @@ func (dnd *dnd) send_next_file_chunk() (err error) { n, err := cr.file.Read(read_buf[:]) if n > 0 { for chunk := range cr.base64.Encode(read_buf[:n], encode_buf[:]) { - dnd.drag_status.remote_item_write_id = dnd.send_remote_item_payload(cr.parent_dir_handle, cr.idx_in_parent, 0, chunk) + dnd.drag_status.remote_item_write_id = dnd.send_remote_item_payload(cr.parent_dir_handle, cr.idx_in_parent, cr.idx_in_uri_list, 0, chunk) } } if err != nil { if errors.Is(err, io.EOF) { cr.file.Close() dnd.drag_status.current_remote_file = nil - dnd.drag_status.remote_items = dnd.drag_status.remote_items[1:] if chunk := cr.base64.Finish(); len(chunk) > 0 { - dnd.send_remote_item_payload(cr.parent_dir_handle, cr.idx_in_parent, 0, chunk) + dnd.send_remote_item_payload(cr.parent_dir_handle, cr.idx_in_parent, cr.idx_in_uri_list, 0, chunk) } - dnd.drag_status.remote_item_write_id = dnd.send_remote_item_payload(cr.parent_dir_handle, cr.idx_in_parent, 0, nil) + dnd.drag_status.remote_item_write_id = dnd.send_remote_item_payload(cr.parent_dir_handle, cr.idx_in_parent, cr.idx_in_uri_list, 0, nil) return } dnd.finish_drag("EIO") @@ -300,20 +300,22 @@ func (dnd *dnd) send_next_file_chunk() (err error) { func (dnd *dnd) next_remote_item() (err error) { if len(dnd.drag_status.remote_items) < 1 { + dnd.lp.QueueDnDData(DC{Type: 'k'}) // inform terminal remote data is finished if len(dnd.drag_status.data_requests) > 0 { err = dnd.send_data_for_data_request(0) } return } x := dnd.drag_status.remote_items[0] + dnd.drag_status.remote_items = dnd.drag_status.remote_items[1:] if x.metadata.IsDir() { - children, err := dnd.send_remote_dir(x.path, x.parent_dir_handle, x.idx_in_parent) + children, err := dnd.send_remote_dir(x.path, x.idx_in_uri_list, x.parent_dir_handle, x.idx_in_parent) if err != nil { return err } dnd.drag_status.remote_items = append(dnd.drag_status.remote_items, children...) } else if x.metadata.Mode().Type()&os.ModeSymlink != 0 { - if err = dnd.send_remote_symlink(x.path, x.parent_dir_handle, x.idx_in_parent); err != nil { + if err = dnd.send_remote_symlink(x.path, x.idx_in_uri_list, x.parent_dir_handle, x.idx_in_parent); err != nil { return } } else { @@ -335,13 +337,13 @@ func (dnd *dnd) start_remote_data_send(ds *drag_source) (err error) { items := []*remote_data_item{} for i, x := range ds.uri_list { if x.metadata.IsDir() { - if children, err := dnd.send_remote_dir(x.path, 0, i); err != nil { + if children, err := dnd.send_remote_dir(x.path, i, 0, i); err != nil { return err } else { items = append(items, children...) } } else if x.metadata.Mode().Type()&os.ModeSymlink != 0 { - if err = dnd.send_remote_symlink(x.path, 0, i); err != nil { + if err = dnd.send_remote_symlink(x.path, i, 0, i); err != nil { return err } } else { diff --git a/kitty/dnd.c b/kitty/dnd.c index 971ae8d25..a77a8a8a1 100644 --- a/kitty/dnd.c +++ b/kitty/dnd.c @@ -1492,6 +1492,10 @@ drag_get_data(Window *w, const char *mime_type, size_t *sz, int *err_code) { ds.items[i].fd_plus_one = 0; return NULL; } + if (ds.items[i].requested_remote_files) { // wait for remote files to be read + *err_code = EAGAIN; + return NULL; + } if (ds.items[i].fd_plus_one > 0) { // data_size = read position, data_capacity = bytes written to file if (ds.items[i].data_capacity > ds.items[i].data_size) { @@ -1728,6 +1732,7 @@ write_all(int fd, const void *buf, size_t sz) { static void finish_remote_data(Window *w, size_t item_idx) { const int fd = ds.items[item_idx].fd_plus_one - 1; + ds.items[item_idx].requested_remote_files = false; if (safe_ftruncate(fd, 0) != 0) abrt(errno); if (lseek(fd, 0, SEEK_SET) == -1) abrt(errno); for (size_t i = 0; i < ds.items[item_idx].num_uris; i++) { @@ -1738,7 +1743,7 @@ finish_remote_data(Window *w, size_t item_idx) { } free(ds.items[item_idx].uri_list); ds.items[item_idx].uri_list = NULL; ds.items[item_idx].num_uris = 0; int ret = dnd_is_test_mode() ? 0 : notify_drag_data_ready(global_state.drag_source.from_os_window, ds.items[item_idx].mime_type); - abrt(ret); + if (ret) abrt(ret); } #define mi ds.items[mime_item_idx] diff --git a/kitty_tests/dnd_kitten.py b/kitty_tests/dnd_kitten.py index fa170dcda..d942854c4 100644 --- a/kitty_tests/dnd_kitten.py +++ b/kitty_tests/dnd_kitten.py @@ -329,13 +329,20 @@ class TestDnDKitten(BaseTest): def test_dnd_kitten_drag(self): img_drag_path = 'image.png' - with open(os.path.join(self.kitten_wd, img_drag_path), 'wb') as f: - self.img_drag_data = os.urandom(10113) - f.write(self.img_drag_data) - create_fs(self.src_data_dir) + def create_files(): + with open(os.path.join(self.kitten_wd, img_drag_path), 'wb') as f: + self.img_drag_data = os.urandom(10113) + f.write(self.img_drag_data) + create_fs(self.src_data_dir) + create_files() tl = tuple(os.path.join(self.src_data_dir, x) for x in os.listdir(self.src_data_dir)) self.finish_setup(cli_args=(f'--drag=image/png:{img_drag_path}', ) + tl) # ))) - self.dnd_kitten_drag(False, img_drag_path) + with self.subTest(remote_client=False): + self.dnd_kitten_drag(False, img_drag_path) + self.reset_kitten(True) + create_files() + with self.subTest(remote_client=True): + self.dnd_kitten_drag(True, img_drag_path) self.exit_kitten() self.img_drag_data = None @@ -352,7 +359,7 @@ class TestDnDKitten(BaseTest): if err.errno == errno.EAGAIN: self.pty.process_input_from_child() continue - del chunk, ans + chunk = ans = b'' raise return ans @@ -388,6 +395,8 @@ class TestDnDKitten(BaseTest): self.send_dnd_command_to_kitten('DRAG_STATUS') self.wait_for_responses('text/uri-list:2:true') self.assertEqual(self.img_drag_data, self.read_drag_data('image/png')) + # if remote_client: + # self.pty.log_data_flow = True uri_list = self.read_drag_data('text/uri-list').decode().splitlines() paths = set() for line in uri_list: @@ -396,5 +405,10 @@ class TestDnDKitten(BaseTest): purl = urlparse(line) if purl.scheme == 'file': paths.add(purl.path) - self.assertEqual(set(os.listdir(self.src_data_dir)), {os.path.basename(x) for x in paths}) + if remote_client: + self.assertNotEqual(self.src_data_dir, os.path.dirname(purl.path)) + else: + self.assertEqual(self.src_data_dir, os.path.dirname(purl.path)) + src_items = set(os.listdir(self.src_data_dir)) + self.assertEqual(src_items, {os.path.basename(x) for x in paths}) end_drag(False)