mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-16 17:24:23 +00:00
Improve compatibility with some clients of the Motif drop protocol
* lisp/select.el (x-dnd-targets-list): New defvar. (xselect-convert-to-targets): Convert XdndSelection based on the DND targets list. * src/xfns.c (Fx_begin_drag): Pass new argument. * src/xselect.c (struct x_selection_request): New struct. (x_push_current_selection_request): (x_pop_current_selection_request): New functions. (x_selection_request_lisp_error, x_reply_selection_request) (x_handle_selection_request, x_convert_selection) (syms_of_xselect_for_pdumper): Correctly handle recursive requests for MULTIPLE by maintaining a stack of selection requests, converted selections, and other data. * src/xterm.c (x_dnd_begin_drag_and_drop): New argument `selection_target_list'. Bind it to the DND targets list. (syms_of_xterm): New defvar and associated defsym. * src/xterm.h: Update prototypes.
This commit is contained in:
parent
6871e649b5
commit
3bdeedd8ac
5 changed files with 115 additions and 43 deletions
|
|
@ -601,19 +601,29 @@ two markers or an overlay. Otherwise, it is nil."
|
|||
(if len
|
||||
(xselect--int-to-cons len))))
|
||||
|
||||
(defvar x-dnd-targets-list)
|
||||
|
||||
(defun xselect-convert-to-targets (selection _type value)
|
||||
;; Return a vector of atoms, but remove duplicates first.
|
||||
(apply #'vector
|
||||
(delete-dups
|
||||
`( TIMESTAMP MULTIPLE
|
||||
. ,(delq '_EMACS_INTERNAL
|
||||
(mapcar (lambda (conv)
|
||||
(if (or (not (consp (cdr conv)))
|
||||
(funcall (cadr conv) selection
|
||||
(car conv) value))
|
||||
(car conv)
|
||||
'_EMACS_INTERNAL))
|
||||
selection-converter-alist))))))
|
||||
(if (eq selection 'XdndSelection)
|
||||
;; This isn't required by the XDND protocol, and sure enough no
|
||||
;; clients seem to dependent on it, but Emacs implements the
|
||||
;; receiver side of the Motif drop protocol by looking at the
|
||||
;; initiator selection's TARGETS target (which Motif provides)
|
||||
;; instead of the target table on the drag window, so it seems
|
||||
;; plausible for other clients to rely on that as well.
|
||||
(apply #'vector (mapcar #'intern x-dnd-targets-list))
|
||||
(apply #'vector
|
||||
(delete-dups
|
||||
`( TIMESTAMP MULTIPLE
|
||||
. ,(delq '_EMACS_INTERNAL
|
||||
(mapcar (lambda (conv)
|
||||
(if (or (not (consp (cdr conv)))
|
||||
(funcall (cadr conv) selection
|
||||
(car conv) value))
|
||||
(car conv)
|
||||
'_EMACS_INTERNAL))
|
||||
selection-converter-alist)))))))
|
||||
|
||||
(defun xselect-convert-to-delete (selection _type _value)
|
||||
;; This should be handled by the caller of `x-begin-drag'.
|
||||
|
|
|
|||
|
|
@ -6985,7 +6985,7 @@ that mouse buttons are being held down, such as immediately after a
|
|||
xaction, return_frame, action_list,
|
||||
(const char **) &name_list, nnames,
|
||||
!NILP (allow_current_frame), target_atoms,
|
||||
ntargets);
|
||||
ntargets, original);
|
||||
|
||||
SAFE_FREE ();
|
||||
return lval;
|
||||
|
|
|
|||
107
src/xselect.c
107
src/xselect.c
|
|
@ -417,14 +417,6 @@ x_decline_selection_request (struct selection_input_event *event)
|
|||
unblock_input ();
|
||||
}
|
||||
|
||||
/* This is the selection request currently being processed.
|
||||
It is set to zero when the request is fully processed. */
|
||||
static struct selection_input_event *x_selection_current_request;
|
||||
|
||||
/* Display info in x_selection_request. */
|
||||
|
||||
static struct x_display_info *selection_request_dpyinfo;
|
||||
|
||||
/* Raw selection data, for sending to a requestor window. */
|
||||
|
||||
struct selection_data
|
||||
|
|
@ -442,12 +434,59 @@ struct selection_data
|
|||
struct selection_data *next;
|
||||
};
|
||||
|
||||
/* Linked list of the above (in support of MULTIPLE targets). */
|
||||
struct x_selection_request
|
||||
{
|
||||
/* The last element in this stack. */
|
||||
struct x_selection_request *last;
|
||||
|
||||
static struct selection_data *converted_selections;
|
||||
/* Its display info. */
|
||||
struct x_display_info *dpyinfo;
|
||||
|
||||
/* "Data" to send a requestor for a failed MULTIPLE subtarget. */
|
||||
static Atom conversion_fail_tag;
|
||||
/* Its selection input event. */
|
||||
struct selection_input_event *request;
|
||||
|
||||
/* Linked list of the above (in support of MULTIPLE targets). */
|
||||
struct selection_data *converted_selections;
|
||||
|
||||
/* "Data" to send a requestor for a failed MULTIPLE subtarget. */
|
||||
Atom conversion_fail_tag;
|
||||
|
||||
/* Whether or not conversion was successful. */
|
||||
bool converted;
|
||||
};
|
||||
|
||||
/* Stack of selections currently being processed.
|
||||
NULL if all requests have been fully processed. */
|
||||
|
||||
struct x_selection_request *selection_request_stack;
|
||||
|
||||
static void
|
||||
x_push_current_selection_request (struct selection_input_event *se,
|
||||
struct x_display_info *dpyinfo)
|
||||
{
|
||||
struct x_selection_request *frame;
|
||||
|
||||
frame = xmalloc (sizeof *frame);
|
||||
frame->converted = false;
|
||||
frame->last = selection_request_stack;
|
||||
frame->request = se;
|
||||
frame->dpyinfo = dpyinfo;
|
||||
frame->converted_selections = NULL;
|
||||
frame->conversion_fail_tag = None;
|
||||
|
||||
selection_request_stack = frame;
|
||||
}
|
||||
|
||||
static void
|
||||
x_pop_current_selection_request (void)
|
||||
{
|
||||
struct x_selection_request *tem;
|
||||
|
||||
tem = selection_request_stack;
|
||||
selection_request_stack = selection_request_stack->last;
|
||||
|
||||
xfree (tem);
|
||||
}
|
||||
|
||||
/* Used as an unwind-protect clause so that, if a selection-converter signals
|
||||
an error, we tell the requestor that we were unable to do what they wanted
|
||||
|
|
@ -457,19 +496,21 @@ static void
|
|||
x_selection_request_lisp_error (void)
|
||||
{
|
||||
struct selection_data *cs, *next;
|
||||
struct x_selection_request *frame;
|
||||
|
||||
for (cs = converted_selections; cs; cs = next)
|
||||
frame = selection_request_stack;
|
||||
|
||||
for (cs = frame->converted_selections; cs; cs = next)
|
||||
{
|
||||
next = cs->next;
|
||||
if (! cs->nofree && cs->data)
|
||||
xfree (cs->data);
|
||||
xfree (cs);
|
||||
}
|
||||
converted_selections = NULL;
|
||||
frame->converted_selections = NULL;
|
||||
|
||||
if (x_selection_current_request != 0
|
||||
&& selection_request_dpyinfo->display)
|
||||
x_decline_selection_request (x_selection_current_request);
|
||||
if (!frame->converted && frame->dpyinfo->display)
|
||||
x_decline_selection_request (frame->request);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -535,6 +576,9 @@ x_reply_selection_request (struct selection_input_event *event,
|
|||
int max_bytes = selection_quantum (display);
|
||||
specpdl_ref count = SPECPDL_INDEX ();
|
||||
struct selection_data *cs;
|
||||
struct x_selection_request *frame;
|
||||
|
||||
frame = selection_request_stack;
|
||||
|
||||
reply->type = SelectionNotify;
|
||||
reply->display = display;
|
||||
|
|
@ -558,7 +602,7 @@ x_reply_selection_request (struct selection_input_event *event,
|
|||
(section 2.7.2 of ICCCM). Note that we store the data for a
|
||||
MULTIPLE request in the opposite order; the ICCM says only that
|
||||
the conversion itself must be done in the same order. */
|
||||
for (cs = converted_selections; cs; cs = cs->next)
|
||||
for (cs = frame->converted_selections; cs; cs = cs->next)
|
||||
{
|
||||
if (cs->property == None)
|
||||
continue;
|
||||
|
|
@ -613,7 +657,7 @@ x_reply_selection_request (struct selection_input_event *event,
|
|||
be improved; there's a chance of deadlock if more than one
|
||||
subtarget in a MULTIPLE selection requires an INCR transfer, and
|
||||
the requestor and Emacs loop waiting on different transfers. */
|
||||
for (cs = converted_selections; cs; cs = cs->next)
|
||||
for (cs = frame->converted_selections; cs; cs = cs->next)
|
||||
if (cs->wait_object)
|
||||
{
|
||||
int format_bytes = cs->format / 8;
|
||||
|
|
@ -749,9 +793,11 @@ x_handle_selection_request (struct selection_input_event *event)
|
|||
&& local_selection_time > SELECTION_EVENT_TIME (event))
|
||||
goto DONE;
|
||||
|
||||
x_selection_current_request = event;
|
||||
selection_request_dpyinfo = dpyinfo;
|
||||
block_input ();
|
||||
x_push_current_selection_request (event, dpyinfo);
|
||||
record_unwind_protect_void (x_pop_current_selection_request);
|
||||
record_unwind_protect_void (x_selection_request_lisp_error);
|
||||
unblock_input ();
|
||||
|
||||
TRACE2 ("x_handle_selection_request: selection=%s, target=%s",
|
||||
SDATA (SYMBOL_NAME (selection_symbol)),
|
||||
|
|
@ -808,11 +854,12 @@ x_handle_selection_request (struct selection_input_event *event)
|
|||
|
||||
DONE:
|
||||
|
||||
selection_request_stack->converted = true;
|
||||
|
||||
if (success)
|
||||
x_reply_selection_request (event, dpyinfo);
|
||||
else
|
||||
x_decline_selection_request (event);
|
||||
x_selection_current_request = 0;
|
||||
|
||||
/* Run the `x-sent-selection-functions' abnormal hook. */
|
||||
if (!NILP (Vx_sent_selection_functions)
|
||||
|
|
@ -837,11 +884,14 @@ x_convert_selection (Lisp_Object selection_symbol,
|
|||
{
|
||||
Lisp_Object lisp_selection;
|
||||
struct selection_data *cs;
|
||||
struct x_selection_request *frame;
|
||||
|
||||
lisp_selection
|
||||
= x_get_local_selection (selection_symbol, target_symbol,
|
||||
false, dpyinfo);
|
||||
|
||||
frame = selection_request_stack;
|
||||
|
||||
/* A nil return value means we can't perform the conversion. */
|
||||
if (NILP (lisp_selection)
|
||||
|| (CONSP (lisp_selection) && NILP (XCDR (lisp_selection))))
|
||||
|
|
@ -849,15 +899,16 @@ x_convert_selection (Lisp_Object selection_symbol,
|
|||
if (for_multiple)
|
||||
{
|
||||
cs = xmalloc (sizeof *cs);
|
||||
cs->data = (unsigned char *) &conversion_fail_tag;
|
||||
cs->data = ((unsigned char *)
|
||||
&selection_request_stack->conversion_fail_tag);
|
||||
cs->size = 1;
|
||||
cs->format = 32;
|
||||
cs->type = XA_ATOM;
|
||||
cs->nofree = true;
|
||||
cs->property = property;
|
||||
cs->wait_object = NULL;
|
||||
cs->next = converted_selections;
|
||||
converted_selections = cs;
|
||||
cs->next = frame->converted_selections;
|
||||
frame->converted_selections = cs;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -869,8 +920,8 @@ x_convert_selection (Lisp_Object selection_symbol,
|
|||
cs->nofree = true;
|
||||
cs->property = property;
|
||||
cs->wait_object = NULL;
|
||||
cs->next = converted_selections;
|
||||
converted_selections = cs;
|
||||
cs->next = frame->converted_selections;
|
||||
frame->converted_selections = cs;
|
||||
lisp_data_to_selection_data (dpyinfo, lisp_selection, cs);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2777,6 +2828,4 @@ syms_of_xselect_for_pdumper (void)
|
|||
property_change_wait_list = 0;
|
||||
prop_location_identifier = 0;
|
||||
property_change_reply = Fcons (Qnil, Qnil);
|
||||
converted_selections = NULL;
|
||||
conversion_fail_tag = None;
|
||||
}
|
||||
|
|
|
|||
14
src/xterm.c
14
src/xterm.c
|
|
@ -10645,7 +10645,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
|
|||
Lisp_Object return_frame, Atom *ask_action_list,
|
||||
const char **ask_action_names, size_t n_ask_actions,
|
||||
bool allow_current_frame, Atom *target_atoms,
|
||||
int ntargets)
|
||||
int ntargets, Lisp_Object selection_target_list)
|
||||
{
|
||||
#ifndef USE_GTK
|
||||
XEvent next_event;
|
||||
|
|
@ -10674,6 +10674,10 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
|
|||
|
||||
base = SPECPDL_INDEX ();
|
||||
|
||||
/* Bind this here to avoid juggling bindings and SAFE_FREE in
|
||||
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
|
||||
|
|
@ -26516,6 +26520,7 @@ syms_of_xterm (void)
|
|||
DEFSYM (Qvendor_specific_keysyms, "vendor-specific-keysyms");
|
||||
DEFSYM (Qlatin_1, "latin-1");
|
||||
DEFSYM (Qnow, "now");
|
||||
DEFSYM (Qx_dnd_targets_list, "x-dnd-targets-list");
|
||||
|
||||
#ifdef USE_GTK
|
||||
xg_default_icon_file = build_pure_c_string ("icons/hicolor/scalable/apps/emacs.svg");
|
||||
|
|
@ -26752,4 +26757,11 @@ operation, and TIME is the X server time when the drop happened. */);
|
|||
doc: /* Max number of buckets allowed per display in the internal color cache.
|
||||
Values less than 1 mean 128. This option is for debugging only. */);
|
||||
x_color_cache_bucket_size = 128;
|
||||
|
||||
DEFVAR_LISP ("x-dnd-targets-list", Vx_dnd_targets_list,
|
||||
doc: /* List of drag-and-drop targets.
|
||||
This variable contains the list of drag-and-drop selection targets
|
||||
during a drag-and-drop operation, in the same format as the TARGET
|
||||
argument to `x-begin-drag'. */);
|
||||
Vx_dnd_targets_list = Qnil;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1460,7 +1460,8 @@ extern void x_handle_pending_selection_requests (void);
|
|||
extern bool x_detect_pending_selection_requests (void);
|
||||
extern Lisp_Object x_dnd_begin_drag_and_drop (struct frame *, Time, Atom,
|
||||
Lisp_Object, Atom *, const char **,
|
||||
size_t, bool, Atom *, int);
|
||||
size_t, bool, Atom *, int,
|
||||
Lisp_Object);
|
||||
extern void x_dnd_do_unsupported_drop (struct x_display_info *, Lisp_Object,
|
||||
Lisp_Object, Lisp_Object, Window, int,
|
||||
int, Time);
|
||||
|
|
|
|||
Loading…
Reference in a new issue