diff -ruN a/config.def.h b/config.def.h --- a/config.def.h 2026-05-20 11:57:26.574344832 +0200 +++ b/config.def.h 2026-05-20 11:57:26.578344915 +0200 @@ -3,3 +3,9 @@ /* progress bar glyphs (ASCII default) */ static const char *const BAR_FILL = ">"; static const char *const BAR_EMPTY = "-"; + +/* persistent alert: keep ringing the bell until any key is pressed. + * Foreground-only; backgrounded runs always fall through. + * Override per-run with --no-persist. + */ +static const int ALERT_PERSIST = 1; diff -ruN a/ttym.c b/ttym.c --- a/ttym.c 2026-05-20 11:57:26.574344832 +0200 +++ b/ttym.c 2026-05-20 11:57:26.578344915 +0200 @@ -23,6 +23,7 @@ typedef struct { int quiet; /* -q: suppress completion bell */ int silent; /* -s: no bell, no extras */ + int alert_persist; /* alert loop until keypress; --no-persist disables */ } Config; static struct termios oldtios; @@ -226,6 +227,48 @@ fflush(stdout); } +/* ===== alert loop ===== + * Bell + poll for keypress, ~1.5s per beat; 4-min safety cap. + */ + +static int +is_foreground(void) +{ + if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) return 0; + pid_t pgrp = tcgetpgrp(STDIN_FILENO); + if (pgrp < 0) return 0; + return pgrp == getpgrp(); +} + +static void +alert_loop(void) +{ + fputs("\033[?25h\033[?7h", stdout); + int ticks = 0; + while (!interrupted) { + fputc('\a', stdout); + fflush(stdout); + sleep_ms(150); + + int acked = 0; + for (int i = 0; i < 15 && !interrupted; i++) { + if (ttyfd < 0) { sleep_ms(100); continue; } + struct pollfd pfd = { .fd = ttyfd, .events = POLLIN }; + int pr = poll(&pfd, 1, 100); + if (pr > 0 && (pfd.revents & POLLIN)) { + char ch; + ssize_t rd = read(ttyfd, &ch, 1); + if (rd >= 0) { acked = 1; break; } + } else if (pr < 0) { + sleep_ms(100); + } + } + if (acked) break; + if (++ticks > 160) break; + } + fflush(stdout); +} + /* ===== usage ===== * Patches MUST update this in lockstep with new flags so -h always * reflects the build. @@ -243,6 +286,7 @@ "FLAGS:\n" " -q no sound\n" " -s silent\n" +" --no-persist skip alert-until-keypress loop\n" " -- end of options\n" " -h help\n" "\n" @@ -375,6 +419,10 @@ fflush(stdout); sleep_ms(50); } + if (!cfg->silent && cfg->alert_persist && is_foreground()) { + interrupted = 0; + alert_loop(); + } } (void)cfg; @@ -387,6 +435,7 @@ main(int argc, char **argv) { Config cfg = {0}; + cfg.alert_persist = ALERT_PERSIST; /* Pre-strip long-form flags before getopt. Stop at "--". */ int new_argc = 0; @@ -399,6 +448,10 @@ new_argv[new_argc++] = argv[i]; continue; } + if (!past_dd && !strcmp(argv[i], "--no-persist")) { + cfg.alert_persist = 0; + continue; + } new_argv[new_argc++] = argv[i]; } new_argv[new_argc] = NULL;