diff --git a/lisp/dnd.el b/lisp/dnd.el index 8b11973eb41..4f71edf1aa1 100644 --- a/lisp/dnd.el +++ b/lisp/dnd.el @@ -107,31 +107,33 @@ program." (defun dnd-handle-movement (posn) "Handle mouse movement to POSN when receiving a drop from another program." - (when dnd-scroll-margin - (ignore-errors - (let* ((row (cdr (posn-col-row posn))) - (window (when (windowp (posn-window posn)) - (posn-window posn))) - (text-height (window-text-height window)) - ;; Make sure it's possible to scroll both up - ;; and down if the margin is too large for the - ;; window. - (margin (min (/ text-height 3) dnd-scroll-margin))) - ;; At 2 lines, the window becomes too small for any - ;; meaningful scrolling. - (unless (<= text-height 2) - (cond - ;; Inside the bottom scroll margin, scroll up. - ((> row (- text-height margin)) - (with-selected-window window - (scroll-up 1))) - ;; Inside the top scroll margin, scroll down. - ((< row margin) - (with-selected-window window - (scroll-down 1)))))))) - (when dnd-indicate-insertion-point - (ignore-errors - (goto-char (posn-point posn))))) + (when (windowp (posn-window posn)) + (with-selected-window (posn-window posn) + (when dnd-scroll-margin + (ignore-errors + (let* ((row (cdr (posn-col-row posn))) + (window (when (windowp (posn-window posn)) + (posn-window posn))) + (text-height (window-text-height window)) + ;; Make sure it's possible to scroll both up + ;; and down if the margin is too large for the + ;; window. + (margin (min (/ text-height 3) dnd-scroll-margin))) + ;; At 2 lines, the window becomes too small for any + ;; meaningful scrolling. + (unless (<= text-height 2) + (cond + ;; Inside the bottom scroll margin, scroll up. + ((> row (- text-height margin)) + (with-selected-window window + (scroll-up 1))) + ;; Inside the top scroll margin, scroll down. + ((< row margin) + (with-selected-window window + (scroll-down 1)))))))) + (when dnd-indicate-insertion-point + (ignore-errors + (goto-char (posn-point posn))))))) (defun dnd-handle-one-url (window action url) "Handle one dropped url by calling the appropriate handler. diff --git a/lisp/term/x-win.el b/lisp/term/x-win.el index 9ae238661e0..f0b9b27f66b 100644 --- a/lisp/term/x-win.el +++ b/lisp/term/x-win.el @@ -85,6 +85,7 @@ (defvar x-selection-timeout) (defvar x-session-id) (defvar x-session-previous-id) +(defvar x-dnd-movement-function) (defun x-handle-no-bitmap-icon (_switch) (setq default-frame-alist (cons '(icon-type) default-frame-alist))) @@ -1576,6 +1577,13 @@ frames on all displays." (add-variable-watcher 'x-gtk-use-native-input #'x-gtk-use-native-input-watcher) +(defun x-dnd-movement (_frame position) + "Handle movement to POSITION during drag-and-drop." + (dnd-handle-movement position) + (redisplay)) + +(setq x-dnd-movement-function #'x-dnd-movement) + (provide 'x-win) (provide 'term/x-win) diff --git a/src/frame.c b/src/frame.c index 7a9ed3302e4..05b22ac72ba 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1987,6 +1987,10 @@ delete_frame (Lisp_Object frame, Lisp_Object force) else error ("Attempt to delete the only frame"); } +#ifdef HAVE_X_WINDOWS + else if (x_dnd_in_progress && f == x_dnd_frame) + error ("Attempt to delete the drop source frame"); +#endif XSETFRAME (frame, f); diff --git a/src/xterm.c b/src/xterm.c index 2e4df67c76e..d29a7a122aa 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -846,7 +846,10 @@ static int x_filter_event (struct x_display_info *, XEvent *); /* Global state maintained during a drag-and-drop operation. */ /* Flag that indicates if a drag-and-drop operation is in progress. */ -static bool x_dnd_in_progress; +bool x_dnd_in_progress; + +/* The frame where the drag-and-drop operation originated. */ +struct frame *x_dnd_frame; /* Flag that indicates if a drag-and-drop operation is no longer in progress, but the nested event loop should continue to run, because @@ -946,9 +949,6 @@ static Atom *x_dnd_targets = NULL; /* The number of elements in that array. */ static int x_dnd_n_targets; -/* The frame where the drag-and-drop operation originated. */ -static struct frame *x_dnd_frame; - /* The old window attributes of the root window before the drag-and-drop operation started. It is used to keep the old event mask around, since that should be restored after the operation @@ -959,6 +959,13 @@ static XWindowAttributes x_dnd_old_window_attrs; up the drag and drop operation. */ static bool x_dnd_unwind_flag; +/* The frame for which `x-dnd-movement-function' should be called. */ +static struct frame *x_dnd_movement_frame; + +/* The coordinates which the movement function should be called + with. */ +static int x_dnd_movement_x, x_dnd_movement_y; + struct x_client_list_window { Window window; @@ -3137,6 +3144,23 @@ x_dnd_send_position (struct frame *f, Window target, int supported, { struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); XEvent msg; + struct frame *target_frame; + int dest_x, dest_y; + Window child_return; + + target_frame = x_top_window_to_frame (dpyinfo, target); + + if (target_frame && XTranslateCoordinates (dpyinfo->display, + dpyinfo->root_window, + FRAME_X_WINDOW (target_frame), + root_x, root_y, &dest_x, + &dest_y, &child_return)) + { + x_dnd_movement_frame = target_frame; + x_dnd_movement_x = dest_x; + x_dnd_movement_y = dest_y; + return; + } if (target == x_dnd_mouse_rect_target && x_dnd_mouse_rect.width @@ -3151,9 +3175,6 @@ x_dnd_send_position (struct frame *f, Window target, int supported, return; } - if (x_top_window_to_frame (dpyinfo, target)) - return; - msg.xclient.type = ClientMessage; msg.xclient.message_type = dpyinfo->Xatom_XdndPosition; msg.xclient.format = 32; @@ -9143,6 +9164,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, ptrdiff_t i, end, fill; XTextProperty prop; xm_drop_start_message dmsg; + Lisp_Object frame_object, x, y; if (!FRAME_VISIBLE_P (f)) error ("Frame is invisible"); @@ -9229,6 +9251,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, = x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_client_list_stacking); x_dnd_toplevels = NULL; x_dnd_allow_current_frame = allow_current_frame; + x_dnd_movement_frame = NULL; if (x_dnd_use_toplevels) { @@ -9307,6 +9330,28 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, #endif #endif + if (x_dnd_movement_frame) + { + XSETFRAME (frame_object, x_dnd_movement_frame); + XSETINT (x, x_dnd_movement_x); + XSETINT (y, x_dnd_movement_y); + x_dnd_movement_frame = NULL; + + if (!NILP (Vx_dnd_movement_function) + && !FRAME_TOOLTIP_P (XFRAME (frame_object))) + { + x_dnd_old_window_attrs = root_window_attrs; + x_dnd_unwind_flag = true; + + ref = SPECPDL_INDEX (); + record_unwind_protect_ptr (x_dnd_cleanup_drag_and_drop, f); + call2 (Vx_dnd_movement_function, frame_object, + Fposn_at_x_y (x, y, frame_object, Qnil)); + x_dnd_unwind_flag = false; + unbind_to (ref, Qnil); + } + } + if (hold_quit.kind != NO_EVENT) { if (hold_quit.kind == SELECTION_REQUEST_EVENT) @@ -20746,46 +20791,6 @@ x_free_frame_resources (struct frame *f) Lisp_Object bar; struct scroll_bar *b; #endif - xm_drop_start_message dmsg; - - if (x_dnd_in_progress && f == x_dnd_frame) - { - block_input (); - if (x_dnd_last_seen_window != None - && x_dnd_last_protocol_version != -1) - x_dnd_send_leave (f, x_dnd_last_seen_window); - else if (x_dnd_last_seen_window != None - && !XM_DRAG_STYLE_IS_DROP_ONLY (x_dnd_last_motif_style) - && x_dnd_last_motif_style != XM_DRAG_STYLE_NONE - && x_dnd_motif_setup_p) - { - dmsg.reason = XM_DRAG_REASON (XM_DRAG_ORIGINATOR_INITIATOR, - XM_DRAG_REASON_DROP_START); - dmsg.byte_order = XM_TARGETS_TABLE_CUR; - dmsg.timestamp = 0; - dmsg.side_effects - = XM_DRAG_SIDE_EFFECT (xm_side_effect_from_action (dpyinfo, - x_dnd_wanted_action), - XM_DROP_SITE_VALID, - xm_side_effect_from_action (dpyinfo, - x_dnd_wanted_action), - XM_DROP_ACTION_DROP_CANCEL); - dmsg.x = 0; - dmsg.y = 0; - dmsg.index_atom = dpyinfo->Xatom_XdndSelection; - dmsg.source_window = FRAME_X_WINDOW (f); - - xm_send_drop_message (dpyinfo, FRAME_X_WINDOW (f), - x_dnd_last_seen_window, &dmsg); - } - unblock_input (); - - x_dnd_end_window = None; - x_dnd_last_seen_window = None; - x_dnd_in_progress = false; - x_dnd_waiting_for_finish = false; - x_dnd_frame = NULL; - } block_input (); @@ -23054,4 +23059,11 @@ coordinates to a Motif drop receiver when the mouse moves outside it during a drag-and-drop session, to work around broken implementations of Motif. */); x_dnd_fix_motif_leave = true; + + DEFVAR_LISP ("x-dnd-movement-function", Vx_dnd_movement_function, + doc: /* Function called upon mouse movement on a frame during drag-and-drop. +It should either be nil, or accept two arguments FRAME and POSITION, +where FRAME is the frame the mouse is on top of, and POSITION is a +mouse position list. */); + Vx_dnd_movement_function = Qnil; } diff --git a/src/xterm.h b/src/xterm.h index 57036af2bb5..5627fd23c57 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -1548,6 +1548,9 @@ extern void x_session_close (void); extern struct input_event xg_pending_quit_event; #endif +extern bool x_dnd_in_progress; +extern struct frame *x_dnd_frame; + #ifdef HAVE_XINPUT2 struct xi_device_t *xi_device_from_id (struct x_display_info *, int); #endif