aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.c
diff options
context:
space:
mode:
authorsl0p <sl0p@sl0p.foo>2026-08-13 22:26:02 +0200
committersl0p <sl0p@sl0p.foo>2026-08-13 22:26:02 +0200
commit34fcd16e59e3087e9bf84ebce4e05d439eea9198 (patch)
treee07b9dfc235e3a0b67047d59a9c521c6e98a28ae /src/main.c
parentsl0ptty: input decoder + per-pane re-encode (diff)
downloadslosh-34fcd16e59e3087e9bf84ebce4e05d439eea9198.tar.gz
slosh-34fcd16e59e3087e9bf84ebce4e05d439eea9198.tar.xz
slosh-34fcd16e59e3087e9bf84ebce4e05d439eea9198.zip
sl0ptty: M0.5 — the headless driver and the harness everything rests on
--script runs the whole compositor with no tty: commands in (send, raw, settle, resize, snapshot, alive), composited screen out as JSON — rows of text, style runs, cursor, and the hit list. Styles are emitted as runs rather than per-cell objects, because a test wants 'columns 0..5 of row 0 are bold red', not 1920 objects. The hit list is empty until M1 paints something clickable, but the type, the JSON, and hit_test() exist now: one geometry, established before there is anything to get wrong. Also fixes two host-policy defaults found by the cursor never appearing. lib-vt is a VT library, not a terminal, so it starts with DECTCEM (mode 25) off and grapheme clustering (2027) off. Both are now set via MODE_DEFAULT, so they survive RIS. tests/harness.py + tests/test_screen.py: 19 checks over composition, styling, wide chars/clusters, scrollback, resize and the full input round trip — including the kitty/legacy re-encoding matrix, which no longer needs a pty. make test is now 2.4s for 59 checks; make test-live keeps the four that genuinely need a tty.
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c41
1 files changed, 6 insertions, 35 deletions
diff --git a/src/main.c b/src/main.c
index a2a587c..1c097f2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -81,39 +81,9 @@ static int64_t now_ms(void) {
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
-/* ---- headless: run a command, settle, dump the composited screen -------- */
-
-static int run_headless(const char *const argv[], uint16_t cols, uint16_t rows,
- int idle_ms) {
- pane_t *p = pane_new(argv, cols, rows, NULL);
- if (!p) {
- fprintf(stderr, "sl0ptty: cannot spawn pane\n");
- return 1;
- }
- screen_t s;
- screen_init(&s, cols, rows);
-
- int64_t last_activity = now_ms();
- while (pane_alive(p)) {
- struct pollfd pfd = {.fd = pane_fd(p), .events = POLLIN};
- int n = poll(&pfd, 1, 20);
- if (n > 0) {
- if (pane_pump(p) > 0) last_activity = now_ms();
- if (!pane_alive(p)) break;
- }
- if (now_ms() - last_activity > idle_ms) break;
- }
- pane_pump(p);
-
- pane_compose(p, &s, 0, 0, true);
- char *dump = screen_dump(&s);
- fputs(dump, stdout);
- free(dump);
-
- screen_free(&s);
- pane_free(p);
- return 0;
-}
+/* headless lives in headless.c */
+int run_headless(const char *const argv[], uint16_t cols, uint16_t rows,
+ int idle_ms, bool script);
/* ---- interactive -------------------------------------------------------- */
@@ -241,7 +211,7 @@ static int run_interactive(const char *const argv[]) {
}
int main(int argc, char **argv) {
- bool headless = false;
+ bool headless = false, script = false;
uint16_t cols = 80, rows = 24;
int idle_ms = 300;
const char *cmd_argv[64];
@@ -249,6 +219,7 @@ int main(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--headless") == 0) headless = true;
+ else if (strcmp(argv[i], "--script") == 0) headless = script = true;
else if (strcmp(argv[i], "--cols") == 0 && i + 1 < argc) cols = (uint16_t)atoi(argv[++i]);
else if (strcmp(argv[i], "--rows") == 0 && i + 1 < argc) rows = (uint16_t)atoi(argv[++i]);
else if (strcmp(argv[i], "--idle-ms") == 0 && i + 1 < argc) idle_ms = atoi(argv[++i]);
@@ -274,6 +245,6 @@ int main(int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN); /* no zombies; EOF on the pty is our signal */
- return headless ? run_headless(cmd_argv, cols, rows, idle_ms)
+ return headless ? run_headless(cmd_argv, cols, rows, idle_ms, script)
: run_interactive(cmd_argv);
}