commit 153c67fa92eaad39410b1809ab9b125616bdc5c1 (HEAD, refs/remotes/origin/master) Author: Jim Porter Date: Sun Nov 27 22:21:10 2022 -0800 Make 'server-stop-automatically' into a defcustom This changes the meaning of the (formerly internal) variable 'server-stop-automatically': it now always holds the requested configuration, even when Emacs was not started as a daemon (bug#59668). * lisp/server.el (server-stop-automatically): Convert the variable to a defcustom, and make the function simply set the defcustom. (server-stop-automatically--timer): New variable. (server-apply-stop-automatically): New function... (server-stop, server-start): ... call it. (server-save-buffers-kill-terminal): Adjust the conditions for stopping automatically to account for the change of meaning for 'server-stop-automatically'. (server-stop-automatically--handle-delete-frame): Remove unnecessary test of the 'server-stop-automatically' option; this hook is only set when it should do its job. (server-stop-automatically--maybe-kill-emacs): Update docstring. * doc/emacs/misc.texi (Emacs Server): Update documentation. diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi index 29c0bed19c0..702c72bac25 100644 --- a/doc/emacs/misc.texi +++ b/doc/emacs/misc.texi @@ -1808,31 +1808,28 @@ Emacs Server emacs --daemon=foo @end example -@findex server-stop-automatically +@vindex server-stop-automatically The Emacs server can optionally be stopped automatically when -certain conditions are met. To do this, call the function -@code{server-stop-automatically} in your init file (@pxref{Init -File}), with one of the following arguments: +certain conditions are met. To do this, set the option +@code{server-stop-automatically} to one of the following values: -@itemize -@item -With the argument @code{empty}, the server is stopped when it has no -clients, no unsaved file-visiting buffers and no running processes -anymore. - -@item -With the argument @code{delete-frame}, when the last client frame is -being closed, you are asked whether each unsaved file-visiting buffer -must be saved and each unfinished process can be stopped, and if so, -the server is stopped. - -@item -With the argument @code{kill-terminal}, when the last client frame is -being closed with @kbd{C-x C-c} (@code{save-buffers-kill-terminal}), -you are asked whether each unsaved file-visiting buffer must be saved -and each unfinished process can be stopped, and if so, the server is +@table @code +@item empty +This value causes the server to be stopped when it has no clients, no +unsaved file-visiting buffers and no running processes anymore. + +@item delete-frame +This value means that when the last client frame is being closed, you +are asked whether each unsaved file-visiting buffer must be saved and +each unfinished process can be stopped, and if so, the server is stopped. -@end itemize + +@item kill-terminal +This value means that when the last client frame is being closed with +@kbd{C-x C-c} (@code{save-buffers-kill-terminal}), you are asked +whether each unsaved file-visiting buffer must be saved and each +unfinished process can be stopped, and if so, the server is stopped. +@end table @findex server-eval-at If you have defined a server by a unique server name, it is possible diff --git a/lisp/server.el b/lisp/server.el index c988d3af0e5..b225e4d536d 100644 --- a/lisp/server.el +++ b/lisp/server.el @@ -273,6 +273,11 @@ server-client-instructions :version "28.1" :type 'boolean) +(defvar server-stop-automatically) ; Defined below to avoid recursive load. + +(defvar server-stop-automatically--timer nil + "The timer object for `server-stop-automatically--maybe-kill-emacs'.") + ;; We do not use `temporary-file-directory' here, because emacsclient ;; does not read the init file. (defvar server-socket-dir @@ -636,7 +641,8 @@ server-stop (setq stopped-p t server-process nil server-mode nil - global-minor-modes (delq 'server-mode global-minor-modes))) + global-minor-modes (delq 'server-mode global-minor-modes)) + (server-apply-stop-automatically)) (unwind-protect ;; Delete the socket files made by previous server ;; invocations. @@ -757,6 +763,7 @@ server-start (list :family 'local :service server-file :plist '(:authenticated t))))) + (server-apply-stop-automatically) (unless server-process (error "Could not start server process")) (server-log "Started server") (process-put server-process :server-file server-file) @@ -1769,9 +1776,6 @@ server-switch-buffer (when server-raise-frame (select-frame-set-input-focus (window-frame))))) -(defvar server-stop-automatically nil - "Internal status variable for `server-stop-automatically'.") - ;;;###autoload (defun server-save-buffers-kill-terminal (arg) ;; Called from save-buffers-kill-terminal in files.el. @@ -1779,11 +1783,19 @@ server-save-buffers-kill-terminal With ARG non-nil, silently save all file-visiting buffers, then kill. If emacsclient was started with a list of filenames to edit, then -only these files will be asked to be saved." - (let ((proc (frame-parameter nil 'client))) +only these files will be asked to be saved. + +When running Emacs as a daemon and with +`server-stop-automatically' (which see) set to `kill-terminal' or +`delete-frame', this function may call `save-buffers-kill-emacs' +if there are no other active clients." + (let ((stop-automatically + (and (daemonp) + (memq server-stop-automatically '(kill-terminal delete-frame)))) + (proc (frame-parameter nil 'client))) (cond ((eq proc 'nowait) ;; Nowait frames have no client buffer list. - (if (length> (frame-list) (if server-stop-automatically 2 1)) + (if (length> (frame-list) (if stop-automatically 2 1)) ;; If there are any other frames, only delete this one. ;; When `server-stop-automatically' is set, don't count ;; the daemon frame. @@ -1792,7 +1804,7 @@ server-save-buffers-kill-terminal ;; If we're the last frame standing, kill Emacs. (save-buffers-kill-emacs arg))) ((processp proc) - (if (or (not server-stop-automatically) + (if (or (not stop-automatically) (length> server-clients 1) (seq-some (lambda (frame) @@ -1819,14 +1831,13 @@ server-save-buffers-kill-terminal (t (error "Invalid client frame"))))) (defun server-stop-automatically--handle-delete-frame (_frame) - "Handle deletion of FRAME when `server-stop-automatically' is used." - (when (and server-stop-automatically - (null (cddr (frame-list)))) + "Handle deletion of FRAME when `server-stop-automatically' is `delete-frame'." + (when (null (cddr (frame-list))) (let ((server-stop-automatically nil)) (save-buffers-kill-emacs)))) (defun server-stop-automatically--maybe-kill-emacs () - "Handle closing of Emacs daemon when `server-stop-automatically' is used." + "Handle closing of Emacs daemon when `server-stop-automatically' is `empty'." (unless (cdr (frame-list)) (when (and (not (memq t (mapcar (lambda (b) @@ -1840,41 +1851,70 @@ server-stop-automatically--maybe-kill-emacs (process-list))))) (kill-emacs)))) -;;;###autoload -(defun server-stop-automatically (arg) - "Automatically stop server as specified by ARG. - -If ARG is the symbol `empty', stop the server when it has no +(defun server-apply-stop-automatically () + "Apply the current value of `server-stop-automatically'. +This function adds or removes the necessary helpers to manage +stopping the Emacs server automatically, depending on the whether +the server is running or not. This function only applies when +running Emacs as a daemon." + (when (daemonp) + (let (empty-timer-p delete-frame-p) + (when server-process + (pcase server-stop-automatically + ('empty (setq empty-timer-p t)) + ('delete-frame (setq delete-frame-p t)))) + ;; Start or stop the timer. + (if empty-timer-p + (unless server-stop-automatically--timer + (setq server-stop-automatically--timer + (run-with-timer + 10 2 + #'server-stop-automatically--maybe-kill-emacs))) + (when server-stop-automatically--timer + (cancel-timer server-stop-automatically--timer) + (setq server-stop-automatically--timer nil))) + ;; Add or remove the delete-frame hook. + (if delete-frame-p + (add-hook 'delete-frame-functions + #'server-stop-automatically--handle-delete-frame) + (remove-hook 'delete-frame-functions + #'server-stop-automatically--handle-delete-frame)))) + ;; Return the current value of `server-stop-automatically'. + server-stop-automatically) + +(defcustom server-stop-automatically nil + "If non-nil, stop the server under the requested conditions. + +If this is the symbol `empty', stop the server when it has no remaining clients, no remaining unsaved file-visiting buffers, and no running processes with a `query-on-exit' flag. -If ARG is the symbol `delete-frame', ask the user when the last +If this is the symbol `delete-frame', ask the user when the last frame is deleted whether each unsaved file-visiting buffer must be saved and each running process with a `query-on-exit' flag can be stopped, and if so, stop the server itself. -If ARG is the symbol `kill-terminal', ask the user when the +If this is the symbol `kill-terminal', ask the user when the terminal is killed with \\[save-buffers-kill-terminal] \ whether each unsaved file-visiting buffer must be saved and each running process with a `query-on-exit' -flag can be stopped, and if so, stop the server itself. - -Any other value of ARG will cause this function to signal an error. +flag can be stopped, and if so, stop the server itself." + :type '(choice + (const :tag "Never" nil) + (const :tag "When no clients, unsaved files, or processes" + empty) + (const :tag "When killing last terminal" kill-terminal) + (const :tag "When killing last terminal or frame" delete-frame)) + :set (lambda (symbol value) + (set-default symbol value) + (server-apply-stop-automatically)) + :version "29.1") -This function is meant to be called from the user init file." - (when (daemonp) - (setq server-stop-automatically arg) - (cond - ((eq arg 'empty) - (setq server-stop-automatically nil) - (run-with-timer 10 2 - #'server-stop-automatically--maybe-kill-emacs)) - ((eq arg 'delete-frame) - (add-hook 'delete-frame-functions - #'server-stop-automatically--handle-delete-frame)) - ((eq arg 'kill-terminal)) - (t - (error "Unexpected argument"))))) +;;;###autoload +(defun server-stop-automatically (value) + "Automatically stop the Emacs server as specified by VALUE. +This sets the variable `server-stop-automatically' (which see)." + (setopt server-stop-automatically value)) (define-key ctl-x-map "#" 'server-edit) commit 5d68cc99177569a661da0ac75bb7df1ec4098fda Author: Po Lu Date: Thu Dec 8 10:30:50 2022 +0800 Remove conversion_fail_tag from selection requests * src/xselect.c (struct x_selection_request): Delete `conversion_fail_tag'. (x_push_current_selection_request, x_start_selection_transfer): Stop checking against conversion_fail_tag. (x_handle_selection_request): Adjust callers. (x_convert_selection): Remove arg `for_multiple'. Never link nonconvertible data onto frame->converted_selections. diff --git a/src/xselect.c b/src/xselect.c index 121b17df1b0..191f0770337 100644 --- a/src/xselect.c +++ b/src/xselect.c @@ -16,7 +16,6 @@ You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ - /* Rewritten by jwz */ #include @@ -44,7 +43,7 @@ struct selection_data; static void x_decline_selection_request (struct selection_input_event *); -static bool x_convert_selection (Lisp_Object, Lisp_Object, Atom, bool, +static bool x_convert_selection (Lisp_Object, Lisp_Object, Atom, struct x_display_info *, bool); static bool waiting_for_other_props_on_window (Display *, Window); static struct prop_location *expect_property_change (Display *, Window, @@ -544,9 +543,6 @@ #define SELECTED_EVENTS 1 /* Linked list of the above (in support of MULTIPLE targets). */ struct selection_data *converted_selections; - /* "Data" to send a requestor for a failed MULTIPLE subtarget. */ - Atom conversion_fail_tag; - /* Whether or not conversion was successful. */ bool converted; }; @@ -608,7 +604,6 @@ x_push_current_selection_request (struct selection_input_event *se, frame->request = se; frame->dpyinfo = dpyinfo; frame->converted_selections = NULL; - frame->conversion_fail_tag = None; selection_request_stack = frame; } @@ -839,10 +834,6 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, secs = timeout / 1000; nsecs = (timeout % 1000) * 1000000; - if ((Atom *) data->data - == &selection_request_stack->conversion_fail_tag) - return; - transfer = xzalloc (sizeof *transfer); transfer->requestor = requestor; transfer->dpyinfo = dpyinfo; @@ -1135,7 +1126,9 @@ x_handle_selection_request (struct selection_input_event *event) ptrdiff_t j, nselections; struct selection_data cs; - if (property == None) goto DONE; + if (property == None) + goto DONE; + multprop = x_get_window_property_as_lisp_data (dpyinfo, requestor, property, QMULTIPLE, selection, true); @@ -1147,17 +1140,17 @@ x_handle_selection_request (struct selection_input_event *event) /* Perform conversions. This can signal. */ for (j = 0; j < nselections; j++) { - Lisp_Object subtarget = AREF (multprop, 2*j); + Lisp_Object subtarget = AREF (multprop, 2 * j); Atom subproperty = symbol_to_x_atom (dpyinfo, AREF (multprop, 2*j+1)); bool subsuccess = false; if (subproperty != None) subsuccess = x_convert_selection (selection_symbol, subtarget, - subproperty, true, dpyinfo, + subproperty, dpyinfo, use_alternate); if (!subsuccess) - ASET (multprop, 2*j+1, Qnil); + ASET (multprop, 2 * j + 1, Qnil); } /* Save conversion results */ @@ -1183,8 +1176,7 @@ x_handle_selection_request (struct selection_input_event *event) success = x_convert_selection (selection_symbol, target_symbol, property, - false, dpyinfo, - use_alternate); + dpyinfo, use_alternate); } DONE: @@ -1212,15 +1204,13 @@ x_handle_selection_request (struct selection_input_event *event) /* Perform the requested selection conversion, and write the data to the converted_selections linked list, where it can be accessed by - x_reply_selection_request. If FOR_MULTIPLE, write out - the data even if conversion fails, using conversion_fail_tag. + x_reply_selection_request. Return true if successful. */ static bool -x_convert_selection (Lisp_Object selection_symbol, - Lisp_Object target_symbol, Atom property, - bool for_multiple, struct x_display_info *dpyinfo, +x_convert_selection (Lisp_Object selection_symbol, Lisp_Object target_symbol, + Atom property, struct x_display_info *dpyinfo, bool use_alternate) { Lisp_Object lisp_selection; @@ -1236,23 +1226,7 @@ x_convert_selection (Lisp_Object selection_symbol, /* A nil return value means we can't perform the conversion. */ if (NILP (lisp_selection) || (CONSP (lisp_selection) && NILP (XCDR (lisp_selection)))) - { - if (for_multiple) - { - cs = xmalloc (sizeof *cs); - cs->data = ((unsigned char *) - &selection_request_stack->conversion_fail_tag); - cs->size = 1; - cs->string = Qnil; - cs->format = 32; - cs->type = XA_ATOM; - cs->property = property; - cs->next = frame->converted_selections; - frame->converted_selections = cs; - } - - return false; - } + return false; /* Otherwise, record the converted selection to binary. */ cs = xmalloc (sizeof *cs); commit 7ac9e57bedabcb260d9b73fed16a14bba2b377e9 Author: Sean Whitton Date: Wed Dec 7 13:12:13 2022 -0700 Font lock Commit: and CommitDate: lines in Git logs * lisp/vc/vc-git.el (vc-git-log-view-mode): Additionally recognize and font lock Commit: and CommitDate: lines. This is relevant when, for example, the user has added "--format=fuller" to vc-git-log-switches. diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 59dfb6c1252..83138a6d107 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -1434,11 +1434,11 @@ vc-git-log-view-mode `((,log-view-message-re (1 'change-log-acknowledgment))) ;; Handle the case: ;; user: foo@bar - '(("^Author:[ \t]+\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)" + '(("^\\(?:Author\\|Commit\\):[ \t]+\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)" (1 'change-log-email)) ;; Handle the case: ;; user: FirstName LastName - ("^Author:[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]" + ("^\\(?:Author\\|Commit\\):[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]" (1 'change-log-name) (2 'change-log-email)) ("^ +\\(?:\\(?:[Aa]cked\\|[Ss]igned-[Oo]ff\\)-[Bb]y:\\)[ \t]+\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)" @@ -1449,7 +1449,7 @@ vc-git-log-view-mode ("^Merge: \\([0-9a-z]+\\) \\([0-9a-z]+\\)" (1 'change-log-acknowledgment) (2 'change-log-acknowledgment)) - ("^\\(?:Date: \\|AuthorDate: \\)\\(.+\\)" (1 'change-log-date)) + ("^\\(?:Date: \\|AuthorDate: \\|CommitDate: \\)\\(.+\\)" (1 'change-log-date)) ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message))))))) commit 08d8fe0c33d3518ca322e6cecaf4b8a204496c96 Author: Rudolf Adamkovič Date: Wed Nov 30 01:01:43 2022 +0100 Capitalize unit names in Calc uniformly * lisp/calc/calc-units.el (math-unit-prefixes): Unify the inconsistent capitalization of the metric prefixes to lowercase. (Bug#59706) diff --git a/lisp/calc/calc-units.el b/lisp/calc/calc-units.el index 42156b94606..cc1f5085a7c 100644 --- a/lisp/calc/calc-units.el +++ b/lisp/calc/calc-units.el @@ -319,28 +319,28 @@ math-additional-units (defvar math-unit-prefixes '( ( ?Q (^ 10 30) "quetta" ) ( ?R (^ 10 27) "ronna" ) - ( ?Y (^ 10 24) "Yotta" ) - ( ?Z (^ 10 21) "Zetta" ) - ( ?E (^ 10 18) "Exa" ) - ( ?P (^ 10 15) "Peta" ) - ( ?T (^ 10 12) "Tera" ) - ( ?G (^ 10 9) "Giga" ) - ( ?M (^ 10 6) "Mega" ) - ( ?k (^ 10 3) "Kilo" ) - ( ?K (^ 10 3) "Kilo" ) - ( ?h (^ 10 2) "Hecto" ) - ( ?H (^ 10 2) "Hecto" ) - ( ?D (^ 10 1) "Deka" ) + ( ?Y (^ 10 24) "yotta" ) + ( ?Z (^ 10 21) "zetta" ) + ( ?E (^ 10 18) "exa" ) + ( ?P (^ 10 15) "peta" ) + ( ?T (^ 10 12) "tera" ) + ( ?G (^ 10 9) "giga" ) + ( ?M (^ 10 6) "mega" ) + ( ?k (^ 10 3) "kilo" ) + ( ?K (^ 10 3) "kilo" ) + ( ?h (^ 10 2) "hecto" ) + ( ?H (^ 10 2) "hecto" ) + ( ?D (^ 10 1) "deka" ) ( 0 (^ 10 0) nil ) - ( ?d (^ 10 -1) "Deci" ) - ( ?c (^ 10 -2) "Centi" ) - ( ?m (^ 10 -3) "Milli" ) - ( ?u (^ 10 -6) "Micro" ) - ( ?μ (^ 10 -6) "Micro" ) - ( ?n (^ 10 -9) "Nano" ) - ( ?p (^ 10 -12) "Pico" ) - ( ?f (^ 10 -15) "Femto" ) - ( ?a (^ 10 -18) "Atto" ) + ( ?d (^ 10 -1) "deci" ) + ( ?c (^ 10 -2) "centi" ) + ( ?m (^ 10 -3) "milli" ) + ( ?u (^ 10 -6) "micro" ) + ( ?μ (^ 10 -6) "micro" ) + ( ?n (^ 10 -9) "nano" ) + ( ?p (^ 10 -12) "pico" ) + ( ?f (^ 10 -15) "femto" ) + ( ?a (^ 10 -18) "atto" ) ( ?z (^ 10 -21) "zepto" ) ( ?y (^ 10 -24) "yocto" ) ( ?r (^ 10 -27) "ronto" ) commit 949bc1c72d77aed1cea8d52422825cd8baf6d9ba Author: Miha Rihtaršič Date: Sat Nov 26 23:01:24 2022 +0100 comint-fontify-input: Fix field boundary issue (bug#59626) * lisp/comint.el (comint--intersect-regions): Don't call 'field-end' if we are on a field boundary already. Copyright-paperwork-exempt: yes diff --git a/lisp/comint.el b/lisp/comint.el index 4d9fc501874..6742360d16b 100644 --- a/lisp/comint.el +++ b/lisp/comint.el @@ -4121,9 +4121,15 @@ comint--intersect-regions (save-restriction (let ((beg2 beg1) (end2 end1)) - (when (= beg2 beg) + (when (and (= beg2 beg) + (> beg2 (point-min)) + (eq is-output + (eq (get-text-property (1- beg2) 'field) 'output))) (setq beg2 (field-beginning beg2))) - (when (= end2 end) + (when (and (= end2 end) + (< end2 (point-max)) + (eq is-output + (eq (get-text-property (1+ end2) 'field) 'output))) (setq end2 (field-end end2))) ;; Narrow to the whole field surrounding the region (narrow-to-region beg2 end2)) commit a6ada6bd26195a7b5359b3b13323dee35f12a40e Author: Eli Zaretskii Date: Wed Dec 7 16:54:47 2022 +0200 ; Minor copyedits of the latest xterm change * src/xterm.h (struct x_display_info): Fix a comment. * src/xterm.c (syms_of_xterm) : Doc fix. diff --git a/src/xterm.c b/src/xterm.c index f2dbff1c446..4c859c46c31 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -31837,12 +31837,12 @@ syms_of_xterm (void) Vx_use_fast_mouse_position = Qnil; DEFVAR_LISP ("x-detect-server-trust", Vx_detect_server_trust, - doc: /* Whether or not Emacs should detect whether or not it is trusted by X. + doc: /* If non-nil, Emacs should detect whether or not it is trusted by X. If non-nil, Emacs will make an X request at connection startup that is prohibited to untrusted clients under the X Security Extension and check whether or not a resulting Access error is generated by the X -server. If the X server reports the error, then Emacs will disable -certain features that do not work for untrusted clients. */); +server. If the X server reports the error, Emacs will disable certain +features that do not work for untrusted clients. */); Vx_detect_server_trust = Qnil; } diff --git a/src/xterm.h b/src/xterm.h index c3bd647b6f3..9ce5403ef9b 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -376,8 +376,8 @@ #define X_INVALID_WINDOW 0xffffffff /* Number of frames that are on this display. */ int reference_count; - /* True if this client cannot communicate with the window manager - because it is untrusted. */ + /* True if we cannot communicate with the window manager because it + does not trust us. */ bool untrusted; /* The Screen this connection is connected to. */ commit 51a192023ae161a31c90f3ef3e163f10da52075b Author: Eli Zaretskii Date: Wed Dec 7 14:50:41 2022 +0200 ; * etc/NEWS: Fix the 'bs-default-action-list' entry. diff --git a/etc/NEWS b/etc/NEWS index fa4fdc4e910..7f073c4e2d3 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -53,8 +53,8 @@ switches for shortlogs, such as the one produced by 'C-x v L'. --- *** New user option 'bs-default-action-list'. -How to display the "*buffer-selection*" buffer can now be configured -with this new option or by setting 'display-buffer-alist'. +You can now configure how to display the "*buffer-selection*" buffer +using this new option. (Or set 'display-buffer-alist' directly.) * New Modes and Packages in Emacs 30.1 commit 78efe08c0706c2719cb12c7fcd1c8f47386d7b15 Author: Po Lu Date: Wed Dec 7 19:19:34 2022 +0800 Fix some more problems with running Emacs as untrusted * etc/PROBLEMS (X security problems): Describe new variable. * src/xfns.c (append_wm_protocols): Don't support ping when it does not work. * src/xterm.c (x_wm_supports_1): Don't support anything when untrusted. (x_term_init): Implement Vx_detect_server_trust. (syms_of_xterm): New variable `x-detect-server-trust'. * src/xterm.h (struct x_display_info): New field `untrusted'. diff --git a/etc/PROBLEMS b/etc/PROBLEMS index 2169ed0f80b..68f7cdb0560 100644 --- a/etc/PROBLEMS +++ b/etc/PROBLEMS @@ -1227,6 +1227,20 @@ you should use an Emacs input method instead. * X runtime problems +** X security problems + +*** Emacs faces trouble when running as an untrusted client. + +When Emacs is running as an untrusted client under X servers with the +Security extension, it is unable to use some window manager features +but reports them to the window manager anyway. This can lead to +constant prompting by the window manager about Emacs being +unresponsive. To resolve the problem, place: + + (setq x-detect-server-trust t) + +in your early-init.el. + ** X keyboard problems *** `x-focus-frame' fails to activate the frame. diff --git a/src/xfns.c b/src/xfns.c index 9951ced6611..2bf282fd243 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -2642,12 +2642,18 @@ append_wm_protocols (struct x_display_info *dpyinfo, if (existing) XFree (existing); - if (!found_wm_ping) - protos[num_protos++] = dpyinfo->Xatom_net_wm_ping; + if (!dpyinfo->untrusted) + { + /* Untrusted clients cannot use these protocols which require + communicating with the window manager. */ + + if (!found_wm_ping) + protos[num_protos++] = dpyinfo->Xatom_net_wm_ping; #if !defined HAVE_GTK3 && defined HAVE_XSYNC - if (!found_wm_sync_request && dpyinfo->xsync_supported_p) - protos[num_protos++] = dpyinfo->Xatom_net_wm_sync_request; + if (!found_wm_sync_request && dpyinfo->xsync_supported_p) + protos[num_protos++] = dpyinfo->Xatom_net_wm_sync_request; #endif + } if (num_protos) XChangeProperty (dpyinfo->display, diff --git a/src/xterm.c b/src/xterm.c index f446d093ef4..f2dbff1c446 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -26775,6 +26775,12 @@ x_wm_supports_1 (struct x_display_info *dpyinfo, Atom want_atom) if (!NILP (Vx_no_window_manager)) return false; + /* If the window system says Emacs is untrusted, there will be no + way to send any information to the window manager, making any + hints useless. */ + if (dpyinfo->untrusted) + return false; + block_input (); x_catch_errors (dpy); @@ -29507,6 +29513,7 @@ xi_check_toolkit (Display *display) x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) { Display *dpy; + XKeyboardState keyboard_state; struct terminal *terminal; struct x_display_info *dpyinfo; XrmDatabase xrdb; @@ -29726,6 +29733,32 @@ #define NUM_ARGV 10 dpyinfo = xzalloc (sizeof *dpyinfo); terminal = x_create_terminal (dpyinfo); + if (!NILP (Vx_detect_server_trust)) + { + /* Detect whether or not the X server trusts this client, which + is done by making a SetKeyboardControl request and checking + for an Access error. */ + XGrabServer (dpy); + XGetKeyboardControl (dpy, &keyboard_state); + + x_catch_errors (dpy); + + /* At this point, the display is not on x_display_list, so + x_uncatch_errors won't sync. However, that's okay because + x_had_errors_p will. */ + + if (keyboard_state.global_auto_repeat + == AutoRepeatModeOn) + XAutoRepeatOn (dpy); + else + XAutoRepeatOff (dpy); + + if (x_had_errors_p (dpy)) + dpyinfo->untrusted = true; + x_uncatch_errors_after_check (); + XUngrabServer (dpy); + } + dpyinfo->next_failable_request = dpyinfo->failable_requests; { @@ -31802,4 +31835,14 @@ syms_of_xterm (void) If that is still too slow, setting this variable to the symbol `really-fast' will make Emacs return only cached values. */); Vx_use_fast_mouse_position = Qnil; + + DEFVAR_LISP ("x-detect-server-trust", Vx_detect_server_trust, + doc: /* Whether or not Emacs should detect whether or not it is trusted by X. + +If non-nil, Emacs will make an X request at connection startup that is +prohibited to untrusted clients under the X Security Extension and +check whether or not a resulting Access error is generated by the X +server. If the X server reports the error, then Emacs will disable +certain features that do not work for untrusted clients. */); + Vx_detect_server_trust = Qnil; } diff --git a/src/xterm.h b/src/xterm.h index fae40930e9b..c3bd647b6f3 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -376,6 +376,10 @@ #define X_INVALID_WINDOW 0xffffffff /* Number of frames that are on this display. */ int reference_count; + /* True if this client cannot communicate with the window manager + because it is untrusted. */ + bool untrusted; + /* The Screen this connection is connected to. */ Screen *screen;