aboutsummaryrefslogtreecommitdiff
path: root/patches/ttym-config-file-2.0.diff
diff options
context:
space:
mode:
Diffstat (limited to 'patches/ttym-config-file-2.0.diff')
-rw-r--r--patches/ttym-config-file-2.0.diff237
1 files changed, 237 insertions, 0 deletions
diff --git a/patches/ttym-config-file-2.0.diff b/patches/ttym-config-file-2.0.diff
new file mode 100644
index 0000000..422d724
--- /dev/null
+++ b/patches/ttym-config-file-2.0.diff
@@ -0,0 +1,237 @@
+diff -ruN a/config.def.h b/config.def.h
+--- a/config.def.h 2026-05-20 13:18:06.149571116 +0200
++++ b/config.def.h 2026-05-20 13:19:12.046956614 +0200
+@@ -1,23 +1,29 @@
+ /* See LICENSE file for copyright and license details. */
+
+-/* progress bar glyphs (ASCII default) */
++/* progress bar glyphs (ASCII default).
++ * Runtime config file ~/.config/ttym/config can override these via
++ * bar_fill / bar_empty keys.
++ */
+ 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.
++ * Override per-run with --no-persist, or via the alert_persist key
++ * in the runtime config file (~/.config/ttym/config).
+ */
+ static const int ALERT_PERSIST = 1;
+
+ /* reverse-video flash during the alert loop.
+- * Override per-run with --flash on|off.
++ * Override per-run with --flash on|off, or via the flash key in the
++ * runtime config file (~/.config/ttym/config).
+ */
+ static const int FLASH = 1;
+
+ /* desktop notification on countdown completion.
+ * Tokens are split on whitespace; placeholders {title} and {msg} are expanded.
+ * Set NULL or "" to disable.
++ * The runtime config file can override these via title / notify_cmd keys.
+ */
+ static const char *const TITLE = "Timer";
+ static const char *const NOTIFY_CMD = "notify-send -u critical {title} {msg}";
+@@ -25,6 +31,7 @@
+ /* sound on countdown completion. If SOUND_CMD is "" the binary autodetects
+ * a player (paplay/aplay/mpv/ffplay) and plays a default freedesktop sound.
+ * SOUND_CMD is split on whitespace; same token rules as NOTIFY_CMD.
++ * The runtime config file can override this via the sound_cmd key.
+ */
+ static const char *const SOUND_CMD = "";
+
+@@ -35,5 +42,6 @@
+
+ /* default bar style: "" autodetects (unicode if locale is UTF-8, else ascii).
+ * Available: unicode | ascii | hash | dots | line | block | arrow | minimal.
++ * The runtime config file can override this via the bar_style key.
+ */
+ static const char *const BAR_STYLE = "";
+diff -ruN a/ttym.c b/ttym.c
+--- a/ttym.c 2026-05-20 13:18:06.149571116 +0200
++++ b/ttym.c 2026-05-20 13:19:12.046956614 +0200
+@@ -30,10 +30,12 @@
+ int alert_persist; /* alert loop until keypress; --no-persist disables */
+ int flash; /* reverse-video flash during alert loop */
+ char comment[512]; /* -c TEXT: logged with each entry */
++ char bar_fill[16]; /* override (utf-8 ok); from runtime config */
++ char bar_empty[16]; /* override */
+ char bar_style[32]; /* --bar STYLE */
+- const char *title;
+- const char *notify_cmd;
+- const char *sound_cmd;
++ char title[256]; /* override; from runtime config */
++ char notify_cmd[512]; /* override; from runtime config */
++ char sound_cmd[512]; /* override; from runtime config */
+ char msg[1024];
+ } Config;
+
+@@ -409,7 +411,7 @@
+ static void
+ notify_and_sound(const Config *c)
+ {
+- if (c->notify_cmd && c->notify_cmd[0] && command_exists("notify-send")) {
++ if (c->notify_cmd[0] && command_exists("notify-send")) {
+ char **a;
+ if (build_argv(c->notify_cmd, c, &a) > 0) {
+ spawn_bg(a);
+@@ -417,7 +419,7 @@
+ }
+ }
+ if (!c->quiet) {
+- if (c->sound_cmd && c->sound_cmd[0]) {
++ if (c->sound_cmd[0]) {
+ char **a;
+ if (build_argv(c->sound_cmd, c, &a) > 0) {
+ spawn_bg(a);
+@@ -832,6 +834,112 @@
+ }
+ }
+
++/* ===== runtime config file (~/.config/ttym/config) =====
++ * key = value lines; '#' comments. Unknown keys ignored.
++ */
++
++static void
++trim(char *s)
++{
++ char *p = s;
++ while (*p && isspace((unsigned char)*p)) p++;
++ if (p != s) memmove(s, p, strlen(p) + 1);
++ size_t n = strlen(s);
++ while (n > 0 && isspace((unsigned char)s[n-1])) s[--n] = '\0';
++}
++
++static void
++ensure_default_config_file(const char *path)
++{
++ if (access(path, F_OK) == 0) return;
++ char dir[512];
++ snprintf(dir, sizeof(dir), "%s", path);
++ char *slash = strrchr(dir, '/');
++ if (slash) { *slash = '\0'; mkdir_p(dir, 0700); }
++ FILE *f = fopen(path, "w");
++ if (!f) return;
++ fputs(
++"# ttym configuration file\n"
++"#\n"
++"# Location : ~/.config/ttym/config (/etc/ttym.conf is read first, if present)\n"
++"# Syntax : key = value -- one entry per line; '#' begins a comment.\n"
++"# A commented-out or absent key keeps its built-in default.\n"
++"# Command-line flags override anything set in this file.\n"
++"#\n"
++"# Every key below is recognised by this build. Uncomment and edit to taste.\n"
++"# ---------------------------------------------------------------------------\n"
++"\n"
++"# Progress-bar glyphs. Set BOTH or NEITHER -- mismatched display widths\n"
++"# break bar alignment. Multi-byte UTF-8 is fine. These override bar_style.\n"
++"# bar_fill = >\n"
++"# bar_empty = -\n"
++"\n"
++"# Named progress-bar style. One of:\n"
++"# unicode ascii hash dots line block arrow minimal\n"
++"# Leave unset to auto-detect (unicode on a UTF-8 locale, otherwise ascii).\n"
++"# bar_style = unicode\n"
++"\n"
++"# Reverse-video flash during the completion alert. on | off\n"
++"# flash = on\n"
++"\n"
++"# Persistent alert: keep ringing the bell until a key is pressed\n"
++"# (foreground runs only). on | off\n"
++"# alert_persist = on\n"
++"\n"
++"# Desktop-notification title.\n"
++"# title = Timer\n"
++"\n"
++"# Desktop-notification command run when a countdown finishes. Tokens are\n"
++"# split on whitespace; {title}/{msg} placeholders expand. Empty disables.\n"
++"# notify_cmd = notify-send -u critical {title} {msg}\n"
++"\n"
++"# Sound played when a countdown finishes. Leave unset to auto-detect a\n"
++"# player (paplay/aplay/mpv/ffplay) and play a default system sound.\n"
++"# Tokens are split on whitespace; {title}/{msg} placeholders expand.\n"
++"# sound_cmd = paplay /usr/share/sounds/freedesktop/stereo/complete.oga\n",
++ f);
++ fclose(f);
++ chmod(path, 0600);
++}
++
++static void
++load_config_file(Config *c, const char *path)
++{
++ FILE *f = fopen(path, "r");
++ if (!f) return;
++ char line[1024];
++ while (fgets(line, sizeof(line), f)) {
++ char *hash = strchr(line, '#');
++ if (hash) *hash = '\0';
++ char *eq = strchr(line, '=');
++ if (!eq) continue;
++ *eq = '\0';
++ char *k = line, *v = eq + 1;
++ trim(k); trim(v);
++ if (!*k) continue;
++ if (!strcmp(k, "bar_fill")) snprintf(c->bar_fill, sizeof(c->bar_fill), "%s", v);
++ else if (!strcmp(k, "bar_empty")) snprintf(c->bar_empty, sizeof(c->bar_empty), "%s", v);
++ else if (!strcmp(k, "bar_style")) snprintf(c->bar_style, sizeof(c->bar_style), "%s", v);
++ else if (!strcmp(k, "flash")) c->flash = parse_bool_str(v);
++ else if (!strcmp(k, "alert_persist")) c->alert_persist = parse_bool_str(v);
++ else if (!strcmp(k, "title")) snprintf(c->title, sizeof(c->title), "%s", v);
++ else if (!strcmp(k, "notify_cmd")) snprintf(c->notify_cmd, sizeof(c->notify_cmd), "%s", v);
++ else if (!strcmp(k, "sound_cmd")) snprintf(c->sound_cmd, sizeof(c->sound_cmd), "%s", v);
++ }
++ fclose(f);
++}
++
++static void
++load_config(Config *c)
++{
++ load_config_file(c, "/etc/ttym.conf");
++ char path[512];
++ if (build_config_path(path, sizeof(path), "config") == 0) {
++ ensure_default_config_file(path);
++ load_config_file(c, path);
++ }
++}
++
+ /* ===== bar styles ===== */
+
+ typedef struct { const char *name, *fill, *empty; } BarStyle;
+@@ -908,7 +1016,8 @@
+ " -h help\n"
+ "\n"
+ "controls: [space] pause/resume [q | Ctrl+C] quit\n"
+-"hooks: ~/.config/ttym/hooks/{on_start,on_done,on_quit} (executable)\n");
++"hooks: ~/.config/ttym/hooks/{on_start,on_done,on_quit} (executable)\n"
++"config: ~/.config/ttym/config (auto-generated on first run)\n");
+ }
+
+ /* ===== run loop ===== */
+@@ -924,6 +1033,10 @@
+ const char *fill_glyph, *empty_glyph;
+ int minimal = 0;
+ resolve_bar(cfg, &fill_glyph, &empty_glyph, &minimal);
++ /* explicit per-glyph overrides from the runtime config file
++ * (bar_fill / bar_empty) win over the named --bar style. */
++ if (cfg->bar_fill[0]) fill_glyph = cfg->bar_fill;
++ if (cfg->bar_empty[0]) empty_glyph = cfg->bar_empty;
+
+ ttyfd = open("/dev/tty", O_RDONLY);
+ if (ttyfd >= 0 && isatty(ttyfd)) {
+@@ -1075,10 +1188,11 @@
+ Config cfg = {0};
+ cfg.alert_persist = ALERT_PERSIST;
+ cfg.flash = FLASH;
+- cfg.title = TITLE;
+- cfg.notify_cmd = NOTIFY_CMD;
+- cfg.sound_cmd = SOUND_CMD;
++ snprintf(cfg.title, sizeof(cfg.title), "%s", TITLE ? TITLE : "");
++ snprintf(cfg.notify_cmd, sizeof(cfg.notify_cmd), "%s", NOTIFY_CMD ? NOTIFY_CMD : "");
++ snprintf(cfg.sound_cmd, sizeof(cfg.sound_cmd), "%s", SOUND_CMD ? SOUND_CMD : "");
+ snprintf(cfg.msg, sizeof(cfg.msg), "%s", "Time is up");
++ load_config(&cfg);
+
+ /* Pre-strip long-form flags before getopt. Stop at "--". */
+ int new_argc = 0;