diff options
| author | user <user@clank> | 2026-08-14 12:13:43 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-14 12:13:43 +0200 |
| commit | cba63d04627c7bd06ce80f2be83b7c0d54fb8568 (patch) | |
| tree | f6a72b8950a673f3eb3cf433279bde1122c0c0ac /src/headless.c | |
| parent | sl0ppty: rename from sl0ptty (diff) | |
| download | slosh-cba63d04627c7bd06ce80f2be83b7c0d54fb8568.tar.gz slosh-cba63d04627c7bd06ce80f2be83b7c0d54fb8568.tar.xz slosh-cba63d04627c7bd06ce80f2be83b7c0d54fb8568.zip | |
sl0ppty: one-shot --headless printed a blank screen
`--headless` is documented as "run once, print the screen", and it printed
nothing for any command that finished — which is most of them. `make smoke`
had been showing six blank rows.
settle() calls app_reap() every iteration, and app_reap closes a pane whose
child has exited; closing the last one takes the tab with it. A command like
`sh -c printf ...` exits immediately, so the pane and its content were gone
before app_compose ever ran, and the compose found an app with no tabs.
So settle() now takes an optional screen and composes into it before the reap
rather than after. This is exact rather than a narrowed race: pane_pump() only
marks a pane dead on read() returning 0, and it reaches EOF only after it has
drained everything the pty had, so a pane cannot be marked dead while its
output is still pending and the last snapshot always holds the finished
screen. --script passes NULL and is byte-for-byte unchanged.
smoke also asked for 6 rows, which after the outer margin, the tab strip and
two borders leaves about one row for the pane — under min_pane rows, so it
collapsed to an empty frame and would have shown nothing even with the fix.
It asks for 12 now.
Found while renaming; it predates the rename, reproduced on the commit before.
Diffstat (limited to '')
| -rw-r--r-- | src/headless.c | 19 |
1 files changed, 14 insertions, 5 deletions
diff --git a/src/headless.c b/src/headless.c index dab817d..451996c 100644 --- a/src/headless.c +++ b/src/headless.c @@ -27,8 +27,14 @@ static int64_t ms_now(void) { return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; } -/* Pump every pane until none has produced anything for `quiet` ms. */ -static void settle(app_t *a, int quiet) { +/* Pump every pane until none has produced anything for `quiet` ms. + * + * `snap`, when given, is composed after each pump and *before* the reap that + * would close an exited pane, so a one-shot run still prints what the command + * drew. This is exact, not a narrowed race: pane_pump() only marks a pane dead + * on EOF, which it reaches after consuming everything the pty had, so the last + * snapshot always holds the finished output. */ +static void settle(app_t *a, int quiet, screen_t *snap) { int64_t last = ms_now(); int64_t deadline = last + 3000; while (!app_should_quit(a) && ms_now() < deadline) { @@ -45,6 +51,7 @@ static void settle(app_t *a, int quiet) { last = ms_now(); } } + if (snap) app_compose(a, snap); app_reap(a); if (ms_now() - last >= quiet) break; } @@ -68,8 +75,10 @@ int run_headless(const char *const argv[], uint16_t cols, uint16_t rows, input_parser_t *in = input_new(); if (!script) { /* one-shot: run it, let it settle, print the screen */ - settle(a, idle_ms); - app_compose(a, &s); + settle(a, idle_ms, &s); + /* Panes still alive: take the freshest frame. Otherwise keep the one + * settle() snapshotted just before the last pane was reaped. */ + if (!app_should_quit(a)) app_compose(a, &s); char *dump = screen_dump(&s); fputs(dump, stdout); free(dump); @@ -87,7 +96,7 @@ int run_headless(const char *const argv[], uint16_t cols, uint16_t rows, if (!line[0]) continue; if (strncmp(line, "settle", 6) == 0) { - settle(a, line[6] == ' ' ? atoi(line + 7) : idle_ms); + settle(a, line[6] == ' ' ? atoi(line + 7) : idle_ms, NULL); continue; } |
