From 9accc800a75529c1eaf81d6844c53b6ca2f5622f Mon Sep 17 00:00:00 2001 From: Po Lu Date: Thu, 16 Jun 2022 10:08:12 +0800 Subject: [PATCH] Comply with the Motif requirement for unique drag atoms * src/xselect.c (x_handle_selection_request) (Fx_get_selection_internal, syms_of_xselect): New variable `x-selection-alias-alist'. Respect that alist of aliases. * src/xterm.c (x_atom_refs): Intern _EMACS_DRAG_ATOM. (xm_get_drag_atom_1, xm_get_drag_atom): New functions. (xm_setup_drag_info, x_dnd_cleanup_drag_and_drop) (x_dnd_begin_drag_and_drop, x_dnd_update_state, handle_one_xevent) (x_connection_closed, x_intern_cached_atom): Alias the drag atom to XdndSelection. Use it instead of XdndSelection to set the Motif index atom. (x_get_atom_name): Handle new atoms. (syms_of_xterm): New defsym. * src/xterm.h (struct x_display_info): New fields for new atoms and their names. --- src/xselect.c | 40 ++++++++++++++ src/xterm.c | 148 +++++++++++++++++++++++++++++++++++++++++++------- src/xterm.h | 15 ++++- 3 files changed, 182 insertions(+), 21 deletions(-) diff --git a/src/xselect.c b/src/xselect.c index 96c1e9830fb..fff79fb99f8 100644 --- a/src/xselect.c +++ b/src/xselect.c @@ -774,6 +774,25 @@ x_handle_selection_request (struct selection_input_event *event) bool success = false; specpdl_ref count = SPECPDL_INDEX (); bool pushed; + Lisp_Object alias, tem; + + alias = Vx_selection_alias_alist; + + FOR_EACH_TAIL_SAFE (alias) + { + tem = Qnil; + + if (CONSP (alias)) + tem = XCAR (alias); + + if (CONSP (tem) + && EQ (XCAR (tem), selection_symbol) + && SYMBOLP (XCDR (tem))) + { + selection_symbol = XCDR (tem); + break; + } + } pushed = false; @@ -2055,15 +2074,27 @@ On Nextstep, TIME-STAMP and TERMINAL are unused. */) Lisp_Object time_stamp, Lisp_Object terminal) { Lisp_Object val = Qnil; + Lisp_Object maybe_alias; struct frame *f = frame_for_x_selection (terminal); CHECK_SYMBOL (selection_symbol); CHECK_SYMBOL (target_type); + if (EQ (target_type, QMULTIPLE)) error ("Retrieving MULTIPLE selections is currently unimplemented"); if (!f) error ("X selection unavailable for this frame"); + /* Quitting inside this function is okay, so we don't have to use + FOR_EACH_TAIL_SAFE. */ + maybe_alias = Fassq (selection_symbol, Vx_selection_alias_alist); + + if (!NILP (maybe_alias)) + { + selection_symbol = XCDR (maybe_alias); + CHECK_SYMBOL (selection_symbol); + } + val = x_get_local_selection (selection_symbol, target_type, true, FRAME_DISPLAY_INFO (f)); @@ -2818,6 +2849,15 @@ If non-nil, selection converters for string types (`STRING', when Emacs itself is converting the selection. */); Vx_treat_local_requests_remotely = Qnil; + DEFVAR_LISP ("x-selection-alias-alist", Vx_selection_alias_alist, + doc: /* List of selections to alias to another. +It should be an alist of a selection name to another. When a +selection request arrives for the first selection, Emacs will respond +as if the request was meant for the other. + +Note that this does not affect setting or owning selections. */); + Vx_selection_alias_alist = Qnil; + /* QPRIMARY is defined in keyboard.c. */ DEFSYM (QSECONDARY, "SECONDARY"); DEFSYM (QSTRING, "STRING"); diff --git a/src/xterm.c b/src/xterm.c index f2f80b42be3..0e006321749 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -931,6 +931,7 @@ static const struct x_atom_ref x_atom_refs[] = ATOM_REFS_INIT ("CLIPBOARD_MANAGER", Xatom_CLIPBOARD_MANAGER) ATOM_REFS_INIT ("_XEMBED_INFO", Xatom_XEMBED_INFO) ATOM_REFS_INIT ("_MOTIF_WM_HINTS", Xatom_MOTIF_WM_HINTS) + ATOM_REFS_INIT ("_EMACS_DRAG_ATOM", Xatom_EMACS_DRAG_ATOM) /* For properties of font. */ ATOM_REFS_INIT ("PIXEL_SIZE", Xatom_PIXEL_SIZE) ATOM_REFS_INIT ("AVERAGE_WIDTH", Xatom_AVERAGE_WIDTH) @@ -1297,6 +1298,11 @@ static bool x_dnd_inside_handle_one_xevent; started. */ static int x_dnd_recursion_depth; +/* The cons cell containing the selection alias between the Motif drag + selection and `XdndSelection'. The car and cdr are only set when + initiating Motif drag-and-drop for the first time. */ +static Lisp_Object x_dnd_selection_alias_cell; + /* Structure describing a single window that can be the target of drag-and-drop operations. */ struct x_client_list_window @@ -2172,12 +2178,95 @@ xm_setup_dnd_targets (struct x_display_info *dpyinfo, return idx; } +static Atom +xm_get_drag_atom_1 (struct x_display_info *dpyinfo) +{ + Atom actual_type, atom; + unsigned long nitems, bytes_remaining; + unsigned char *tmp_data; + unsigned long inumber; + int rc, actual_format; + char *buffer; + + /* Make sure this operation is done atomically. */ + XGrabServer (dpyinfo->display); + + rc = XGetWindowProperty (dpyinfo->display, dpyinfo->root_window, + dpyinfo->Xatom_EMACS_DRAG_ATOM, + 0, 1, False, XA_CARDINAL, &actual_type, + &actual_format, &nitems, &bytes_remaining, + &tmp_data); + + if (rc == Success + && actual_format == 32 && nitems == 1 + && actual_type == XA_CARDINAL) + { + inumber = *(unsigned long *) tmp_data; + inumber &= 0xffffffff; + } + else + inumber = 0; + + if (tmp_data) + XFree (tmp_data); + + if (X_LONG_MAX - inumber < 1) + inumber = 0; + + inumber += 1; + buffer = dpyinfo->motif_drag_atom_name; + + /* FIXME: this interns a unique atom for every Emacs session. + Eventually the atoms simply pile up. It may be worth + implementing the Motif atoms table logic here. */ + sprintf (buffer, "_EMACS_ATOM_%lu", inumber); + atom = XInternAtom (dpyinfo->display, buffer, False); + + XChangeProperty (dpyinfo->display, dpyinfo->root_window, + dpyinfo->Xatom_EMACS_DRAG_ATOM, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) &inumber, 1); + + XUngrabServer (dpyinfo->display); + return atom; +} + +static Atom +xm_get_drag_atom (struct x_display_info *dpyinfo) +{ + Atom atom; + + if (dpyinfo->motif_drag_atom != None) + atom = dpyinfo->motif_drag_atom; + else + atom = xm_get_drag_atom_1 (dpyinfo); + + dpyinfo->motif_drag_atom = atom; + return atom; +} + static void xm_setup_drag_info (struct x_display_info *dpyinfo, struct frame *source_frame) { + Atom atom; xm_drag_initiator_info drag_initiator_info; - int idx; + int idx, rc; + + atom = xm_get_drag_atom (dpyinfo); + + x_catch_errors (dpyinfo->display); + XSetSelectionOwner (dpyinfo->display, atom, + FRAME_X_WINDOW (source_frame), + dpyinfo->last_user_time); + rc = x_had_errors_p (dpyinfo->display); + x_uncatch_errors_after_check (); + + if (rc) + return; + + XSETCAR (x_dnd_selection_alias_cell, + x_atom_to_symbol (dpyinfo, atom)); + XSETCDR (x_dnd_selection_alias_cell, QXdndSelection); idx = xm_setup_dnd_targets (dpyinfo, x_dnd_targets, x_dnd_n_targets); @@ -2187,10 +2276,10 @@ xm_setup_drag_info (struct x_display_info *dpyinfo, drag_initiator_info.byteorder = XM_BYTE_ORDER_CUR_FIRST; drag_initiator_info.protocol = XM_DRAG_PROTOCOL_VERSION; drag_initiator_info.table_index = idx; - drag_initiator_info.selection = dpyinfo->Xatom_XdndSelection; + drag_initiator_info.selection = atom; - xm_write_drag_initiator_info (dpyinfo->display, FRAME_X_WINDOW (source_frame), - dpyinfo->Xatom_XdndSelection, + xm_write_drag_initiator_info (dpyinfo->display, + FRAME_X_WINDOW (source_frame), atom, dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO, &drag_initiator_info); @@ -4334,7 +4423,7 @@ x_dnd_cleanup_drag_and_drop (void *frame) XM_DROP_ACTION_DROP_CANCEL); dmsg.x = 0; dmsg.y = 0; - dmsg.index_atom = FRAME_DISPLAY_INFO (f)->Xatom_XdndSelection; + dmsg.index_atom = xm_get_drag_atom (FRAME_DISPLAY_INFO (f)); dmsg.source_window = FRAME_X_WINDOW (f); x_dnd_send_xm_leave_for_drop (FRAME_DISPLAY_INFO (f), f, @@ -11189,6 +11278,16 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, record_unwind_protect_void (release_xg_select); #endif + /* Set up a meaningless alias. */ + XSETCAR (x_dnd_selection_alias_cell, QSECONDARY); + XSETCDR (x_dnd_selection_alias_cell, QSECONDARY); + + /* Bind this here. The cell doesn't actually alias between + anything until `xm_setup_dnd_targets' is called. */ + specbind (Qx_selection_alias_alist, + Fcons (x_dnd_selection_alias_cell, + Vx_selection_alias_alist)); + /* Initialize most of the state for the drag-and-drop operation. */ x_dnd_in_progress = true; x_dnd_recursion_depth = command_loop_level + minibuf_level; @@ -11392,7 +11491,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, XM_DROP_ACTION_DROP_CANCEL); dmsg.x = 0; dmsg.y = 0; - dmsg.index_atom = FRAME_DISPLAY_INFO (f)->Xatom_XdndSelection; + dmsg.index_atom = xm_get_drag_atom (FRAME_DISPLAY_INFO (f)); dmsg.source_window = FRAME_X_WINDOW (f); x_dnd_send_xm_leave_for_drop (FRAME_DISPLAY_INFO (f), f, @@ -11430,7 +11529,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, /* Delete the Motif drag initiator info if it was set up. */ if (x_dnd_motif_setup_p) XDeleteProperty (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - FRAME_DISPLAY_INFO (f)->Xatom_XdndSelection); + xm_get_drag_atom (FRAME_DISPLAY_INFO (f))); /* Remove any type list set as well. */ @@ -11485,7 +11584,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, XM_DROP_ACTION_DROP_CANCEL); dmsg.x = 0; dmsg.y = 0; - dmsg.index_atom = FRAME_DISPLAY_INFO (f)->Xatom_XdndSelection; + dmsg.index_atom = xm_get_drag_atom (FRAME_DISPLAY_INFO (f)); dmsg.source_window = FRAME_X_WINDOW (f); x_dnd_send_xm_leave_for_drop (FRAME_DISPLAY_INFO (f), f, @@ -11522,7 +11621,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, /* Delete the Motif drag initiator info if it was set up. */ if (x_dnd_motif_setup_p) XDeleteProperty (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - FRAME_DISPLAY_INFO (f)->Xatom_XdndSelection); + xm_get_drag_atom (FRAME_DISPLAY_INFO (f))); /* Remove any type list set as well. */ @@ -11566,7 +11665,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, /* Delete the Motif drag initiator info if it was set up. */ if (x_dnd_motif_setup_p) XDeleteProperty (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - FRAME_DISPLAY_INFO (f)->Xatom_XdndSelection); + xm_get_drag_atom (FRAME_DISPLAY_INFO (f))); /* Remove any type list set as well. */ if (x_dnd_init_type_lists && x_dnd_n_targets > 3) @@ -15629,7 +15728,7 @@ x_dnd_update_state (struct x_display_info *dpyinfo, Time timestamp) emsg.zero = 0; emsg.timestamp = timestamp; emsg.source_window = FRAME_X_WINDOW (x_dnd_frame); - emsg.index_atom = dpyinfo->Xatom_XdndSelection; + emsg.index_atom = xm_get_drag_atom (dpyinfo); if (x_dnd_motif_setup_p) xm_send_top_level_enter_message (dpyinfo, FRAME_X_WINDOW (x_dnd_frame), @@ -15701,8 +15800,7 @@ x_dnd_update_state (struct x_display_info *dpyinfo, Time timestamp) XM_DROP_ACTION_DROP_CANCEL); dsmsg.x = 0; dsmsg.y = 0; - dsmsg.index_atom - = FRAME_DISPLAY_INFO (x_dnd_frame)->Xatom_XdndSelection; + dsmsg.index_atom = xm_get_drag_atom (dpyinfo); dsmsg.source_window = FRAME_X_WINDOW (x_dnd_frame); x_dnd_send_xm_leave_for_drop (dpyinfo, x_dnd_frame, @@ -16502,7 +16600,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, if (x_dnd_waiting_for_finish && x_dnd_waiting_for_motif_finish == 2 && dpyinfo == x_dnd_waiting_for_motif_finish_display - && eventp->selection == dpyinfo->Xatom_XdndSelection + && eventp->selection == xm_get_drag_atom (dpyinfo) && (eventp->target == dpyinfo->Xatom_XmTRANSFER_SUCCESS || eventp->target == dpyinfo->Xatom_XmTRANSFER_FAILURE)) { @@ -17911,7 +18009,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, emsg.zero = 0; emsg.timestamp = event->xbutton.time; emsg.source_window = FRAME_X_WINDOW (x_dnd_frame); - emsg.index_atom = dpyinfo->Xatom_XdndSelection; + emsg.index_atom = xm_get_drag_atom (dpyinfo); if (x_dnd_motif_setup_p) xm_send_top_level_enter_message (dpyinfo, FRAME_X_WINDOW (x_dnd_frame), @@ -18525,7 +18623,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, dmsg.timestamp = event->xbutton.time; dmsg.x = event->xbutton.x_root; dmsg.y = event->xbutton.y_root; - dmsg.index_atom = dpyinfo->Xatom_XdndSelection; + dmsg.index_atom = xm_get_drag_atom (dpyinfo); dmsg.source_window = FRAME_X_WINDOW (x_dnd_frame); if (!XM_DRAG_STYLE_IS_DROP_ONLY (drag_receiver_info.protocol_style)) @@ -19636,7 +19734,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, emsg.zero = 0; emsg.timestamp = xev->time; emsg.source_window = FRAME_X_WINDOW (x_dnd_frame); - emsg.index_atom = dpyinfo->Xatom_XdndSelection; + emsg.index_atom = xm_get_drag_atom (dpyinfo); if (x_dnd_motif_setup_p) xm_send_top_level_enter_message (dpyinfo, FRAME_X_WINDOW (x_dnd_frame), @@ -19932,7 +20030,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, instances of Emacs try to drag into the same window at the same time. */ - dmsg.index_atom = dpyinfo->Xatom_XdndSelection; + dmsg.index_atom = xm_get_drag_atom (dpyinfo); dmsg.source_window = FRAME_X_WINDOW (x_dnd_frame); if (!XM_DRAG_STYLE_IS_DROP_ONLY (drag_receiver_info.protocol_style)) @@ -22823,7 +22921,7 @@ x_connection_closed (Display *dpy, const char *error_message, bool ioerror) XM_DROP_ACTION_DROP_CANCEL); dmsg.x = 0; dmsg.y = 0; - dmsg.index_atom = FRAME_DISPLAY_INFO (f)->Xatom_XdndSelection; + dmsg.index_atom = xm_get_drag_atom (FRAME_DISPLAY_INFO (f)); dmsg.source_window = FRAME_X_WINDOW (f); x_dnd_send_xm_leave_for_drop (FRAME_DISPLAY_INFO (f), f, @@ -25253,6 +25351,10 @@ x_intern_cached_atom (struct x_display_info *dpyinfo, if (!strcmp (name, "WINDOW")) return XA_WINDOW; + if (dpyinfo->motif_drag_atom != None + && !strcmp (name, dpyinfo->motif_drag_atom_name)) + return dpyinfo->motif_drag_atom; + for (i = 0; i < ARRAYELTS (x_atom_refs); ++i) { ptr = (char *) dpyinfo; @@ -25311,6 +25413,10 @@ x_get_atom_name (struct x_display_info *dpyinfo, Atom atom, return xstrdup ("WINDOW"); default: + if (dpyinfo->motif_drag_atom + && atom == dpyinfo->motif_drag_atom) + return xstrdup (dpyinfo->motif_drag_atom_name); + if (atom == dpyinfo->Xatom_xsettings_sel) { sprintf (buffer, "_XSETTINGS_S%d", @@ -27203,6 +27309,9 @@ syms_of_xterm (void) x_dnd_action_symbol = Qnil; staticpro (&x_dnd_action_symbol); + x_dnd_selection_alias_cell = Fcons (Qnil, Qnil); + staticpro (&x_dnd_selection_alias_cell); + DEFSYM (Qvendor_specific_keysyms, "vendor-specific-keysyms"); DEFSYM (Qlatin_1, "latin-1"); DEFSYM (Qnow, "now"); @@ -27291,6 +27400,7 @@ With MS Windows, Haiku windowing or Nextstep, the value is t. */); DEFSYM (Qsuper, "super"); Fput (Qsuper, Qmodifier_value, make_fixnum (super_modifier)); DEFSYM (QXdndSelection, "XdndSelection"); + DEFSYM (Qx_selection_alias_alist, "x-selection-alias-alist"); DEFVAR_LISP ("x-ctrl-keysym", Vx_ctrl_keysym, doc: /* Which keys Emacs uses for the ctrl modifier. diff --git a/src/xterm.h b/src/xterm.h index d710069fadd..9df1feae5bc 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -428,8 +428,8 @@ struct x_display_info /* More atoms for font properties. The last three are private properties, see the comments in src/fontset.h. */ Atom Xatom_PIXEL_SIZE, Xatom_AVERAGE_WIDTH, - Xatom_MULE_BASELINE_OFFSET, Xatom_MULE_RELATIVE_COMPOSE, - Xatom_MULE_DEFAULT_ASCENT; + Xatom_MULE_BASELINE_OFFSET, Xatom_MULE_RELATIVE_COMPOSE, + Xatom_MULE_DEFAULT_ASCENT; /* More atoms for Ghostscript support. */ Atom Xatom_DONE, Xatom_PAGE; @@ -448,6 +448,9 @@ struct x_display_info Xatom_MOTIF_DRAG_TARGETS, Xatom_MOTIF_DRAG_AND_DROP_MESSAGE, Xatom_MOTIF_DRAG_INITIATOR_INFO, Xatom_MOTIF_DRAG_RECEIVER_INFO; + /* Atoms used by Emacs internally. */ + Atom Xatom_EMACS_DRAG_ATOM; + /* Special selections used by the Motif drop protocol to indicate success or failure. */ Atom Xatom_XmTRANSFER_SUCCESS, Xatom_XmTRANSFER_FAILURE; @@ -562,6 +565,14 @@ struct x_display_info ptrdiff_t x_dnd_atoms_size; ptrdiff_t x_dnd_atoms_length; + /* The unique drag and drop atom used on Motif. None if it was not + already computed. */ + Atom motif_drag_atom; + + /* Its name. */ + char motif_drag_atom_name[sizeof "_EMACS_ATOM_%lu" - 3 + + INT_STRLEN_BOUND (unsigned long)]; + /* Extended window manager hints, Atoms supported by the window manager and atoms for setting the window type. */ Atom Xatom_net_supported, Xatom_net_supporting_wm_check; -- 2.39.2