From dc55052f0cc2fd64af7ae08c74f69b3ee4110193 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Apr 2026 08:33:12 +0530 Subject: [PATCH] Wayland: set drop accepted operations --- glfw/glfw3.h | 1 + glfw/input.c | 2 ++ glfw/internal.h | 1 + glfw/wl_platform.h | 2 ++ glfw/wl_window.c | 20 +++++++++++++++++--- kitty/dnd.c | 14 ++++++-------- kitty/glfw-wrapper.h | 1 + kitty/glfw.c | 20 ++++++++++++++++++-- 8 files changed, 48 insertions(+), 13 deletions(-) diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 83890b617..ca7fa744e 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1407,6 +1407,7 @@ typedef struct GLFWDropEvent { // Positions are only valid for GLFW_DROP_ENTER and GLFW_DROP_MOVE. // They are in window co-ordinates same as for mouse events double xpos, ypos; + struct { GLFWDragOperationType preferred; int allowed; } operation; bool from_self; // Only valid upto GLFW_DROP_DROP ssize_t (*read_data)(GLFWwindow *w, struct GLFWDropEvent* ev, char *buffer, size_t sz); // Only valid for GLFW_DROP_DATA_AVAILABLE void (*finish_drop)(GLFWwindow *w, GLFWDragOperationType op); // Only valid for GLFW_DROP_DROP and GLFW_DROP_DATA_AVAILABLE diff --git a/glfw/input.c b/glfw/input.c index c91eaf026..314c58299 100644 --- a/glfw/input.c +++ b/glfw/input.c @@ -415,8 +415,10 @@ size_t _glfwInputDropEvent(_GLFWwindow *window, GLFWDropEventType type, double x .mimes=mimes, .type=type, .xpos=xpos, .ypos=ypos, .num_mimes=num_mimes, .from_self=from_self, .read_data=type == GLFW_DROP_DATA_AVAILABLE ? _glfwPlatformReadAvailableDropData : NULL, .finish_drop=type == GLFW_DROP_DATA_AVAILABLE || type == GLFW_DROP_DROP ? _glfwPlatformEndDrop : NULL, + .operation.allowed = window->drop_operation.allowed, .operation.preferred = window->drop_operation.preferred, }; window->callbacks.drop_event((GLFWwindow*)window, &ev); + window->drop_operation.preferred = ev.operation.preferred; window->drop_operation.allowed = ev.operation.allowed; return ev.num_mimes; } diff --git a/glfw/internal.h b/glfw/internal.h index ea8f64521..bce35d328 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -461,6 +461,7 @@ struct _GLFWwindow #else const bool swaps_disallowed; #endif + struct { GLFWDragOperationType preferred; int allowed; } drop_operation; struct { GLFWwindowposfun pos; diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 8e8f72ba7..7af11bda3 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -314,6 +314,8 @@ typedef struct _GLFWWaylandDataOffer const char **copy_mimes; // Working copy passed to callbacks; pointers into mimes[] size_t copy_mimes_count; // Count of entries in copy_mimes (accepted count after callback) bool drag_accepted, dropped; + enum wl_data_device_manager_dnd_action preferred; + int allowed; uint32_t serial; struct { id_type watch_id; diff --git a/glfw/wl_window.c b/glfw/wl_window.c index ea0bc5983..47242960f 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -2510,14 +2510,25 @@ static void handle_primary_selection_offer(void *data UNUSED, struct zwp_primary // Helper function to update drop state from callback results static void -update_drop_state(_GLFWWaylandDataOffer *d, _GLFWwindow* window UNUSED, size_t accepted_count) { +update_drop_state(_GLFWWaylandDataOffer *d, _GLFWwindow* window, size_t accepted_count) { d->copy_mimes_count = accepted_count; bool accepted = accepted_count > 0; bool acceptance_changed = (accepted != d->drag_accepted); // The first entry in the accepted (sorted) copy is the preferred MIME. const char* new_preferred_mime = (accepted && d->copy_mimes) ? d->copy_mimes[0] : NULL; bool mime_changed = false; - + enum wl_data_device_manager_dnd_action preferred = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; int allowed = 0; + if (accepted && window->drop_operation.allowed) { + if (window->drop_operation.allowed & GLFW_DRAG_OPERATION_GENERIC) allowed |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + if (window->drop_operation.allowed & GLFW_DRAG_OPERATION_COPY) allowed |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (window->drop_operation.allowed & GLFW_DRAG_OPERATION_MOVE) allowed |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + switch (window->drop_operation.preferred) { + case GLFW_DRAG_OPERATION_GENERIC: preferred = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; break; + case GLFW_DRAG_OPERATION_COPY: preferred = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; break; + case GLFW_DRAG_OPERATION_MOVE: preferred = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; break; + case GLFW_DRAG_OPERATION_NONE: break; + } + } // Check if the preferred MIME changed if (d->mime_for_drop == NULL && new_preferred_mime != NULL) { mime_changed = true; @@ -2526,11 +2537,14 @@ update_drop_state(_GLFWWaylandDataOffer *d, _GLFWwindow* window UNUSED, size_t a } else if (d->mime_for_drop != NULL && new_preferred_mime != NULL) { mime_changed = (strcmp(d->mime_for_drop, new_preferred_mime) != 0); } + const bool op_changed = preferred != d->preferred || allowed != d->allowed; - if (acceptance_changed || mime_changed) { + if (acceptance_changed || mime_changed || op_changed) { d->drag_accepted = accepted; d->mime_for_drop = new_preferred_mime; + d->preferred = preferred; d->allowed = allowed; wl_data_offer_accept(d->id, d->serial, d->mime_for_drop); + wl_data_offer_set_actions(d->id, d->allowed, d->preferred); } } diff --git a/kitty/dnd.c b/kitty/dnd.c index c28e5bdf1..71c3684f1 100644 --- a/kitty/dnd.c +++ b/kitty/dnd.c @@ -305,10 +305,11 @@ drop_move_on_child(Window *w, const char** mimes, size_t num_mimes, bool is_drop 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; + drop_free_accepted_mimes(w); w->drop.accept_in_progress = true; switch(operation) { - case 1: case 2: w->drop.accepted_operation = operation; break; - default: w->drop.accepted_operation = 0; break; + 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; } } if (payload_sz) { @@ -335,17 +336,14 @@ void drop_finish(Window *w) { OSWindow *osw = os_window_for_kitty_window(w->id); if (osw && osw->handle) { - int op = GLFW_DRAG_OPERATION_GENERIC; - if (w->drop.accepted_operation == 1) op = GLFW_DRAG_OPERATION_COPY; - else if (w->drop.accepted_operation == 2) op = GLFW_DRAG_OPERATION_MOVE; - glfwEndDrop(osw->handle, op); + glfwEndDrop(osw->handle, w->drop.accepted_operation); } } 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) return 0; + 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)); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 38d7f433d..fac49dd00 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1135,6 +1135,7 @@ typedef struct GLFWDropEvent { // Positions are only valid for GLFW_DROP_ENTER and GLFW_DROP_MOVE. // They are in window co-ordinates same as for mouse events double xpos, ypos; + struct { GLFWDragOperationType preferred; int allowed; } operation; bool from_self; // Only valid upto GLFW_DROP_DROP ssize_t (*read_data)(GLFWwindow *w, struct GLFWDropEvent* ev, char *buffer, size_t sz); // Only valid for GLFW_DROP_DATA_AVAILABLE void (*finish_drop)(GLFWwindow *w, GLFWDragOperationType op); // Only valid for GLFW_DROP_DROP and GLFW_DROP_DATA_AVAILABLE diff --git a/kitty/glfw.c b/kitty/glfw.c index 89bacf884..aeed6c844 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -801,8 +801,11 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) { os_window->last_drag_event.y = (int)(ev->ypos * os_window->viewport_y_ratio); on_mouse_position_update(ev->xpos, ev->ypos); if (is_client_drop) { + if (ev->type == GLFW_DROP_ENTER) w->drop.accepted_operation = GLFW_DRAG_OPERATION_COPY; drop_move_on_child(w, ev->mimes, ev->num_mimes, false); ev->num_mimes = drop_update_mimes(w, ev->mimes, ev->num_mimes); + ev->operation.allowed = w->drop.accepted_operation; + ev->operation.preferred = w->drop.accepted_operation; return; } call_boss(on_drop_move, "KiiOO", @@ -810,8 +813,21 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) { ev->from_self ? Py_True : Py_False, Py_False); /* fallthrough */ case GLFW_DROP_STATUS_UPDATE: - if (is_client_drop) ev->num_mimes = drop_update_mimes(w, ev->mimes, ev->num_mimes); - else update_allowed_mimes_for_drop(ev); + if (is_client_drop) { + ev->num_mimes = drop_update_mimes(w, ev->mimes, ev->num_mimes); + ev->operation.allowed = w->drop.accepted_operation; + ev->operation.preferred = w->drop.accepted_operation; + } else { + update_allowed_mimes_for_drop(ev); + if (ev->num_mimes == 0) zero_at_ptr(&ev->operation); + else if (is_kitty_ui_drag) { + ev->operation.preferred = GLFW_DRAG_OPERATION_MOVE; + ev->operation.allowed = GLFW_DRAG_OPERATION_MOVE; + } else { + ev->operation.preferred = GLFW_DRAG_OPERATION_MOVE; + ev->operation.allowed = GLFW_DRAG_OPERATION_MOVE; + } + } break; case GLFW_DROP_LEAVE: for (size_t tc = 0; tc < os_window->num_tabs; tc++) {