aboutsummaryrefslogtreecommitdiff
path: root/patches/ttym-hooks-3.0.diff
blob: 245eba68d95ddf434446acd945e11b5b00870ab2 (plain) (blame)
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
diff -ruN a/ttym.c b/ttym.c
--- a/ttym.c	2026-08-25 19:07:53.181902760 +0200
+++ b/ttym.c	2026-08-25 19:07:53.185902833 +0200
@@ -13,6 +13,8 @@
 #include <string.h>
 #include <strings.h>
 #include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
 #include <termios.h>
 #include <time.h>
 #include <unistd.h>
@@ -24,6 +26,7 @@
 typedef struct {
 	int quiet;            /* -q: suppress completion bell */
 	int silent;           /* -s: no bell, no extras */
+	char comment[512];    /* passed as 3rd arg to hooks */
 	int alert_persist;    /* alert loop until keypress; --no-persist disables */
 	int flash;            /* reverse-video flash during alert loop */
 	char bar_style[32];   /* --bar STYLE */
@@ -236,6 +239,43 @@
 	fflush(stdout);
 }
 
+/* ===== hooks ===== */
+
+static int
+build_hook_path(char *buf, size_t bufsz, const char *name)
+{
+	const char *home = getenv("HOME");
+	const char *xdg  = getenv("XDG_CONFIG_HOME");
+	if (xdg && *xdg)        snprintf(buf, bufsz, "%s/ttym/hooks/%s", xdg, name);
+	else if (home && *home) snprintf(buf, bufsz, "%s/.config/ttym/hooks/%s", home, name);
+	else return -1;
+	return 0;
+}
+
+static void
+run_hook(const char *name, const char *mode, long duration_s, const char *comment)
+{
+	char path[512];
+	if (build_hook_path(path, sizeof(path), name) < 0) return;
+	if (access(path, X_OK) != 0) return;
+	char dur[32];
+	snprintf(dur, sizeof(dur), "%ld", duration_s);
+	pid_t pid = fork();
+	if (pid == 0) {
+		int devnull = open("/dev/null", O_RDWR);
+		if (devnull >= 0) {
+			dup2(devnull, 0); dup2(devnull, 1); dup2(devnull, 2);
+			if (devnull > 2) close(devnull);
+		}
+		char *argv[] = { path, (char *)mode, dur,
+		                 (char *)(comment ? comment : ""), NULL };
+		execvp(argv[0], argv);
+		_exit(127);
+	} else if (pid > 0) {
+		waitpid(pid, NULL, 0);
+	}
+}
+
 /* ===== alert loop =====
  * Bell + poll for keypress, ~1.5s per beat; 4-min safety cap.
  */
@@ -345,6 +385,7 @@
 "\n"
 "DURATION:  25m | 90s | 2h | 1h30m | 01:30:00 | 25:00\n"
 "FLAGS:\n"
+"  -c TEXT         comment, passed as 3rd arg to hooks\n"
 "  -q              no sound\n"
 "  -s              silent\n"
 "  --no-persist    skip alert-until-keypress loop\n"
@@ -353,7 +394,8 @@
 "  --              end of options\n"
 "  -h              help\n"
 "\n"
-"controls:  [space] pause/resume   [q | Ctrl+C] quit\n");
+"controls:  [space] pause/resume   [q | Ctrl+C] quit\n"
+"hooks:     ~/.config/ttym/hooks/{on_start,on_done,on_quit}  (executable)\n");
 }
 
 /* ===== run loop ===== */
@@ -393,6 +435,9 @@
 	if (out_tty)
 		fputs("\033[?25l\033[?7l", stdout);
 
+	run_hook("on_start", stopwatch ? "stopwatch" : "countdown",
+	         stopwatch ? 0 : total, cfg->comment);
+
 	long start_ms    = now_ms();
 	long pause_accum = 0;
 	long pause_start = 0;
@@ -457,6 +502,7 @@
 			printf("\r\033[32mSTOP %s\033[0m\033[K\n", el);
 		else
 			printf("STOP %s\n", el);
+		run_hook("on_quit", "stopwatch", elapsed_s, cfg->comment);
 	} else if (user_quit) {
 		char el[24];
 		fmt_clock(elapsed_s, el, sizeof(el));
@@ -464,6 +510,7 @@
 			printf("\r\033[33mQUIT at %s\033[0m\033[K\n", el);
 		else
 			printf("QUIT at %s\n", el);
+		run_hook("on_quit", "countdown", elapsed_s, cfg->comment);
 	} else {
 		char tb[24];
 		fmt_clock(total, tb, sizeof(tb));
@@ -471,6 +518,7 @@
 			printf("\r\033[32mDONE %s 100%%\033[0m\033[K\n", tb);
 		else
 			printf("DONE %s 100%%\n", tb);
+		run_hook("on_done", "countdown", total, cfg->comment);
 		if (!cfg->silent && !cfg->quiet && out_tty) {
 			fputc('\a', stdout);
 			fflush(stdout);
@@ -520,10 +568,11 @@
 	argv[nargc] = NULL;
 
 	int opt;
-	while ((opt = getopt(nargc, argv, "qsh")) != -1) {
+	while ((opt = getopt(nargc, argv, "qsc:h")) != -1) {
 		switch (opt) {
 		case 'q': cfg.quiet  = 1; break;
 		case 's': cfg.silent = 1; break;
+		case 'c': snprintf(cfg.comment, sizeof(cfg.comment), "%s", optarg); break;
 		case 'h': usage(stdout); return 0;
 		default:  usage(stderr); return 1;
 		}