GNU bug report logs - #79513
30.1.90; selected-window and current-buffer can start out of sync for text terminal emacsclient

Previous Next

Package: emacs;

Reported by: Spencer Baugh <sbaugh <at> janestreet.com>

Date: Thu, 25 Sep 2025 18:39:03 UTC

Severity: normal

Found in version 30.1.90

To reply to this bug, email your comments to 79513 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


Report forwarded to app-emacs-dev <at> janestreet.com, bug-gnu-emacs <at> gnu.org:
bug#79513; Package emacs. (Thu, 25 Sep 2025 18:39:03 GMT) Full text and rfc822 format available.

Acknowledgement sent to Spencer Baugh <sbaugh <at> janestreet.com>:
New bug report received and forwarded. Copy sent to app-emacs-dev <at> janestreet.com, bug-gnu-emacs <at> gnu.org. (Thu, 25 Sep 2025 18:39:03 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Spencer Baugh <sbaugh <at> janestreet.com>
To: bug-gnu-emacs <at> gnu.org
Subject: 30.1.90; selected-window and current-buffer can start out of sync
 for text terminal emacsclient
Date: Thu, 25 Sep 2025 14:38:03 -0400
Running emacsclient to start open a new text terminal frame, and using
--eval to change the selected window on that frame, causes
current-buffer to get out of sync with selected-window for the first key
sequence read on that frame.  The key sequence will be looked up in the
keymaps for current-buffer, and afterwards the found command will be run
in the window-buffer of selected-window.

This only happens on some terminals; so far I've reproduced it inside
screen, tmux, and vterm.  I think this is because other terminals emit
spurious events before the first user input, so by the time the user
provides input, current-buffer and selected-window are in sync again.

1. emacs -Q --fg-daemon=test
2. In screen, tmux, or vterm:
emacsclient -t --socket-name=test --eval '(pop-to-buffer (list-buffers-noselect))'
3. Hit q
4. Observe the error: "Buffer is read-only: #<buffer *Buffer List*>"
   This is because q gets looked up in the keymap for *scratch*, which
   has it bound to self-insert-command, which is then run in *Buffer List*.
5. Hit q
6. Observe the buffer list window being quit, since the second key
   sequence is correctly looked up in *Buffer List*, producing quit-window.

This is the result of this hack in read_key_sequence, called by
command_loop_1:

   if ((FIXNUMP (key) && XFIXNUM (key) == -2) /* wrong_kboard_jmpbuf */
       /* When switching to a new tty (with a new keyboard),
          read_char returns the new buffer, rather than -2
          (Bug#5095).  This is because `terminal-init-xterm'
          calls read-char, which eats the wrong_kboard_jmpbuf
          return.  Any better way to fix this? -- cyd  */
       || (interrupted_kboard != current_kboard))

Because of this, even though command_loop_1 calls read_key_sequence with
a "true" value for fix_current_buffer, the fix_current_buffer
conditional isn't hit in read_key_sequence, and we don't run
"Fset_buffer (XWINDOW (selected_window)->contents)" as we should.



In GNU Emacs 30.1.90 (build 57, x86_64-pc-linux-gnu, X toolkit, cairo
 version 1.15.12, Xaw scroll bars) of 2025-09-23 built on
 igm-qws-u22796a
Repository revision: 61402089ce10ee7d50e8923083de56571968a239
Repository branch: emacs-30
Windowing system distributor 'The X.Org Foundation', version 11.0.12011000
System Description: Rocky Linux 8.10 (Green Obsidian)

Configured using:
 'configure --with-x-toolkit=lucid --without-gpm --without-gconf
 --without-selinux --without-imagemagick --with-modules --with-gif=no
 --with-cairo --with-rsvg --without-compress-install --with-tree-sitter
 --with-native-compilation=aot
 PKG_CONFIG_PATH=/usr/local/home/garnish/libtree-sitter/0.22.6-1/lib/pkgconfig/'

Configured features:
CAIRO DBUS FREETYPE GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG LIBSYSTEMD
LIBXML2 MODULES NATIVE_COMP NOTIFY INOTIFY PDUMPER PNG RSVG SECCOMP
SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS TREE_SITTER X11 XDBE XIM
XINPUT2 XPM LUCID ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79513; Package emacs. (Tue, 21 Oct 2025 17:07:02 GMT) Full text and rfc822 format available.

Message #8 received at 79513 <at> debbugs.gnu.org (full text, mbox):

From: Sean Whitton <spwhitton <at> spwhitton.name>
To: 79513 <at> debbugs.gnu.org, Spencer Baugh <sbaugh <at> janestreet.com>
Cc: app-emacs-dev <at> janestreet.com
Subject: Re: bug#79513: 30.1.90; selected-window and current-buffer can
 start out of sync for text terminal emacsclient
Date: Tue, 21 Oct 2025 18:06:17 +0100
[Message part 1 (text/plain, inline)]
Hello,

On Thu 25 Sep 2025 at 02:38pm -04, Spencer Baugh wrote:

> Running emacsclient to start open a new text terminal frame, and using
> --eval to change the selected window on that frame, causes
> current-buffer to get out of sync with selected-window for the first key
> sequence read on that frame.  The key sequence will be looked up in the
> keymaps for current-buffer, and afterwards the found command will be run
> in the window-buffer of selected-window.
>
> This only happens on some terminals; so far I've reproduced it inside
> screen, tmux, and vterm.  I think this is because other terminals emit
> spurious events before the first user input, so by the time the user
> provides input, current-buffer and selected-window are in sync again.

I can reproduce it with the foot terminal emulator.

> 1. emacs -Q --fg-daemon=test
> 2. In screen, tmux, or vterm:
> emacsclient -t --socket-name=test --eval '(pop-to-buffer (list-buffers-noselect))'
> 3. Hit q
> 4. Observe the error: "Buffer is read-only: #<buffer *Buffer List*>"
>    This is because q gets looked up in the keymap for *scratch*, which
>    has it bound to self-insert-command, which is then run in *Buffer List*.
> 5. Hit q
> 6. Observe the buffer list window being quit, since the second key
>    sequence is correctly looked up in *Buffer List*, producing quit-window.
>
> This is the result of this hack in read_key_sequence, called by
> command_loop_1:
>
>    if ((FIXNUMP (key) && XFIXNUM (key) == -2) /* wrong_kboard_jmpbuf */
>        /* When switching to a new tty (with a new keyboard),
>           read_char returns the new buffer, rather than -2
>           (Bug#5095).  This is because `terminal-init-xterm'
>           calls read-char, which eats the wrong_kboard_jmpbuf
>           return.  Any better way to fix this? -- cyd  */
>        || (interrupted_kboard != current_kboard))
>
> Because of this, even though command_loop_1 calls read_key_sequence with
> a "true" value for fix_current_buffer, the fix_current_buffer
> conditional isn't hit in read_key_sequence, and we don't run
> "Fset_buffer (XWINDOW (selected_window)->contents)" as we should.

There are two instances where we act on fix_current_buffer and call
Fset_buffer, and I believe the relevant one to be this:

--8<---------------cut here---------------start------------->8---
	  if (NILP (first_event))
	    {
	      first_event = key;
	      /* Even if first_event does not specify a particular
		 window/position, it's important to recompute the maps here
		 since a long time might have passed since we entered
		 read_key_sequence, and a timer (or process-filter or
		 special-event-map, ...) might have switched the current buffer
		 or the selected window from under us in the mean time.  */
	      if (fix_current_buffer
		  && (XBUFFER (XWINDOW (selected_window)->contents)
		      != current_buffer))
		Fset_buffer (XWINDOW (selected_window)->contents);
	      current_binding = active_maps (first_event, Qnil);
	    }
--8<---------------cut here---------------end--------------->8---

read_key_sequence does a lot of replaying.  The point of this code, I
think, is to handle the case where either we have just called read_char
for the very first time, or we are replaying with mock_input==0.

The branch of code Spencer has pointed out handles the case of being
interrupted while initializing the terminal.  In contrast to the above,
that's a case in which we are not calling read_char for the very first
time and are replaying with mock_input==1.

Given that these situations are disjoint, I believe that rather than
changing control flow, we need to add an additional fix_current_buffer
check, as is done in the attached patch.
Spencer, does this fix the problem for you?

(I don't understand the full sequence of occurrences in the case we are
considering because the comment talks about read_char returning "the new
buffer" but what it actually returns is the 'q' that's typed.)

-- 
Sean Whitton
[0001-read_key_sequence-Additional-check-for-fix_current_b.patch (text/x-diff, attachment)]

This bug report was last modified 15 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.