From 8b17088b5893c9f86c28c4b640b22c8c04f4b5e5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 13 May 2026 12:35:10 +0530 Subject: [PATCH] dnd fix remote drag source premature removal of remote file data Instead the remote data is removed from the cache on: 1) new drag start 2) window destruction 3) application exit --- kitty/dnd.c | 26 ++++++++++++++++---------- kitty/dnd.h | 2 +- kitty/screen.c | 2 +- kitty/state.c | 2 +- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/kitty/dnd.c b/kitty/dnd.c index 28d237228..edd14bb58 100644 --- a/kitty/dnd.c +++ b/kitty/dnd.c @@ -1254,8 +1254,17 @@ drag_free_remote_item(DragRemoteItem *x) { zero_at_ptr(x); } +static void +delete_basedir_for_remote_items(Window *w) { + if (ds.base_dir_fd_plus_one) { + rmtree_best_effort(".", ds.base_dir_fd_plus_one - 1); + ds.base_dir_fd_plus_one = 0; + } + free(ds.base_dir_for_remote_items); ds.base_dir_for_remote_items = NULL; +} + void -drag_free_offer(Window *w) { +drag_free_offer(Window *w, bool remove_remote_items) { free(ds.mimes_buf); ds.mimes_buf = NULL; ds.bufsz = 0; if (ds.items) { for (size_t i=0; i < ds.num_mimes; i++) { @@ -1285,11 +1294,6 @@ drag_free_offer(Window *w) { ds.state = DRAG_SOURCE_NONE; ds.pre_sent_total_sz = 0; ds.images_sent_total_sz = 0; - if (ds.base_dir_fd_plus_one) { - rmtree_best_effort(".", ds.base_dir_fd_plus_one - 1); - ds.base_dir_fd_plus_one = 0; - } - free(ds.base_dir_for_remote_items); ds.base_dir_for_remote_items = NULL; if (ds.file_promises) { for (size_t i = 0; i < ds.file_promises_count; i++) { drag_free_remote_item(&ds.file_promises[i].ri); @@ -1298,6 +1302,7 @@ drag_free_offer(Window *w) { ds.file_promises = NULL; } ds.file_promises_count = 0; ds.file_promises_capacity = 0; + if (remove_remote_items) delete_basedir_for_remote_items(w); } static void @@ -1315,7 +1320,7 @@ static void cancel_drag(Window *w, int error_code, const char *details) { if (error_code) drag_send_error(w, error_code, details); if (global_state.drag_source.is_active && global_state.drag_source.from_window == w->id) cancel_current_drag_source(); - drag_free_offer(w); + if (error_code) drag_free_offer(w, true); } #define abrt(code, details) { cancel_drag(w, code, details); return; } @@ -1328,7 +1333,7 @@ drag_start_offerring(Window *w, const char *client_machine_id, size_t sz) { void drag_stop_offerring(Window *w) { - drag_free_offer(w); + drag_free_offer(w, true); ds.can_offer = false; ds.is_remote_client = false; } @@ -1515,6 +1520,7 @@ parse_uri_list(Window *w, char *data, const ssize_t sz, size_t *num_uris_out) { void drag_start(Window *w) { if (ds.state != DRAG_SOURCE_BEING_BUILT) abrt(EINVAL, "cannot start drag as drag source is not being built"); + delete_basedir_for_remote_items(w); size_t total_size = 0; for (size_t idx = 0; idx < arraysz(ds.images); idx++) { if (img.sz) { @@ -1638,7 +1644,7 @@ drag_notify(Window *w, DragNotifyType type) { sz += snprintf(buf + sz, sizeof(buf) - sz, ":y=%d", global_state.drag_source.was_canceled ? 1 : 0); break; } queue_payload_to_child(w->id, w->drag_source.client_id, &w->drag_source.pending, buf, sz, NULL, 0, false); - if (type == DRAG_NOTIFY_FINISHED) drag_free_offer(w); + if (type == DRAG_NOTIFY_FINISHED) drag_free_offer(w, false); } int @@ -2304,7 +2310,7 @@ static void destroy_fake_window_contents(Window *w) { // Free window resources without touching GPU objects (none allocated for fake windows). drop_free_data(w); - drag_free_offer(w); + drag_free_offer(w, false); free(w->pending_clicks.clicks); zero_at_ptr(&w->pending_clicks); free(w->buffered_keys.key_data); zero_at_ptr(&w->buffered_keys); Py_CLEAR(w->render_data.screen); diff --git a/kitty/dnd.h b/kitty/dnd.h index d2a172c21..86833d549 100644 --- a/kitty/dnd.h +++ b/kitty/dnd.h @@ -24,7 +24,7 @@ void drop_dispatch_data(Window *w, const char *mime_type, const char *data, ssiz void drop_finish(Window *w); typedef enum { DRAG_NOTIFY_ACCEPTED, DRAG_NOTIFY_ACTION_CHANGED, DRAG_NOTIFY_DROPPED, DRAG_NOTIFY_FINISHED } DragNotifyType; -void drag_free_offer(Window *w); +void drag_free_offer(Window *w, bool remove_remote_items); void drag_add_mimes(Window *w, int allowed_operations, uint32_t client_id, const char *data, size_t sz, bool has_more); void drag_add_pre_sent_data(Window *w, unsigned idx, const uint8_t *payload, size_t sz); void drag_add_image(Window *w, unsigned idx_, int fmt, int width, int height, int opacity, const uint8_t *payload, size_t sz); diff --git a/kitty/screen.c b/kitty/screen.c index b13d9cb04..bcc5579d9 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1573,7 +1573,7 @@ screen_handle_dnd_command(Screen *self, const DnDCommand *cmd_, const uint8_t *p } break; case 'E': { if (cmd->cell_y == -1) { - drag_free_offer(w); + drag_free_offer(w, true); if (global_state.drag_source.is_active && global_state.drag_source.from_window == w->id) { cancel_current_drag_source(); } diff --git a/kitty/state.c b/kitty/state.c index 63f7cffd9..d4a122e47 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -404,7 +404,7 @@ update_os_window_title(OSWindow *os_window) { static void destroy_window(Window *w) { drop_free_data(w); - drag_free_offer(w); + drag_free_offer(w, true); free(w->pending_clicks.clicks); zero_at_ptr(&w->pending_clicks); free(w->buffered_keys.key_data); zero_at_ptr(&w->buffered_keys); Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title);