aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPo Lu2022-06-29 10:24:14 +0800
committerPo Lu2022-06-29 10:24:14 +0800
commit0e6516a1f022e18f4e32848331954deb0e850d4e (patch)
treeaaf82c350a8aee4ea7867587af7a6555ca6804af
parent9705609c0ef5e426606300da95fed4bec54923fb (diff)
downloademacs-0e6516a1f022e18f4e32848331954deb0e850d4e.tar.gz
emacs-0e6516a1f022e18f4e32848331954deb0e850d4e.zip
Fix reported problem with drag-and-drop inside VirtualBox
* lisp/x-dnd.el (x-dnd-handle-old-kde, x-dnd-handle-offix) (x-dnd-handle-motif): Select window before handling drop, like on Xdnd. (x-dnd-convert-to-offix, x-dnd-do-offix-drop) (x-dnd-handle-unsupported-drop): Accept local selection data and use that instead. * src/keyboard.c (kbd_buffer_get_event): Call unsupported drop function with local selection data as 8th arg. * src/xselect.c (x_get_local_selection): Accept new arg `local_value'. All callers changed. (Fx_get_local_selection): New function. (syms_of_xselect): Update defsubrs. * src/xterm.c (x_dnd_lose_ownership): New function. (x_dnd_begin_drag_and_drop): Unless new variable is true, disown XdndSelection after returning. This supposedly makes drag-and-drop from guest to host work in VirtualBox without causing pointer motion to become choppy afterwards. (syms_of_xterm): New variable `x_dnd_preserve_selection_data' and update doc string of `x-dnd-unsupported-drop-function'. * test/lisp/dnd-tests.el (dnd-tests-begin-text-drag) (dnd-tests-begin-file-drag, dnd-tests-begin-drag-files): Set new variable to nil during tests.
-rw-r--r--lisp/x-dnd.el41
-rw-r--r--src/keyboard.c5
-rw-r--r--src/xselect.c74
-rw-r--r--src/xterm.c51
-rw-r--r--test/lisp/dnd-tests.el10
5 files changed, 142 insertions, 39 deletions
diff --git a/lisp/x-dnd.el b/lisp/x-dnd.el
index 22277033f52..5c6d25ba686 100644
--- a/lisp/x-dnd.el
+++ b/lisp/x-dnd.el
@@ -443,6 +443,8 @@ EVENT, FRAME, WINDOW and DATA mean the same thing they do in
443 ;; Now call the test function to decide what action to perform. 443 ;; Now call the test function to decide what action to perform.
444 (x-dnd-maybe-call-test-function window 'private) 444 (x-dnd-maybe-call-test-function window 'private)
445 (unwind-protect 445 (unwind-protect
446 (when (windowp window)
447 (select-window window))
446 (x-dnd-drop-data event frame window data 448 (x-dnd-drop-data event frame window data
447 (symbol-name type)) 449 (symbol-name type))
448 (x-dnd-forget-drop window)))))) 450 (x-dnd-forget-drop window))))))
@@ -500,6 +502,8 @@ message (format 32) that caused EVENT to be generated."
500 ;; Now call the test function to decide what action to perform. 502 ;; Now call the test function to decide what action to perform.
501 (x-dnd-maybe-call-test-function window 'private) 503 (x-dnd-maybe-call-test-function window 'private)
502 (unwind-protect 504 (unwind-protect
505 (when (windowp window)
506 (select-window window))
503 (x-dnd-drop-data event frame window data 507 (x-dnd-drop-data event frame window data
504 (symbol-name type)) 508 (symbol-name type))
505 (x-dnd-forget-drop window)))) 509 (x-dnd-forget-drop window))))
@@ -926,6 +930,8 @@ Return a vector of atoms containing the selection targets."
926 reply))) 930 reply)))
927 931
928 ((eq message-type 'XmDROP_START) 932 ((eq message-type 'XmDROP_START)
933 (when (windowp window)
934 (select-window window))
929 (let* ((x (x-dnd-motif-value-to-list 935 (let* ((x (x-dnd-motif-value-to-list
930 (x-dnd-get-motif-value data 8 2 source-byteorder) 936 (x-dnd-get-motif-value data 8 2 source-byteorder)
931 2 my-byteorder)) 937 2 my-byteorder))
@@ -1014,19 +1020,22 @@ Return a vector of atoms containing the selection targets."
1014;;; Handling drops. 1020;;; Handling drops.
1015 1021
1016(defvar x-treat-local-requests-remotely) 1022(defvar x-treat-local-requests-remotely)
1023(declare-function x-get-local-selection "xfns.c")
1017 1024
1018(defun x-dnd-convert-to-offix (targets) 1025(defun x-dnd-convert-to-offix (targets local-selection)
1019 "Convert the contents of `XdndSelection' to OffiX data. 1026 "Convert local selection data to OffiX data.
1020TARGETS should be the list of targets currently available in 1027TARGETS should be the list of targets currently available in
1021`XdndSelection'. Return a list of an OffiX type, and data 1028`XdndSelection'. Return a list of an OffiX type, and data
1022suitable for passing to `x-change-window-property', or nil if the 1029suitable for passing to `x-change-window-property', or nil if the
1023data could not be converted." 1030data could not be converted.
1031LOCAL-SELECTION should be the local selection data describing the
1032selection data to convert."
1024 (let ((x-treat-local-requests-remotely t) 1033 (let ((x-treat-local-requests-remotely t)
1025 file-name-data string-data) 1034 file-name-data string-data)
1026 (cond 1035 (cond
1027 ((and (member "FILE_NAME" targets) 1036 ((and (member "FILE_NAME" targets)
1028 (setq file-name-data 1037 (setq file-name-data
1029 (gui-get-selection 'XdndSelection 'FILE_NAME))) 1038 (x-get-local-selection local-selection 'FILE_NAME)))
1030 (if (string-match-p "\0" file-name-data) 1039 (if (string-match-p "\0" file-name-data)
1031 ;; This means there are multiple file names in 1040 ;; This means there are multiple file names in
1032 ;; XdndSelection. Convert the file name data to a format 1041 ;; XdndSelection. Convert the file name data to a format
@@ -1035,19 +1044,23 @@ data could not be converted."
1035 (cons 'DndTypeFile (concat file-name-data "\0")))) 1044 (cons 'DndTypeFile (concat file-name-data "\0"))))
1036 ((and (member "STRING" targets) 1045 ((and (member "STRING" targets)
1037 (setq string-data 1046 (setq string-data
1038 (gui-get-selection 'XdndSelection 'STRING))) 1047 (x-get-local-selection local-selection 'STRING)))
1039 (cons 'DndTypeText (encode-coding-string string-data 1048 (cons 'DndTypeText (encode-coding-string string-data
1040 'latin-1)))))) 1049 'latin-1))))))
1041 1050
1042(defun x-dnd-do-offix-drop (targets x y frame window-id) 1051(defun x-dnd-do-offix-drop (targets x y frame window-id contents)
1043 "Perform an OffiX drop on WINDOW-ID with the contents of `XdndSelection'. 1052 "Perform an OffiX drop on WINDOW-ID with the given selection contents.
1044Return non-nil if the drop succeeded, or nil if it did not 1053Return non-nil if the drop succeeded, or nil if it did not
1045happen, which can happen if TARGETS didn't contain anything that 1054happen, which can happen if TARGETS didn't contain anything that
1046the OffiX protocol can represent. 1055the OffiX protocol can represent.
1047 1056
1048X and Y are the root window coordinates of the drop. TARGETS is 1057X and Y are the root window coordinates of the drop. TARGETS is
1049the list of targets `XdndSelection' can be converted to." 1058the list of targets CONTENTS can be converted to, and CONTENTS is
1050 (if-let* ((data (x-dnd-convert-to-offix targets)) 1059the local selection data to drop onto the target window.
1060
1061FRAME is the frame that will act as a source window for the
1062drop."
1063 (if-let* ((data (x-dnd-convert-to-offix targets contents))
1051 (type-id (car (rassq (car data) 1064 (type-id (car (rassq (car data)
1052 x-dnd-offix-id-to-name))) 1065 x-dnd-offix-id-to-name)))
1053 (source-id (string-to-number 1066 (source-id (string-to-number
@@ -1074,18 +1087,20 @@ the list of targets `XdndSelection' can be converted to."
1074 frame "_DND_PROTOCOL" 1087 frame "_DND_PROTOCOL"
1075 32 message-data)))) 1088 32 message-data))))
1076 1089
1077(defun x-dnd-handle-unsupported-drop (targets x y action window-id frame _time) 1090(defun x-dnd-handle-unsupported-drop (targets x y action window-id frame _time local-selection-data)
1078 "Return non-nil if the drop described by TARGETS and ACTION should not proceed. 1091 "Return non-nil if the drop described by TARGETS and ACTION should not proceed.
1079X and Y are the root window coordinates of the drop. 1092X and Y are the root window coordinates of the drop.
1080FRAME is the frame the drop originated on. 1093FRAME is the frame the drop originated on.
1081WINDOW-ID is the X window the drop should happen to." 1094WINDOW-ID is the X window the drop should happen to.
1095LOCAL-SELECTION-DATA is the local selection data of the drop."
1082 (not (and (or (eq action 'XdndActionCopy) 1096 (not (and (or (eq action 'XdndActionCopy)
1083 (eq action 'XdndActionMove)) 1097 (eq action 'XdndActionMove))
1084 (not (and x-dnd-use-offix-drop 1098 (not (and x-dnd-use-offix-drop local-selection-data
1085 (or (not (eq x-dnd-use-offix-drop 'files)) 1099 (or (not (eq x-dnd-use-offix-drop 'files))
1086 (member "FILE_NAME" targets)) 1100 (member "FILE_NAME" targets))
1087 (x-dnd-do-offix-drop targets x 1101 (x-dnd-do-offix-drop targets x
1088 y frame window-id))) 1102 y frame window-id
1103 local-selection-data)))
1089 (or 1104 (or
1090 (member "STRING" targets) 1105 (member "STRING" targets)
1091 (member "UTF8_STRING" targets) 1106 (member "UTF8_STRING" targets)
diff --git a/src/keyboard.c b/src/keyboard.c
index e5708c06d93..8b8d348c41a 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -4056,12 +4056,13 @@ kbd_buffer_get_event (KBOARD **kbp,
4056 4056
4057 if (!NILP (Vx_dnd_unsupported_drop_function)) 4057 if (!NILP (Vx_dnd_unsupported_drop_function))
4058 { 4058 {
4059 if (!NILP (call7 (Vx_dnd_unsupported_drop_function, 4059 if (!NILP (call8 (Vx_dnd_unsupported_drop_function,
4060 XCAR (XCDR (event->ie.arg)), event->ie.x, 4060 XCAR (XCDR (event->ie.arg)), event->ie.x,
4061 event->ie.y, XCAR (XCDR (XCDR (event->ie.arg))), 4061 event->ie.y, XCAR (XCDR (XCDR (event->ie.arg))),
4062 make_uint (event->ie.code), 4062 make_uint (event->ie.code),
4063 event->ie.frame_or_window, 4063 event->ie.frame_or_window,
4064 make_int (event->ie.timestamp)))) 4064 make_int (event->ie.timestamp),
4065 Fcopy_sequence (XCAR (event->ie.arg)))))
4065 break; 4066 break;
4066 } 4067 }
4067 4068
diff --git a/src/xselect.c b/src/xselect.c
index d90916c6b63..a1f590632f8 100644
--- a/src/xselect.c
+++ b/src/xselect.c
@@ -307,18 +307,30 @@ x_own_selection (Lisp_Object selection_name, Lisp_Object selection_value,
307 This function is used both for remote requests (LOCAL_REQUEST is zero) 307 This function is used both for remote requests (LOCAL_REQUEST is zero)
308 and for local x-get-selection-internal (LOCAL_REQUEST is nonzero). 308 and for local x-get-selection-internal (LOCAL_REQUEST is nonzero).
309 309
310 If LOCAL_VALUE is non-nil, use it as the local copy. Also allow
311 quitting in that case, and let DPYINFO be NULL.
312
310 This calls random Lisp code, and may signal or gc. */ 313 This calls random Lisp code, and may signal or gc. */
311 314
312static Lisp_Object 315static Lisp_Object
313x_get_local_selection (Lisp_Object selection_symbol, Lisp_Object target_type, 316x_get_local_selection (Lisp_Object selection_symbol, Lisp_Object target_type,
314 bool local_request, struct x_display_info *dpyinfo) 317 bool local_request, struct x_display_info *dpyinfo,
318 Lisp_Object local_value)
315{ 319{
316 Lisp_Object local_value, tem; 320 Lisp_Object tem;
317 Lisp_Object handler_fn, value, check; 321 Lisp_Object handler_fn, value, check;
322 bool may_quit;
323 specpdl_ref count;
324
325 may_quit = false;
318 326
319 local_value = LOCAL_SELECTION (selection_symbol, dpyinfo); 327 if (NILP (local_value))
328 local_value = LOCAL_SELECTION (selection_symbol, dpyinfo);
329 else
330 may_quit = true;
320 331
321 if (NILP (local_value)) return Qnil; 332 if (NILP (local_value))
333 return Qnil;
322 334
323 /* TIMESTAMP is a special case. */ 335 /* TIMESTAMP is a special case. */
324 if (EQ (target_type, QTIMESTAMP)) 336 if (EQ (target_type, QTIMESTAMP))
@@ -331,8 +343,10 @@ x_get_local_selection (Lisp_Object selection_symbol, Lisp_Object target_type,
331 /* Don't allow a quit within the converter. 343 /* Don't allow a quit within the converter.
332 When the user types C-g, he would be surprised 344 When the user types C-g, he would be surprised
333 if by luck it came during a converter. */ 345 if by luck it came during a converter. */
334 specpdl_ref count = SPECPDL_INDEX (); 346 count = SPECPDL_INDEX ();
335 specbind (Qinhibit_quit, Qt); 347
348 if (!may_quit)
349 specbind (Qinhibit_quit, Qt);
336 350
337 CHECK_SYMBOL (target_type); 351 CHECK_SYMBOL (target_type);
338 handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist)); 352 handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist));
@@ -804,7 +818,9 @@ x_handle_selection_request (struct selection_input_event *event)
804 target that doesn't support XDND. */ 818 target that doesn't support XDND. */
805 if (SELECTION_EVENT_TIME (event) == pending_dnd_time + 1 819 if (SELECTION_EVENT_TIME (event) == pending_dnd_time + 1
806 || SELECTION_EVENT_TIME (event) == pending_dnd_time + 2) 820 || SELECTION_EVENT_TIME (event) == pending_dnd_time + 2)
807 selection_symbol = QXdndSelection; 821 /* Always reply with the contents of PRIMARY, since that's where
822 the selection data is. */
823 selection_symbol = QPRIMARY;
808 824
809 local_selection_data = LOCAL_SELECTION (selection_symbol, dpyinfo); 825 local_selection_data = LOCAL_SELECTION (selection_symbol, dpyinfo);
810 826
@@ -915,7 +931,7 @@ x_convert_selection (Lisp_Object selection_symbol,
915 931
916 lisp_selection 932 lisp_selection
917 = x_get_local_selection (selection_symbol, target_symbol, 933 = x_get_local_selection (selection_symbol, target_symbol,
918 false, dpyinfo); 934 false, dpyinfo, Qnil);
919 935
920 frame = selection_request_stack; 936 frame = selection_request_stack;
921 937
@@ -2131,7 +2147,7 @@ On Nextstep, TIME-STAMP and TERMINAL are unused. */)
2131 } 2147 }
2132 2148
2133 val = x_get_local_selection (selection_symbol, target_type, true, 2149 val = x_get_local_selection (selection_symbol, target_type, true,
2134 FRAME_DISPLAY_INFO (f)); 2150 FRAME_DISPLAY_INFO (f), Qnil);
2135 2151
2136 if (NILP (val) && FRAME_LIVE_P (f)) 2152 if (NILP (val) && FRAME_LIVE_P (f))
2137 { 2153 {
@@ -2273,6 +2289,45 @@ On Nextstep, TERMINAL is unused. */)
2273 return (owner ? Qt : Qnil); 2289 return (owner ? Qt : Qnil);
2274} 2290}
2275 2291
2292DEFUN ("x-get-local-selection", Fx_get_local_selection, Sx_get_local_selection,
2293 0, 2, 0,
2294 doc: /* Run selection converters for VALUE, and return the result.
2295TARGET is the selection target that is used to find a suitable
2296converter. VALUE is a list of 4 values NAME, SELECTION-VALUE,
2297TIMESTAMP and FRAME. NAME is the name of the selection that will be
2298passed to selection converters, SELECTION-VALUE is the value of the
2299selection used by the converter, TIMESTAMP is not meaningful (but must
2300be a number that fits in an X timestamp), and FRAME is the frame
2301describing the terminal for which the selection converter will be
2302run. */)
2303 (Lisp_Object value, Lisp_Object target)
2304{
2305 Time time;
2306 Lisp_Object name, timestamp, frame, result;
2307
2308 CHECK_SYMBOL (target);
2309 name = Fnth (make_fixnum (0), value);
2310 timestamp = Fnth (make_fixnum (2), value);
2311 frame = Fnth (make_fixnum (3), value);
2312
2313 CHECK_SYMBOL (name);
2314 CONS_TO_INTEGER (timestamp, Time, time);
2315 check_window_system (decode_live_frame (frame));
2316
2317 result = x_get_local_selection (name, target, true,
2318 NULL, value);
2319
2320 if (CONSP (result) && SYMBOLP (XCAR (result)))
2321 {
2322 result = XCDR (result);
2323
2324 if (CONSP (result) && NILP (XCDR (result)))
2325 result = XCAR (result);
2326 }
2327
2328 return clean_local_selection_data (result);
2329}
2330
2276 2331
2277/* Send clipboard manager a SAVE_TARGETS request with a UTF8_STRING 2332/* Send clipboard manager a SAVE_TARGETS request with a UTF8_STRING
2278 property (https://www.freedesktop.org/wiki/ClipboardManager/). */ 2333 property (https://www.freedesktop.org/wiki/ClipboardManager/). */
@@ -2809,6 +2864,7 @@ syms_of_xselect (void)
2809 defsubr (&Sx_get_atom_name); 2864 defsubr (&Sx_get_atom_name);
2810 defsubr (&Sx_send_client_message); 2865 defsubr (&Sx_send_client_message);
2811 defsubr (&Sx_register_dnd_atom); 2866 defsubr (&Sx_register_dnd_atom);
2867 defsubr (&Sx_get_local_selection);
2812 2868
2813 reading_selection_reply = Fcons (Qnil, Qnil); 2869 reading_selection_reply = Fcons (Qnil, Qnil);
2814 staticpro (&reading_selection_reply); 2870 staticpro (&reading_selection_reply);
diff --git a/src/xterm.c b/src/xterm.c
index d7c3bfa7aff..7298feb43a1 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -11234,6 +11234,19 @@ x_dnd_delete_action_list (Lisp_Object frame)
11234 unblock_input (); 11234 unblock_input ();
11235} 11235}
11236 11236
11237static void
11238x_dnd_lose_ownership (Lisp_Object timestamp_and_frame)
11239{
11240 struct frame *f;
11241
11242 f = XFRAME (XCDR (timestamp_and_frame));
11243
11244 if (FRAME_LIVE_P (f))
11245 Fx_disown_selection_internal (QXdndSelection,
11246 XCAR (timestamp_and_frame),
11247 XCDR (timestamp_and_frame));
11248}
11249
11237/* This function is defined far away from the rest of the XDND code so 11250/* This function is defined far away from the rest of the XDND code so
11238 it can utilize `x_any_window_to_frame'. */ 11251 it can utilize `x_any_window_to_frame'. */
11239 11252
@@ -11324,12 +11337,13 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
11324 11337
11325 if (!NILP (Vx_dnd_unsupported_drop_function)) 11338 if (!NILP (Vx_dnd_unsupported_drop_function))
11326 { 11339 {
11327 if (!NILP (call7 (Vx_dnd_unsupported_drop_function, 11340 if (!NILP (call8 (Vx_dnd_unsupported_drop_function,
11328 XCAR (XCDR (event->ie.arg)), event->ie.x, 11341 XCAR (XCDR (event->ie.arg)), event->ie.x,
11329 event->ie.y, XCAR (XCDR (XCDR (event->ie.arg))), 11342 event->ie.y, XCAR (XCDR (XCDR (event->ie.arg))),
11330 make_uint (event->ie.code), 11343 make_uint (event->ie.code),
11331 event->ie.frame_or_window, 11344 event->ie.frame_or_window,
11332 make_int (event->ie.timestamp)))) 11345 make_int (event->ie.timestamp),
11346 Fcopy_sequence (XCAR (event->ie.arg)))))
11333 continue; 11347 continue;
11334 } 11348 }
11335 11349
@@ -11364,12 +11378,6 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
11364 /* If local_value is nil, then we lost ownership of XdndSelection. 11378 /* If local_value is nil, then we lost ownership of XdndSelection.
11365 Signal a more informative error than args-out-of-range. */ 11379 Signal a more informative error than args-out-of-range. */
11366 if (NILP (local_value)) 11380 if (NILP (local_value))
11367 error ("Lost ownership of XdndSelection");
11368
11369 if (CONSP (local_value))
11370 x_own_selection (QXdndSelection,
11371 Fnth (make_fixnum (1), local_value), frame);
11372 else
11373 error ("No local value for XdndSelection"); 11381 error ("No local value for XdndSelection");
11374 11382
11375 if (popup_activated ()) 11383 if (popup_activated ())
@@ -11387,6 +11395,14 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
11387 else 11395 else
11388 x_dnd_selection_timestamp = XFIXNUM (ltimestamp); 11396 x_dnd_selection_timestamp = XFIXNUM (ltimestamp);
11389 11397
11398 /* Release ownership of XdndSelection after this function returns.
11399 VirtualBox uses the owner of XdndSelection to determine whether
11400 or not mouse motion is part of a drag-and-drop operation. */
11401
11402 if (!x_dnd_preserve_selection_data)
11403 record_unwind_protect (x_dnd_lose_ownership,
11404 Fcons (ltimestamp, frame));
11405
11390 x_dnd_motif_operations 11406 x_dnd_motif_operations
11391 = xm_side_effect_from_action (FRAME_DISPLAY_INFO (f), xaction); 11407 = xm_side_effect_from_action (FRAME_DISPLAY_INFO (f), xaction);
11392 11408
@@ -27959,17 +27975,21 @@ mouse position list. */);
27959 27975
27960 DEFVAR_LISP ("x-dnd-unsupported-drop-function", Vx_dnd_unsupported_drop_function, 27976 DEFVAR_LISP ("x-dnd-unsupported-drop-function", Vx_dnd_unsupported_drop_function,
27961 doc: /* Function called when trying to drop on an unsupported window. 27977 doc: /* Function called when trying to drop on an unsupported window.
27978
27962This function is called whenever the user tries to drop something on a 27979This function is called whenever the user tries to drop something on a
27963window that does not support either the XDND or Motif protocols for 27980window that does not support either the XDND or Motif protocols for
27964drag-and-drop. It should return a non-nil value if the drop was 27981drag-and-drop. It should return a non-nil value if the drop was
27965handled by the function, and nil if it was not. It should accept 27982handled by the function, and nil if it was not. It should accept
27966several arguments TARGETS, X, Y, ACTION, WINDOW-ID, FRAME and TIME, 27983several arguments TARGETS, X, Y, ACTION, WINDOW-ID, FRAME, TIME and
27967where TARGETS is the list of targets that was passed to 27984LOCAL-SELECTION, where TARGETS is the list of targets that was passed
27968`x-begin-drag', WINDOW-ID is the numeric XID of the window that is 27985to `x-begin-drag', WINDOW-ID is the numeric XID of the window that is
27969being dropped on, X and Y are the root window-relative coordinates 27986being dropped on, X and Y are the root window-relative coordinates
27970where the drop happened, ACTION is the action that was passed to 27987where the drop happened, ACTION is the action that was passed to
27971`x-begin-drag', FRAME is the frame which initiated the drag-and-drop 27988`x-begin-drag', FRAME is the frame which initiated the drag-and-drop
27972operation, and TIME is the X server time when the drop happened. */); 27989operation, TIME is the X server time when the drop happened, and
27990LOCAL-SELECTION is the contents of the `XdndSelection' when
27991`x-begin-drag' was run, which can be passed to
27992`x-get-local-selection'. */);
27973 Vx_dnd_unsupported_drop_function = Qnil; 27993 Vx_dnd_unsupported_drop_function = Qnil;
27974 27994
27975 DEFVAR_INT ("x-color-cache-bucket-size", x_color_cache_bucket_size, 27995 DEFVAR_INT ("x-color-cache-bucket-size", x_color_cache_bucket_size,
@@ -27996,4 +28016,11 @@ should return a symbol describing what to return from
27996If the value is nil, or the function returns a value that is not 28016If the value is nil, or the function returns a value that is not
27997a symbol, a drop on an Emacs frame will be canceled. */); 28017a symbol, a drop on an Emacs frame will be canceled. */);
27998 Vx_dnd_native_test_function = Qnil; 28018 Vx_dnd_native_test_function = Qnil;
28019
28020 DEFVAR_BOOL ("x-dnd-preserve-selection-data", x_dnd_preserve_selection_data,
28021 doc: /* Preserve selection data after `x-begin-drag' returns.
28022This lets you inspect the contents of `XdndSelection' after a
28023drag-and-drop operation, which is useful when writing tests for
28024drag-and-drop code. */);
28025 x_dnd_preserve_selection_data = false;
27999} 28026}
diff --git a/test/lisp/dnd-tests.el b/test/lisp/dnd-tests.el
index aae9c80273f..18dd55c206c 100644
--- a/test/lisp/dnd-tests.el
+++ b/test/lisp/dnd-tests.el
@@ -38,6 +38,7 @@
38 "Alist of selection names to their values.") 38 "Alist of selection names to their values.")
39 39
40(defvar x-treat-local-requests-remotely) 40(defvar x-treat-local-requests-remotely)
41(defvar x-dnd-preserve-selection-data)
41 42
42;; Define some replacements for functions used by the drag-and-drop 43;; Define some replacements for functions used by the drag-and-drop
43;; code on X when running under something else. 44;; code on X when running under something else.
@@ -152,7 +153,8 @@ This function only tries to handle strings."
152 ;; program with reasonably correct behavior, such as dtpad, gedit, 153 ;; program with reasonably correct behavior, such as dtpad, gedit,
153 ;; or Mozilla. 154 ;; or Mozilla.
154 ;; ASCII Latin-1 UTF-8 155 ;; ASCII Latin-1 UTF-8
155 (let ((test-text "hello, everyone! sæl öllsömul! всем привет")) 156 (let ((test-text "hello, everyone! sæl öllsömul! всем привет")
157 (x-dnd-preserve-selection-data t))
156 ;; Verify that dragging works. 158 ;; Verify that dragging works.
157 (should (eq (dnd-begin-text-drag test-text) 'copy)) 159 (should (eq (dnd-begin-text-drag test-text) 'copy))
158 (should (eq (dnd-begin-text-drag test-text nil 'move) 'move)) 160 (should (eq (dnd-begin-text-drag test-text nil 'move) 'move))
@@ -187,7 +189,8 @@ This function only tries to handle strings."
187 (normal-multibyte-file (expand-file-name 189 (normal-multibyte-file (expand-file-name
188 (make-temp-name "тест-на-перетаскивание") 190 (make-temp-name "тест-на-перетаскивание")
189 temporary-file-directory)) 191 temporary-file-directory))
190 (remote-temp-file (dnd-tests-make-temp-name))) 192 (remote-temp-file (dnd-tests-make-temp-name))
193 (x-dnd-preserve-selection-data t))
191 ;; Touch those files if they don't exist. 194 ;; Touch those files if they don't exist.
192 (unless (file-exists-p normal-temp-file) 195 (unless (file-exists-p normal-temp-file)
193 (write-region "" 0 normal-temp-file)) 196 (write-region "" 0 normal-temp-file))
@@ -273,7 +276,8 @@ This function only tries to handle strings."
273 (expand-file-name (make-temp-name "dnd-test") 276 (expand-file-name (make-temp-name "dnd-test")
274 temporary-file-directory)) 277 temporary-file-directory))
275 (nonexistent-remote-file (dnd-tests-make-temp-name)) 278 (nonexistent-remote-file (dnd-tests-make-temp-name))
276 (nonexistent-remote-file-1 (dnd-tests-make-temp-name))) 279 (nonexistent-remote-file-1 (dnd-tests-make-temp-name))
280 (x-dnd-preserve-selection-data t))
277 ;; Touch those files if they don't exist. 281 ;; Touch those files if they don't exist.
278 (unless (file-exists-p normal-temp-file) 282 (unless (file-exists-p normal-temp-file)
279 (write-region "" 0 normal-temp-file)) 283 (write-region "" 0 normal-temp-file))