Repository navigation
Conversation
A Windows initial pane keeps a deferred start for 2 s after new-session, so input sent at once is queued until its ConPTY console is isolated from the launching client. While that start is open, observe_runtime_pane_exit reported no exit at all, and the exit handler gave up after 20 fast retries (about 200 ms). A pane whose program exited at once never committed its exit: no remain-on-exit message, no pane-died hook, and without remain-on-exit the session never closed. The start exists to protect input for a live console. Once the pane's process tree has exited there is no console left to receive it, so the observation now ends that start, as kill-pane and kill-session already do, and the exit commits at once. The start task skips its queued-input flush when the start has ended this way. Tested on Windows 11 (build 26300): the new test fails at the fork point (the session never closes) and passes with the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: rhys <5460583+xanth@users.noreply.github.com>
…ts message
A format query (display-message, list-panes) refreshes #{pane_dead} by
observing the pane's exit itself, so pane_dead flips without waiting for
the exit handler. That observation is cached in the dead-pane map, and the
exit handler reads the same map to tell a first exit from a repeated one.
When a query observed the exit first, the handler took the exit for one
it had already committed and wrote no remain-on-exit message; a capture
taken as soon as pane_dead read 1 also preceded the message.
In tmux the pane becomes dead in server_destroy_pane, which writes the
message in the same step, so pane_dead=1 always comes with it. A query now
leaves a pane whose remain-on-exit is not off to its exit commit, which
remains prompt, and still observes the exit of a pane that closes. Panes
without remain-on-exit keep the prompt refresh.
Tested on Windows 11 (build 26300): the new test holds the state lock past
the child's exit, runs the query, and checks that the pane stays alive
until the commit and then shows the message; it fails at the fork point.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: rhys <5460583+xanth@users.noreply.github.com>
tmux writes the dead-pane message after a wrapped linefeed from the bottom row (server_destroy_pane: screen_write_linefeed(ctx, 1, 8)), so `capture-pane -J` joins the row above it with the message. RMUX wrote a plain newline and an erase-line, so -J kept one more line break than tmux, and its scroll region was not reset. Reset the scroll region, write the message, then mark the row above it wrapped: text written at column 0 clears that mark. The mark needs the screen writer under the transcript lock, so the byte-only transcript append becomes one case of a transcript mutation. Tested on Windows 11 (build 26300): the new pane_transcript test passes (it calls the method this commit adds); the probe that shows the old behaviour is capture-pane -p -e -J -S - of a dead pane against tmux 3.6, now equal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: rhys <5460583+xanth@users.noreply.github.com>
…eflowing When a pane narrows or widens, reflow joins each wrapped row with the row after it, at the row's full width. That is right for a row filled by auto-wrap, and for a wrapped row DCH or ICH shortened later: tmux keeps such a row's cellused, so its blanks stay data (tmux 3.7b, pinned by the existing screen and keyframe tests). It is wrong for a row marked wrapped while short, as tmux marks the row above the remain-on-exit message (screen_write_linefeed(ctx, 1, 8)): tmux joins it at its cellused, the written cells only. RMUX padded it, so after a dead pane was resized from 80 to 20 columns the row above "Pane is dead" became 80 spaces over four rows. Such a row is a compact ASCII row whose stored data end, which keeps written trailing spaces, is short of the width. A wrapped compact row now joins at that data end; a wrapped cell row keeps its full width. Neither history compaction nor an ASCII write over a blank row turns a wrapped cell row back into a compact one, so a DCH- or ICH-edited row keeps its blanks. Tested on Windows 11 (build 26300): the new screen test fails at the fork point and passes with the fix; rmux-core and rmux-server lib tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: rhys <5460583+xanth@users.noreply.github.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #241
Closes #242
Summary
A program that drives RMUX polls
#{pane_dead}and then captures the pane. Four commits make what it sees match tmux: the exit is always committed,pane_deadnever reads 1 before the remain-on-exit message, the message is joined to the row above it as tmux joins it, and a resize keeps that joined row's written cells only. Each commit builds and passes its own tests, so it can be reviewed commit by commit.End the deferred start of a pane whose process exited (
3941944b, Windows). A Windows initial pane keeps a deferred start for 2 s afternew-session, so input sent at once is queued until its ConPTY console is isolated from the launching client. While that start was open,observe_runtime_pane_exitreported no exit at all, and the exit handler gave up after 20 fast retries (about 200 ms). A pane whose program exited at once never committed its exit: no remain-on-exit message, nopane-diedhook, and withoutremain-on-exitthe session never closed. The start exists to protect input for a live console. Once the pane's process tree has exited there is no console left, so the observation now ends that start, as kill-pane and kill-session already do, and the exit commits at once. The start task skips its queued-input flush when the start has ended this way.Leave a kept pane's death to the exit commit that writes its message (
5f95dc68). A format query (display-message,list-panes) refreshes#{pane_dead}by observing the pane's exit itself. That observation is cached in the dead-pane map, and the exit handler reads the same map to tell a first exit from a repeated one. When a query observed the exit first, the handler took it for one it had already committed and wrote no message; a capture taken as soon aspane_deadread 1 also came before the message. In tmux the pane becomes dead inserver_destroy_pane, which writes the message in the same step. A query now leaves a pane whoseremain-on-exitis notoffto its exit commit, which stays prompt, and still observes the exit of a pane that closes. Panes withoutremain-on-exitkeep the prompt refresh.Wrap the row above the message like tmux (
4cd1bdb1). tmux writes the dead-pane message after a wrapped linefeed from the bottom row (screen_write_linefeed(ctx, 1, 8)), socapture-pane -Jjoins the row above it with the message. RMUX wrote a plain newline and an erase-line, so-Jkept one more line break than tmux, and the program's scroll region was not reset. The message is now written after a scroll-region reset, and then the row above it is marked wrapped (text written at column 0 clears that mark, so the order matters). The mark needs the screen writer under the transcript lock, so the byte-only transcript append becomes one case of a transcript mutation (mutate_runtime_pane_transcript).Join a short wrapped row by its written cells when reflowing (
f5604c56). Reflow joined each wrapped row with the next one at the row's full width. That is right for a row filled by auto-wrap, and for a wrapped row that DCH or ICH shortened later: tmux keeps such a row'scellused, so its blanks stay data (tmux 3.7b; pinned by the existing screen and keyframe tests). It is wrong for a row marked wrapped while short, which is what the previous commit (and tmux) does to the row above the message: tmux joins it at its written cells only. RMUX padded it, so after a dead pane was resized from 80 to 20 columns the row above "Pane is dead" became 80 spaces over four rows. A wrapped compact ASCII row now joins at its stored data end, which keeps written trailing spaces (GridLine::wrapped_end()incrates/rmux-core/src/grid/cell.rs), and a wrapped cell row keeps its full width. Neither history compaction nor an ASCII write over a blank row turns a wrapped cell row back into a compact one, so a DCH- or ICH-edited row keeps its blanks. The unusedline_widthhelper goes.Testing
Tested on: Windows 11 Pro 10.0.26300 (build 26300), with these commits on RMUX 0.10.0.
3941944b: new Windows testpane_exiting_during_deferred_start_closes_its_session_before_the_start_endsinhandler_pane_exit_format_tests.rs.new-sessionwith the commandexit 3;has-sessionreports no session within 1.5 s, before the 2 s start would have closed. It fails at the fork point (the session never closes) and passes with the fix. By hand, the session closes with status 3 andpane-diedfires; the death is seen after about 0.23 s.5f95dc68: new testpane_dead_query_leaves_a_kept_pane_to_its_exit_commit. It holds the state lock past the child's exit, runs the query, and checks that the pane stays alive until the commit and then shows the message. It fails at the fork point and passes with the fix. A probe that captures at the firstpane_dead1 had the message in all of 20 runs. The existingdisplay_message_pane_dead_observes_exited_child_promptlytest still passes.4cd1bdb1: newpane_transcripttestdead_pane_footer_joins_the_row_above_like_tmux. A 20x3 pane with a scroll region writeserror, then the footer; a plain capture gives\n\nPane is dead\n, and a capture withjoin_wrappedgives\nPane is dead\n. It calls the method this commit adds, so the probe for the old behaviour iscapture-pane -p -e -J -S -of a dead pane compared with tmux 3.6: it differed before and is equal now.f5604c56: newscreentestwidth_resize_joins_a_short_wrapped_row_by_its_written_cells. An 8-column screen withabon row 1 andcdon row 2, row 1 marked wrapped, resized to 4 columns: the first non-empty row isabcd. It fails at the fork point (ab, a blank row,cd) and passes with the fix. The existing DCH, ICH and keyframe tests still pass, includingwidth_resize_line_edits_materialize_a_wide_placement_gap_as_spaceandinsert_character_full_suffix_clears_requested_cells_product_divergence.remain-on-exitpane, captured plain, with-S -, with-e -J -S -, and after a resize to 80x24, 80x40 and 20x24, gives the samecapture-paneoutput as tmux 3.6.rmux-coreandrmux-serverlib tests pass on a tree with these commits and the other proposed fixes merged, apart from timing-sensitive tests that pass when run alone.Linux and macOS are covered by this PR's CI.
Cargo gates on the workspace:
cargo fmt --all -- --check: pass.cargo clippy --workspace --all-targets -- -D warnings: pass with the pinned toolchain (1.96.1).cargo build --workspace: pass.cargo test --workspace --all-targets: run once on Windows with all of these fixes merged, with the same settings as the Windows CI job. 7,018 passed. 45 tests in the release and bench specs fail on this machine because Python is not onPATH(43) or the temp path is too long (2); they fail the same way without these fixes. Not run here, because they open console windows:windows_attach_exit,windows_attach_fuzz,windows_nested_mouse_issue96,windows_prompt_overlay_chain,windows_mouse_border_resize,rmux-pty'swindows_conpty, andrmux-client'smanaged_attach_stdout_has_single_console_mode_owner.🤖 Generated with Claude Code