From 20218353262dc38d82adb945a6a38d6e629c1417 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 31 May 2022 18:05:41 +0800 Subject: [PATCH] Implement `return-frame' for DND on NS * src/nsselect.m (Fns_begin_drag): New argument `return-frame'. (syms_of_nsselect): New defsym. * src/nsterm.h (EmacsWindow): New fields. * src/nsterm.m (ns_read_socket): Split parts off to ns_read_socket_1. (ns_read_socket_1): New function. (ns_flush_display): Use that function instead. ([EmacsWindow beginDrag:forPasteboard:]): Update for return-frame. --- src/nsselect.m | 36 +++++++++++++-- src/nsterm.h | 14 +++++- src/nsterm.m | 121 ++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 139 insertions(+), 32 deletions(-) diff --git a/src/nsselect.m b/src/nsselect.m index 1ff627e6579..63cea365e23 100644 --- a/src/nsselect.m +++ b/src/nsselect.m @@ -662,7 +662,7 @@ ns_dnd_action_from_operation (NSDragOperation operation) } } -DEFUN ("ns-begin-drag", Fns_begin_drag, Sns_begin_drag, 3, 3, 0, +DEFUN ("ns-begin-drag", Fns_begin_drag, Sns_begin_drag, 3, 4, 0, doc: /* Begin a drag-and-drop operation on FRAME. FRAME must be a window system frame. PBOARD is an alist of (TYPE @@ -680,13 +680,30 @@ data inside PBOARD. Return the action that the drop target actually chose to perform, or nil if no action was performed (either because there was no drop -target, or the drop was rejected). */) - (Lisp_Object frame, Lisp_Object pboard, Lisp_Object action) +target, or the drop was rejected). If RETURN_FRAME is the symbol +`now', also return any frame that mouse moves into during the +drag-and-drop operation, whilst simultaneously cancelling it. Any +other non-nil value means to do the same, but to wait for the mouse to +leave FRAME first. */) + (Lisp_Object frame, Lisp_Object pboard, Lisp_Object action, + Lisp_Object return_frame) { - struct frame *f; + struct frame *f, *return_to; NSPasteboard *pasteboard; EmacsWindow *window; NSDragOperation operation; + enum ns_return_frame_mode mode; + Lisp_Object val; + + if (EQ (return_frame, Qnow)) + mode = RETURN_FRAME_NOW; + else if (!NILP (return_frame)) + mode = RETURN_FRAME_EVENTUALLY; + else + mode = RETURN_FRAME_NEVER; + + if (NILP (pboard)) + signal_error ("Empty pasteboard", pboard); f = decode_window_system_frame (frame); pasteboard = [NSPasteboard pasteboardWithName: NSPasteboardNameDrag]; @@ -696,7 +713,15 @@ target, or the drop was rejected). */) ns_lisp_to_pasteboard (pboard, pasteboard); operation = [window beginDrag: operation - forPasteboard: pasteboard]; + forPasteboard: pasteboard + withMode: mode + returnFrameTo: &return_to]; + + if (return_to) + { + XSETFRAME (val, return_to); + return val; + } return ns_dnd_action_from_operation (operation); } @@ -714,6 +739,7 @@ syms_of_nsselect (void) DEFSYM (QXdndActionMove, "XdndActionMove"); DEFSYM (QXdndActionLink, "XdndActionLink"); DEFSYM (QXdndActionPrivate, "XdndActionPrivate"); + DEFSYM (Qnow, "now"); defsubr (&Sns_disown_selection_internal); defsubr (&Sns_get_selection); diff --git a/src/nsterm.h b/src/nsterm.h index f74c457fe33..c39b66534f6 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -408,6 +408,13 @@ typedef id instancetype; @end #endif +enum ns_return_frame_mode + { + RETURN_FRAME_NEVER, + RETURN_FRAME_EVENTUALLY, + RETURN_FRAME_NOW, + }; + /* EmacsWindow */ @interface EmacsWindow : NSWindow { @@ -415,6 +422,9 @@ typedef id instancetype; NSEvent *last_drag_event; NSDragOperation drag_op; NSDragOperation selected_op; + + struct frame *dnd_return_frame; + enum ns_return_frame_mode dnd_mode; } #ifdef NS_IMPL_GNUSTEP @@ -432,7 +442,9 @@ typedef id instancetype; - (void) setAppearance; - (void) setLastDragEvent: (NSEvent *) event; - (NSDragOperation) beginDrag: (NSDragOperation) op - forPasteboard: (NSPasteboard *) pasteboard; + forPasteboard: (NSPasteboard *) pasteboard + withMode: (enum ns_return_frame_mode) mode + returnFrameTo: (struct frame **) frame_return; @end diff --git a/src/nsterm.m b/src/nsterm.m index 0f1b5974574..f4fde9bd127 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -4529,11 +4529,14 @@ check_native_fs () static int -ns_read_socket (struct terminal *terminal, struct input_event *hold_quit) +ns_read_socket_1 (struct terminal *terminal, struct input_event *hold_quit, + BOOL no_release) /* -------------------------------------------------------------------------- External (hook): Post an event to ourself and keep reading events until we read it back again. In effect process all events which were waiting. From 21+ we have to manage the event buffer ourselves. + + NO_RELEASE means not to touch the global autorelease pool. -------------------------------------------------------------------------- */ { struct input_event ev; @@ -4564,11 +4567,14 @@ ns_read_socket (struct terminal *terminal, struct input_event *hold_quit) ns_init_events (&ev); q_event_ptr = hold_quit; - /* We manage autorelease pools by allocate/reallocate each time around - the loop; strict nesting is occasionally violated but seems not to - matter... earlier methods using full nesting caused major memory leaks. */ - [outerpool release]; - outerpool = [[NSAutoreleasePool alloc] init]; + if (!no_release) + { + /* We manage autorelease pools by allocate/reallocate each time around + the loop; strict nesting is occasionally violated but seems not to + matter... earlier methods using full nesting caused major memory leaks. */ + [outerpool release]; + outerpool = [[NSAutoreleasePool alloc] init]; + } /* If have pending open-file requests, attend to the next one of those. */ if (ns_pending_files && [ns_pending_files count] != 0 @@ -4607,6 +4613,12 @@ ns_read_socket (struct terminal *terminal, struct input_event *hold_quit) return nevents; } +static int +ns_read_socket (struct terminal *terminal, struct input_event *hold_quit) +{ + return ns_read_socket_1 (terminal, hold_quit, NO); +} + static int ns_select_1 (int nfds, fd_set *readfds, fd_set *writefds, @@ -5191,19 +5203,10 @@ ns_update_window_end (struct window *w, bool cursor_on_p, static void ns_flush_display (struct frame *f) { - NSAutoreleasePool *ap; - - ap = [[NSAutoreleasePool alloc] init]; - - /* Called from some of the minibuffer code. Run the event loop once - to make the toolkit make changes that were made to the back - buffer visible again. */ - - send_appdefined = YES; - ns_send_appdefined (-1); + struct input_event ie; - [NSApp run]; - [ap release]; + EVENT_INIT (ie); + ns_read_socket_1 (FRAME_TERMINAL (f), &ie, YES); } /* This and next define (many of the) public functions in this @@ -9579,14 +9582,51 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) selected_op = operation; } +#ifdef NS_IMPL_COCOA +- (void) draggedImage: (NSImage *) dragged_image + movedTo: (NSPoint) screen_point +{ + NSInteger window_number; + NSWindow *w; + + if (dnd_mode == RETURN_FRAME_NEVER) + return; + + window_number = [NSWindow windowNumberAtPoint: [NSEvent mouseLocation] + belowWindowWithWindowNumber: 0]; + w = [NSApp windowWithWindowNumber: window_number]; + + if (!w || w != self) + dnd_mode = RETURN_FRAME_NOW; + + if (dnd_mode != RETURN_FRAME_NOW + || ![[w delegate] isKindOfClass: [EmacsView class]]) + return; + + dnd_return_frame = ((EmacsView *) [w delegate])->emacsframe; + + /* FIXME: there must be a better way to leave the event loop. */ + [NSException raise: @"" + format: @"Must return DND frame"]; +} +#endif + - (NSDragOperation) beginDrag: (NSDragOperation) op forPasteboard: (NSPasteboard *) pasteboard + withMode: (enum ns_return_frame_mode) mode + returnFrameTo: (struct frame **) frame_return { NSImage *image; +#ifdef NS_IMPL_COCOA + NSInteger window_number; + NSWindow *w; +#endif drag_op = op; selected_op = NSDragOperationNone; image = [[NSImage alloc] initWithSize: NSMakeSize (1.0, 1.0)]; + dnd_mode = mode; + dnd_return_frame = NULL; /* Now draw transparency onto the image. */ [image lockFocus]; @@ -9596,18 +9636,47 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) [image unlockFocus]; block_input (); - if (last_drag_event) - [self dragImage: image - at: NSMakePoint (0, 0) - offset: NSMakeSize (0, 0) - event: last_drag_event - pasteboard: pasteboard - source: self - slideBack: NO]; +#ifdef NS_IMPL_COCOA + if (mode == RETURN_FRAME_NOW) + { + window_number = [NSWindow windowNumberAtPoint: [NSEvent mouseLocation] + belowWindowWithWindowNumber: 0]; + w = [NSApp windowWithWindowNumber: window_number]; + + if (w && [[w delegate] isKindOfClass: [EmacsView class]]) + { + *frame_return = ((EmacsView *) [w delegate])->emacsframe; + [image release]; + unblock_input (); + + return NSDragOperationNone; + } + } + + @try + { +#endif + if (last_drag_event) + [self dragImage: image + at: NSMakePoint (0, 0) + offset: NSMakeSize (0, 0) + event: last_drag_event + pasteboard: pasteboard + source: self + slideBack: NO]; +#ifdef NS_IMPL_COCOA + } + @catch (NSException *e) + { + /* Ignore. This is probably the wrong way to leave the + drag-and-drop run loop. */ + } +#endif unblock_input (); [image release]; + *frame_return = dnd_return_frame; return selected_op; } -- 2.39.2