Implement Wayland xdg-toplevel-drag protocol for make_toplevel in _glfwPlatformChangeDragImage

Fixes #9544
This commit is contained in:
copilot-swe-agent[bot]
2026-02-22 08:47:27 +00:00
committed by Kovid Goyal
parent 6d59469cf1
commit accee908aa
4 changed files with 80 additions and 1 deletions

View File

@@ -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",

4
glfw/wl_init.c vendored
View File

@@ -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)

7
glfw/wl_platform.h vendored
View File

@@ -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;

69
glfw/wl_window.c vendored
View File

@@ -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);