aboutsummaryrefslogtreecommitdiffstats
path: root/src/xterm.c
diff options
context:
space:
mode:
authorDaniel Colascione2022-08-06 23:42:36 -0400
committerDaniel Colascione2022-08-06 23:44:07 -0400
commit4b98a79a508ebdc719abfcf51ee6de32e46d0e1c (patch)
tree7580788968b149de63378447c91afeb88405f2cb /src/xterm.c
parent02ee0254873d4c102728c942dc9659f942c5cfa6 (diff)
downloademacs-4b98a79a508ebdc719abfcf51ee6de32e46d0e1c.tar.gz
emacs-4b98a79a508ebdc719abfcf51ee6de32e46d0e1c.zip
Improve X event timestamp tracking
Fix two problems with our handling of X timestamps 1) We're not properly updating the X interaction timestamp after receiving certain input events, and 2) X events sent in response to emacsclient commands get stale timestamps because the timestamp tracking doesn't take into account that interactions with the user can occur outside the X input event channel. * src/xterm.c: (x_display_set_last_user_time_1): New function. (x_display_set_last_user_time): Call it. (x_ewmh_activate_frame): Refactor. (x_focus_frame): Don't call XSetInputFocus if we can use EWMH activation. (server_timestamp_predicate): New function. (x_get_server_time): New function. (x_note_oob_interaction): New function. (x_create_terminal): Register new function as terminal hook. * src/termhooks.h: New hook: note_oob_interaction_hook. * src/gtkutil.h: (xg_set_user_timestamp): Declare. * src/gtkutil.c: (xg_set_user_timestamp): New function. * src/frame.c: (Fframe_note_oob_interaction): New function. (syms_of_frame): Register it. * lisp/server.el: (server-switch-buffer): Call frame-note-oob-interaction when user requests frame be raised.
Diffstat (limited to 'src/xterm.c')
-rw-r--r--src/xterm.c126
1 files changed, 94 insertions, 32 deletions
diff --git a/src/xterm.c b/src/xterm.c
index 97985c8d9e7..29295cfe7bb 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -6581,12 +6581,6 @@ x_set_frame_alpha (struct frame *f)
6581 x_stop_ignoring_errors (dpyinfo); 6581 x_stop_ignoring_errors (dpyinfo);
6582} 6582}
6583 6583
6584/***********************************************************************
6585 Starting and ending an update
6586 ***********************************************************************/
6587
6588#if defined HAVE_XSYNC && !defined USE_GTK
6589
6590/* Wait for an event matching PREDICATE to show up in the event 6584/* Wait for an event matching PREDICATE to show up in the event
6591 queue, or TIMEOUT to elapse. 6585 queue, or TIMEOUT to elapse.
6592 6586
@@ -6640,6 +6634,12 @@ x_if_event (Display *dpy, XEvent *event_return,
6640 } 6634 }
6641} 6635}
6642 6636
6637/***********************************************************************
6638 Starting and ending an update
6639 ***********************************************************************/
6640
6641#if defined HAVE_XSYNC && !defined USE_GTK
6642
6643/* Return the monotonic time corresponding to the high-resolution 6643/* Return the monotonic time corresponding to the high-resolution
6644 server timestamp TIMESTAMP. Return 0 if the necessary information 6644 server timestamp TIMESTAMP. Return 0 if the necessary information
6645 is not available. */ 6645 is not available. */
@@ -7521,26 +7521,25 @@ static void x_check_font (struct frame *, struct font *);
7521 user time. We don't sanitize timestamps from events sent by the X 7521 user time. We don't sanitize timestamps from events sent by the X
7522 server itself because some Lisp might have set the user time to a 7522 server itself because some Lisp might have set the user time to a
7523 ridiculously large value, and this way a more reasonable timestamp 7523 ridiculously large value, and this way a more reasonable timestamp
7524 can be obtained upon the next event. */ 7524 can be obtained upon the next event. If EXPLICIT_FRAME is NULL,
7525 update the focused frame's timestamp; otherwise, update
7526 EXPLICIT_FRAME's. */
7525 7527
7526static void 7528static void
7527x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time, 7529x_display_set_last_user_time_1 (struct x_display_info *dpyinfo, Time time,
7528 bool send_event) 7530 bool send_event,
7531 struct frame *explicit_frame)
7529{ 7532{
7530#ifndef USE_GTK 7533 struct frame *frame;
7531 struct frame *focus_frame;
7532 Time old_time; 7534 Time old_time;
7533#if defined HAVE_XSYNC 7535#if defined HAVE_XSYNC && !defined USE_GTK
7534 uint64_t monotonic_time; 7536 uint64_t monotonic_time;
7535#endif 7537#endif
7536 7538
7537 focus_frame = dpyinfo->x_focus_frame; 7539 frame = explicit_frame ? explicit_frame : dpyinfo->x_focus_frame;
7538 old_time = dpyinfo->last_user_time; 7540 old_time = dpyinfo->last_user_time;
7539#endif
7540 7541
7541#ifdef ENABLE_CHECKING
7542 eassert (time <= X_ULONG_MAX); 7542 eassert (time <= X_ULONG_MAX);
7543#endif
7544 7543
7545 if (!send_event || time > dpyinfo->last_user_time) 7544 if (!send_event || time > dpyinfo->last_user_time)
7546 dpyinfo->last_user_time = time; 7545 dpyinfo->last_user_time = time;
@@ -7567,23 +7566,35 @@ x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time,
7567 } 7566 }
7568#endif 7567#endif
7569 7568
7570#ifndef USE_GTK 7569 /* Don't waste bandwidth if the time hasn't actually changed.
7571 /* Don't waste bandwidth if the time hasn't actually changed. */ 7570 Update anyway if we're updating the timestamp for a non-focused
7572 if (focus_frame && old_time != dpyinfo->last_user_time) 7571 frame, since the event loop might not have gotten around to
7572 updating that frame's timestamp. */
7573 if (frame && (explicit_frame || old_time != dpyinfo->last_user_time))
7573 { 7574 {
7574 time = dpyinfo->last_user_time; 7575 time = dpyinfo->last_user_time;
7575 7576
7576 while (FRAME_PARENT_FRAME (focus_frame)) 7577 while (FRAME_PARENT_FRAME (frame))
7577 focus_frame = FRAME_PARENT_FRAME (focus_frame); 7578 frame = FRAME_PARENT_FRAME (frame);
7578 7579
7579 if (FRAME_X_OUTPUT (focus_frame)->user_time_window != None) 7580#if defined USE_GTK
7581 xg_set_user_timestamp (frame, time);
7582#else
7583 if (FRAME_X_OUTPUT (frame)->user_time_window != None)
7580 XChangeProperty (dpyinfo->display, 7584 XChangeProperty (dpyinfo->display,
7581 FRAME_X_OUTPUT (focus_frame)->user_time_window, 7585 FRAME_X_OUTPUT (frame)->user_time_window,
7582 dpyinfo->Xatom_net_wm_user_time, 7586 dpyinfo->Xatom_net_wm_user_time,
7583 XA_CARDINAL, 32, PropModeReplace, 7587 XA_CARDINAL, 32, PropModeReplace,
7584 (unsigned char *) &time, 1); 7588 (unsigned char *) &time, 1);
7585 }
7586#endif 7589#endif
7590 }
7591}
7592
7593static void
7594x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time,
7595 bool send_event)
7596{
7597 x_display_set_last_user_time_1 (dpyinfo, time, send_event, NULL);
7587} 7598}
7588 7599
7589#ifdef USE_GTK 7600#ifdef USE_GTK
@@ -25883,9 +25894,11 @@ xembed_request_focus (struct frame *f)
25883 XEMBED_REQUEST_FOCUS, 0, 0, 0); 25894 XEMBED_REQUEST_FOCUS, 0, 0, 0);
25884} 25895}
25885 25896
25886/* Activate frame with Extended Window Manager Hints */ 25897/* Activate frame with Extended Window Manager Hints
25887 25898
25888static void 25899Return whether we were successful in doing so. */
25900
25901static bool
25889x_ewmh_activate_frame (struct frame *f) 25902x_ewmh_activate_frame (struct frame *f)
25890{ 25903{
25891 XEvent msg; 25904 XEvent msg;
@@ -25893,8 +25906,7 @@ x_ewmh_activate_frame (struct frame *f)
25893 25906
25894 dpyinfo = FRAME_DISPLAY_INFO (f); 25907 dpyinfo = FRAME_DISPLAY_INFO (f);
25895 25908
25896 if (FRAME_VISIBLE_P (f) 25909 if (x_wm_supports (f, dpyinfo->Xatom_net_active_window))
25897 && x_wm_supports (f, dpyinfo->Xatom_net_active_window))
25898 { 25910 {
25899 /* See the documentation at 25911 /* See the documentation at
25900 https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html 25912 https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html
@@ -25914,7 +25926,9 @@ x_ewmh_activate_frame (struct frame *f)
25914 XSendEvent (dpyinfo->display, dpyinfo->root_window, 25926 XSendEvent (dpyinfo->display, dpyinfo->root_window,
25915 False, (SubstructureRedirectMask 25927 False, (SubstructureRedirectMask
25916 | SubstructureNotifyMask), &msg); 25928 | SubstructureNotifyMask), &msg);
25929 return true;
25917 } 25930 }
25931 return false;
25918} 25932}
25919 25933
25920static Lisp_Object 25934static Lisp_Object
@@ -25952,16 +25966,14 @@ x_focus_frame (struct frame *f, bool noactivate)
25952 events. See XEmbed Protocol Specification at 25966 events. See XEmbed Protocol Specification at
25953 https://freedesktop.org/wiki/Specifications/xembed-spec/ */ 25967 https://freedesktop.org/wiki/Specifications/xembed-spec/ */
25954 xembed_request_focus (f); 25968 xembed_request_focus (f);
25955 else 25969 else if (noactivate ||
25970 (!FRAME_PARENT_FRAME (f) && !x_ewmh_activate_frame (f)))
25956 { 25971 {
25957 /* Ignore any BadMatch error this request might result in. */ 25972 /* Ignore any BadMatch error this request might result in. */
25958 x_ignore_errors_for_next_request (dpyinfo); 25973 x_ignore_errors_for_next_request (dpyinfo);
25959 XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), 25974 XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
25960 RevertToParent, CurrentTime); 25975 RevertToParent, CurrentTime);
25961 x_stop_ignoring_errors (dpyinfo); 25976 x_stop_ignoring_errors (dpyinfo);
25962
25963 if (!noactivate)
25964 x_ewmh_activate_frame (f);
25965 } 25977 }
25966} 25978}
25967 25979
@@ -28576,6 +28588,55 @@ x_have_any_grab (struct x_display_info *dpyinfo)
28576} 28588}
28577#endif 28589#endif
28578 28590
28591static Bool
28592server_timestamp_predicate (Display *display,
28593 XEvent *xevent,
28594 XPointer arg)
28595{
28596 XID *args = (XID *) arg;
28597
28598 if (xevent->type == PropertyNotify
28599 && xevent->xproperty.window == args[0]
28600 && xevent->xproperty.atom == args[1])
28601 return True;
28602
28603 return False;
28604}
28605
28606static bool
28607x_get_server_time (struct frame *f, Time *time)
28608{
28609 struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
28610 Atom property_atom = dpyinfo->Xatom_EMACS_SERVER_TIME_PROP;
28611 XEvent event;
28612
28613 XChangeProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f),
28614 property_atom, XA_ATOM, 32,
28615 PropModeReplace, (unsigned char *) &property_atom, 1);
28616
28617 if (x_if_event (dpyinfo->display, &event, server_timestamp_predicate,
28618 (XPointer) &(XID[]) {FRAME_OUTER_WINDOW (f), property_atom},
28619 dtotimespec (XFLOAT_DATA (Vx_wait_for_event_timeout))))
28620 return false;
28621 *time = event.xproperty.time;
28622 return true;
28623}
28624
28625static void
28626x_note_oob_interaction (struct frame *f)
28627{
28628 while (FRAME_PARENT_FRAME (f))
28629 f = FRAME_PARENT_FRAME (f);
28630 if (FRAME_LIVE_P (f))
28631 {
28632 Time server_time;
28633 if (!x_get_server_time (f, &server_time))
28634 error ("Timed out waiting for server timestamp");
28635 x_display_set_last_user_time_1 (
28636 FRAME_DISPLAY_INFO (f), server_time, false, f);
28637 }
28638}
28639
28579/* Create a struct terminal, initialize it with the X11 specific 28640/* Create a struct terminal, initialize it with the X11 specific
28580 functions and make DISPLAY->TERMINAL point to it. */ 28641 functions and make DISPLAY->TERMINAL point to it. */
28581 28642
@@ -28646,6 +28707,7 @@ x_create_terminal (struct x_display_info *dpyinfo)
28646#ifdef HAVE_XINPUT2 28707#ifdef HAVE_XINPUT2
28647 terminal->any_grab_hook = x_have_any_grab; 28708 terminal->any_grab_hook = x_have_any_grab;
28648#endif 28709#endif
28710 terminal->note_oob_interaction_hook = x_note_oob_interaction;
28649 /* Other hooks are NULL by default. */ 28711 /* Other hooks are NULL by default. */
28650 28712
28651 return terminal; 28713 return terminal;