From accee908aa8f733bd653ebf0522820463b40cc5a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 22 Feb 2026 08:47:27 +0000 Subject: [PATCH] Implement Wayland xdg-toplevel-drag protocol for make_toplevel in _glfwPlatformChangeDragImage Fixes #9544 --- glfw/source-info.json | 1 + glfw/wl_init.c | 4 +++ glfw/wl_platform.h | 7 +++++ glfw/wl_window.c | 69 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/glfw/source-info.json b/glfw/source-info.json index eddfd6674..e98cf502b 100644 --- a/glfw/source-info.json +++ b/glfw/source-info.json @@ -88,6 +88,7 @@ "staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml", "staging/xdg-system-bell/xdg-system-bell-v1.xml", "staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml", + "staging/xdg-toplevel-drag/xdg-toplevel-drag-v1.xml", "kwin-blur-v1.xml", "wlr-layer-shell-unstable-v1.xml", diff --git a/glfw/wl_init.c b/glfw/wl_init.c index 6414e3f32..fbbc402fe 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -675,6 +675,8 @@ static void registryHandleGlobal(void* data UNUSED, _glfw.wl.xdg_system_bell_v1 = wl_registry_bind(registry, name, &xdg_system_bell_v1_interface, 1); } else if (is(xdg_toplevel_tag_manager_v1)) { _glfw.wl.xdg_toplevel_tag_manager_v1 = wl_registry_bind(registry, name, &xdg_toplevel_tag_manager_v1_interface, 1); + } else if (is(xdg_toplevel_drag_manager_v1)) { + _glfw.wl.xdg_toplevel_drag_manager_v1 = wl_registry_bind(registry, name, &xdg_toplevel_drag_manager_v1_interface, 1); } #undef is } @@ -975,6 +977,8 @@ void _glfwPlatformTerminate(void) xdg_system_bell_v1_destroy(_glfw.wl.xdg_system_bell_v1); if (_glfw.wl.xdg_toplevel_tag_manager_v1) xdg_toplevel_tag_manager_v1_destroy(_glfw.wl.xdg_toplevel_tag_manager_v1); + if (_glfw.wl.xdg_toplevel_drag_manager_v1) + xdg_toplevel_drag_manager_v1_destroy(_glfw.wl.xdg_toplevel_drag_manager_v1); if (_glfw.wl.wp_single_pixel_buffer_manager_v1) wp_single_pixel_buffer_manager_v1_destroy(_glfw.wl.wp_single_pixel_buffer_manager_v1); if (_glfw.wl.wp_cursor_shape_manager_v1) diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 66b6ce539..0d6c2e929 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -71,6 +71,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #include "wayland-xdg-toplevel-icon-v1-client-protocol.h" #include "wayland-xdg-system-bell-v1-client-protocol.h" #include "wayland-xdg-toplevel-tag-v1-client-protocol.h" +#include "wayland-xdg-toplevel-drag-v1-client-protocol.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) @@ -346,6 +347,7 @@ typedef struct _GLFWlibraryWayland struct xdg_toplevel_icon_manager_v1* xdg_toplevel_icon_manager_v1; struct xdg_system_bell_v1* xdg_system_bell_v1; struct xdg_toplevel_tag_manager_v1* xdg_toplevel_tag_manager_v1; + struct xdg_toplevel_drag_manager_v1* xdg_toplevel_drag_manager_v1; struct wp_cursor_shape_manager_v1* wp_cursor_shape_manager_v1; struct wp_cursor_shape_device_v1* wp_cursor_shape_device_v1; struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_v1; @@ -415,6 +417,11 @@ typedef struct _GLFWlibraryWayland struct wl_data_source* source; struct wl_surface *drag_icon; struct wp_viewport *drag_viewport; + struct xdg_toplevel_drag_v1 *toplevel_drag; + struct wl_surface *toplevel_surface; + struct xdg_surface *toplevel_xdg_surface; + struct xdg_toplevel *toplevel_xdg_toplevel; + struct wl_buffer *toplevel_buffer; struct { const char *mime_type; int fd; diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 763ee6ce4..efc9e5ba6 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -3078,6 +3078,42 @@ GLFWAPI bool glfwWaylandBeep(GLFWwindow *handle) { } // Drag source {{{ + +static void +drag_toplevel_xdg_surface_configure(void *data UNUSED, struct xdg_surface *surface, uint32_t serial) { + xdg_surface_ack_configure(surface, serial); + if (_glfw.wl.drag.toplevel_buffer) { + wl_surface_attach(_glfw.wl.drag.toplevel_surface, _glfw.wl.drag.toplevel_buffer, 0, 0); + wl_surface_damage(_glfw.wl.drag.toplevel_surface, 0, 0, INT32_MAX, INT32_MAX); + wl_buffer_destroy(_glfw.wl.drag.toplevel_buffer); + _glfw.wl.drag.toplevel_buffer = NULL; + } + if (_glfw.wl.drag.toplevel_surface) wl_surface_commit(_glfw.wl.drag.toplevel_surface); +} + +static const struct xdg_surface_listener drag_toplevel_xdg_surface_listener = { + .configure = drag_toplevel_xdg_surface_configure, +}; + +static void drag_toplevel_configure(void *data UNUSED, struct xdg_toplevel *toplevel UNUSED, + int32_t width UNUSED, int32_t height UNUSED, + struct wl_array *states UNUSED) {} +static void drag_toplevel_close(void *data UNUSED, struct xdg_toplevel *toplevel UNUSED) {} +#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION +static void drag_toplevel_configure_bounds(void *data UNUSED, struct xdg_toplevel *toplevel UNUSED, + int32_t width UNUSED, int32_t height UNUSED) {} +static void drag_toplevel_wm_capabilities(void *data UNUSED, struct xdg_toplevel *toplevel UNUSED, + struct wl_array *caps UNUSED) {} +#endif +static const struct xdg_toplevel_listener drag_toplevel_listener = { + .configure = drag_toplevel_configure, + .close = drag_toplevel_close, +#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION + .configure_bounds = drag_toplevel_configure_bounds, + .wm_capabilities = drag_toplevel_wm_capabilities, +#endif +}; + static void cancel_drag(GLFWDragEventType type) { _GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id); @@ -3191,7 +3227,28 @@ add_drag_watch(int fd) { int _glfwPlatformChangeDragImage(const GLFWimage *thumbnail, int make_toplevel) { - (void)make_toplevel; // TODO: Implement me + if (make_toplevel && _glfw.wl.drag.toplevel_drag && !_glfw.wl.drag.toplevel_surface + && _glfw.wl.wmBase && thumbnail && thumbnail->pixels) { + _glfw.wl.drag.toplevel_surface = wl_compositor_create_surface(_glfw.wl.compositor); + if (_glfw.wl.drag.toplevel_surface) { + _glfw.wl.drag.toplevel_xdg_surface = xdg_wm_base_get_xdg_surface( + _glfw.wl.wmBase, _glfw.wl.drag.toplevel_surface); + if (_glfw.wl.drag.toplevel_xdg_surface) { + xdg_surface_add_listener(_glfw.wl.drag.toplevel_xdg_surface, + &drag_toplevel_xdg_surface_listener, NULL); + _glfw.wl.drag.toplevel_xdg_toplevel = xdg_surface_get_toplevel( + _glfw.wl.drag.toplevel_xdg_surface); + if (_glfw.wl.drag.toplevel_xdg_toplevel) { + xdg_toplevel_add_listener(_glfw.wl.drag.toplevel_xdg_toplevel, + &drag_toplevel_listener, NULL); + _glfw.wl.drag.toplevel_buffer = createShmBuffer(thumbnail, false, true); + xdg_toplevel_drag_v1_attach(_glfw.wl.drag.toplevel_drag, + _glfw.wl.drag.toplevel_xdg_toplevel, 0, 0); + wl_surface_commit(_glfw.wl.drag.toplevel_surface); + } + } + } + } if (!_glfw.wl.drag.drag_icon || !thumbnail || !thumbnail->pixels) return 0; struct wl_buffer* icon_buffer = createShmBuffer(thumbnail, false, true); if (!icon_buffer) return ENOMEM; @@ -3308,6 +3365,11 @@ void _glfwPlatformFreeDragSourceData(void) { if (_glfw.wl.drag.drag_viewport) wp_viewport_destroy(_glfw.wl.drag.drag_viewport); if (_glfw.wl.drag.drag_icon) wl_surface_destroy(_glfw.wl.drag.drag_icon); + if (_glfw.wl.drag.toplevel_drag) xdg_toplevel_drag_v1_destroy(_glfw.wl.drag.toplevel_drag); + if (_glfw.wl.drag.toplevel_buffer) wl_buffer_destroy(_glfw.wl.drag.toplevel_buffer); + if (_glfw.wl.drag.toplevel_xdg_toplevel) xdg_toplevel_destroy(_glfw.wl.drag.toplevel_xdg_toplevel); + if (_glfw.wl.drag.toplevel_xdg_surface) xdg_surface_destroy(_glfw.wl.drag.toplevel_xdg_surface); + if (_glfw.wl.drag.toplevel_surface) wl_surface_destroy(_glfw.wl.drag.toplevel_surface); if (_glfw.wl.drag.source) wl_data_source_destroy(_glfw.wl.drag.source); if (_glfw.wl.drag.data_requests) { for (size_t i = 0; i < _glfw.wl.drag.count; i++) { @@ -3377,6 +3439,11 @@ _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) { } } } + // Create xdg_toplevel_drag_v1 before starting the drag (must precede start_drag) + if (_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); + } // Start the drag operation wl_data_device_start_drag(_glfw.wl.dataDevice, _glfw.wl.drag.source, window->wl.surface, _glfw.wl.drag.drag_icon, _glfw.wl.pointer_serial);