bug#78899: 30.1; garbage inserted in the terminal buffer when quitting Emacs
Vincent Lefevre <[email protected]>
| Newsgroups | gmane.emacs.bugs |
|---|---|
| Message-ID | <[email protected]> |
On 2026-08-16 08:18:57 +0300, Eli Zaretskii wrote: > > First, some hunks fail for lisp/term/xterm.el and lisp/xt-mouse.el, > > but that's easily fixable since the failures only come from > > unchanged lines that have been modified in the repository. > > > > But when compiling, I get the following error: > > > > error: ‘inhibit_lisp_code’ undeclared > > > > This symbol was removed in 855ad6d870852144221a15d04f4b31969689bffb. > > Please always show complete compilation error messages. > > Chrystal ball says that you need to add to src/lisp.h a line like > this: > > extern Lisp_Object inhibit_lisp_code; I no longer get any issue with the resulting patch (attached), except the following errors that appear: xterm--read-string: Wrong type argument: sequencep, 99 xterm--read-string: Wrong type argument: sequencep, 27 -- Vincent Lefèvre <[email protected]> - Web: <https://www.vinc17.net/> 100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/> Work: CR INRIA - computer arithmetic / Pascaline project (LIP, ENS-Lyon)
emacs-new2.patch
(text/plain, 21.8 KB)
diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el
index 60a992a7e7b..8238f55694c 100644
--- a/lisp/term/xterm.el
+++ b/lisp/term/xterm.el
@@ -769,9 +769,10 @@ xterm-standard-colors
(defun xterm--report-background-handler ()
;; The reply should be: \e ] 11 ; rgb: NUMBER1 / NUMBER2 / NUMBER3 \e \\
- (let ((str (xterm--read-string ?\e ?\\)))
- (when (string-match
- "rgb:\\([a-f0-9]+\\)/\\([a-f0-9]+\\)/\\([a-f0-9]+\\)" str)
+ (let ((str (xterm--read-string "\e\\")))
+ (when (and str
+ (string-match
+ "rgb:\\([a-f0-9]+\\)/\\([a-f0-9]+\\)/\\([a-f0-9]+\\)" str))
(set-terminal-parameter
nil 'xterm--background-color
(list (string-to-number (match-string 1 str) 16)
@@ -791,14 +792,10 @@ xterm--report-foreground-handler
(defun xterm--version-handler ()
;; The reply should be: \e [ > NUMBER1 ; NUMBER2 ; NUMBER3 c
- ;; If the timeout is completely removed for read-event, this
- ;; might hang for terminals that pretend to be xterm, but don't
- ;; respond to this escape sequence. RMS' opinion was to remove
- ;; it completely. That might be right, but let's first try to
- ;; see if by using a longer timeout we get rid of most issues.
- (let ((str (xterm--read-string ?c)))
+ (let ((str (xterm--read-string "c")))
;; Since xterm-280, the terminal type (NUMBER1) is now 41 instead of 0.
- (when (string-match "\\([0-9]+\\);\\([0-9]+\\);[01]" str)
+ (when (and str
+ (string-match "\\([0-9]+\\);\\([0-9]+\\);[01]" str))
(let ((version (string-to-number (match-string 2 str))))
(when (and (> version 2000)
(or (equal (match-string 1 str) "1")
@@ -858,6 +855,22 @@ xterm--primary-da-handler
;; https://github.com/contour-terminal/vt-extensions/blob/master/clipboard-extension.md
(xterm--init-activate-set-selection))))
+(defun xterm--name-and-version-handler ()
+ ;; The reply should be: \e [ > NUMBER1 ; NUMBER2 ; NUMBER3 c
+ ;; If the timeout is completely removed for read-event, this
+ ;; might hang for terminals that pretend to be xterm, but don't
+ ;; respond to this escape sequence. RMS' opinion was to remove
+ ;; it completely. That might be right, but let's first try to
+ ;; see if by using a longer timeout we get rid of most issues.
+ (let ((str (xterm--read-string "\e\\")))
+ (and str
+ (not xterm-mouse-mode-called)
+ ;; Only automatically enable xterm mouse on terminals
+ ;; confirmed to still support all critical editing
+ ;; workflows (bug#74833).
+ (string-match-p xterm--auto-xt-mouse-allowed-names str)
+ (xterm-mouse-mode 1))))
+
(defvar xterm-query-timeout 2
"Seconds to wait for an answer from the terminal.
Can be nil to mean \"no timeout\".")
@@ -865,100 +878,42 @@ xterm-query-timeout
(defvar xterm-query-redisplay-timeout 0.2
"Seconds to wait before allowing redisplay during terminal query." )
-(defun xterm--read-event-for-query ()
+(defun xterm--read-event-for-query (end-time)
"Like `read-event', but inhibit redisplay.
By not redisplaying right away for xterm queries, we can avoid
unsightly flashing during initialization. Give up and redisplay
anyway if we've been waiting a little while."
- (let ((start-time (current-time)))
+ (let* ((timeout (float-time (time-subtract end-time (current-time))))
+ (first-timeout (min xterm-query-redisplay-timeout timeout))
+ (second-timeout (- timeout first-timeout)))
(or (let ((inhibit-redisplay t))
- (read-event nil nil xterm-query-redisplay-timeout))
- (read-event nil nil
- (and xterm-query-timeout
- (max 0 (float-time
- (time-subtract
- xterm-query-timeout
- (time-since start-time)))))))))
-
-(defun xterm--read-string (term1 &optional term2)
- "Read a string with terminating characters.
-This uses `xterm--read-event-for-query' internally."
- (let ((str "")
- chr last)
- (while (and (setq last chr
- chr (xterm--read-event-for-query))
- (if term2
- (not (and (equal last term1) (equal chr term2)))
- (not (equal chr term1))))
- (setq str (concat str (string chr))))
- (if term2
- (substring str 0 -1)
- str)))
-
-(defun xterm--query (query handlers &optional no-async)
+ (read-event nil nil (max 0 first-timeout)))
+ (read-event nil nil (max 0 second-timeout)))))
+
+(defun xterm--query (query handlers)
"Send QUERY string to the terminal and watch for a response.
HANDLERS is an alist with elements of the form (STRING . FUNCTION).
We run the first FUNCTION whose STRING matches the input events."
- ;; We used to query synchronously, but the need to use `discard-input' is
- ;; rather annoying (bug#6758). Maybe we could always use the asynchronous
- ;; approach, but it's less tested.
- ;; FIXME: Merge the two branches.
- (let ((register
- (lambda (handlers)
- (dolist (handler handlers)
- (define-key input-decode-map (car handler)
- (lambda (&optional _prompt)
- ;; Unregister the handler, since we don't expect
- ;; further answers.
- (dolist (handler handlers)
- (define-key input-decode-map (car handler) nil))
- (funcall (cdr handler))
- []))))))
- (if (and (or (null xterm-query-timeout) (input-pending-p))
- (not no-async))
- (progn
- (funcall register handlers)
- (send-string-to-terminal query))
- ;; Pending input can be mistakenly returned by the calls to
- ;; read-event below: discard it.
- (discard-input)
- (send-string-to-terminal query)
- (while handlers
- (let ((handler (pop handlers))
- (i 0))
- (while (and (< i (length (car handler)))
- (let ((evt (xterm--read-event-for-query)))
- (if (and (null evt) (= i 0) (not no-async))
- ;; Timeout on the first event: fallback on async.
- (progn
- (funcall register (cons handler handlers))
- (setq handlers nil)
- nil)
- (or (eq evt (aref (car handler) i))
- (progn (if evt (push evt unread-command-events))
- nil)))))
- (setq i (1+ i)))
- (if (= i (length (car handler)))
- (progn (setq handlers nil)
- (funcall (cdr handler)))
- (while (> i 0)
- (push (aref (car handler) (setq i (1- i)))
- unread-command-events))))))))
-
-(defun xterm--query-name-and-version ()
- "Get the terminal name and version string (XTVERSION)."
- ;; Reduce query timeout time. The default value causes a noticeable
- ;; startup delay on terminals that ignore the query.
- (let ((xterm-query-timeout 0.1))
- (catch 'result
- (xterm--query
- "\e[>0q"
- `(("\eP>|" . ,(lambda ()
- ;; The reply should be: \e P > | STRING \e \\
- (let ((str (xterm--read-string ?\e ?\\)))
- (throw 'result str))))))
- nil)))
+ (let (unregister-functions)
+ (dolist (handler handlers)
+ (let* ((binding
+ (lambda (&optional _prompt)
+ ;; Unregister the handlers, since we don't expect
+ ;; further answers.
+ (mapc #'funcall unregister-functions)
+ (funcall (cdr handler))
+ []))
+ (unregister
+ (lambda ()
+ (when (eq (lookup-key input-decode-map (car handler))
+ binding)
+ (define-key input-decode-map (car handler) nil)))))
+ (define-key input-decode-map (car handler) binding)
+ (push unregister unregister-functions)))
+ (send-string-to-terminal query)
+ (run-with-timer xterm-query-timeout nil
+ (lambda () (mapc #'funcall unregister-functions)))))
(defun xterm--push-map (map basemap)
;; Use inheritance to let the main keymaps override those defaults.
@@ -1032,16 +987,9 @@ xterm--init
;; for initial frame.
(xterm--maybe-update-default-face (selected-frame))))
- (when (and (not xterm-mouse-mode-called)
- ;; Only automatically enable xterm mouse on terminals
- ;; confirmed to still support all critical editing
- ;; workflows (bug#74833).
- (or (string-match-p xterm--auto-xt-mouse-allowed-types
- (tty-type (selected-frame)))
- (and-let* ((name-and-version (xterm--query-name-and-version)))
- (string-match-p xterm--auto-xt-mouse-allowed-names
- name-and-version))))
- (xterm-mouse-mode 1))
+ (when (not xterm-mouse-mode-called)
+ (xterm--query "\e[>0q"
+ '(("\eP>|" . xterm--name-and-version-handler))))
;; Unconditionally enable bracketed paste mode: terminals that don't
;; support it just ignore the sequence.
(xterm--init-bracketed-paste-mode)
@@ -1130,6 +1078,33 @@ xterm--selection-char
('CLIPBOARD "c")
(_ (error "Invalid selection type: %S" type))))
+(defun xterm--read-string (sequence &optional unread-all-events)
+ "Read input until we see SEQUENCE. Return string of the input characters
+before its appearance, or nil if we hit a timeout. Non-characters are
+stored in unread-command-events. If UNREAD-ALL-EVENTS is non-nil, all
+events are stored there."
+ (setq sequence (nreverse (append sequence nil)))
+ (let ((end-time (time-add (current-time) xterm-query-timeout))
+ (n (length sequence))
+ events chars ret)
+ (unwind-protect
+ (catch 'failure
+ (while (not (equal (take n chars) sequence))
+ (let ((event (xterm--read-event-for-query end-time)))
+ (if event
+ (push event events)
+ (throw 'failure nil))
+ (when (characterp event)
+ (push event chars))))
+ (setq ret (concat (nreverse (nthcdr n chars)))))
+ (dolist (event events)
+ (cond ((eq event (car sequence))
+ (pop sequence))
+ ((or (not (characterp event))
+ (not (stringp ret))
+ unread-all-events)
+ (push event unread-command-events)))))))
+
(cl-defmethod gui-backend-get-selection
(type data-type
&context (window-system nil)
@@ -1141,27 +1116,16 @@ gui-backend-get-selection
(eql nil)))
(unless (eq data-type 'STRING)
(error "Unsupported data type %S" data-type))
- (let ((query (concat "\e]52;" (xterm--selection-char type) ";")))
- (with-temp-buffer
+ (with-temp-buffer
+ (let* ((query (concat "\e]52;" (xterm--selection-char type) ";")))
(set-buffer-multibyte nil)
- (xterm--query
- ;; Use ST as query terminator to get ST as reply terminator (bug#36879).
- (concat query "?\e\\")
- (list (cons query
- (lambda ()
- ;; Read data up to the string terminator, ST.
- (let (char last)
- (while (and (setq char (read-char
- nil nil
- xterm-query-timeout))
- (not (and (eq char ?\\)
- (eq last ?\e))))
- (when last
- (insert last))
- (setq last char))))))
- 'no-async)
- (base64-decode-region (point-min) (point-max))
- (decode-coding-region (point-min) (point-max) 'utf-8-unix t))))
+ (send-string-to-terminal (concat query "?\e\\"))
+ (xterm--read-string query t)
+ (let ((str (xterm--read-string "\e\\")))
+ (when str
+ (insert str)
+ (base64-decode-region (point-min) (point-max))
+ (decode-coding-region (point-min) (point-max) 'utf-8-unix t))))))
(cl-defmethod gui-backend-set-selection
(type data
diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el
index b93d914380f..834323f43c4 100644
--- a/lisp/xt-mouse.el
+++ b/lisp/xt-mouse.el
@@ -387,6 +387,9 @@ xterm-mouse-mode-called
"If `xterm-mouse-mode' has been called already.
This can be used to detect if xterm-mouse-mode was explicitly set.")
+(defvar xterm-mouse--terminals (make-hash-table :test 'eq :weakness 'key)
+ "Hash table of cleanups to be performed when xterm-mouse-mode is disabled.")
+
;;;###autoload
(define-minor-mode xterm-mouse-mode
"Toggle XTerm mouse mode.
@@ -507,6 +510,7 @@ xterm-mouse--tracking-sequence
(defun turn-on-xterm-mouse-tracking-on-terminal (&optional terminal)
"Enable xterm mouse tracking on TERMINAL."
+ (setq terminal (or terminal (frame-terminal)))
(when (and xterm-mouse-mode (eq t (terminal-live-p terminal))
;; Avoid the initial terminal which is not a termcap device.
(not (frame-initial-p terminal)))
@@ -523,7 +527,8 @@ turn-on-xterm-mouse-tracking-on-terminal
(define-key input-decode-map "\e[M" 'xterm-mouse-translate)
(define-key input-decode-map "\e[<" 'xterm-mouse-translate-extended))
(let ((enable (xterm-mouse-tracking-enable-sequence))
- (disable (xterm-mouse-tracking-disable-sequence)))
+ (disable (xterm-mouse-tracking-disable-sequence))
+ (sync (lambda () (xterm-mouse--sync terminal))))
(condition-case err
(send-string-to-terminal enable terminal)
;; FIXME: This should use a dedicated error signal.
@@ -531,12 +536,27 @@ turn-on-xterm-mouse-tracking-on-terminal
"Terminal is currently suspended")
nil ; The sequence will be sent upon resume.
(signal err))))
- (push enable (terminal-parameter nil 'tty-mode-set-strings))
- (push disable (terminal-parameter nil 'tty-mode-reset-strings))
+ (push enable (terminal-parameter terminal 'tty-mode-set-strings))
+ (push sync (terminal-parameter terminal 'tty-mode-reset-strings))
+ (push disable (terminal-parameter terminal 'tty-mode-reset-strings))
+ (puthash terminal (list enable disable sync) xterm-mouse--terminals)
(set-terminal-parameter terminal 'xterm-mouse-mode t)
(set-terminal-parameter terminal 'xterm-mouse-utf-8
xterm-mouse-utf-8)))))
+(declare-function xterm--read-string "xterm" (sequence &optional unread-all-events))
+
+(defun xterm-mouse--sync (terminal)
+ "Ensure that the TERMINAL is in a synchronized state, but obey
+xterm-query-timeout.
+
+When the sequence to disable mouse tracking has been sent and this
+function returns successfully, no further mouse events should be
+produced."
+ (send-string-to-terminal "\e[0c" terminal)
+ (xterm--read-string "\e[?" t)
+ (xterm--read-string "c"))
+
(defun turn-off-xterm-mouse-tracking-on-terminal (terminal)
"Disable xterm mouse tracking on TERMINAL."
;; Only send the disable command to those terminals to which we've already
@@ -549,19 +569,23 @@ turn-off-xterm-mouse-tracking-on-terminal
;; to send it too few times (or to fail to let xterm-mouse events
;; pass by untranslated).
(condition-case err
- (send-string-to-terminal xterm-mouse-tracking-disable-sequence
- terminal)
+ (progn
+ (send-string-to-terminal xterm-mouse-tracking-disable-sequence
+ terminal)
+ (xterm-mouse--sync terminal))
;; FIXME: This should use a dedicated error signal.
(error (if (equal (error-slot-value err 1)
"Terminal is currently suspended")
nil
(signal err))))
- (setf (terminal-parameter nil 'tty-mode-set-strings)
- (remq xterm-mouse-tracking-enable-sequence
- (terminal-parameter nil 'tty-mode-set-strings)))
- (setf (terminal-parameter nil 'tty-mode-reset-strings)
- (remq xterm-mouse-tracking-disable-sequence
- (terminal-parameter nil 'tty-mode-reset-strings)))
+ (dolist (el (gethash terminal xterm-mouse--terminals))
+ (setf (terminal-parameter terminal 'tty-mode-set-strings)
+ (remq el
+ (terminal-parameter terminal 'tty-mode-set-strings)))
+ (setf (terminal-parameter terminal 'tty-mode-reset-strings)
+ (remq el
+ (terminal-parameter terminal 'tty-mode-reset-strings))))
+ (remhash terminal xterm-mouse--terminals)
(set-terminal-parameter terminal 'xterm-mouse-mode nil)))
(provide 'xt-mouse)
diff --git a/src/dispnew.c b/src/dispnew.c
index dd799c62d02..ecd38e084e2 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -3456,8 +3456,8 @@ frames_in_reverse_z_order (struct frame *f, bool visible_only)
struct frame *root = root_frame (f);
Lisp_Object frames = frames_with_root (root, visible_only);
frames = CALLN (Fsort, frames, QClessp, Qframe__z_order_lessp);
- eassert (FRAMEP (XCAR (frames)));
- eassert (XFRAME (XCAR (frames)) == root);
+ eassert (NILP (frames) || FRAMEP (XCAR (frames)));
+ eassert (NILP (frames) || XFRAME (XCAR (frames)) == root);
return frames;
}
@@ -3517,7 +3517,7 @@ is_tty_root_frame_with_visible_child (struct frame *f)
if (!is_tty_root_frame (f))
return false;
Lisp_Object z_order = frames_in_reverse_z_order (f, true);
- return CONSP (XCDR (z_order));
+ return CONSP (z_order) && CONSP (XCDR (z_order));
}
/* Return the index of the first enabled row in MATRIX, or -1 if there
diff --git a/src/emacs.c b/src/emacs.c
index 95e0c170435..e2a712d651d 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -460,6 +460,8 @@ terminate_due_to_signal (int sig, int backtrace_limit)
Fkill_emacs (make_fixnum (sig), Qnil);
}
+ /* Prevent running of Lisp code from now on. */
+ inhibit_lisp_code = Qt;
shut_down_emacs (sig, Qnil);
emacs_backtrace (backtrace_limit);
}
@@ -3148,6 +3150,7 @@ shut_down_emacs (int sig, Lisp_Object stuff)
fflush (stdout);
reset_all_sys_modes ();
#endif
+ inhibit_lisp_code = Qt;
stuff_buffered_input (stuff);
diff --git a/src/eval.c b/src/eval.c
index 9d47f04f92a..8029555f2d4 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -3247,6 +3247,8 @@ safe_eval (Lisp_Object sexp)
return safe_calln (Qeval, sexp, Qt);
}
+Lisp_Object inhibit_lisp_code;
+
/* Apply a C subroutine SUBR to the NUMARGS evaluated arguments in ARG_VECTOR
and return the result of evaluation. */
@@ -4619,6 +4621,9 @@ syms_of_eval (void)
staticpro (&list_of_t);
list_of_t = list1 (Qt);
+ inhibit_lisp_code = Qnil;
+ staticpro (&inhibit_lisp_code);
+
defsubr (&Sor);
defsubr (&Sand);
defsubr (&Sif);
diff --git a/src/keyboard.c b/src/keyboard.c
index 1129fd3b58c..94df32ec68c 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -12628,7 +12628,10 @@ handle_interrupt (bool in_signal_handler)
fflush (stdout);
}
+ Lisp_Object old_inhibit_lisp_code = inhibit_lisp_code;
+ inhibit_lisp_code = Qt;
reset_all_sys_modes ();
+ inhibit_lisp_code = old_inhibit_lisp_code;
#ifdef SIGTSTP
/*
diff --git a/src/lisp.h b/src/lisp.h
index de94b2fa9d1..81e61b20d2f 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4769,6 +4769,7 @@ intern_c_string (const char *str)
extern Lisp_Object Vautoload_queue;
extern Lisp_Object Vrun_hooks;
extern Lisp_Object Vsignaling_function;
+extern Lisp_Object inhibit_lisp_code;
extern bool signal_quit_p (Lisp_Object);
/* To run a normal hook, use the appropriate function from the list below.
diff --git a/src/term.c b/src/term.c
index 7e05f5b5139..91673b57d51 100644
--- a/src/term.c
+++ b/src/term.c
@@ -194,6 +194,10 @@ tty_send_additional_strings (struct terminal *terminal, Lisp_Object sym)
if (tty->termscript)
fwrite (SDATA (string), 1, sbytes, tty->termscript);
}
+ else if (NILP (inhibit_lisp_code) && FUNCTIONP (string))
+ {
+ safe_calln (string);
+ }
}
}
@@ -2469,14 +2473,16 @@ DEFUN ("suspend-tty", Fsuspend_tty, Ssuspend_tty, 0, 1, 0,
if (f)
{
- /* First run `suspend-tty-functions' and then clean up the tty
- state because `suspend-tty-functions' might need to change
- the tty state. */
+ /* The Emacs server uses `suspend-tty-functions' to perform the
+ actual suspension of secondary terminals. Therefore, we must
+ run them after resetting the terminal state. */
Lisp_Object term;
XSETTERMINAL (term, t);
- CALLN (Frun_hook_with_args, Qsuspend_tty_functions, term);
reset_sys_modes (t->display_info.tty);
+
+ CALLN (Frun_hook_with_args, Qsuspend_tty_functions, term);
+
delete_keyboard_wait_descriptor (fileno (f));
#ifndef MSDOS
@@ -4951,6 +4957,8 @@ vfatal (const char *str, va_list ap)
maybe_fatal (bool must_succeed, struct terminal *terminal,
const char *str1, const char *str2, ...)
{
+ Lisp_Object old_inhibit_lisp_code = inhibit_lisp_code;
+ inhibit_lisp_code = Qt;
va_list ap;
va_start (ap, str2);
@@ -4965,6 +4973,8 @@ maybe_fatal (bool must_succeed, struct terminal *terminal,
vfatal (str2, ap);
else
verror (str1, ap);
+
+ inhibit_lisp_code = old_inhibit_lisp_code;
}
void
diff --git a/src/terminal.c b/src/terminal.c
index f3a90740d8e..0ee7d21658f 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -412,10 +412,15 @@ DEFUN ("delete-terminal", Fdelete_terminal, Sdelete_terminal, 0, 2, 0,
else
safe_calln (Qrun_hook_with_args, Qdelete_terminal_functions, terminal);
+ Lisp_Object old_inhibit_lisp_code = inhibit_lisp_code;
+ if (EQ (force, Qnoelisp))
+ inhibit_lisp_code = Qt;
if (t->delete_terminal_hook)
(*t->delete_terminal_hook) (t);
else
delete_terminal (t);
+ if (EQ (force, Qnoelisp))
+ inhibit_lisp_code = old_inhibit_lisp_code;
return Qnil;
}