From eb6d74a26c64e6aa444f21b12a7ab9951a00bfa5 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 2 Jul 2022 09:06:36 +0800 Subject: [PATCH] Return the correct action from the Lisp side of drag-and-drop * lisp/x-dnd.el (x-dnd-handle-drag-n-drop-event): Select `window' when handling internal DND events. (x-dnd-handle-unsupported-drop): Return an appropriate action. * src/keyboard.c (kbd_buffer_get_event): * src/termhooks.h (enum event_kind): Delete `UNSUPPORTED_DROP_EVENT'. * src/xterm.c (x_dnd_send_unsupported_drop): Set flags instead of recording input event. (x_clear_dnd_monitors): Rename to `x_clear_dnd_variables'. Also clear unsupported drop data. (x_dnd_begin_drag_and_drop): Run unsupported drop function inline (and safely), and use its return value if it returned a symbol. (syms_of_xterm): Update doc string of `x-dnd-unsupported-drop-function'. * src/xterm.h: Update declarations. --- lisp/x-dnd.el | 34 +++++---- src/keyboard.c | 50 -------------- src/termhooks.h | 24 ------- src/xterm.c | 180 +++++++++++++++++++++++------------------------- src/xterm.h | 1 - 5 files changed, 106 insertions(+), 183 deletions(-) diff --git a/lisp/x-dnd.el b/lisp/x-dnd.el index d78f926ee7a..8a7f3e24363 100644 --- a/lisp/x-dnd.el +++ b/lisp/x-dnd.el @@ -422,6 +422,8 @@ Currently XDND, Motif and old KDE 1.x protocols are recognized." x-dnd-xdnd-to-action))) (targets (cddr client-message)) (local-value (nth 2 client-message))) + (when (windowp window) + (select-window window)) (x-dnd-save-state window nil nil (apply #'vector targets)) (x-dnd-maybe-call-test-function window action) @@ -1154,19 +1156,25 @@ X and Y are the root window coordinates of the drop. FRAME is the frame the drop originated on. WINDOW-ID is the X window the drop should happen to. LOCAL-SELECTION-DATA is the local selection data of the drop." - (not (and (or (eq action 'XdndActionCopy) - (eq action 'XdndActionMove)) - (not (and x-dnd-use-offix-drop local-selection-data - (or (not (eq x-dnd-use-offix-drop 'files)) - (member "FILE_NAME" targets)) - (x-dnd-do-offix-drop targets x - y frame window-id - local-selection-data))) - (or - (member "STRING" targets) - (member "UTF8_STRING" targets) - (member "COMPOUND_TEXT" targets) - (member "TEXT" targets))))) + (let ((chosen-action nil)) + (not (and (or (eq action 'XdndActionCopy) + (eq action 'XdndActionMove)) + (not (and x-dnd-use-offix-drop local-selection-data + (or (not (eq x-dnd-use-offix-drop 'files)) + (member "FILE_NAME" targets)) + (when (x-dnd-do-offix-drop targets x + y frame window-id + local-selection-data) + (setq chosen-action 'XdndActionCopy)))) + (let ((delegate-p (or (member "STRING" targets) + (member "UTF8_STRING" targets) + (member "COMPOUND_TEXT" targets) + (member "TEXT" targets)))) + (prog1 delegate-p + ;; A string will avoid the drop emulation done in C + ;; code, but won't be returned from `x-begin-drag'. + (setq chosen-action (unless delegate-p "")))))) + chosen-action)) (defvar x-dnd-targets-list) (defvar x-dnd-native-test-function) diff --git a/src/keyboard.c b/src/keyboard.c index 4cac20eb4b7..bed8307b6f2 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4036,56 +4036,6 @@ kbd_buffer_get_event (KBOARD **kbp, } break; -#ifdef HAVE_X_WINDOWS - case UNSUPPORTED_DROP_EVENT: - { - struct frame *f; - - kbd_fetch_ptr = next_kbd_event (event); - input_pending = readable_events (0); - - /* This means this event was already handled in - `x_dnd_begin_drag_and_drop'. */ - if (event->ie.modifiers < x_dnd_unsupported_event_level) - break; - - f = XFRAME (event->ie.frame_or_window); - - if (!FRAME_LIVE_P (f)) - break; - - if (!NILP (Vx_dnd_unsupported_drop_function)) - { - if (!NILP (call8 (Vx_dnd_unsupported_drop_function, - XCAR (XCDR (event->ie.arg)), event->ie.x, - event->ie.y, XCAR (XCDR (XCDR (event->ie.arg))), - make_uint (event->ie.code), - event->ie.frame_or_window, - make_int (event->ie.timestamp), - Fcopy_sequence (XCAR (event->ie.arg))))) - break; - } - - /* `x-dnd-unsupported-drop-function' could have deleted the - event frame. */ - if (!FRAME_LIVE_P (f) - /* This means `x-dnd-use-unsupported-drop' was nil when the - event was generated. */ - || NILP (XCAR (XCDR (XCDR (XCDR (event->ie.arg)))))) - break; - - x_dnd_do_unsupported_drop (FRAME_DISPLAY_INFO (f), - event->ie.frame_or_window, - XCAR (event->ie.arg), - XCAR (XCDR (event->ie.arg)), - (Window) event->ie.code, - XFIXNUM (event->ie.x), - XFIXNUM (event->ie.y), - event->ie.timestamp); - break; - } -#endif - case MONITORS_CHANGED_EVENT: { kbd_fetch_ptr = next_kbd_event (event); diff --git a/src/termhooks.h b/src/termhooks.h index a1e3e2cde9a..c5f1e286e92 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -209,30 +209,6 @@ enum event_kind representation of the dropped items. .timestamp gives a timestamp (in milliseconds) for the click. */ -#ifdef HAVE_X_WINDOWS - UNSUPPORTED_DROP_EVENT, /* Event sent when the regular C - drag-and-drop machinery could not - handle a drop to a window. - - .code is the XID of the window that - could not be dropped to. - - .arg is a list of the local value of - XdndSelection, a list of selection - targets, and the intended action to - be taken upon drop, and .timestamp - gives the timestamp where the drop - happened. - - .modifiers gives a number that - determines if an event was already - handled by - `x_dnd_begin_drag_and_drop'. - - .x and .y give the coordinates of - the drop originating from the root - window. */ -#endif USER_SIGNAL_EVENT, /* A user signal. code is a number identifying it, index into lispy_user_signals. */ diff --git a/src/xterm.c b/src/xterm.c index c83ddc6b9ea..061bca0684b 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -1128,10 +1128,6 @@ static Window x_get_window_below (Display *, Window, int, int, int *, int *); /* Flag that indicates if a drag-and-drop operation is in progress. */ bool x_dnd_in_progress; -/* Number that indicates the last "generation" of - UNSUPPORTED_DROP_EVENTs handled. */ -unsigned x_dnd_unsupported_event_level; - /* The frame where the drag-and-drop operation originated. */ struct frame *x_dnd_frame; @@ -1146,6 +1142,20 @@ struct frame *x_dnd_finish_frame; important information. */ bool x_dnd_waiting_for_finish; +/* Flag that means (when set in addition to + `x_dnd_waiting_for_finish') to run the unsupported drop function + with the given arguments. */ +static bool x_dnd_run_unsupported_drop_function; + +/* The "before"-time of the unsupported drop. */ +static Time x_dnd_unsupported_drop_time; + +/* The target window of the unsupported drop. */ +static Window x_dnd_unsupported_drop_window; + +/* The Lisp data associated with the unsupported drop function. */ +static Lisp_Object x_dnd_unsupported_drop_data; + /* Whether or not to move the tooltip along with the mouse pointer during drag-and-drop. */ static bool x_dnd_update_tooltip; @@ -3881,12 +3891,10 @@ static void x_dnd_send_unsupported_drop (struct x_display_info *dpyinfo, Window target_window, int root_x, int root_y, Time before) { - struct input_event ie; Lisp_Object targets, arg; int i; char **atom_names, *name; - EVENT_INIT (ie); targets = Qnil; atom_names = alloca (sizeof *atom_names * x_dnd_n_targets); @@ -3894,8 +3902,6 @@ x_dnd_send_unsupported_drop (struct x_display_info *dpyinfo, Window target_windo x_dnd_n_targets, atom_names)) return; - x_dnd_action = dpyinfo->Xatom_XdndActionPrivate; - for (i = x_dnd_n_targets; i > 0; --i) { targets = Fcons (build_string (atom_names[i - 1]), @@ -3914,20 +3920,17 @@ x_dnd_send_unsupported_drop (struct x_display_info *dpyinfo, Window target_windo else arg = Qnil; - ie.kind = UNSUPPORTED_DROP_EVENT; - ie.code = (unsigned) target_window; - ie.modifiers = x_dnd_unsupported_event_level; - ie.arg = list4 (assq_no_quit (QXdndSelection, - dpyinfo->terminal->Vselection_alist), - targets, arg, (x_dnd_use_unsupported_drop - ? Qt : Qnil)); - ie.timestamp = before; - - XSETINT (ie.x, root_x); - XSETINT (ie.y, root_y); - XSETFRAME (ie.frame_or_window, x_dnd_frame); + x_dnd_run_unsupported_drop_function = true; + x_dnd_unsupported_drop_time = before; + x_dnd_unsupported_drop_window = target_window; + x_dnd_unsupported_drop_data + = listn (5, assq_no_quit (QXdndSelection, + dpyinfo->terminal->Vselection_alist), + targets, arg, make_fixnum (root_x), + make_fixnum (root_y)); - kbd_buffer_store_event (&ie); + x_dnd_waiting_for_finish = true; + x_dnd_finish_display = dpyinfo->display; } static Window @@ -4529,10 +4532,14 @@ x_free_dnd_targets (void) x_dnd_n_targets = 0; } +/* Clear some Lisp variables after the drop finishes, so they are + freed by the GC. */ + static void -x_clear_dnd_monitors (void) +x_clear_dnd_variables (void) { x_dnd_monitors = Qnil; + x_dnd_unsupported_drop_data = Qnil; } static void @@ -11311,7 +11318,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, XWindowAttributes root_window_attrs; struct input_event hold_quit; char *atom_name, *ask_actions; - Lisp_Object action, ltimestamp; + Lisp_Object action, ltimestamp, val; specpdl_ref ref, count, base; ptrdiff_t i, end, fill; XTextProperty prop; @@ -11324,9 +11331,6 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, #ifndef USE_GTK struct x_display_info *event_display; #endif - union buffered_input_event *events, *event; - int n_events; - struct frame *event_frame; base = SPECPDL_INDEX (); @@ -11334,70 +11338,6 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, Fx_begin_drag. */ specbind (Qx_dnd_targets_list, selection_target_list); - /* Before starting drag-and-drop, walk through the keyboard buffer - to see if there are any UNSUPPORTED_DROP_EVENTs, and run them now - if they exist, to prevent race conditions from happening due to - multiple unsupported drops running at once. */ - - block_input (); - events = alloca (sizeof *events * KBD_BUFFER_SIZE); - n_events = 0; - event = kbd_fetch_ptr; - - while (event != kbd_store_ptr) - { - if (event->ie.kind == UNSUPPORTED_DROP_EVENT - && event->ie.modifiers < x_dnd_unsupported_event_level) - events[n_events++] = *event; - - event = (event == kbd_buffer + KBD_BUFFER_SIZE - 1 - ? kbd_buffer : event + 1); - } - - x_dnd_unsupported_event_level += 1; - unblock_input (); - - for (i = 0; i < n_events; ++i) - { - maybe_quit (); - - event = &events[i]; - event_frame = XFRAME (event->ie.frame_or_window); - - if (!FRAME_LIVE_P (event_frame)) - continue; - - if (!NILP (Vx_dnd_unsupported_drop_function)) - { - if (!NILP (call8 (Vx_dnd_unsupported_drop_function, - XCAR (XCDR (event->ie.arg)), event->ie.x, - event->ie.y, XCAR (XCDR (XCDR (event->ie.arg))), - make_uint (event->ie.code), - event->ie.frame_or_window, - make_int (event->ie.timestamp), - Fcopy_sequence (XCAR (event->ie.arg))))) - continue; - } - - /* `x-dnd-unsupported-drop-function' could have deleted the - event frame. */ - if (!FRAME_LIVE_P (event_frame) - /* This means `x-dnd-use-unsupported-drop' was nil when the - event was generated. */ - || NILP (XCAR (XCDR (XCDR (XCDR (event->ie.arg)))))) - continue; - - x_dnd_do_unsupported_drop (FRAME_DISPLAY_INFO (event_frame), - event->ie.frame_or_window, - XCAR (event->ie.arg), - XCAR (XCDR (event->ie.arg)), - (Window) event->ie.code, - XFIXNUM (event->ie.x), - XFIXNUM (event->ie.y), - event->ie.timestamp); - break; - } - if (!FRAME_VISIBLE_P (f)) error ("Frame must be visible"); @@ -11500,6 +11440,8 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, unbind_to (count, Qnil); } + record_unwind_protect_void (x_clear_dnd_variables); + if (follow_tooltip) { #if defined HAVE_XRANDR || defined USE_GTK @@ -11510,8 +11452,6 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, #endif x_dnd_monitors = Fx_display_monitor_attributes_list (frame); - - record_unwind_protect_void (x_clear_dnd_monitors); } x_dnd_update_tooltip = follow_tooltip; @@ -11558,6 +11498,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, x_dnd_xm_use_help = false; x_dnd_motif_setup_p = false; x_dnd_end_window = None; + x_dnd_run_unsupported_drop_function = false; x_dnd_use_toplevels = x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_client_list_stacking); x_dnd_toplevels = NULL; @@ -11812,6 +11753,50 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, unbind_to (ref, Qnil); } + if (x_dnd_run_unsupported_drop_function + && x_dnd_waiting_for_finish) + { + x_dnd_run_unsupported_drop_function = false; + x_dnd_waiting_for_finish = false; + x_dnd_unwind_flag = true; + + ref = SPECPDL_INDEX (); + record_unwind_protect_ptr (x_dnd_cleanup_drag_and_drop, f); + + if (!NILP (Vx_dnd_unsupported_drop_function)) + val = call8 (Vx_dnd_unsupported_drop_function, + XCAR (XCDR (x_dnd_unsupported_drop_data)), + Fnth (make_fixnum (3), x_dnd_unsupported_drop_data), + Fnth (make_fixnum (4), x_dnd_unsupported_drop_data), + Fnth (make_fixnum (2), x_dnd_unsupported_drop_data), + make_uint (x_dnd_unsupported_drop_window), + frame, make_uint (x_dnd_unsupported_drop_time), + Fcopy_sequence (XCAR (x_dnd_unsupported_drop_data))); + else + val = Qnil; + + if (NILP (val)) + x_dnd_do_unsupported_drop (FRAME_DISPLAY_INFO (f), + frame, XCAR (x_dnd_unsupported_drop_data), + XCAR (XCDR (x_dnd_unsupported_drop_data)), + x_dnd_unsupported_drop_window, + XFIXNUM (Fnth (make_fixnum (3), + x_dnd_unsupported_drop_data)), + XFIXNUM (Fnth (make_fixnum (4), + x_dnd_unsupported_drop_data)), + x_dnd_unsupported_drop_time); + + if (SYMBOLP (val)) + x_dnd_action_symbol = val; + + x_dnd_unwind_flag = false; + unbind_to (ref, Qnil); + + /* Break out of the loop now, since DND has + completed. */ + break; + } + #ifdef USE_GTK if (xg_pending_quit_event.kind != NO_EVENT) { @@ -27809,6 +27794,9 @@ syms_of_xterm (void) x_dnd_selection_alias_cell = Fcons (Qnil, Qnil); staticpro (&x_dnd_selection_alias_cell); + x_dnd_unsupported_drop_data = Qnil; + staticpro (&x_dnd_unsupported_drop_data); + DEFSYM (Qvendor_specific_keysyms, "vendor-specific-keysyms"); DEFSYM (Qlatin_1, "latin-1"); DEFSYM (Qnow, "now"); @@ -28033,7 +28021,6 @@ mouse position list. */); DEFVAR_LISP ("x-dnd-unsupported-drop-function", Vx_dnd_unsupported_drop_function, doc: /* Function called when trying to drop on an unsupported window. - This function is called whenever the user tries to drop something on a window that does not support either the XDND or Motif protocols for drag-and-drop. It should return a non-nil value if the drop was @@ -28046,8 +28033,11 @@ where the drop happened, ACTION is the action that was passed to `x-begin-drag', FRAME is the frame which initiated the drag-and-drop operation, TIME is the X server time when the drop happened, and LOCAL-SELECTION is the contents of the `XdndSelection' when -`x-begin-drag' was run, which can be passed to -`x-get-local-selection'. */); +`x-begin-drag' was run; its contents can be retrieved by calling the +function `x-get-local-selection'. + +If a symbol is returned, then it will be used as the return value of +`x-begin-drag'. */); Vx_dnd_unsupported_drop_function = Qnil; DEFVAR_INT ("x-color-cache-bucket-size", x_color_cache_bucket_size, diff --git a/src/xterm.h b/src/xterm.h index 76d35aaf34f..eee76724268 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -1669,7 +1669,6 @@ extern bool x_dnd_in_progress; extern bool x_dnd_waiting_for_finish; extern struct frame *x_dnd_frame; extern struct frame *x_dnd_finish_frame; -extern unsigned x_dnd_unsupported_event_level; extern int x_error_message_count; #ifdef HAVE_XINPUT2 -- 2.39.2