diff options
| author | Daniel Colascione | 2022-08-06 23:42:36 -0400 |
|---|---|---|
| committer | Daniel Colascione | 2022-08-06 23:44:07 -0400 |
| commit | 4b98a79a508ebdc719abfcf51ee6de32e46d0e1c (patch) | |
| tree | 7580788968b149de63378447c91afeb88405f2cb /src/xterm.c | |
| parent | 02ee0254873d4c102728c942dc9659f942c5cfa6 (diff) | |
| download | emacs-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.c | 126 |
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 | ||
| 7526 | static void | 7528 | static void |
| 7527 | x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time, | 7529 | x_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 | |||
| 7593 | static void | ||
| 7594 | x_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 | ||
| 25888 | static void | 25899 | Return whether we were successful in doing so. */ |
| 25900 | |||
| 25901 | static bool | ||
| 25889 | x_ewmh_activate_frame (struct frame *f) | 25902 | x_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 | ||
| 25920 | static Lisp_Object | 25934 | static 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 | ||
| 28591 | static Bool | ||
| 28592 | server_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 | |||
| 28606 | static bool | ||
| 28607 | x_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 | |||
| 28625 | static void | ||
| 28626 | x_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; |