Fix self drag not working under Wayland

Fixes #9483
This commit is contained in:
copilot-swe-agent[bot] 2026-02-07 08:32:05 +00:00 • committed by Kovid Goyal
parent 3839dcc082
commit 7ca6fd66f8
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
11 changed files with 421 additions and 45 deletions

View file

@ -179,6 +179,11 @@ typedef struct _GLFWwindowNS
// Drag source state
char** sourceMimes; // Array of MIME type strings for drag source
int sourceMimeCount; // Number of source MIME types
// Pending drag source data requests (for cleanup on cancellation)
GLFWDragSourceData** pendingDragSourceData;
int pendingDragSourceDataCount;
int pendingDragSourceDataCapacity;
} _GLFWwindowNS;
// Cocoa-specific global data

View file

@ -770,6 +770,92 @@ typedef struct {
int errorCode; // Error code if any
} GLFWFilePromiseState;
// Helper function to clean up a single drag source data
static void
cleanup_ns_drag_source_data(GLFWDragSourceData* data) {
if (!data) return;
if (data->platform_data) {
GLFWFilePromiseState* state = (GLFWFilePromiseState*)data->platform_data;
// If the data wasn't finished, call completion with error
if (!data->finished && state->completionHandler) {
NSError* error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ECANCELED userInfo:nil];
state->completionHandler(error);
Block_release(state->completionHandler);
state->completionHandler = nil;
}
if (state->fileHandle) {
@try {
[state->fileHandle closeFile];
} @catch (NSException* e) {
(void)e;
}
[state->fileHandle release];
state->fileHandle = nil;
}
if (state->destinationURL) {
[state->destinationURL release];
state->destinationURL = nil;
}
free(state);
}
free(data->mime_type);
free(data);
}
// Add a drag source data to the pending array for tracking
static bool
add_ns_pending_drag_source_data(_GLFWwindow* window, GLFWDragSourceData* data) {
if (!window || !data) return false;
// Grow array if needed
if (window->ns.pendingDragSourceDataCount >= window->ns.pendingDragSourceDataCapacity) {
// Cap maximum capacity to prevent excessive memory use
if (window->ns.pendingDragSourceDataCapacity >= 512) {
return false;
}
int new_capacity = window->ns.pendingDragSourceDataCapacity ? window->ns.pendingDragSourceDataCapacity * 2 : 4;
GLFWDragSourceData** new_array = realloc(window->ns.pendingDragSourceData,
new_capacity * sizeof(GLFWDragSourceData*));
if (!new_array) return false;
window->ns.pendingDragSourceData = new_array;
window->ns.pendingDragSourceDataCapacity = new_capacity;
}
window->ns.pendingDragSourceData[window->ns.pendingDragSourceDataCount++] = data;
return true;
}
// Remove a specific drag source data from the pending array
static void
remove_ns_pending_drag_source_data(_GLFWwindow* window, GLFWDragSourceData* data) {
if (!window || !data) return;
for (int i = 0; i < window->ns.pendingDragSourceDataCount; i++) {
if (window->ns.pendingDragSourceData[i] == data) {
// Shift remaining elements
for (int j = i; j < window->ns.pendingDragSourceDataCount - 1; j++) {
window->ns.pendingDragSourceData[j] = window->ns.pendingDragSourceData[j + 1];
}
window->ns.pendingDragSourceDataCount--;
return;
}
}
}
// Clean up all pending drag source data for a window
static void
cleanup_all_ns_pending_drag_source_data(_GLFWwindow* window) {
if (!window) return;
for (int i = 0; i < window->ns.pendingDragSourceDataCount; i++) {
cleanup_ns_drag_source_data(window->ns.pendingDragSourceData[i]);
}
free(window->ns.pendingDragSourceData);
window->ns.pendingDragSourceData = NULL;
window->ns.pendingDragSourceDataCount = 0;
window->ns.pendingDragSourceDataCapacity = 0;
}
@interface GLFWFilePromiseProviderDelegate : NSObject <NSFilePromiseProviderDelegate>
{
GLFWid windowId;
@ -876,6 +962,16 @@ typedef struct {
return;
}
// Track this source data for cleanup on cancellation
if (!add_ns_pending_drag_source_data(window, source_data)) {
// Call completion handler with memory error before cleanup
completionHandler([NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]);
// Mark as finished to prevent cleanup_ns_drag_source_data from calling completionHandler again
source_data->finished = true;
cleanup_ns_drag_source_data(source_data);
return;
}
// Notify the application via callback - this will trigger glfwSendDragData calls
_glfwInputDragSourceRequest(window, mimeType, source_data);
@ -1656,7 +1752,10 @@ static void freeFilteredDragMimes(_GLFWwindow* window, int old_count, int new_co
drop_data->eof_reached = false;
drop_data->current_data = NULL;
drop_data->data_offset = 0;
_glfwInputDrop(window, drop_data);
// Check if the drop is from this application
// draggingSource returns the source object if the drag started in this application
bool from_self = ([sender draggingSource] != nil);
_glfwInputDrop(window, drop_data, from_self);
// Note: drop_data is NOT freed here - application must call glfwFinishDrop
return YES;
}
@ -1685,6 +1784,8 @@ static void freeFilteredDragMimes(_GLFWwindow* window, int old_count, int new_co
(void)session;
(void)screenPoint;
(void)operation;
// Clean up all pending drag source data
cleanup_all_ns_pending_drag_source_data(window);
// Notify the application that the drag source is closed
_glfwInputDragSourceRequest(window, NULL, NULL);
// Clean up source MIME types
@ -3908,6 +4009,8 @@ void _glfwCocoaPostEmptyEvent(void) {
}
void _glfwPlatformCancelDrag(_GLFWwindow* window) {
// Clean up all pending drag source data
cleanup_all_ns_pending_drag_source_data(window);
// Clean up source MIME types
for (int i = 0; i < window->ns.sourceMimeCount; i++) {
free(window->ns.sourceMimes[i]);
@ -4056,11 +4159,13 @@ ssize_t _glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* d
state->completionHandler = nil;
}
// Clean up
[state->fileHandle release];
state->fileHandle = nil;
[state->destinationURL release];
state->destinationURL = nil;
// Remove from pending list and clean up
_GLFWwindow* window = _glfwWindowForId(source_data->window_id);
if (window) {
remove_ns_pending_drag_source_data(window, source_data);
}
// source_data->finished is true, so cleanup_ns_drag_source_data won't call completionHandler again
cleanup_ns_drag_source_data(source_data);
return 0;
}
@ -4086,11 +4191,13 @@ ssize_t _glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* d
state->completionHandler = nil;
}
// Clean up
[state->fileHandle release];
state->fileHandle = nil;
[state->destinationURL release];
state->destinationURL = nil;
// Remove from pending list and clean up
_GLFWwindow* window = _glfwWindowForId(source_data->window_id);
if (window) {
remove_ns_pending_drag_source_data(window, source_data);
}
// source_data->finished is true, so cleanup_ns_drag_source_data won't call completionHandler again
cleanup_ns_drag_source_data(source_data);
return 0;
}

9
glfw/glfw3.h vendored
View file

@ -1800,6 +1800,12 @@ typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
* responsible for calling glfwFinishDrop when it has finished reading
* the dropped data, even if reading fails or is not needed.
*
* @param[in] window The window that received the drop.
* @param[in] drop Opaque drop data pointer (heap-allocated).
* @param[in] from_self true if the drop originated from this application
* (i.e., the application is both the drag source and drop target), false
* if the drop came from an external application.
*
* @sa @ref path_drop
* @sa @ref glfwSetDropCallback
* @sa @ref glfwGetDropMimeTypes
@ -1807,10 +1813,11 @@ typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
* @sa @ref glfwFinishDrop
*
* @since Changed in version 4.0 to receive opaque drop data pointer.
* @since Changed in version 4.0 to receive from_self parameter.
*
* @ingroup input
*/
typedef void (* GLFWdropfun)(GLFWwindow*, GLFWDropData*);
typedef void (* GLFWdropfun)(GLFWwindow*, GLFWDropData*, bool from_self);
/*! @brief Drag event types.
*

4
glfw/input.c vendored
View file

@ -404,10 +404,10 @@ void _glfwInputCursorEnter(_GLFWwindow* window, bool entered)
// Notifies shared code of files or directories dropped on a window
//
void _glfwInputDrop(_GLFWwindow* window, GLFWDropData* drop)
void _glfwInputDrop(_GLFWwindow* window, GLFWDropData* drop, bool from_self)
{
if (window->callbacks.drop)
window->callbacks.drop((GLFWwindow*) window, drop);
window->callbacks.drop((GLFWwindow*) window, drop, from_self);
}
// Notifies shared code of a drag event

2
glfw/internal.h vendored
View file

@ -857,7 +857,7 @@ void _glfwInputScroll(_GLFWwindow* window, const GLFWScrollEvent *ev);
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
void _glfwInputCursorEnter(_GLFWwindow* window, bool entered);
void _glfwInputDrop(_GLFWwindow* window, GLFWDropData* drop);
void _glfwInputDrop(_GLFWwindow* window, GLFWDropData* drop, bool from_self);
int _glfwInputDragEvent(_GLFWwindow* window, int event, double xpos, double ypos, const char** mime_types, int* mime_count);
void _glfwInputDragSourceRequest(_GLFWwindow* window, const char* mime_type, GLFWDragSourceData* source_data);

4
glfw/wl_platform.h vendored
View file

@ -415,7 +415,9 @@ typedef struct _GLFWlibraryWayland
char** mimes; // Array of MIME type strings
int mime_count; // Number of MIME types
GLFWid window_id; // Window that initiated the drag
GLFWDragSourceData* current_request; // Current data request being processed
GLFWDragSourceData** pending_requests; // Array of pending data requests
int pending_request_count; // Number of pending requests
int pending_request_capacity; // Capacity of the pending requests array
struct wl_surface *drag_icon;
struct wp_viewport *drag_viewport;
} drag;

89
glfw/wl_window.c vendored
View file

@ -2554,7 +2554,9 @@ drop(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED) {
destroy_data_offer(offer);
drop_data->eof_reached = false;
memset(offer, 0, sizeof(offer[0]));
_glfwInputDrop(window, drop_data);
// Check if the drop is from this application
bool from_self = (_glfw.wl.drag.window_id != 0);
_glfwInputDrop(window, drop_data, from_self);
// Note: drop_data is NOT freed here - application must call glfwFinishDrop
break;
}
@ -3007,7 +3009,7 @@ GLFWAPI bool glfwWaylandBeep(GLFWwindow *handle) {
// Drag operation implementation
static void
cleanup_drag_source_data(GLFWDragSourceData* data) {
cleanup_wl_drag_source_data(GLFWDragSourceData* data) {
if (!data) return;
if (data->write_fd >= 0) {
close(data->write_fd);
@ -3017,17 +3019,74 @@ cleanup_drag_source_data(GLFWDragSourceData* data) {
free(data);
}
// Remove a finished request from the pending requests array
static void
remove_wl_pending_request(int index) {
if (index < 0 || index >= _glfw.wl.drag.pending_request_count) return;
cleanup_wl_drag_source_data(_glfw.wl.drag.pending_requests[index]);
// Shift remaining elements
for (int i = index; i < _glfw.wl.drag.pending_request_count - 1; i++) {
_glfw.wl.drag.pending_requests[i] = _glfw.wl.drag.pending_requests[i + 1];
}
_glfw.wl.drag.pending_request_count--;
}
// Clean up all finished requests from the pending requests array
static void
cleanup_wl_finished_requests(void) {
for (int i = _glfw.wl.drag.pending_request_count - 1; i >= 0; i--) {
if (_glfw.wl.drag.pending_requests[i]->finished) {
remove_wl_pending_request(i);
}
}
}
// Clean up all pending requests
static void
cleanup_all_wl_pending_requests(void) {
for (int i = 0; i < _glfw.wl.drag.pending_request_count; i++) {
cleanup_wl_drag_source_data(_glfw.wl.drag.pending_requests[i]);
}
free(_glfw.wl.drag.pending_requests);
_glfw.wl.drag.pending_requests = NULL;
_glfw.wl.drag.pending_request_count = 0;
_glfw.wl.drag.pending_request_capacity = 0;
}
// Add a request to the pending requests array
static bool
add_wl_pending_request(GLFWDragSourceData* request) {
// First, clean up any finished requests to make room
cleanup_wl_finished_requests();
// Grow the array if necessary
if (_glfw.wl.drag.pending_request_count >= _glfw.wl.drag.pending_request_capacity) {
// Cap maximum capacity to prevent excessive memory use
if (_glfw.wl.drag.pending_request_capacity >= 512) {
return false;
}
int new_capacity = _glfw.wl.drag.pending_request_capacity ? _glfw.wl.drag.pending_request_capacity * 2 : 4;
GLFWDragSourceData** new_array = realloc(_glfw.wl.drag.pending_requests,
new_capacity * sizeof(GLFWDragSourceData*));
if (!new_array) return false;
_glfw.wl.drag.pending_requests = new_array;
_glfw.wl.drag.pending_request_capacity = new_capacity;
}
_glfw.wl.drag.pending_requests[_glfw.wl.drag.pending_request_count++] = request;
return true;
}
static void
cleanup_drag(struct wl_data_source *source) {
// Notify the application that the drag source is closed
_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) {
cleanup_drag_source_data(_glfw.wl.drag.current_request);
_glfw.wl.drag.current_request = NULL;
}
// Clean up all pending data requests
cleanup_all_wl_pending_requests();
// Clean up MIME type strings
for (int i = 0; i < _glfw.wl.drag.mime_count; i++) free(_glfw.wl.drag.mimes[i]);
@ -3065,13 +3124,15 @@ drag_source_send(void *data UNUSED, struct wl_data_source *source UNUSED, const
request->error_code = 0;
if (!request->mime_type) {
cleanup_drag_source_data(request);
cleanup_wl_drag_source_data(request);
return;
}
// Store as current request
if (_glfw.wl.drag.current_request) cleanup_drag_source_data(_glfw.wl.drag.current_request);
_glfw.wl.drag.current_request = request;
// Add to pending requests array
if (!add_wl_pending_request(request)) {
cleanup_wl_drag_source_data(request);
return;
}
// Notify the application via callback
_glfwInputDragSourceRequest(window, mime_type, request);
@ -3210,6 +3271,8 @@ _glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* data, siz
source_data->finished = true;
close(source_data->write_fd);
source_data->write_fd = -1;
// Clean up this and any other finished requests
cleanup_wl_finished_requests();
return 0;
}
@ -3219,6 +3282,8 @@ _glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* data, siz
source_data->error_code = (int)size;
close(source_data->write_fd);
source_data->write_fd = -1;
// Clean up this and any other finished requests
cleanup_wl_finished_requests();
return 0;
}
@ -3238,6 +3303,8 @@ _glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* data, siz
source_data->error_code = errno;
close(source_data->write_fd);
source_data->write_fd = -1;
// Clean up this and any other finished requests
cleanup_wl_finished_requests();
return -errno;
}

4
glfw/x11_platform.h vendored
View file

@ -400,7 +400,9 @@ typedef struct _GLFWlibraryX11
Atom action_atom; // XdndActionCopy, XdndActionMove, or XdndActionLink
bool active;
_GLFWwindow* window; // Window that initiated the drag
GLFWDragSourceData* current_request; // Current data request being processed
GLFWDragSourceData** pending_requests; // Array of pending data requests
int pending_request_count; // Number of pending requests
int pending_request_capacity; // Capacity of the pending requests array
} drag;
struct {

204
glfw/x11_window.c vendored
View file

@ -88,6 +88,10 @@ x11_cancel_momentum_scroll_timer(void) {
#define _GLFW_XDND_VERSION 5
// Forward declarations for drag source data handling
static void cleanup_x11_drag_source_data(GLFWDragSourceData* data);
static bool add_x11_pending_request(GLFWDragSourceData* request);
// Wait for data to arrive using poll
// This avoids blocking other threads via the per-display Xlib lock that also
// covers GLX functions
@ -1101,13 +1105,97 @@ static void handleSelectionRequest(XEvent* event)
const XSelectionRequestEvent* request = &event->xselectionrequest;
XEvent reply = { SelectionNotify };
reply.xselection.property = writeTargetToProperty(request);
reply.xselection.display = request->display;
reply.xselection.requestor = request->requestor;
reply.xselection.selection = request->selection;
reply.xselection.target = request->target;
reply.xselection.time = request->time;
// Handle XdndSelection (drag and drop) specially
if (request->selection == _glfw.x11.XdndSelection && _glfw.x11.drag.active && _glfw.x11.drag.window) {
// Handle TARGETS request for XdndSelection
if (request->target == _glfw.x11.TARGETS) {
// Return the list of supported MIME type atoms
Atom *targets = calloc(_glfw.x11.drag.mime_count + 2, sizeof(Atom));
if (targets) {
targets[0] = _glfw.x11.TARGETS;
targets[1] = _glfw.x11.MULTIPLE;
for (int i = 0; i < _glfw.x11.drag.mime_count; i++) {
targets[i + 2] = _glfw.x11.drag.type_atoms[i];
}
XChangeProperty(_glfw.x11.display,
request->requestor,
request->property,
XA_ATOM,
32,
PropModeReplace,
(unsigned char*)targets,
_glfw.x11.drag.mime_count + 2);
free(targets);
reply.xselection.property = request->property;
} else {
reply.xselection.property = None;
}
} else {
// Find the matching MIME type for the requested target
const char* mime_type = NULL;
for (int i = 0; i < _glfw.x11.drag.mime_count; i++) {
if (_glfw.x11.drag.type_atoms[i] == request->target) {
mime_type = _glfw.x11.drag.mimes[i];
break;
}
}
if (mime_type) {
// Create a drag source data request
GLFWDragSourceData* source_data = calloc(1, sizeof(GLFWDragSourceData));
if (source_data) {
source_data->window_id = _glfw.x11.drag.window->id;
source_data->mime_type = _glfw_strdup(mime_type);
source_data->write_fd = -1;
source_data->finished = false;
source_data->error_code = 0;
// Store request info in platform_data for later use
// We'll use a simple struct to hold the X11-specific data
struct {
Window requestor;
Atom property;
Atom target;
} *x11_data = malloc(sizeof(*x11_data));
if (x11_data && source_data->mime_type) {
x11_data->requestor = request->requestor;
x11_data->property = request->property;
x11_data->target = request->target;
source_data->platform_data = x11_data;
if (add_x11_pending_request(source_data)) {
// Notify the application via callback
_glfwInputDragSourceRequest(_glfw.x11.drag.window, mime_type, source_data);
reply.xselection.property = request->property;
} else {
free(x11_data);
free(source_data->mime_type);
free(source_data);
reply.xselection.property = None;
}
} else {
free(x11_data);
free(source_data->mime_type);
free(source_data);
reply.xselection.property = None;
}
} else {
reply.xselection.property = None;
}
} else {
reply.xselection.property = None;
}
}
} else {
// Handle regular clipboard/primary selection
reply.xselection.property = writeTargetToProperty(request);
}
XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
}
@ -1974,7 +2062,10 @@ static void processEvent(XEvent *event)
drop_data->x11_source = _glfw.x11.xdnd.source;
drop_data->x11_version = _glfw.x11.xdnd.version;
_glfwInputDrop(window, drop_data);
// Check if the drop is from this application
bool from_self = (_glfw.x11.drag.source_window != None &&
_glfw.x11.xdnd.source == _glfw.x11.drag.source_window);
_glfwInputDrop(window, drop_data, from_self);
// Note: drop_data is NOT freed here - application must call glfwFinishDrop
}
@ -3717,21 +3808,79 @@ static void cleanup_x11_drag_source_data(GLFWDragSourceData* data) {
close(data->write_fd);
data->write_fd = -1;
}
free(data->platform_data);
free(data->mime_type);
free(data);
}
// Remove a finished request from the pending requests array
static void
remove_x11_pending_request(int index) {
if (index < 0 || index >= _glfw.x11.drag.pending_request_count) return;
cleanup_x11_drag_source_data(_glfw.x11.drag.pending_requests[index]);
// Shift remaining elements
for (int i = index; i < _glfw.x11.drag.pending_request_count - 1; i++) {
_glfw.x11.drag.pending_requests[i] = _glfw.x11.drag.pending_requests[i + 1];
}
_glfw.x11.drag.pending_request_count--;
}
// Clean up all finished requests from the pending requests array
static void
cleanup_x11_finished_requests(void) {
for (int i = _glfw.x11.drag.pending_request_count - 1; i >= 0; i--) {
if (_glfw.x11.drag.pending_requests[i]->finished) {
remove_x11_pending_request(i);
}
}
}
// Clean up all pending requests
static void
cleanup_all_x11_pending_requests(void) {
for (int i = 0; i < _glfw.x11.drag.pending_request_count; i++) {
cleanup_x11_drag_source_data(_glfw.x11.drag.pending_requests[i]);
}
free(_glfw.x11.drag.pending_requests);
_glfw.x11.drag.pending_requests = NULL;
_glfw.x11.drag.pending_request_count = 0;
_glfw.x11.drag.pending_request_capacity = 0;
}
// Add a request to the pending requests array
static bool
add_x11_pending_request(GLFWDragSourceData* request) {
// First, clean up any finished requests to make room
cleanup_x11_finished_requests();
// Grow the array if necessary
if (_glfw.x11.drag.pending_request_count >= _glfw.x11.drag.pending_request_capacity) {
// Cap maximum capacity to prevent excessive memory use
if (_glfw.x11.drag.pending_request_capacity >= 512) {
return false;
}
int new_capacity = _glfw.x11.drag.pending_request_capacity ? _glfw.x11.drag.pending_request_capacity * 2 : 4;
GLFWDragSourceData** new_array = realloc(_glfw.x11.drag.pending_requests,
new_capacity * sizeof(GLFWDragSourceData*));
if (!new_array) return false;
_glfw.x11.drag.pending_requests = new_array;
_glfw.x11.drag.pending_request_capacity = new_capacity;
}
_glfw.x11.drag.pending_requests[_glfw.x11.drag.pending_request_count++] = request;
return true;
}
static void cleanupDragSource(void) {
// Notify the application that the drag source is closed
if (_glfw.x11.drag.window && _glfw.x11.drag.window->callbacks.dragSource) {
_glfwInputDragSourceRequest(_glfw.x11.drag.window, NULL, NULL);
}
// Clean up any pending data request
if (_glfw.x11.drag.current_request) {
cleanup_x11_drag_source_data(_glfw.x11.drag.current_request);
_glfw.x11.drag.current_request = NULL;
}
// Clean up all pending data requests
cleanup_all_x11_pending_requests();
// Clean up MIME type strings and atoms
for (int i = 0; i < _glfw.x11.drag.mime_count; i++) {
@ -3834,8 +3983,7 @@ int _glfwPlatformStartDrag(_GLFWwindow* window,
ssize_t _glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* data, size_t size) {
if (!source_data || source_data->finished) return -EINVAL;
// For X11, we typically set properties for SelectionRequest events
// The write_fd is used if we set up a pipe-based transfer
// For X11, we set properties via XChangeProperty in response to SelectionRequest
// End of data: NULL data pointer and size zero
if (!data && size == 0) {
@ -3844,6 +3992,8 @@ ssize_t _glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* d
close(source_data->write_fd);
source_data->write_fd = -1;
}
// Clean up this and any other finished requests
cleanup_x11_finished_requests();
return 0;
}
@ -3855,12 +4005,36 @@ ssize_t _glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* d
close(source_data->write_fd);
source_data->write_fd = -1;
}
// Clean up this and any other finished requests
cleanup_x11_finished_requests();
return 0;
}
// For X11, data is typically set via XChangeProperty in response to SelectionRequest
// Store the data in platform_data for the SelectionRequest handler
// Non-blocking write - retry on EINTR, return 0 on would-block
// For X11, use XChangeProperty to set the data on the requestor window
if (source_data->platform_data) {
struct {
Window requestor;
Atom property;
Atom target;
} *x11_data = source_data->platform_data;
XChangeProperty(_glfw.x11.display,
x11_data->requestor,
x11_data->property,
x11_data->target,
8,
PropModeReplace,
(unsigned char*)data,
size);
XFlush(_glfw.x11.display);
// Mark as finished after sending data
source_data->finished = true;
cleanup_x11_finished_requests();
return (ssize_t)size;
}
// Fallback: Non-blocking write if we have an fd
if (source_data->write_fd >= 0) {
ssize_t written;
do {
@ -3876,13 +4050,15 @@ ssize_t _glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* d
source_data->error_code = errno;
close(source_data->write_fd);
source_data->write_fd = -1;
// Clean up this and any other finished requests
cleanup_x11_finished_requests();
return -errno;
}
return written;
}
// No fd available, all data is accepted (buffered by platform_data)
return (ssize_t)size;
// No valid mechanism to send data
return -EINVAL;
}
void _glfwPlatformUpdateDragState(_GLFWwindow* window) {

3
kitty/glfw-wrapper.h generated
View file

@ -1535,10 +1535,11 @@ typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
* @sa @ref glfwFinishDrop
*
* @since Changed in version 4.0 to receive opaque drop data pointer.
* @since Changed in version 4.0 to receive from_self parameter.
*
* @ingroup input
*/
typedef void (* GLFWdropfun)(GLFWwindow*, GLFWDropData*);
typedef void (* GLFWdropfun)(GLFWwindow*, GLFWDropData*, bool from_self);
/*! @brief Drag event types.
*

View file

@ -742,11 +742,20 @@ get_mime_data(GLFWDropData *drop, const char **mimes, int mime_count, PyObject *
}
static void
drop_callback(GLFWwindow *w, GLFWDropData *drop) {
drop_callback(GLFWwindow *w, GLFWDropData *drop, bool from_self) {
int num_mimes;
const char** mimes = glfwGetDropMimeTypes(drop, &num_mimes);
RAII_PyObject(ans, PyDict_New());
get_mime_data(drop, mimes, num_mimes, ans);
if (from_self && global_state.drag_source.drag_data) {
// For self-drops, copy data directly from drag_source.drag_data
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(global_state.drag_source.drag_data, &pos, &key, &value)) {
if (PyDict_SetItem(ans, key, value) != 0) break;
}
} else {
get_mime_data(drop, mimes, num_mimes, ans);
}
RAII_PyObject(exc, PyErr_GetRaisedException());
glfwFinishDrop(drop, GLFW_DRAG_OPERATION_COPY, true);
if (!set_callback_window(w)) return;