Implement missing parts of the Motif drag and drop protocol

* src/xterm.c (xm_drop_start_reply): New structure.
(xm_get_drag_window): Don't grab the server since this leads to
weird freezes when creating the drag window.
(xm_read_drop_start_reply): New function.
(x_dnd_begin_drag_and_drop): Set Motif finish flag to 0.
(handle_one_xevent): When starting a motif drop, set the finish
flag to 1.  When the receiver replies to our drop message, set
the finish flag to 2 if the drop was accepted, and only clear
the waiting for finish flag when a selection request for
XmTRANSFER_SUCCESS or XmTRANSFER_FAILURE arrives.
(x_term_init): New atoms.
* src/xterm.h (struct x_display_info): New atoms.
This commit is contained in:
Po Lu 2022-03-31 21:28:09 +08:00
parent af0ea35ea0
commit 1bd1438702
2 changed files with 148 additions and 23 deletions

View file

@ -807,6 +807,13 @@ static int x_filter_event (struct x_display_info *, XEvent *);
static bool x_dnd_in_progress;
static bool x_dnd_waiting_for_finish;
/* 0 means nothing has happened. 1 means an XmDROP_START message was
sent to the target, but no response has yet been received. 2 means
a response to our XmDROP_START message was received and the target
accepted the drop, so Emacs should start waiting for the drop
target to convert one of the special selections XmTRANSFER_SUCCESS
or XmTRANSFER_FAILURE. */
static int x_dnd_waiting_for_motif_finish;
static Window x_dnd_pending_finish_target;
static int x_dnd_waiting_for_finish_proto;
static bool x_dnd_allow_current_frame;
@ -920,6 +927,16 @@ typedef struct xm_drop_start_message
/* CARD32 */ uint32_t source_window;
} xm_drop_start_message;
typedef struct xm_drop_start_reply
{
/* BYTE */ uint8_t reason;
/* BYTE */ uint8_t byte_order;
/* CARD16 */ uint16_t side_effects;
/* CARD16 */ uint16_t better_x;
/* CARD16 */ uint16_t better_y;
} xm_drop_start_reply;
typedef struct xm_drag_initiator_info
{
/* BYTE */ uint8_t byteorder;
@ -942,33 +959,38 @@ typedef struct xm_drag_receiver_info
} xm_drag_receiver_info;
#define XM_DRAG_SIDE_EFFECT(op, site, ops, act) \
((op) | ((site) << 4) | ((ops) << 8) | ((act) << 16))
((op) | ((site) << 4) | ((ops) << 8) | ((act) << 12))
/* Some of the macros below are temporarily unused. */
/* #define XM_DRAG_SIDE_EFFECT_OPERATION(effect) ((effect) & 0xf) */
/* #define XM_DRAG_SIDE_EFFECT_SITE_STATUS(effect) (((effect) & 0xf0) >> 4) */
#define XM_DRAG_SIDE_EFFECT_OPERATION(effect) ((effect) & 0xf)
#define XM_DRAG_SIDE_EFFECT_SITE_STATUS(effect) (((effect) & 0xf0) >> 4)
/* #define XM_DRAG_SIDE_EFFECT_OPERATIONS(effect) (((effect) & 0xf00) >> 8) */
/* #define XM_DRAG_SIDE_EFFECT_DROP_ACTION(effect) (((effect) & 0xf000) >> 16) */
#define XM_DRAG_SIDE_EFFECT_DROP_ACTION(effect) (((effect) & 0xf000) >> 12)
#define XM_DRAG_NOOP 0
#define XM_DRAG_MOVE (1L << 0)
#define XM_DRAG_COPY (1L << 1)
#define XM_DRAG_LINK (1L << 2)
#define XM_DROP_ACTION_DROP 0
#define XM_DROP_SITE_VALID 1
#define XM_DROP_ACTION_DROP 0
/* #define XM_DROP_ACTION_DROP_HELP 1 */
#define XM_DROP_ACTION_DROP_CANCEL 2
#define XM_DRAG_REASON(originator, code) ((code) | ((originator) << 7))
/* #define XM_DRAG_REASON_ORIGINATOR(reason) (((reason) & 0x80) ? 1 : 0) */
/* #define XM_DRAG_REASON_CODE(reason) ((reason) & 0x7f) */
#define XM_DRAG_REASON_ORIGINATOR(reason) (((reason) & 0x80) ? 1 : 0)
#define XM_DRAG_REASON_CODE(reason) ((reason) & 0x7f)
#define XM_DRAG_REASON_DROP_START 5
#define XM_DRAG_ORIGINATOR_INITIATOR 0
/* #define XM_DRAG_ORIGINATOR_RECEIVER 1 */
#define XM_DRAG_ORIGINATOR_RECEIVER 1
#define XM_DRAG_STYLE_NONE 0
#define XM_DROP_SITE_VALID 3
/* #define XM_DROP_SITE_INVALID 2 */
/* #define XM_DROP_SITE_NONE 1 */
static uint8_t
xm_side_effect_from_action (struct x_display_info *dpyinfo, Atom action)
{
@ -1150,7 +1172,6 @@ xm_get_drag_window (struct x_display_info *dpyinfo)
Display *temp_display;
drag_window = None;
XGrabServer (dpyinfo->display);
rc = XGetWindowProperty (dpyinfo->display, dpyinfo->root_window,
dpyinfo->Xatom_MOTIF_DRAG_WINDOW,
0, 1, False, XA_WINDOW, &actual_type,
@ -1177,8 +1198,6 @@ xm_get_drag_window (struct x_display_info *dpyinfo)
XFree (tmp_data);
}
XUngrabServer (dpyinfo->display);
if (drag_window == None)
{
unrequest_sigio ();
@ -1189,8 +1208,6 @@ xm_get_drag_window (struct x_display_info *dpyinfo)
return None;
XSetCloseDownMode (temp_display, RetainPermanent);
XGrabServer (temp_display);
attrs.override_redirect = True;
drag_window = XCreateWindow (temp_display, DefaultRootWindow (temp_display),
-1, -1, 1, 1, 0, CopyFromParent, InputOnly,
@ -1200,7 +1217,6 @@ xm_get_drag_window (struct x_display_info *dpyinfo)
"_MOTIF_DRAG_WINDOW", False),
XA_WINDOW, 32, PropModeReplace,
(unsigned char *) &drag_window, 1);
XUngrabServer (temp_display);
XCloseDisplay (temp_display);
/* Make sure the drag window created is actually valid for the
@ -1396,6 +1412,37 @@ xm_send_drop_message (struct x_display_info *dpyinfo, Window source,
x_uncatch_errors ();
}
static int
xm_read_drop_start_reply (XEvent *msg, xm_drop_start_reply *reply)
{
uint8_t *data;
data = (uint8_t *) &msg->xclient.data.b[0];
if ((XM_DRAG_REASON_ORIGINATOR (data[0])
!= XM_DRAG_ORIGINATOR_RECEIVER)
|| (XM_DRAG_REASON_CODE (data[0])
!= XM_DRAG_REASON_DROP_START))
return 1;
reply->reason = *(data++);
reply->byte_order = *(data++);
reply->side_effects = *(uint16_t *) data;
reply->better_x = *(uint16_t *) (data + 2);
reply->better_y = *(uint16_t *) (data + 4);
if (reply->byte_order != XM_TARGETS_TABLE_CUR)
{
SWAPCARD16 (reply->side_effects);
SWAPCARD16 (reply->better_x);
SWAPCARD16 (reply->better_y);
}
reply->byte_order = XM_TARGETS_TABLE_CUR;
return 0;
}
static int
xm_read_drag_receiver_info (struct x_display_info *dpyinfo,
Window wdesc, xm_drag_receiver_info *rec)
@ -8475,6 +8522,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
x_dnd_wanted_action = xaction;
x_dnd_return_frame = 0;
x_dnd_waiting_for_finish = false;
x_dnd_waiting_for_motif_finish = 0;
x_dnd_end_window = None;
x_dnd_use_toplevels
= x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_client_list_stacking);
@ -12384,7 +12432,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
if (event->xclient.message_type == dpyinfo->Xatom_XdndFinished
&& x_dnd_waiting_for_finish
&& (x_dnd_waiting_for_finish && !x_dnd_waiting_for_motif_finish)
&& event->xclient.data.l[0] == x_dnd_pending_finish_target)
{
x_dnd_waiting_for_finish = false;
@ -12397,6 +12445,59 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_dnd_action = None;
}
if ((event->xclient.message_type
== dpyinfo->Xatom_MOTIF_DRAG_AND_DROP_MESSAGE)
/* FIXME: There should probably be a check that the event
comes from the same display where the drop event was
sent, but there's no way to get that information here
safely. */
&& x_dnd_waiting_for_finish
&& x_dnd_waiting_for_motif_finish == 1)
{
xm_drop_start_reply reply;
uint16_t operation, status, action;
if (!xm_read_drop_start_reply (event, &reply))
{
operation = XM_DRAG_SIDE_EFFECT_OPERATION (reply.side_effects);
status = XM_DRAG_SIDE_EFFECT_SITE_STATUS (reply.side_effects);
action = XM_DRAG_SIDE_EFFECT_DROP_ACTION (reply.side_effects);
if (operation != XM_DRAG_MOVE
&& operation != XM_DRAG_COPY
&& operation != XM_DRAG_LINK)
{
x_dnd_waiting_for_finish = false;
goto OTHER;
}
if (status != XM_DROP_SITE_VALID
|| action == XM_DROP_ACTION_DROP_CANCEL)
{
x_dnd_waiting_for_finish = false;
goto OTHER;
}
switch (operation)
{
case XM_DRAG_MOVE:
x_dnd_action = dpyinfo->Xatom_XdndActionMove;
break;
case XM_DRAG_COPY:
x_dnd_action = dpyinfo->Xatom_XdndActionCopy;
break;
case XM_DRAG_LINK:
x_dnd_action = dpyinfo->Xatom_XdndActionLink;
break;
}
x_dnd_waiting_for_motif_finish = 2;
goto OTHER;
}
}
if (event->xclient.message_type == dpyinfo->Xatom_wm_protocols
&& event->xclient.format == 32)
{
@ -12703,6 +12804,13 @@ handle_one_xevent (struct x_display_info *dpyinfo,
*hold_quit = inev.ie;
EVENT_INIT (inev.ie);
}
if (x_dnd_waiting_for_finish
&& x_dnd_waiting_for_motif_finish == 2
&& eventp->selection == dpyinfo->Xatom_XdndSelection
&& (eventp->target == dpyinfo->Xatom_XmTRANSFER_SUCCESS
|| eventp->target == dpyinfo->Xatom_XmTRANSFER_FAILURE))
x_dnd_waiting_for_finish = false;
}
break;
@ -14197,7 +14305,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
xm_write_drag_initiator_info (dpyinfo->display,
FRAME_X_WINDOW (x_dnd_frame),
dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO,
dpyinfo->Xatom_XdndSelection,
dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO,
&drag_initiator_info);
@ -14207,18 +14315,19 @@ handle_one_xevent (struct x_display_info *dpyinfo,
dmsg.side_effects
= XM_DRAG_SIDE_EFFECT (xm_side_effect_from_action (dpyinfo,
x_dnd_wanted_action),
XM_DROP_SITE_VALID,
xm_side_effect_from_action (dpyinfo,
x_dnd_wanted_action),
XM_DROP_SITE_VALID, XM_DRAG_NOOP,
XM_DROP_ACTION_DROP);
dmsg.timestamp = event->xbutton.time;
dmsg.x = event->xbutton.x_root;
dmsg.y = event->xbutton.y_root;
dmsg.index_atom = dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO;
dmsg.index_atom = dpyinfo->Xatom_XdndSelection;
dmsg.source_window = FRAME_X_WINDOW (x_dnd_frame);
xm_send_drop_message (dpyinfo, FRAME_X_WINDOW (x_dnd_frame),
x_dnd_last_seen_window, &dmsg);
x_dnd_waiting_for_finish = true;
x_dnd_waiting_for_motif_finish = 1;
}
}
}
@ -15248,7 +15357,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
xm_write_drag_initiator_info (dpyinfo->display,
FRAME_X_WINDOW (x_dnd_frame),
dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO,
dpyinfo->Xatom_XdndSelection,
dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO,
&drag_initiator_info);
@ -15265,11 +15374,23 @@ handle_one_xevent (struct x_display_info *dpyinfo,
dmsg.timestamp = xev->time;
dmsg.x = lrint (xev->root_x);
dmsg.y = lrint (xev->root_y);
dmsg.index_atom = dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO;
/* This atom technically has to be
unique to each drag-and-drop
operation, but that isn't easy to
accomplish, since we cannot
randomly move data around between
selections. Let's hope no two
instances of Emacs try to drag
into the same window at the same
time. */
dmsg.index_atom = dpyinfo->Xatom_XdndSelection;
dmsg.source_window = FRAME_X_WINDOW (x_dnd_frame);
xm_send_drop_message (dpyinfo, FRAME_X_WINDOW (x_dnd_frame),
x_dnd_last_seen_window, &dmsg);
x_dnd_waiting_for_finish = true;
x_dnd_waiting_for_motif_finish = 1;
}
}
}
@ -21140,6 +21261,8 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
Xatom_MOTIF_DRAG_INITIATOR_INFO)
ATOM_REFS_INIT ("_MOTIF_DRAG_RECEIVER_INFO",
Xatom_MOTIF_DRAG_RECEIVER_INFO)
ATOM_REFS_INIT ("XmTRANSFER_SUCCESS", Xatom_XmTRANSFER_SUCCESS)
ATOM_REFS_INIT ("XmTRANSFER_FAILURE", Xatom_XmTRANSFER_FAILURE)
};
int i;

View file

@ -437,6 +437,8 @@ struct x_display_info
Xatom_MOTIF_DRAG_TARGETS, Xatom_MOTIF_DRAG_AND_DROP_MESSAGE,
Xatom_MOTIF_DRAG_INITIATOR_INFO, Xatom_MOTIF_DRAG_RECEIVER_INFO;
Atom Xatom_XmTRANSFER_SUCCESS, Xatom_XmTRANSFER_FAILURE;
/* The frame (if any) which has the X window that has keyboard focus.
Zero if none. This is examined by Ffocus_frame in xfns.c. Note
that a mere EnterNotify event can set this; if you need to know the