1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-21 06:55:39 +00:00

Implement Motif drop protocol

This is the second most widely implemented drag-and-drop
protocol on X Windows, but seems to have some unsolvable
problems (i.e. stuff will keep accumulating in the drag window
as long the target lists keep changing.)  The implementation is
not yet complete and doesn't work with some programs.

* lisp/select.el (xselect-convert-xm-special): New functions.
(selection-converter-alist): Add new converters.
* lisp/x-dnd.el (x-dnd-handle-motif): Ignore messages sent by
the receiver.
* src/xterm.c (xm_targets_table_byte_order): New enum;
(SWAPCARD32, SWAPCARD16): New macros.
(xm_targets_table_rec, xm_drop_start_message)
(xm_drag_initiator_info, xm_drag_receiver_info): New structures.
(XM_DRAG_SIDE_EFFECT, xm_read_targets_table_header)
(xm_read_targets_table_rec, xm_find_targets_table_idx)
(x_atoms_compare, xm_write_targets_table)
(xm_write_drag_initiator_info, xm_get_drag_window)
(xm_setup_dnd_targets, xm_send_drop_message)
(xm_read_drag_receiver_info): New functions.
(x_dnd_compute_toplevels): Correctly free some temp data.
(x_dnd_get_window_proxy, x_dnd_get_window_proto)
(x_set_frame_alpha): Likewise.
(handle_one_xevent): If the window has no XDND proto but has
motif drag receiver data, send a motif drop protocol request.
(x_term_init): New atoms for Motif DND support.
* src/xterm.h (struct x_display_info): Add new atoms.
This commit is contained in:
Po Lu 2022-03-31 17:21:37 +08:00
parent 6f973faa91
commit 1d4306a8a7
4 changed files with 895 additions and 173 deletions

View File

@ -655,6 +655,9 @@ VALUE is the local selection value of SELECTION."
(stringp value)
(file-exists-p value)))
(defun xselect-convert-xm-special (_selection _type _value)
"")
(setq selection-converter-alist
'((TEXT . xselect-convert-to-string)
(COMPOUND_TEXT . xselect-convert-to-string)
@ -679,7 +682,9 @@ VALUE is the local selection value of SELECTION."
(ATOM . xselect-convert-to-atom)
(INTEGER . xselect-convert-to-integer)
(SAVE_TARGETS . xselect-convert-to-save-targets)
(_EMACS_INTERNAL . xselect-convert-to-identity)))
(_EMACS_INTERNAL . xselect-convert-to-identity)
(XmTRANSFER_SUCCESS . xselect-convert-xm-special)
(XmTRANSFER_FAILURE . xselect-convert-xm-special)))
(provide 'select)

View File

@ -603,174 +603,177 @@ FORMAT is 32 (not used). MESSAGE is the data part of an XClientMessageEvent."
(2 . private)) ; Motif does not have private, so use copy for private.
"Mapping from number to operation for Motif DND.")
(defun x-dnd-handle-motif (event frame window message-atom _format data)
(let* ((message-type (cdr (assoc (aref data 0) x-dnd-motif-message-types)))
(defun x-dnd-handle-motif (event frame window _message-atom _format data)
(let* ((message-type (cdr (assoc (logand (aref data 0) #x3f)
x-dnd-motif-message-types)))
(initiator-p (eq (lsh (aref data 0) -7) 0))
(source-byteorder (aref data 1))
(my-byteorder (byteorder))
(source-flags (x-dnd-get-motif-value data 2 2 source-byteorder))
(source-action (cdr (assoc (logand ?\xF source-flags)
x-dnd-motif-to-action))))
(cond ((eq message-type 'XmTOP_LEVEL_ENTER)
(let* ((dnd-source (x-dnd-get-motif-value
data 8 4 source-byteorder))
(selection-atom (x-dnd-get-motif-value
data 12 4 source-byteorder))
(atom-name (x-get-atom-name selection-atom))
(types (when atom-name
(x-get-selection-internal (intern atom-name)
'TARGETS))))
(x-dnd-forget-drop frame)
(when types (x-dnd-save-state window nil nil
types
dnd-source))))
(when initiator-p
(cond ((eq message-type 'XmTOP_LEVEL_ENTER)
(let* ((dnd-source (x-dnd-get-motif-value
data 8 4 source-byteorder))
(selection-atom (x-dnd-get-motif-value
data 12 4 source-byteorder))
(atom-name (x-get-atom-name selection-atom))
(types (when atom-name
(x-get-selection-internal (intern atom-name)
'TARGETS))))
(x-dnd-forget-drop frame)
(when types (x-dnd-save-state window nil nil
types
dnd-source))))
;; Can not forget drop here, LEAVE comes before DROP_START and
;; we need the state in DROP_START.
((eq message-type 'XmTOP_LEVEL_LEAVE)
nil)
;; Can not forget drop here, LEAVE comes before DROP_START and
;; we need the state in DROP_START.
((eq message-type 'XmTOP_LEVEL_LEAVE)
nil)
((eq message-type 'XmDRAG_MOTION)
(let* ((state (x-dnd-get-state-for-frame frame))
(timestamp (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 4 4
source-byteorder)
4 my-byteorder))
(x (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 8 2 source-byteorder)
((eq message-type 'XmDRAG_MOTION)
(let* ((state (x-dnd-get-state-for-frame frame))
(timestamp (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 4 4
source-byteorder)
4 my-byteorder))
(x (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 8 2 source-byteorder)
2 my-byteorder))
(y (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 10 2 source-byteorder)
2 my-byteorder))
(dnd-source (aref state 6))
(first-move (not (aref state 3)))
(action-type (x-dnd-maybe-call-test-function
window
source-action))
(reply-action (car (rassoc (car action-type)
x-dnd-motif-to-action)))
(reply-flags
(x-dnd-motif-value-to-list
(if reply-action
(+ reply-action
?\x30 ; 30: valid drop site
?\x700) ; 700: can do copy, move or link
?\x30) ; 30: drop site, but noop.
2 my-byteorder))
(y (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 10 2 source-byteorder)
(reply (append
(list
(+ ?\x80 ; 0x80 indicates a reply.
(if first-move
3 ; First time, reply is SITE_ENTER.
2)) ; Not first time, reply is DRAG_MOTION.
my-byteorder)
reply-flags
timestamp
x
y)))
(x-send-client-message frame
dnd-source
frame
"_MOTIF_DRAG_AND_DROP_MESSAGE"
8
reply)))
((eq message-type 'XmOPERATION_CHANGED)
(let* ((state (x-dnd-get-state-for-frame frame))
(timestamp (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 4 4 source-byteorder)
4 my-byteorder))
(dnd-source (aref state 6))
(action-type (x-dnd-maybe-call-test-function
window
source-action))
(reply-action (car (rassoc (car action-type)
x-dnd-motif-to-action)))
(reply-flags
(x-dnd-motif-value-to-list
(if reply-action
(+ reply-action
?\x30 ; 30: valid drop site
?\x700) ; 700: can do copy, move or link
?\x30) ; 30: drop site, but noop
2 my-byteorder))
(dnd-source (aref state 6))
(first-move (not (aref state 3)))
(action-type (x-dnd-maybe-call-test-function
window
source-action))
(reply-action (car (rassoc (car action-type)
x-dnd-motif-to-action)))
(reply-flags
(x-dnd-motif-value-to-list
(if reply-action
(+ reply-action
?\x30 ; 30: valid drop site
?\x700) ; 700: can do copy, move or link
?\x30) ; 30: drop site, but noop.
2 my-byteorder))
(reply (append
(list
(+ ?\x80 ; 0x80 indicates a reply.
(if first-move
3 ; First time, reply is SITE_ENTER.
2)) ; Not first time, reply is DRAG_MOTION.
my-byteorder)
reply-flags
timestamp
x
y)))
(x-send-client-message frame
dnd-source
frame
"_MOTIF_DRAG_AND_DROP_MESSAGE"
8
reply)))
(reply (append
(list
(+ ?\x80 ; 0x80 indicates a reply.
8) ; 8 is OPERATION_CHANGED
my-byteorder)
reply-flags
timestamp)))
(x-send-client-message frame
dnd-source
frame
"_MOTIF_DRAG_AND_DROP_MESSAGE"
8
reply)))
((eq message-type 'XmOPERATION_CHANGED)
(let* ((state (x-dnd-get-state-for-frame frame))
(timestamp (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 4 4 source-byteorder)
4 my-byteorder))
(dnd-source (aref state 6))
(action-type (x-dnd-maybe-call-test-function
window
source-action))
(reply-action (car (rassoc (car action-type)
x-dnd-motif-to-action)))
(reply-flags
(x-dnd-motif-value-to-list
(if reply-action
(+ reply-action
?\x30 ; 30: valid drop site
?\x700) ; 700: can do copy, move or link
?\x30) ; 30: drop site, but noop
2 my-byteorder))
(reply (append
(list
(+ ?\x80 ; 0x80 indicates a reply.
8) ; 8 is OPERATION_CHANGED
my-byteorder)
reply-flags
timestamp)))
(x-send-client-message frame
dnd-source
frame
"_MOTIF_DRAG_AND_DROP_MESSAGE"
8
reply)))
((eq message-type 'XmDROP_START)
(let* ((x (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 8 2 source-byteorder)
((eq message-type 'XmDROP_START)
(let* ((x (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 8 2 source-byteorder)
2 my-byteorder))
(y (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 10 2 source-byteorder)
2 my-byteorder))
(selection-atom (x-dnd-get-motif-value
data 12 4 source-byteorder))
(atom-name (x-get-atom-name selection-atom))
(dnd-source (x-dnd-get-motif-value
data 16 4 source-byteorder))
(action-type (x-dnd-maybe-call-test-function
window
source-action))
(reply-action (car (rassoc (car action-type)
x-dnd-motif-to-action)))
(reply-flags
(x-dnd-motif-value-to-list
(if reply-action
(+ reply-action
?\x30 ; 30: valid drop site
?\x700) ; 700: can do copy, move or link
(+ ?\x30 ; 30: drop site, but noop.
?\x200)) ; 200: drop cancel.
2 my-byteorder))
(y (x-dnd-motif-value-to-list
(x-dnd-get-motif-value data 10 2 source-byteorder)
2 my-byteorder))
(selection-atom (x-dnd-get-motif-value
data 12 4 source-byteorder))
(atom-name (x-get-atom-name selection-atom))
(dnd-source (x-dnd-get-motif-value
data 16 4 source-byteorder))
(action-type (x-dnd-maybe-call-test-function
window
source-action))
(reply-action (car (rassoc (car action-type)
x-dnd-motif-to-action)))
(reply-flags
(x-dnd-motif-value-to-list
(if reply-action
(+ reply-action
?\x30 ; 30: valid drop site
?\x700) ; 700: can do copy, move or link
(+ ?\x30 ; 30: drop site, but noop.
?\x200)) ; 200: drop cancel.
2 my-byteorder))
(reply (append
(list
(+ ?\x80 ; 0x80 indicates a reply.
5) ; DROP_START.
my-byteorder)
reply-flags
x
y))
(timestamp (x-dnd-get-motif-value
data 4 4 source-byteorder))
action)
(reply (append
(list
(+ ?\x80 ; 0x80 indicates a reply.
5) ; DROP_START.
my-byteorder)
reply-flags
x
y))
(timestamp (x-dnd-get-motif-value
data 4 4 source-byteorder))
action)
(x-send-client-message frame
dnd-source
frame
"_MOTIF_DRAG_AND_DROP_MESSAGE"
8
reply)
(setq action
(when (and reply-action atom-name)
(let* ((value (x-get-selection-internal
(intern atom-name)
(intern (x-dnd-current-type window)))))
(when value
(condition-case info
(x-dnd-drop-data event frame window value
(x-dnd-current-type window))
(error
(message "Error: %s" info)
nil))))))
(x-get-selection-internal
(intern atom-name)
(if action 'XmTRANSFER_SUCCESS 'XmTRANSFER_FAILURE)
timestamp)
(x-dnd-forget-drop frame)))
(x-send-client-message frame
dnd-source
frame
"_MOTIF_DRAG_AND_DROP_MESSAGE"
8
reply)
(setq action
(when (and reply-action atom-name)
(let* ((value (x-get-selection-internal
(intern atom-name)
(intern (x-dnd-current-type window)))))
(when value
(condition-case info
(x-dnd-drop-data event frame window value
(x-dnd-current-type window))
(error
(message "Error: %s" info)
nil))))))
(x-get-selection-internal
(intern atom-name)
(if action 'XmTRANSFER_SUCCESS 'XmTRANSFER_FAILURE)
timestamp)
(x-dnd-forget-drop frame)))
(t (error "Unknown Motif DND message %s %s" message-atom data)))))
(t (message "Unknown Motif drag-and-drop message: %s" (logand (aref data 0) #x3f)))))))
;;;

View File

@ -861,6 +861,594 @@ struct x_client_list_window
static struct x_client_list_window *x_dnd_toplevels = NULL;
static bool x_dnd_use_toplevels;
/* Motif drag-and-drop protocol support. */
typedef enum xm_targets_table_byte_order
{
XM_TARGETS_TABLE_LSB = 'l',
XM_TARGETS_TABLE_MSB = 'B',
#ifndef WORDS_BIGENDIAN
XM_TARGETS_TABLE_CUR = 'l',
#else
XM_TARGETS_TABLE_CUR = 'B',
#endif
} xm_targets_table_byte_order;
#define SWAPCARD32(l) \
{ \
struct { unsigned t : 32; } bit32; \
char n, *tp = (char *) &bit32; \
bit32.t = l; \
n = tp[0]; tp[0] = tp[3]; tp[3] = n; \
n = tp[1]; tp[1] = tp[2]; tp[2] = n; \
l = bit32.t; \
}
#define SWAPCARD16(s) \
{ \
struct { unsigned t : 16; } bit16; \
char n, *tp = (char *) &bit16; \
bit16.t = s; \
n = tp[0]; tp[0] = tp[1]; tp[1] = n; \
s = bit16.t; \
}
typedef struct xm_targets_table_header
{
/* BYTE */ uint8_t byte_order;
/* BYTE */ uint8_t protocol;
/* CARD16 */ uint16_t target_list_count;
/* CARD32 */ uint32_t total_data_size;
} xm_targets_table_header;
typedef struct xm_targets_table_rec
{
/* CARD16 */ uint16_t n_targets;
/* CARD32 */ uint32_t targets[FLEXIBLE_ARRAY_MEMBER];
} xm_targets_table_rec;
typedef struct xm_drop_start_message
{
/* BYTE */ uint8_t reason;
/* BYTE */ uint8_t byte_order;
/* CARD16 */ uint16_t side_effects;
/* CARD32 */ uint32_t timestamp;
/* CARD16 */ uint16_t x, y;
/* CARD32 */ uint32_t index_atom;
/* CARD32 */ uint32_t source_window;
} xm_drop_start_message;
typedef struct xm_drag_initiator_info
{
/* BYTE */ uint8_t byteorder;
/* BYTE */ uint8_t protocol;
/* CARD16 */ uint16_t table_index;
/* CARD32 */ uint32_t selection;
} xm_drag_initiator_info;
typedef struct xm_drag_receiver_info
{
/* BYTE */ uint8_t byteorder;
/* BYTE */ uint8_t protocol;
/* BYTE */ uint8_t protocol_style;
/* BYTE */ uint8_t unspecified0;
/* CARD32 */ uint32_t unspecified1;
/* CARD32 */ uint32_t unspecified2;
/* CARD32 */ uint32_t unspecified3;
} xm_drag_receiver_info;
#define XM_DRAG_SIDE_EFFECT(op, site, ops, act) \
((op) | ((site) << 4) | ((ops) << 8) | ((act) << 16))
/* 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_OPERATIONS(effect) (((effect) & 0xf00) >> 8) */
/* #define XM_DRAG_SIDE_EFFECT_DROP_ACTION(effect) (((effect) & 0xf000) >> 16) */
#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_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_DROP_START 5
#define XM_DRAG_ORIGINATOR_INITIATOR 0
/* #define XM_DRAG_ORIGINATOR_RECEIVER 1 */
#define XM_DRAG_STYLE_NONE 0
static uint8_t
xm_side_effect_from_action (struct x_display_info *dpyinfo, Atom action)
{
if (action == dpyinfo->Xatom_XdndActionCopy)
return XM_DRAG_COPY;
else if (action == dpyinfo->Xatom_XdndActionMove)
return XM_DRAG_MOVE;
else if (action == dpyinfo->Xatom_XdndActionLink)
return XM_DRAG_LINK;
return XM_DRAG_NOOP;
}
static int
xm_read_targets_table_header (uint8_t *bytes, ptrdiff_t length,
xm_targets_table_header *header_return,
xm_targets_table_byte_order *byteorder_return)
{
if (length < 8)
return -1;
header_return->byte_order = *byteorder_return = *(bytes++);
header_return->protocol = *(bytes++);
header_return->target_list_count = *(uint16_t *) bytes;
header_return->total_data_size = *(uint32_t *) (bytes + 2);
if (header_return->byte_order != XM_TARGETS_TABLE_CUR)
{
SWAPCARD16 (header_return->target_list_count);
SWAPCARD32 (header_return->total_data_size);
}
header_return->byte_order = XM_TARGETS_TABLE_CUR;
return 8;
}
static xm_targets_table_rec *
xm_read_targets_table_rec (uint8_t *bytes, ptrdiff_t length,
xm_targets_table_byte_order byteorder)
{
uint16_t nitems, i;
xm_targets_table_rec *rec;
if (length < 2)
return NULL;
nitems = *(uint16_t *) bytes;
if (length < 2 + nitems * 4)
return NULL;
if (byteorder != XM_TARGETS_TABLE_CUR)
SWAPCARD16 (nitems);
rec = xmalloc (sizeof *rec + nitems * 4);
rec->n_targets = nitems;
for (i = 0; i < nitems; ++i)
{
rec->targets[i] = ((uint32_t *) (bytes + 2))[i];
if (byteorder != XM_TARGETS_TABLE_CUR)
SWAPCARD32 (rec->targets[i]);
}
return rec;
}
static int
xm_find_targets_table_idx (xm_targets_table_header *header,
xm_targets_table_rec **recs,
Atom *sorted_targets, int ntargets)
{
int j;
uint16_t i;
uint32_t *targets;
targets = alloca (sizeof *targets * ntargets);
for (j = 0; j < ntargets; ++j)
targets[j] = sorted_targets[j];
for (i = 0; i < header->target_list_count; ++i)
{
if (recs[i]->n_targets == ntargets
&& !memcmp (&recs[i]->targets, targets,
sizeof *targets * ntargets))
return i;
}
return -1;
}
static int
x_atoms_compare (const void *a, const void *b)
{
return *(Atom *) a - *(Atom *) b;
}
static void
xm_write_targets_table (Display *dpy, Window wdesc,
Atom targets_table_atom,
xm_targets_table_header *header,
xm_targets_table_rec **recs)
{
uint8_t *header_buffer, *ptr, *rec_buffer;
ptrdiff_t rec_buffer_size;
uint16_t i, j;
header_buffer = alloca (8);
ptr = header_buffer;
*(header_buffer++) = header->byte_order;
*(header_buffer++) = header->protocol;
*((uint16_t *) header_buffer) = header->target_list_count;
*((uint32_t *) (header_buffer + 2)) = header->total_data_size;
rec_buffer = xmalloc (600);
rec_buffer_size = 600;
XGrabServer (dpy);
XChangeProperty (dpy, wdesc, targets_table_atom,
targets_table_atom, 8, PropModeReplace,
(unsigned char *) ptr, 8);
for (i = 0; i < header->target_list_count; ++i)
{
if (rec_buffer_size < 2 + recs[i]->n_targets * 4)
{
rec_buffer_size = 2 + recs[i]->n_targets * 4;
rec_buffer = xrealloc (rec_buffer, rec_buffer_size);
}
*((uint16_t *) rec_buffer) = recs[i]->n_targets;
for (j = 0; j < recs[i]->n_targets; ++j)
((uint32_t *) (rec_buffer + 2))[j] = recs[i]->targets[j];
XChangeProperty (dpy, wdesc, targets_table_atom,
targets_table_atom, 8, PropModeAppend,
(unsigned char *) rec_buffer,
2 + recs[i]->n_targets * 4);
}
XUngrabServer (dpy);
xfree (rec_buffer);
}
static void
xm_write_drag_initiator_info (Display *dpy, Window wdesc,
Atom prop_name, Atom type_name,
xm_drag_initiator_info *info)
{
uint8_t *buf;
buf = alloca (8);
buf[0] = info->byteorder;
buf[1] = info->protocol;
*((uint16_t *) (buf + 2)) = info->table_index;
*((uint32_t *) (buf + 4)) = info->selection;
XChangeProperty (dpy, wdesc, prop_name, type_name, 8,
PropModeReplace, (unsigned char *) buf, 8);
}
static Window
xm_get_drag_window (struct x_display_info *dpyinfo)
{
Atom actual_type;
int rc, actual_format;
unsigned long nitems, bytes_remaining;
unsigned char *tmp_data = NULL;
Window drag_window;
XSetWindowAttributes attrs;
XWindowAttributes wattrs;
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,
&actual_format, &nitems, &bytes_remaining,
&tmp_data) == Success;
if (rc)
{
if (actual_type == XA_WINDOW
&& actual_format == 32 && nitems == 1)
{
drag_window = *(Window *) tmp_data;
x_catch_errors (dpyinfo->display);
XGetWindowAttributes (dpyinfo->display,
drag_window, &wattrs);
rc = !x_had_errors_p (dpyinfo->display);
x_uncatch_errors_after_check ();
if (!rc)
drag_window = None;
}
if (tmp_data)
XFree (tmp_data);
}
XUngrabServer (dpyinfo->display);
if (drag_window == None)
{
unrequest_sigio ();
temp_display = XOpenDisplay (XDisplayString (dpyinfo->display));
request_sigio ();
if (!temp_display)
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,
CopyFromParent, CWOverrideRedirect, &attrs);
XChangeProperty (temp_display, DefaultRootWindow (temp_display),
XInternAtom (temp_display,
"_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
current display, and the XOpenDisplay above didn't
accidentally connect to some other display. */
x_catch_errors (dpyinfo->display);
XGetWindowAttributes (dpyinfo->display,
drag_window, &wattrs);
rc = !x_had_errors_p (dpyinfo->display);
x_uncatch_errors_after_check ();
/* We connected to the wrong display, so just give up. */
if (!rc)
drag_window = None;
}
return drag_window;
}
/* TODO: overflow checks when inserting targets. */
static int
xm_setup_dnd_targets (struct x_display_info *dpyinfo,
Atom *targets, int ntargets)
{
Window drag_window;
Atom *targets_sorted, actual_type;
unsigned char *tmp_data = NULL;
unsigned long nitems, bytes_remaining;
int rc, actual_format, idx;
xm_targets_table_header header;
xm_targets_table_rec **recs;
xm_targets_table_byte_order byteorder;
uint8_t *data;
ptrdiff_t total_bytes, total_items, i;
drag_window = xm_get_drag_window (dpyinfo);
if (drag_window == None || ntargets > 64)
return -1;
targets_sorted = xmalloc (sizeof *targets * ntargets);
memcpy (targets_sorted, targets,
sizeof *targets * ntargets);
qsort (targets_sorted, ntargets,
sizeof (Atom), x_atoms_compare);
XGrabServer (dpyinfo->display);
rc = XGetWindowProperty (dpyinfo->display, drag_window,
dpyinfo->Xatom_MOTIF_DRAG_TARGETS,
/* Do larger values occur in practice? */
0L, 20000L, False,
dpyinfo->Xatom_MOTIF_DRAG_TARGETS,
&actual_type, &actual_format, &nitems,
&bytes_remaining, &tmp_data) == Success;
if (rc && tmp_data && !bytes_remaining
&& actual_type == dpyinfo->Xatom_MOTIF_DRAG_TARGETS
&& actual_format == 8)
{
data = (uint8_t *) tmp_data;
if (xm_read_targets_table_header ((uint8_t *) tmp_data,
nitems, &header,
&byteorder) == 8)
{
data += 8;
nitems -= 8;
total_bytes = 0;
total_items = 0;
/* The extra rec is used to store a new target list if a
preexisting one doesn't already exist. */
recs = xmalloc ((header.target_list_count + 1)
* sizeof *recs);
while (total_items < header.target_list_count)
{
recs[total_items] = xm_read_targets_table_rec (data + total_bytes,
nitems, byteorder);
if (!recs[total_items])
break;
total_bytes += 2 + recs[total_items]->n_targets * 4;
nitems -= 2 + recs[total_items]->n_targets * 4;
total_items++;
}
if (header.target_list_count != total_items
|| header.total_data_size != 8 + total_bytes)
{
for (i = 0; i < total_items; ++i)
{
if (recs[i])
xfree (recs[i]);
else
break;
}
xfree (recs);
rc = false;
}
}
else
rc = false;
}
else
rc = false;
if (tmp_data)
XFree (tmp_data);
/* Now rc means whether or not the target lists weren't updated and
shouldn't be written to the drag window. */
if (!rc)
{
header.byte_order = XM_TARGETS_TABLE_CUR;
header.protocol = 0;
header.target_list_count = 1;
header.total_data_size = 8 + 2 + ntargets * 4;
recs = xmalloc (sizeof *recs);
recs[0] = xmalloc (sizeof **recs + ntargets * 4);
recs[0]->n_targets = ntargets;
for (i = 0; i < ntargets; ++i)
recs[0]->targets[i] = targets_sorted[i];
idx = 0;
}
else
{
idx = xm_find_targets_table_idx (&header, recs,
targets_sorted,
ntargets);
if (idx == -1)
{
header.target_list_count++;
header.total_data_size += 2 + ntargets * 4;
recs[header.target_list_count - 1] = xmalloc (sizeof **recs + ntargets * 4);
recs[header.target_list_count - 1]->n_targets = ntargets;
for (i = 0; i < ntargets; ++i)
recs[header.target_list_count - 1]->targets[i] = targets_sorted[i];
idx = header.target_list_count - 1;
rc = false;
}
}
if (!rc)
xm_write_targets_table (dpyinfo->display, drag_window,
dpyinfo->Xatom_MOTIF_DRAG_TARGETS,
&header, recs);
XUngrabServer (dpyinfo->display);
for (i = 0; i < header.target_list_count; ++i)
xfree (recs[i]);
xfree (recs);
xfree (targets_sorted);
return idx;
}
static void
xm_send_drop_message (struct x_display_info *dpyinfo, Window source,
Window target, xm_drop_start_message *dmsg)
{
XEvent msg;
msg.xclient.type = ClientMessage;
msg.xclient.message_type
= dpyinfo->Xatom_MOTIF_DRAG_AND_DROP_MESSAGE;
msg.xclient.format = 8;
msg.xclient.window = target;
msg.xclient.data.b[0] = dmsg->reason;
msg.xclient.data.b[1] = dmsg->byte_order;
*((uint16_t *) &msg.xclient.data.b[2]) = dmsg->side_effects;
*((uint32_t *) &msg.xclient.data.b[4]) = dmsg->timestamp;
*((uint16_t *) &msg.xclient.data.b[8]) = dmsg->x;
*((uint16_t *) &msg.xclient.data.b[10]) = dmsg->y;
*((uint32_t *) &msg.xclient.data.b[12]) = dmsg->index_atom;
*((uint32_t *) &msg.xclient.data.b[16]) = dmsg->source_window;
x_catch_errors (dpyinfo->display);
XSendEvent (dpyinfo->display, target, False, NoEventMask, &msg);
x_uncatch_errors ();
}
static int
xm_read_drag_receiver_info (struct x_display_info *dpyinfo,
Window wdesc, xm_drag_receiver_info *rec)
{
Atom actual_type;
int rc, actual_format;
unsigned long nitems, bytes_remaining;
unsigned char *tmp_data = NULL;
uint8_t *data;
x_catch_errors (dpyinfo->display);
rc = XGetWindowProperty (dpyinfo->display, wdesc,
dpyinfo->Xatom_MOTIF_DRAG_RECEIVER_INFO,
0, LONG_MAX, False,
dpyinfo->Xatom_MOTIF_DRAG_RECEIVER_INFO,
&actual_type, &actual_format, &nitems,
&bytes_remaining,
&tmp_data) == Success;
if (x_had_errors_p (dpyinfo->display)
|| actual_format != 8 || nitems < 16 || !tmp_data
|| actual_type != dpyinfo->Xatom_MOTIF_DRAG_RECEIVER_INFO)
rc = 0;
x_uncatch_errors_after_check ();
if (rc)
{
data = (uint8_t *) tmp_data;
rec->byteorder = data[0];
rec->protocol = data[1];
rec->protocol_style = data[2];
rec->unspecified0 = data[3];
rec->unspecified1 = *(uint32_t *) &data[4];
rec->unspecified2 = *(uint32_t *) &data[8];
rec->unspecified3 = *(uint32_t *) &data[12];
if (rec->byteorder != XM_TARGETS_TABLE_CUR)
{
SWAPCARD32 (rec->unspecified1);
SWAPCARD32 (rec->unspecified2);
SWAPCARD32 (rec->unspecified3);
}
rec->byteorder = XM_TARGETS_TABLE_CUR;
}
if (tmp_data)
XFree (tmp_data);
return !rc;
}
static void
x_dnd_free_toplevels (void)
{
@ -1124,10 +1712,6 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo)
tem->previous_event_mask = attrs.your_event_mask;
tem->wm_state = wmstate[0];
#ifndef USE_XCB
XFree (wmstate_data);
#endif
#ifdef HAVE_XSHAPE
#ifndef USE_XCB
tem->border_width = attrs.border_width;
@ -1360,6 +1944,14 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo)
if (geometry_reply)
free (geometry_reply);
#endif
#ifndef USE_XCB
if (wmstate_data)
{
XFree (wmstate_data);
wmstate_data = NULL;
}
#endif
}
return 0;
@ -1715,7 +2307,7 @@ x_dnd_get_window_proxy (struct x_display_info *dpyinfo, Window wdesc)
{
int rc, actual_format;
unsigned long actual_size, bytes_remaining;
unsigned char *tmp_data;
unsigned char *tmp_data = NULL;
XWindowAttributes attrs;
Atom actual_type;
Window proxy;
@ -1731,12 +2323,12 @@ x_dnd_get_window_proxy (struct x_display_info *dpyinfo, Window wdesc)
if (!x_had_errors_p (dpyinfo->display)
&& rc == Success
&& tmp_data
&& actual_type == XA_WINDOW
&& actual_format == 32
&& actual_size == 1)
{
proxy = *(Window *) tmp_data;
XFree (tmp_data);
/* Verify the proxy window exists. */
XGetWindowAttributes (dpyinfo->display, proxy, &attrs);
@ -1744,6 +2336,9 @@ x_dnd_get_window_proxy (struct x_display_info *dpyinfo, Window wdesc)
if (x_had_errors_p (dpyinfo->display))
proxy = None;
}
if (tmp_data)
XFree (tmp_data);
x_uncatch_errors_after_check ();
return proxy;
@ -1753,7 +2348,7 @@ static int
x_dnd_get_window_proto (struct x_display_info *dpyinfo, Window wdesc)
{
Atom actual, value;
unsigned char *tmp_data;
unsigned char *tmp_data = NULL;
int rc, format;
unsigned long n, left;
bool had_errors;
@ -1769,8 +2364,13 @@ x_dnd_get_window_proto (struct x_display_info *dpyinfo, Window wdesc)
had_errors = x_had_errors_p (dpyinfo->display);
x_uncatch_errors_after_check ();
if (had_errors || rc != Success || actual != XA_ATOM || format != 32 || n < 1)
return -1;
if (had_errors || rc != Success || actual != XA_ATOM || format != 32 || n < 1
|| !tmp_data)
{
if (tmp_data)
XFree (tmp_data);
return -1;
}
value = (int) *(Atom *) tmp_data;
XFree (tmp_data);
@ -3545,7 +4145,7 @@ x_set_frame_alpha (struct frame *f)
/* return unless necessary */
{
unsigned char *data;
unsigned char *data = NULL;
Atom actual;
int rc, format;
unsigned long n, left;
@ -3555,16 +4155,19 @@ x_set_frame_alpha (struct frame *f)
&actual, &format, &n, &left,
&data);
if (rc == Success && actual != None)
if (rc == Success && actual != None && data)
{
unsigned long value = *(unsigned long *)data;
XFree (data);
unsigned long value = *(unsigned long *) data;
if (value == opac)
{
x_uncatch_errors ();
XFree (data);
return;
}
}
if (data)
XFree (data);
}
XChangeProperty (dpy, win, dpyinfo->Xatom_net_wm_window_opacity,
@ -12144,12 +12747,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (!x_had_errors_p (dpyinfo->display) && rc == Success && data
&& nitems == 2 && actual_format == 32)
{
tem->wm_state = ((unsigned long *) data)[0];
XFree (data);
}
tem->wm_state = ((unsigned long *) data)[0];
else
tem->wm_state = WithdrawnState;
if (data)
XFree (data);
x_uncatch_errors_after_check ();
}
@ -13569,6 +14172,56 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_dnd_frame = NULL;
x_set_dnd_targets (NULL, 0);
}
else if (x_dnd_last_seen_window != None)
{
xm_drag_receiver_info drag_receiver_info;
xm_drag_initiator_info drag_initiator_info;
xm_drop_start_message dmsg;
int idx;
if (!xm_read_drag_receiver_info (dpyinfo, x_dnd_last_seen_window,
&drag_receiver_info)
&& drag_receiver_info.protocol_style != XM_DRAG_STYLE_NONE)
{
idx = xm_setup_dnd_targets (dpyinfo, x_dnd_targets,
x_dnd_n_targets);
if (idx != -1)
{
drag_initiator_info.byteorder = XM_TARGETS_TABLE_CUR;
drag_initiator_info.protocol = 0;
drag_initiator_info.table_index = idx;
drag_initiator_info.selection = dpyinfo->Xatom_XdndSelection;
memset (&dmsg, 0, sizeof dmsg);
xm_write_drag_initiator_info (dpyinfo->display,
FRAME_X_WINDOW (x_dnd_frame),
dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO,
dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO,
&drag_initiator_info);
dmsg.reason = XM_DRAG_REASON (XM_DRAG_ORIGINATOR_INITIATOR,
XM_DRAG_REASON_DROP_START);
dmsg.byte_order = XM_TARGETS_TABLE_CUR;
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_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.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);
}
}
}
goto OTHER;
}
@ -14562,6 +15215,56 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_dnd_selection_timestamp,
x_dnd_last_protocol_version);
}
else if (x_dnd_last_seen_window != None)
{
xm_drag_receiver_info drag_receiver_info;
xm_drag_initiator_info drag_initiator_info;
xm_drop_start_message dmsg;
int idx;
if (!xm_read_drag_receiver_info (dpyinfo, x_dnd_last_seen_window,
&drag_receiver_info)
&& drag_receiver_info.protocol_style != XM_DRAG_STYLE_NONE)
{
idx = xm_setup_dnd_targets (dpyinfo, x_dnd_targets,
x_dnd_n_targets);
if (idx != -1)
{
drag_initiator_info.byteorder = XM_TARGETS_TABLE_CUR;
drag_initiator_info.protocol = 0;
drag_initiator_info.table_index = idx;
drag_initiator_info.selection = dpyinfo->Xatom_XdndSelection;
memset (&dmsg, 0, sizeof dmsg);
xm_write_drag_initiator_info (dpyinfo->display,
FRAME_X_WINDOW (x_dnd_frame),
dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO,
dpyinfo->Xatom_MOTIF_DRAG_INITIATOR_INFO,
&drag_initiator_info);
dmsg.reason = XM_DRAG_REASON (XM_DRAG_ORIGINATOR_INITIATOR,
XM_DRAG_REASON_DROP_START);
dmsg.byte_order = XM_TARGETS_TABLE_CUR;
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_ACTION_DROP);
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;
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_last_protocol_version = -1;
x_dnd_last_seen_window = None;
@ -20420,6 +21123,15 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
ATOM_REFS_INIT ("XdndLeave", Xatom_XdndLeave)
ATOM_REFS_INIT ("XdndDrop", Xatom_XdndDrop)
ATOM_REFS_INIT ("XdndFinished", Xatom_XdndFinished)
/* Motif drop protocol support. */
ATOM_REFS_INIT ("_MOTIF_DRAG_WINDOW", Xatom_MOTIF_DRAG_WINDOW)
ATOM_REFS_INIT ("_MOTIF_DRAG_TARGETS", Xatom_MOTIF_DRAG_TARGETS)
ATOM_REFS_INIT ("_MOTIF_DRAG_AND_DROP_MESSAGE",
Xatom_MOTIF_DRAG_AND_DROP_MESSAGE)
ATOM_REFS_INIT ("_MOTIF_DRAG_INITIATOR_INFO",
Xatom_MOTIF_DRAG_INITIATOR_INFO)
ATOM_REFS_INIT ("_MOTIF_DRAG_RECEIVER_INFO",
Xatom_MOTIF_DRAG_RECEIVER_INFO)
};
int i;

View File

@ -433,7 +433,9 @@ struct x_display_info
/* Atom used to determine whether or not the screen is composited. */
Atom Xatom_NET_WM_CM_Sn;
Atom Xatom_MOTIF_WM_HINTS;
Atom Xatom_MOTIF_WM_HINTS, Xatom_MOTIF_DRAG_WINDOW,
Xatom_MOTIF_DRAG_TARGETS, Xatom_MOTIF_DRAG_AND_DROP_MESSAGE,
Xatom_MOTIF_DRAG_INITIATOR_INFO, Xatom_MOTIF_DRAG_RECEIVER_INFO;
/* 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