diff options
| author | user <user@clank> | 2026-08-14 21:32:40 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-14 21:32:40 +0200 |
| commit | c5f4bb75403df3ca7e0bf6c30981c0cb4e54921e (patch) | |
| tree | 44bddd3b1ba1ea0f4782b50d9c095c6b813f7b7b /src/config.h | |
| parent | sl0ppty: let a child's exit status survive long enough to be read (diff) | |
| download | slosh-c5f4bb75403df3ca7e0bf6c30981c0cb4e54921e.tar.gz slosh-c5f4bb75403df3ca7e0bf6c30981c0cb4e54921e.tar.xz slosh-c5f4bb75403df3ca7e0bf6c30981c0cb4e54921e.zip | |
sl0ppty: a pane outlives the program that was in it
A pane whose program exited was reaped before the next paint. The pane went,
and with it everything the program had printed -- including, always, the one
line that would have explained why it stopped. Close the last one and the
session went too, so a mistyped command in a fresh session closed the
terminal you were typing in.
Now the pane stays. It keeps its contents, writes one line into its own
backlog saying how it ended -- `[process exited: status 3]`, `signal 9` --
says the same thing in its frame and in the status line, and offers the two
verbs that still mean anything:
╰ exited: status 3 ──────────────────────────────[re-run]─[close]─╯
`[re-run]` runs the same argv in the same cwd in the *same pane*: same id,
same node in the tree, same terminal. Nothing that referred to that pane has
to be told anything, and the run that ended stays above the new one in the
scrollback, which is what makes comparing two runs possible at all. C-a r
and {"cmd":"rerun"} do the same thing; a live pane is refused rather than
restarted, because "run it again" is not a request to kill something.
Those buttons are the OSC 5577 row, not a second mechanism. A dead pane's
own buttons are inert -- a click would write a report into a closed pty --
so the row is given over rather than added to. `close` is listed last, so it
is drawn rightmost and is the one that survives a narrow frame: being unable
to dismiss a dead pane is a trap, being unable to re-run one is an
inconvenience.
`dead` is a shader state now, which D13 said it could never be: it named a
frame that could not be drawn, and now there is one. Dead panes drain to
grey by default, so a wall of them reads at a glance. A collapsed header
says "exited" in words as well, because the shader pass does not reach one
and a flattened tab draws nothing else of a pane.
Two things about this are load-bearing rather than incidental:
- the pty fd is closed the moment EOF is seen. A pane that outlives its
program stays in the poll set, and an fd at EOF is readable forever: the
session would spin a core behind a pane that looks idle.
- the exit status is collected with a bounded wait, never a blocking one.
EOF means the program already closed its fds, so it is on its way out and
a few hundred microseconds wins that race. A blocking waitpid would hang
the whole session on one program that closes its terminal and keeps
running; losing the race costs the words "status 0" and nothing else.
`keep_dead false` restores the old behaviour for a one-shot layout that
wants it. `panes` reports alive/exit_code/exit_signal, so a script can find
the pane whose command failed and rerun it.
Diffstat (limited to 'src/config.h')
| -rw-r--r-- | src/config.h | 20 |
1 files changed, 17 insertions, 3 deletions
diff --git a/src/config.h b/src/config.h index 76a9b85..a854fe8 100644 --- a/src/config.h +++ b/src/config.h @@ -15,13 +15,15 @@ #include "sl0ppty.h" /* Pane states a shader can be hung off, in the order they are tested. Only - * states a pane can actually be seen in: a pane whose program exits is reaped - * before the next paint, so there is deliberately no "dead" here — it would - * name a shader that could never draw a frame. */ + * states a pane can actually be seen in — which `dead` now is: a pane whose + * program exits is kept until it is dismissed, so there is a frame to draw. + * It ranks just under the drag states, above every ambient one: nothing about + * a pane is more worth knowing than that it is over. */ typedef enum { PSTATE_DRAGGING, /* the pane you have hold of */ PSTATE_DROP_HOVER, /* the one under the pointer, where it would land */ PSTATE_DROP_TARGET, /* the others, all of them somewhere it could go */ + PSTATE_DEAD, /* its program exited; waiting to be re-run or closed */ PSTATE_SUSPENDED, /* laid out, never started */ PSTATE_SCROLLED, /* looking at scrollback rather than the present */ PSTATE_UNFOCUSED, @@ -36,6 +38,7 @@ typedef enum { ACT_SPLIT_COLS, ACT_SPLIT_ROWS, ACT_CLOSE_PANE, + ACT_RERUN, ACT_ZOOM, ACT_MINIMIZE, ACT_FOCUS_LEFT, @@ -100,6 +103,13 @@ typedef struct { * terminals while chrome here is booked as one — so the default is narrow * and anyone who knows their terminal can say otherwise. */ char bell_mark[16]; + /* A pane whose program exits stays, showing what it printed and offering to + * run it again, until it is closed. Off restores the old behaviour: the + * pane vanishes on the next paint, and with the last pane so does the + * session. That is a real choice — a one-shot layout wants it — but it is + * not the default, because a pane that disappears takes its own error + * message with it. */ + bool keep_dead; uint16_t min_pane_cols, min_pane_rows; /* The smallest pane a split is allowed to *produce*. min_pane is the point * below which the layout gives up and collapses a pane; this is the point @@ -194,6 +204,10 @@ typedef struct { /* the bell mark in a pane's titlebar */ color_t bell; + /* what a dead pane says: the line in its backlog, and the word in the + * status line and its own frame */ + color_t dead; + /* the frame's own buttons */ color_t pane_button, pane_button_hover; |
