]> git.eshelyaron.com Git - emacs.git/commitdiff
Implement `return-frame' for DND on NS
authorPo Lu <luangruo@yahoo.com>
Tue, 31 May 2022 10:05:41 +0000 (18:05 +0800)
committerPo Lu <luangruo@yahoo.com>
Tue, 31 May 2022 10:05:41 +0000 (18:05 +0800)
* 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
src/nsterm.h
src/nsterm.m

index 1ff627e65790be455f5103eb9c80540c9270d2a8..63cea365e233916ca164b4e479173da6ab0d070a 100644 (file)
@@ -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);
index f74c457fe336e90ba307ac85858e55b68db59d84..c39b66534f628f6cb998a52fb97343b7c86f600f 100644 (file)
@@ -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
 
 
index 0f1b5974574fe133b94b36a562157f29719ca9df..f4fde9bd127a61518210bf7a68b58397244007ba 100644 (file)
@@ -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;
 }