From 021377c1b6a2869832e76b00fabac8cf93e875da Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 4 May 2026 22:53:05 +0530 Subject: [PATCH] Yet another workaround for Wayland's awful design --- glfw/glfw3.h | 1 + glfw/wl_window.c | 16 +++++++++++----- kitty/glfw-wrapper.h | 1 + kitty/glfw.c | 8 +++++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/glfw/glfw3.h b/glfw/glfw3.h index ca7fa744e..724f58ce1 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1840,6 +1840,7 @@ typedef struct GLFWDragEvent { const char *data; size_t data_sz; int err_num; // POSIX error code indicating failure fetching data GLFWDragOperationType action; // can be 0 indicating no action + bool drop_maybe_a_cancel; // Happens on wayland compositors that dont implement top-level drag } GLFWDragEvent; typedef void (* GLFWdragsourcefun)(GLFWwindow* window, GLFWDragEvent *ev); diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 3e70903cd..4d2fa7150 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -3190,15 +3190,17 @@ static const struct xdg_toplevel_listener drag_toplevel_listener = { }; static void -cancel_drag(GLFWDragEventType type) { +cancel_drag2(GLFWDragEventType type, bool maybe_a_cancel) { _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id); if (window) { - GLFWDragEvent ev = {.type=type}; + GLFWDragEvent ev = {.type=type, .drop_maybe_a_cancel=maybe_a_cancel}; _glfwInputDragSourceRequest(window, &ev); } _glfwFreeDragSourceData(); } +static void cancel_drag(GLFWDragEventType type) { cancel_drag2(type, false); } + #define dr _glfw.wl.drag.data_requests[i] static void @@ -3424,9 +3426,13 @@ drag_source_cancelled(void *data UNUSED, struct wl_data_source *source UNUSED) { // impossible to distinguish between drag cancelled and dropped but not // accepted. https://gitlab.freedesktop.org/wayland/wayland/-/issues/140 // so we assume this is a drop unless we are in top-level mode. Sigh. - const GLFWDragEventType t = _glfw.wl.drag.toplevel_xdg_toplevel ? GLFW_DRAG_CANCELLED : GLFW_DRAG_DROPPED; - debug_input("Drag source cancelled: is_drop: %d\n", t == GLFW_DRAG_DROPPED); - cancel_drag(t); + if (_glfw.wl.drag.toplevel_xdg_toplevel) { + debug_input("Drag source cancelled\n"); + cancel_drag(GLFW_DRAG_CANCELLED); + } else { + debug_input("Drag source cancelled or dropped on something that doesnt accept it\n"); + cancel_drag2(GLFW_DRAG_DROPPED, true); + } } diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index fac49dd00..7471002dc 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1568,6 +1568,7 @@ typedef struct GLFWDragEvent { const char *data; size_t data_sz; int err_num; // POSIX error code indicating failure fetching data GLFWDragOperationType action; // can be 0 indicating no action + bool drop_maybe_a_cancel; // Happens on wayland compositors that dont implement top-level drag } GLFWDragEvent; typedef void (* GLFWdragsourcefun)(GLFWwindow* window, GLFWDragEvent *ev); diff --git a/kitty/glfw.c b/kitty/glfw.c index 8257075a7..5eee172d1 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -943,8 +943,14 @@ drag_source_callback(GLFWwindow *window UNUSED, GLFWDragEvent *ev) { if (ds.from_window && (w = window_for_window_id(ds.from_window)) && w->drag_source.state) { is_client_drag = true; } + // On Wayland, wen compositor doesnt support top level drag protocol we get + // a drop event for what is either a cancel or a drop on something that + // does not accept the drop. In both of these cases we need to send the + // client a drop cancel. + GLFWDragEventType t = ev->type; + if (is_client_drag && ev->drop_maybe_a_cancel) t = GLFW_DRAG_CANCELLED; - switch (ev->type) { + switch (t) { case GLFW_DRAG_DATA_REQUEST: if (is_client_drag) { ev->err_num = 0;