Speed up receiving drops over slow connections

* lisp/x-dnd.el (x-dnd-debug-errors): New variable.
(x-dnd-handle-drag-n-drop-event): Bind
`x-fast-protocol-requests' to t if that is off.

* src/xfns.c (Fx_change_window_property):
(Fx_delete_window_property):
* src/xselect.c (Fx_send_client_message, x_send_client_event):
Don't sync to check for errors if fast protocol requests are
enabled.

* src/xterm.c (x_catch_errors_for_lisp, x_check_errors_for_lisp)
(x_uncatch_errors_for_lisp): New functions.
(syms_of_xterm): New variable `x-fast-protocol-requests'.
* src/xterm.h: Update prototypes.
This commit is contained in:
Po Lu 2022-07-03 09:41:32 +08:00
parent 782e48b3db
commit 4ef1e4daf5
5 changed files with 104 additions and 31 deletions

View file

@ -151,6 +151,12 @@ data types in this list."
;; Internal variables
(defvar x-dnd-debug-errors nil
"Whether or not to signal protocol errors during drag-and-drop.
This is useful for debugging errors in the DND code, but makes
drag-and-drop much slower over network connections with high
latency.")
(defvar x-dnd-current-state nil
"The current state for a drop.
This is an alist with one entry for each display. The value for each display
@ -425,11 +431,14 @@ nil if not."
(select-frame frame)
(funcall handler window action data))))))
(defvar x-fast-protocol-requests)
(defun x-dnd-handle-drag-n-drop-event (event)
"Receive drag and drop events (X client messages).
Currently XDND, Motif and old KDE 1.x protocols are recognized."
(interactive "e")
(let* ((client-message (car (cdr (cdr event))))
(x-fast-protocol-requests (not x-dnd-debug-errors))
(window (posn-window (event-start event))))
(if (eq (and (consp client-message)
(car client-message))

View file

@ -7339,18 +7339,23 @@ If VALUE is a string and FORMAT is 32, then the format of VALUE is
system-specific. VALUE must contain unsigned integer data in native
endian-ness in multiples of the size of the C type 'long': the low 32
bits of each such number are used as the value of each element of the
property. */)
property.
Wait for the request to complete and signal any error, unless
`x-fast-protocol-requests' is non-nil, in which case errors will be
silently ignored. */)
(Lisp_Object prop, Lisp_Object value, Lisp_Object frame,
Lisp_Object type, Lisp_Object format, Lisp_Object outer_p,
Lisp_Object window_id)
{
struct frame *f = decode_window_system_frame (frame);
struct frame *f;
Atom prop_atom;
Atom target_type = XA_STRING;
int element_format = 8;
unsigned char *data;
int nelements;
Window target_window;
struct x_display_info *dpyinfo;
#ifdef USE_XCB
bool intern_prop;
bool intern_target;
@ -7361,6 +7366,9 @@ property. */)
bool rc;
#endif
f = decode_window_system_frame (frame);
dpyinfo = FRAME_DISPLAY_INFO (f);
CHECK_STRING (prop);
if (! NILP (format))
@ -7412,7 +7420,7 @@ property. */)
{
CONS_TO_INTEGER (window_id, Window, target_window);
if (! target_window)
target_window = FRAME_DISPLAY_INFO (f)->root_window;
target_window = dpyinfo->root_window;
}
else
{
@ -7424,47 +7432,47 @@ property. */)
block_input ();
#ifndef USE_XCB
prop_atom = x_intern_cached_atom (FRAME_DISPLAY_INFO (f),
SSDATA (prop), false);
prop_atom = x_intern_cached_atom (dpyinfo, SSDATA (prop),
false);
if (! NILP (type))
{
CHECK_STRING (type);
target_type = x_intern_cached_atom (FRAME_DISPLAY_INFO (f),
SSDATA (type), false);
target_type = x_intern_cached_atom (dpyinfo, SSDATA (type),
false);
}
#else
rc = true;
intern_target = true;
intern_prop = true;
prop_atom = x_intern_cached_atom (FRAME_DISPLAY_INFO (f),
SSDATA (prop), true);
prop_atom = x_intern_cached_atom (dpyinfo, SSDATA (prop),
true);
if (prop_atom != None)
intern_prop = false;
else
prop_atom_cookie
= xcb_intern_atom (FRAME_DISPLAY_INFO (f)->xcb_connection,
= xcb_intern_atom (dpyinfo->xcb_connection,
0, SBYTES (prop), SSDATA (prop));
if (!NILP (type))
{
CHECK_STRING (type);
target_type = x_intern_cached_atom (FRAME_DISPLAY_INFO (f),
SSDATA (type), true);
target_type = x_intern_cached_atom (dpyinfo, SSDATA (type),
true);
if (target_type)
intern_target = false;
else
target_type_cookie
= xcb_intern_atom (FRAME_DISPLAY_INFO (f)->xcb_connection,
= xcb_intern_atom (dpyinfo->xcb_connection,
0, SBYTES (type), SSDATA (type));
}
if (intern_prop)
{
reply = xcb_intern_atom_reply (FRAME_DISPLAY_INFO (f)->xcb_connection,
reply = xcb_intern_atom_reply (dpyinfo->xcb_connection,
prop_atom_cookie, &generic_error);
if (reply)
@ -7481,7 +7489,7 @@ property. */)
if (!NILP (type) && intern_target)
{
reply = xcb_intern_atom_reply (FRAME_DISPLAY_INFO (f)->xcb_connection,
reply = xcb_intern_atom_reply (dpyinfo->xcb_connection,
target_type_cookie, &generic_error);
if (reply)
@ -7500,15 +7508,18 @@ property. */)
error ("Failed to intern type or property atom");
#endif
x_catch_errors (FRAME_X_DISPLAY (f));
XChangeProperty (FRAME_X_DISPLAY (f), target_window,
prop_atom, target_type, element_format, PropModeReplace,
data, nelements);
x_catch_errors_for_lisp (dpyinfo);
if (CONSP (value)) xfree (data);
x_check_errors (FRAME_X_DISPLAY (f),
"Couldn't change window property: %s");
x_uncatch_errors_after_check ();
XChangeProperty (dpyinfo->display, target_window,
prop_atom, target_type, element_format,
PropModeReplace, data, nelements);
if (CONSP (value))
xfree (data);
x_check_errors_for_lisp (dpyinfo,
"Couldn't change window property: %s");
x_uncatch_errors_for_lisp (dpyinfo);
unblock_input ();
return value;
@ -7525,7 +7536,11 @@ If WINDOW-ID is non-nil, remove property from that window instead
across X displays or screens on the same display, so FRAME provides
context for the window ID.
Value is PROP. */)
Value is PROP.
Wait for the request to complete and signal any error, unless
`x-fast-protocol-requests' is non-nil, in which case errors will be
silently ignored. */)
(Lisp_Object prop, Lisp_Object frame, Lisp_Object window_id)
{
struct frame *f = decode_window_system_frame (frame);
@ -7545,11 +7560,11 @@ Value is PROP. */)
prop_atom = x_intern_cached_atom (FRAME_DISPLAY_INFO (f),
SSDATA (prop), false);
x_catch_errors (FRAME_X_DISPLAY (f));
x_catch_errors_for_lisp (FRAME_DISPLAY_INFO (f));
XDeleteProperty (FRAME_X_DISPLAY (f), target_window, prop_atom);
x_check_errors (FRAME_X_DISPLAY (f),
"Couldn't delete window property: %s");
x_uncatch_errors_after_check ();
x_check_errors_for_lisp (FRAME_DISPLAY_INFO (f),
"Couldn't delete window property: %s");
x_uncatch_errors_for_lisp (FRAME_DISPLAY_INFO (f));
unblock_input ();
return prop;

View file

@ -2749,7 +2749,11 @@ to send. If a value is a string, it is converted to an Atom and the value of
the Atom is sent. If a value is a cons, it is converted to a 32 bit number
with the high 16 bits from the car and the lower 16 bit from the cdr.
If more values than fits into the event is given, the excessive values
are ignored. */)
are ignored.
Wait for the event to be sent and signal any error, unless
`x-fast-protocol-requests' is non-nil, in which case errors will be
silently ignored. */)
(Lisp_Object display, Lisp_Object dest, Lisp_Object from,
Lisp_Object message_type, Lisp_Object format, Lisp_Object values)
{
@ -2830,7 +2834,7 @@ x_send_client_event (Lisp_Object display, Lisp_Object dest, Lisp_Object from,
the destination window. But if we are sending to the root window,
there is no such client. Then we set the event mask to 0xffffff. The
event then goes to clients selecting for events on the root window. */
x_catch_errors (dpyinfo->display);
x_catch_errors_for_lisp (dpyinfo);
{
bool propagate = !to_root;
long mask = to_root ? 0xffffff : 0;
@ -2838,7 +2842,8 @@ x_send_client_event (Lisp_Object display, Lisp_Object dest, Lisp_Object from,
XSendEvent (dpyinfo->display, wdest, propagate, mask, &event);
XFlush (dpyinfo->display);
}
x_uncatch_errors ();
x_check_errors_for_lisp (dpyinfo, "Failed to send client event: %s");
x_uncatch_errors_for_lisp (dpyinfo);
unblock_input ();
}

View file

@ -27836,6 +27836,36 @@ mark_xterm (void)
#endif
}
/* Error handling functions for Lisp functions that expose X protocol
requests. They are mostly like `x_catch_errors' and friends, but
respect `x-fast-protocol-requests'. */
void
x_catch_errors_for_lisp (struct x_display_info *dpyinfo)
{
if (!x_fast_protocol_requests)
x_catch_errors (dpyinfo->display);
else
x_ignore_errors_for_next_request (dpyinfo);
}
void
x_check_errors_for_lisp (struct x_display_info *dpyinfo,
const char *format)
{
if (!x_fast_protocol_requests)
x_check_errors (dpyinfo->display, format);
}
void
x_uncatch_errors_for_lisp (struct x_display_info *dpyinfo)
{
if (!x_fast_protocol_requests)
x_uncatch_errors ();
else
x_stop_ignoring_errors (dpyinfo);
}
void
syms_of_xterm (void)
{
@ -28141,4 +28171,13 @@ When nil, do not use the primary selection and synthetic mouse clicks
to emulate the drag-and-drop of `STRING', `UTF8_STRING',
`COMPOUND_TEXT' or `TEXT'. */);
x_dnd_use_unsupported_drop = true;
DEFVAR_BOOL ("x-fast-protocol-requests", x_fast_protocol_requests,
doc: /* Whether or not X protocol-related functions should wait for errors.
When this is nil, functions such as `x-delete-window-property',
`x-change-window-property' and `x-send-client-message' will wait for a
reply from the X server, and signal any errors that occurred while
executing the protocol request. Otherwise, errors will be silently
ignored without waiting, which is generally faster. */);
x_fast_protocol_requests = false;
}

View file

@ -1445,6 +1445,11 @@ extern bool x_text_icon (struct frame *, const char *);
extern void x_catch_errors (Display *);
extern void x_catch_errors_with_handler (Display *, x_special_error_handler,
void *);
extern void x_catch_errors_for_lisp (struct x_display_info *);
extern void x_uncatch_errors_for_lisp (struct x_display_info *);
extern void x_check_errors_for_lisp (struct x_display_info *,
const char *)
ATTRIBUTE_FORMAT_PRINTF (2, 0);
extern void x_check_errors (Display *, const char *)
ATTRIBUTE_FORMAT_PRINTF (2, 0);
extern bool x_had_errors_p (Display *);