Package: emacs;
Reported by: Vincent Lefevre <vincent <at> vinc17.net>
Date: Thu, 26 Jun 2025 00:39:01 UTC
Severity: normal
Found in version 30.1
To reply to this bug, email your comments to 78899 AT debbugs.gnu.org.
Toggle the display of automated, internal messages from the tracker.
View this report as an mbox folder, status mbox, maintainer mbox
bug-gnu-emacs <at> gnu.org
:bug#78899
; Package emacs
.
(Thu, 26 Jun 2025 00:39:01 GMT) Full text and rfc822 format available.Vincent Lefevre <vincent <at> vinc17.net>
:bug-gnu-emacs <at> gnu.org
.
(Thu, 26 Jun 2025 00:39:02 GMT) Full text and rfc822 format available.Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: Vincent Lefevre <vincent <at> vinc17.net> To: bug-gnu-emacs <at> gnu.org Subject: 30.1; garbage inserted in the terminal buffer when quitting Emacs Date: Thu, 26 Jun 2025 02:37:58 +0200
I ran "emacs -nw some_file" on my Android phone via ssh in xterm, where emacs is provided by Termux. Just after I quit Emacs with C-x C-c, I got the following in the terminal buffer, which became input at the shell prompt: ^[[<35;42;25M35;40;24M35;37;24M35;42;25M35;33;23M I think that this was the first time this occurred. Note that there were no issues with the network (this is on the local network at home). In GNU Emacs 30.1 (build 1, aarch64-unknown-linux-android) of 2025-06-18 built on localhost Configured using: 'configure --disable-dependency-tracking --prefix=/data/data/com.termux/files/usr --libdir=/data/data/com.termux/files/usr/lib --includedir=/data/data/com.termux/files/usr/include --sbindir=/data/data/com.termux/files/usr/bin --disable-rpath --disable-rpath-hack --host=aarch64-linux-android --disable-autodepend --with-dumping=none --with-gif=no --with-gnutls --with-jpeg=no --with-modules --with-pdumper=yes --with-png=no --with-tiff=no --with-xml2 --with-xpm=no --with-tree-sitter --without-dbus --without-gconf --without-gsettings --without-lcms2 --without-selinux --without-x emacs_cv_alternate_stack=yes emacs_cv_sanitize_address=yes emacs_cv_prog_cc_no_pie=no ac_cv_lib_elf_elf_begin=no gl_cv_func_dup2_works=no ac_cv_func_setrlimit=no --disable-nls --enable-shared --enable-static --libexecdir=/data/data/com.termux/files/usr/libexec 'CFLAGS= -fstack-protector-strong -Oz' 'CPPFLAGS= -isystem/data/data/com.termux/files/usr/include/c++/v1 -isystem/data/data/com.termux/files/usr/include' 'LDFLAGS=-L/data/data/com.termux/files/usr/lib -Wl,-rpath=/data/data/com.termux/files/usr/lib -Wl,--enable-new-dtags -Wl,--as-needed -Wl,-z,relro,-z,now'' Configured features: GMP GNUTLS LIBXML2 MODULES NOTIFY INOTIFY PDUMPER SECCOMP SQLITE3 THREADS TREE_SITTER XIM ZLIB Important settings: value of $LANG: en_US.UTF-8 locale-coding-system: utf-8-unix Major mode: Fundamental Minor modes in effect: display-time-mode: t xterm-mouse-mode: t tooltip-mode: t global-eldoc-mode: t show-paren-mode: t electric-indent-mode: t menu-bar-mode: t file-name-shadow-mode: t global-font-lock-mode: t font-lock-mode: t blink-cursor-mode: t minibuffer-regexp-mode: t column-number-mode: t line-number-mode: t indent-tabs-mode: t transient-mark-mode: t auto-composition-mode: t auto-encryption-mode: t auto-compression-mode: t Load-path shadows: None found. Features: (shadow sort mail-extr cl-extra help-mode tool-bar warnings icons emacsbug message mailcap yank-media puny dired dnd dired-loaddefs rfc822 mml mml-sec password-cache epa derived epg rfc6068 epg-config gnus-util text-property-search time-date subr-x mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils mailheader sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils vc-dispatcher vc-svn term/xterm xterm byte-opt gv bytecomp byte-compile time image cus-load cc-styles cc-align cc-engine cc-vars cc-defs regexp-opt cl-loaddefs cl-lib xt-mouse rmc iso-transl tooltip cconv eldoc paren electric uniquify ediff-hook vc-hooks lisp-float-type elisp-mode tabulated-list replace newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu timer select mouse jit-lock font-lock syntax font-core term/tty-colors frame minibuffer nadvice seq simple cl-generic indonesian philippine cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech european ethiopic indian cyrillic chinese composite emoji-zwj charscript charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs theme-loaddefs faces cus-face macroexp files window text-properties overlay sha1 md5 base64 format env code-pages mule custom widget keymap hashtable-print-readable backquote threads inotify multi-tty make-network-process emacs) -- Vincent Lefèvre <vincent <at> vinc17.net> - 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)
bug-gnu-emacs <at> gnu.org
:bug#78899
; Package emacs
.
(Thu, 26 Jun 2025 06:19:01 GMT) Full text and rfc822 format available.Message #8 received at 78899 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Vincent Lefevre <vincent <at> vinc17.net>, Po Lu <luangruo <at> yahoo.com> Cc: 78899 <at> debbugs.gnu.org Subject: Re: bug#78899: 30.1; garbage inserted in the terminal buffer when quitting Emacs Date: Thu, 26 Jun 2025 09:18:45 +0300
> Date: Thu, 26 Jun 2025 02:37:58 +0200 > From: Vincent Lefevre <vincent <at> vinc17.net> > > I ran "emacs -nw some_file" on my Android phone via ssh in xterm, > where emacs is provided by Termux. > > Just after I quit Emacs with C-x C-c, I got the following in the > terminal buffer, which became input at the shell prompt: > > ^[[<35;42;25M35;40;24M35;37;24M35;42;25M35;33;23M > > I think that this was the first time this occurred. > > Note that there were no issues with the network (this is on the > local network at home). That could be some kind of command we send to the terminal, which the terminal doesn't support? Po Lu, any ideas?
bug-gnu-emacs <at> gnu.org
:bug#78899
; Package emacs
.
(Thu, 26 Jun 2025 11:38:02 GMT) Full text and rfc822 format available.Message #11 received at 78899 <at> debbugs.gnu.org (full text, mbox):
From: Pip Cet <pipcet <at> protonmail.com> To: Eli Zaretskii <eliz <at> gnu.org> Cc: Po Lu <luangruo <at> yahoo.com>, 78899 <at> debbugs.gnu.org, Vincent Lefevre <vincent <at> vinc17.net> Subject: Re: bug#78899: 30.1; garbage inserted in the terminal buffer when quitting Emacs Date: Thu, 26 Jun 2025 11:37:12 +0000
"Eli Zaretskii" <eliz <at> gnu.org> writes: >> Date: Thu, 26 Jun 2025 02:37:58 +0200 >> From: Vincent Lefevre <vincent <at> vinc17.net> >> >> I ran "emacs -nw some_file" on my Android phone via ssh in xterm, >> where emacs is provided by Termux. >> >> Just after I quit Emacs with C-x C-c, I got the following in the >> terminal buffer, which became input at the shell prompt: >> >> ^[[<35;42;25M35;40;24M35;37;24M35;42;25M35;33;23M >> >> I think that this was the first time this occurred. >> >> Note that there were no issues with the network (this is on the >> local network at home). > > That could be some kind of command we send to the terminal, which the > terminal doesn't support? > > Po Lu, any ideas? Those are mouse movements reported by xterm. I can reproduce this by using a slow connection, enabling xterm-mouse-mode, quitting Emacs, and moving the mouse while that process is being sent. I think what happens is that we send the escape sequence to disable those just before exiting, without waiting for that escape sequence to be processed by the terminal, so xterm keeps sending them to the next application for a while. Note that xterm--query-name-and-version fails on slow connections, too: the timeout is set to 0.1 s, and if we don't get a response in that time, we get an asynchronous throw to a tag that no longer has a catch. This produces the error message: No catch for tag: result, "XTerm(400)" IIUC, the xterm--query-name-and-version bug hides the other bug: usually, if you have a slow connection, xterm mouse mode won't be enabled automatically because we fail to recognize the terminal. If you have a fast connection, such as a local display, the window for mouse movements to be reported is small. We could possibly fix this by asking for another report from xterm upon termination and waiting for the response? Pip
bug-gnu-emacs <at> gnu.org
:bug#78899
; Package emacs
.
(Thu, 26 Jun 2025 12:36:01 GMT) Full text and rfc822 format available.Message #14 received at 78899 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Pip Cet <pipcet <at> protonmail.com> Cc: luangruo <at> yahoo.com, 78899 <at> debbugs.gnu.org, vincent <at> vinc17.net Subject: Re: bug#78899: 30.1; garbage inserted in the terminal buffer when quitting Emacs Date: Thu, 26 Jun 2025 15:35:27 +0300
> Date: Thu, 26 Jun 2025 11:37:12 +0000 > From: Pip Cet <pipcet <at> protonmail.com> > Cc: Vincent Lefevre <vincent <at> vinc17.net>, Po Lu <luangruo <at> yahoo.com>, 78899 <at> debbugs.gnu.org > > We could possibly fix this by asking for another report from xterm upon > termination and waiting for the response? Yes, I think something like that would make sense.
bug-gnu-emacs <at> gnu.org
:bug#78899
; Package emacs
.
(Mon, 30 Jun 2025 22:49:02 GMT) Full text and rfc822 format available.Message #17 received at 78899 <at> debbugs.gnu.org (full text, mbox):
From: Pip Cet <pipcet <at> protonmail.com> To: Eli Zaretskii <eliz <at> gnu.org> Cc: luangruo <at> yahoo.com, 78899 <at> debbugs.gnu.org, vincent <at> vinc17.net Subject: Re: bug#78899: 30.1; garbage inserted in the terminal buffer when quitting Emacs Date: Mon, 30 Jun 2025 22:47:45 +0000
"Eli Zaretskii" <eliz <at> gnu.org> writes: >> Date: Thu, 26 Jun 2025 11:37:12 +0000 >> From: Pip Cet <pipcet <at> protonmail.com> >> Cc: Vincent Lefevre <vincent <at> vinc17.net>, Po Lu <luangruo <at> yahoo.com>, 78899 <at> debbugs.gnu.org >> >> We could possibly fix this by asking for another report from xterm upon >> termination and waiting for the response? > > Yes, I think something like that would make sense. It turns out that the request-response cycle to ensure the remote xterm has caught up to our output is a little tricky: we don't want to use the command loop like xterm--query does, because we're usually called from within kill-emacs and won't return to the command loop. We also can't do it in C, because the code to read from a terminal without blocking is highly system-dependent. So that leaves read-event. I went to some trouble to collect all events in a list, removing only those that constitute our response escape sequence and pushing the rest to unread-command-events; I believe that's the right thing to do because we might want to auto-disable xterm-mouse-mode when we detect a slow terminal one day, as the old code effectively did. However, maybe the low-level key thing would be a better alternative and allow restricting input to one terminal only? Here are the two patches; I'm aware of a whitespace issue, and we need to decide on a sensible timeout for xterm-sync: I used 2.0 seconds because I think the most common scenario for slow xterm connections is a Tor connection, and Tor appears to add up to about 1.2 seconds of (round-trip) latency. From 4cc6d43fd3f105b15f8079cf647c83bb511db339 Mon Sep 17 00:00:00 2001 From: Pip Cet <pipcet <at> protonmail.com> Subject: [PATCH 1/2] Make xterm--query-name-and-version use a callback pattern (bug#78899) The previous code would throw without a containing catch on slow terminal connections. * lisp/term/xterm.el (xterm--query-name-and-version): Accept 'callback' argument. Call it. (xterm--init): Handle asynchronous terminal id responses. --- lisp/term/xterm.el | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el index 4f23a909b69..4fe27775364 100644 --- a/lisp/term/xterm.el +++ b/lisp/term/xterm.el @@ -904,19 +904,19 @@ xterm--query (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)." +(defun xterm--query-name-and-version (callback) + "Get the terminal name and version string (XTVERSION). +CALLBACK is called asynchronously when a response is received." ;; 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 ((xterm-query-timeout nil)) + (xterm--query + "\e[>0q" + `(("\eP>|" . ,(lambda () + ;; The reply should be: \e P > | STRING \e \\ + (let ((str (xterm--read-string ?\e ?\\))) + (funcall callback str)))))) + nil)) (defun xterm--push-map (map basemap) ;; Use inheritance to let the main keymaps override those defaults. @@ -965,16 +965,18 @@ xterm--init (when xterm-set-window-title (xterm--init-frame-title)) - (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) + ;; Only automatically enable xterm mouse on terminals + ;; confirmed to still support all critical editing + ;; workflows (bug#74833). + (if (string-match-p xterm--auto-xt-mouse-allowed-types + (tty-type (selected-frame))) + (xterm-mouse-mode 1) + (xterm--query-name-and-version + (lambda (name-and-version) + (when (string-match-p xterm--auto-xt-mouse-allowed-names + name-and-version) + (xterm-mouse-mode 1)))))) ;; Unconditionally enable bracketed paste mode: terminals that don't ;; support it just ignore the sequence. (xterm--init-bracketed-paste-mode) -- 2.50.0 From 8d9bc5b59ba34252b9cefc23fe673e6345a3e6fd Mon Sep 17 00:00:00 2001 From: Pip Cet <pipcet <at> protonmail.com> Subject: [PATCH 2/2] Don't let xterm mouse events arrive after we quit (bug#78899) * lisp/term/xterm.el (xterm-sync): New function. * lisp/xt-mouse.el (turn-on-xterm-mouse-tracking-on-terminal): Register 'kill-emacs' hook function. (xterm-sync): Declare. (turn-off-xterm-mouse-tracking-on-terminal): Call 'xterm-sync' before we stop processing mouse events. --- lisp/term/xterm.el | 40 ++++++++++++++++++++++++++++++++++++++++ lisp/xt-mouse.el | 10 ++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el index 4fe27775364..cbc8a2b8398 100644 --- a/lisp/term/xterm.el +++ b/lisp/term/xterm.el @@ -918,6 +918,46 @@ xterm--query-name-and-version (funcall callback str)))))) nil)) +(defun xterm-sync (terminal &optional timeout) + "Ensure that the TERMINAL is in a synchronized state, but obey TIMEOUT." + ;; This function tries very hard to handle all events that do not belong + ;; to the escape sequence we are looking for. It's usually called only + ;; when Emacs quits, so this might be overkill. + (setq timeout (or timeout 2.0)) + (let ((i 0) + events all-events) + ;; request Primary DA from terminal + (send-string-to-terminal "\e[0c" terminal) + ;; build two lists: ALL-EVENTS contains all events, while EVENTS + ;; associates character events (only) to their index in ALL-EVENTS. + ;; We filter out the expected escape sequence ("\e[?") from the + ;; ALL-EVENTS list. + (with-timeout (timeout + (setq unread-command-events + (append unread-command-events + (nreverse all-events)))) + (while (not + (pcase events + (`((?? . ,i0) + (?\[ . ,i1) + (?\e . ,i2) . ,_) + (setq all-events (nreverse all-events)) + (pop (nthcdr i0 all-events)) + (pop (nthcdr i1 all-events)) + (pop (nthcdr i2 all-events)) + t))) + (let ((event (read-event))) + (push event all-events) + (when (characterp event) + (push (cons event i) events)) + (incf i))) + ;; read the rest of the response + (xterm--read-string ?c) + ;; replay events that preceded or occurred within the escape + ;; sequence. + (setq unread-command-events + (append unread-command-events all-events))))) + (defun xterm--push-map (map basemap) ;; Use inheritance to let the main keymaps override those defaults. ;; This way we don't override terminfo-derived settings or settings diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el index 89f9bbab608..884ae3ab274 100644 --- a/lisp/xt-mouse.el +++ b/lisp/xt-mouse.el @@ -528,10 +528,14 @@ turn-on-xterm-mouse-tracking-on-terminal (signal (car err) (cdr err))))) (push enable (terminal-parameter nil 'tty-mode-set-strings)) (push disable (terminal-parameter nil 'tty-mode-reset-strings)) + (add-hook 'kill-emacs-hook (lambda () + (turn-off-xterm-mouse-tracking-on-terminal terminal))) (set-terminal-parameter terminal 'xterm-mouse-mode t) (set-terminal-parameter terminal 'xterm-mouse-utf-8 xterm-mouse-utf-8))))) +(declare-function xterm-sync "xterm" (terminal &optional timeout)) + (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 @@ -544,8 +548,10 @@ 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-sync terminal)) ;; FIXME: This should use a dedicated error signal. (error (if (equal (cadr err) "Terminal is currently suspended") nil -- 2.50.0
bug-gnu-emacs <at> gnu.org
:bug#78899
; Package emacs
.
(Tue, 01 Jul 2025 11:54:02 GMT) Full text and rfc822 format available.Message #20 received at 78899 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Pip Cet <pipcet <at> protonmail.com>, Jared Finder <jared <at> finder.org>, Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: luangruo <at> yahoo.com, 78899 <at> debbugs.gnu.org, vincent <at> vinc17.net Subject: Re: bug#78899: 30.1; garbage inserted in the terminal buffer when quitting Emacs Date: Tue, 01 Jul 2025 14:53:28 +0300
> Date: Mon, 30 Jun 2025 22:47:45 +0000 > From: Pip Cet <pipcet <at> protonmail.com> > Cc: vincent <at> vinc17.net, luangruo <at> yahoo.com, 78899 <at> debbugs.gnu.org > > "Eli Zaretskii" <eliz <at> gnu.org> writes: > > >> Date: Thu, 26 Jun 2025 11:37:12 +0000 > >> From: Pip Cet <pipcet <at> protonmail.com> > >> Cc: Vincent Lefevre <vincent <at> vinc17.net>, Po Lu <luangruo <at> yahoo.com>, 78899 <at> debbugs.gnu.org > >> > >> We could possibly fix this by asking for another report from xterm upon > >> termination and waiting for the response? > > > > Yes, I think something like that would make sense. > > It turns out that the request-response cycle to ensure the remote xterm > has caught up to our output is a little tricky: we don't want to use the > command loop like xterm--query does, because we're usually called from > within kill-emacs and won't return to the command loop. We also can't > do it in C, because the code to read from a terminal without blocking is > highly system-dependent. > > So that leaves read-event. I went to some trouble to collect all events > in a list, removing only those that constitute our response escape > sequence and pushing the rest to unread-command-events; I believe that's > the right thing to do because we might want to auto-disable > xterm-mouse-mode when we detect a slow terminal one day, as the old code > effectively did. However, maybe the low-level key thing would be a > better alternative and allow restricting input to one terminal only? > > Here are the two patches; I'm aware of a whitespace issue, and we need > to decide on a sensible timeout for xterm-sync: I used 2.0 seconds > because I think the most common scenario for slow xterm connections is a > Tor connection, and Tor appears to add up to about 1.2 seconds of > (round-trip) latency. Thanks. Jared and Stefan, any comments?
bug-gnu-emacs <at> gnu.org
:bug#78899
; Package emacs
.
(Tue, 01 Jul 2025 22:10:03 GMT) Full text and rfc822 format available.Message #23 received at 78899 <at> debbugs.gnu.org (full text, mbox):
From: Stefan Monnier <monnier <at> iro.umontreal.ca> To: Pip Cet <pipcet <at> protonmail.com> Cc: luangruo <at> yahoo.com, Eli Zaretskii <eliz <at> gnu.org>, vincent <at> vinc17.net, 78899 <at> debbugs.gnu.org Subject: Re: bug#78899: 30.1; garbage inserted in the terminal buffer when quitting Emacs Date: Tue, 01 Jul 2025 18:09:21 -0400
> * lisp/term/xterm.el (xterm--query-name-and-version): Accept > 'callback' argument. Call it. > (xterm--init): Handle asynchronous terminal id responses. Hmm... you may want to check the code history, because I have a vague recollection that we had something along these lines at some point and it bumped into other problems, probably when ELisp code runs between `xterm--query-name-and-version` and its callback, and sends other things to the terminal or reads from the terminal. Maybe I misremember, of course. > +(defun xterm-sync (terminal &optional timeout) > + "Ensure that the TERMINAL is in a synchronized state, but obey TIMEOUT." > + ;; This function tries very hard to handle all events that do not belong > + ;; to the escape sequence we are looking for. It's usually called only > + ;; when Emacs quits, so this might be overkill. > + (setq timeout (or timeout 2.0)) > + (let ((i 0) > + events all-events) > + ;; request Primary DA from terminal > + (send-string-to-terminal "\e[0c" terminal) > + ;; build two lists: ALL-EVENTS contains all events, while EVENTS > + ;; associates character events (only) to their index in ALL-EVENTS. > + ;; We filter out the expected escape sequence ("\e[?") from the > + ;; ALL-EVENTS list. > + (with-timeout (timeout > + (setq unread-command-events > + (append unread-command-events > + (nreverse all-events)))) > + (while (not > + (pcase events > + (`((?? . ,i0) > + (?\[ . ,i1) > + (?\e . ,i2) . ,_) > + (setq all-events (nreverse all-events)) > + (pop (nthcdr i0 all-events)) > + (pop (nthcdr i1 all-events)) > + (pop (nthcdr i2 all-events)) > + t))) > + (let ((event (read-event))) > + (push event all-events) > + (when (characterp event) > + (push (cons event i) events)) > + (incf i))) > + ;; read the rest of the response > + (xterm--read-string ?c) > + ;; replay events that preceded or occurred within the escape > + ;; sequence. > + (setq unread-command-events > + (append unread-command-events all-events))))) Can we consolidate the two "nreverse + append to u-c-e", maybe to an unwind-handler? Also, I have the impression that we should *prepend* rather than append (in practice u-c-e should be nil at that point so it likely doesn't matter, tho). Oh, and `(xterm-sync TERMINAL)` sounds like a very generic functionality, whereas the code is much more specialized and presumes we've send a specific escape sequence. I'd use a longer name that makes it more clear what it's doing: "xterm-sync" says what it's for but not what it really does. > @@ -544,8 +548,10 @@ 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-sync terminal)) > ;; FIXME: This should use a dedicated error signal. > (error (if (equal (cadr err) "Terminal is currently suspended") > nil Do we need the `xterm-sync` in all cases? IIUC we need it when we "leave" a terminal, but not when we merely turn the mode off. Stefan
bug-gnu-emacs <at> gnu.org
:bug#78899
; Package emacs
.
(Wed, 02 Jul 2025 10:58:02 GMT) Full text and rfc822 format available.Message #26 received at 78899 <at> debbugs.gnu.org (full text, mbox):
From: Pip Cet <pipcet <at> protonmail.com> To: Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: luangruo <at> yahoo.com, Eli Zaretskii <eliz <at> gnu.org>, vincent <at> vinc17.net, 78899 <at> debbugs.gnu.org Subject: Re: bug#78899: 30.1; garbage inserted in the terminal buffer when quitting Emacs Date: Wed, 02 Jul 2025 10:57:41 +0000
"Stefan Monnier" <monnier <at> iro.umontreal.ca> writes: >> * lisp/term/xterm.el (xterm--query-name-and-version): Accept >> 'callback' argument. Call it. >> (xterm--init): Handle asynchronous terminal id responses. > > Hmm... you may want to check the code history, because I have a vague > recollection that we had something along these lines at some point and > it bumped into other problems, probably when ELisp code runs between > `xterm--query-name-and-version` and its callback, and sends other things > to the terminal or reads from the terminal. > > Maybe I misremember, of course. Thanks! Maybe a conditional throw would be better? I don't think it's a problem that we don't recognize xterms which are so slow to respond that mouse-mode would be painful to use, anyway (it certainly is over tor). >> +(defun xterm-sync (terminal &optional timeout) >> + "Ensure that the TERMINAL is in a synchronized state, but obey TIMEOUT." >> + ;; This function tries very hard to handle all events that do not belong >> + ;; to the escape sequence we are looking for. It's usually called only >> + ;; when Emacs quits, so this might be overkill. >> + (setq timeout (or timeout 2.0)) >> + (let ((i 0) >> + events all-events) >> + ;; request Primary DA from terminal >> + (send-string-to-terminal "\e[0c" terminal) >> + ;; build two lists: ALL-EVENTS contains all events, while EVENTS >> + ;; associates character events (only) to their index in ALL-EVENTS. >> + ;; We filter out the expected escape sequence ("\e[?") from the >> + ;; ALL-EVENTS list. >> + (with-timeout (timeout >> + (setq unread-command-events >> + (append unread-command-events >> + (nreverse all-events)))) >> + (while (not >> + (pcase events >> + (`((?? . ,i0) >> + (?\[ . ,i1) >> + (?\e . ,i2) . ,_) >> + (setq all-events (nreverse all-events)) >> + (pop (nthcdr i0 all-events)) >> + (pop (nthcdr i1 all-events)) >> + (pop (nthcdr i2 all-events)) >> + t))) >> + (let ((event (read-event))) >> + (push event all-events) >> + (when (characterp event) >> + (push (cons event i) events)) >> + (incf i))) >> + ;; read the rest of the response >> + (xterm--read-string ?c) >> + ;; replay events that preceded or occurred within the escape >> + ;; sequence. >> + (setq unread-command-events >> + (append unread-command-events all-events))))) > > Can we consolidate the two "nreverse + append to u-c-e", maybe to an > unwind-handler? That would make sense, yes. Premature optimization there. (Moving the mouse during xterm-sync does cause "a lot" of input, but it's nowhere near enough to warrant this microoptimization). > Also, I have the impression that we should *prepend* rather than append > (in practice u-c-e should be nil at that point so it likely doesn't > matter, tho). You're right. There are two cases, but prepending is the right thing to do in both of them :-) > Oh, and `(xterm-sync TERMINAL)` sounds like a very generic > functionality, whereas the code is much more specialized and presumes > we've send a specific escape sequence. Is it specialized? It ensures that commands sent before the function call affect input received after the function call. The precise escape sequence (which is sent from within xterm-sync, not before) doesn't matter, we just need something that will trigger a response from the terminal. > I'd use a longer name that makes it more clear what it's doing: > "xterm-sync" says what it's for but not what it really does. I agree; this function isn't generally useful, because there is no way to actually do what it tries to do in all cases. It's good enough to prevent mouse events from showing up in the usual case (a single xterm, no fake input from timers, no silly users hitting ESC [ ? c just for fun just before xterm-sync runs), but that's about it. >> @@ -544,8 +548,10 @@ 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-sync terminal)) >> ;; FIXME: This should use a dedicated error signal. >> (error (if (equal (cadr err) "Terminal is currently suspended") >> nil > > Do we need the `xterm-sync` in all cases? > IIUC we need it when we "leave" a terminal, but not when we merely turn > the mode off. I'm not sure. Do we care about: (progn (xterm-mouse-mode 0) (discard-input) (read-event)) producing extra text (and returning 27)? Thanks again for the suggestions. Here's the current version of the patch. I tried doing something about suspending ttys, too, but calling (suspend-tty) currently aborts for me on an xterm. From 72ca13cb048a0fb328b6ff05d890759f3ab99ed4 Mon Sep 17 00:00:00 2001 From: Pip Cet <pipcet <at> protonmail.com> Subject: [PATCH 1/2] Fix throw-without-catch on slow xterms (bug#78899) * lisp/term/xterm.el (xterm--query-name-and-version): Don't throw if the 'catch' has gone out of scope. --- lisp/term/xterm.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el index 4f23a909b69..73dc15b1c25 100644 --- a/lisp/term/xterm.el +++ b/lisp/term/xterm.el @@ -908,14 +908,17 @@ 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)) + (let ((too-late nil) + (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)))))) + (unless too-late + (throw 'result str))))))) + (setq too-late t) nil))) (defun xterm--push-map (map basemap) -- 2.50.0 From 4335c11c174742e1c162c8eb1f37acd1cb29b65e Mon Sep 17 00:00:00 2001 From: Pip Cet <pipcet <at> protonmail.com> Subject: [PATCH 2/2] Avoid unhandled mouse events on slow xterms (bug#78899) * lisp/xt-mouse.el (turn-on-xterm-mouse-tracking-on-terminal): Register 'kill-emacs' hook function. (xterm-mouse--sync): New function. (turn-off-xterm-mouse-tracking-on-terminal): Call 'xterm-mouse--sync' when disabling mouse tracking. --- lisp/xt-mouse.el | 53 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el index 89f9bbab608..057ed7e18e8 100644 --- a/lisp/xt-mouse.el +++ b/lisp/xt-mouse.el @@ -528,10 +528,57 @@ turn-on-xterm-mouse-tracking-on-terminal (signal (car err) (cdr err))))) (push enable (terminal-parameter nil 'tty-mode-set-strings)) (push disable (terminal-parameter nil 'tty-mode-reset-strings)) + (add-hook 'kill-emacs-hook + (lambda () + (turn-off-xterm-mouse-tracking-on-terminal terminal))) (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" (term1 &optional term2)) + +(defun xterm-mouse--sync (terminal &optional timeout) + "Ensure that the TERMINAL is in a synchronized state, but obey TIMEOUT. + +When the sequence to disable mouse tracking has been sent and this +function returns successfully, no further mouse events should be +produced." + ;; This function tries very hard to handle all events that do not belong + ;; to the escape sequence we are looking for. + (setq timeout (or timeout 2.0)) + (let ((i 0) + (skip (length unread-command-events)) + events all-events) + ;; request Primary DA from terminal + (send-string-to-terminal "\e[0c" terminal) + ;; build two lists: ALL-EVENTS contains all events, while EVENTS + ;; associates character events (only) to their index in ALL-EVENTS. + ;; We filter out the expected escape sequence ("\e[?") from the + ;; ALL-EVENTS list. + (unwind-protect + (with-timeout (timeout nil) + (while (not + (pcase events + (`((?? . ,i0) + (?\[ . ,i1) + (?\e . ,i2) . ,_) + (when (> i2 skip) + (pop (nthcdr (- i i2) all-events)) + (pop (nthcdr (- i i1) all-events)) + (pop (nthcdr (- i i0) all-events)) + t)))) + (let ((event (read-event))) + (push event all-events) + (incf i) + (when (characterp event) + (push (cons event i) events)))) + ;; read the rest of the response + (xterm--read-string ?c)) + ;; replay events that preceded or occurred within the escape + ;; sequence. + (setq unread-command-events + (append (nreverse all-events) unread-command-events))))) + (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 @@ -544,8 +591,10 @@ 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 (cadr err) "Terminal is currently suspended") nil -- 2.50.0
bug-gnu-emacs <at> gnu.org
:bug#78899
; Package emacs
.
(Sat, 05 Jul 2025 20:21:02 GMT) Full text and rfc822 format available.Message #29 received at 78899 <at> debbugs.gnu.org (full text, mbox):
From: Jared Finder <jared <at> finder.org> To: Eli Zaretskii <eliz <at> gnu.org> Cc: luangruo <at> yahoo.com, Pip Cet <pipcet <at> protonmail.com>, vincent <at> vinc17.net, Stefan Monnier <monnier <at> iro.umontreal.ca>, 78899 <at> debbugs.gnu.org Subject: Re: bug#78899: 30.1; garbage inserted in the terminal buffer when quitting Emacs Date: Sat, 05 Jul 2025 13:20:30 -0700
(I appear to have gotten dropped off the bug thread.) On 2025-07-01 04:53, Eli Zaretskii wrote: >> Date: Mon, 30 Jun 2025 22:47:45 +0000 >> From: Pip Cet <pipcet <at> protonmail.com> >> Cc: vincent <at> vinc17.net, luangruo <at> yahoo.com, 78899 <at> debbugs.gnu.org > > Thanks. Jared and Stefan, any comments? Does the most recent proposed patch work when Emacs is used across multiple terminals? I expect it does not because kill-emacs-hook is only run when Emacs is fully killed. I think it'd be more appropriate to add additional functionality to the terminal parameter tty-reset-strings to ensure the message is processed. But there's a scary message there about these escape strings and the emergency escape that I do not understand the ramifications of. -- MJF
bug-gnu-emacs <at> gnu.org
:bug#78899
; Package emacs
.
(Wed, 09 Jul 2025 11:42:02 GMT) Full text and rfc822 format available.Message #32 received at 78899 <at> debbugs.gnu.org (full text, mbox):
From: Pip Cet <pipcet <at> protonmail.com> To: Jared Finder <jared <at> finder.org> Cc: luangruo <at> yahoo.com, Eli Zaretskii <eliz <at> gnu.org>, vincent <at> vinc17.net, Stefan Monnier <monnier <at> iro.umontreal.ca>, 78899 <at> debbugs.gnu.org Subject: Re: bug#78899: 30.1; garbage inserted in the terminal buffer when quitting Emacs Date: Wed, 09 Jul 2025 11:40:53 +0000
"Jared Finder" <jared <at> finder.org> writes: > On 2025-07-01 04:53, Eli Zaretskii wrote: >>> Date: Mon, 30 Jun 2025 22:47:45 +0000 >>> From: Pip Cet <pipcet <at> protonmail.com> >>> Cc: vincent <at> vinc17.net, luangruo <at> yahoo.com, 78899 <at> debbugs.gnu.org >> >> Thanks. Jared and Stefan, any comments? > > Does the most recent proposed patch work when Emacs is used across > multiple terminals? Probably not, no. It doesn't even work for suspending a terminal. Working on something better, based on your suggestion below. I'm not sure whether I'm doing it right, but if I create a second terminal with emacsclient -nw and suspend it with C-z, Emacs crashes. I've worked around that for now, but if it's not just a local problem, we should probably fix it. (I also had xterm crash once while working on the selection code. Unfortunately, all I have is the single-line segfault report in dmesg, which isn't very helpful.) > I expect it does not because kill-emacs-hook is > only run when Emacs is fully killed. I think it'd be more appropriate > to add additional functionality to the terminal parameter > tty-reset-strings to ensure the message is processed. I agree that it would be nice to push a function to the tty-mode-reset-strings terminal parameter, and run it from C if we can (we can't run Lisp from within an Emergency Escape). This requires reordering two calls: suspend-tty-functions does the actual suspending in "emacsclient -nw" instances, so it can only be run after we reset the tty modes, and shut_down_emacs should not inhibit Lisp until after it has reset the sys modes . There don't appear to be any other users that expect the order to be different. > But there's a scary message there about these escape strings and the > emergency escape that I do not understand the ramifications of. I think I understand what's being said there: we definitely need an additional flag to prevent any potentially dangerous activity such as running Lisp, or even inspecting Lisp data that might have mark bits that we might fail to account for. I resurrected inhibit_lisp_code for this purpose. I'm still seeing other problems on slow xterms: xterm--query's synchronous branch fails to work if other input arrives after the (discard-input) but before the response to our query, which means that if you start typing before the initial xterm--query calls complete, you'll get the background description as input. Since the delay of running multiple synchronous queries is also significant, I think it would be best to switch to asynchronous xterm--query in all but two cases: the new synchronization method needs to be synchronous to ensure proper ordering, and the gui-backend-get-selection method needs to be synchronous because the API is. There are also some minor improvements: * turn-off-xterm-mouse-tracking-on-terminal now removes the sequences successfully. * xterm--query now uninstalls the input-decode-map binding when the timeout expires. Here's a snapshot (including the workaround to make frame suspension works). diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el index 4f23a909b69..9618e44ea30 100644 --- a/lisp/term/xterm.el +++ b/lisp/term/xterm.el @@ -743,9 +743,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)) (let ((recompute-faces (xterm-maybe-set-dark-background-mode (string-to-number (match-string 1 str) 16) @@ -763,14 +764,10 @@ xterm--report-background-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") @@ -816,6 +813,22 @@ xterm--version-handler ;;(xterm--init-activate-get-selection) (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\".") @@ -823,100 +836,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. @@ -965,16 +920,9 @@ xterm--init (when xterm-set-window-title (xterm--init-frame-title)) - (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) @@ -1053,6 +1001,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) @@ -1064,27 +1039,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 89f9bbab608..4a4cb3a37b3 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. ;; FIXME: is there more elegant way to detect the initial @@ -519,19 +523,35 @@ 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. (error (if (equal (cadr err) "Terminal is currently suspended") nil ; The sequence will be sent upon resume. (signal (car err) (cdr 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 @@ -544,18 +564,22 @@ 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 (cadr err) "Terminal is currently suspended") nil (signal (car err) (cdr 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 d65a7cbc1f1..025b1fbd99a 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -3455,8 +3455,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; } @@ -3516,7 +3516,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 cf8f4bd63f7..ac81886f206 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); } @@ -3093,6 +3095,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 4c514001d9d..d7d3b109bc6 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3187,6 +3187,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. */ @@ -4554,6 +4556,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 fcb66f4c58a..ed346135aaa 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -12293,7 +12293,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/term.c b/src/term.c index 8aa47322d19..cd6ed09c29a 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); + } } } @@ -2415,14 +2419,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 @@ -4861,6 +4867,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); @@ -4875,6 +4883,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 668b8845029..6a2fa12f748 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; }
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.