Fix preservation of the original value of PRIMARY after dropping on xterm

* src/xselect.c (x_own_selection): New arg `dnd_data'.  Record
it.
(x_get_local_selection, x_handle_selection_request)
(x_convert_selection): Convert the DND data instead if the
situation warrants.
(Fx_own_selection_internal, Fx_get_selection_internal)
(Fx_get_local_selection): Update calls to x_get_local_selection.

* src/xterm.c (x_dnd_do_unsupported_drop): If obtaining
selection ownership failed, return.  Record DND value and
preserve the current value of PRIMARY, if it exists.

* src/xterm.h: Update prototypes.
This commit is contained in:
Po Lu 2022-06-30 09:45:49 +08:00
parent d7dc8c5fe4
commit e161b5fa3c
3 changed files with 69 additions and 31 deletions

View file

@ -47,7 +47,7 @@ struct selection_data;
static void x_decline_selection_request (struct selection_input_event *);
static bool x_convert_selection (Lisp_Object, Lisp_Object, Atom, bool,
struct x_display_info *);
struct x_display_info *, bool);
static bool waiting_for_other_props_on_window (Display *, Window);
static struct prop_location *expect_property_change (Display *, Window,
Atom, int);
@ -250,20 +250,26 @@ x_atom_to_symbol (struct x_display_info *dpyinfo, Atom atom)
/* Do protocol to assert ourself as a selection owner.
FRAME shall be the owner; it must be a valid X frame.
TIMESTAMP should be the timestamp where selection ownership will be
assumed.
DND_DATA is the local value that will be used for selection requests
with `pending_dnd_time'.
Update the Vselection_alist so that we can reply to later requests for
our selection. */
void
x_own_selection (Lisp_Object selection_name, Lisp_Object selection_value,
Lisp_Object frame)
Lisp_Object frame, Lisp_Object dnd_data, Time timestamp)
{
struct frame *f = XFRAME (frame);
Window selecting_window = FRAME_X_WINDOW (f);
struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
Display *display = dpyinfo->display;
Time timestamp = dpyinfo->last_user_time;
Atom selection_atom = symbol_to_x_atom (dpyinfo, selection_name);
if (!timestamp)
timestamp = dpyinfo->last_user_time;
block_input ();
x_catch_errors (display);
XSetSelectionOwner (display, selection_atom, selecting_window, timestamp);
@ -276,8 +282,9 @@ x_own_selection (Lisp_Object selection_name, Lisp_Object selection_value,
Lisp_Object selection_data;
Lisp_Object prev_value;
selection_data = list4 (selection_name, selection_value,
INT_TO_INTEGER (timestamp), frame);
selection_data = list5 (selection_name, selection_value,
INT_TO_INTEGER (timestamp), frame,
dnd_data);
prev_value = LOCAL_SELECTION (selection_name, dpyinfo);
tset_selection_alist
@ -310,12 +317,15 @@ x_own_selection (Lisp_Object selection_name, Lisp_Object selection_value,
If LOCAL_VALUE is non-nil, use it as the local copy. Also allow
quitting in that case, and let DPYINFO be NULL.
If NEED_ALTERNATE is true, use the drag-and-drop local value
instead.
This calls random Lisp code, and may signal or gc. */
static Lisp_Object
x_get_local_selection (Lisp_Object selection_symbol, Lisp_Object target_type,
bool local_request, struct x_display_info *dpyinfo,
Lisp_Object local_value)
Lisp_Object local_value, bool need_alternate)
{
Lisp_Object tem;
Lisp_Object handler_fn, value, check;
@ -354,7 +364,10 @@ x_get_local_selection (Lisp_Object selection_symbol, Lisp_Object target_type,
if (CONSP (handler_fn))
handler_fn = XCDR (handler_fn);
tem = XCAR (XCDR (local_value));
if (!need_alternate)
tem = XCAR (XCDR (local_value));
else
tem = XCAR (XCDR (XCDR (XCDR (XCDR (local_value)))));
if (STRINGP (tem))
{
@ -788,7 +801,7 @@ x_handle_selection_request (struct selection_input_event *event)
Lisp_Object local_selection_data;
bool success = false;
specpdl_ref count = SPECPDL_INDEX ();
bool pushed;
bool pushed, use_alternate;
Lisp_Object alias, tem;
alias = Vx_selection_alias_alist;
@ -814,14 +827,6 @@ x_handle_selection_request (struct selection_input_event *event)
if (!dpyinfo)
goto REALLY_DONE;
/* This is how the XDND protocol recommends dropping text onto a
target that doesn't support XDND. */
if (SELECTION_EVENT_TIME (event) == pending_dnd_time + 1
|| SELECTION_EVENT_TIME (event) == pending_dnd_time + 2)
/* Always reply with the contents of PRIMARY, since that's where
the selection data is. */
selection_symbol = QPRIMARY;
local_selection_data = LOCAL_SELECTION (selection_symbol, dpyinfo);
/* Decline if we don't own any selections. */
@ -834,6 +839,14 @@ x_handle_selection_request (struct selection_input_event *event)
&& local_selection_time > SELECTION_EVENT_TIME (event))
goto DONE;
use_alternate = false;
/* This is how the XDND protocol recommends dropping text onto a
target that doesn't support XDND. */
if (SELECTION_EVENT_TIME (event) == pending_dnd_time + 1
|| SELECTION_EVENT_TIME (event) == pending_dnd_time + 2)
use_alternate = true;
block_input ();
pushed = true;
x_push_current_selection_request (event, dpyinfo);
@ -874,7 +887,8 @@ x_handle_selection_request (struct selection_input_event *event)
if (subproperty != None)
subsuccess = x_convert_selection (selection_symbol, subtarget,
subproperty, true, dpyinfo);
subproperty, true, dpyinfo,
use_alternate);
if (!subsuccess)
ASET (multprop, 2*j+1, Qnil);
}
@ -891,7 +905,8 @@ x_handle_selection_request (struct selection_input_event *event)
property = SELECTION_EVENT_TARGET (event);
success = x_convert_selection (selection_symbol,
target_symbol, property,
false, dpyinfo);
false, dpyinfo,
use_alternate);
}
DONE:
@ -926,7 +941,8 @@ x_handle_selection_request (struct selection_input_event *event)
static bool
x_convert_selection (Lisp_Object selection_symbol,
Lisp_Object target_symbol, Atom property,
bool for_multiple, struct x_display_info *dpyinfo)
bool for_multiple, struct x_display_info *dpyinfo,
bool use_alternate)
{
Lisp_Object lisp_selection;
struct selection_data *cs;
@ -934,7 +950,7 @@ x_convert_selection (Lisp_Object selection_symbol,
lisp_selection
= x_get_local_selection (selection_symbol, target_symbol,
false, dpyinfo, Qnil);
false, dpyinfo, Qnil, use_alternate);
frame = selection_request_stack;
@ -2100,7 +2116,7 @@ On Nextstep, FRAME is unused. */)
CHECK_SYMBOL (selection);
if (NILP (value)) error ("VALUE may not be nil");
x_own_selection (selection, value, frame);
x_own_selection (selection, value, frame, Qnil, 0);
return value;
}
@ -2150,7 +2166,7 @@ On Nextstep, TIME-STAMP and TERMINAL are unused. */)
}
val = x_get_local_selection (selection_symbol, target_type, true,
FRAME_DISPLAY_INFO (f), Qnil);
FRAME_DISPLAY_INFO (f), Qnil, false);
if (NILP (val) && FRAME_LIVE_P (f))
{
@ -2318,7 +2334,7 @@ run. */)
check_window_system (decode_live_frame (frame));
result = x_get_local_selection (name, target, true,
NULL, value);
NULL, value, false);
if (CONSP (result) && SYMBOLP (XCAR (result)))
{

View file

@ -3798,7 +3798,14 @@ x_dnd_do_unsupported_drop (struct x_display_info *dpyinfo,
{
XEvent event;
int dest_x, dest_y;
Window child_return, child;
Window child_return, child, owner;
Lisp_Object current_value;
struct frame *f;
f = decode_window_system_frame (frame);
if (NILP (value))
return;
event.xbutton.serial = 0;
event.xbutton.send_event = True;
@ -3806,7 +3813,6 @@ x_dnd_do_unsupported_drop (struct x_display_info *dpyinfo,
event.xbutton.root = dpyinfo->root_window;
event.xbutton.x_root = root_x;
event.xbutton.y_root = root_y;
x_catch_errors (dpyinfo->display);
child = dpyinfo->root_window;
@ -3819,11 +3825,25 @@ x_dnd_do_unsupported_drop (struct x_display_info *dpyinfo,
&& child_return != None)
child = child_return;
if (CONSP (value))
x_own_selection (QPRIMARY, Fnth (make_fixnum (1), value),
frame);
else
error ("Lost ownership of XdndSelection");
if (!CONSP (value))
goto cancel;
current_value = assq_no_quit (QPRIMARY,
dpyinfo->terminal->Vselection_alist);
if (!NILP (current_value))
current_value = XCAR (XCDR (current_value));
x_own_selection (QPRIMARY, current_value, frame,
XCAR (XCDR (value)), before);
owner = XGetSelectionOwner (dpyinfo->display, XA_PRIMARY);
/* If we didn't successfully obtain selection ownership, refrain
from generating events that will insert something else. */
if (owner != FRAME_X_WINDOW (f))
goto cancel;
event.xbutton.window = child;
event.xbutton.subwindow = None;
@ -3847,6 +3867,7 @@ x_dnd_do_unsupported_drop (struct x_display_info *dpyinfo,
XSendEvent (dpyinfo->display, child,
True, ButtonReleaseMask, &event);
cancel:
x_uncatch_errors ();
}

View file

@ -1600,7 +1600,8 @@ extern void x_clipboard_manager_save_all (void);
extern Lisp_Object x_timestamp_for_selection (struct x_display_info *,
Lisp_Object);
extern void x_set_pending_dnd_time (Time);
extern void x_own_selection (Lisp_Object, Lisp_Object, Lisp_Object);
extern void x_own_selection (Lisp_Object, Lisp_Object, Lisp_Object,
Lisp_Object, Time);
extern Atom x_intern_cached_atom (struct x_display_info *, const char *,
bool);
extern char *x_get_atom_name (struct x_display_info *, Atom, bool *)