From: Po Lu Date: Thu, 31 Mar 2022 13:28:09 +0000 (+0800) Subject: Implement missing parts of the Motif drag and drop protocol X-Git-Tag: emacs-29.0.90~1931^2~850 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=1bd14387027d5fa93ccbc38b6e4ce715c916bbc6;p=emacs.git 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. --- diff --git a/src/xterm.c b/src/xterm.c index 81b84c8609c..2f53bb469b3 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -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; diff --git a/src/xterm.h b/src/xterm.h index eb9e25d3cdd..062b34b35ce 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -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