Fix pieces of code being too expensive over slow network connections

* lisp/menu-bar.el (menu-bar-edit-menu): Test buffer-read-only
before gui-backend-selection-exists-p.  This places the less
expensive condition before the more expensive one.
* src/xfns.c (compute_tip_xy): Use cached monitor attributes
whenever available.
(Fx_show_tip): Remove code that really did nothing.
(Fx_backspace_delete_keys_p): Do not download the entire keymap
from the server upon creating a frame.
* src/xmenu.c (create_and_show_popup_menu): Use
x_translate_coordinates_to_root.
(x_menu_show): Use x_translate_coordinates_to_root.
* src/xselect.c (Fx_selection_exists_p): If a temporary
selection owner can be found, use it.
* src/xterm.c (x_translate_coordinates_to_root)
(x_handle_selection_monitor_event, x_find_selection_owner): New
functions.  These functions try to avoid downloading data from
the X server in places that are called very often (i.e. during
tool bar updates.)
(handle_one_xevent): Handle selection notify events.  Also catch
some mistakes found.  Fetch all kinds of key names as well.
(x_create_special_window): New function.
(x_term_init, x_delete_display): Ask for all key names.  Also,
passively monitor selections that are given to
`x-selection-exists-p' during redisplay, so we do not have to
ask the server about them upon each redisplay.
(syms_of_xterm): New variable `x-fast-selection-list'.
* src/xterm.h (struct x_monitored_selection): New structure.
(X_INVALID_WINDOW): New define.
(struct x_display_info): New fields for selection monitoring.
Also, record the fixes extension base.
This commit is contained in:
Po Lu 2022-10-17 20:56:20 +08:00
parent b9aff5fdb8
commit abf683bb03
6 changed files with 348 additions and 132 deletions

View file

@ -527,12 +527,12 @@
`(menu-item "Paste" yank
:enable (funcall
',(lambda ()
(and (or
(and (not buffer-read-only)
(or
(gui-backend-selection-exists-p 'CLIPBOARD)
(if (featurep 'ns) ; like paste-from-menu
(cdr yank-menu)
kill-ring))
(not buffer-read-only))))
kill-ring)))))
:help "Paste (yank) text most recently cut/copied"
:keys ,(lambda ()
(if cua-mode

View file

@ -8443,7 +8443,17 @@ compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx,
unblock_input ();
XSETFRAME (frame, f);
attributes = Fx_display_monitor_attributes_list (frame);
#if defined HAVE_XRANDR || defined USE_GTK
if (!NILP (FRAME_DISPLAY_INFO (f)->last_monitor_attributes_list))
/* Use cached values if available to avoid fetching the
monitor list from the X server. If XRandR is not
available, then fetching the attributes will probably not
sync anyway, and will thus be relatively harmless. */
attributes = FRAME_DISPLAY_INFO (f)->last_monitor_attributes_list;
else
#endif
attributes = Fx_display_monitor_attributes_list (frame);
/* Try to determine the monitor where the mouse pointer is and
its geometry. See bug#22549. */
@ -8693,9 +8703,6 @@ Text larger than the specified size is clipped. */)
int old_windows_or_buffers_changed = windows_or_buffers_changed;
specpdl_ref count = SPECPDL_INDEX ();
Lisp_Object window, size, tip_buf;
Window child;
XWindowAttributes child_attrs;
int dest_x_return, dest_y_return;
bool displayed;
#ifdef ENABLE_CHECKING
struct glyph_row *row, *end;
@ -8946,41 +8953,6 @@ Text larger than the specified size is clipped. */)
/* Show tooltip frame. */
block_input ();
/* If the display is composited, then WM_TRANSIENT_FOR must be set
as well, or else the compositing manager won't display
decorations correctly, even though the tooltip window is override
redirect. See
https://specifications.freedesktop.org/wm-spec/1.4/ar01s08.html
Perhaps WM_TRANSIENT_FOR should be used in place of
override-redirect anyway. The ICCCM only recommends
override-redirect if the pointer will be grabbed. */
if (XTranslateCoordinates (FRAME_X_DISPLAY (f),
FRAME_DISPLAY_INFO (f)->root_window,
FRAME_DISPLAY_INFO (f)->root_window,
root_x, root_y, &dest_x_return,
&dest_y_return, &child)
&& child != None)
{
/* But only if the child is not override-redirect, which can
happen if the pointer is above a menu. */
if (XGetWindowAttributes (FRAME_X_DISPLAY (f),
child, &child_attrs)
|| child_attrs.override_redirect)
XDeleteProperty (FRAME_X_DISPLAY (tip_f),
FRAME_X_WINDOW (tip_f),
FRAME_DISPLAY_INFO (tip_f)->Xatom_wm_transient_for);
else
XSetTransientForHint (FRAME_X_DISPLAY (tip_f),
FRAME_X_WINDOW (tip_f), child);
}
else
XDeleteProperty (FRAME_X_DISPLAY (tip_f),
FRAME_X_WINDOW (tip_f),
FRAME_DISPLAY_INFO (tip_f)->Xatom_wm_transient_for);
#ifndef USE_XCB
XMoveResizeWindow (FRAME_X_DISPLAY (tip_f), FRAME_X_WINDOW (tip_f),
root_x, root_y, width, height);
@ -9452,13 +9424,21 @@ usual X keysyms. Value is `lambda' if we cannot determine if both keys are
present and mapped to the usual X keysyms. */)
(Lisp_Object frame)
{
#ifdef HAVE_XKB
XkbDescPtr kb;
struct frame *f;
Display *dpy;
Lisp_Object have_keys;
int delete_keycode, backspace_keycode, i;
#endif
#ifndef HAVE_XKB
return Qlambda;
#else
XkbDescPtr kb;
struct frame *f = decode_window_system_frame (frame);
Display *dpy = FRAME_X_DISPLAY (f);
Lisp_Object have_keys;
delete_keycode = 0;
backspace_keycode = 0;
f = decode_window_system_frame (frame);
dpy = FRAME_X_DISPLAY (f);
if (!FRAME_DISPLAY_INFO (f)->supports_xkb)
return Qlambda;
@ -9474,50 +9454,39 @@ present and mapped to the usual X keysyms. */)
XK_Delete are mapped to any key. But if any of those are mapped to
some non-intuitive key combination (Meta-Shift-Ctrl-whatever) and the
user doesn't know about it, it is better to return false here.
It is more obvious to the user what to do if she/he has two keys
It is more obvious to the user what to do if there are two keys
clearly marked with names/symbols and one key does something not
expected (i.e. she/he then tries the other).
expected (and the user then tries the other).
The cases where Backspace/Delete is mapped to some other key combination
are rare, and in those cases, normal-erase-is-backspace can be turned on
manually. */
have_keys = Qnil;
kb = XkbGetMap (dpy, XkbAllMapComponentsMask, XkbUseCoreKbd);
if (kb)
kb = FRAME_DISPLAY_INFO (f)->xkb_desc;
if (kb && kb->names)
{
int delete_keycode = 0, backspace_keycode = 0, i;
if (XkbGetNames (dpy, XkbAllNamesMask, kb) == Success)
for (i = kb->min_key_code; (i < kb->max_key_code
&& (delete_keycode == 0
|| backspace_keycode == 0));
++i)
{
for (i = kb->min_key_code;
(i < kb->max_key_code
&& (delete_keycode == 0 || backspace_keycode == 0));
++i)
{
/* The XKB symbolic key names can be seen most easily in
the PS file generated by `xkbprint -label name
$DISPLAY'. */
if (memcmp ("DELE", kb->names->keys[i].name, 4) == 0)
delete_keycode = i;
else if (memcmp ("BKSP", kb->names->keys[i].name, 4) == 0)
backspace_keycode = i;
}
XkbFreeNames (kb, 0, True);
/* The XKB symbolic key names can be seen most easily in
the PS file generated by `xkbprint -label name
$DISPLAY'. */
if (!memcmp ("DELE", kb->names->keys[i].name, 4))
delete_keycode = i;
else if (!memcmp ("BKSP", kb->names->keys[i].name, 4))
backspace_keycode = i;
}
/* As of libX11-1.6.2, XkbGetMap manual says that you should use
XkbFreeClientMap to free the data returned by XkbGetMap. But
this function just frees the data referenced from KB and not
KB itself. To free KB as well, call XkbFreeKeyboard. */
XkbFreeKeyboard (kb, XkbAllMapComponentsMask, True);
if (delete_keycode
&& backspace_keycode
if (delete_keycode && backspace_keycode
&& XKeysymToKeycode (dpy, XK_Delete) == delete_keycode
&& XKeysymToKeycode (dpy, XK_BackSpace) == backspace_keycode)
have_keys = Qt;
}
else
/* The keyboard names couldn't be obtained for some reason. */
have_keys = Qlambda;
unblock_input ();
return have_keys;
#endif

View file

@ -1521,26 +1521,15 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
if (use_pos_func)
{
Window dummy_window;
/* Not invoked by a click. pop up at x/y. */
pos_func = menu_position_func;
/* Adjust coordinates to be root-window-relative. */
block_input ();
XTranslateCoordinates (FRAME_X_DISPLAY (f),
/* From-window, to-window. */
FRAME_X_WINDOW (f),
FRAME_DISPLAY_INFO (f)->root_window,
/* From-position, to-position. */
x, y, &x, &y,
/* Child of win. */
&dummy_window);
x_translate_coordinates_to_root (f, x, y, &x, &y);
#ifdef HAVE_GTK3
/* Use window scaling factor to adjust position for hidpi screens. */
/* Use window scaling factor to adjust position for scaled
outputs. */
x /= xg_get_scale (f);
y /= xg_get_scale (f);
#endif
@ -1743,7 +1732,6 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
XButtonPressedEvent *event = &(dummy.xbutton);
LWLIB_ID menu_id;
Widget menu;
Window dummy_window;
#if defined HAVE_XINPUT2 && defined USE_MOTIF
XEvent property_dummy;
Atom property_atom;
@ -1775,17 +1763,7 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
/* Adjust coordinates to be root-window-relative. */
block_input ();
x += FRAME_LEFT_SCROLL_BAR_AREA_WIDTH (f);
XTranslateCoordinates (FRAME_X_DISPLAY (f),
/* From-window, to-window. */
FRAME_X_WINDOW (f),
FRAME_DISPLAY_INFO (f)->root_window,
/* From-position, to-position. */
x, y, &x, &y,
/* Child of win. */
&dummy_window);
x_translate_coordinates_to_root (f, x, y, &x, &y);
unblock_input ();
event->x_root = x;
@ -2569,9 +2547,6 @@ Lisp_Object
x_menu_show (struct frame *f, int x, int y, int menuflags,
Lisp_Object title, const char **error_name)
{
#ifdef HAVE_X_WINDOWS
Window dummy_window;
#endif
Window root;
XMenu *menu;
int pane, selidx, lpane, status;
@ -2620,17 +2595,7 @@ x_menu_show (struct frame *f, int x, int y, int menuflags,
inhibit_garbage_collection ();
#ifdef HAVE_X_WINDOWS
XTranslateCoordinates (FRAME_X_DISPLAY (f),
/* From-window, to-window. */
FRAME_X_WINDOW (f),
FRAME_DISPLAY_INFO (f)->root_window,
/* From-position, to-position. */
x, y, &x, &y,
/* Child of win. */
&dummy_window);
x_translate_coordinates_to_root (f, x, y, &x, &y);
#else
/* MSDOS without X support. */
x += f->left_pos;

View file

@ -2376,12 +2376,19 @@ On Nextstep, TERMINAL is unused. */)
{
Window owner;
Atom atom;
#ifdef HAVE_XFIXES
Window temp_owner;
#endif
struct frame *f = frame_for_x_selection (terminal);
struct x_display_info *dpyinfo;
CHECK_SYMBOL (selection);
if (NILP (selection)) selection = QPRIMARY;
if (EQ (selection, Qt)) selection = QSECONDARY;
if (NILP (selection))
selection = QPRIMARY;
if (EQ (selection, Qt))
selection = QSECONDARY;
if (!f)
return Qnil;
@ -2392,10 +2399,22 @@ On Nextstep, TERMINAL is unused. */)
return Qt;
atom = symbol_to_x_atom (dpyinfo, selection);
if (atom == 0) return Qnil;
if (!atom)
return Qnil;
#ifdef HAVE_XFIXES
/* See if this information can be obtained without a roundtrip. */
temp_owner = x_find_selection_owner (dpyinfo, atom);
if (temp_owner != X_INVALID_WINDOW)
return (temp_owner != None ? Qt : Qnil);
#endif
block_input ();
owner = XGetSelectionOwner (dpyinfo->display, atom);
unblock_input ();
return (owner ? Qt : Qnil);
}

View file

@ -13659,6 +13659,43 @@ x_translate_coordinates (struct frame *f, int root_x, int root_y,
}
}
/* Translate the given coordinates from the edit window of FRAME,
taking into account any cached root window offsets. This is mainly
used from the popup menu code. */
void
x_translate_coordinates_to_root (struct frame *f, int x, int y,
int *x_out, int *y_out)
{
struct x_output *output;
Window dummy;
output = FRAME_X_OUTPUT (f);
if (output->window_offset_certain_p)
{
/* Use the cached root window offset. */
*x_out = x + output->root_x;
*y_out = y + output->root_y;
return;
}
/* Otherwise, do the transform manually and compute and cache the
root window position. */
if (!XTranslateCoordinates (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
FRAME_DISPLAY_INFO (f)->root_window,
x, y, x_out, y_out, &dummy))
*x_out = 0, *y_out = 0;
else
{
/* Cache the root window offset of the edit window. */
output->window_offset_certain_p = true;
output->root_x = *x_out - x;
output->root_y = *y_out - y;
}
}
/* The same, but for an XIDeviceEvent. */
#ifdef HAVE_XINPUT2
@ -17816,6 +17853,44 @@ x_handle_wm_state (struct frame *f, struct input_event *ie)
XFree (data);
}
#ifdef HAVE_XFIXES
static void
x_handle_selection_monitor_event (struct x_display_info *dpyinfo,
XEvent *event)
{
XFixesSelectionNotifyEvent *notify;
int i;
notify = (XFixesSelectionNotifyEvent *) event;
if (notify->window != dpyinfo->selection_tracking_window)
return;
for (i = 0; i < dpyinfo->n_monitored_selections; ++i)
{
/* We don't have to keep track of timestamps here. */
if (notify->selection == dpyinfo->monitored_selections[i].name)
dpyinfo->monitored_selections[i].owner = notify->owner;
}
}
Window
x_find_selection_owner (struct x_display_info *dpyinfo, Atom selection)
{
int i;
for (i = 0; i < dpyinfo->n_monitored_selections; ++i)
{
if (selection == dpyinfo->monitored_selections[i].name)
return dpyinfo->monitored_selections[i].owner;
}
return X_INVALID_WINDOW;
}
#endif
/* Handles the XEvent EVENT on display DPYINFO.
*FINISH is X_EVENT_GOTO_OUT if caller should stop reading events.
@ -20495,7 +20570,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
{
int old_left = f->left_pos;
int old_top = f->top_pos;
Lisp_Object frame = Qnil;
Lisp_Object frame;
XSETFRAME (frame, f);
@ -23348,7 +23423,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
/* Handle all disabled devices now, to prevent
things happening out-of-order later. */
if (ndevices)
if (n_disabled)
{
xi_disable_devices (dpyinfo, disabled, n_disabled);
n_disabled = 0;
@ -23753,12 +23828,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
| XkbModifierMapMask
| XkbVirtualModsMask),
dpyinfo->xkb_desc) == Success)
XkbGetNames (dpyinfo->display,
XkbGroupNamesMask | XkbVirtualModNamesMask,
XkbGetNames (dpyinfo->display, XkbAllNamesMask,
dpyinfo->xkb_desc);
else
{
XkbFreeKeyboard (dpyinfo->xkb_desc, XkbAllComponentsMask, True);
XkbFreeKeyboard (dpyinfo->xkb_desc,
XkbAllComponentsMask, True);
dpyinfo->xkb_desc = NULL;
}
}
@ -23772,8 +23847,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
XkbUseCoreKbd);
if (dpyinfo->xkb_desc)
XkbGetNames (dpyinfo->display,
XkbGroupNamesMask | XkbVirtualModNamesMask,
XkbGetNames (dpyinfo->display, XkbAllNamesMask,
dpyinfo->xkb_desc);
}
@ -24063,6 +24137,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (inev.ie.kind != NO_EVENT)
x_dnd_update_tooltip_now ();
}
#endif
#ifdef HAVE_XFIXES
if (dpyinfo->xfixes_supported_p
&& event->type == (dpyinfo->xfixes_event_base
+ XFixesSelectionNotify))
x_handle_selection_monitor_event (dpyinfo, event);
#endif
OTHER:
#ifdef USE_X_TOOLKIT
@ -28564,6 +28644,27 @@ xi_check_toolkit (Display *display)
#endif
#ifdef HAVE_XFIXES
/* Create and return a special window for receiving events such as
selection notify events. The window is an 1x1 unmapped
override-redirect InputOnly window at -1, -1, which should prevent
it from doing anything. */
static Window
x_create_special_window (struct x_display_info *dpyinfo)
{
XSetWindowAttributes attrs;
attrs.override_redirect = True;
return XCreateWindow (dpyinfo->display, dpyinfo->root_window,
-1, -1, 1, 1, 0, CopyFromParent, InputOnly,
CopyFromParent, CWOverrideRedirect, &attrs);
}
#endif
/* Open a connection to X display DISPLAY_NAME, and return the
structure that describes the open display. If obtaining the XCB
connection or toolkit-specific display fails, return NULL. Signal
@ -28585,6 +28686,22 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
GdkDisplay *gdpy;
GdkScreen *gscr;
#endif
#ifdef HAVE_XFIXES
Lisp_Object tem, lisp_name;
int num_fast_selections;
Atom selection_name;
#ifdef USE_XCB
xcb_get_selection_owner_cookie_t *selection_cookies;
xcb_get_selection_owner_reply_t *selection_reply;
xcb_generic_error_t *selection_error;
#endif
#endif
int i;
USE_SAFE_ALLOCA;
/* Avoid warnings when SAFE_ALLOCA is not actually used. */
((void) SAFE_ALLOCA (0));
block_input ();
@ -28737,12 +28854,14 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
#endif
unblock_input ();
SAFE_FREE ();
return 0;
}
#ifdef USE_XCB
xcb_conn = XGetXCBConnection (dpy);
if (xcb_conn == 0)
if (!xcb_conn)
{
#ifdef USE_GTK
xg_display_close (dpy);
@ -28755,6 +28874,8 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
#endif /* ! USE_GTK */
unblock_input ();
SAFE_FREE ();
return 0;
}
#endif
@ -29307,8 +29428,7 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
XkbUseCoreKbd);
if (dpyinfo->xkb_desc)
XkbGetNames (dpyinfo->display,
XkbGroupNamesMask | XkbVirtualModNamesMask,
XkbGetNames (dpyinfo->display, XkbAllNamesMask,
dpyinfo->xkb_desc);
XkbSelectEvents (dpyinfo->display, XkbUseCoreKbd,
@ -29318,9 +29438,10 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
#endif
#ifdef HAVE_XFIXES
int xfixes_event_base, xfixes_error_base;
int xfixes_error_base;
dpyinfo->xfixes_supported_p
= XFixesQueryExtension (dpyinfo->display, &xfixes_event_base,
= XFixesQueryExtension (dpyinfo->display,
&dpyinfo->xfixes_event_base,
&xfixes_error_base);
if (dpyinfo->xfixes_supported_p)
@ -29371,7 +29492,6 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
XScreenNumberOfScreen (dpyinfo->screen));
{
int i;
enum { atom_count = ARRAYELTS (x_atom_refs) };
/* 1 for _XSETTINGS_SN. */
enum { total_atom_count = 2 + atom_count };
@ -29539,8 +29659,100 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
dpyinfo->protected_windows_max = 256;
#endif
#ifdef HAVE_XFIXES
/* Initialize selection tracking for the selections in
x-fast-selection-list. */
if (CONSP (Vx_fast_selection_list)
&& dpyinfo->xfixes_supported_p
&& dpyinfo->xfixes_major >= 1)
{
num_fast_selections = 0;
tem = Vx_fast_selection_list;
FOR_EACH_TAIL_SAFE (tem)
{
if (!SYMBOLP (XCAR (tem)))
continue;
num_fast_selections++;
}
dpyinfo->n_monitored_selections = num_fast_selections;
dpyinfo->selection_tracking_window
= x_create_special_window (dpyinfo);
dpyinfo->monitored_selections
= xmalloc (num_fast_selections
* sizeof *dpyinfo->monitored_selections);
num_fast_selections = 0;
tem = Vx_fast_selection_list;
FOR_EACH_TAIL_SAFE (tem)
{
lisp_name = XCAR (tem);
if (!SYMBOLP (lisp_name))
continue;
selection_name = symbol_to_x_atom (dpyinfo, lisp_name);
dpyinfo->monitored_selections[num_fast_selections++].name
= selection_name;
dpyinfo->monitored_selections[num_fast_selections - 1].owner
= X_INVALID_WINDOW;
/* Select for selection input. */
XFixesSelectSelectionInput (dpyinfo->display,
dpyinfo->selection_tracking_window,
selection_name,
(XFixesSetSelectionOwnerNotifyMask
| XFixesSetSelectionOwnerNotifyMask
| XFixesSelectionClientCloseNotifyMask));
}
#ifdef USE_XCB
selection_cookies = SAFE_ALLOCA (sizeof *selection_cookies
* num_fast_selections);
#endif
/* Now, ask for the current owners of all those selections. */
for (i = 0; i < num_fast_selections; ++i)
{
#ifdef USE_XCB
selection_cookies[i]
= xcb_get_selection_owner (dpyinfo->xcb_connection,
dpyinfo->monitored_selections[i].name);
#else
dpyinfo->monitored_selections[i].owner
= XGetSelectionOwner (dpyinfo->display,
dpyinfo->monitored_selections[i].name);
#endif
}
#ifdef USE_XCB
for (i = 0; i < num_fast_selections; ++i)
{
selection_reply
= xcb_get_selection_owner_reply (dpyinfo->xcb_connection,
selection_cookies[i],
&selection_error);
if (selection_reply)
{
dpyinfo->monitored_selections[i].owner
= selection_reply->owner;
free (selection_reply);
}
else if (selection_error)
free (selection_error);
}
#endif
}
#endif
unblock_input ();
SAFE_FREE ();
return dpyinfo;
}
@ -29676,6 +29888,10 @@ x_delete_display (struct x_display_info *dpyinfo)
xfree (dpyinfo->x_id_name);
xfree (dpyinfo->x_dnd_atoms);
xfree (dpyinfo->color_cells);
#ifdef HAVE_XFIXES
if (dpyinfo->monitored_selections)
xfree (dpyinfo->monitored_selections);
#endif
#ifdef USE_TOOLKIT_SCROLL_BARS
xfree (dpyinfo->protected_windows);
#endif
@ -30643,4 +30859,17 @@ It should accept a single argument, a string describing the locale of
the input method, and return a coding system that can decode keyboard
input generated by said input method. */);
Vx_input_coding_function = Qnil;
DEFVAR_LISP ("x-fast-selection-list", Vx_fast_selection_list,
doc: /* List of selections for which `x-selection-exists-p' should be fast.
List of selection names as atoms that will be monitored by Emacs for
ownership changes when the X server supports the XFIXES extension.
The result of the monitoring is then used by `x-selection-exists-p' to
avoid a server round trip, which is important as it is called while
updating the tool bar. The value of this variable is only read upon
connection setup. */);
/* The default value of this variable is chosen so that updating the
tool bar does not require a call to _XReply. */
Vx_fast_selection_list = list1 (QCLIPBOARD);
}

View file

@ -308,6 +308,22 @@ struct x_failable_request
unsigned long end;
};
#ifdef HAVE_XFIXES
struct x_monitored_selection
{
/* The name of the selection. */
Atom name;
/* The current owner of the selection. */
Window owner;
};
/* An invalid window. */
#define X_INVALID_WINDOW 0xffffffff
#endif
/* For each X display, we have a structure that records
information about it. */
@ -778,6 +794,7 @@ struct x_display_info
bool xfixes_supported_p;
int xfixes_major;
int xfixes_minor;
int xfixes_event_base;
#endif
#ifdef HAVE_XSYNC
@ -828,6 +845,17 @@ struct x_display_info
/* Pointer to the next request in `failable_requests'. */
struct x_failable_request *next_failable_request;
#ifdef HAVE_XFIXES
/* Array of selections being monitored and their owners. */
struct x_monitored_selection *monitored_selections;
/* Window used to monitor those selections. */
Window selection_tracking_window;
/* The number of those selections. */
int n_monitored_selections;
#endif
/* The pending drag-and-drop time for middle-click based
drag-and-drop emulation. */
Time pending_dnd_time;
@ -1656,6 +1684,10 @@ extern void x_cr_draw_frame (cairo_t *, struct frame *);
extern Lisp_Object x_cr_export_frames (Lisp_Object, cairo_surface_type_t);
#endif
#ifdef HAVE_XFIXES
extern Window x_find_selection_owner (struct x_display_info *, Atom);
#endif
#ifdef HAVE_XRENDER
extern void x_xrender_color_from_gc_background (struct frame *, GC,
XRenderColor *, bool);
@ -1664,6 +1696,8 @@ extern void x_xr_apply_ext_clip (struct frame *, GC);
extern void x_xr_reset_ext_clip (struct frame *);
#endif
extern void x_translate_coordinates_to_root (struct frame *, int, int,
int *, int *);
extern Bool x_query_pointer (Display *, Window, Window *, Window *, int *,
int *, int *, int *, unsigned int *);