From 5359062be603e22d1ee07c21b0840fdb98a704a3 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 26 Mar 2022 10:15:53 +0800 Subject: [PATCH] Avoid ClientMessage overhead when dragging stuff to other frames * lisp/dired.el (dired-mouse-drag): Handle correctly dragging from dired buffers in nonselected windows. * lisp/x-dnd.el (x-dnd-handle-drag-n-drop-event): Understand new client message type. * src/xterm.c (x_dnd_send_enter, x_dnd_send_position) (x_dnd_send_leave): Ignore if window is the top window of a frame. (x_dnd_send_drop): Send special DND event in that case. --- lisp/dired.el | 7 ++++-- lisp/x-dnd.el | 55 +++++++++++++++++++++++++++++-------------- src/xterm.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 19 deletions(-) diff --git a/lisp/dired.el b/lisp/dired.el index c5e389c9ce1..3c37a887baf 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -1704,7 +1704,8 @@ see `dired-use-ls-dired' for more details.") (when mark-active (deactivate-mark)) (save-excursion - (goto-char (posn-point (event-end event))) + (with-selected-window (posn-window (event-end event)) + (goto-char (posn-point (event-end event)))) (track-mouse (let ((new-event (read-event))) (if (not (eq (event-basic-type new-event) 'mouse-movement)) @@ -1715,7 +1716,9 @@ see `dired-use-ls-dired' for more details.") (condition-case nil (progn (gui-backend-set-selection 'XdndSelection - (dired-file-name-at-point)) + (with-selected-window (posn-window + (event-end event)) + (dired-file-name-at-point))) (x-begin-drag '("text/uri-list" "text/x-dnd-username") (if (eq 'dired-mouse-drag-files 'link) diff --git a/lisp/x-dnd.el b/lisp/x-dnd.el index 0529d223dbe..e801c4fdfce 100644 --- a/lisp/x-dnd.el +++ b/lisp/x-dnd.el @@ -115,6 +115,9 @@ the type we want for the drop, the action we want for the drop, any protocol specific data.") +(declare-function x-get-selection-internal "xselect.c" + (selection-symbol target-type &optional time-stamp terminal)) + (defvar x-dnd-empty-state [nil nil nil nil nil nil nil]) (declare-function x-register-dnd-atom "xselect.c") @@ -336,21 +339,41 @@ nil if not." Currently XDND, Motif and old KDE 1.x protocols are recognized." (interactive "e") (let* ((client-message (car (cdr (cdr event)))) - (window (posn-window (event-start event))) - (message-atom (aref client-message 0)) - (frame (aref client-message 1)) - (format (aref client-message 2)) - (data (aref client-message 3))) - - (cond ((equal "DndProtocol" message-atom) ; Old KDE 1.x. - (x-dnd-handle-old-kde event frame window message-atom format data)) - - ((equal "_MOTIF_DRAG_AND_DROP_MESSAGE" message-atom) ; Motif - (x-dnd-handle-motif event frame window message-atom format data)) - - ((and (> (length message-atom) 4) ; XDND protocol. - (equal "Xdnd" (substring message-atom 0 4))) - (x-dnd-handle-xdnd event frame window message-atom format data))))) + (window (posn-window (event-start event)))) + (if (eq (and (consp client-message) + (car client-message)) + 'XdndSelection) + ;; This is an internal Emacs message caused by something being + ;; dropped on top of a frame. + (progn + (let ((action (cdr (assoc (symbol-name (cadr client-message)) + x-dnd-xdnd-to-action))) + (targets (cddr client-message))) + (x-dnd-save-state window nil nil + (apply #'vector targets)) + (x-dnd-maybe-call-test-function window action) + (unwind-protect + (x-dnd-drop-data event (if (framep window) window + (window-frame window)) + window + (x-get-selection-internal + 'XdndSelection + (intern (x-dnd-current-type window))) + (x-dnd-current-type window)) + (x-dnd-forget-drop window)))) + (let ((message-atom (aref client-message 0)) + (frame (aref client-message 1)) + (format (aref client-message 2)) + (data (aref client-message 3))) + (cond ((equal "DndProtocol" message-atom) ; Old KDE 1.x. + (x-dnd-handle-old-kde event frame window message-atom format data)) + + ((equal "_MOTIF_DRAG_AND_DROP_MESSAGE" message-atom) ; Motif + (x-dnd-handle-motif event frame window message-atom format data)) + + ((and (> (length message-atom) 4) ; XDND protocol. + (equal "Xdnd" (substring message-atom 0 4))) + (x-dnd-handle-xdnd event frame window message-atom format data))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -425,8 +448,6 @@ otherwise return the frame coordinates." (declare-function x-get-atom-name "xselect.c" (value &optional frame)) (declare-function x-send-client-message "xselect.c" (display dest from message-type format values)) -(declare-function x-get-selection-internal "xselect.c" - (selection-symbol target-type &optional time-stamp terminal)) (defun x-dnd-version-from-flags (flags) "Return the version byte from the 32 bit FLAGS in an XDndEnter message." diff --git a/src/xterm.c b/src/xterm.c index 6bd43511f8d..deb6d62a27e 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -1396,6 +1396,9 @@ x_dnd_send_enter (struct frame *f, Window target, int supported) int i; XEvent msg; + if (x_top_window_to_frame (dpyinfo, target)) + return; + msg.xclient.type = ClientMessage; msg.xclient.message_type = dpyinfo->Xatom_XdndEnter; msg.xclient.format = 32; @@ -1443,6 +1446,9 @@ 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; @@ -1470,6 +1476,9 @@ x_dnd_send_leave (struct frame *f, Window target) struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); XEvent msg; + if (x_top_window_to_frame (dpyinfo, target)) + return; + msg.xclient.type = ClientMessage; msg.xclient.message_type = dpyinfo->Xatom_XdndLeave; msg.xclient.format = 32; @@ -1491,6 +1500,62 @@ x_dnd_send_drop (struct frame *f, Window target, Time timestamp, { struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); XEvent msg; + struct input_event ie; + struct frame *self_frame; + int root_x, root_y, win_x, win_y, i; + unsigned int mask; + Window root, child; + Lisp_Object lval; + char **atom_names; + char *name; + + self_frame = x_top_window_to_frame (dpyinfo, target); + + if (self_frame) + { + /* Send a special drag-and-drop event when dropping on top of an + Emacs frame to avoid all the overhead involved with sending + client events. */ + EVENT_INIT (ie); + + if (XQueryPointer (dpyinfo->display, FRAME_X_WINDOW (self_frame), + &root, &child, &root_x, &root_y, &win_x, &win_y, + &mask)) + { + ie.kind = DRAG_N_DROP_EVENT; + XSETFRAME (ie.frame_or_window, self_frame); + + lval = Qnil; + atom_names = alloca (x_dnd_n_targets * sizeof *atom_names); + name = XGetAtomName (dpyinfo->display, x_dnd_wanted_action); + + if (!XGetAtomNames (dpyinfo->display, x_dnd_targets, + x_dnd_n_targets, atom_names)) + { + XFree (name); + return; + } + + for (i = x_dnd_n_targets; i != 0; --i) + { + lval = Fcons (intern (atom_names[i - 1]), lval); + XFree (atom_names[i - 1]); + } + + lval = Fcons (intern (name), lval); + lval = Fcons (QXdndSelection, lval); + ie.arg = lval; + ie.timestamp = CurrentTime; + + XSETINT (ie.x, win_x); + XSETINT (ie.y, win_y); + + XFree (name); + kbd_buffer_store_event (&ie); + + return; + } + } msg.xclient.type = ClientMessage; msg.xclient.message_type = dpyinfo->Xatom_XdndDrop; -- 2.39.5