More work on cocoa darg drop still not functional
I think I am going to rewrite the API to integrate with the event loop, now that I have a good handle on the semantics of DnD in the two major platforms.
This commit is contained in:
@@ -763,7 +763,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
|
|
||||||
// Structure to hold async drag state
|
// Structure to hold async drag state
|
||||||
typedef struct {
|
typedef struct {
|
||||||
NSURL* destinationURL; // URL to write to
|
|
||||||
void (^completionHandler)(NSError*); // Completion block to call
|
void (^completionHandler)(NSError*); // Completion block to call
|
||||||
NSFileHandle* fileHandle; // File handle for writing
|
NSFileHandle* fileHandle; // File handle for writing
|
||||||
bool finished; // Whether writing is complete
|
bool finished; // Whether writing is complete
|
||||||
@@ -792,10 +791,6 @@ cleanup_ns_drag_source_data(GLFWDragSourceData* data) {
|
|||||||
[state->fileHandle release];
|
[state->fileHandle release];
|
||||||
state->fileHandle = nil;
|
state->fileHandle = nil;
|
||||||
}
|
}
|
||||||
if (state->destinationURL) {
|
|
||||||
[state->destinationURL release];
|
|
||||||
state->destinationURL = nil;
|
|
||||||
}
|
|
||||||
free(state);
|
free(state);
|
||||||
}
|
}
|
||||||
free(data->mime_type);
|
free(data->mime_type);
|
||||||
@@ -930,38 +925,27 @@ cleanup_all_ns_pending_drag_source_data(_GLFWwindow* window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the file promise state
|
// Create the file promise state
|
||||||
|
char *mt = _glfw_strdup(mimeType);
|
||||||
GLFWFilePromiseState* state = calloc(1, sizeof(GLFWFilePromiseState));
|
GLFWFilePromiseState* state = calloc(1, sizeof(GLFWFilePromiseState));
|
||||||
if (!state) {
|
if (!state || !mt) {
|
||||||
free(source_data);
|
free(source_data); free(mt); free(state);
|
||||||
[fileHandle closeFile];
|
[fileHandle closeFile];
|
||||||
completionHandler([NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]);
|
completionHandler([NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->destinationURL = [url retain];
|
|
||||||
state->completionHandler = [completionHandler copy];
|
state->completionHandler = [completionHandler copy];
|
||||||
state->fileHandle = [fileHandle retain];
|
state->fileHandle = [fileHandle retain];
|
||||||
state->finished = false;
|
state->finished = false;
|
||||||
state->errorCode = 0;
|
state->errorCode = 0;
|
||||||
|
|
||||||
source_data->window_id = windowId;
|
source_data->window_id = windowId;
|
||||||
source_data->mime_type = _glfw_strdup(mimeType);
|
source_data->mime_type = mt;
|
||||||
source_data->write_fd = -1;
|
source_data->write_fd = -1;
|
||||||
source_data->finished = false;
|
source_data->finished = false;
|
||||||
source_data->error_code = 0;
|
source_data->error_code = 0;
|
||||||
source_data->platform_data = state;
|
source_data->platform_data = state;
|
||||||
|
|
||||||
if (!source_data->mime_type) {
|
|
||||||
[state->fileHandle closeFile];
|
|
||||||
[state->fileHandle release];
|
|
||||||
[state->destinationURL release];
|
|
||||||
Block_release(state->completionHandler);
|
|
||||||
free(state);
|
|
||||||
free(source_data);
|
|
||||||
completionHandler([NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track this source data for cleanup on cancellation
|
// Track this source data for cleanup on cancellation
|
||||||
if (!add_ns_pending_drag_source_data(window, source_data)) {
|
if (!add_ns_pending_drag_source_data(window, source_data)) {
|
||||||
// Call completion handler with memory error before cleanup
|
// Call completion handler with memory error before cleanup
|
||||||
@@ -1619,7 +1603,7 @@ static void freeFilteredDragMimes(_GLFWwindow* window, int old_count, int new_co
|
|||||||
// Count total types across all pasteboard items plus 2 for uri-list and text/plain
|
// Count total types across all pasteboard items plus 2 for uri-list and text/plain
|
||||||
size_t max_types = 2;
|
size_t max_types = 2;
|
||||||
for (NSPasteboardItem* item in pasteboard.pasteboardItems) max_types += [item.types count];
|
for (NSPasteboardItem* item in pasteboard.pasteboardItems) max_types += [item.types count];
|
||||||
NSArray *classes = [NSArray arrayWithObject:[NSFilePromiseReceiver class]];
|
NSArray *classes = @[[NSFilePromiseReceiver class]];
|
||||||
NSArray *receivers = [pasteboard readObjectsForClasses:classes options:@{}];
|
NSArray *receivers = [pasteboard readObjectsForClasses:classes options:@{}];
|
||||||
for (NSFilePromiseReceiver *receiver in receivers) max_types += [receiver.fileTypes count];
|
for (NSFilePromiseReceiver *receiver in receivers) max_types += [receiver.fileTypes count];
|
||||||
|
|
||||||
@@ -1758,6 +1742,7 @@ static void freeFilteredDragMimes(_GLFWwindow* window, int old_count, int new_co
|
|||||||
drop_data->eof_reached = false;
|
drop_data->eof_reached = false;
|
||||||
drop_data->current_data = NULL;
|
drop_data->current_data = NULL;
|
||||||
drop_data->data_offset = 0;
|
drop_data->data_offset = 0;
|
||||||
|
drop_data->data_is_file_promise = false;
|
||||||
// Check if the drop is from this application
|
// Check if the drop is from this application
|
||||||
// draggingSource returns the source object if the drag started in this application
|
// draggingSource returns the source object if the drag started in this application
|
||||||
bool from_self = ([sender draggingSource] != nil);
|
bool from_self = ([sender draggingSource] != nil);
|
||||||
@@ -1789,11 +1774,12 @@ static void freeFilteredDragMimes(_GLFWwindow* window, int old_count, int new_co
|
|||||||
{
|
{
|
||||||
(void)session;
|
(void)session;
|
||||||
(void)screenPoint;
|
(void)screenPoint;
|
||||||
(void)operation;
|
if (operation == NSDragOperationNone) { // drag was canceled
|
||||||
// Clean up all pending drag source data
|
// Clean up all pending drag source data
|
||||||
cleanup_all_ns_pending_drag_source_data(window);
|
cleanup_all_ns_pending_drag_source_data(window);
|
||||||
// Notify the application that the drag source is closed
|
// Notify the application that the drag source is closed
|
||||||
_glfwInputDragSourceRequest(window, NULL, NULL);
|
_glfwInputDragSourceRequest(window, NULL, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)hasMarkedText
|
- (BOOL)hasMarkedText
|
||||||
@@ -4113,7 +4099,8 @@ int _glfwPlatformStartDrag(_GLFWwindow* window,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t _glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* data, size_t size) {
|
ssize_t
|
||||||
|
_glfwPlatformSendDragData(GLFWDragSourceData* source_data, const void* data, size_t size) {
|
||||||
if (!source_data || source_data->finished) return -EINVAL;
|
if (!source_data || source_data->finished) return -EINVAL;
|
||||||
if (!source_data->platform_data) return -EINVAL;
|
if (!source_data->platform_data) return -EINVAL;
|
||||||
|
|
||||||
@@ -4255,16 +4242,18 @@ _glfwPlatformReadDropData(GLFWDropData* drop, const char* mime, void* buffer, si
|
|||||||
// If switching MIME types, release previous data
|
// If switching MIME types, release previous data
|
||||||
if (drop->current_mime && strcmp(drop->current_mime, mime) != 0) {
|
if (drop->current_mime && strcmp(drop->current_mime, mime) != 0) {
|
||||||
if (drop->current_data) {
|
if (drop->current_data) {
|
||||||
[(NSData*)drop->current_data release];
|
[(NSObject*)drop->current_data release];
|
||||||
drop->current_data = NULL;
|
drop->current_data = NULL;
|
||||||
}
|
}
|
||||||
drop->data_offset = 0;
|
drop->data_offset = 0;
|
||||||
|
drop->data_is_file_promise = false;
|
||||||
free(drop->current_mime); drop->current_mime = NULL;
|
free(drop->current_mime); drop->current_mime = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we need to fetch data for this MIME type
|
// If we need to fetch data for this MIME type
|
||||||
if (drop->current_data == NULL || drop->current_mime == NULL) {
|
if (drop->current_data == NULL || drop->current_mime == NULL) {
|
||||||
NSData* data = nil;
|
NSData* data = nil; NSFilePromiseReceiver *file_promise = nil;
|
||||||
|
drop->data_is_file_promise = false;
|
||||||
// Handle special MIME types
|
// Handle special MIME types
|
||||||
if (strcmp(mime, "text/uri-list") == 0) {
|
if (strcmp(mime, "text/uri-list") == 0) {
|
||||||
NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};
|
NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};
|
||||||
@@ -4284,39 +4273,87 @@ _glfwPlatformReadDropData(GLFWDropData* drop, const char* mime, void* buffer, si
|
|||||||
NSString* str = strings[0];
|
NSString* str = strings[0];
|
||||||
data = [str dataUsingEncoding:NSUTF8StringEncoding];
|
data = [str dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
if (data == nil) {
|
||||||
// Try to read data for other MIME types using UTI
|
// Try to read data for other MIME types using UTI
|
||||||
NSString* uti = mime_to_uti(mime);
|
NSString* uti = mime_to_uti(mime);
|
||||||
if (uti) {
|
if (uti) {
|
||||||
NSPasteboardType pbType = [pasteboard availableTypeFromArray:@[uti]];
|
NSPasteboardType pbType = [pasteboard availableTypeFromArray:@[uti]];
|
||||||
if (pbType) {
|
if (pbType) data = [pasteboard dataForType:pbType];
|
||||||
data = [pasteboard dataForType:pbType];
|
}
|
||||||
|
if (data == nil) {
|
||||||
|
// look in the file promise providers
|
||||||
|
NSArray *receivers = [pasteboard readObjectsForClasses:@[[NSFilePromiseReceiver class]] options:@{}];
|
||||||
|
for (NSFilePromiseReceiver *receiver in receivers) {
|
||||||
|
for (NSString *uti in receiver.fileTypes) {
|
||||||
|
const char *q = uti_to_mime(uti);
|
||||||
|
if (q && strcmp(q, mime) == 0) {
|
||||||
|
file_promise = receiver;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file_promise) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!data) return -ENOENT;
|
if (!data && !file_promise) return -ENOENT;
|
||||||
drop->current_data = [data retain];
|
|
||||||
drop->current_mime = _glfw_strdup(mime);
|
drop->current_mime = _glfw_strdup(mime);
|
||||||
drop->data_offset = 0;
|
drop->data_offset = 0;
|
||||||
|
if (file_promise != nil) {
|
||||||
|
drop->data_is_file_promise = true;
|
||||||
|
[file_promise receivePromisedFilesAtDestination:[NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES]
|
||||||
|
options:@{} operationQueue:[NSOperationQueue mainQueue] reader:^(NSURL *fileURL, NSError *errorOrNil) {
|
||||||
|
if (errorOrNil) {
|
||||||
|
NSLog(@"Error receiving file: %@: %@", fileURL, errorOrNil);
|
||||||
|
drop->file_io_error = [errorOrNil retain];
|
||||||
|
} else {
|
||||||
|
NSError *err = nil;
|
||||||
|
drop->file_handle = [NSFileHandle fileHandleForReadingFromURL:fileURL error:&err];
|
||||||
|
if (err) drop->file_io_error = [err retain];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
drop->current_data = [data retain];
|
||||||
|
drop->data_is_file_promise = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read data from buffer
|
// Read data from buffer
|
||||||
NSData* data = (NSData*)drop->current_data;
|
if (drop->data_is_file_promise) {
|
||||||
NSUInteger dataLength = [data length];
|
if (drop->file_io_error == nil && drop->file_handle == nil) return -EAGAIN;
|
||||||
|
if (drop->file_io_error) {
|
||||||
if (drop->data_offset >= dataLength) return 0; // EOF
|
NSError *err = drop->file_io_error;
|
||||||
NSUInteger remaining = dataLength - drop->data_offset;
|
if ([err.domain isEqualToString:NSPOSIXErrorDomain]) return -err.code;
|
||||||
NSUInteger to_read = (remaining < capacity) ? remaining : capacity;
|
NSError *underlyingError = err.userInfo[NSUnderlyingErrorKey];
|
||||||
|
if (underlyingError && [underlyingError.domain isEqualToString:NSPOSIXErrorDomain]) return -underlyingError.code;
|
||||||
[data getBytes:buffer range:NSMakeRange(drop->data_offset, to_read)];
|
return -EIO;
|
||||||
drop->data_offset += to_read;
|
}
|
||||||
return (ssize_t)to_read;
|
int fd = ((NSFileHandle*)drop->file_handle).fileDescriptor;
|
||||||
|
ssize_t bytesRead; do {
|
||||||
|
bytesRead = read(fd, buffer, capacity);
|
||||||
|
} while (bytesRead == -1 && errno == EINTR);
|
||||||
|
if (bytesRead < 0) return -errno;
|
||||||
|
return bytesRead;
|
||||||
|
} else {
|
||||||
|
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
|
void
|
||||||
_glfwPlatformFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation UNUSED, bool success UNUSED) {
|
_glfwPlatformFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation UNUSED, bool success UNUSED) {
|
||||||
if (!drop) return;
|
if (!drop) return;
|
||||||
free(drop->current_mime); drop->current_mime = NULL;
|
free(drop->current_mime); drop->current_mime = NULL;
|
||||||
|
if (drop->file_io_error) [(NSError*)drop->file_io_error release];
|
||||||
|
drop->file_io_error = NULL;
|
||||||
|
if (drop->file_handle) [(NSFileHandle*)drop->file_handle closeFile];
|
||||||
|
drop->file_handle = NULL;
|
||||||
// Release the retained current data
|
// Release the retained current data
|
||||||
if (drop->current_data) {
|
if (drop->current_data) {
|
||||||
[(NSData*)drop->current_data release];
|
[(NSData*)drop->current_data release];
|
||||||
|
|||||||
7
glfw/internal.h
vendored
7
glfw/internal.h
vendored
@@ -98,12 +98,19 @@ struct GLFWDropData {
|
|||||||
// Platform-specific data fields
|
// Platform-specific data fields
|
||||||
void* current_data; // NSData* (Cocoa) or unsigned char* from XGetWindowProperty (X11)
|
void* current_data; // NSData* (Cocoa) or unsigned char* from XGetWindowProperty (X11)
|
||||||
size_t data_offset; // Read offset in current data (Cocoa/X11)
|
size_t data_offset; // Read offset in current data (Cocoa/X11)
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// Cocoa specific fields
|
||||||
|
bool data_is_file_promise; // true if current_data is a file promise provider rather than NSData
|
||||||
|
void *file_handle, *file_io_error;
|
||||||
|
#endif
|
||||||
|
#ifdef _GLFW_X11
|
||||||
// X11-specific fields
|
// X11-specific fields
|
||||||
size_t x11_data_size; // Size of current X11 data
|
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_target; // Window handle where the drop occurred (X11)
|
||||||
unsigned long x11_drop_time; // Time from the drop event (X11)
|
unsigned long x11_drop_time; // Time from the drop event (X11)
|
||||||
unsigned long x11_source; // Source window for XdndFinished (X11)
|
unsigned long x11_source; // Source window for XdndFinished (X11)
|
||||||
int x11_version; // Xdnd protocol version (X11)
|
int x11_version; // Xdnd protocol version (X11)
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// Drag source data structure for chunked writing of drag data
|
// Drag source data structure for chunked writing of drag data
|
||||||
|
|||||||
@@ -721,8 +721,7 @@ read_drop_data(GLFWDropData *drop, const char *mime) {
|
|||||||
} else if (ret == 0) {
|
} else if (ret == 0) {
|
||||||
if (_PyBytes_Resize(&ans, pos) != 0) return NULL;
|
if (_PyBytes_Resize(&ans, pos) != 0) return NULL;
|
||||||
return Py_NewRef(ans);
|
return Py_NewRef(ans);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
errno = -ret;
|
errno = -ret;
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -801,7 +800,7 @@ try_sending_drag_source_data(id_type timer_id UNUSED, void *callback_data UNUSED
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
drag_source_callback(GLFWwindow *window UNUSED, const char* mime_type, GLFWDragSourceData* source_data) {
|
drag_source_callback(GLFWwindow *window UNUSED, const char* mime_type, GLFWDragSourceData* source_data) {
|
||||||
PyObject *data;
|
PyObject *data = NULL;
|
||||||
if (mime_type == NULL) {
|
if (mime_type == NULL) {
|
||||||
ds.is_active = false;
|
ds.is_active = false;
|
||||||
Py_CLEAR(ds.drag_data);
|
Py_CLEAR(ds.drag_data);
|
||||||
|
|||||||
Reference in New Issue
Block a user