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:
parent
19f24f2623
commit
0d465f71a3
3 changed files with 90 additions and 47 deletions
|
|
@ -763,7 +763,6 @@ - (void)doCommandBySelector:(SEL)selector
|
|||
|
||||
// Structure to hold async drag state
|
||||
typedef struct {
|
||||
NSURL* destinationURL; // URL to write to
|
||||
void (^completionHandler)(NSError*); // Completion block to call
|
||||
NSFileHandle* fileHandle; // File handle for writing
|
||||
bool finished; // Whether writing is complete
|
||||
|
|
@ -792,10 +791,6 @@ - (void)doCommandBySelector:(SEL)selector
|
|||
[state->fileHandle release];
|
||||
state->fileHandle = nil;
|
||||
}
|
||||
if (state->destinationURL) {
|
||||
[state->destinationURL release];
|
||||
state->destinationURL = nil;
|
||||
}
|
||||
free(state);
|
||||
}
|
||||
free(data->mime_type);
|
||||
|
|
@ -930,38 +925,27 @@ - (void)filePromiseProvider:(NSFilePromiseProvider*)filePromiseProvider
|
|||
}
|
||||
|
||||
// Create the file promise state
|
||||
char *mt = _glfw_strdup(mimeType);
|
||||
GLFWFilePromiseState* state = calloc(1, sizeof(GLFWFilePromiseState));
|
||||
if (!state) {
|
||||
free(source_data);
|
||||
if (!state || !mt) {
|
||||
free(source_data); free(mt); free(state);
|
||||
[fileHandle closeFile];
|
||||
completionHandler([NSError errorWithDomain:NSPOSIXErrorDomain code:ENOMEM userInfo:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
state->destinationURL = [url retain];
|
||||
state->completionHandler = [completionHandler copy];
|
||||
state->fileHandle = [fileHandle retain];
|
||||
state->finished = false;
|
||||
state->errorCode = 0;
|
||||
|
||||
source_data->window_id = windowId;
|
||||
source_data->mime_type = _glfw_strdup(mimeType);
|
||||
source_data->mime_type = mt;
|
||||
source_data->write_fd = -1;
|
||||
source_data->finished = false;
|
||||
source_data->error_code = 0;
|
||||
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
|
||||
if (!add_ns_pending_drag_source_data(window, source_data)) {
|
||||
// Call completion handler with memory error before cleanup
|
||||
|
|
@ -1619,7 +1603,7 @@ - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
|
|||
// Count total types across all pasteboard items plus 2 for uri-list and text/plain
|
||||
size_t max_types = 2;
|
||||
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:@{}];
|
||||
for (NSFilePromiseReceiver *receiver in receivers) max_types += [receiver.fileTypes count];
|
||||
|
||||
|
|
@ -1758,6 +1742,7 @@ - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
|||
drop_data->eof_reached = false;
|
||||
drop_data->current_data = NULL;
|
||||
drop_data->data_offset = 0;
|
||||
drop_data->data_is_file_promise = false;
|
||||
// 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);
|
||||
|
|
@ -1789,11 +1774,12 @@ - (void)draggingSession:(NSDraggingSession *)session
|
|||
{
|
||||
(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);
|
||||
if (operation == NSDragOperationNone) { // drag was canceled
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
- (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->platform_data) return -EINVAL;
|
||||
|
||||
|
|
@ -4255,16 +4242,18 @@ void _glfwPlatformUpdateDragState(_GLFWwindow* window) {
|
|||
// 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];
|
||||
[(NSObject*)drop->current_data release];
|
||||
drop->current_data = NULL;
|
||||
}
|
||||
drop->data_offset = 0;
|
||||
drop->data_is_file_promise = false;
|
||||
free(drop->current_mime); drop->current_mime = NULL;
|
||||
}
|
||||
|
||||
// If we need to fetch data for this MIME type
|
||||
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
|
||||
if (strcmp(mime, "text/uri-list") == 0) {
|
||||
NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};
|
||||
|
|
@ -4284,39 +4273,87 @@ void _glfwPlatformUpdateDragState(_GLFWwindow* window) {
|
|||
NSString* str = strings[0];
|
||||
data = [str dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if (data == nil) {
|
||||
// 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 (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;
|
||||
drop->current_data = [data retain];
|
||||
if (!data && !file_promise) return -ENOENT;
|
||||
drop->current_mime = _glfw_strdup(mime);
|
||||
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
|
||||
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;
|
||||
if (drop->data_is_file_promise) {
|
||||
if (drop->file_io_error == nil && drop->file_handle == nil) return -EAGAIN;
|
||||
if (drop->file_io_error) {
|
||||
NSError *err = drop->file_io_error;
|
||||
if ([err.domain isEqualToString:NSPOSIXErrorDomain]) return -err.code;
|
||||
NSError *underlyingError = err.userInfo[NSUnderlyingErrorKey];
|
||||
if (underlyingError && [underlyingError.domain isEqualToString:NSPOSIXErrorDomain]) return -underlyingError.code;
|
||||
return -EIO;
|
||||
}
|
||||
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
|
||||
_glfwPlatformFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation UNUSED, bool success UNUSED) {
|
||||
if (!drop) return;
|
||||
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
|
||||
if (drop->current_data) {
|
||||
[(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
|
||||
void* current_data; // NSData* (Cocoa) or unsigned char* from XGetWindowProperty (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
|
||||
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)
|
||||
#endif
|
||||
};
|
||||
|
||||
// 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) {
|
||||
if (_PyBytes_Resize(&ans, pos) != 0) return NULL;
|
||||
return Py_NewRef(ans);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
errno = -ret;
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return NULL;
|
||||
|
|
@ -801,7 +800,7 @@ try_sending_drag_source_data(id_type timer_id UNUSED, void *callback_data UNUSED
|
|||
|
||||
static void
|
||||
drag_source_callback(GLFWwindow *window UNUSED, const char* mime_type, GLFWDragSourceData* source_data) {
|
||||
PyObject *data;
|
||||
PyObject *data = NULL;
|
||||
if (mime_type == NULL) {
|
||||
ds.is_active = false;
|
||||
Py_CLEAR(ds.drag_data);
|
||||
|
|
|
|||
Loading…
Reference in a new issue