mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-10-21 20:52:19 +00:00
parent
0ab7aec690
commit
ed5eb8f45c
7 changed files with 759 additions and 135 deletions
|
|
@ -33,6 +33,7 @@
|
|||
#include <Availability.h>
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
|
@ -1496,32 +1497,34 @@ static void freeFilteredDragMimes(_GLFWwindow* window, int old_count, int new_co
|
|||
_glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y);
|
||||
|
||||
NSPasteboard* pasteboard = [sender draggingPasteboard];
|
||||
NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};
|
||||
NSArray* objs = [pasteboard readObjectsForClasses:@[[NSURL class], [NSString class]]
|
||||
options:options];
|
||||
if (!objs) return NO;
|
||||
const NSUInteger count = [objs count];
|
||||
NSMutableString *uri_list = [NSMutableString stringWithCapacity:4096]; // auto-released
|
||||
if (count)
|
||||
{
|
||||
for (NSUInteger i = 0; i < count; i++)
|
||||
{
|
||||
id obj = objs[i];
|
||||
if ([obj isKindOfClass:[NSURL class]]) {
|
||||
NSURL *url = (NSURL*)obj;
|
||||
if ([uri_list length] > 0) [uri_list appendString:@("\n")];
|
||||
if (url.fileURL) [uri_list appendString:url.filePathURL.absoluteString];
|
||||
else [uri_list appendString:url.absoluteString];
|
||||
} else if ([obj isKindOfClass:[NSString class]]) {
|
||||
const char *text = [obj UTF8String];
|
||||
_glfwInputDrop(window, "text/plain;charset=utf-8", text, strlen(text));
|
||||
} else {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Object is neither a URL nor a string");
|
||||
}
|
||||
}
|
||||
|
||||
// Heap-allocate drop data structure for chunked reading
|
||||
// The application is responsible for freeing this via glfwFinishDrop
|
||||
GLFWDropData* drop_data = calloc(1, sizeof(GLFWDropData));
|
||||
if (!drop_data) {
|
||||
_glfwInputError(GLFW_OUT_OF_MEMORY, "Cocoa: Failed to allocate drop data");
|
||||
return NO;
|
||||
}
|
||||
if ([uri_list length] > 0) _glfwInputDrop(window, "text/uri-list", uri_list.UTF8String, strlen(uri_list.UTF8String));
|
||||
// Transfer ownership of mimes array from window to drop object
|
||||
drop_data->mime_types = window->ns.dragMimes;
|
||||
drop_data->mime_count = window->ns.dragMimeCount;
|
||||
drop_data->mime_array_size = window->ns.dragMimeArraySize;
|
||||
// Clear window's references since drop object now owns the mimes
|
||||
window->ns.dragMimes = NULL;
|
||||
window->ns.dragMimeCount = 0;
|
||||
window->ns.dragMimeArraySize = 0;
|
||||
|
||||
drop_data->current_mime = NULL;
|
||||
drop_data->read_fd = -1;
|
||||
drop_data->bytes_read = 0;
|
||||
drop_data->platform_data = [pasteboard retain]; // Retain the pasteboard
|
||||
drop_data->eof_reached = false;
|
||||
drop_data->current_data = NULL;
|
||||
drop_data->data_offset = 0;
|
||||
|
||||
_glfwInputDrop(window, drop_data);
|
||||
|
||||
// Note: drop_data is NOT freed here - application must call glfwFinishDrop
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
|
@ -3872,3 +3875,128 @@ void _glfwPlatformUpdateDragState(_GLFWwindow* window) {
|
|||
}
|
||||
}
|
||||
|
||||
const char**
|
||||
_glfwPlatformGetDropMimeTypes(GLFWDropData* drop, int* count) {
|
||||
if (!drop || !count) return NULL;
|
||||
*count = drop->mime_count;
|
||||
return drop->mime_types;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
_glfwPlatformReadDropData(GLFWDropData* drop, const char* mime, void* buffer, size_t capacity, monotonic_t timeout UNUSED) {
|
||||
if (!drop || !mime || !buffer || capacity == 0) return -EINVAL;
|
||||
|
||||
NSPasteboard* pasteboard = (__bridge NSPasteboard*)drop->platform_data;
|
||||
if (!pasteboard) return -EINVAL;
|
||||
|
||||
// Check if the MIME type is available
|
||||
bool mime_found = false;
|
||||
for (int i = 0; i < drop->mime_count; i++) {
|
||||
if (drop->mime_types[i] && strcmp(drop->mime_types[i], mime) == 0) {
|
||||
mime_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mime_found) return -ENOENT;
|
||||
|
||||
// If switching MIME types, release previous data
|
||||
if (drop->current_mime && strcmp(drop->current_mime, mime) != 0) {
|
||||
if (drop->current_data) {
|
||||
[(NSData*)drop->current_data release];
|
||||
drop->current_data = NULL;
|
||||
}
|
||||
drop->data_offset = 0;
|
||||
}
|
||||
|
||||
// If we need to fetch data for this MIME type
|
||||
if (drop->current_data == NULL || drop->current_mime == NULL ||
|
||||
strcmp(drop->current_mime, mime) != 0) {
|
||||
|
||||
NSData* data = nil;
|
||||
|
||||
// Handle special MIME types
|
||||
if (strcmp(mime, "text/uri-list") == 0) {
|
||||
NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};
|
||||
NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]] options:options];
|
||||
if (urls && [urls count] > 0) {
|
||||
NSMutableString *uri_list = [NSMutableString stringWithCapacity:4096];
|
||||
for (NSURL* url in urls) {
|
||||
if ([uri_list length] > 0) [uri_list appendString:@"\n"];
|
||||
if (url.fileURL) [uri_list appendString:url.filePathURL.absoluteString];
|
||||
else [uri_list appendString:url.absoluteString];
|
||||
}
|
||||
data = [uri_list dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
} else if (strcmp(mime, "text/plain") == 0 || strcmp(mime, "text/plain;charset=utf-8") == 0) {
|
||||
NSArray* strings = [pasteboard readObjectsForClasses:@[[NSString class]] options:nil];
|
||||
if (strings && [strings count] > 0) {
|
||||
NSString* str = strings[0];
|
||||
data = [str dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
} else {
|
||||
// Try to read data for other MIME types using UTI
|
||||
NSString* uti = mime_to_uti(mime);
|
||||
if (uti) {
|
||||
NSPasteboardType pbType = [pasteboard availableTypeFromArray:@[uti]];
|
||||
if (pbType) {
|
||||
data = [pasteboard dataForType:pbType];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) return -ENOENT;
|
||||
|
||||
drop->current_data = [data retain];
|
||||
drop->current_mime = mime;
|
||||
drop->data_offset = 0;
|
||||
}
|
||||
|
||||
// Read data from buffer
|
||||
NSData* data = (NSData*)drop->current_data;
|
||||
NSUInteger dataLength = [data length];
|
||||
|
||||
if (drop->data_offset >= dataLength) {
|
||||
return 0; // EOF
|
||||
}
|
||||
|
||||
NSUInteger remaining = dataLength - drop->data_offset;
|
||||
NSUInteger to_read = (remaining < capacity) ? remaining : capacity;
|
||||
|
||||
[data getBytes:buffer range:NSMakeRange(drop->data_offset, to_read)];
|
||||
drop->data_offset += to_read;
|
||||
|
||||
return (ssize_t)to_read;
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation UNUSED, bool success UNUSED) {
|
||||
if (!drop) return;
|
||||
|
||||
// Release the retained current data
|
||||
if (drop->current_data) {
|
||||
[(NSData*)drop->current_data release];
|
||||
drop->current_data = NULL;
|
||||
}
|
||||
|
||||
// Release the retained pasteboard
|
||||
// Note: Cocoa drag operations don't have a way to report back to the source
|
||||
// like X11's XdndFinished, so we just clean up our resources
|
||||
if (drop->platform_data) {
|
||||
[(NSPasteboard*)drop->platform_data release];
|
||||
drop->platform_data = NULL;
|
||||
}
|
||||
|
||||
// Free the mime types array (owned by drop object)
|
||||
if (drop->mime_types) {
|
||||
for (int i = 0; i < drop->mime_array_size; i++) {
|
||||
if (drop->mime_types[i])
|
||||
free((char*)drop->mime_types[i]);
|
||||
}
|
||||
free(drop->mime_types);
|
||||
drop->mime_types = NULL;
|
||||
}
|
||||
|
||||
// Free the heap-allocated drop data structure
|
||||
free(drop);
|
||||
}
|
||||
|
||||
|
|
|
|||
136
glfw/glfw3.h
vendored
136
glfw/glfw3.h
vendored
|
|
@ -109,6 +109,16 @@ extern "C" {
|
|||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Include for ssize_t on POSIX systems. On Windows we define it if needed. */
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/types.h>
|
||||
#else
|
||||
#if !defined(SSIZE_T_DEFINED)
|
||||
typedef intptr_t ssize_t;
|
||||
#define SSIZE_T_DEFINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(GLFW_INCLUDE_VULKAN)
|
||||
#include <vulkan/vulkan.h>
|
||||
#endif /* Vulkan header */
|
||||
|
|
@ -1267,6 +1277,22 @@ typedef struct GLFWwindow GLFWwindow;
|
|||
*/
|
||||
typedef struct GLFWcursor GLFWcursor;
|
||||
|
||||
/*! @brief Opaque drop data object.
|
||||
*
|
||||
* Opaque drop data object representing data from a drag and drop operation.
|
||||
* This object is passed to the drop callback and can be used to query
|
||||
* available MIME types and read the dropped data in chunks.
|
||||
*
|
||||
* @see @ref path_drop
|
||||
* @see @ref glfwGetDropMimeTypes
|
||||
* @see @ref glfwReadDropData
|
||||
*
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef struct GLFWDropData GLFWDropData;
|
||||
|
||||
typedef enum {
|
||||
GLFW_RELEASE = 0,
|
||||
GLFW_PRESS = 1,
|
||||
|
|
@ -1761,25 +1787,30 @@ typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
|
|||
* This is the function pointer type for drop callbacks. A drop
|
||||
* callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, const char* mime, const char* data, size_t sz)
|
||||
* void function_name(GLFWwindow* window, GLFWDropData* drop)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] mime The UTF-8 encoded drop mime-type
|
||||
* @param[in] data The dropped data.
|
||||
* @param[in] sz The size of the dropped data
|
||||
* @param[in] drop A heap-allocated opaque pointer representing the dropped data. Use
|
||||
* @ref glfwGetDropMimeTypes to get available MIME types and
|
||||
* @ref glfwReadDropData to read the data in chunks.
|
||||
*
|
||||
* @pointer_lifetime The data is valid until the
|
||||
* callback function returns.
|
||||
* @note The drop object is heap-allocated and remains valid until the
|
||||
* application calls @ref glfwFinishDrop to free it. The application is
|
||||
* responsible for calling glfwFinishDrop when it has finished reading
|
||||
* the dropped data, even if reading fails or is not needed.
|
||||
*
|
||||
* @sa @ref path_drop
|
||||
* @sa @ref glfwSetDropCallback
|
||||
* @sa @ref glfwGetDropMimeTypes
|
||||
* @sa @ref glfwReadDropData
|
||||
* @sa @ref glfwFinishDrop
|
||||
*
|
||||
* @since Added in version 3.1.
|
||||
* @since Changed in version 4.0 to receive opaque drop data pointer.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWdropfun)(GLFWwindow*, const char *, const char*, size_t);
|
||||
typedef void (* GLFWdropfun)(GLFWwindow*, GLFWDropData*);
|
||||
|
||||
/*! @brief Drag event types.
|
||||
*
|
||||
|
|
@ -5105,6 +5136,95 @@ GLFWAPI int glfwStartDrag(GLFWwindow* window, const GLFWdragitem* items, int ite
|
|||
*/
|
||||
GLFWAPI void glfwUpdateDragState(GLFWwindow* window);
|
||||
|
||||
/*! @brief Gets the list of available MIME types from drop data.
|
||||
*
|
||||
* This function returns an array of MIME type strings available from
|
||||
* the dropped data. The array and strings are owned by GLFW and should
|
||||
* not be freed by the application. They are only valid until the drop
|
||||
* callback returns.
|
||||
*
|
||||
* @param[in] drop The drop data object from the drop callback.
|
||||
* @param[out] count The number of MIME types in the returned array.
|
||||
* @return An array of MIME type strings, or `NULL` if the drop data is invalid.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function must only be called from the main thread.
|
||||
*
|
||||
* @sa @ref path_drop
|
||||
* @sa @ref glfwSetDropCallback
|
||||
* @sa @ref glfwReadDropData
|
||||
*
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI const char** glfwGetDropMimeTypes(GLFWDropData* drop, int* count);
|
||||
|
||||
/*! @brief Reads a chunk of dropped data for a specified MIME type.
|
||||
*
|
||||
* This function reads a chunk of the dropped data of the specified MIME type
|
||||
* into the provided buffer. Repeated calls read subsequent chunks of data.
|
||||
*
|
||||
* If this function is called for a different MIME type than the previous call,
|
||||
* resources associated with the previous MIME type are automatically freed.
|
||||
*
|
||||
* @param[in] drop The drop data object from the drop callback.
|
||||
* @param[in] mime The MIME type to read data for.
|
||||
* @param[out] buffer The buffer to read data into.
|
||||
* @param[in] capacity The size of the buffer in bytes.
|
||||
* @param[in] timeout The maximum time to wait for data, as a monotonic time value.
|
||||
* Use 0 for non-blocking, or a positive value for the timeout duration.
|
||||
* @return The number of bytes written to the buffer on success. Returns 0 when
|
||||
* all data has been read (end of data). Returns a negative value on error:
|
||||
* `-ENOENT` if the MIME type is not available, `-EIO` for an I/O error,
|
||||
* `-ETIME` if the timeout expires before data is available.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @remark The returned byte count may be less than the buffer capacity even
|
||||
* when more data is available.
|
||||
*
|
||||
* @thread_safety This function must only be called from the main thread.
|
||||
*
|
||||
* @sa @ref path_drop
|
||||
* @sa @ref glfwSetDropCallback
|
||||
* @sa @ref glfwGetDropMimeTypes
|
||||
*
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI ssize_t glfwReadDropData(GLFWDropData* drop, const char* mime, void* buffer, size_t capacity, monotonic_t timeout);
|
||||
|
||||
/*! @brief Finishes a drop operation and frees associated resources.
|
||||
*
|
||||
* This function finishes the drop operation, informs the drag source of the
|
||||
* result, and frees the heap-allocated drop data object. After calling this
|
||||
* function, the drop data object must not be used anymore.
|
||||
*
|
||||
* The application MUST call this function when it has finished reading the
|
||||
* dropped data. Failure to do so will result in a memory leak and may cause
|
||||
* the drag source to hang waiting for the drop operation to complete.
|
||||
*
|
||||
* @param[in] drop The drop data object to finish and free.
|
||||
* @param[in] operation The type of operation that was performed (copy, move, etc).
|
||||
* @param[in] success Whether the drop operation was successful.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function must only be called from the main thread.
|
||||
*
|
||||
* @sa @ref path_drop
|
||||
* @sa @ref glfwSetDropCallback
|
||||
* @sa @ref glfwReadDropData
|
||||
*
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI void glfwFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation, bool success);
|
||||
|
||||
/*! @brief Returns whether the specified joystick is present.
|
||||
*
|
||||
* This function returns whether the specified joystick is present.
|
||||
|
|
|
|||
33
glfw/input.c
vendored
33
glfw/input.c
vendored
|
|
@ -31,6 +31,7 @@
|
|||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -403,10 +404,10 @@ void _glfwInputCursorEnter(_GLFWwindow* window, bool entered)
|
|||
|
||||
// Notifies shared code of files or directories dropped on a window
|
||||
//
|
||||
void _glfwInputDrop(_GLFWwindow* window, const char *mime, const char *text, size_t sz)
|
||||
void _glfwInputDrop(_GLFWwindow* window, GLFWDropData* drop)
|
||||
{
|
||||
if (window->callbacks.drop)
|
||||
window->callbacks.drop((GLFWwindow*) window, mime, text, sz);
|
||||
window->callbacks.drop((GLFWwindow*) window, drop);
|
||||
}
|
||||
|
||||
// Notifies shared code of a drag event
|
||||
|
|
@ -1149,6 +1150,34 @@ GLFWAPI void glfwUpdateDragState(GLFWwindow* handle)
|
|||
_glfwPlatformUpdateDragState(window);
|
||||
}
|
||||
|
||||
GLFWAPI const char** glfwGetDropMimeTypes(GLFWDropData* drop, int* count)
|
||||
{
|
||||
assert(drop != NULL);
|
||||
assert(count != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return _glfwPlatformGetDropMimeTypes(drop, count);
|
||||
}
|
||||
|
||||
GLFWAPI ssize_t glfwReadDropData(GLFWDropData* drop, const char* mime, void* buffer, size_t capacity, monotonic_t timeout)
|
||||
{
|
||||
assert(drop != NULL);
|
||||
assert(mime != NULL);
|
||||
assert(buffer != NULL);
|
||||
assert(capacity > 0);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(-1);
|
||||
return _glfwPlatformReadDropData(drop, mime, buffer, capacity, timeout);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation, bool success)
|
||||
{
|
||||
assert(drop != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
_glfwPlatformFinishDrop(drop, operation, success);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwJoystickPresent(int jid)
|
||||
{
|
||||
_GLFWjoystick* js;
|
||||
|
|
|
|||
28
glfw/internal.h
vendored
28
glfw/internal.h
vendored
|
|
@ -85,6 +85,27 @@ typedef struct _GLFWjoystick _GLFWjoystick;
|
|||
typedef struct _GLFWtls _GLFWtls;
|
||||
typedef struct _GLFWmutex _GLFWmutex;
|
||||
|
||||
// Drop data structure for chunked reading of drag and drop data
|
||||
struct GLFWDropData {
|
||||
const char** mime_types; // Array of available MIME types (owned by drop object)
|
||||
int mime_count; // Number of MIME types
|
||||
int mime_array_size; // Original array size for proper cleanup
|
||||
const char* current_mime; // Currently being read MIME type
|
||||
int read_fd; // File descriptor for reading data (Wayland/X11)
|
||||
size_t bytes_read; // Total bytes read so far for current mime
|
||||
void* platform_data; // Platform-specific data (offer for Wayland, pasteboard for Cocoa)
|
||||
bool eof_reached; // Whether EOF has been reached for current mime
|
||||
// Platform-specific data fields
|
||||
void* current_data; // NSData* (Cocoa) or unsigned char* from XGetWindowProperty (X11)
|
||||
size_t data_offset; // Read offset in current data (Cocoa/X11)
|
||||
// X11-specific fields
|
||||
size_t x11_data_size; // Size of current X11 data
|
||||
unsigned long x11_drop_target; // Window handle where the drop occurred (X11)
|
||||
unsigned long x11_drop_time; // Time from the drop event (X11)
|
||||
unsigned long x11_source; // Source window for XdndFinished (X11)
|
||||
int x11_version; // Xdnd protocol version (X11)
|
||||
};
|
||||
|
||||
typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*);
|
||||
typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*);
|
||||
typedef void (* _GLFWswapintervalfun)(int);
|
||||
|
|
@ -822,8 +843,13 @@ 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, const char *mime, const char *text, size_t sz);
|
||||
void _glfwInputDrop(_GLFWwindow* window, GLFWDropData* drop);
|
||||
int _glfwInputDragEvent(_GLFWwindow* window, int event, double xpos, double ypos, const char** mime_types, int* mime_count);
|
||||
|
||||
// Platform functions for drop data reading
|
||||
const char** _glfwPlatformGetDropMimeTypes(GLFWDropData* drop, int* count);
|
||||
ssize_t _glfwPlatformReadDropData(GLFWDropData* drop, const char* mime, void* buffer, size_t capacity, monotonic_t timeout);
|
||||
void _glfwPlatformFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation, bool success);
|
||||
void _glfwInputColorScheme(GLFWColorScheme, bool);
|
||||
void _glfwPlatformInputColorScheme(GLFWColorScheme);
|
||||
void _glfwInputJoystick(_GLFWjoystick* js, int event);
|
||||
|
|
|
|||
21
glfw/null_window.c
vendored
21
glfw/null_window.c
vendored
|
|
@ -30,6 +30,7 @@
|
|||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void applySizeLimits(_GLFWwindow* window, int* width, int* height)
|
||||
|
|
@ -544,6 +545,26 @@ void _glfwPlatformUpdateDragState(_GLFWwindow* window UNUSED)
|
|||
// No-op for null platform
|
||||
}
|
||||
|
||||
const char** _glfwPlatformGetDropMimeTypes(GLFWDropData* drop UNUSED, int* count)
|
||||
{
|
||||
if (count) *count = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ssize_t _glfwPlatformReadDropData(GLFWDropData* drop UNUSED, const char* mime UNUSED,
|
||||
void* buffer UNUSED, size_t capacity UNUSED,
|
||||
monotonic_t timeout UNUSED)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
void _glfwPlatformFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation UNUSED, bool success UNUSED)
|
||||
{
|
||||
if (!drop) return;
|
||||
// Free the heap-allocated drop data structure
|
||||
free(drop);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetClipboardString(const char* string)
|
||||
{
|
||||
char* copy = _glfw_strdup(string);
|
||||
|
|
|
|||
222
glfw/wl_window.c
vendored
222
glfw/wl_window.c
vendored
|
|
@ -39,6 +39,7 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <sys/mman.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -2258,35 +2259,6 @@ read_offer(int data_pipe, GLFWclipboardwritedatafun write_data, void *object) {
|
|||
}
|
||||
|
||||
|
||||
typedef struct chunked_writer {
|
||||
char *buf; size_t sz, cap;
|
||||
} chunked_writer;
|
||||
|
||||
static bool
|
||||
write_chunk(void *object, const char *data, size_t sz) {
|
||||
chunked_writer *cw = object;
|
||||
if (cw->cap < cw->sz + sz) {
|
||||
cw->cap = MAX(cw->cap * 2, cw->sz + 8*sz);
|
||||
cw->buf = realloc(cw->buf, cw->cap * sizeof(cw->buf[0]));
|
||||
}
|
||||
memcpy(cw->buf + cw->sz, data, sz);
|
||||
cw->sz += sz;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
read_offer_string(int data_pipe, size_t *sz) {
|
||||
chunked_writer cw = {0};
|
||||
read_offer(data_pipe, write_chunk, &cw);
|
||||
if (cw.buf) {
|
||||
*sz = cw.sz;
|
||||
return cw.buf;
|
||||
}
|
||||
*sz = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
read_clipboard_data_offer(struct wl_data_offer *data_offer, const char *mime, GLFWclipboardwritedatafun write_data, void *object) {
|
||||
int pipefd[2];
|
||||
|
|
@ -2305,14 +2277,6 @@ read_primary_selection_offer(struct zwp_primary_selection_offer_v1 *primary_sele
|
|||
read_offer(pipefd[0], write_data, object);
|
||||
}
|
||||
|
||||
static char* read_data_offer(struct wl_data_offer *data_offer, const char *mime, size_t *sz) {
|
||||
int pipefd[2];
|
||||
if (pipe2(pipefd, O_CLOEXEC) != 0) return NULL;
|
||||
wl_data_offer_receive(data_offer, mime, pipefd[1]);
|
||||
close(pipefd[1]);
|
||||
return read_offer_string(pipefd[0], sz);
|
||||
}
|
||||
|
||||
static void data_source_canceled(void *data UNUSED, struct wl_data_source *wl_data_source) {
|
||||
if (_glfw.wl.dataSourceForClipboard == wl_data_source) {
|
||||
_glfw.wl.dataSourceForClipboard = NULL;
|
||||
|
|
@ -2560,27 +2524,45 @@ static void drag_leave(void *data UNUSED, struct wl_data_device *wl_data_device
|
|||
|
||||
static void drop(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP && _glfw.wl.dataOffers[i].mime_for_drop) {
|
||||
size_t sz = 0;
|
||||
char *d = read_data_offer(_glfw.wl.dataOffers[i].id, _glfw.wl.dataOffers[i].mime_for_drop, &sz);
|
||||
if (d) {
|
||||
// We dont do finish as this requires version 3 for wl_data_device_manager
|
||||
// which then requires more work with calling set_actions for drag and drop to function
|
||||
// wl_data_offer_finish(_glfw.wl.dataOffers[i].id);
|
||||
if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP) {
|
||||
_GLFWWaylandDataOffer *offer = &_glfw.wl.dataOffers[i];
|
||||
|
||||
_GLFWwindow* window = _glfw.windowListHead;
|
||||
while (window)
|
||||
{
|
||||
if (window->wl.surface == _glfw.wl.dataOffers[i].surface) {
|
||||
_glfwInputDrop(window, _glfw.wl.dataOffers[i].mime_for_drop, d, sz);
|
||||
_GLFWwindow* window = _glfw.windowListHead;
|
||||
while (window)
|
||||
{
|
||||
if (window->wl.surface == offer->surface) {
|
||||
// Heap-allocate drop data structure for chunked reading
|
||||
// The application is responsible for freeing this via glfwFinishDrop
|
||||
GLFWDropData* drop_data = calloc(1, sizeof(GLFWDropData));
|
||||
if (!drop_data) {
|
||||
_glfwInputError(GLFW_OUT_OF_MEMORY, "Wayland: Failed to allocate drop data");
|
||||
destroy_data_offer(offer); // Clean up the offer on allocation failure
|
||||
break;
|
||||
}
|
||||
window = window->next;
|
||||
}
|
||||
// Transfer ownership of mimes array from offer to drop object
|
||||
drop_data->mime_types = offer->mimes;
|
||||
drop_data->mime_count = (int)offer->mimes_count;
|
||||
drop_data->mime_array_size = (int)offer->mimes_count;
|
||||
// Clear offer's references since drop object now owns the mimes
|
||||
offer->mimes = NULL;
|
||||
offer->mimes_count = 0;
|
||||
|
||||
free(d);
|
||||
drop_data->current_mime = NULL;
|
||||
drop_data->read_fd = -1;
|
||||
drop_data->bytes_read = 0;
|
||||
drop_data->platform_data = offer; // Store the offer for later use
|
||||
drop_data->eof_reached = false;
|
||||
|
||||
_glfwInputDrop(window, drop_data);
|
||||
|
||||
// Note: drop_data is NOT freed here - application must call glfwFinishDrop
|
||||
break;
|
||||
}
|
||||
window = window->next;
|
||||
}
|
||||
destroy_data_offer(&_glfw.wl.dataOffers[i]);
|
||||
|
||||
// Note: We no longer destroy the offer here as the drop_data holds a reference
|
||||
// The offer will be destroyed when glfwFinishDrop is called
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -3198,3 +3180,137 @@ _glfwPlatformUpdateDragState(_GLFWwindow* window) {
|
|||
}
|
||||
}
|
||||
|
||||
const char**
|
||||
_glfwPlatformGetDropMimeTypes(GLFWDropData* drop, int* count) {
|
||||
if (!drop || !count) return NULL;
|
||||
*count = drop->mime_count;
|
||||
return drop->mime_types;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
_glfwPlatformReadDropData(GLFWDropData* drop, const char* mime, void* buffer, size_t capacity, monotonic_t timeout) {
|
||||
if (!drop || !mime || !buffer || capacity == 0) return -EINVAL;
|
||||
|
||||
_GLFWWaylandDataOffer *offer = (_GLFWWaylandDataOffer*)drop->platform_data;
|
||||
if (!offer || !offer->id) return -EINVAL;
|
||||
|
||||
// Check if the MIME type is available
|
||||
bool mime_found = false;
|
||||
for (int i = 0; i < drop->mime_count; i++) {
|
||||
if (drop->mime_types[i] && strcmp(drop->mime_types[i], mime) == 0) {
|
||||
mime_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mime_found) return -ENOENT;
|
||||
|
||||
// If switching MIME types, close the previous file descriptor
|
||||
if (drop->current_mime && strcmp(drop->current_mime, mime) != 0) {
|
||||
if (drop->read_fd >= 0) {
|
||||
close(drop->read_fd);
|
||||
drop->read_fd = -1;
|
||||
}
|
||||
drop->bytes_read = 0;
|
||||
drop->eof_reached = false;
|
||||
}
|
||||
|
||||
// If we've reached EOF for this MIME type, return 0
|
||||
if (drop->eof_reached && drop->current_mime && strcmp(drop->current_mime, mime) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Open a new pipe if we don't have one for this MIME type
|
||||
if (drop->read_fd < 0 || drop->current_mime == NULL || strcmp(drop->current_mime, mime) != 0) {
|
||||
int pipefd[2];
|
||||
if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) != 0) return -EIO;
|
||||
|
||||
wl_data_offer_receive(offer->id, mime, pipefd[1]);
|
||||
close(pipefd[1]);
|
||||
|
||||
// Round trip to ensure the compositor processes the receive request
|
||||
wl_display_roundtrip(_glfw.wl.display);
|
||||
|
||||
drop->read_fd = pipefd[0];
|
||||
// mime points to drop->mime_types entry, valid for duration of drop callback
|
||||
drop->current_mime = mime;
|
||||
drop->bytes_read = 0;
|
||||
drop->eof_reached = false;
|
||||
}
|
||||
|
||||
// Wait for data with timeout using poll
|
||||
monotonic_t start = monotonic();
|
||||
while (true) {
|
||||
struct pollfd pfd = { .fd = drop->read_fd, .events = POLLIN };
|
||||
|
||||
monotonic_t remaining = timeout - (monotonic() - start);
|
||||
if (timeout > 0 && remaining <= 0) return -ETIME;
|
||||
|
||||
int poll_timeout_ms = (timeout <= 0) ? 0 : monotonic_t_to_ms(remaining);
|
||||
if (poll_timeout_ms < 1 && timeout > 0) poll_timeout_ms = 1;
|
||||
|
||||
int ret = poll(&pfd, 1, poll_timeout_ms);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
return -EIO;
|
||||
}
|
||||
if (ret == 0) {
|
||||
if (timeout <= 0) {
|
||||
// Non-blocking mode: no data available yet, try reading anyway
|
||||
break;
|
||||
}
|
||||
return -ETIME;
|
||||
}
|
||||
break; // Data available
|
||||
}
|
||||
|
||||
// Read data from the pipe
|
||||
ssize_t bytes_read = read(drop->read_fd, buffer, capacity);
|
||||
if (bytes_read < 0) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
return -ETIME; // No data available yet
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (bytes_read == 0) {
|
||||
// EOF reached
|
||||
drop->eof_reached = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
drop->bytes_read += bytes_read;
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation UNUSED, bool success UNUSED) {
|
||||
if (!drop) return;
|
||||
|
||||
// Close any open file descriptor
|
||||
if (drop->read_fd >= 0) {
|
||||
close(drop->read_fd);
|
||||
drop->read_fd = -1;
|
||||
}
|
||||
|
||||
// Destroy the associated data offer
|
||||
// Note: Wayland doesn't have a way to report the operation type or success back to the source
|
||||
// in the same way as X11, as the source is notified through other means
|
||||
_GLFWWaylandDataOffer* offer = (_GLFWWaylandDataOffer*)drop->platform_data;
|
||||
if (offer) {
|
||||
destroy_data_offer(offer);
|
||||
}
|
||||
|
||||
// Free the mime types array (owned by drop object)
|
||||
if (drop->mime_types) {
|
||||
for (int i = 0; i < drop->mime_array_size; i++) {
|
||||
if (drop->mime_types[i])
|
||||
free((char*)drop->mime_types[i]);
|
||||
}
|
||||
free(drop->mime_types);
|
||||
drop->mime_types = NULL;
|
||||
}
|
||||
|
||||
// Free the heap-allocated drop data structure
|
||||
free(drop);
|
||||
}
|
||||
|
||||
|
|
|
|||
276
glfw/x11_window.c
vendored
276
glfw/x11_window.c
vendored
|
|
@ -1923,23 +1923,60 @@ static void processEvent(XEvent *event)
|
|||
else if (event->xclient.message_type == _glfw.x11.XdndDrop)
|
||||
{
|
||||
// The drag operation has finished by dropping on the window
|
||||
Time time = CurrentTime;
|
||||
|
||||
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
|
||||
return;
|
||||
|
||||
if (_glfw.x11.xdnd.format_priority > 0)
|
||||
if (_glfw.x11.xdnd.drag_accepted && _glfw.x11.xdnd.mimes_count > 0)
|
||||
{
|
||||
if (_glfw.x11.xdnd.version >= 1)
|
||||
time = event->xclient.data.l[2];
|
||||
// Heap-allocate drop data structure for chunked reading
|
||||
// The application is responsible for freeing this via glfwFinishDrop
|
||||
GLFWDropData* drop_data = calloc(1, sizeof(GLFWDropData));
|
||||
if (!drop_data) {
|
||||
_glfwInputError(GLFW_OUT_OF_MEMORY, "X11: Failed to allocate drop data");
|
||||
// Send XdndFinished to avoid leaving drag source hanging
|
||||
if (_glfw.x11.xdnd.version >= 2)
|
||||
{
|
||||
XEvent reply = { ClientMessage };
|
||||
reply.xclient.window = _glfw.x11.xdnd.source;
|
||||
reply.xclient.message_type = _glfw.x11.XdndFinished;
|
||||
reply.xclient.format = 32;
|
||||
reply.xclient.data.l[0] = window->x11.handle;
|
||||
reply.xclient.data.l[1] = 0; // Failure
|
||||
reply.xclient.data.l[2] = None;
|
||||
|
||||
// Request the chosen format from the source window
|
||||
XConvertSelection(_glfw.x11.display,
|
||||
_glfw.x11.XdndSelection,
|
||||
XInternAtom(_glfw.x11.display, _glfw.x11.xdnd.format, 0),
|
||||
_glfw.x11.XdndSelection,
|
||||
window->x11.handle,
|
||||
time);
|
||||
XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
|
||||
False, NoEventMask, &reply);
|
||||
XFlush(_glfw.x11.display);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Transfer ownership of mimes array from global to drop object
|
||||
drop_data->mime_types = (const char**)_glfw.x11.xdnd.mimes;
|
||||
drop_data->mime_count = _glfw.x11.xdnd.mimes_count;
|
||||
drop_data->mime_array_size = _glfw.x11.xdnd.mimes_array_size;
|
||||
// Clear global references since drop object now owns the mimes
|
||||
_glfw.x11.xdnd.mimes = NULL;
|
||||
_glfw.x11.xdnd.mimes_count = 0;
|
||||
_glfw.x11.xdnd.mimes_array_size = 0;
|
||||
|
||||
drop_data->current_mime = NULL;
|
||||
drop_data->read_fd = -1;
|
||||
drop_data->bytes_read = 0;
|
||||
drop_data->platform_data = NULL;
|
||||
drop_data->eof_reached = false;
|
||||
drop_data->current_data = NULL;
|
||||
drop_data->data_offset = 0;
|
||||
drop_data->x11_data_size = 0;
|
||||
// Store X11-specific drop state in the drop object
|
||||
drop_data->x11_drop_target = window->x11.handle;
|
||||
drop_data->x11_drop_time = (_glfw.x11.xdnd.version >= 1) ?
|
||||
(unsigned long)event->xclient.data.l[2] : CurrentTime;
|
||||
drop_data->x11_source = _glfw.x11.xdnd.source;
|
||||
drop_data->x11_version = _glfw.x11.xdnd.version;
|
||||
|
||||
_glfwInputDrop(window, drop_data);
|
||||
|
||||
// Note: drop_data is NOT freed here - application must call glfwFinishDrop
|
||||
}
|
||||
else if (_glfw.x11.xdnd.version >= 2)
|
||||
{
|
||||
|
|
@ -2046,40 +2083,9 @@ static void processEvent(XEvent *event)
|
|||
|
||||
case SelectionNotify:
|
||||
{
|
||||
if (event->xselection.property == _glfw.x11.XdndSelection)
|
||||
{
|
||||
// The converted data from the drag operation has arrived
|
||||
char* data;
|
||||
const unsigned long result =
|
||||
_glfwGetWindowPropertyX11(event->xselection.requestor,
|
||||
event->xselection.property,
|
||||
event->xselection.target,
|
||||
(unsigned char**) &data);
|
||||
|
||||
if (result)
|
||||
{
|
||||
_glfwInputDrop(window, _glfw.x11.xdnd.format, data, result);
|
||||
}
|
||||
|
||||
if (data)
|
||||
XFree(data);
|
||||
|
||||
if (_glfw.x11.xdnd.version >= 2)
|
||||
{
|
||||
XEvent reply = { ClientMessage };
|
||||
reply.xclient.window = _glfw.x11.xdnd.source;
|
||||
reply.xclient.message_type = _glfw.x11.XdndFinished;
|
||||
reply.xclient.format = 32;
|
||||
reply.xclient.data.l[0] = window->x11.handle;
|
||||
reply.xclient.data.l[1] = result;
|
||||
reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
|
||||
|
||||
XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
|
||||
False, NoEventMask, &reply);
|
||||
XFlush(_glfw.x11.display);
|
||||
}
|
||||
}
|
||||
|
||||
// SelectionNotify for XdndSelection is now handled synchronously
|
||||
// in _glfwPlatformReadDropData for chunked reading
|
||||
// This case is left here for clipboard operations
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -3868,3 +3874,181 @@ void _glfwPlatformUpdateDragState(_GLFWwindow* window) {
|
|||
XFlush(_glfw.x11.display);
|
||||
}
|
||||
|
||||
const char**
|
||||
_glfwPlatformGetDropMimeTypes(GLFWDropData* drop, int* count) {
|
||||
if (!drop || !count) return NULL;
|
||||
*count = drop->mime_count;
|
||||
return drop->mime_types;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
_glfwPlatformReadDropData(GLFWDropData* drop, const char* mime, void* buffer, size_t capacity, monotonic_t timeout) {
|
||||
if (!drop || !mime || !buffer || capacity == 0) return -EINVAL;
|
||||
|
||||
// Check if the MIME type is available
|
||||
bool mime_found = false;
|
||||
for (int i = 0; i < drop->mime_count; i++) {
|
||||
if (drop->mime_types[i] && strcmp(drop->mime_types[i], mime) == 0) {
|
||||
mime_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mime_found) return -ENOENT;
|
||||
|
||||
// If switching MIME types, free the previous data
|
||||
if (drop->current_mime && strcmp(drop->current_mime, mime) != 0) {
|
||||
if (drop->current_data) {
|
||||
XFree(drop->current_data);
|
||||
drop->current_data = NULL;
|
||||
}
|
||||
drop->x11_data_size = 0;
|
||||
drop->data_offset = 0;
|
||||
drop->current_mime = NULL;
|
||||
}
|
||||
|
||||
// If we need to fetch data for this MIME type
|
||||
if (drop->current_data == NULL || drop->current_mime == NULL ||
|
||||
strcmp(drop->current_mime, mime) != 0) {
|
||||
|
||||
Atom target_atom = XInternAtom(_glfw.x11.display, mime, False);
|
||||
|
||||
// Request the data via XConvertSelection
|
||||
XConvertSelection(_glfw.x11.display,
|
||||
_glfw.x11.XdndSelection,
|
||||
target_atom,
|
||||
_glfw.x11.XdndSelection,
|
||||
(Window)drop->x11_drop_target,
|
||||
(Time)drop->x11_drop_time);
|
||||
XFlush(_glfw.x11.display);
|
||||
|
||||
// Wait for SelectionNotify event with timeout
|
||||
monotonic_t start = monotonic();
|
||||
XEvent event;
|
||||
bool got_data = false;
|
||||
|
||||
while (true) {
|
||||
monotonic_t elapsed = monotonic() - start;
|
||||
if (timeout > 0 && elapsed >= timeout) return -ETIME;
|
||||
|
||||
int timeout_ms = (timeout > 0) ? monotonic_t_to_ms(timeout - elapsed) : 100;
|
||||
if (timeout_ms < 1) timeout_ms = 1;
|
||||
|
||||
// Use poll to wait for X11 events
|
||||
struct pollfd pfd = { .fd = ConnectionNumber(_glfw.x11.display), .events = POLLIN };
|
||||
int ret = poll(&pfd, 1, timeout_ms);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// Process pending events
|
||||
while (XPending(_glfw.x11.display)) {
|
||||
XNextEvent(_glfw.x11.display, &event);
|
||||
|
||||
if (event.type == SelectionNotify &&
|
||||
event.xselection.selection == _glfw.x11.XdndSelection &&
|
||||
event.xselection.property == _glfw.x11.XdndSelection) {
|
||||
|
||||
// Get the data
|
||||
unsigned char* data = NULL;
|
||||
unsigned long size = _glfwGetWindowPropertyX11(
|
||||
event.xselection.requestor,
|
||||
event.xselection.property,
|
||||
event.xselection.target,
|
||||
&data);
|
||||
|
||||
if (size > 0 && data) {
|
||||
drop->current_data = data;
|
||||
drop->x11_data_size = size;
|
||||
drop->data_offset = 0;
|
||||
// mime points to drop->mime_types entry, valid for duration of drop callback
|
||||
drop->current_mime = mime;
|
||||
got_data = true;
|
||||
} else {
|
||||
if (data) XFree(data);
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (got_data) break;
|
||||
|
||||
if (timeout == 0) {
|
||||
// Non-blocking mode - no data yet
|
||||
return -ETIME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read data from buffer
|
||||
if (drop->data_offset >= drop->x11_data_size) {
|
||||
return 0; // EOF
|
||||
}
|
||||
|
||||
size_t remaining = drop->x11_data_size - drop->data_offset;
|
||||
size_t to_read = (remaining < capacity) ? remaining : capacity;
|
||||
|
||||
memcpy(buffer, (unsigned char*)drop->current_data + drop->data_offset, to_read);
|
||||
drop->data_offset += to_read;
|
||||
|
||||
return (ssize_t)to_read;
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation, bool success) {
|
||||
if (!drop) return;
|
||||
|
||||
// Free current data if any
|
||||
if (drop->current_data) {
|
||||
XFree(drop->current_data);
|
||||
drop->current_data = NULL;
|
||||
}
|
||||
|
||||
// Send XdndFinished
|
||||
if (drop->x11_version >= 2)
|
||||
{
|
||||
Atom action_atom = None;
|
||||
if (success) {
|
||||
switch (operation) {
|
||||
case GLFW_DRAG_OPERATION_MOVE:
|
||||
action_atom = _glfw.x11.XdndActionMove;
|
||||
break;
|
||||
case GLFW_DRAG_OPERATION_COPY:
|
||||
action_atom = _glfw.x11.XdndActionCopy;
|
||||
break;
|
||||
case GLFW_DRAG_OPERATION_GENERIC:
|
||||
default:
|
||||
action_atom = _glfw.x11.XdndActionCopy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
XEvent reply = { ClientMessage };
|
||||
reply.xclient.window = (Window)drop->x11_source;
|
||||
reply.xclient.message_type = _glfw.x11.XdndFinished;
|
||||
reply.xclient.format = 32;
|
||||
reply.xclient.data.l[0] = (long)drop->x11_drop_target;
|
||||
reply.xclient.data.l[1] = success ? 1 : 0;
|
||||
reply.xclient.data.l[2] = action_atom;
|
||||
|
||||
XSendEvent(_glfw.x11.display, (Window)drop->x11_source,
|
||||
False, NoEventMask, &reply);
|
||||
XFlush(_glfw.x11.display);
|
||||
}
|
||||
|
||||
// Free the mime types array (owned by drop object, allocated by X11 XGetAtomNames)
|
||||
if (drop->mime_types) {
|
||||
for (int i = 0; i < drop->mime_array_size; i++) {
|
||||
if (drop->mime_types[i])
|
||||
XFree((char*)drop->mime_types[i]);
|
||||
}
|
||||
free(drop->mime_types);
|
||||
drop->mime_types = NULL;
|
||||
}
|
||||
|
||||
// Free the heap-allocated drop data structure
|
||||
free(drop);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue