Wayland: Implement ridiculous workaround for broken dnd

Wayland is *the worst* window system I have ever had the
displeasure of trying to support.
This commit is contained in:
Kovid Goyal
2026-02-22 16:24:46 +05:30
parent 322a51da98
commit 9f2a4ad2dd
11 changed files with 50 additions and 26 deletions

2
glfw/glfw3.h vendored
View File

@@ -4984,7 +4984,7 @@ GLFWAPI GLFWdragsourcefun glfwSetDragSourceCallback(GLFWwindow* window, GLFWdrag
// Start a drag. If called with operations == -1 indicates that previously
// requested data via GLFW_DRAG_DATA_REQUEST is ready. operations == -2 means
// that the drag image is changed.
GLFWAPI int glfwStartDrag(GLFWwindow* window, const GLFWDragSourceItem *items, size_t mime_count, const GLFWimage* thumbnail, int operations);
GLFWAPI int glfwStartDrag(GLFWwindow* window, const GLFWDragSourceItem *items, size_t mime_count, const GLFWimage* thumbnail, int operations, bool needs_toplevel_on_wayland);
/*! @brief Returns whether the specified joystick is present.
*

3
glfw/input.c vendored
View File

@@ -1161,7 +1161,7 @@ _glfwFreeDragSourceData(void) {
}
GLFWAPI int
glfwStartDrag(GLFWwindow* handle, const GLFWDragSourceItem *items, size_t item_count, const GLFWimage* thumbnail, int operations) {
glfwStartDrag(GLFWwindow* handle, const GLFWDragSourceItem *items, size_t item_count, const GLFWimage* thumbnail, int operations, bool needs_toplevel_on_wayland) {
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(EINVAL);
@@ -1188,6 +1188,7 @@ glfwStartDrag(GLFWwindow* handle, const GLFWDragSourceItem *items, size_t item_c
}
_glfw.drag.window_id = window->id;
_glfw.drag.operations = operations;
_glfw.drag.needs_toplevel_on_wayland = needs_toplevel_on_wayland;
int ans = _glfwPlatformStartDrag(window, thumbnail);
if (ans != 0) _glfwFreeDragSourceData();
return ans;

1
glfw/internal.h vendored
View File

@@ -668,6 +668,7 @@ struct _GLFWlibrary
struct {
GLFWDragSourceItem *items; size_t item_count;
GLFWid window_id, instance_id; int operations;
bool needs_toplevel_on_wayland;
} drag;
};

2
glfw/wl_init.c vendored
View File

@@ -789,7 +789,7 @@ get_compositor_missing_capabilities(void) {
C(single_pixel_buffer, wp_single_pixel_buffer_manager_v1); C(preferred_scale, has_preferred_buffer_scale);
C(idle_inhibit, idle_inhibit_manager); C(icon, xdg_toplevel_icon_manager_v1); C(bell, xdg_system_bell_v1);
C(window-tag, xdg_toplevel_tag_manager_v1); C(keyboard_shortcuts_inhibit, keyboard_shortcuts_inhibit_manager);
C(key-repeat, has_key_repeat_events);
C(key-repeat, has_key_repeat_events); C(top_level_drag, xdg_toplevel_drag_manager_v1);
#define P(x) p += snprintf(p, sizeof(buf) - (p - buf), "%s ", x);
if (_glfw.wl.xdg_wm_base_version < 6) P("window-state-suspended");
if (_glfw.wl.xdg_wm_base_version < 5) P("window-capabilities");

6
glfw/wl_window.c vendored
View File

@@ -3325,8 +3325,8 @@ drag_source_cancelled(void *data UNUSED, struct wl_data_source *source UNUSED) {
// Uber competent Wayland people contravene their own spec and make it
// 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. Sigh.
cancel_drag(GLFW_DRAG_DROPPED);
// so we assume this is a drop unless we are in top-level mode. Sigh.
cancel_drag(_glfw.wl.drag.toplevel_xdg_toplevel ? GLFW_DRAG_CANCELLED : GLFW_DRAG_DROPPED);
}
@@ -3411,7 +3411,7 @@ _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {
wl_surface_set_buffer_scale(_glfw.wl.drag.drag_icon, scale);
}
if (_glfw.wl.xdg_toplevel_drag_manager_v1) {
if (_glfw.drag.needs_toplevel_on_wayland && _glfw.wl.xdg_toplevel_drag_manager_v1) {
_glfw.wl.drag.toplevel_drag = xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(
_glfw.wl.xdg_toplevel_drag_manager_v1, _glfw.wl.drag.source);
if (!_glfw.wl.drag.toplevel_drag) return ENOMEM;