:type 'boolean
:version "26.1")
+(defcustom mouse-drag-and-drop-region-cross-program nil
+ "If non-nil, allow dragging text to other programs."
+ :type 'boolean
+ :version "29.1")
+
(defface mouse-drag-and-drop-region '((t :inherit region))
"Face to highlight original text during dragging.
This face is used by `mouse-drag-and-drop-region' to temporarily
(declare-function rectangle-dimensions "rect" (start end))
(declare-function rectangle-position-as-coordinates "rect" (position))
(declare-function rectangle-intersect-p "rect" (pos1 size1 pos2 size2))
+(declare-function x-begin-drag "xfns.c")
(defun mouse-drag-and-drop-region (event)
"Move text in the region to point where mouse is dragged to.
states))))
(ignore-errors
- (track-mouse
- (setq track-mouse 'dropping)
- ;; When event was "click" instead of "drag", skip loop.
- (while (progn
- (setq event (read-key)) ; read-event or read-key
- (or (mouse-movement-p event)
- ;; Handle `mouse-autoselect-window'.
- (memq (car event) '(select-window switch-frame))))
- ;; Obtain the dragged text in region. When the loop was
- ;; skipped, value-selection remains nil.
- (unless value-selection
- (setq value-selection (funcall region-extract-function nil))
- (when mouse-drag-and-drop-region-show-tooltip
- (let ((text-size mouse-drag-and-drop-region-show-tooltip))
- (setq text-tooltip
- (if (and (integerp text-size)
- (> (length value-selection) text-size))
- (concat
- (substring value-selection 0 (/ text-size 2))
- "\n...\n"
- (substring value-selection (- (/ text-size 2)) -1))
- value-selection))))
-
- ;; Check if selected text is read-only.
- (setq text-from-read-only
- (or text-from-read-only
- (catch 'loop
- (dolist (bound (region-bounds))
- (when (text-property-not-all
- (car bound) (cdr bound) 'read-only nil)
- (throw 'loop t)))))))
-
- (setq window-to-paste (posn-window (event-end event)))
- (setq point-to-paste (posn-point (event-end event)))
- ;; Set nil when target buffer is minibuffer.
- (setq buffer-to-paste (let (buf)
- (when (windowp window-to-paste)
- (setq buf (window-buffer window-to-paste))
- (when (not (minibufferp buf))
- buf))))
- (setq cursor-in-text-area (and window-to-paste
- point-to-paste
- buffer-to-paste))
-
- (when cursor-in-text-area
- ;; Check if point under mouse is read-only.
- (save-window-excursion
- (select-window window-to-paste)
- (setq point-to-paste-read-only
- (or buffer-read-only
- (get-text-property point-to-paste 'read-only))))
-
- ;; Check if "drag but negligible". Operation "drag but
- ;; negligible" is defined as drag-and-drop the text to
- ;; the original region. When modifier is pressed, the
- ;; text will be inserted to inside of the original
- ;; region.
- ;;
- ;; If the region is rectangular, check if the newly inserted
- ;; rectangular text would intersect the already selected
- ;; region. If it would, then set "drag-but-negligible" to t.
- ;; As a special case, allow dragging the region freely anywhere
- ;; to the left, as this will never trigger its contents to be
- ;; inserted into the overlays tracking it.
- (setq drag-but-negligible
- (and (eq (overlay-buffer (car mouse-drag-and-drop-overlays))
- buffer-to-paste)
- (if region-noncontiguous
- (let ((dimensions (rectangle-dimensions start end))
- (start-coordinates
- (rectangle-position-as-coordinates start))
- (point-to-paste-coordinates
- (rectangle-position-as-coordinates
- point-to-paste)))
- (and (rectangle-intersect-p
- start-coordinates dimensions
- point-to-paste-coordinates dimensions)
- (not (< (car point-to-paste-coordinates)
- (car start-coordinates)))))
- (and (<= (overlay-start
- (car mouse-drag-and-drop-overlays))
- point-to-paste)
- (<= point-to-paste
- (overlay-end
- (car mouse-drag-and-drop-overlays))))))))
-
- ;; Show a tooltip.
- (if mouse-drag-and-drop-region-show-tooltip
- (tooltip-show text-tooltip)
- (tooltip-hide))
-
- ;; Show cursor and highlight the original region.
- (when mouse-drag-and-drop-region-show-cursor
- ;; Modify cursor even when point is out of frame.
- (setq cursor-type (cond
- ((not cursor-in-text-area)
- nil)
- ((or point-to-paste-read-only
- drag-but-negligible)
- 'hollow)
- (t
- 'bar)))
+ (catch 'cross-program-drag
+ (track-mouse
+ (setq track-mouse 'dropping)
+ ;; When event was "click" instead of "drag", skip loop.
+ (while (progn
+ (setq event (read-key)) ; read-event or read-key
+ (or (mouse-movement-p event)
+ ;; Handle `mouse-autoselect-window'.
+ (memq (car event) '(select-window switch-frame))))
+ ;; Obtain the dragged text in region. When the loop was
+ ;; skipped, value-selection remains nil.
+ (unless value-selection
+ (setq value-selection (funcall region-extract-function nil))
+ (when mouse-drag-and-drop-region-show-tooltip
+ (let ((text-size mouse-drag-and-drop-region-show-tooltip))
+ (setq text-tooltip
+ (if (and (integerp text-size)
+ (> (length value-selection) text-size))
+ (concat
+ (substring value-selection 0 (/ text-size 2))
+ "\n...\n"
+ (substring value-selection (- (/ text-size 2)) -1))
+ value-selection))))
+
+ ;; Check if selected text is read-only.
+ (setq text-from-read-only
+ (or text-from-read-only
+ (catch 'loop
+ (dolist (bound (region-bounds))
+ (when (text-property-not-all
+ (car bound) (cdr bound) 'read-only nil)
+ (throw 'loop t)))))))
+
+ (when (and mouse-drag-and-drop-region-cross-program
+ (fboundp 'x-begin-drag)
+ (framep (posn-window (event-end event)))
+ (let ((location (posn-x-y (event-end event)))
+ (frame (posn-window (event-end event))))
+ (or (< (car location) 0)
+ (< (cdr location) 0)
+ (> (car location)
+ (frame-pixel-width frame))
+ (> (cdr location)
+ (frame-pixel-height frame)))))
+ (tooltip-hide)
+ (gui-set-selection 'XdndSelection value-selection)
+ (x-begin-drag '("UTF8_STRING" "STRING")
+ 'XdndActionMove (posn-window (event-end event)))
+ (throw 'cross-program-drag nil))
+
+ (setq window-to-paste (posn-window (event-end event)))
+ (setq point-to-paste (posn-point (event-end event)))
+ ;; Set nil when target buffer is minibuffer.
+ (setq buffer-to-paste (let (buf)
+ (when (windowp window-to-paste)
+ (setq buf (window-buffer window-to-paste))
+ (when (not (minibufferp buf))
+ buf))))
+ (setq cursor-in-text-area (and window-to-paste
+ point-to-paste
+ buffer-to-paste))
+
(when cursor-in-text-area
- (dolist (overlay mouse-drag-and-drop-overlays)
- (overlay-put overlay
- 'face 'mouse-drag-and-drop-region))
- (deactivate-mark) ; Maintain region in other window.
- (mouse-set-point event)))))
+ ;; Check if point under mouse is read-only.
+ (save-window-excursion
+ (select-window window-to-paste)
+ (setq point-to-paste-read-only
+ (or buffer-read-only
+ (get-text-property point-to-paste 'read-only))))
+
+ ;; Check if "drag but negligible". Operation "drag but
+ ;; negligible" is defined as drag-and-drop the text to
+ ;; the original region. When modifier is pressed, the
+ ;; text will be inserted to inside of the original
+ ;; region.
+ ;;
+ ;; If the region is rectangular, check if the newly inserted
+ ;; rectangular text would intersect the already selected
+ ;; region. If it would, then set "drag-but-negligible" to t.
+ ;; As a special case, allow dragging the region freely anywhere
+ ;; to the left, as this will never trigger its contents to be
+ ;; inserted into the overlays tracking it.
+ (setq drag-but-negligible
+ (and (eq (overlay-buffer (car mouse-drag-and-drop-overlays))
+ buffer-to-paste)
+ (if region-noncontiguous
+ (let ((dimensions (rectangle-dimensions start end))
+ (start-coordinates
+ (rectangle-position-as-coordinates start))
+ (point-to-paste-coordinates
+ (rectangle-position-as-coordinates
+ point-to-paste)))
+ (and (rectangle-intersect-p
+ start-coordinates dimensions
+ point-to-paste-coordinates dimensions)
+ (not (< (car point-to-paste-coordinates)
+ (car start-coordinates)))))
+ (and (<= (overlay-start
+ (car mouse-drag-and-drop-overlays))
+ point-to-paste)
+ (<= point-to-paste
+ (overlay-end
+ (car mouse-drag-and-drop-overlays))))))))
+
+ ;; Show a tooltip.
+ (if mouse-drag-and-drop-region-show-tooltip
+ (tooltip-show text-tooltip)
+ (tooltip-hide))
+
+ ;; Show cursor and highlight the original region.
+ (when mouse-drag-and-drop-region-show-cursor
+ ;; Modify cursor even when point is out of frame.
+ (setq cursor-type (cond
+ ((not cursor-in-text-area)
+ nil)
+ ((or point-to-paste-read-only
+ drag-but-negligible)
+ 'hollow)
+ (t
+ 'bar)))
+ (when cursor-in-text-area
+ (dolist (overlay mouse-drag-and-drop-overlays)
+ (overlay-put overlay
+ 'face 'mouse-drag-and-drop-region))
+ (deactivate-mark) ; Maintain region in other window.
+ (mouse-set-point event))))))
;; Hide a tooltip.
(when mouse-drag-and-drop-region-show-tooltip (tooltip-hide))
The coordinates X and Y are interpreted in pixels relative to a position
\(0, 0) of the selected frame's display. */)
(Lisp_Object x, Lisp_Object y)
- {
+{
struct frame *f = SELECTED_FRAME ();
if (FRAME_INITIAL_P (f) || !FRAME_X_P (f))
return Qnil;
}
+DEFUN ("x-begin-drag", Fx_begin_drag, Sx_begin_drag, 1, 3, 0,
+ doc: /* Begin dragging contents on FRAME, with targets TARGETS.
+TARGETS is a list of strings, which defines the X selection targets
+that will be available to the drop target. Dragging starts when the
+mouse is pressed on FRAME, and the contents of the selection
+`XdndSelection' will be sent to the X window underneath the mouse
+pointer (the drop target) when the mouse button is released. ACTION
+is a symbol which tells the target what to do, and can be one of the
+following:
+
+ - `XdndActionCopy', which means to copy the contents from the drag
+ source (FRAME) to the drop target.
+
+ - `XdndActionMove', which means to first take the contents of
+ `XdndSelection', and to delete whatever was saved into that
+ selection afterwards.
+
+There are also some other valid values of ACTION that depend on
+details of both the drop target's implementation details and that of
+Emacs. For that reason, they are not mentioned here. Consult
+"Drag-and-Drop Protocol for the X Window System" for more details:
+https://freedesktop.org/wiki/Specifications/XDND/.
+
+If ACTION is not specified or nil, `XdndActionCopy' is used instead.
+
+Block until the mouse buttons are released, then return the action
+chosen by the target, or `nil' if the drop was not accepted by the
+drop target. */)
+ (Lisp_Object targets, Lisp_Object action, Lisp_Object frame)
+{
+ struct frame *f = decode_window_system_frame (frame);
+ int ntargets = 0;
+ char *target_names[2048];
+ Atom *target_atoms;
+ Lisp_Object lval;
+ Atom xaction;
+
+ CHECK_LIST (targets);
+
+ for (; CONSP (targets); targets = XCDR (targets))
+ {
+ CHECK_STRING (XCAR (targets));
+
+ if (ntargets < 2048)
+ {
+ target_names[ntargets] = SSDATA (XCAR (targets));
+ ntargets++;
+ }
+ else
+ error ("Too many targets");
+ }
+
+ if (NILP (action) || EQ (action, QXdndActionCopy))
+ xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionCopy;
+ else if (EQ (action, QXdndActionMove))
+ xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionMove;
+ else if (EQ (action, QXdndActionLink))
+ xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionLink;
+ else if (EQ (action, QXdndActionAsk))
+ xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionAsk;
+ else if (EQ (action, QXdndActionPrivate))
+ xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionPrivate;
+ else
+ signal_error ("Invalid drag-and-drop action", action);
+
+ target_atoms = xmalloc (ntargets * sizeof *target_atoms);
+
+ block_input ();
+ XInternAtoms (FRAME_X_DISPLAY (f), target_names,
+ ntargets, False, target_atoms);
+ unblock_input ();
+
+ x_set_dnd_targets (target_atoms, ntargets);
+ lval = x_dnd_begin_drag_and_drop (f, FRAME_DISPLAY_INFO (f)->last_user_time,
+ xaction);
+
+ return lval;
+}
+
/************************************************************************
X Displays
************************************************************************/
DEFSYM (Qreverse_landscape, "reverse-landscape");
#endif
+ DEFSYM (QXdndActionCopy, "XdndActionCopy");
+ DEFSYM (QXdndActionMove, "XdndActionMove");
+ DEFSYM (QXdndActionLink, "XdndActionLink");
+ DEFSYM (QXdndActionAsk, "XdndActionAsk");
+ DEFSYM (QXdndActionPrivate, "XdndActionPrivate");
+
Fput (Qundefined_color, Qerror_conditions,
pure_list (Qundefined_color, Qerror));
Fput (Qundefined_color, Qerror_message,
defsubr (&Sx_show_tip);
defsubr (&Sx_hide_tip);
defsubr (&Sx_double_buffered_p);
+ defsubr (&Sx_begin_drag);
tip_timer = Qnil;
staticpro (&tip_timer);
tip_frame = Qnil;
static void x_scroll_bar_end_update (struct x_display_info *, struct scroll_bar *);
#endif
+static bool x_dnd_in_progress;
+static Window x_dnd_last_seen_window;
+static int x_dnd_last_protocol_version;
+static Time x_dnd_selection_timestamp;
+
+static Window x_dnd_mouse_rect_target;
+static XRectangle x_dnd_mouse_rect;
+static Atom x_dnd_action;
+static Atom x_dnd_wanted_action;
+
+static Atom *x_dnd_targets = NULL;
+static int x_dnd_n_targets;
+static struct frame *x_dnd_frame;
+
+#define X_DND_SUPPORTED_VERSION 5
+
+static Window
+x_dnd_get_target_window (struct x_display_info *dpyinfo,
+ int root_x, int root_y)
+{
+ Window child_return, child, dummy, proxy;
+ int dest_x_return, dest_y_return;
+ int rc;
+ int actual_format;
+ unsigned long actual_size, bytes_remaining;
+ unsigned char *tmp_data;
+ XWindowAttributes attrs;
+ Atom actual_type;
+
+ child_return = dpyinfo->root_window;
+ dest_x_return = root_x;
+ dest_y_return = root_y;
+
+ /* Not strictly necessary, but satisfies GCC. */
+ child = dpyinfo->root_window;
+
+ while (child_return != None)
+ {
+ child = child_return;
+
+ x_catch_errors (dpyinfo->display);
+ rc = XTranslateCoordinates (dpyinfo->display,
+ child_return, child_return,
+ dest_x_return, dest_y_return,
+ &dest_x_return, &dest_y_return,
+ &child_return);
+
+ if (x_had_errors_p (dpyinfo->display) || !rc)
+ {
+ x_uncatch_errors_after_check ();
+ break;
+ }
+
+ if (child_return)
+ {
+ rc = XTranslateCoordinates (dpyinfo->display,
+ child, child_return,
+ dest_x_return, dest_y_return,
+ &dest_x_return, &dest_y_return,
+ &dummy);
+
+ if (x_had_errors_p (dpyinfo->display) || !rc)
+ {
+ x_uncatch_errors_after_check ();
+ return None;
+ }
+ }
+
+ x_uncatch_errors_after_check ();
+ }
+
+ if (child != None)
+ {
+ x_catch_errors (dpyinfo->display);
+ rc = XGetWindowProperty (dpyinfo->display, child,
+ dpyinfo->Xatom_XdndProxy,
+ 0, 1, False, XA_WINDOW,
+ &actual_type, &actual_format,
+ &actual_size, &bytes_remaining,
+ &tmp_data);
+
+ if (!x_had_errors_p (dpyinfo->display)
+ && rc == Success
+ && actual_type == XA_WINDOW
+ && actual_format == 32
+ && actual_size == 1)
+ {
+ proxy = *(Window *) tmp_data;
+ XFree (tmp_data);
+
+ /* Verify the proxy window exists. */
+ XGetWindowAttributes (dpyinfo->display, proxy, &attrs);
+
+ if (!x_had_errors_p (dpyinfo->display))
+ child = proxy;
+ }
+
+ x_uncatch_errors_after_check ();
+ }
+
+ return child;
+}
+
+static int
+x_dnd_get_window_proto (struct x_display_info *dpyinfo, Window wdesc)
+{
+ Atom actual, value;
+ unsigned char *tmp_data;
+ int rc, format;
+ unsigned long n, left;
+ bool had_errors;
+
+ if (wdesc == None || wdesc == FRAME_X_WINDOW (x_dnd_frame))
+ return -1;
+
+ x_catch_errors (dpyinfo->display);
+ rc = XGetWindowProperty (dpyinfo->display, wdesc, dpyinfo->Xatom_XdndAware,
+ 0, 1, False, XA_ATOM, &actual, &format, &n, &left,
+ &tmp_data);
+ had_errors = x_had_errors_p (dpyinfo->display);
+ x_uncatch_errors_after_check ();
+
+ if (had_errors || rc != Success || actual != XA_ATOM || format != 32 || n < 1)
+ return -1;
+
+ value = (int) *(Atom *) tmp_data;
+ XFree (tmp_data);
+
+ return (int) value;
+}
+
+static void
+x_dnd_send_enter (struct frame *f, Window target, int supported)
+{
+ struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ int i;
+ XEvent msg;
+
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = dpyinfo->Xatom_XdndEnter;
+ msg.xclient.format = 32;
+ msg.xclient.window = target;
+ msg.xclient.data.l[0] = FRAME_X_WINDOW (f);
+ msg.xclient.data.l[1] = (((unsigned int) min (X_DND_SUPPORTED_VERSION,
+ supported) << 24)
+ | (x_dnd_n_targets > 3 ? 1 : 0));
+ msg.xclient.data.l[2] = 0;
+ msg.xclient.data.l[3] = 0;
+ msg.xclient.data.l[4] = 0;
+
+ for (i = 0; i < min (3, x_dnd_n_targets); ++i)
+ msg.xclient.data.l[i + 2] = x_dnd_targets[i];
+
+ if (x_dnd_n_targets > 3)
+ XChangeProperty (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+ dpyinfo->Xatom_XdndTypeList, XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) x_dnd_targets,
+ x_dnd_n_targets);
+
+ XSendEvent (FRAME_X_DISPLAY (f), target, False, 0, &msg);
+}
+
+static void
+x_dnd_send_position (struct frame *f, Window target, int supported,
+ unsigned short root_x, unsigned short root_y,
+ Time timestamp, Atom action)
+{
+ struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ XEvent msg;
+
+ if (target == x_dnd_mouse_rect_target
+ && x_dnd_mouse_rect.width
+ && x_dnd_mouse_rect.height)
+ {
+ if (root_x >= x_dnd_mouse_rect.x
+ && root_x < (x_dnd_mouse_rect.x
+ + x_dnd_mouse_rect.width)
+ && root_y >= x_dnd_mouse_rect.y
+ && root_y < (x_dnd_mouse_rect.y
+ + x_dnd_mouse_rect.height))
+ return;
+ }
+
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = dpyinfo->Xatom_XdndPosition;
+ msg.xclient.format = 32;
+ msg.xclient.window = target;
+ msg.xclient.data.l[0] = FRAME_X_WINDOW (f);
+ msg.xclient.data.l[1] = 0;
+ msg.xclient.data.l[2] = (root_x << 16) | root_y;
+ msg.xclient.data.l[3] = 0;
+ msg.xclient.data.l[4] = 0;
+
+ if (supported >= 3)
+ msg.xclient.data.l[3] = timestamp;
+
+ if (supported >= 4)
+ msg.xclient.data.l[4] = action;
+
+ XSendEvent (FRAME_X_DISPLAY (f), target, False, 0, &msg);
+}
+
+static void
+x_dnd_send_leave (struct frame *f, Window target)
+{
+ struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ XEvent msg;
+
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = dpyinfo->Xatom_XdndLeave;
+ msg.xclient.format = 32;
+ msg.xclient.window = target;
+ msg.xclient.data.l[0] = FRAME_X_WINDOW (f);
+ msg.xclient.data.l[1] = 0;
+ msg.xclient.data.l[2] = 0;
+ msg.xclient.data.l[3] = 0;
+ msg.xclient.data.l[4] = 0;
+
+ XSendEvent (FRAME_X_DISPLAY (f), target, False, 0, &msg);
+}
+
+static void
+x_dnd_send_drop (struct frame *f, Window target, Time timestamp,
+ int supported)
+{
+ struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ XEvent msg;
+
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = dpyinfo->Xatom_XdndDrop;
+ msg.xclient.format = 32;
+ msg.xclient.window = target;
+ msg.xclient.data.l[0] = FRAME_X_WINDOW (f);
+ msg.xclient.data.l[1] = 0;
+ msg.xclient.data.l[2] = 0;
+ msg.xclient.data.l[3] = 0;
+ msg.xclient.data.l[4] = 0;
+
+ if (supported >= 1)
+ msg.xclient.data.l[2] = timestamp;
+
+ XSendEvent (FRAME_X_DISPLAY (f), target, False, 0, &msg);
+}
+
+void
+x_set_dnd_targets (Atom *targets, int ntargets)
+{
+ if (x_dnd_targets)
+ xfree (x_dnd_targets);
+
+ x_dnd_targets = targets;
+ x_dnd_n_targets = ntargets;
+}
+
+Lisp_Object
+x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction)
+{
+ XEvent next_event;
+ struct input_event hold_quit;
+ int finish;
+ char *atom_name;
+ Lisp_Object action, ltimestamp;
+
+ if (x_dnd_in_progress)
+ error ("A drag-and-drop session is already in progress");
+
+ ltimestamp = x_timestamp_for_selection (FRAME_DISPLAY_INFO (f),
+ QXdndSelection);
+
+ if (NILP (ltimestamp))
+ error ("No local value for XdndSelection");
+
+ if (BIGNUMP (ltimestamp))
+ x_dnd_selection_timestamp = bignum_to_intmax (ltimestamp);
+ else
+ x_dnd_selection_timestamp = XFIXNUM (ltimestamp);
+
+ x_dnd_in_progress = true;
+ x_dnd_frame = f;
+ x_dnd_last_seen_window = FRAME_X_WINDOW (f);
+ x_dnd_last_protocol_version = -1;
+ x_dnd_mouse_rect_target = None;
+ x_dnd_action = None;
+ x_dnd_wanted_action = xaction;
+
+ while (x_dnd_in_progress)
+ {
+ hold_quit.kind = NO_EVENT;
+
+ block_input ();
+ XNextEvent (FRAME_X_DISPLAY (f), &next_event);
+
+ handle_one_xevent (FRAME_DISPLAY_INFO (f),
+ &next_event, &finish, &hold_quit);
+ unblock_input ();
+
+ if (hold_quit.kind != NO_EVENT)
+ {
+ if (x_dnd_in_progress)
+ {
+ 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);
+ unblock_input ();
+
+ x_dnd_in_progress = false;
+ x_dnd_frame = NULL;
+ }
+
+ FRAME_DISPLAY_INFO (f)->grabbed = 0;
+ quit ();
+ }
+ }
+
+ FRAME_DISPLAY_INFO (f)->grabbed = 0;
+
+ if (x_dnd_wanted_action != None)
+ {
+ block_input ();
+ atom_name = XGetAtomName (FRAME_X_DISPLAY (f),
+ x_dnd_wanted_action);
+ action = intern (atom_name);
+ XFree (atom_name);
+ unblock_input ();
+
+ return action;
+ }
+
+ return Qnil;
+}
+
/* Flush display of frame F. */
static void
{
case ClientMessage:
{
+ if (x_dnd_in_progress
+ && FRAME_DISPLAY_INFO (x_dnd_frame) == dpyinfo
+ && event->xclient.message_type == dpyinfo->Xatom_XdndStatus)
+ {
+ Window target;
+
+ target = event->xclient.data.l[0];
+
+ if (x_dnd_last_protocol_version != -1
+ && target == x_dnd_last_seen_window
+ && event->xclient.data.l[1] & 2)
+ {
+ x_dnd_mouse_rect_target = target;
+ x_dnd_mouse_rect.x = (event->xclient.data.l[2] & 0xffff0000) >> 16;
+ x_dnd_mouse_rect.y = (event->xclient.data.l[2] & 0xffff);
+ x_dnd_mouse_rect.width = (event->xclient.data.l[3] & 0xffff0000) >> 16;
+ x_dnd_mouse_rect.height = (event->xclient.data.l[3] & 0xffff);
+ }
+ else
+ x_dnd_mouse_rect_target = None;
+
+ if (x_dnd_last_protocol_version != -1
+ && target == x_dnd_last_seen_window)
+ {
+ if (event->xclient.data.l[1] & 1)
+ {
+ if (x_dnd_last_protocol_version >= 2)
+ x_dnd_wanted_action = event->xclient.data.l[4];
+ else
+ x_dnd_wanted_action = dpyinfo->Xatom_XdndActionCopy;
+ }
+ else
+ x_dnd_wanted_action = None;
+ }
+ }
+
if (event->xclient.message_type == dpyinfo->Xatom_wm_protocols
&& event->xclient.format == 32)
{
clear_mouse_face (hlinfo);
}
+ if (x_dnd_in_progress
+ && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame))
+ {
+ Window target;
+
+ target = x_dnd_get_target_window (dpyinfo,
+ event->xmotion.x_root,
+ event->xmotion.y_root);
+
+ if (target != x_dnd_last_seen_window)
+ {
+ if (x_dnd_last_seen_window != None
+ && x_dnd_last_protocol_version != -1
+ && x_dnd_last_seen_window != FRAME_X_WINDOW (x_dnd_frame))
+ x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
+
+ x_dnd_wanted_action = None;
+ x_dnd_last_seen_window = target;
+ x_dnd_last_protocol_version
+ = x_dnd_get_window_proto (dpyinfo, target);
+
+ if (target != None && x_dnd_last_protocol_version != -1)
+ x_dnd_send_enter (x_dnd_frame, target,
+ x_dnd_last_protocol_version);
+ }
+
+ if (x_dnd_last_protocol_version != -1 && target != None)
+ x_dnd_send_position (x_dnd_frame, target,
+ x_dnd_last_protocol_version,
+ event->xmotion.x_root,
+ event->xmotion.y_root,
+ x_dnd_selection_timestamp,
+ dpyinfo->Xatom_XdndActionCopy);
+
+ goto OTHER;
+ }
+
f = mouse_or_wdesc_frame (dpyinfo, event->xmotion.window);
#ifdef USE_GTK
Lisp_Object tab_bar_arg = Qnil;
bool tab_bar_p = false;
bool tool_bar_p = false;
+ bool dnd_grab = false;
+
+ for (int i = 1; i < 8; ++i)
+ {
+ if (i != event->xbutton.button
+ && event->xbutton.state & (Button1Mask << (i - 1)))
+ dnd_grab = true;
+ }
+
+ if (x_dnd_in_progress
+ && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame)
+ && !dnd_grab
+ && event->xbutton.type == ButtonRelease)
+ {
+ x_dnd_in_progress = false;
+
+ if (x_dnd_last_seen_window != None
+ && x_dnd_last_protocol_version != -1)
+ x_dnd_send_drop (x_dnd_frame, x_dnd_last_seen_window,
+ x_dnd_selection_timestamp,
+ x_dnd_last_protocol_version);
+
+ x_dnd_last_protocol_version = -1;
+ x_dnd_last_seen_window = None;
+ x_dnd_frame = NULL;
+ x_set_dnd_targets (NULL, 0);
+
+ goto OTHER;
+ }
+
+ if (x_dnd_in_progress)
+ goto OTHER;
memset (&compose_status, 0, sizeof (compose_status));
dpyinfo->last_mouse_glyph_frame = NULL;
clear_mouse_face (hlinfo);
}
+ if (x_dnd_in_progress
+ && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame))
+ {
+ Window target;
+
+ target = x_dnd_get_target_window (dpyinfo,
+ xev->root_x,
+ xev->root_y);
+
+ if (target != x_dnd_last_seen_window)
+ {
+ if (x_dnd_last_seen_window != None
+ && x_dnd_last_protocol_version != -1
+ && x_dnd_last_seen_window != FRAME_X_WINDOW (x_dnd_frame))
+ x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
+
+ x_dnd_last_seen_window = target;
+ x_dnd_last_protocol_version
+ = x_dnd_get_window_proto (dpyinfo, target);
+
+ if (target != None && x_dnd_last_protocol_version != -1)
+ x_dnd_send_enter (x_dnd_frame, target,
+ x_dnd_last_protocol_version);
+ }
+
+ if (x_dnd_last_protocol_version != -1 && target != None)
+ x_dnd_send_position (x_dnd_frame, target,
+ x_dnd_last_protocol_version,
+ xev->root_x, xev->root_y,
+ x_dnd_selection_timestamp,
+ dpyinfo->Xatom_XdndActionCopy);
+
+ goto XI_OTHER;
+ }
+
f = mouse_or_wdesc_frame (dpyinfo, xev->event);
#ifdef USE_GTK
#endif
/* A fake XButtonEvent for x_construct_mouse_click. */
XButtonEvent bv;
+ bool dnd_grab = false;
+
+ for (int i = 0; i < xev->buttons.mask_len * 8; ++i)
+ {
+ if (i != xev->detail && XIMaskIsSet (xev->buttons.mask, i))
+ dnd_grab = true;
+ }
+
+ if (x_dnd_in_progress
+ && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame)
+ && !dnd_grab
+ && xev->evtype == XI_ButtonRelease)
+ {
+ x_dnd_in_progress = false;
+
+ if (x_dnd_last_seen_window != None
+ && x_dnd_last_protocol_version != -1)
+ x_dnd_send_drop (x_dnd_frame, x_dnd_last_seen_window,
+ x_dnd_selection_timestamp,
+ x_dnd_last_protocol_version);
+
+ x_dnd_last_protocol_version = -1;
+ x_dnd_last_seen_window = None;
+ x_dnd_frame = NULL;
+ x_set_dnd_targets (NULL, 0);
+
+ goto XI_OTHER;
+ }
+
+ if (x_dnd_in_progress)
+ goto XI_OTHER;
#ifdef USE_MOTIF
#ifdef USE_TOOLKIT_SCROLL_BARS
struct scroll_bar *b;
#endif
+ if (x_dnd_in_progress && f == x_dnd_frame)
+ {
+ if (x_dnd_last_seen_window != None
+ && x_dnd_last_protocol_version != -1)
+ x_dnd_send_leave (f, x_dnd_last_seen_window);
+
+ x_dnd_in_progress = false;
+ x_dnd_frame = NULL;
+ }
+
block_input ();
/* If a display connection is dead, don't try sending more
ATOM_REFS_INIT ("ShiftLock", Xatom_ShiftLock)
ATOM_REFS_INIT ("Alt", Xatom_Alt)
#endif
+ /* DND source. */
+ ATOM_REFS_INIT ("XdndAware", Xatom_XdndAware)
+ ATOM_REFS_INIT ("XdndSelection", Xatom_XdndSelection)
+ ATOM_REFS_INIT ("XdndTypeList", Xatom_XdndTypeList)
+ ATOM_REFS_INIT ("XdndActionCopy", Xatom_XdndActionCopy)
+ ATOM_REFS_INIT ("XdndActionMove", Xatom_XdndActionMove)
+ ATOM_REFS_INIT ("XdndActionLink", Xatom_XdndActionLink)
+ ATOM_REFS_INIT ("XdndActionAsk", Xatom_XdndActionAsk)
+ ATOM_REFS_INIT ("XdndActionPrivate", Xatom_XdndActionPrivate)
+ ATOM_REFS_INIT ("XdndActionList", Xatom_XdndActionList)
+ ATOM_REFS_INIT ("XdndActionDescription", Xatom_XdndActionDescription)
+ ATOM_REFS_INIT ("XdndProxy", Xatom_XdndProxy)
+ ATOM_REFS_INIT ("XdndEnter", Xatom_XdndEnter)
+ ATOM_REFS_INIT ("XdndPosition", Xatom_XdndPosition)
+ ATOM_REFS_INIT ("XdndStatus", Xatom_XdndStatus)
+ ATOM_REFS_INIT ("XdndLeave", Xatom_XdndLeave)
+ ATOM_REFS_INIT ("XdndDrop", Xatom_XdndDrop)
+ ATOM_REFS_INIT ("XdndFinished", Xatom_XdndFinished)
};
int i;
Fput (Qmeta, Qmodifier_value, make_fixnum (meta_modifier));
DEFSYM (Qsuper, "super");
Fput (Qsuper, Qmodifier_value, make_fixnum (super_modifier));
+ DEFSYM (QXdndSelection, "XdndSelection");
DEFVAR_LISP ("x-ctrl-keysym", Vx_ctrl_keysym,
doc: /* Which keys Emacs uses for the ctrl modifier.