mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-10-21 12:42:25 +00:00
parent
3839dcc082
commit
7ca6fd66f8
11 changed files with 421 additions and 45 deletions
5
glfw/cocoa_platform.h
vendored
5
glfw/cocoa_platform.h
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
9
glfw/glfw3.h
vendored
|
|
@ -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
4
glfw/input.c
vendored
|
|
@ -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
2
glfw/internal.h
vendored
|
|
@ -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
4
glfw/wl_platform.h
vendored
|
|
@ -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
89
glfw/wl_window.c
vendored
|
|
@ -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
4
glfw/x11_platform.h
vendored
|
|
@ -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
204
glfw/x11_window.c
vendored
|
|
@ -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
3
kitty/glfw-wrapper.h
generated
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
13
kitty/glfw.c
13
kitty/glfw.c
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue