summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2022-10-17 20:56:20 +0800
committerPo Lu <luangruo@yahoo.com>2022-10-17 21:00:09 +0800
commitabf683bb0324b9c5d01adb90aedb6aa6fa7175e9 (patch)
tree1f28271f1c12d4e7bbba87e39433f802d2d0d55c
parentb9aff5fdb89092b68ebd7782c8dc85e6daca14b2 (diff)
downloademacs-abf683bb0324b9c5d01adb90aedb6aa6fa7175e9.tar.gz
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.
-rw-r--r--lisp/menu-bar.el6
-rw-r--r--src/xfns.c115
-rw-r--r--src/xmenu.c45
-rw-r--r--src/xselect.c25
-rw-r--r--src/xterm.c255
-rw-r--r--src/xterm.h34
6 files changed, 348 insertions, 132 deletions
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index c2c18320b15..526bccbbac9 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -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
diff --git a/src/xfns.c b/src/xfns.c
index 91124488994..e8732986eb9 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -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
diff --git a/src/xmenu.c b/src/xmenu.c
index 1452b3c6d12..9d35e3529fb 100644
--- a/src/xmenu.c
+++ b/src/xmenu.c
@@ -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;
diff --git a/src/xselect.c b/src/xselect.c
index 66782d41723..498c28af536 100644
--- a/src/xselect.c
+++ b/src/xselect.c
@@ -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);
}
diff --git a/src/xterm.c b/src/xterm.c
index ee6db62bb94..07a8c5e1c3f 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -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);
}
@@ -24064,6 +24138,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
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
if (*finish != X_EVENT_DROP)
@@ -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);
}
diff --git a/src/xterm.h b/src/xterm.h
index 55fd193a29f..0f00dc42f79 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -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 *);