API to start a drag

This commit is contained in:
Kovid Goyal
2026-02-07 13:50:16 +05:30
parent 5ea35cbbfc
commit 07a9f2bcaa
11 changed files with 158 additions and 54 deletions

2
glfw/glfw3.h vendored
View File

@@ -2068,7 +2068,7 @@ typedef struct GLFWimage
int height;
/*! The pixel data of this image, arranged left-to-right, top-to-bottom.
*/
unsigned char* pixels;
const unsigned char* pixels;
} GLFWimage;
/*! @brief Gamepad input state

4
glfw/wl_init.c vendored
View File

@@ -608,9 +608,7 @@ static void registryHandleGlobal(void* data UNUSED,
else if (is(wl_data_device_manager))
{
_glfw.wl.dataDeviceManager =
wl_registry_bind(registry, name,
&wl_data_device_manager_interface,
1);
wl_registry_bind(registry, name, &wl_data_device_manager_interface, 3);
if (_glfw.wl.seat && _glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) {
_glfwSetupWaylandDataDevice();
}

4
glfw/wl_platform.h vendored
View File

@@ -414,8 +414,10 @@ typedef struct _GLFWlibraryWayland
struct wl_data_source* source;
char** mimes; // Array of MIME type strings
int mime_count; // Number of MIME types
_GLFWwindow* window; // Window that initiated the drag
GLFWid window_id; // Window that initiated the drag
GLFWDragSourceData* current_request; // Current data request being processed
struct wl_surface *drag_icon;
struct wp_viewport *drag_viewport;
} drag;
} _GLFWlibraryWayland;

86
glfw/wl_window.c vendored
View File

@@ -3006,7 +3006,8 @@ GLFWAPI bool glfwWaylandBeep(GLFWwindow *handle) {
// Drag operation implementation
static void cleanup_drag_source_data(GLFWDragSourceData* data) {
static void
cleanup_drag_source_data(GLFWDragSourceData* data) {
if (!data) return;
if (data->write_fd >= 0) {
close(data->write_fd);
@@ -3016,11 +3017,11 @@ static void cleanup_drag_source_data(GLFWDragSourceData* data) {
free(data);
}
static void cleanup_drag(void) {
static void
cleanup_drag(struct wl_data_source *source) {
// Notify the application that the drag source is closed
if (_glfw.wl.drag.window && _glfw.wl.drag.window->callbacks.dragSource) {
_glfwInputDragSourceRequest(_glfw.wl.drag.window, NULL, NULL);
}
_GLFWwindow *window = _glfwWindowForId(_glfw.wl.drag.window_id);
if (window && window->callbacks.dragSource) _glfwInputDragSourceRequest(window, NULL, NULL);
// Clean up any pending data request
if (_glfw.wl.drag.current_request) {
@@ -3029,18 +3030,23 @@ static void cleanup_drag(void) {
}
// Clean up MIME type strings
for (int i = 0; i < _glfw.wl.drag.mime_count; i++) {
free(_glfw.wl.drag.mimes[i]);
}
for (int i = 0; i < _glfw.wl.drag.mime_count; i++) free(_glfw.wl.drag.mimes[i]);
free(_glfw.wl.drag.mimes);
_glfw.wl.drag.mimes = NULL;
_glfw.wl.drag.mime_count = 0;
_glfw.wl.drag.window = NULL;
_glfw.wl.drag.window_id = 0;
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);
_glfw.wl.drag.drag_icon = NULL; _glfw.wl.drag.drag_viewport = NULL;
if (_glfw.wl.drag.source && _glfw.wl.drag.source != source) wl_data_source_destroy(_glfw.wl.drag.source);
_glfw.wl.drag.source = NULL;
if (source) wl_data_source_destroy(source);
}
static void
drag_source_send(void *data UNUSED, struct wl_data_source *source UNUSED, const char *mime_type, int fd) {
if (!_glfw.wl.drag.window) {
_GLFWwindow *window = _glfwWindowForId(_glfw.wl.drag.window_id);
if (!window) {
close(fd);
return;
}
@@ -3052,7 +3058,7 @@ drag_source_send(void *data UNUSED, struct wl_data_source *source UNUSED, const
return;
}
request->window_id = _glfw.wl.drag.window ? _glfw.wl.drag.window->id : 0;
request->window_id = _glfw.wl.drag.window_id;
request->mime_type = _glfw_strdup(mime_type);
request->write_fd = fd;
request->finished = false;
@@ -3064,25 +3070,16 @@ drag_source_send(void *data UNUSED, struct wl_data_source *source UNUSED, const
}
// Store as current request
if (_glfw.wl.drag.current_request) {
cleanup_drag_source_data(_glfw.wl.drag.current_request);
}
if (_glfw.wl.drag.current_request) cleanup_drag_source_data(_glfw.wl.drag.current_request);
_glfw.wl.drag.current_request = request;
// Notify the application via callback
_glfwInputDragSourceRequest(_glfw.wl.drag.window, mime_type, request);
_glfwInputDragSourceRequest(window, mime_type, request);
}
static void
drag_source_cancelled(void *data UNUSED, struct wl_data_source *source) {
// Clean up drag data
if (_glfw.wl.drag.source == source) {
cleanup_drag();
_glfw.wl.drag.source = NULL;
}
if (source) {
wl_data_source_destroy(source);
}
cleanup_drag(source);
}
static void
@@ -3113,10 +3110,7 @@ static const struct wl_data_source_listener drag_source_listener = {
void
_glfwPlatformCancelDrag(_GLFWwindow* window UNUSED) {
if (_glfw.wl.drag.source) {
drag_source_cancelled(NULL, _glfw.wl.drag.source);
_glfw.wl.drag.source = NULL;
}
cleanup_drag(_glfw.wl.drag.source);
}
int
@@ -3154,7 +3148,7 @@ _glfwPlatformStartDrag(_GLFWwindow* window, const char* const* mime_types, int m
// Allocate storage for MIME types
_glfw.wl.drag.mimes = calloc(mime_count, sizeof(char*));
_glfw.wl.drag.mime_count = mime_count;
_glfw.wl.drag.window = window;
_glfw.wl.drag.window_id = window->id;
if (!_glfw.wl.drag.mimes) {
_glfwPlatformCancelDrag(window);
@@ -3176,33 +3170,33 @@ _glfwPlatformStartDrag(_GLFWwindow* window, const char* const* mime_types, int m
wl_data_source_add_listener(_glfw.wl.drag.source, &drag_source_listener, NULL);
// Set up the drag icon surface if thumbnail is provided
struct wl_surface* icon_surface = NULL;
struct wl_buffer* icon_buffer = NULL;
if (thumbnail && thumbnail->pixels) {
icon_surface = wl_compositor_create_surface(_glfw.wl.compositor);
if (icon_surface) {
struct wl_buffer* icon_buffer = NULL;
_glfw.wl.drag.drag_icon = wl_compositor_create_surface(_glfw.wl.compositor);
if (_glfw.wl.drag.drag_icon) {
icon_buffer = createShmBuffer(thumbnail, false, true);
if (icon_buffer) {
wl_surface_attach(icon_surface, icon_buffer, 0, 0);
wl_surface_commit(icon_surface);
if (_glfw.wl.wp_viewporter) {
double f_scale = _glfwWaylandWindowScale(window);
int logical_width = (int)(thumbnail->width / f_scale);
int logical_height = (int)(thumbnail->height / f_scale);
_glfw.wl.drag.drag_viewport = wp_viewporter_get_viewport(
_glfw.wl.wp_viewporter, _glfw.wl.drag.drag_icon);
wp_viewport_set_destination(_glfw.wl.drag.drag_viewport, logical_width, logical_height);
} else {
int scale = _glfwWaylandIntegerWindowScale(window);
wl_surface_set_buffer_scale(_glfw.wl.drag.drag_icon, scale);
}
wl_surface_attach(_glfw.wl.drag.drag_icon, icon_buffer, 0, 0);
wl_surface_commit(_glfw.wl.drag.drag_icon);
wl_buffer_destroy(icon_buffer);
}
}
}
// Start the drag operation
wl_data_device_start_drag(_glfw.wl.dataDevice, _glfw.wl.drag.source,
window->wl.surface, icon_surface,
wl_data_device_start_drag(_glfw.wl.dataDevice, _glfw.wl.drag.source, window->wl.surface, _glfw.wl.drag.drag_icon,
_glfw.wl.pointer_serial);
// Clean up icon resources (the compositor takes ownership)
if (icon_buffer) {
wl_buffer_destroy(icon_buffer);
}
if (icon_surface) {
wl_surface_destroy(icon_surface);
}
return 0;
}

2
glfw/x11_window.c vendored
View File

@@ -2438,7 +2438,7 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
for (j = 0; j < images[i].width * images[i].height; j++)
{
unsigned char *p = images->pixels + j * 4;
const unsigned char *p = images->pixels + j * 4;
const unsigned char r = *p++, g = *p++, b = *p++, a = *p++;
*target++ = a << 24 | (r << 16) | (g << 8) | b;
}