1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
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;
|