Shell prompt is misplaced when exiting Helix after resizing tmux pane
Summary
On exit, Helix sends the "leave alternate screen" sequence (CSI ? 1049 l) twice:
- once from
Application::run→restore_term()→TerminaBackend::restore(), - and again from
impl Drop for TerminaBackend, which unconditionally re-sends the same reset sequence.
DECRST 1049 means "switch to the normal screen and restore the cursor saved by DECSET 1049". xterm and tmux perform that cursor restore even when the alternate screen is already off. So if the terminal was resized while Helix was open, the first ?1049l correctly restores and reflows the normal screen and moves the cursor with it, and the second ?1049l then overwrites the cursor with the stale, pre-resize saved position.
The shell prompt is printed in the middle of the scrollback, over old output.
Without a resize both positions coincide, so the bug is invisible. It appeared with the switch from Crossterm to Termina (#13307, released in 25.07): the Crossterm backend had no Drop impl. Current master still has it.
Reproduction Steps
Observe asciinema here: https://asciinema.org/a/gNuOg0mMU6IXWxsV
Tested with tmux 3.6a; xterm's ctlseqs documents the same DECRST 1049 semantics, "restore cursor as in DECRC", so it is not tmux-specific):
tmux new -s t -x 80 -y 24, then produce some scrollback:seq 1 30hx somefile(or justhx)- Make the pane taller, e.g.
tmux resize-window -y 40from another window :q
Expected: the new prompt appears on the bottom row, below the previous output. Actual: the prompt appears ~10 rows up, on top of the earlier output.
The same result without Helix, proving it is the duplicate ?1049l:
printf '\e[?1049h'; sleep 3; printf '\e[?1049l\e[?1049l' # resize the pane during the sleep
With a single \e[?1049l (or with vim / less) the prompt lands correctly.
Captured Helix exit bytes (tmux pipe-pane), showing the reset block sent twice:
^[[?25h^[[0 q^[[?2004l^[[?1004l^[[?1049l <- restore()
^[[?1000l^[[?1002l^[[?1003l^[[?1015l^[[?1006l^[[2 q^[[34h <- drop(): disable_extensions/mouse
^[[?25h^[[0 q^[[?2004l^[[?1004l^[[?1049l <- drop(): reset block again
Helix log
Not relevant (no error is logged).
Proposed fix (tested in my environment)
Track whether the terminal is claimed (claim() sets a flag, restore() clears it) and make Drop skip the reset when restore() already ran. Drop still resets the terminal if restore() was never called (e.g. an early error). PR: #TODO
Platform
Linux (NixOS)
Terminal Emulator
tmux 3.6a
Installation Method
nix flake
Helix Version
helix 25.07.1 (ef7869d8)
Source: helix-editor/helix