diff options
| author | Po Lu | 2022-08-07 13:46:52 +0800 |
|---|---|---|
| committer | Po Lu | 2022-10-20 19:30:50 +0800 |
| commit | 6f3ade1c08c6cbf56c0dc0d12e9508c261eb42bf (patch) | |
| tree | 5eb2e8320ee3b8eb80acd209112908fb65f3db0e | |
| parent | e0616f2d3cbc559560bf15346dd8a1824603bd80 (diff) | |
| download | emacs-6f3ade1c08c6cbf56c0dc0d12e9508c261eb42bf.tar.gz emacs-6f3ade1c08c6cbf56c0dc0d12e9508c261eb42bf.zip | |
Work around problems setting input focus when a frame is in the background
* src/xterm.c (server_timestamp_predicate, x_get_server_time):
New functions.
(x_ewmh_activate_frame, x_focus_frame, syms_of_xterm): Apply
various workarounds for window manager "focus stealing
prevention". (bug#57012)
| -rw-r--r-- | src/xterm.c | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/src/xterm.c b/src/xterm.c index ade5600f4da..8b3d6f77a6c 100644 --- a/src/xterm.c +++ b/src/xterm.c | |||
| @@ -27134,6 +27134,64 @@ xembed_request_focus (struct frame *f) | |||
| 27134 | XEMBED_REQUEST_FOCUS, 0, 0, 0); | 27134 | XEMBED_REQUEST_FOCUS, 0, 0, 0); |
| 27135 | } | 27135 | } |
| 27136 | 27136 | ||
| 27137 | static Bool | ||
| 27138 | server_timestamp_predicate (Display *display, XEvent *xevent, | ||
| 27139 | XPointer arg) | ||
| 27140 | { | ||
| 27141 | XID *args = (XID *) arg; | ||
| 27142 | |||
| 27143 | if (xevent->type == PropertyNotify | ||
| 27144 | && xevent->xproperty.window == args[0] | ||
| 27145 | && xevent->xproperty.atom == args[1]) | ||
| 27146 | return True; | ||
| 27147 | |||
| 27148 | return False; | ||
| 27149 | } | ||
| 27150 | |||
| 27151 | /* Get the server time. The X server is guaranteed to deliver the | ||
| 27152 | PropertyNotify event, so there is no reason to use x_if_event. */ | ||
| 27153 | |||
| 27154 | static Time | ||
| 27155 | x_get_server_time (struct frame *f) | ||
| 27156 | { | ||
| 27157 | Atom property_atom; | ||
| 27158 | XEvent property_dummy; | ||
| 27159 | struct x_display_info *dpyinfo; | ||
| 27160 | XID client_data[2]; | ||
| 27161 | #if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME | ||
| 27162 | uint_fast64_t current_monotonic_time; | ||
| 27163 | #endif | ||
| 27164 | |||
| 27165 | /* If the server time is the same as the monotonic time, avoid a | ||
| 27166 | roundtrip by using that instead. */ | ||
| 27167 | |||
| 27168 | #if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME | ||
| 27169 | if (FRAME_DISPLAY_INFO (f)->server_time_monotonic_p) | ||
| 27170 | { | ||
| 27171 | current_monotonic_time = x_sync_current_monotonic_time (); | ||
| 27172 | |||
| 27173 | if (current_monotonic_time) | ||
| 27174 | /* Truncate the time to CARD32. */ | ||
| 27175 | return (current_monotonic_time / 1000) & X_ULONG_MAX; | ||
| 27176 | } | ||
| 27177 | #endif | ||
| 27178 | |||
| 27179 | dpyinfo = FRAME_DISPLAY_INFO (f); | ||
| 27180 | property_atom = dpyinfo->Xatom_EMACS_SERVER_TIME_PROP; | ||
| 27181 | client_data[0] = FRAME_OUTER_WINDOW (f); | ||
| 27182 | client_data[1] = property_atom; | ||
| 27183 | |||
| 27184 | XChangeProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f), | ||
| 27185 | property_atom, XA_ATOM, 32, | ||
| 27186 | PropModeReplace, | ||
| 27187 | (unsigned char *) &property_atom, 1); | ||
| 27188 | |||
| 27189 | XIfEvent (dpyinfo->display, &property_dummy, | ||
| 27190 | server_timestamp_predicate, (XPointer) client_data); | ||
| 27191 | |||
| 27192 | return property_dummy.xproperty.time; | ||
| 27193 | } | ||
| 27194 | |||
| 27137 | /* Activate frame with Extended Window Manager Hints */ | 27195 | /* Activate frame with Extended Window Manager Hints */ |
| 27138 | 27196 | ||
| 27139 | static void | 27197 | static void |
| @@ -27141,6 +27199,7 @@ x_ewmh_activate_frame (struct frame *f) | |||
| 27141 | { | 27199 | { |
| 27142 | XEvent msg; | 27200 | XEvent msg; |
| 27143 | struct x_display_info *dpyinfo; | 27201 | struct x_display_info *dpyinfo; |
| 27202 | Time time; | ||
| 27144 | 27203 | ||
| 27145 | dpyinfo = FRAME_DISPLAY_INFO (f); | 27204 | dpyinfo = FRAME_DISPLAY_INFO (f); |
| 27146 | 27205 | ||
| @@ -27161,6 +27220,43 @@ x_ewmh_activate_frame (struct frame *f) | |||
| 27161 | msg.xclient.data.l[3] = 0; | 27220 | msg.xclient.data.l[3] = 0; |
| 27162 | msg.xclient.data.l[4] = 0; | 27221 | msg.xclient.data.l[4] = 0; |
| 27163 | 27222 | ||
| 27223 | /* No frame is currently focused on that display, so apply any | ||
| 27224 | bypass for focus stealing prevention that the user has | ||
| 27225 | specified. */ | ||
| 27226 | if (!dpyinfo->x_focus_frame) | ||
| 27227 | { | ||
| 27228 | if (EQ (Vx_allow_focus_stealing, Qimitate_pager)) | ||
| 27229 | msg.xclient.data.l[0] = 2; | ||
| 27230 | else if (EQ (Vx_allow_focus_stealing, Qnewer_time)) | ||
| 27231 | { | ||
| 27232 | block_input (); | ||
| 27233 | time = x_get_server_time (f); | ||
| 27234 | #ifdef USE_GTK | ||
| 27235 | x_set_gtk_user_time (f, time); | ||
| 27236 | #endif | ||
| 27237 | /* Temporarily override dpyinfo->x_focus_frame so the | ||
| 27238 | user time property is set on the right window. */ | ||
| 27239 | dpyinfo->x_focus_frame = f; | ||
| 27240 | x_display_set_last_user_time (dpyinfo, time, true, true); | ||
| 27241 | dpyinfo->x_focus_frame = NULL; | ||
| 27242 | unblock_input (); | ||
| 27243 | |||
| 27244 | msg.xclient.data.l[1] = time; | ||
| 27245 | } | ||
| 27246 | else if (EQ (Vx_allow_focus_stealing, Qraise_and_focus)) | ||
| 27247 | { | ||
| 27248 | time = x_get_server_time (f); | ||
| 27249 | |||
| 27250 | x_ignore_errors_for_next_request (dpyinfo); | ||
| 27251 | XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), | ||
| 27252 | RevertToParent, time); | ||
| 27253 | XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f)); | ||
| 27254 | x_stop_ignoring_errors (dpyinfo); | ||
| 27255 | |||
| 27256 | return; | ||
| 27257 | } | ||
| 27258 | } | ||
| 27259 | |||
| 27164 | XSendEvent (dpyinfo->display, dpyinfo->root_window, | 27260 | XSendEvent (dpyinfo->display, dpyinfo->root_window, |
| 27165 | False, (SubstructureRedirectMask | 27261 | False, (SubstructureRedirectMask |
| 27166 | | SubstructureNotifyMask), &msg); | 27262 | | SubstructureNotifyMask), &msg); |
| @@ -30649,6 +30745,9 @@ With MS Windows, Haiku windowing or Nextstep, the value is t. */); | |||
| 30649 | Fput (Qsuper, Qmodifier_value, make_fixnum (super_modifier)); | 30745 | Fput (Qsuper, Qmodifier_value, make_fixnum (super_modifier)); |
| 30650 | DEFSYM (QXdndSelection, "XdndSelection"); | 30746 | DEFSYM (QXdndSelection, "XdndSelection"); |
| 30651 | DEFSYM (Qx_selection_alias_alist, "x-selection-alias-alist"); | 30747 | DEFSYM (Qx_selection_alias_alist, "x-selection-alias-alist"); |
| 30748 | DEFSYM (Qimitate_pager, "imitate-pager"); | ||
| 30749 | DEFSYM (Qnewer_time, "newer-time"); | ||
| 30750 | DEFSYM (Qraise_and_focus, "raise-and-focus"); | ||
| 30652 | 30751 | ||
| 30653 | DEFVAR_LISP ("x-ctrl-keysym", Vx_ctrl_keysym, | 30752 | DEFVAR_LISP ("x-ctrl-keysym", Vx_ctrl_keysym, |
| 30654 | doc: /* Which keys Emacs uses for the ctrl modifier. | 30753 | doc: /* Which keys Emacs uses for the ctrl modifier. |
| @@ -30902,4 +31001,24 @@ connection setup. */); | |||
| 30902 | /* The default value of this variable is chosen so that updating the | 31001 | /* The default value of this variable is chosen so that updating the |
| 30903 | tool bar does not require a call to _XReply. */ | 31002 | tool bar does not require a call to _XReply. */ |
| 30904 | Vx_fast_selection_list = list1 (QCLIPBOARD); | 31003 | Vx_fast_selection_list = list1 (QCLIPBOARD); |
| 31004 | |||
| 31005 | DEFVAR_LISP ("x-allow-focus-stealing", Vx_allow_focus_stealing, | ||
| 31006 | doc: /* How to bypass window manager focus stealing prevention. | ||
| 31007 | |||
| 31008 | Some window managers prevent `x-focus-frame' from activating the given | ||
| 31009 | frame when Emacs is in the background, which is especially prone to | ||
| 31010 | cause problems when the Emacs server wants to activate itself. This | ||
| 31011 | variable specifies the strategy used to activate frames when that is | ||
| 31012 | the case, and has several valid values (any other value means to not | ||
| 31013 | bypass window manager focus stealing prevention): | ||
| 31014 | |||
| 31015 | - The symbol `imitate-pager', which means to pretend that Emacs is a | ||
| 31016 | pager. | ||
| 31017 | |||
| 31018 | - The symbol `newer-time', which means to fetch the current time | ||
| 31019 | from the X server and use it to activate the frame. | ||
| 31020 | |||
| 31021 | - The symbol `raise-and-focus', which means to raise the window and | ||
| 31022 | focus it manually. */); | ||
| 31023 | Vx_allow_focus_stealing = Qnewer_time; | ||
| 30905 | } | 31024 | } |