From d2e87c53449189fc00c32e52f54030fca383f377 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Wed, 19 Jan 2022 20:00:56 +0100 Subject: [PATCH 01/19] macos: Make repeated drags work in testdnd example --- gdk/macos/GdkMacosWindow.c | 4 ---- gdk/macos/gdkmacosdrag.c | 1 + gdk/macos/gdkmacossurface.c | 13 ++++++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index 0eb1ec7ca8a..b8b14a64e61 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -665,10 +665,6 @@ typedef NSString *CALayerContentsGravity; return NO; } --(void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation -{ -} - -(void)setStyleMask:(NSWindowStyleMask)styleMask { gboolean was_fullscreen; diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index c9e2dd19a6a..1fa162c1eb8 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -344,6 +344,7 @@ gdk_drag_update (GdkDrag *drag, self->did_update = TRUE; } + gdk_drag_set_selected_action (drag, suggested_action); gdk_drag_set_actions (drag, possible_actions); } diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index d10992ffd47..43ae1bdc364 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -416,13 +416,12 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, double dx, double dy) { + static NSInteger draggingSessionNumber = 1; GdkMacosSurface *self = (GdkMacosSurface *)surface; GdkMacosSurface *drag_surface; GdkMacosDrag *drag; GdkCursor *cursor; - GdkSeat *seat; - double px; - double py; + GdkMacosWindow *window; int sx; int sy; @@ -432,8 +431,10 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, g_assert (GDK_IS_MACOS_DEVICE (device)); g_assert (GDK_IS_CONTENT_PROVIDER (content)); - seat = gdk_device_get_seat (device); - gdk_macos_device_query_state (device, surface, NULL, &px, &py, NULL); + window = (GdkMacosWindow*) _gdk_macos_surface_get_native (GDK_MACOS_SURFACE(surface)); + if (!GDK_IS_MACOS_WINDOW(window)) + return NULL; + _gdk_macos_surface_get_root_coords (GDK_MACOS_SURFACE (surface), &sx, &sy); drag_surface = _gdk_macos_surface_new (GDK_MACOS_DISPLAY (surface->display), GDK_SURFACE_TEMP, @@ -448,6 +449,8 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, NULL); g_clear_object (&drag_surface); + _gdk_macos_display_set_drag([window gdkDisplay], draggingSessionNumber++, GDK_DRAG(drag)); + cursor = gdk_drag_get_cursor (GDK_DRAG (drag), gdk_drag_get_selected_action (GDK_DRAG (drag))); gdk_drag_set_cursor (GDK_DRAG (drag), cursor); -- GitLab From c964cf278f1626d24d379723b4f35ab91e958a8d Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 22 Jan 2022 15:35:29 +0100 Subject: [PATCH 02/19] macos: drag can drag, but UI doesn't get any updates after The idea is to start a "normal" macOS drag session via `NSWindow.beginDraggingSessionWithItems:event:source:`. This starts a new DraggingSession on the drag side. The session seems to work once. It looks like the main loop is broken after one DnD action. --- gdk/macos/GdkMacosWindow.c | 37 ++++++++++++++++ gdk/macos/GdkMacosWindow.h | 2 +- gdk/macos/gdkmacosdisplay-private.h | 1 + gdk/macos/gdkmacosdisplay.c | 10 +++++ gdk/macos/gdkmacosdrag-private.h | 8 +++- gdk/macos/gdkmacosdrag.c | 65 ++++++++++++++++------------- gdk/macos/gdkmacossurface.c | 33 +++++++++++---- 7 files changed, 116 insertions(+), 40 deletions(-) diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index b8b14a64e61..e74c0f264ed 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -29,6 +29,7 @@ #include "gdkmacosclipboard-private.h" #include "gdkmacosdisplay-private.h" +#include "gdkmacosdrag-private.h" #include "gdkmacosdrop-private.h" #include "gdkmacosmonitor-private.h" #include "gdkmacossurface-private.h" @@ -576,6 +577,40 @@ typedef NSString *CALayerContentsGravity; initialResizeLocation = convert_nspoint_to_screen (self, [self mouseLocationOutsideOfEventStream]); } +// Drag + +- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context +{ + // TODO: return NSDragOperation*, based on GDK_ACTION_* + GdkDrag *drag = _gdk_macos_display_find_drag ([self gdkDisplay], [session draggingSequenceNumber]); + NSDragOperation operations = _gdk_macos_drag_operation (GDK_MACOS_DRAG(drag)); + + return operations; +} + +- (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint +{ + // TODO: generate GdkEvent? + [self draggingSession: session movedToPoint: screenPoint]; +} + +- (void)draggingSession:(NSDraggingSession *)session movedToPoint:(NSPoint)screenPoint +{ + GdkDrag *drag; + int x, y; + + drag = _gdk_macos_display_find_drag ([self gdkDisplay], [session draggingSequenceNumber]); + _gdk_macos_display_from_display_coords ([self gdkDisplay], screenPoint.x, screenPoint.y, &x, &y); + _gdk_macos_drag_surface_move(GDK_MACOS_DRAG(drag), x, y); +} + +- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation +{ + _gdk_macos_display_set_drag ([self gdkDisplay], [session draggingSequenceNumber], NULL); +} + +// Drop + -(NSDragOperation)draggingEntered:(id )sender { NSPoint location = [sender draggingLocation]; @@ -604,6 +639,7 @@ typedef NSString *CALayerContentsGravity; -(void)draggingEnded:(id )sender { + // [sender draggingSource] != nil if in-app dnd _gdk_macos_display_set_drop ([self gdkDisplay], [sender draggingSequenceNumber], NULL); } @@ -649,6 +685,7 @@ typedef NSString *CALayerContentsGravity; if (drop == NULL) return NO; + gdk_drop_set_actions (drop, GDK_MACOS_DROP(drop)->preferred_action); gdk_drop_emit_drop_event (drop, TRUE, location.x, diff --git a/gdk/macos/GdkMacosWindow.h b/gdk/macos/GdkMacosWindow.h index 1cf9ec805c0..e5caf8b5935 100644 --- a/gdk/macos/GdkMacosWindow.h +++ b/gdk/macos/GdkMacosWindow.h @@ -32,7 +32,7 @@ #define GDK_IS_MACOS_WINDOW(obj) ([obj isKindOfClass:[GdkMacosWindow class]]) -@interface GdkMacosWindow : NSWindow { +@interface GdkMacosWindow : NSWindow { GdkMacosSurface *gdk_surface; BOOL inMove; diff --git a/gdk/macos/gdkmacosdisplay-private.h b/gdk/macos/gdkmacosdisplay-private.h index 83ae435e49d..122d004cec6 100644 --- a/gdk/macos/gdkmacosdisplay-private.h +++ b/gdk/macos/gdkmacosdisplay-private.h @@ -161,6 +161,7 @@ void _gdk_macos_display_warp_pointer (GdkMacosDisp int x, int y); NSEvent *_gdk_macos_display_get_nsevent (GdkEvent *event); +NSEvent *_gdk_macos_display_get_last_nsevent (void); GdkDrag *_gdk_macos_display_find_drag (GdkMacosDisplay *self, NSInteger sequence_number); GdkDrop *_gdk_macos_display_find_drop (GdkMacosDisplay *self, diff --git a/gdk/macos/gdkmacosdisplay.c b/gdk/macos/gdkmacosdisplay.c index 41be42cfbee..f1a3f06b5fa 100644 --- a/gdk/macos/gdkmacosdisplay.c +++ b/gdk/macos/gdkmacosdisplay.c @@ -1028,6 +1028,16 @@ _gdk_macos_display_get_nsevent (GdkEvent *event) return NULL; } +NSEvent * +_gdk_macos_display_get_last_nsevent () +{ + const GdkToNSEventMap *map = g_queue_peek_tail(&event_map); + if (map) + return map->nsevent; + + return NULL; +} + GdkDrag * _gdk_macos_display_find_drag (GdkMacosDisplay *self, NSInteger sequence_number) diff --git a/gdk/macos/gdkmacosdrag-private.h b/gdk/macos/gdkmacosdrag-private.h index 98075f27ef5..44ec2b23c6e 100644 --- a/gdk/macos/gdkmacosdrag-private.h +++ b/gdk/macos/gdkmacosdrag-private.h @@ -62,8 +62,12 @@ struct _GdkMacosDragClass GdkDragClass parent_class; }; -GType gdk_macos_drag_get_type (void) G_GNUC_CONST; -gboolean _gdk_macos_drag_begin (GdkMacosDrag *self); +GType gdk_macos_drag_get_type (void) G_GNUC_CONST; +gboolean _gdk_macos_drag_begin (GdkMacosDrag *self); +void _gdk_macos_drag_surface_move (GdkMacosDrag *self, + double x_root, + double y_root); +NSDragOperation _gdk_macos_drag_operation (GdkMacosDrag *self); G_END_DECLS diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index 1fa162c1eb8..06e891391e1 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -187,29 +187,6 @@ gdk_macos_drag_set_cursor (GdkDrag *drag, [nscursor set]; } -static gboolean -drag_grab (GdkMacosDrag *self) -{ - GdkSeat *seat; - - g_assert (GDK_IS_MACOS_DRAG (self)); - - seat = gdk_device_get_seat (gdk_drag_get_device (GDK_DRAG (self))); - - if (gdk_seat_grab (seat, - GDK_SURFACE (self->drag_surface), - GDK_SEAT_CAPABILITY_ALL_POINTING, - FALSE, - self->cursor, - NULL, - NULL, - NULL) != GDK_GRAB_SUCCESS) - return FALSE; - - g_set_object (&self->drag_seat, seat); - - return TRUE; -} static void drag_ungrab (GdkMacosDrag *self) @@ -332,11 +309,6 @@ gdk_drag_update (GdkDrag *drag, &suggested_action, &possible_actions); - if (GDK_IS_MACOS_SURFACE (self->drag_surface)) - _gdk_macos_surface_move (GDK_MACOS_SURFACE (self->drag_surface), - x_root - self->hot_x, - y_root - self->hot_y); - if (!self->did_update) { self->start_x = self->last_x; @@ -614,6 +586,28 @@ gdk_macos_drag_init (GdkMacosDrag *self) { } +NSDragOperation +_gdk_macos_drag_operation (GdkMacosDrag *self) +{ + NSDragOperation operation = NSDragOperationNone; + GdkDragAction actions; + + g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), NSDragOperationNone); + + actions = gdk_drag_get_actions(GDK_DRAG(self)); + + if (actions & GDK_ACTION_LINK) + operation |= NSDragOperationLink; + + if (actions & GDK_ACTION_MOVE) + operation |= NSDragOperationMove; + + if (actions & GDK_ACTION_COPY) + operation |= NSDragOperationCopy; + + return operation; +} + gboolean _gdk_macos_drag_begin (GdkMacosDrag *self) { @@ -621,5 +615,18 @@ _gdk_macos_drag_begin (GdkMacosDrag *self) _gdk_macos_surface_show (GDK_MACOS_SURFACE (self->drag_surface)); - return drag_grab (self); + return TRUE; +} + +void +_gdk_macos_drag_surface_move (GdkMacosDrag *self, + double x_root, + double y_root) +{ + g_return_if_fail (GDK_IS_MACOS_DRAG (self)); + + if (GDK_IS_MACOS_SURFACE (self->drag_surface)) + _gdk_macos_surface_move (GDK_MACOS_SURFACE (self->drag_surface), + x_root - self->hot_x, + y_root - self->hot_y); } diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 43ae1bdc364..72b989d6156 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -416,12 +416,15 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, double dx, double dy) { - static NSInteger draggingSessionNumber = 1; GdkMacosSurface *self = (GdkMacosSurface *)surface; GdkMacosSurface *drag_surface; GdkMacosDrag *drag; - GdkCursor *cursor; + GdkSeat *seat; GdkMacosWindow *window; + NSView *view; + NSEvent *event; + NSDraggingItem* dragItem; + NSDraggingSession *session; int sx; int sy; @@ -431,10 +434,26 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, g_assert (GDK_IS_MACOS_DEVICE (device)); g_assert (GDK_IS_CONTENT_PROVIDER (content)); + seat = gdk_device_get_seat (device); + + view = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface)); window = (GdkMacosWindow*) _gdk_macos_surface_get_native (GDK_MACOS_SURFACE(surface)); if (!GDK_IS_MACOS_WINDOW(window)) return NULL; + event = _gdk_macos_display_get_last_nsevent(); + if (!event) + return NULL; + + dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: @"FOO"]; + [dragItem setDraggingFrame: NSMakeRect(0, 0, 10, 10)]; + + session = [view beginDraggingSessionWithItems:[NSArray arrayWithObjects: dragItem, nil] + event:event + source:window]; + + [dragItem release]; + _gdk_macos_surface_get_root_coords (GDK_MACOS_SURFACE (surface), &sx, &sy); drag_surface = _gdk_macos_surface_new (GDK_MACOS_DISPLAY (surface->display), GDK_SURFACE_TEMP, @@ -449,18 +468,16 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, NULL); g_clear_object (&drag_surface); - _gdk_macos_display_set_drag([window gdkDisplay], draggingSessionNumber++, GDK_DRAG(drag)); - - cursor = gdk_drag_get_cursor (GDK_DRAG (drag), - gdk_drag_get_selected_action (GDK_DRAG (drag))); - gdk_drag_set_cursor (GDK_DRAG (drag), cursor); - if (!_gdk_macos_drag_begin (drag)) { g_object_unref (drag); return NULL; } + _gdk_macos_display_set_drag([window gdkDisplay], [session draggingSequenceNumber], GDK_DRAG(drag)); + + gdk_seat_ungrab (seat); + /* Hold a reference until drop_done is called */ g_object_ref (drag); -- GitLab From 580c3c79346d15c40024447c6e4fe75930320731 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 22 Jan 2022 17:06:14 +0100 Subject: [PATCH 03/19] macos: Start DnD action from an idle loop This is how it was done for GTK3. I'm not sure if we really need this. If not, can call the idle function directly, since it makes for a nice sepatation anyhow. It still looks like the main loop is broken after a DnD action is performed. No updates seem to take place in the application during DnD. --- gdk/macos/gdkmacossurface.c | 54 ++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 72b989d6156..cc0d8db4dd0 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -408,6 +408,32 @@ gdk_macos_surface_get_geometry (GdkSurface *surface, *height = surface->height; } +static gboolean +gdk_macos_surface_drag_begin_idle(gpointer arg) +{ + GdkDrag *drag = GDK_DRAG (arg); + GdkSurface *surface = gdk_drag_get_surface(drag); + NSView *view = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface)); + GdkMacosWindow *window = (GdkMacosWindow*) _gdk_macos_surface_get_native (GDK_MACOS_SURFACE(surface)); + + NSEvent *event = _gdk_macos_display_get_last_nsevent(); + if (!event) + return G_SOURCE_REMOVE; + + NSDraggingItem *dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: @"FOO"]; + [dragItem setDraggingFrame: NSMakeRect(0, 0, 10, 10)]; + + NSDraggingSession *session = [view beginDraggingSessionWithItems:[NSArray arrayWithObjects: dragItem, nil] + event:event + source:window]; + + [dragItem release]; + + _gdk_macos_display_set_drag([window gdkDisplay], [session draggingSequenceNumber], GDK_DRAG(drag)); + + return G_SOURCE_REMOVE; +} + static GdkDrag * gdk_macos_surface_drag_begin (GdkSurface *surface, GdkDevice *device, @@ -420,11 +446,6 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, GdkMacosSurface *drag_surface; GdkMacosDrag *drag; GdkSeat *seat; - GdkMacosWindow *window; - NSView *view; - NSEvent *event; - NSDraggingItem* dragItem; - NSDraggingSession *session; int sx; int sy; @@ -436,24 +457,6 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, seat = gdk_device_get_seat (device); - view = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface)); - window = (GdkMacosWindow*) _gdk_macos_surface_get_native (GDK_MACOS_SURFACE(surface)); - if (!GDK_IS_MACOS_WINDOW(window)) - return NULL; - - event = _gdk_macos_display_get_last_nsevent(); - if (!event) - return NULL; - - dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: @"FOO"]; - [dragItem setDraggingFrame: NSMakeRect(0, 0, 10, 10)]; - - session = [view beginDraggingSessionWithItems:[NSArray arrayWithObjects: dragItem, nil] - event:event - source:window]; - - [dragItem release]; - _gdk_macos_surface_get_root_coords (GDK_MACOS_SURFACE (surface), &sx, &sy); drag_surface = _gdk_macos_surface_new (GDK_MACOS_DISPLAY (surface->display), GDK_SURFACE_TEMP, @@ -474,10 +477,11 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, return NULL; } - _gdk_macos_display_set_drag([window gdkDisplay], [session draggingSequenceNumber], GDK_DRAG(drag)); - gdk_seat_ungrab (seat); + /* drag will begin in an idle handler to avoid nested run loops */ + g_idle_add_full (G_PRIORITY_HIGH_IDLE, gdk_macos_surface_drag_begin_idle, drag, NULL); + /* Hold a reference until drop_done is called */ g_object_ref (drag); -- GitLab From f5c15ee8a128b3404498d8082ce09d3307042967 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Wed, 26 Jan 2022 21:38:06 +0100 Subject: [PATCH 04/19] macos: Make DnD work with dragImage The Drag object stored in GdkMacosDisplay by sequence number. So we can match a drag and drop operation internally (each DnD operation in macOS is assigned a sequence number). Note that `dragImage:...` is a deprecated protocol. The code should be replaced by `beginDraggingSessionWithItems:event:source:`. There are some cases that cause segfaults still. --- gdk/macos/GdkMacosWindow.c | 11 +++-- gdk/macos/gdkmacossurface-private.h | 2 + gdk/macos/gdkmacossurface.c | 63 +++++++++++++++++++++++++---- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index e74c0f264ed..3c0ff3654a3 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -581,7 +581,6 @@ typedef NSString *CALayerContentsGravity; - (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context { - // TODO: return NSDragOperation*, based on GDK_ACTION_* GdkDrag *drag = _gdk_macos_display_find_drag ([self gdkDisplay], [session draggingSequenceNumber]); NSDragOperation operations = _gdk_macos_drag_operation (GDK_MACOS_DRAG(drag)); @@ -590,7 +589,13 @@ typedef NSString *CALayerContentsGravity; - (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint { - // TODO: generate GdkEvent? + GdkDrag *drag = _gdk_macos_display_find_drag ([self gdkDisplay], INTERIM_DRAGGING_SEQUENCE_NUMBER); + + g_return_if_fail(drag); + + _gdk_macos_display_set_drag ([self gdkDisplay], [session draggingSequenceNumber], GDK_DRAG(drag)); + _gdk_macos_display_set_drag ([self gdkDisplay], INTERIM_DRAGGING_SEQUENCE_NUMBER, NULL); + [self draggingSession: session movedToPoint: screenPoint]; } @@ -601,7 +606,7 @@ typedef NSString *CALayerContentsGravity; drag = _gdk_macos_display_find_drag ([self gdkDisplay], [session draggingSequenceNumber]); _gdk_macos_display_from_display_coords ([self gdkDisplay], screenPoint.x, screenPoint.y, &x, &y); - _gdk_macos_drag_surface_move(GDK_MACOS_DRAG(drag), x, y); + _gdk_macos_drag_surface_move (GDK_MACOS_DRAG(drag), x, y); } - (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation diff --git a/gdk/macos/gdkmacossurface-private.h b/gdk/macos/gdkmacossurface-private.h index 5f1f551f93b..ad4bd0cf8ed 100644 --- a/gdk/macos/gdkmacossurface-private.h +++ b/gdk/macos/gdkmacossurface-private.h @@ -37,6 +37,8 @@ G_BEGIN_DECLS #define GDK_IS_MACOS_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_MACOS_SURFACE)) #define GDK_MACOS_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_MACOS_SURFACE, GdkMacosSurfaceClass)) +#define INTERIM_DRAGGING_SEQUENCE_NUMBER (-1) + struct _GdkMacosSurface { GdkSurface parent_instance; diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index cc0d8db4dd0..b0f1014ee73 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -408,9 +408,27 @@ gdk_macos_surface_get_geometry (GdkSurface *surface, *height = surface->height; } +static NSImage* +_gdk_macos_surface_to_ns_image (GdkMacosSurface *self) +{ + // From: https://developer.apple.com/forums/thread/88315 + NSView *view = _gdk_macos_surface_get_view (self); + NSSize mySize = view.bounds.size; + NSSize imgSize = NSMakeSize (mySize.width, mySize.height); + + NSBitmapImageRep *bir = [view bitmapImageRepForCachingDisplayInRect:[view bounds]]; + [bir setSize:imgSize]; + [view cacheDisplayInRect:[view bounds] toBitmapImageRep:bir]; + + NSImage* image = [[NSImage alloc]initWithSize:imgSize]; + [image addRepresentation:bir]; + return image; +} + static gboolean gdk_macos_surface_drag_begin_idle(gpointer arg) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; GdkDrag *drag = GDK_DRAG (arg); GdkSurface *surface = gdk_drag_get_surface(drag); NSView *view = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface)); @@ -420,16 +438,47 @@ gdk_macos_surface_drag_begin_idle(gpointer arg) if (!event) return G_SOURCE_REMOVE; - NSDraggingItem *dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: @"FOO"]; - [dragItem setDraggingFrame: NSMakeRect(0, 0, 10, 10)]; + NSPasteboard *pasteboard; + NSPoint point; + NSSet *types; + NSImage *drag_image; + + pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + + // types = _gdk_macos_clipboard_to_ns_type (info->target_list); + + [pasteboard declareTypes:[NSArray arrayWithObjects: NSPasteboardTypeString, nil] owner:nil]; + + // [types release]; + + /* FIXME: If the event isn't a mouse event, use the global cursor position instead */ + point = [event locationInWindow]; + + drag_image = _gdk_macos_surface_to_ns_image (surface); + + if (drag_image == NULL) + { + return G_SOURCE_REMOVE; + } + + // HACK: At this point we do not have the draggingSequence number. + // Ideally, we have a custom DraggingSource object that wraps our GDK Drag object. + _gdk_macos_display_set_drag([window gdkDisplay], INTERIM_DRAGGING_SEQUENCE_NUMBER, GDK_DRAG(drag)); - NSDraggingSession *session = [view beginDraggingSessionWithItems:[NSArray arrayWithObjects: dragItem, nil] - event:event - source:window]; + // I'd like to use beginDraggingSessionWithItems:event:source: + // instead, but this call tends to block UI updates in our app, + // so we use the deprecated dragImage: method for now. + [window dragImage:drag_image + at:point + offset:NSZeroSize + event:event + pasteboard:pasteboard + source:window + slideBack:YES]; - [dragItem release]; + [drag_image release]; - _gdk_macos_display_set_drag([window gdkDisplay], [session draggingSequenceNumber], GDK_DRAG(drag)); + [pool release]; return G_SOURCE_REMOVE; } -- GitLab From 115e9f96d4cc3be5737060345f18c5f4df1a63ab Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 30 Jan 2022 14:20:17 +0100 Subject: [PATCH 05/19] macos: split position and actions update Now actions can be updated independently. This may come in useful if we want to use the macos drag protocol, instead of simulating our own. --- gdk/macos/gdkmacosdrag-private.h | 12 ++++---- gdk/macos/gdkmacosdrag.c | 51 ++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/gdk/macos/gdkmacosdrag-private.h b/gdk/macos/gdkmacosdrag-private.h index 44ec2b23c6e..107858bdc76 100644 --- a/gdk/macos/gdkmacosdrag-private.h +++ b/gdk/macos/gdkmacosdrag-private.h @@ -63,11 +63,13 @@ struct _GdkMacosDragClass }; GType gdk_macos_drag_get_type (void) G_GNUC_CONST; -gboolean _gdk_macos_drag_begin (GdkMacosDrag *self); -void _gdk_macos_drag_surface_move (GdkMacosDrag *self, - double x_root, - double y_root); -NSDragOperation _gdk_macos_drag_operation (GdkMacosDrag *self); +gboolean _gdk_macos_drag_begin (GdkMacosDrag *self); +void _gdk_macos_drag_surface_move (GdkMacosDrag *self, + double x_root, + double y_root); +void _gdk_macos_drag_set_actions (GdkDrag *drag, + GdkModifierType mods); +NSDragOperation _gdk_macos_drag_operation (GdkMacosDrag *self); G_END_DECLS diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index 06e891391e1..59eb6c91a6e 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -290,9 +290,7 @@ gdk_drag_get_current_actions (GdkModifierType state, static void gdk_drag_update (GdkDrag *drag, double x_root, - double y_root, - GdkModifierType mods, - guint32 evtime) + double y_root) { GdkMacosDrag *self = (GdkMacosDrag *)drag; GdkDragAction suggested_action; @@ -303,21 +301,12 @@ gdk_drag_update (GdkDrag *drag, self->last_x = x_root; self->last_y = y_root; - gdk_drag_get_current_actions (mods, - GDK_BUTTON_PRIMARY, - gdk_drag_get_actions (drag), - &suggested_action, - &possible_actions); - if (!self->did_update) { self->start_x = self->last_x; self->start_y = self->last_y; self->did_update = TRUE; } - - gdk_drag_set_selected_action (drag, suggested_action); - gdk_drag_set_actions (drag, possible_actions); } static gboolean @@ -325,7 +314,6 @@ gdk_dnd_handle_motion_event (GdkDrag *drag, GdkEvent *event) { double x, y; - int x_root, y_root; g_assert (GDK_IS_MACOS_DRAG (drag)); g_assert (event != NULL); @@ -335,12 +323,11 @@ gdk_dnd_handle_motion_event (GdkDrag *drag, return FALSE; gdk_event_get_position (event, &x, &y); - x_root = event->surface->x + x; - y_root = event->surface->y + y; - gdk_drag_update (drag, x_root, y_root, - gdk_event_get_modifier_state (event), - gdk_event_get_time (event)); - + gdk_drag_update (drag, + event->surface->x + x, + event->surface->y + y); + _gdk_macos_drag_set_actions (drag, + gdk_event_get_modifier_state (event)); return TRUE; } @@ -467,10 +454,8 @@ gdk_dnd_handle_key_event (GdkDrag *drag, self->last_y); } - gdk_drag_update (drag, - self->last_x, self->last_y, - state, - gdk_event_get_time (event)); + gdk_drag_update (drag, self->last_x, self->last_y); + _gdk_macos_drag_set_actions (drag, state); return TRUE; } @@ -630,3 +615,23 @@ _gdk_macos_drag_surface_move (GdkMacosDrag *self, x_root - self->hot_x, y_root - self->hot_y); } + +void +_gdk_macos_drag_set_actions (GdkDrag *drag, + GdkModifierType mods) +{ + GdkMacosDrag *self = (GdkMacosDrag *)drag; + GdkDragAction suggested_action; + GdkDragAction possible_actions; + + g_assert (GDK_IS_MACOS_DRAG (self)); + + gdk_drag_get_current_actions (mods, + GDK_BUTTON_PRIMARY, + gdk_drag_get_actions (drag), + &suggested_action, + &possible_actions); + + gdk_drag_set_selected_action (drag, suggested_action); + gdk_drag_set_actions (drag, possible_actions); +} -- GitLab From f352b55f3e31e9a8fceb5230ad157a7d79b09786 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 30 Jan 2022 15:36:34 +0100 Subject: [PATCH 06/19] macos: Convert Format mime types to suitable Pastboard types Default to a string, if we have no compatible content formats. --- gdk/macos/gdkmacossurface.c | 44 +++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index b0f1014ee73..d1123ae115b 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -36,6 +36,7 @@ #include "gdksurfaceprivate.h" #include "gdkmacosdevice.h" +#include "gdkmacosclipboard-private.h" #include "gdkmacosdevice-private.h" #include "gdkmacosdisplay-private.h" #include "gdkmacosdrag-private.h" @@ -425,36 +426,55 @@ _gdk_macos_surface_to_ns_image (GdkMacosSurface *self) return image; } +static NSArray* +content_formats_to_pastboard_types (GdkContentFormats *formats) { + gsize n_mime_types; + const char *const *mime_types = gdk_content_formats_get_mime_types (formats, &n_mime_types); + NSMutableArray *types = [NSMutableArray arrayWithCapacity: (NSUInteger) n_mime_types]; + + g_message("Content formats: %lu", n_mime_types); + for (gsize i = 0; i < n_mime_types; i++) { + NSPasteboardType ptype = _gdk_macos_clipboard_to_ns_type(mime_types[i], NULL); + // g_message ("Adding type for mime type '%s' -> %s", mime_types[i], ptype); + if (ptype) + [types addObject: ptype]; + } + + // If no types can be set, default to a string. + // This can happen if we only use GType's for our (internal) DnD. + if ([types count] == 0) + [types addObject: NSPasteboardTypeString]; + + return types; +} + static gboolean gdk_macos_surface_drag_begin_idle(gpointer arg) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; GdkDrag *drag = GDK_DRAG (arg); + GdkContentProvider *content = gdk_drag_get_content (drag); + GdkContentFormats *formats = gdk_content_provider_ref_formats (content); GdkSurface *surface = gdk_drag_get_surface(drag); - NSView *view = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface)); GdkMacosWindow *window = (GdkMacosWindow*) _gdk_macos_surface_get_native (GDK_MACOS_SURFACE(surface)); - NSEvent *event = _gdk_macos_display_get_last_nsevent(); - if (!event) - return G_SOURCE_REMOVE; - NSPasteboard *pasteboard; NSPoint point; - NSSet *types; + NSArray *types; NSImage *drag_image; - pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - - // types = _gdk_macos_clipboard_to_ns_type (info->target_list); + if (!event) + return G_SOURCE_REMOVE; - [pasteboard declareTypes:[NSArray arrayWithObjects: NSPasteboardTypeString, nil] owner:nil]; + pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - // [types release]; + types = content_formats_to_pastboard_types (formats); + [pasteboard declareTypes:types owner:nil]; /* FIXME: If the event isn't a mouse event, use the global cursor position instead */ point = [event locationInWindow]; - drag_image = _gdk_macos_surface_to_ns_image (surface); + drag_image = _gdk_macos_surface_to_ns_image (GDK_MACOS_SURFACE (surface)); if (drag_image == NULL) { -- GitLab From 44fc1a940390f54838b7e1493d8e7b86c6375a66 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 30 Jan 2022 17:47:59 +0100 Subject: [PATCH 07/19] macos: Create a separate dragging source object This isolates the drag state from the main NSWindow in which it was previously. --- gdk/macos/GdkMacosDraggingSource.c | 74 +++++++++++++++++++++++++++++ gdk/macos/GdkMacosDraggingSource.h | 36 ++++++++++++++ gdk/macos/GdkMacosWindow.c | 39 --------------- gdk/macos/gdkmacossurface-private.h | 2 - gdk/macos/gdkmacossurface.c | 7 +-- gdk/macos/meson.build | 1 + 6 files changed, 113 insertions(+), 46 deletions(-) create mode 100644 gdk/macos/GdkMacosDraggingSource.c create mode 100644 gdk/macos/GdkMacosDraggingSource.h diff --git a/gdk/macos/GdkMacosDraggingSource.c b/gdk/macos/GdkMacosDraggingSource.c new file mode 100644 index 00000000000..12e09588fad --- /dev/null +++ b/gdk/macos/GdkMacosDraggingSource.c @@ -0,0 +1,74 @@ +/* GdkMacosDraggingSource.c + * + * Copyright 2020 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#import "GdkMacosDraggingSource.h" + +#include "gdkmacosdrag-private.h" +#include "gdkmacosdisplay-private.h" +#include "gdkdebug.h" + +@implementation GdkMacosDraggingSource + +-(id)init:(GdkDrag *) drag +{ + g_return_val_if_fail (GDK_IS_MACOS_DRAG (drag), self); + + self->gdk_drag = drag; + return self; +} + +- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context +{ + GdkDrag *drag = self->gdk_drag; + // _gdk_macos_drag_set_actions () + NSDragOperation operations = _gdk_macos_drag_operation (GDK_MACOS_DRAG(drag)); + + return operations; +} + +- (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint +{ + GdkDrag *drag = self->gdk_drag; + GdkDisplay *display = gdk_drag_get_display (drag); + + _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), [session draggingSequenceNumber], GDK_DRAG(drag)); + + [self draggingSession: session movedToPoint: screenPoint]; +} + +- (void)draggingSession:(NSDraggingSession *)session movedToPoint:(NSPoint)screenPoint +{ + GdkDrag *drag = self->gdk_drag; + GdkDisplay *display = gdk_drag_get_display (drag); + int x, y; + + _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y); + _gdk_macos_drag_surface_move (GDK_MACOS_DRAG(drag), x, y); +} + +- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation +{ + GdkDisplay *display = gdk_drag_get_display (self->gdk_drag); + _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), [session draggingSequenceNumber], NULL); +} + +@end diff --git a/gdk/macos/GdkMacosDraggingSource.h b/gdk/macos/GdkMacosDraggingSource.h new file mode 100644 index 00000000000..6dbed966acb --- /dev/null +++ b/gdk/macos/GdkMacosDraggingSource.h @@ -0,0 +1,36 @@ +/* GdkMacosDraggingSource.h + * + * Copyright © 2020 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#import +#import + +#include + +#define GDK_IS_MACOS_DRAGGING_SOURCE(obj) ((obj) && [obj isKindOfClass:[GdkMacosDraggingSource class]]) + +@interface GdkMacosDraggingSource : NSObject +{ + GdkDisplay *gdk_display; + GdkDrag *gdk_drag; +} + +-(id)init: (GdkDrag *) drag; + +@end diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index 3c0ff3654a3..030bd86ce3e 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -577,45 +577,6 @@ typedef NSString *CALayerContentsGravity; initialResizeLocation = convert_nspoint_to_screen (self, [self mouseLocationOutsideOfEventStream]); } -// Drag - -- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context -{ - GdkDrag *drag = _gdk_macos_display_find_drag ([self gdkDisplay], [session draggingSequenceNumber]); - NSDragOperation operations = _gdk_macos_drag_operation (GDK_MACOS_DRAG(drag)); - - return operations; -} - -- (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint -{ - GdkDrag *drag = _gdk_macos_display_find_drag ([self gdkDisplay], INTERIM_DRAGGING_SEQUENCE_NUMBER); - - g_return_if_fail(drag); - - _gdk_macos_display_set_drag ([self gdkDisplay], [session draggingSequenceNumber], GDK_DRAG(drag)); - _gdk_macos_display_set_drag ([self gdkDisplay], INTERIM_DRAGGING_SEQUENCE_NUMBER, NULL); - - [self draggingSession: session movedToPoint: screenPoint]; -} - -- (void)draggingSession:(NSDraggingSession *)session movedToPoint:(NSPoint)screenPoint -{ - GdkDrag *drag; - int x, y; - - drag = _gdk_macos_display_find_drag ([self gdkDisplay], [session draggingSequenceNumber]); - _gdk_macos_display_from_display_coords ([self gdkDisplay], screenPoint.x, screenPoint.y, &x, &y); - _gdk_macos_drag_surface_move (GDK_MACOS_DRAG(drag), x, y); -} - -- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation -{ - _gdk_macos_display_set_drag ([self gdkDisplay], [session draggingSequenceNumber], NULL); -} - -// Drop - -(NSDragOperation)draggingEntered:(id )sender { NSPoint location = [sender draggingLocation]; diff --git a/gdk/macos/gdkmacossurface-private.h b/gdk/macos/gdkmacossurface-private.h index ad4bd0cf8ed..5f1f551f93b 100644 --- a/gdk/macos/gdkmacossurface-private.h +++ b/gdk/macos/gdkmacossurface-private.h @@ -37,8 +37,6 @@ G_BEGIN_DECLS #define GDK_IS_MACOS_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_MACOS_SURFACE)) #define GDK_MACOS_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_MACOS_SURFACE, GdkMacosSurfaceClass)) -#define INTERIM_DRAGGING_SEQUENCE_NUMBER (-1) - struct _GdkMacosSurface { GdkSurface parent_instance; diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index d1123ae115b..365ba519c0e 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -24,6 +24,7 @@ #include #import "GdkMacosView.h" +#import "GdkMacosDraggingSource.h" #include "gdkmacossurface-private.h" @@ -481,10 +482,6 @@ gdk_macos_surface_drag_begin_idle(gpointer arg) return G_SOURCE_REMOVE; } - // HACK: At this point we do not have the draggingSequence number. - // Ideally, we have a custom DraggingSource object that wraps our GDK Drag object. - _gdk_macos_display_set_drag([window gdkDisplay], INTERIM_DRAGGING_SEQUENCE_NUMBER, GDK_DRAG(drag)); - // I'd like to use beginDraggingSessionWithItems:event:source: // instead, but this call tends to block UI updates in our app, // so we use the deprecated dragImage: method for now. @@ -493,7 +490,7 @@ gdk_macos_surface_drag_begin_idle(gpointer arg) offset:NSZeroSize event:event pasteboard:pasteboard - source:window + source:[[GdkMacosDraggingSource alloc] init: GDK_DRAG (drag)] slideBack:YES]; [drag_image release]; diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build index bd7bbb53245..51084832c46 100644 --- a/gdk/macos/meson.build +++ b/gdk/macos/meson.build @@ -27,6 +27,7 @@ gdk_macos_sources = files([ 'GdkMacosLayer.c', 'GdkMacosTile.c', 'GdkMacosView.c', + 'GdkMacosDraggingSource.c', 'GdkMacosWindow.c', ]) -- GitLab From b2dc75c870d44a0206faf872bb656e2f2e3b0c58 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 31 Jan 2022 16:39:40 +0100 Subject: [PATCH 08/19] macos: Move DnD activity to macOS handlers Do not rely on event handling for drop actions. Instead use the callbacks of macOS. --- gdk/macos/GdkMacosDraggingSource.c | 25 +++- gdk/macos/GdkMacosWindow.h | 2 +- gdk/macos/gdkmacosdrag-private.h | 22 ++- gdk/macos/gdkmacosdrag.c | 227 ++++------------------------- gdk/macos/gdkmacossurface.c | 1 - 5 files changed, 60 insertions(+), 217 deletions(-) diff --git a/gdk/macos/GdkMacosDraggingSource.c b/gdk/macos/GdkMacosDraggingSource.c index 12e09588fad..41fcb4bbd40 100644 --- a/gdk/macos/GdkMacosDraggingSource.c +++ b/gdk/macos/GdkMacosDraggingSource.c @@ -39,20 +39,25 @@ - (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context { GdkDrag *drag = self->gdk_drag; - // _gdk_macos_drag_set_actions () - NSDragOperation operations = _gdk_macos_drag_operation (GDK_MACOS_DRAG(drag)); + GdkDisplay *display = gdk_drag_get_display (drag); + + GdkModifierType state = _gdk_macos_display_get_current_keyboard_modifiers (GDK_MACOS_DISPLAY (display)); + _gdk_macos_drag_set_actions (drag, state); - return operations; + return _gdk_macos_drag_operation (GDK_MACOS_DRAG(drag)); } - (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint { GdkDrag *drag = self->gdk_drag; GdkDisplay *display = gdk_drag_get_display (drag); + int x, y; _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), [session draggingSequenceNumber], GDK_DRAG(drag)); - - [self draggingSession: session movedToPoint: screenPoint]; + _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y); + _gdk_macos_drag_set_start_position (drag, x, y); + _gdk_macos_drag_set_last_position (drag, x, y); + _gdk_macos_drag_surface_move (GDK_MACOS_DRAG(drag), x, y); } - (void)draggingSession:(NSDraggingSession *)session movedToPoint:(NSPoint)screenPoint @@ -62,12 +67,20 @@ int x, y; _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y); + _gdk_macos_drag_set_last_position (drag, x, y); _gdk_macos_drag_surface_move (GDK_MACOS_DRAG(drag), x, y); } - (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation { - GdkDisplay *display = gdk_drag_get_display (self->gdk_drag); + GdkDrag *drag = self->gdk_drag; + GdkDisplay *display = gdk_drag_get_display (drag); + + if (gdk_drag_get_selected_action (drag) != 0) + g_signal_emit_by_name (drag, "drop-performed"); + else + gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET); + _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), [session draggingSequenceNumber], NULL); } diff --git a/gdk/macos/GdkMacosWindow.h b/gdk/macos/GdkMacosWindow.h index e5caf8b5935..1cf9ec805c0 100644 --- a/gdk/macos/GdkMacosWindow.h +++ b/gdk/macos/GdkMacosWindow.h @@ -32,7 +32,7 @@ #define GDK_IS_MACOS_WINDOW(obj) ([obj isKindOfClass:[GdkMacosWindow class]]) -@interface GdkMacosWindow : NSWindow { +@interface GdkMacosWindow : NSWindow { GdkMacosSurface *gdk_surface; BOOL inMove; diff --git a/gdk/macos/gdkmacosdrag-private.h b/gdk/macos/gdkmacosdrag-private.h index 107858bdc76..4d33e451187 100644 --- a/gdk/macos/gdkmacosdrag-private.h +++ b/gdk/macos/gdkmacosdrag-private.h @@ -62,14 +62,20 @@ struct _GdkMacosDragClass GdkDragClass parent_class; }; -GType gdk_macos_drag_get_type (void) G_GNUC_CONST; -gboolean _gdk_macos_drag_begin (GdkMacosDrag *self); -void _gdk_macos_drag_surface_move (GdkMacosDrag *self, - double x_root, - double y_root); -void _gdk_macos_drag_set_actions (GdkDrag *drag, - GdkModifierType mods); -NSDragOperation _gdk_macos_drag_operation (GdkMacosDrag *self); +GType gdk_macos_drag_get_type (void) G_GNUC_CONST; +gboolean _gdk_macos_drag_begin (GdkMacosDrag *self); +void _gdk_macos_drag_surface_move (GdkMacosDrag *self, + double x_root, + double y_root); +void _gdk_macos_drag_set_start_position (GdkDrag *drag, + int start_x, + int start_y); +void _gdk_macos_drag_set_last_position (GdkDrag *drag, + int last_x, + int last_y); +void _gdk_macos_drag_set_actions (GdkDrag *drag, + GdkModifierType mods); +NSDragOperation _gdk_macos_drag_operation (GdkMacosDrag *self); G_END_DECLS diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index 59eb6c91a6e..38ece196718 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -287,206 +287,6 @@ gdk_drag_get_current_actions (GdkModifierType state, } } -static void -gdk_drag_update (GdkDrag *drag, - double x_root, - double y_root) -{ - GdkMacosDrag *self = (GdkMacosDrag *)drag; - GdkDragAction suggested_action; - GdkDragAction possible_actions; - - g_assert (GDK_IS_MACOS_DRAG (self)); - - self->last_x = x_root; - self->last_y = y_root; - - if (!self->did_update) - { - self->start_x = self->last_x; - self->start_y = self->last_y; - self->did_update = TRUE; - } -} - -static gboolean -gdk_dnd_handle_motion_event (GdkDrag *drag, - GdkEvent *event) -{ - double x, y; - - g_assert (GDK_IS_MACOS_DRAG (drag)); - g_assert (event != NULL); - - /* Ignore motion while doing zoomback */ - if (GDK_MACOS_DRAG (drag)->cancelled) - return FALSE; - - gdk_event_get_position (event, &x, &y); - gdk_drag_update (drag, - event->surface->x + x, - event->surface->y + y); - _gdk_macos_drag_set_actions (drag, - gdk_event_get_modifier_state (event)); - return TRUE; -} - -static gboolean -gdk_dnd_handle_grab_broken_event (GdkDrag *drag, - GdkEvent *event) -{ - GdkMacosDrag *self = GDK_MACOS_DRAG (drag); - gboolean is_implicit = gdk_grab_broken_event_get_implicit (event); - GdkSurface *grab_surface = gdk_grab_broken_event_get_grab_surface (event); - - /* Don't cancel if we break the implicit grab from the initial button_press. */ - if (is_implicit || grab_surface == (GdkSurface *)self->drag_surface) - return FALSE; - - if (gdk_event_get_device (event) != gdk_drag_get_device (drag)) - return FALSE; - - gdk_drag_cancel (drag, GDK_DRAG_CANCEL_ERROR); - - return TRUE; -} - -static gboolean -gdk_dnd_handle_button_event (GdkDrag *drag, - GdkEvent *event) -{ - GdkMacosDrag *self = GDK_MACOS_DRAG (drag); - - g_assert (GDK_IS_MACOS_DRAG (self)); - g_assert (event != NULL); - -#if 0 - /* FIXME: Check the button matches */ - if (event->button != self->button) - return FALSE; -#endif - - if (gdk_drag_get_selected_action (drag) != 0) - g_signal_emit_by_name (drag, "drop-performed"); - else - gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET); - - return TRUE; -} - -static gboolean -gdk_dnd_handle_key_event (GdkDrag *drag, - GdkEvent *event) -{ - GdkMacosDrag *self = GDK_MACOS_DRAG (drag); - GdkModifierType state; - GdkDevice *pointer; - GdkSeat *seat; - int dx, dy; - - dx = dy = 0; - state = gdk_event_get_modifier_state (event); - seat = gdk_event_get_seat (event); - pointer = gdk_seat_get_pointer (seat); - - if (event->event_type == GDK_KEY_PRESS) - { - guint keyval = gdk_key_event_get_keyval (event); - - switch (keyval) - { - case GDK_KEY_Escape: - gdk_drag_cancel (drag, GDK_DRAG_CANCEL_USER_CANCELLED); - return TRUE; - - case GDK_KEY_space: - case GDK_KEY_Return: - case GDK_KEY_ISO_Enter: - case GDK_KEY_KP_Enter: - case GDK_KEY_KP_Space: - if (gdk_drag_get_selected_action (drag) != 0) - g_signal_emit_by_name (drag, "drop-performed"); - else - gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET); - - return TRUE; - - case GDK_KEY_Up: - case GDK_KEY_KP_Up: - dy = (state & GDK_ALT_MASK) ? -BIG_STEP : -SMALL_STEP; - break; - - case GDK_KEY_Down: - case GDK_KEY_KP_Down: - dy = (state & GDK_ALT_MASK) ? BIG_STEP : SMALL_STEP; - break; - - case GDK_KEY_Left: - case GDK_KEY_KP_Left: - dx = (state & GDK_ALT_MASK) ? -BIG_STEP : -SMALL_STEP; - break; - - case GDK_KEY_Right: - case GDK_KEY_KP_Right: - dx = (state & GDK_ALT_MASK) ? BIG_STEP : SMALL_STEP; - break; - - default: - break; - } - } - - /* The state is not yet updated in the event, so we need - * to query it here. We could use XGetModifierMapping, but - * that would be overkill. - */ - gdk_macos_device_query_state (pointer, NULL, NULL, NULL, NULL, &state); - - if (dx != 0 || dy != 0) - { - GdkDisplay *display = gdk_event_get_display ((GdkEvent *)event); - - self->last_x += dx; - self->last_y += dy; - - _gdk_macos_display_warp_pointer (GDK_MACOS_DISPLAY (display), - self->last_x, - self->last_y); - } - - gdk_drag_update (drag, self->last_x, self->last_y); - _gdk_macos_drag_set_actions (drag, state); - - return TRUE; -} - -static gboolean -gdk_macos_drag_handle_event (GdkDrag *drag, - GdkEvent *event) -{ - g_assert (GDK_IS_MACOS_DRAG (drag)); - g_assert (event != NULL); - - switch ((guint) event->event_type) - { - case GDK_MOTION_NOTIFY: - return gdk_dnd_handle_motion_event (drag, event); - - case GDK_BUTTON_RELEASE: - return gdk_dnd_handle_button_event (drag, event); - - case GDK_KEY_PRESS: - case GDK_KEY_RELEASE: - return gdk_dnd_handle_key_event (drag, event); - - case GDK_GRAB_BROKEN: - return gdk_dnd_handle_grab_broken_event (drag, event); - - default: - return FALSE; - } -} - static void gdk_macos_drag_finalize (GObject *object) { @@ -556,7 +356,6 @@ gdk_macos_drag_class_init (GdkMacosDragClass *klass) drag_class->set_cursor = gdk_macos_drag_set_cursor; drag_class->cancel = gdk_macos_drag_cancel; drag_class->drop_performed = gdk_macos_drag_drop_performed; - drag_class->handle_event = gdk_macos_drag_handle_event; properties [PROP_DRAG_SURFACE] = g_param_spec_object ("drag-surface", NULL, NULL, @@ -616,6 +415,32 @@ _gdk_macos_drag_surface_move (GdkMacosDrag *self, y_root - self->hot_y); } +void +_gdk_macos_drag_set_start_position (GdkDrag *drag, + int start_x, + int start_y) +{ + GdkMacosDrag *self = (GdkMacosDrag *)drag; + + g_assert (GDK_IS_MACOS_DRAG (self)); + + self->start_x = start_x; + self->start_y = start_y; +} + +void +_gdk_macos_drag_set_last_position (GdkDrag *drag, + int last_x, + int last_y) +{ + GdkMacosDrag *self = (GdkMacosDrag *)drag; + + g_assert (GDK_IS_MACOS_DRAG (self)); + + self->last_x = last_x; + self->last_y = last_y; +} + void _gdk_macos_drag_set_actions (GdkDrag *drag, GdkModifierType mods) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 365ba519c0e..2ddebe906cf 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -433,7 +433,6 @@ content_formats_to_pastboard_types (GdkContentFormats *formats) { const char *const *mime_types = gdk_content_formats_get_mime_types (formats, &n_mime_types); NSMutableArray *types = [NSMutableArray arrayWithCapacity: (NSUInteger) n_mime_types]; - g_message("Content formats: %lu", n_mime_types); for (gsize i = 0; i < n_mime_types; i++) { NSPasteboardType ptype = _gdk_macos_clipboard_to_ns_type(mime_types[i], NULL); // g_message ("Adding type for mime type '%s' -> %s", mime_types[i], ptype); -- GitLab From 6d5ffaa1449abcc8ce333b61cb41e4872ff4d378 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 31 Jan 2022 17:03:06 +0100 Subject: [PATCH 09/19] macos: Initiate drag from the NSView --- gdk/macos/gdkmacossurface.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 2ddebe906cf..581da99a0dd 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -456,7 +456,7 @@ gdk_macos_surface_drag_begin_idle(gpointer arg) GdkContentProvider *content = gdk_drag_get_content (drag); GdkContentFormats *formats = gdk_content_provider_ref_formats (content); GdkSurface *surface = gdk_drag_get_surface(drag); - GdkMacosWindow *window = (GdkMacosWindow*) _gdk_macos_surface_get_native (GDK_MACOS_SURFACE(surface)); + NSView *view = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface)); NSEvent *event = _gdk_macos_display_get_last_nsevent(); NSPasteboard *pasteboard; NSPoint point; @@ -484,13 +484,13 @@ gdk_macos_surface_drag_begin_idle(gpointer arg) // I'd like to use beginDraggingSessionWithItems:event:source: // instead, but this call tends to block UI updates in our app, // so we use the deprecated dragImage: method for now. - [window dragImage:drag_image - at:point - offset:NSZeroSize - event:event - pasteboard:pasteboard - source:[[GdkMacosDraggingSource alloc] init: GDK_DRAG (drag)] - slideBack:YES]; + [view dragImage:drag_image + at:point + offset:NSZeroSize + event:event + pasteboard:pasteboard + source:[[GdkMacosDraggingSource alloc] init: GDK_DRAG (drag)] + slideBack:YES]; [drag_image release]; -- GitLab From 0234af7a67448f99d8b4af906b0061db960be61d Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 31 Jan 2022 17:40:18 +0100 Subject: [PATCH 10/19] macos: Take ownership of the drag while in progress --- gdk/macos/GdkMacosDraggingSource.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gdk/macos/GdkMacosDraggingSource.c b/gdk/macos/GdkMacosDraggingSource.c index 41fcb4bbd40..8dd6033a18f 100644 --- a/gdk/macos/GdkMacosDraggingSource.c +++ b/gdk/macos/GdkMacosDraggingSource.c @@ -32,10 +32,16 @@ { g_return_val_if_fail (GDK_IS_MACOS_DRAG (drag), self); - self->gdk_drag = drag; + self->gdk_drag = g_object_ref (drag); return self; } +-(void)dealloc +{ + g_object_unref (self->gdk_drag); + self->gdk_drag = nil; +} + - (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context { GdkDrag *drag = self->gdk_drag; -- GitLab From 4de4d101a73c3bf8480d394ef2e7301727c28c7b Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Mon, 31 Jan 2022 17:42:28 +0100 Subject: [PATCH 11/19] macos: Release dragging source after drag --- gdk/macos/gdkmacossurface.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 581da99a0dd..c314a72c86d 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -462,6 +462,7 @@ gdk_macos_surface_drag_begin_idle(gpointer arg) NSPoint point; NSArray *types; NSImage *drag_image; + GdkMacosDraggingSource *source; if (!event) return G_SOURCE_REMOVE; @@ -481,19 +482,22 @@ gdk_macos_surface_drag_begin_idle(gpointer arg) return G_SOURCE_REMOVE; } + source = [[GdkMacosDraggingSource alloc] init: GDK_DRAG (drag)]; + // I'd like to use beginDraggingSessionWithItems:event:source: // instead, but this call tends to block UI updates in our app, // so we use the deprecated dragImage: method for now. + // NB. This call is blocking: the code proceeds after the drag ends! [view dragImage:drag_image at:point offset:NSZeroSize event:event pasteboard:pasteboard - source:[[GdkMacosDraggingSource alloc] init: GDK_DRAG (drag)] + source:source slideBack:YES]; [drag_image release]; - + [source release]; [pool release]; return G_SOURCE_REMOVE; -- GitLab From 6fc40d00e3c6e11104c876c2453e21410c8ab76c Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Wed, 2 Feb 2022 14:57:08 +0100 Subject: [PATCH 12/19] macos: Fix formatting Clean up formatting and make it look more like GNU style C. --- gdk/macos/GdkMacosDraggingSource.c | 19 ++++++++-------- gdk/macos/GdkMacosDraggingSource.h | 2 +- gdk/macos/GdkMacosWindow.c | 4 ++-- gdk/macos/gdkmacosdisplay.c | 2 +- gdk/macos/gdkmacosdrag.c | 20 ++++++++--------- gdk/macos/gdkmacossurface.c | 36 ++++++++++++++++-------------- 6 files changed, 42 insertions(+), 41 deletions(-) diff --git a/gdk/macos/GdkMacosDraggingSource.c b/gdk/macos/GdkMacosDraggingSource.c index 8dd6033a18f..366a4f46bfc 100644 --- a/gdk/macos/GdkMacosDraggingSource.c +++ b/gdk/macos/GdkMacosDraggingSource.c @@ -22,13 +22,13 @@ #import "GdkMacosDraggingSource.h" -#include "gdkmacosdrag-private.h" -#include "gdkmacosdisplay-private.h" #include "gdkdebug.h" +#include "gdkmacosdisplay-private.h" +#include "gdkmacosdrag-private.h" @implementation GdkMacosDraggingSource --(id)init:(GdkDrag *) drag +- (id)init:(GdkDrag *)drag { g_return_val_if_fail (GDK_IS_MACOS_DRAG (drag), self); @@ -36,10 +36,9 @@ return self; } --(void)dealloc +- (void)dealloc { - g_object_unref (self->gdk_drag); - self->gdk_drag = nil; + g_clear_object (&self->gdk_drag); } - (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context @@ -50,7 +49,7 @@ GdkModifierType state = _gdk_macos_display_get_current_keyboard_modifiers (GDK_MACOS_DISPLAY (display)); _gdk_macos_drag_set_actions (drag, state); - return _gdk_macos_drag_operation (GDK_MACOS_DRAG(drag)); + return _gdk_macos_drag_operation (GDK_MACOS_DRAG (drag)); } - (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint @@ -59,11 +58,11 @@ GdkDisplay *display = gdk_drag_get_display (drag); int x, y; - _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), [session draggingSequenceNumber], GDK_DRAG(drag)); + _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), [session draggingSequenceNumber], GDK_DRAG (drag)); _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y); _gdk_macos_drag_set_start_position (drag, x, y); _gdk_macos_drag_set_last_position (drag, x, y); - _gdk_macos_drag_surface_move (GDK_MACOS_DRAG(drag), x, y); + _gdk_macos_drag_surface_move (GDK_MACOS_DRAG (drag), x, y); } - (void)draggingSession:(NSDraggingSession *)session movedToPoint:(NSPoint)screenPoint @@ -74,7 +73,7 @@ _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y); _gdk_macos_drag_set_last_position (drag, x, y); - _gdk_macos_drag_surface_move (GDK_MACOS_DRAG(drag), x, y); + _gdk_macos_drag_surface_move (GDK_MACOS_DRAG (drag), x, y); } - (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation diff --git a/gdk/macos/GdkMacosDraggingSource.h b/gdk/macos/GdkMacosDraggingSource.h index 6dbed966acb..5c550a3f7c1 100644 --- a/gdk/macos/GdkMacosDraggingSource.h +++ b/gdk/macos/GdkMacosDraggingSource.h @@ -31,6 +31,6 @@ GdkDrag *gdk_drag; } --(id)init: (GdkDrag *) drag; +- (id)init:(GdkDrag *)drag; @end diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index 030bd86ce3e..9174846a5f6 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -32,8 +32,8 @@ #include "gdkmacosdrag-private.h" #include "gdkmacosdrop-private.h" #include "gdkmacosmonitor-private.h" -#include "gdkmacossurface-private.h" #include "gdkmacospopupsurface-private.h" +#include "gdkmacossurface-private.h" #include "gdkmacostoplevelsurface-private.h" #include "gdkmacosutils-private.h" @@ -651,7 +651,7 @@ typedef NSString *CALayerContentsGravity; if (drop == NULL) return NO; - gdk_drop_set_actions (drop, GDK_MACOS_DROP(drop)->preferred_action); + gdk_drop_set_actions (drop, GDK_MACOS_DROP (drop)->preferred_action); gdk_drop_emit_drop_event (drop, TRUE, location.x, diff --git a/gdk/macos/gdkmacosdisplay.c b/gdk/macos/gdkmacosdisplay.c index f1a3f06b5fa..7c133f9c0bd 100644 --- a/gdk/macos/gdkmacosdisplay.c +++ b/gdk/macos/gdkmacosdisplay.c @@ -1031,7 +1031,7 @@ _gdk_macos_display_get_nsevent (GdkEvent *event) NSEvent * _gdk_macos_display_get_last_nsevent () { - const GdkToNSEventMap *map = g_queue_peek_tail(&event_map); + const GdkToNSEventMap *map = g_queue_peek_tail (&event_map); if (map) return map->nsevent; diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index 38ece196718..c0d3517374d 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -378,7 +378,7 @@ _gdk_macos_drag_operation (GdkMacosDrag *self) g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), NSDragOperationNone); - actions = gdk_drag_get_actions(GDK_DRAG(self)); + actions = gdk_drag_get_actions (GDK_DRAG (self)); if (actions & GDK_ACTION_LINK) operation |= NSDragOperationLink; @@ -404,8 +404,8 @@ _gdk_macos_drag_begin (GdkMacosDrag *self) void _gdk_macos_drag_surface_move (GdkMacosDrag *self, - double x_root, - double y_root) + double x_root, + double y_root) { g_return_if_fail (GDK_IS_MACOS_DRAG (self)); @@ -417,10 +417,10 @@ _gdk_macos_drag_surface_move (GdkMacosDrag *self, void _gdk_macos_drag_set_start_position (GdkDrag *drag, - int start_x, - int start_y) + int start_x, + int start_y) { - GdkMacosDrag *self = (GdkMacosDrag *)drag; + GdkMacosDrag *self = (GdkMacosDrag *) drag; g_assert (GDK_IS_MACOS_DRAG (self)); @@ -430,10 +430,10 @@ _gdk_macos_drag_set_start_position (GdkDrag *drag, void _gdk_macos_drag_set_last_position (GdkDrag *drag, - int last_x, - int last_y) + int last_x, + int last_y) { - GdkMacosDrag *self = (GdkMacosDrag *)drag; + GdkMacosDrag *self = (GdkMacosDrag *) drag; g_assert (GDK_IS_MACOS_DRAG (self)); @@ -445,7 +445,7 @@ void _gdk_macos_drag_set_actions (GdkDrag *drag, GdkModifierType mods) { - GdkMacosDrag *self = (GdkMacosDrag *)drag; + GdkMacosDrag *self = (GdkMacosDrag *) drag; GdkDragAction suggested_action; GdkDragAction possible_actions; diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index c314a72c86d..8b2380a56b8 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -36,9 +36,9 @@ #include "gdkseatprivate.h" #include "gdksurfaceprivate.h" -#include "gdkmacosdevice.h" #include "gdkmacosclipboard-private.h" #include "gdkmacosdevice-private.h" +#include "gdkmacosdevice.h" #include "gdkmacosdisplay-private.h" #include "gdkmacosdrag-private.h" #include "gdkmacosdragsurface-private.h" @@ -410,7 +410,7 @@ gdk_macos_surface_get_geometry (GdkSurface *surface, *height = surface->height; } -static NSImage* +static NSImage * _gdk_macos_surface_to_ns_image (GdkMacosSurface *self) { // From: https://developer.apple.com/forums/thread/88315 @@ -422,42 +422,44 @@ _gdk_macos_surface_to_ns_image (GdkMacosSurface *self) [bir setSize:imgSize]; [view cacheDisplayInRect:[view bounds] toBitmapImageRep:bir]; - NSImage* image = [[NSImage alloc]initWithSize:imgSize]; + NSImage *image = [[NSImage alloc] initWithSize:imgSize]; [image addRepresentation:bir]; return image; } -static NSArray* -content_formats_to_pastboard_types (GdkContentFormats *formats) { +static NSArray * +content_formats_to_pastboard_types (GdkContentFormats *formats) +{ gsize n_mime_types; const char *const *mime_types = gdk_content_formats_get_mime_types (formats, &n_mime_types); - NSMutableArray *types = [NSMutableArray arrayWithCapacity: (NSUInteger) n_mime_types]; + NSMutableArray *types = [NSMutableArray arrayWithCapacity:(NSUInteger) n_mime_types]; - for (gsize i = 0; i < n_mime_types; i++) { - NSPasteboardType ptype = _gdk_macos_clipboard_to_ns_type(mime_types[i], NULL); - // g_message ("Adding type for mime type '%s' -> %s", mime_types[i], ptype); - if (ptype) - [types addObject: ptype]; - } + for (gsize i = 0; i < n_mime_types; i++) + { + NSPasteboardType ptype = _gdk_macos_clipboard_to_ns_type (mime_types[i], NULL); + // g_message ("Adding type for mime type '%s' -> %s", mime_types[i], ptype); + if (ptype) + [types addObject:ptype]; + } // If no types can be set, default to a string. // This can happen if we only use GType's for our (internal) DnD. if ([types count] == 0) - [types addObject: NSPasteboardTypeString]; + [types addObject:NSPasteboardTypeString]; return types; } static gboolean -gdk_macos_surface_drag_begin_idle(gpointer arg) +gdk_macos_surface_drag_begin_idle (gpointer arg) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; GdkDrag *drag = GDK_DRAG (arg); GdkContentProvider *content = gdk_drag_get_content (drag); GdkContentFormats *formats = gdk_content_provider_ref_formats (content); - GdkSurface *surface = gdk_drag_get_surface(drag); + GdkSurface *surface = gdk_drag_get_surface (drag); NSView *view = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface)); - NSEvent *event = _gdk_macos_display_get_last_nsevent(); + NSEvent *event = _gdk_macos_display_get_last_nsevent (); NSPasteboard *pasteboard; NSPoint point; NSArray *types; @@ -482,7 +484,7 @@ gdk_macos_surface_drag_begin_idle(gpointer arg) return G_SOURCE_REMOVE; } - source = [[GdkMacosDraggingSource alloc] init: GDK_DRAG (drag)]; + source = [[GdkMacosDraggingSource alloc] init:GDK_DRAG (drag)]; // I'd like to use beginDraggingSessionWithItems:event:source: // instead, but this call tends to block UI updates in our app, -- GitLab From d1686d315232e63591cef358f45f138fd5659384 Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sat, 5 Feb 2022 17:52:22 +0100 Subject: [PATCH 13/19] macos: Fix missing deallocation call --- gdk/macos/GdkMacosDraggingSource.c | 1 + gdk/macos/gdkmacossurface.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/gdk/macos/GdkMacosDraggingSource.c b/gdk/macos/GdkMacosDraggingSource.c index 366a4f46bfc..60775dfe51b 100644 --- a/gdk/macos/GdkMacosDraggingSource.c +++ b/gdk/macos/GdkMacosDraggingSource.c @@ -39,6 +39,7 @@ - (void)dealloc { g_clear_object (&self->gdk_drag); + [super dealloc]; } - (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 8b2380a56b8..23fd927663b 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -540,7 +540,7 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, "content", content, "actions", actions, NULL); - g_clear_object (&drag_surface); + g_object_unref (drag_surface); if (!_gdk_macos_drag_begin (drag)) { -- GitLab From efd8e1e9db2323026c6b732c8578b9fc494e077f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 18 Jun 2021 11:27:54 -0700 Subject: [PATCH 14/19] macos: refactor pasteboard components into separate module This will make it easier to reuse from drag integration so that we don't require clipboards for everything. We will need to subclass the pasteboard provider twice, however, both for clipboard and dragging. --- gdk/macos/GdkMacosWindow.c | 4 +- gdk/macos/gdkmacosclipboard-private.h | 22 +- gdk/macos/gdkmacosclipboard.c | 373 +--------------------- gdk/macos/gdkmacospasteboard-private.h | 64 ++++ gdk/macos/gdkmacospasteboard.c | 417 +++++++++++++++++++++++++ gdk/macos/meson.build | 1 + 6 files changed, 500 insertions(+), 381 deletions(-) create mode 100644 gdk/macos/gdkmacospasteboard-private.h create mode 100644 gdk/macos/gdkmacospasteboard.c diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index 9174846a5f6..8c5a19e26b4 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -27,11 +27,11 @@ #import "GdkMacosView.h" #import "GdkMacosWindow.h" -#include "gdkmacosclipboard-private.h" #include "gdkmacosdisplay-private.h" #include "gdkmacosdrag-private.h" #include "gdkmacosdrop-private.h" #include "gdkmacosmonitor-private.h" +#include "gdkmacospasteboard-private.h" #include "gdkmacospopupsurface-private.h" #include "gdkmacossurface-private.h" #include "gdkmacostoplevelsurface-private.h" @@ -247,7 +247,7 @@ typedef NSString *CALayerContentsGravity; [view release]; /* TODO: We might want to make this more extensible at some point */ - _gdk_macos_clipboard_register_drag_types (self); + _gdk_macos_pasteboard_register_drag_types (self); return self; } diff --git a/gdk/macos/gdkmacosclipboard-private.h b/gdk/macos/gdkmacosclipboard-private.h index ba0b52bf0a2..a1fa83f7ca0 100644 --- a/gdk/macos/gdkmacosclipboard-private.h +++ b/gdk/macos/gdkmacosclipboard-private.h @@ -24,6 +24,7 @@ #include "gdkclipboardprivate.h" #include "gdkmacosdisplay-private.h" +#include "gdkmacospasteboard-private.h" G_BEGIN_DECLS @@ -41,28 +42,13 @@ NSPasteboardType _gdk_macos_clipboard_to_ns_type (const char NSPasteboardType *alternate); const char *_gdk_macos_clipboard_from_ns_type (NSPasteboardType ns_type); void _gdk_macos_clipboard_register_drag_types (NSWindow *window); -GdkContentFormats *_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard); -void _gdk_macos_pasteboard_read_async (GObject *object, - NSPasteboard *pasteboard, - GdkContentFormats *formats, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -GInputStream *_gdk_macos_pasteboard_read_finish (GObject *object, - GAsyncResult *result, - const char **out_mime_type, - GError **error); -@interface GdkMacosClipboardDataProvider : NSObject +@interface GdkMacosClipboardDataProvider : GdkMacosPasteboardDataProvider { - GCancellable *cancellable; - GdkClipboard *clipboard; - char **mimeTypes; + GdkClipboard *clipboard; } --(id)initClipboard:(GdkClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; --(NSArray *)types; +-(id)initClipboard:(GdkMacosClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; @end diff --git a/gdk/macos/gdkmacosclipboard.c b/gdk/macos/gdkmacosclipboard.c index ba755db0be7..0af6dd3c8fc 100644 --- a/gdk/macos/gdkmacosclipboard.c +++ b/gdk/macos/gdkmacosclipboard.c @@ -22,6 +22,7 @@ #include #include "gdkmacosclipboard-private.h" +#include "gdkmacospasteboard-private.h" #include "gdkmacosutils-private.h" #include "gdk-private.h" @@ -41,56 +42,8 @@ typedef struct guint done : 1; } WriteRequest; -enum { - TYPE_STRING, - TYPE_PBOARD, - TYPE_URL, - TYPE_FILE_URL, - TYPE_COLOR, - TYPE_TIFF, - TYPE_PNG, - TYPE_LAST -}; - -#define PTYPE(k) (get_pasteboard_type(TYPE_##k)) - -static NSPasteboardType pasteboard_types[TYPE_LAST]; - G_DEFINE_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK_TYPE_CLIPBOARD) -static NSPasteboardType -get_pasteboard_type (int type) -{ - static gsize initialized = FALSE; - - g_assert (type >= 0); - g_assert (type < TYPE_LAST); - - if (g_once_init_enter (&initialized)) - { - pasteboard_types[TYPE_PNG] = NSPasteboardTypePNG; - pasteboard_types[TYPE_STRING] = NSPasteboardTypeString; - pasteboard_types[TYPE_TIFF] = NSPasteboardTypeTIFF; - pasteboard_types[TYPE_COLOR] = NSPasteboardTypeColor; - - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - pasteboard_types[TYPE_PBOARD] = NSStringPboardType; - G_GNUC_END_IGNORE_DEPRECATIONS - -#ifdef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER - pasteboard_types[TYPE_URL] = NSPasteboardTypeURL; - pasteboard_types[TYPE_FILE_URL] = NSPasteboardTypeFileURL; -#else - pasteboard_types[TYPE_URL] = [[NSString alloc] initWithUTF8String:"public.url"]; - pasteboard_types[TYPE_FILE_URL] = [[NSString alloc] initWithUTF8String:"public.file-url"]; -#endif - - g_once_init_leave (&initialized, TRUE); - } - - return pasteboard_types[type]; -} - static void write_request_free (WriteRequest *wr) { @@ -100,95 +53,6 @@ write_request_free (WriteRequest *wr) g_slice_free (WriteRequest, wr); } -const char * -_gdk_macos_clipboard_from_ns_type (NSPasteboardType type) -{ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - - if ([type isEqualToString:PTYPE(STRING)] || - [type isEqualToString:PTYPE(PBOARD)]) - return g_intern_string ("text/plain;charset=utf-8"); - else if ([type isEqualToString:PTYPE(URL)] || - [type isEqualToString:PTYPE(FILE_URL)]) - return g_intern_string ("text/uri-list"); - else if ([type isEqualToString:PTYPE(COLOR)]) - return g_intern_string ("application/x-color"); - else if ([type isEqualToString:PTYPE(TIFF)]) - return g_intern_string ("image/tiff"); - else if ([type isEqualToString:PTYPE(PNG)]) - return g_intern_string ("image/png"); - - G_GNUC_END_IGNORE_DEPRECATIONS; - - return NULL; -} - -NSPasteboardType -_gdk_macos_clipboard_to_ns_type (const char *mime_type, - NSPasteboardType *alternate) -{ - if (alternate) - *alternate = NULL; - - if (g_strcmp0 (mime_type, "text/plain;charset=utf-8") == 0) - { - return PTYPE(STRING); - } - else if (g_strcmp0 (mime_type, "text/uri-list") == 0) - { - if (alternate) - *alternate = PTYPE(URL); - return PTYPE(FILE_URL); - } - else if (g_strcmp0 (mime_type, "application/x-color") == 0) - { - return PTYPE(COLOR); - } - else if (g_strcmp0 (mime_type, "image/tiff") == 0) - { - return PTYPE(TIFF); - } - else if (g_strcmp0 (mime_type, "image/png") == 0) - { - return PTYPE(PNG); - } - - return nil; -} - -static void -populate_content_formats (GdkContentFormatsBuilder *builder, - NSPasteboardType type) -{ - const char *mime_type; - - g_return_if_fail (builder != NULL); - g_return_if_fail (type != NULL); - - mime_type = _gdk_macos_clipboard_from_ns_type (type); - - if (mime_type != NULL) - gdk_content_formats_builder_add_mime_type (builder, mime_type); -} - -static GdkContentFormats * -load_offer_formats (NSPasteboard *pasteboard) -{ - GDK_BEGIN_MACOS_ALLOC_POOL; - - GdkContentFormatsBuilder *builder; - GdkContentFormats *formats; - - builder = gdk_content_formats_builder_new (); - for (NSPasteboardType type in [pasteboard types]) - populate_content_formats (builder, type); - formats = gdk_content_formats_builder_free_to_formats (builder); - - GDK_END_MACOS_ALLOC_POOL; - - return g_steal_pointer (&formats); -} - static void _gdk_macos_clipboard_load_contents (GdkMacosClipboard *self) { @@ -199,22 +63,13 @@ _gdk_macos_clipboard_load_contents (GdkMacosClipboard *self) change_count = [self->pasteboard changeCount]; - formats = load_offer_formats (self->pasteboard); + formats = _gdk_macos_pasteboard_load_formats (self->pasteboard); gdk_clipboard_claim_remote (GDK_CLIPBOARD (self), formats); gdk_content_formats_unref (formats); self->last_change_count = change_count; } -static GInputStream * -create_stream_from_nsdata (NSData *data) -{ - const guint8 *bytes = [data bytes]; - gsize len = [data length]; - - return g_memory_input_stream_new_from_data (g_memdup2 (bytes, len), len, g_free); -} - static void _gdk_macos_clipboard_read_async (GdkClipboard *clipboard, GdkContentFormats *formats, @@ -245,35 +100,25 @@ static void _gdk_macos_clipboard_send_to_pasteboard (GdkMacosClipboard *self, GdkContentProvider *content) { - GDK_BEGIN_MACOS_ALLOC_POOL; - GdkMacosClipboardDataProvider *dataProvider; GdkContentFormats *serializable; - NSPasteboardItem *item; const char * const *mime_types; gsize n_mime_types; - g_return_if_fail (GDK_IS_MACOS_CLIPBOARD (self)); - g_return_if_fail (GDK_IS_CONTENT_PROVIDER (content)); + g_assert (GDK_IS_MACOS_CLIPBOARD (self)); + g_assert (GDK_IS_CONTENT_PROVIDER (content)); serializable = gdk_content_provider_ref_storable_formats (content); serializable = gdk_content_formats_union_serialize_mime_types (serializable); mime_types = gdk_content_formats_get_mime_types (serializable, &n_mime_types); - dataProvider = [[GdkMacosClipboardDataProvider alloc] initClipboard:GDK_CLIPBOARD (self) - mimetypes:mime_types]; - item = [[NSPasteboardItem alloc] init]; - [item setDataProvider:dataProvider forTypes:[dataProvider types]]; - - [self->pasteboard clearContents]; - if ([self->pasteboard writeObjects:[NSArray arrayWithObject:item]] == NO) - g_warning ("Failed to write object to pasteboard"); + dataProvider = [[GdkMacosClipboardDataProvider alloc] initClipboard:self mimetypes:mime_types]; + _gdk_macos_pasteboard_send_content (self->pasteboard, content, dataProvider); + [dataProvider release]; self->last_change_count = [self->pasteboard changeCount]; g_clear_pointer (&serializable, gdk_content_formats_unref); - - GDK_END_MACOS_ALLOC_POOL; } static gboolean @@ -368,23 +213,18 @@ _gdk_macos_clipboard_check_externally_modified (GdkMacosClipboard *self) @implementation GdkMacosClipboardDataProvider --(id)initClipboard:(GdkClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; +-(id)initClipboard:(GdkMacosClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; { - [super init]; + [super initPasteboard:gdkClipboard->pasteboard mimetypes:mime_types]; - self->mimeTypes = g_strdupv ((char **)mime_types); - self->clipboard = g_object_ref (gdkClipboard); + self->clipboard = g_object_ref (GDK_CLIPBOARD (gdkClipboard)); return self; } -(void)dealloc { - g_cancellable_cancel (self->cancellable); - - g_clear_pointer (&self->mimeTypes, g_strfreev); g_clear_object (&self->clipboard); - g_clear_object (&self->cancellable); [super dealloc]; } @@ -394,27 +234,6 @@ _gdk_macos_clipboard_check_externally_modified (GdkMacosClipboard *self) g_clear_object (&self->clipboard); } --(NSArray *)types -{ - NSMutableArray *ret = [[NSMutableArray alloc] init]; - - for (guint i = 0; self->mimeTypes[i]; i++) - { - const char *mime_type = self->mimeTypes[i]; - NSPasteboardType type; - NSPasteboardType alternate = nil; - - if ((type = _gdk_macos_clipboard_to_ns_type (mime_type, &alternate))) - { - [ret addObject:type]; - if (alternate) - [ret addObject:alternate]; - } - } - - return g_steal_pointer (&ret); -} - static void on_data_ready_cb (GObject *object, GAsyncResult *result, @@ -460,11 +279,9 @@ on_data_ready_cb (GObject *object, GDK_END_MACOS_ALLOC_POOL; } --(void) pasteboard:(NSPasteboard *)pasteboard - item:(NSPasteboardItem *)item - provideDataForType:(NSPasteboardType)type +-(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type { - const char *mime_type = _gdk_macos_clipboard_from_ns_type (type); + const char *mime_type = _gdk_macos_pasteboard_from_ns_type (type); GMainContext *main_context = g_main_context_default (); WriteRequest *wr; @@ -500,170 +317,4 @@ on_data_ready_cb (GObject *object, write_request_free (wr); } -void -_gdk_macos_clipboard_register_drag_types (NSWindow *window) -{ - [window registerForDraggedTypes:[NSArray arrayWithObjects:PTYPE(STRING), - PTYPE(PBOARD), - PTYPE(URL), - PTYPE(FILE_URL), - PTYPE(COLOR), - PTYPE(TIFF), - PTYPE(PNG), - nil]]; -} - @end - -GdkContentFormats * -_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard) -{ - return load_offer_formats (pasteboard); -} - -void -_gdk_macos_pasteboard_read_async (GObject *object, - NSPasteboard *pasteboard, - GdkContentFormats *formats, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GDK_BEGIN_MACOS_ALLOC_POOL; - - GdkContentFormats *offer_formats = NULL; - const char *mime_type; - GInputStream *stream = NULL; - GTask *task = NULL; - - g_assert (G_IS_OBJECT (object)); - g_assert (pasteboard != NULL); - g_assert (formats != NULL); - - task = g_task_new (object, cancellable, callback, user_data); - g_task_set_source_tag (task, _gdk_macos_pasteboard_read_async); - g_task_set_priority (task, io_priority); - - offer_formats = load_offer_formats (pasteboard); - mime_type = gdk_content_formats_match_mime_type (formats, offer_formats); - - if (mime_type == NULL) - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "%s", - _("No compatible transfer format found")); - goto cleanup; - } - - if (strcmp (mime_type, "text/plain;charset=utf-8") == 0) - { - NSString *nsstr = [pasteboard stringForType:NSPasteboardTypeString]; - - if (nsstr != NULL) - { - const char *str = [nsstr UTF8String]; - stream = g_memory_input_stream_new_from_data (g_strdup (str), - strlen (str) + 1, - g_free); - } - } - else if (strcmp (mime_type, "text/uri-list") == 0) - { - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - - if ([[pasteboard types] containsObject:PTYPE(FILE_URL)]) - { - GString *str = g_string_new (NULL); - NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType]; - gsize n_files = [files count]; - char *data; - guint len; - - for (gsize i = 0; i < n_files; ++i) - { - NSString* uriString = [files objectAtIndex:i]; - uriString = [@"file://" stringByAppendingString:uriString]; - uriString = [uriString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - - g_string_append_printf (str, - "%s\r\n", - [uriString cStringUsingEncoding:NSUTF8StringEncoding]); - } - - len = str->len; - data = g_string_free (str, FALSE); - stream = g_memory_input_stream_new_from_data (data, len, g_free); - } - - G_GNUC_END_IGNORE_DEPRECATIONS; - } - else if (strcmp (mime_type, "application/x-color") == 0) - { - NSColorSpace *colorspace; - NSColor *nscolor; - guint16 color[4]; - - colorspace = [NSColorSpace genericRGBColorSpace]; - nscolor = [[NSColor colorFromPasteboard:pasteboard] - colorUsingColorSpace:colorspace]; - - color[0] = 0xffff * [nscolor redComponent]; - color[1] = 0xffff * [nscolor greenComponent]; - color[2] = 0xffff * [nscolor blueComponent]; - color[3] = 0xffff * [nscolor alphaComponent]; - - stream = g_memory_input_stream_new_from_data (g_memdup2 (&color, sizeof color), - sizeof color, - g_free); - } - else if (strcmp (mime_type, "image/tiff") == 0) - { - NSData *data = [pasteboard dataForType:PTYPE(TIFF)]; - stream = create_stream_from_nsdata (data); - } - else if (strcmp (mime_type, "image/png") == 0) - { - NSData *data = [pasteboard dataForType:PTYPE(PNG)]; - stream = create_stream_from_nsdata (data); - } - - if (stream != NULL) - { - g_task_set_task_data (task, g_strdup (mime_type), g_free); - g_task_return_pointer (task, g_steal_pointer (&stream), g_object_unref); - } - else - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Failed to decode contents with mime-type of '%s'"), - mime_type); - } - -cleanup: - g_clear_object (&task); - g_clear_pointer (&offer_formats, gdk_content_formats_unref); - - GDK_END_MACOS_ALLOC_POOL; -} - -GInputStream * -_gdk_macos_pasteboard_read_finish (GObject *object, - GAsyncResult *result, - const char **out_mime_type, - GError **error) -{ - GTask *task = (GTask *)result; - - g_assert (G_IS_OBJECT (object)); - g_assert (G_IS_TASK (task)); - - if (out_mime_type != NULL) - *out_mime_type = g_strdup (g_task_get_task_data (task)); - - return g_task_propagate_pointer (task, error); -} diff --git a/gdk/macos/gdkmacospasteboard-private.h b/gdk/macos/gdkmacospasteboard-private.h new file mode 100644 index 00000000000..c135d82d58e --- /dev/null +++ b/gdk/macos/gdkmacospasteboard-private.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2021 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __GDK_MACOS_PASTEBOARD_PRIVATE_H__ +#define __GDK_MACOS_PASTEBOARD_PRIVATE_H__ + +#include +#include + +#include "gdkclipboardprivate.h" + +G_BEGIN_DECLS + +@interface GdkMacosPasteboardDataProvider : NSObject +{ + NSPasteboard *pasteboard; + GCancellable *cancellable; + char **mimeTypes; +} + +-(id)initPasteboard:(NSPasteboard *)pasteBoard mimetypes:(const char * const *)mime_types; +-(NSArray *)types; + +@end + +NSPasteboardType _gdk_macos_pasteboard_to_ns_type (const char *mime_type, + NSPasteboardType *alternate); +const char *_gdk_macos_pasteboard_from_ns_type (NSPasteboardType type); +GdkContentFormats *_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard); +void _gdk_macos_pasteboard_register_drag_types (NSWindow *window); +void _gdk_macos_pasteboard_read_async (GObject *object, + NSPasteboard *pasteboard, + GdkContentFormats *formats, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GInputStream *_gdk_macos_pasteboard_read_finish (GObject *object, + GAsyncResult *result, + const char **out_mime_type, + GError **error); +void _gdk_macos_pasteboard_send_content (NSPasteboard *pasteboard, + GdkContentProvider *content, + GdkMacosPasteboardDataProvider *dataProvider); + +G_END_DECLS + +#endif /* __GDK_MACOS_PASTEBOARD_PRIVATE_H__ */ diff --git a/gdk/macos/gdkmacospasteboard.c b/gdk/macos/gdkmacospasteboard.c new file mode 100644 index 00000000000..0de91518e0c --- /dev/null +++ b/gdk/macos/gdkmacospasteboard.c @@ -0,0 +1,417 @@ +/* + * Copyright © 2021 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include + +#include "gdkmacospasteboard-private.h" +#include "gdkmacosutils-private.h" +#include "gdk-private.h" + +enum { + TYPE_STRING, + TYPE_PBOARD, + TYPE_URL, + TYPE_FILE_URL, + TYPE_COLOR, + TYPE_TIFF, + TYPE_PNG, + TYPE_LAST +}; + +#define PTYPE(k) (get_pasteboard_type(TYPE_##k)) + +static NSPasteboardType pasteboard_types[TYPE_LAST]; + +static NSPasteboardType +get_pasteboard_type (int type) +{ + static gsize initialized = FALSE; + + g_assert (type >= 0); + g_assert (type < TYPE_LAST); + + if (g_once_init_enter (&initialized)) + { + pasteboard_types[TYPE_PNG] = NSPasteboardTypePNG; + pasteboard_types[TYPE_STRING] = NSPasteboardTypeString; + pasteboard_types[TYPE_TIFF] = NSPasteboardTypeTIFF; + pasteboard_types[TYPE_COLOR] = NSPasteboardTypeColor; + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + pasteboard_types[TYPE_PBOARD] = NSStringPboardType; + G_GNUC_END_IGNORE_DEPRECATIONS + +#ifdef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER + pasteboard_types[TYPE_URL] = NSPasteboardTypeURL; + pasteboard_types[TYPE_FILE_URL] = NSPasteboardTypeFileURL; +#else + pasteboard_types[TYPE_URL] = [[NSString alloc] initWithUTF8String:"public.url"]; + pasteboard_types[TYPE_FILE_URL] = [[NSString alloc] initWithUTF8String:"public.file-url"]; +#endif + + g_once_init_leave (&initialized, TRUE); + } + + return pasteboard_types[type]; +} + +const char * +_gdk_macos_pasteboard_from_ns_type (NSPasteboardType type) +{ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + + if ([type isEqualToString:PTYPE(STRING)] || + [type isEqualToString:PTYPE(PBOARD)]) + return g_intern_string ("text/plain;charset=utf-8"); + else if ([type isEqualToString:PTYPE(URL)] || + [type isEqualToString:PTYPE(FILE_URL)]) + return g_intern_string ("text/uri-list"); + else if ([type isEqualToString:PTYPE(COLOR)]) + return g_intern_string ("application/x-color"); + else if ([type isEqualToString:PTYPE(TIFF)]) + return g_intern_string ("image/tiff"); + else if ([type isEqualToString:PTYPE(PNG)]) + return g_intern_string ("image/png"); + + G_GNUC_END_IGNORE_DEPRECATIONS; + + return NULL; +} + +NSPasteboardType +_gdk_macos_pasteboard_to_ns_type (const char *mime_type, + NSPasteboardType *alternate) +{ + if (alternate) + *alternate = NULL; + + if (g_strcmp0 (mime_type, "text/plain;charset=utf-8") == 0) + { + return PTYPE(STRING); + } + else if (g_strcmp0 (mime_type, "text/uri-list") == 0) + { + if (alternate) + *alternate = PTYPE(URL); + return PTYPE(FILE_URL); + } + else if (g_strcmp0 (mime_type, "application/x-color") == 0) + { + return PTYPE(COLOR); + } + else if (g_strcmp0 (mime_type, "image/tiff") == 0) + { + return PTYPE(TIFF); + } + else if (g_strcmp0 (mime_type, "image/png") == 0) + { + return PTYPE(PNG); + } + + return nil; +} + +static void +populate_content_formats (GdkContentFormatsBuilder *builder, + NSPasteboardType type) +{ + const char *mime_type; + + g_assert (builder != NULL); + g_assert (type != NULL); + + if ((mime_type = _gdk_macos_pasteboard_from_ns_type (type))) + gdk_content_formats_builder_add_mime_type (builder, mime_type); +} + +static GdkContentFormats * +load_offer_formats (NSPasteboard *pasteboard) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + GdkContentFormatsBuilder *builder; + GdkContentFormats *formats; + + builder = gdk_content_formats_builder_new (); + for (NSPasteboardType type in [pasteboard types]) + populate_content_formats (builder, type); + formats = gdk_content_formats_builder_free_to_formats (builder); + + GDK_END_MACOS_ALLOC_POOL; + + return g_steal_pointer (&formats); +} + +GdkContentFormats * +_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard) +{ + return load_offer_formats (pasteboard); +} + +void +_gdk_macos_pasteboard_send_content (NSPasteboard *pasteboard, + GdkContentProvider *content, + GdkMacosPasteboardDataProvider *dataProvider) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + NSPasteboardItem *item; + + g_return_if_fail (pasteboard != NULL); + g_return_if_fail (GDK_IS_CONTENT_PROVIDER (content)); + + item = [[NSPasteboardItem alloc] init]; + [item setDataProvider:dataProvider forTypes:[dataProvider types]]; + + [pasteboard clearContents]; + if ([pasteboard writeObjects:[NSArray arrayWithObject:item]] == NO) + g_warning ("Failed to write object to pasteboard"); + + GDK_END_MACOS_ALLOC_POOL; +} + +@implementation GdkMacosPasteboardDataProvider + +-(id)initPasteboard:(NSPasteboard *)pasteBoard mimetypes:(const char * const *)mime_types; +{ + [super init]; + + self->mimeTypes = g_strdupv ((char **)mime_types); + self->pasteboard = [pasteBoard retain]; + + return self; +} + +-(void)dealloc +{ + g_cancellable_cancel (self->cancellable); + + if (self->pasteboard) + { + [self->pasteboard release]; + self->pasteboard = nil; + } + + g_clear_pointer (&self->mimeTypes, g_strfreev); + g_clear_object (&self->cancellable); + + [super dealloc]; +} + +-(void)pasteboardFinishedWithDataProvider:(NSPasteboard *)pasteboard +{ +} + +-(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type +{ +} + +-(NSArray *)types +{ + NSMutableArray *ret = [[NSMutableArray alloc] init]; + + for (guint i = 0; self->mimeTypes[i]; i++) + { + const char *mime_type = self->mimeTypes[i]; + NSPasteboardType type; + NSPasteboardType alternate = nil; + + if ((type = _gdk_macos_pasteboard_to_ns_type (mime_type, &alternate))) + { + [ret addObject:type]; + if (alternate) + [ret addObject:alternate]; + } + } + + return g_steal_pointer (&ret); +} + +@end + +static GInputStream * +create_stream_from_nsdata (NSData *data) +{ + const guint8 *bytes = [data bytes]; + gsize len = [data length]; + + return g_memory_input_stream_new_from_data (g_memdup2 (bytes, len), len, g_free); +} + +void +_gdk_macos_pasteboard_read_async (GObject *object, + NSPasteboard *pasteboard, + GdkContentFormats *formats, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + GdkContentFormats *offer_formats = NULL; + const char *mime_type; + GInputStream *stream = NULL; + GTask *task = NULL; + + g_assert (G_IS_OBJECT (object)); + g_assert (pasteboard != NULL); + g_assert (formats != NULL); + + task = g_task_new (object, cancellable, callback, user_data); + g_task_set_source_tag (task, _gdk_macos_pasteboard_read_async); + g_task_set_priority (task, io_priority); + + offer_formats = load_offer_formats (pasteboard); + mime_type = gdk_content_formats_match_mime_type (formats, offer_formats); + + if (mime_type == NULL) + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "%s", + _("No compatible transfer format found")); + goto cleanup; + } + + if (strcmp (mime_type, "text/plain;charset=utf-8") == 0) + { + NSString *nsstr = [pasteboard stringForType:NSPasteboardTypeString]; + + if (nsstr != NULL) + { + const char *str = [nsstr UTF8String]; + stream = g_memory_input_stream_new_from_data (g_strdup (str), + strlen (str) + 1, + g_free); + } + } + else if (strcmp (mime_type, "text/uri-list") == 0) + { + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + + if ([[pasteboard types] containsObject:PTYPE(FILE_URL)]) + { + GString *str = g_string_new (NULL); + NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType]; + gsize n_files = [files count]; + char *data; + guint len; + + for (gsize i = 0; i < n_files; ++i) + { + NSString* uriString = [files objectAtIndex:i]; + uriString = [@"file://" stringByAppendingString:uriString]; + uriString = [uriString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + g_string_append_printf (str, + "%s\r\n", + [uriString cStringUsingEncoding:NSUTF8StringEncoding]); + } + + len = str->len; + data = g_string_free (str, FALSE); + stream = g_memory_input_stream_new_from_data (data, len, g_free); + } + + G_GNUC_END_IGNORE_DEPRECATIONS; + } + else if (strcmp (mime_type, "application/x-color") == 0) + { + NSColorSpace *colorspace; + NSColor *nscolor; + guint16 color[4]; + + colorspace = [NSColorSpace genericRGBColorSpace]; + nscolor = [[NSColor colorFromPasteboard:pasteboard] + colorUsingColorSpace:colorspace]; + + color[0] = 0xffff * [nscolor redComponent]; + color[1] = 0xffff * [nscolor greenComponent]; + color[2] = 0xffff * [nscolor blueComponent]; + color[3] = 0xffff * [nscolor alphaComponent]; + + stream = g_memory_input_stream_new_from_data (g_memdup2 (&color, sizeof color), + sizeof color, + g_free); + } + else if (strcmp (mime_type, "image/tiff") == 0) + { + NSData *data = [pasteboard dataForType:PTYPE(TIFF)]; + stream = create_stream_from_nsdata (data); + } + else if (strcmp (mime_type, "image/png") == 0) + { + NSData *data = [pasteboard dataForType:PTYPE(PNG)]; + stream = create_stream_from_nsdata (data); + } + + if (stream != NULL) + { + g_task_set_task_data (task, g_strdup (mime_type), g_free); + g_task_return_pointer (task, g_steal_pointer (&stream), g_object_unref); + } + else + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Failed to decode contents with mime-type of '%s'"), + mime_type); + } + +cleanup: + g_clear_object (&task); + g_clear_pointer (&offer_formats, gdk_content_formats_unref); + + GDK_END_MACOS_ALLOC_POOL; +} + +GInputStream * +_gdk_macos_pasteboard_read_finish (GObject *object, + GAsyncResult *result, + const char **out_mime_type, + GError **error) +{ + GTask *task = (GTask *)result; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_TASK (task)); + + if (out_mime_type != NULL) + *out_mime_type = g_strdup (g_task_get_task_data (task)); + + return g_task_propagate_pointer (task, error); +} + +void +_gdk_macos_pasteboard_register_drag_types (NSWindow *window) +{ + [window registerForDraggedTypes:[NSArray arrayWithObjects:PTYPE(STRING), + PTYPE(PBOARD), + PTYPE(URL), + PTYPE(FILE_URL), + PTYPE(COLOR), + PTYPE(TIFF), + PTYPE(PNG), + nil]]; +} diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build index 51084832c46..8f8d66c6e48 100644 --- a/gdk/macos/meson.build +++ b/gdk/macos/meson.build @@ -19,6 +19,7 @@ gdk_macos_sources = files([ 'gdkmacoseventsource.c', 'gdkmacoskeymap.c', 'gdkmacosmonitor.c', + 'gdkmacospasteboard.c', 'gdkmacospopupsurface.c', 'gdkmacosseat.c', 'gdkmacossurface.c', -- GitLab From 820220eaeef08ae7244a381e8b8c5a103f44d3dd Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 18 Jun 2021 19:51:13 -0700 Subject: [PATCH 15/19] macos: abstract pasteboard for use in clipboard and drag This will allow us to share a single NSPasteboardItem and data provider implementation for both GdkClipboard and GdkDrag. --- gdk/macos/gdkmacosclipboard-private.h | 9 - gdk/macos/gdkmacosclipboard.c | 150 +----------- gdk/macos/gdkmacospasteboard-private.h | 28 ++- gdk/macos/gdkmacospasteboard.c | 325 +++++++++++++++++++------ 4 files changed, 276 insertions(+), 236 deletions(-) diff --git a/gdk/macos/gdkmacosclipboard-private.h b/gdk/macos/gdkmacosclipboard-private.h index a1fa83f7ca0..ef70ee3e330 100644 --- a/gdk/macos/gdkmacosclipboard-private.h +++ b/gdk/macos/gdkmacosclipboard-private.h @@ -43,15 +43,6 @@ NSPasteboardType _gdk_macos_clipboard_to_ns_type (const char const char *_gdk_macos_clipboard_from_ns_type (NSPasteboardType ns_type); void _gdk_macos_clipboard_register_drag_types (NSWindow *window); -@interface GdkMacosClipboardDataProvider : GdkMacosPasteboardDataProvider -{ - GdkClipboard *clipboard; -} - --(id)initClipboard:(GdkMacosClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; - -@end - G_END_DECLS #endif /* __GDK_MACOS_CLIPBOARD_PRIVATE_H__ */ diff --git a/gdk/macos/gdkmacosclipboard.c b/gdk/macos/gdkmacosclipboard.c index 0af6dd3c8fc..db4a2620926 100644 --- a/gdk/macos/gdkmacosclipboard.c +++ b/gdk/macos/gdkmacosclipboard.c @@ -33,26 +33,8 @@ struct _GdkMacosClipboard NSInteger last_change_count; }; -typedef struct -{ - GMemoryOutputStream *stream; - NSPasteboardItem *item; - NSPasteboardType type; - GMainContext *main_context; - guint done : 1; -} WriteRequest; - G_DEFINE_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK_TYPE_CLIPBOARD) -static void -write_request_free (WriteRequest *wr) -{ - g_clear_pointer (&wr->main_context, g_main_context_unref); - g_clear_object (&wr->stream); - [wr->item release]; - g_slice_free (WriteRequest, wr); -} - static void _gdk_macos_clipboard_load_contents (GdkMacosClipboard *self) { @@ -100,25 +82,27 @@ static void _gdk_macos_clipboard_send_to_pasteboard (GdkMacosClipboard *self, GdkContentProvider *content) { - GdkMacosClipboardDataProvider *dataProvider; - GdkContentFormats *serializable; - const char * const *mime_types; - gsize n_mime_types; + GdkMacosPasteboardItem *item; + NSArray *items; g_assert (GDK_IS_MACOS_CLIPBOARD (self)); g_assert (GDK_IS_CONTENT_PROVIDER (content)); - serializable = gdk_content_provider_ref_storable_formats (content); - serializable = gdk_content_formats_union_serialize_mime_types (serializable); - mime_types = gdk_content_formats_get_mime_types (serializable, &n_mime_types); + if (self->pasteboard == NULL) + return; - dataProvider = [[GdkMacosClipboardDataProvider alloc] initClipboard:self mimetypes:mime_types]; - _gdk_macos_pasteboard_send_content (self->pasteboard, content, dataProvider); - [dataProvider release]; + GDK_BEGIN_MACOS_ALLOC_POOL; + + item = [[GdkMacosPasteboardItem alloc] initForClipboard:GDK_CLIPBOARD (self) withContentProvider:content]; + items = [NSArray arrayWithObject:item]; + + [self->pasteboard clearContents]; + if ([self->pasteboard writeObjects:items] == NO) + g_warning ("Failed to send clipboard to pasteboard"); self->last_change_count = [self->pasteboard changeCount]; - g_clear_pointer (&serializable, gdk_content_formats_unref); + GDK_END_MACOS_ALLOC_POOL; } static gboolean @@ -210,111 +194,3 @@ _gdk_macos_clipboard_check_externally_modified (GdkMacosClipboard *self) if ([self->pasteboard changeCount] != self->last_change_count) _gdk_macos_clipboard_load_contents (self); } - -@implementation GdkMacosClipboardDataProvider - --(id)initClipboard:(GdkMacosClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; -{ - [super initPasteboard:gdkClipboard->pasteboard mimetypes:mime_types]; - - self->clipboard = g_object_ref (GDK_CLIPBOARD (gdkClipboard)); - - return self; -} - --(void)dealloc -{ - g_clear_object (&self->clipboard); - - [super dealloc]; -} - --(void)pasteboardFinishedWithDataProvider:(NSPasteboard *)pasteboard -{ - g_clear_object (&self->clipboard); -} - -static void -on_data_ready_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GDK_BEGIN_MACOS_ALLOC_POOL; - - GdkClipboard *clipboard = (GdkClipboard *)object; - WriteRequest *wr = user_data; - GError *error = NULL; - NSData *data = nil; - - g_assert (GDK_IS_CLIPBOARD (clipboard)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (wr != NULL); - g_assert (G_IS_MEMORY_OUTPUT_STREAM (wr->stream)); - g_assert ([wr->item isKindOfClass:[NSPasteboardItem class]]); - - if (gdk_clipboard_write_finish (clipboard, result, &error)) - { - gsize size; - gpointer bytes; - - g_output_stream_close (G_OUTPUT_STREAM (wr->stream), NULL, NULL); - - size = g_memory_output_stream_get_size (wr->stream); - bytes = g_memory_output_stream_steal_data (wr->stream); - data = [[NSData alloc] initWithBytesNoCopy:bytes - length:size - deallocator:^(void *alloc, NSUInteger length) { g_free (alloc); }]; - } - else - { - g_warning ("Failed to serialize clipboard contents: %s", - error->message); - g_clear_error (&error); - } - - [wr->item setData:data forType:wr->type]; - - wr->done = TRUE; - - GDK_END_MACOS_ALLOC_POOL; -} - --(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type -{ - const char *mime_type = _gdk_macos_pasteboard_from_ns_type (type); - GMainContext *main_context = g_main_context_default (); - WriteRequest *wr; - - if (self->clipboard == NULL || mime_type == NULL) - { - [item setData:[NSData data] forType:type]; - return; - } - - wr = g_slice_new0 (WriteRequest); - wr->item = [item retain]; - wr->stream = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new_resizable ()); - wr->type = type; - wr->main_context = g_main_context_ref (main_context); - wr->done = FALSE; - - gdk_clipboard_write_async (self->clipboard, - mime_type, - G_OUTPUT_STREAM (wr->stream), - G_PRIORITY_DEFAULT, - self->cancellable, - on_data_ready_cb, - wr); - - /* We're forced to provide data synchronously via this API - * so we must block on the main loop. Using another main loop - * than the default tends to get us locked up here, so that is - * what we'll do for now. - */ - while (!wr->done) - g_main_context_iteration (wr->main_context, TRUE); - - write_request_free (wr); -} - -@end diff --git a/gdk/macos/gdkmacospasteboard-private.h b/gdk/macos/gdkmacospasteboard-private.h index c135d82d58e..fdeb9365356 100644 --- a/gdk/macos/gdkmacospasteboard-private.h +++ b/gdk/macos/gdkmacospasteboard-private.h @@ -27,15 +27,28 @@ G_BEGIN_DECLS -@interface GdkMacosPasteboardDataProvider : NSObject +@interface GdkMacosPasteboardItemDataProvider : NSObject { - NSPasteboard *pasteboard; - GCancellable *cancellable; - char **mimeTypes; + GdkContentProvider *_contentProvider; + GdkClipboard *_clipboard; + GdkDrag *_drag; } --(id)initPasteboard:(NSPasteboard *)pasteBoard mimetypes:(const char * const *)mime_types; --(NSArray *)types; +-(id)initForClipboard:(GdkClipboard *)clipboard withContentProvider:(GdkContentProvider *)contentProvider; +-(id)initForDrag:(GdkDrag *)drag withContentProvider:(GdkContentProvider *)contentProvider; + +@end + +@interface GdkMacosPasteboardItem : NSPasteboardItem +{ + GdkContentProvider *_contentProvider; + GdkClipboard *_clipboard; + GdkDrag *_drag; + NSRect _draggingFrame; +} + +-(id)initForClipboard:(GdkClipboard *)clipboard withContentProvider:(GdkContentProvider *)contentProvider; +-(id)initForDrag:(GdkDrag *)drag withContentProvider:(GdkContentProvider *)contentProvider; @end @@ -55,9 +68,6 @@ GInputStream *_gdk_macos_pasteboard_read_finish (GObject GAsyncResult *result, const char **out_mime_type, GError **error); -void _gdk_macos_pasteboard_send_content (NSPasteboard *pasteboard, - GdkContentProvider *content, - GdkMacosPasteboardDataProvider *dataProvider); G_END_DECLS diff --git a/gdk/macos/gdkmacospasteboard.c b/gdk/macos/gdkmacospasteboard.c index 0de91518e0c..fcfc5b6b966 100644 --- a/gdk/macos/gdkmacospasteboard.c +++ b/gdk/macos/gdkmacospasteboard.c @@ -166,87 +166,6 @@ _gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard) return load_offer_formats (pasteboard); } -void -_gdk_macos_pasteboard_send_content (NSPasteboard *pasteboard, - GdkContentProvider *content, - GdkMacosPasteboardDataProvider *dataProvider) -{ - GDK_BEGIN_MACOS_ALLOC_POOL; - - NSPasteboardItem *item; - - g_return_if_fail (pasteboard != NULL); - g_return_if_fail (GDK_IS_CONTENT_PROVIDER (content)); - - item = [[NSPasteboardItem alloc] init]; - [item setDataProvider:dataProvider forTypes:[dataProvider types]]; - - [pasteboard clearContents]; - if ([pasteboard writeObjects:[NSArray arrayWithObject:item]] == NO) - g_warning ("Failed to write object to pasteboard"); - - GDK_END_MACOS_ALLOC_POOL; -} - -@implementation GdkMacosPasteboardDataProvider - --(id)initPasteboard:(NSPasteboard *)pasteBoard mimetypes:(const char * const *)mime_types; -{ - [super init]; - - self->mimeTypes = g_strdupv ((char **)mime_types); - self->pasteboard = [pasteBoard retain]; - - return self; -} - --(void)dealloc -{ - g_cancellable_cancel (self->cancellable); - - if (self->pasteboard) - { - [self->pasteboard release]; - self->pasteboard = nil; - } - - g_clear_pointer (&self->mimeTypes, g_strfreev); - g_clear_object (&self->cancellable); - - [super dealloc]; -} - --(void)pasteboardFinishedWithDataProvider:(NSPasteboard *)pasteboard -{ -} - --(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type -{ -} - --(NSArray *)types -{ - NSMutableArray *ret = [[NSMutableArray alloc] init]; - - for (guint i = 0; self->mimeTypes[i]; i++) - { - const char *mime_type = self->mimeTypes[i]; - NSPasteboardType type; - NSPasteboardType alternate = nil; - - if ((type = _gdk_macos_pasteboard_to_ns_type (mime_type, &alternate))) - { - [ret addObject:type]; - if (alternate) - [ret addObject:alternate]; - } - } - - return g_steal_pointer (&ret); -} - -@end - static GInputStream * create_stream_from_nsdata (NSData *data) { @@ -415,3 +334,247 @@ _gdk_macos_pasteboard_register_drag_types (NSWindow *window) PTYPE(PNG), nil]]; } + +@implementation GdkMacosPasteboardItemDataProvider + +-(id)initForClipboard:(GdkClipboard*)clipboard withContentProvider:(GdkContentProvider*)contentProvider +{ + [super init]; + g_set_object (&self->_clipboard, clipboard); + g_set_object (&self->_contentProvider, contentProvider); + return self; +} + +-(id)initForDrag:(GdkDrag*)drag withContentProvider:(GdkContentProvider*)contentProvider +{ + [super init]; + g_set_object (&self->_drag, drag); + g_set_object (&self->_contentProvider, contentProvider); + return self; +} + +-(void)dealloc +{ + g_clear_object (&self->_contentProvider); + g_clear_object (&self->_clipboard); + g_clear_object (&self->_drag); + [super dealloc]; +} + +-(NSArray *)types +{ + NSMutableArray *ret = [[NSMutableArray alloc] init]; + GdkContentFormats *serializable; + const char * const *mime_types; + gsize n_mime_types; + + serializable = gdk_content_provider_ref_storable_formats (self->_contentProvider); + serializable = gdk_content_formats_union_serialize_mime_types (serializable); + mime_types = gdk_content_formats_get_mime_types (serializable, &n_mime_types); + + for (guint i = 0; mime_types[i]; i++) + { + const char *mime_type = mime_types[i]; + NSPasteboardType type; + NSPasteboardType alternate = nil; + + if ((type = _gdk_macos_pasteboard_to_ns_type (mime_type, &alternate))) + { + [ret addObject:type]; + if (alternate) + [ret addObject:alternate]; + } + } + + return g_steal_pointer (&ret); +} + +typedef struct +{ + GMemoryOutputStream *stream; + NSPasteboardItem *item; + NSPasteboardType type; + GMainContext *main_context; + guint done : 1; +} WriteRequest; + +static void +write_request_free (WriteRequest *wr) +{ + g_clear_pointer (&wr->main_context, g_main_context_unref); + g_clear_object (&wr->stream); + [wr->item release]; + g_slice_free (WriteRequest, wr); +} + +static void +on_data_ready_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + WriteRequest *wr = user_data; + GError *error = NULL; + NSData *data = nil; + gboolean ret; + + g_assert (G_IS_OBJECT (object)); + g_assert (GDK_IS_CLIPBOARD (object) || GDK_IS_DRAG (object)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (wr != NULL); + g_assert (G_IS_MEMORY_OUTPUT_STREAM (wr->stream)); + g_assert ([wr->item isKindOfClass:[NSPasteboardItem class]]); + + if (GDK_IS_CLIPBOARD (object)) + ret = gdk_clipboard_write_finish (GDK_CLIPBOARD (object), result, &error); + else + ret = gdk_drag_write_finish (GDK_DRAG (object), result, &error); + + if (ret) + { + gsize size; + gpointer bytes; + + g_output_stream_close (G_OUTPUT_STREAM (wr->stream), NULL, NULL); + + size = g_memory_output_stream_get_size (wr->stream); + bytes = g_memory_output_stream_steal_data (wr->stream); + data = [[NSData alloc] initWithBytesNoCopy:bytes + length:size + deallocator:^(void *alloc, NSUInteger length) { g_free (alloc); }]; + } + else + { + g_warning ("Failed to serialize pasteboard contents: %s", error->message); + g_clear_error (&error); + } + + [wr->item setData:data forType:wr->type]; + + wr->done = TRUE; + + GDK_END_MACOS_ALLOC_POOL; +} + +-(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type +{ + const char *mime_type = _gdk_macos_pasteboard_from_ns_type (type); + GMainContext *main_context = g_main_context_default (); + WriteRequest *wr; + + if (self->_contentProvider == NULL || mime_type == NULL) + { + [item setData:[NSData data] forType:type]; + return; + } + + wr = g_slice_new0 (WriteRequest); + wr->item = [item retain]; + wr->stream = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new_resizable ()); + wr->type = type; + wr->main_context = g_main_context_ref (main_context); + wr->done = FALSE; + + if (GDK_IS_CLIPBOARD (self->_clipboard)) + gdk_clipboard_write_async (self->_clipboard, + mime_type, + G_OUTPUT_STREAM (wr->stream), + G_PRIORITY_DEFAULT, + NULL, + on_data_ready_cb, + wr); + else if (GDK_IS_DRAG (self->_drag)) + gdk_drag_write_async (self->_drag, + mime_type, + G_OUTPUT_STREAM (wr->stream), + G_PRIORITY_DEFAULT, + NULL, + on_data_ready_cb, + wr); + else + g_return_if_reached (); + + /* We're forced to provide data synchronously via this API + * so we must block on the main loop. Using another main loop + * than the default tends to get us locked up here, so that is + * what we'll do for now. + */ + while (!wr->done) + g_main_context_iteration (wr->main_context, TRUE); + + write_request_free (wr); +} + +-(void)pasteboardFinishedWithDataProvider:(NSPasteboard *)pasteboard +{ + g_clear_object (&self->_clipboard); + g_clear_object (&self->_drag); + g_clear_object (&self->_contentProvider); +} + +@end + +@implementation GdkMacosPasteboardItem + +-(id)initForClipboard:(GdkClipboard*)clipboard withContentProvider:(GdkContentProvider*)contentProvider +{ + GdkMacosPasteboardItemDataProvider *dataProvider; + + dataProvider = [[GdkMacosPasteboardItemDataProvider alloc] initForClipboard:clipboard withContentProvider:contentProvider]; + + [super init]; + g_set_object (&self->_clipboard, clipboard); + g_set_object (&self->_contentProvider, contentProvider); + [self setDataProvider:dataProvider forTypes:[dataProvider types]]; + + [dataProvider release]; + + return self; +} + +-(id)initForDrag:(GdkDrag*)drag withContentProvider:(GdkContentProvider*)contentProvider +{ + GdkMacosPasteboardItemDataProvider *dataProvider; + + dataProvider = [[GdkMacosPasteboardItemDataProvider alloc] initForDrag:drag withContentProvider:contentProvider]; + + [super init]; + g_set_object (&self->_drag, drag); + g_set_object (&self->_contentProvider, contentProvider); + [self setDataProvider:dataProvider forTypes:[dataProvider types]]; + + [dataProvider release]; + + return self; +} + +-(void)dealloc +{ + g_clear_object (&self->_contentProvider); + g_clear_object (&self->_clipboard); + g_clear_object (&self->_drag); + [super dealloc]; +} + +-(NSRect)draggingFrame +{ + return self->_draggingFrame; +} + +-(void)setDraggingFrame:(NSRect)draggingFrame; +{ + self->_draggingFrame = draggingFrame; +} + +-(id)item +{ + return self; +} + +-(NSArray* (^) (void))imageComponentsProvider +{ + return nil; +} + +@end -- GitLab From d885d8fefc83c78a817bf367dbbad1904a7dcbab Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 18 Jun 2021 22:29:56 -0700 Subject: [PATCH 16/19] macos: internal drag handling Beginning a dragging session is somewhat working. The window is no longer refreshing during a drag operation. --- gdk/macos/GdkMacosBaseView.c | 21 +++++ gdk/macos/gdkmacosdrag-private.h | 9 +- gdk/macos/gdkmacosdrag.c | 99 +++++++++++++++++----- gdk/macos/gdkmacospasteboard-private.h | 2 + gdk/macos/gdkmacospasteboard.c | 26 ++++++ gdk/macos/gdkmacossurface.c | 111 +++---------------------- 6 files changed, 145 insertions(+), 123 deletions(-) diff --git a/gdk/macos/GdkMacosBaseView.c b/gdk/macos/GdkMacosBaseView.c index a86d9712b24..93bcf440b24 100644 --- a/gdk/macos/GdkMacosBaseView.c +++ b/gdk/macos/GdkMacosBaseView.c @@ -29,6 +29,7 @@ #include "gdkmacossurface-private.h" #include "gdkdebug.h" +#include "gdkdragprivate.h" @implementation GdkMacosBaseView @@ -712,4 +713,24 @@ GUINT_TO_POINTER (GIC_FILTER_PASSTHRU)); } +- (void)draggingSession:(NSDraggingSession *)session + endedAtPoint:(NSPoint)screenPoint + operation:(NSDragOperation)operation +{ + NSInteger sequence = [session draggingSequenceNumber]; + GdkMacosDisplay *display = [self gdkDisplay]; + GdkDrag *drag = _gdk_macos_display_find_drag (display, sequence); + gboolean success = operation != NSDragOperationNone; + + if (drag == NULL) + return; + + g_print ("Dragging session ended!: success=%d\n", success); + + if (!success) + gdk_drag_cancel (drag, GDK_DRAG_CANCEL_ERROR); + else + g_signal_emit_by_name (drag, "dnd-finished"); +} + @end diff --git a/gdk/macos/gdkmacosdrag-private.h b/gdk/macos/gdkmacosdrag-private.h index 4d33e451187..200cbee83e8 100644 --- a/gdk/macos/gdkmacosdrag-private.h +++ b/gdk/macos/gdkmacosdrag-private.h @@ -23,6 +23,7 @@ #include "gdkdragprivate.h" #include "gdkmacosdragsurface-private.h" +#include "gdkmacospasteboard-private.h" G_BEGIN_DECLS @@ -44,6 +45,8 @@ struct _GdkMacosDrag GdkSeat *drag_seat; GdkCursor *cursor; + NSInteger sequence; + int hot_x; int hot_y; @@ -63,7 +66,11 @@ struct _GdkMacosDragClass }; GType gdk_macos_drag_get_type (void) G_GNUC_CONST; -gboolean _gdk_macos_drag_begin (GdkMacosDrag *self); +gboolean _gdk_macos_drag_begin (GdkMacosDrag *self, + GdkContentProvider *provider, + NSWindow *window, + double quartz_x, + double quartz_y); void _gdk_macos_drag_surface_move (GdkMacosDrag *self, double x_root, double y_root); diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index c0d3517374d..93bd1b99899 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -53,6 +53,17 @@ enum { static GParamSpec *properties [N_PROPS]; +static void +gdk_macos_drag_drop_from_display (GdkMacosDrag *self) +{ + GdkDisplay *display; + + g_assert (GDK_IS_MACOS_DRAG (self)); + + display = gdk_drag_get_display (GDK_DRAG (self)); + _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), self->sequence, NULL); +} + static double ease_out_cubic (double t) { @@ -85,12 +96,12 @@ gdk_macos_zoomback_timeout (gpointer data) frame_clock = zb->frame_clock; if (!frame_clock) - return G_SOURCE_REMOVE; + goto hide_surface; current_time = gdk_frame_clock_get_frame_time (frame_clock); f = (current_time - zb->start_time) / (double) ANIM_TIME; if (f >= 1.0) - return G_SOURCE_REMOVE; + goto hide_surface; t = ease_out_cubic (f); @@ -105,6 +116,11 @@ gdk_macos_zoomback_timeout (gpointer data) _gdk_macos_surface_show (GDK_MACOS_SURFACE (drag->drag_surface)); return G_SOURCE_CONTINUE; + +hide_surface: + gdk_surface_hide (GDK_SURFACE (drag->drag_surface)); + + return G_SOURCE_REMOVE; } static GdkSurface * @@ -146,6 +162,8 @@ gdk_macos_drag_drop_done (GdkDrag *drag, g_assert (GDK_IS_MACOS_DRAG (self)); + g_print ("Drop done! success=%d\n", success); + if (success) { gdk_surface_hide (GDK_SURFACE (self->drag_surface)); @@ -212,22 +230,8 @@ gdk_macos_drag_cancel (GdkDrag *drag, self->cancelled = TRUE; drag_ungrab (self); - gdk_drag_drop_done (drag, FALSE); -} - -static void -gdk_macos_drag_drop_performed (GdkDrag *drag, - guint32 time) -{ - GdkMacosDrag *self = (GdkMacosDrag *)drag; - - g_assert (GDK_IS_MACOS_DRAG (self)); - - g_object_ref (self); - drag_ungrab (self); - g_signal_emit_by_name (drag, "dnd-finished"); - gdk_drag_drop_done (drag, TRUE); - g_object_unref (self); + gdk_surface_hide (GDK_SURFACE (self->drag_surface)); + gdk_macos_drag_drop_from_display (self); } static void @@ -287,6 +291,12 @@ gdk_drag_get_current_actions (GdkModifierType state, } } +static void +gdk_macos_drag_dnd_finished (GdkDrag *drag) +{ + gdk_macos_drag_drop_from_display (GDK_MACOS_DRAG (drag)); +} + static void gdk_macos_drag_finalize (GObject *object) { @@ -355,7 +365,8 @@ gdk_macos_drag_class_init (GdkMacosDragClass *klass) drag_class->drop_done = gdk_macos_drag_drop_done; drag_class->set_cursor = gdk_macos_drag_set_cursor; drag_class->cancel = gdk_macos_drag_cancel; - drag_class->drop_performed = gdk_macos_drag_drop_performed; + drag_class->handle_event = gdk_macos_drag_handle_event; + drag_class->dnd_finished = gdk_macos_drag_dnd_finished; properties [PROP_DRAG_SURFACE] = g_param_spec_object ("drag-surface", NULL, NULL, @@ -393,13 +404,59 @@ _gdk_macos_drag_operation (GdkMacosDrag *self) } gboolean -_gdk_macos_drag_begin (GdkMacosDrag *self) +_gdk_macos_drag_begin (GdkMacosDrag *self, + GdkContentProvider *content, + NSWindow *window, + double quartz_x, + double quartz_y) { + NSArray *items; + NSDraggingSession *session; + NSPasteboardItem *item; + NSTimeInterval nstime; + NSEvent *nsevent; + g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), FALSE); + GDK_BEGIN_MACOS_ALLOC_POOL; + + item = [[GdkMacosPasteboardItem alloc] initForDrag:GDK_DRAG (self) withContentProvider:content]; + items = [NSArray arrayWithObject:item]; + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + nstime = [[NSDate dateWithTimeIntervalSince1970: (gint64)time / 1000L] timeIntervalSinceReferenceDate]; + nsevent = [NSEvent mouseEventWithType: NSEventTypeLeftMouseDown + location: NSMakePoint (quartz_x, quartz_y) + modifierFlags: 0 + timestamp: nstime + windowNumber: [window windowNumber] + context: [window graphicsContext] + eventNumber: 0 + clickCount: 1 + pressure: 0.0]; + G_GNUC_END_IGNORE_DEPRECATIONS + + session = [[window contentView] beginDraggingSessionWithItems:items + event:nsevent + source:[window contentView]]; + self->sequence = [session draggingSequenceNumber]; + + GDK_END_MACOS_ALLOC_POOL; + + gdk_surface_hide (GDK_SURFACE (self->drag_surface)); +#if 0 _gdk_macos_surface_show (GDK_MACOS_SURFACE (self->drag_surface)); - return TRUE; + if (drag_grab (self)) +#endif + { + _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (gdk_drag_get_display (GDK_DRAG (self))), + self->sequence, + GDK_DRAG (self)); + return TRUE; + } + + //return FALSE; } void diff --git a/gdk/macos/gdkmacospasteboard-private.h b/gdk/macos/gdkmacospasteboard-private.h index fdeb9365356..166fa9b6a95 100644 --- a/gdk/macos/gdkmacospasteboard-private.h +++ b/gdk/macos/gdkmacospasteboard-private.h @@ -27,6 +27,8 @@ G_BEGIN_DECLS +#define GDK_MACOS_LOCAL_DND_MIME_TYPE "application/x-gtk-local-dnd" + @interface GdkMacosPasteboardItemDataProvider : NSObject { GdkContentProvider *_contentProvider; diff --git a/gdk/macos/gdkmacospasteboard.c b/gdk/macos/gdkmacospasteboard.c index fcfc5b6b966..84ee58db301 100644 --- a/gdk/macos/gdkmacospasteboard.c +++ b/gdk/macos/gdkmacospasteboard.c @@ -24,6 +24,7 @@ #include "gdkmacospasteboard-private.h" #include "gdkmacosutils-private.h" #include "gdk-private.h" +#include "gdkdragprivate.h" enum { TYPE_STRING, @@ -33,6 +34,7 @@ enum { TYPE_COLOR, TYPE_TIFF, TYPE_PNG, + TYPE_INTERNAL, TYPE_LAST }; @@ -67,6 +69,8 @@ get_pasteboard_type (int type) pasteboard_types[TYPE_FILE_URL] = [[NSString alloc] initWithUTF8String:"public.file-url"]; #endif + pasteboard_types[TYPE_INTERNAL] = [[NSString alloc] initWithUTF8String:"org.gtk.pasteboard.internal"]; + g_once_init_leave (&initialized, TRUE); } @@ -90,6 +94,8 @@ _gdk_macos_pasteboard_from_ns_type (NSPasteboardType type) return g_intern_string ("image/tiff"); else if ([type isEqualToString:PTYPE(PNG)]) return g_intern_string ("image/png"); + else if ([type isEqualToString:PTYPE(INTERNAL)]) + return g_intern_string (GDK_MACOS_LOCAL_DND_MIME_TYPE); G_GNUC_END_IGNORE_DEPRECATIONS; @@ -125,6 +131,10 @@ _gdk_macos_pasteboard_to_ns_type (const char *mime_type, { return PTYPE(PNG); } + else if (g_strcmp0 (mime_type, GDK_MACOS_LOCAL_DND_MIME_TYPE) == 0) + { + return PTYPE(INTERNAL); + } return nil; } @@ -283,6 +293,11 @@ _gdk_macos_pasteboard_read_async (GObject *object, NSData *data = [pasteboard dataForType:PTYPE(PNG)]; stream = create_stream_from_nsdata (data); } + else if (strcmp (mime_type, GDK_MACOS_LOCAL_DND_MIME_TYPE) == 0) + { + /* Should be internal copy */ + g_warn_if_reached (); + } if (stream != NULL) { @@ -332,6 +347,7 @@ _gdk_macos_pasteboard_register_drag_types (NSWindow *window) PTYPE(COLOR), PTYPE(TIFF), PTYPE(PNG), + PTYPE(INTERNAL), nil]]; } @@ -386,6 +402,16 @@ _gdk_macos_pasteboard_register_drag_types (NSWindow *window) } } + if ([ret count] == 0) + { + NSPasteboardType type; + NSPasteboardType alternate = nil; + + if ((type = _gdk_macos_pasteboard_to_ns_type (GDK_MACOS_LOCAL_DND_MIME_TYPE, &alternate))) + [ret addObject:type]; + } + + return g_steal_pointer (&ret); } diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 23fd927663b..31da0b37130 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -410,101 +410,6 @@ gdk_macos_surface_get_geometry (GdkSurface *surface, *height = surface->height; } -static NSImage * -_gdk_macos_surface_to_ns_image (GdkMacosSurface *self) -{ - // From: https://developer.apple.com/forums/thread/88315 - NSView *view = _gdk_macos_surface_get_view (self); - NSSize mySize = view.bounds.size; - NSSize imgSize = NSMakeSize (mySize.width, mySize.height); - - NSBitmapImageRep *bir = [view bitmapImageRepForCachingDisplayInRect:[view bounds]]; - [bir setSize:imgSize]; - [view cacheDisplayInRect:[view bounds] toBitmapImageRep:bir]; - - NSImage *image = [[NSImage alloc] initWithSize:imgSize]; - [image addRepresentation:bir]; - return image; -} - -static NSArray * -content_formats_to_pastboard_types (GdkContentFormats *formats) -{ - gsize n_mime_types; - const char *const *mime_types = gdk_content_formats_get_mime_types (formats, &n_mime_types); - NSMutableArray *types = [NSMutableArray arrayWithCapacity:(NSUInteger) n_mime_types]; - - for (gsize i = 0; i < n_mime_types; i++) - { - NSPasteboardType ptype = _gdk_macos_clipboard_to_ns_type (mime_types[i], NULL); - // g_message ("Adding type for mime type '%s' -> %s", mime_types[i], ptype); - if (ptype) - [types addObject:ptype]; - } - - // If no types can be set, default to a string. - // This can happen if we only use GType's for our (internal) DnD. - if ([types count] == 0) - [types addObject:NSPasteboardTypeString]; - - return types; -} - -static gboolean -gdk_macos_surface_drag_begin_idle (gpointer arg) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - GdkDrag *drag = GDK_DRAG (arg); - GdkContentProvider *content = gdk_drag_get_content (drag); - GdkContentFormats *formats = gdk_content_provider_ref_formats (content); - GdkSurface *surface = gdk_drag_get_surface (drag); - NSView *view = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface)); - NSEvent *event = _gdk_macos_display_get_last_nsevent (); - NSPasteboard *pasteboard; - NSPoint point; - NSArray *types; - NSImage *drag_image; - GdkMacosDraggingSource *source; - - if (!event) - return G_SOURCE_REMOVE; - - pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - - types = content_formats_to_pastboard_types (formats); - [pasteboard declareTypes:types owner:nil]; - - /* FIXME: If the event isn't a mouse event, use the global cursor position instead */ - point = [event locationInWindow]; - - drag_image = _gdk_macos_surface_to_ns_image (GDK_MACOS_SURFACE (surface)); - - if (drag_image == NULL) - { - return G_SOURCE_REMOVE; - } - - source = [[GdkMacosDraggingSource alloc] init:GDK_DRAG (drag)]; - - // I'd like to use beginDraggingSessionWithItems:event:source: - // instead, but this call tends to block UI updates in our app, - // so we use the deprecated dragImage: method for now. - // NB. This call is blocking: the code proceeds after the drag ends! - [view dragImage:drag_image - at:point - offset:NSZeroSize - event:event - pasteboard:pasteboard - source:source - slideBack:YES]; - - [drag_image release]; - [source release]; - [pool release]; - - return G_SOURCE_REMOVE; -} - static GdkDrag * gdk_macos_surface_drag_begin (GdkSurface *surface, GdkDevice *device, @@ -516,7 +421,9 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, GdkMacosSurface *self = (GdkMacosSurface *)surface; GdkMacosSurface *drag_surface; GdkMacosDrag *drag; + GdkDisplay *display; GdkSeat *seat; + int quartz_x, quartz_y; int sx; int sy; @@ -526,13 +433,18 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, g_assert (GDK_IS_MACOS_DEVICE (device)); g_assert (GDK_IS_CONTENT_PROVIDER (content)); + display = gdk_surface_get_display (surface); + _gdk_macos_display_to_display_coords (GDK_MACOS_DISPLAY (display), + surface->x + dx, + surface->y + dy, + &quartz_x, &quartz_y); + seat = gdk_device_get_seat (device); - _gdk_macos_surface_get_root_coords (GDK_MACOS_SURFACE (surface), &sx, &sy); drag_surface = _gdk_macos_surface_new (GDK_MACOS_DISPLAY (surface->display), GDK_SURFACE_TEMP, surface, - sx, sy, 1, 1); + quartz_x, quartz_y, 1, 1); drag = g_object_new (GDK_TYPE_MACOS_DRAG, "drag-surface", drag_surface, "surface", surface, @@ -542,7 +454,7 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, NULL); g_object_unref (drag_surface); - if (!_gdk_macos_drag_begin (drag)) + if (!_gdk_macos_drag_begin (drag, content, self->window, quartz_x, quartz_y)) { g_object_unref (drag); return NULL; @@ -550,9 +462,6 @@ gdk_macos_surface_drag_begin (GdkSurface *surface, gdk_seat_ungrab (seat); - /* drag will begin in an idle handler to avoid nested run loops */ - g_idle_add_full (G_PRIORITY_HIGH_IDLE, gdk_macos_surface_drag_begin_idle, drag, NULL); - /* Hold a reference until drop_done is called */ g_object_ref (drag); -- GitLab From 219e0f00911d72d7adc6efb52aab75d6d144807f Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 6 Feb 2022 15:29:41 +0100 Subject: [PATCH 17/19] macos: Use dedicated Dragging Source object --- gdk/macos/GdkMacosBaseView.c | 20 -------------------- gdk/macos/gdkmacosdrag-private.h | 3 +-- gdk/macos/gdkmacosdrag.c | 24 ++++++++---------------- gdk/macos/gdkmacospasteboard.c | 2 +- 4 files changed, 10 insertions(+), 39 deletions(-) diff --git a/gdk/macos/GdkMacosBaseView.c b/gdk/macos/GdkMacosBaseView.c index 93bcf440b24..42bbe1ab17a 100644 --- a/gdk/macos/GdkMacosBaseView.c +++ b/gdk/macos/GdkMacosBaseView.c @@ -713,24 +713,4 @@ GUINT_TO_POINTER (GIC_FILTER_PASSTHRU)); } -- (void)draggingSession:(NSDraggingSession *)session - endedAtPoint:(NSPoint)screenPoint - operation:(NSDragOperation)operation -{ - NSInteger sequence = [session draggingSequenceNumber]; - GdkMacosDisplay *display = [self gdkDisplay]; - GdkDrag *drag = _gdk_macos_display_find_drag (display, sequence); - gboolean success = operation != NSDragOperationNone; - - if (drag == NULL) - return; - - g_print ("Dragging session ended!: success=%d\n", success); - - if (!success) - gdk_drag_cancel (drag, GDK_DRAG_CANCEL_ERROR); - else - g_signal_emit_by_name (drag, "dnd-finished"); -} - @end diff --git a/gdk/macos/gdkmacosdrag-private.h b/gdk/macos/gdkmacosdrag-private.h index 200cbee83e8..f2b20010c33 100644 --- a/gdk/macos/gdkmacosdrag-private.h +++ b/gdk/macos/gdkmacosdrag-private.h @@ -56,8 +56,7 @@ struct _GdkMacosDrag int start_x; int start_y; - guint did_update : 1; - guint cancelled : 1; + gboolean cancelled; }; struct _GdkMacosDragClass diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index 93bd1b99899..b0f930eaec9 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -32,6 +32,8 @@ #include "gdk/gdkseatprivate.h" #include "gdk/gdk-private.h" +#import "GdkMacosDraggingSource.h" + #define BIG_STEP 20 #define SMALL_STEP 1 #define ANIM_TIME 500000 /* .5 seconds */ @@ -365,7 +367,6 @@ gdk_macos_drag_class_init (GdkMacosDragClass *klass) drag_class->drop_done = gdk_macos_drag_drop_done; drag_class->set_cursor = gdk_macos_drag_set_cursor; drag_class->cancel = gdk_macos_drag_cancel; - drag_class->handle_event = gdk_macos_drag_handle_event; drag_class->dnd_finished = gdk_macos_drag_dnd_finished; properties [PROP_DRAG_SURFACE] = @@ -413,8 +414,8 @@ _gdk_macos_drag_begin (GdkMacosDrag *self, NSArray *items; NSDraggingSession *session; NSPasteboardItem *item; - NSTimeInterval nstime; NSEvent *nsevent; + GdkMacosDraggingSource *source; g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), FALSE); @@ -422,25 +423,16 @@ _gdk_macos_drag_begin (GdkMacosDrag *self, item = [[GdkMacosPasteboardItem alloc] initForDrag:GDK_DRAG (self) withContentProvider:content]; items = [NSArray arrayWithObject:item]; - - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - nstime = [[NSDate dateWithTimeIntervalSince1970: (gint64)time / 1000L] timeIntervalSinceReferenceDate]; - nsevent = [NSEvent mouseEventWithType: NSEventTypeLeftMouseDown - location: NSMakePoint (quartz_x, quartz_y) - modifierFlags: 0 - timestamp: nstime - windowNumber: [window windowNumber] - context: [window graphicsContext] - eventNumber: 0 - clickCount: 1 - pressure: 0.0]; - G_GNUC_END_IGNORE_DEPRECATIONS + source = [[GdkMacosDraggingSource alloc] init:GDK_DRAG (self)]; + nsevent = _gdk_macos_display_get_last_nsevent (); session = [[window contentView] beginDraggingSessionWithItems:items event:nsevent - source:[window contentView]]; + source:source]; self->sequence = [session draggingSequenceNumber]; + [source autorelease]; + GDK_END_MACOS_ALLOC_POOL; gdk_surface_hide (GDK_SURFACE (self->drag_surface)); diff --git a/gdk/macos/gdkmacospasteboard.c b/gdk/macos/gdkmacospasteboard.c index 84ee58db301..6a2959a7cca 100644 --- a/gdk/macos/gdkmacospasteboard.c +++ b/gdk/macos/gdkmacospasteboard.c @@ -388,7 +388,7 @@ _gdk_macos_pasteboard_register_drag_types (NSWindow *window) serializable = gdk_content_formats_union_serialize_mime_types (serializable); mime_types = gdk_content_formats_get_mime_types (serializable, &n_mime_types); - for (guint i = 0; mime_types[i]; i++) + for (guint i = 0; i < n_mime_types; i++) { const char *mime_type = mime_types[i]; NSPasteboardType type; -- GitLab From 74fd6caf52b28e6c6d67f362ebea92631412f2dd Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 6 Feb 2022 15:35:52 +0100 Subject: [PATCH 18/19] macos: Remove redundant calls for caching the drag object --- gdk/macos/GdkMacosDraggingSource.c | 1 - gdk/macos/gdkmacosdrag.c | 16 ++++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/gdk/macos/GdkMacosDraggingSource.c b/gdk/macos/GdkMacosDraggingSource.c index 60775dfe51b..dc8debcc903 100644 --- a/gdk/macos/GdkMacosDraggingSource.c +++ b/gdk/macos/GdkMacosDraggingSource.c @@ -59,7 +59,6 @@ GdkDisplay *display = gdk_drag_get_display (drag); int x, y; - _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (display), [session draggingSequenceNumber], GDK_DRAG (drag)); _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y); _gdk_macos_drag_set_start_position (drag, x, y); _gdk_macos_drag_set_last_position (drag, x, y); diff --git a/gdk/macos/gdkmacosdrag.c b/gdk/macos/gdkmacosdrag.c index b0f930eaec9..9039d3c55c0 100644 --- a/gdk/macos/gdkmacosdrag.c +++ b/gdk/macos/gdkmacosdrag.c @@ -436,19 +436,11 @@ _gdk_macos_drag_begin (GdkMacosDrag *self, GDK_END_MACOS_ALLOC_POOL; gdk_surface_hide (GDK_SURFACE (self->drag_surface)); -#if 0 - _gdk_macos_surface_show (GDK_MACOS_SURFACE (self->drag_surface)); - if (drag_grab (self)) -#endif - { - _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (gdk_drag_get_display (GDK_DRAG (self))), - self->sequence, - GDK_DRAG (self)); - return TRUE; - } - - //return FALSE; + _gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (gdk_drag_get_display (GDK_DRAG (self))), + self->sequence, + GDK_DRAG (self)); + return TRUE; } void -- GitLab From 829bf0d118a9861545df9e50af46a584b97a7b1d Mon Sep 17 00:00:00 2001 From: Arjan Molenaar Date: Sun, 6 Feb 2022 15:59:40 +0100 Subject: [PATCH 19/19] macos: Use proper glib function for setting an object ref --- gdk/macos/GdkMacosDraggingSource.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdk/macos/GdkMacosDraggingSource.c b/gdk/macos/GdkMacosDraggingSource.c index dc8debcc903..fc53a9e68b2 100644 --- a/gdk/macos/GdkMacosDraggingSource.c +++ b/gdk/macos/GdkMacosDraggingSource.c @@ -32,7 +32,7 @@ { g_return_val_if_fail (GDK_IS_MACOS_DRAG (drag), self); - self->gdk_drag = g_object_ref (drag); + g_set_object (&self->gdk_drag, drag); return self; } -- GitLab