From 8f99f1650e4a7363af1d83190b4082fb72a9f73f Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Tue, 25 Aug 2026 19:02:35 +0200 Subject: ttym 3.0: fix terminal and config bugs, fold terminal-only patches into base Correctness fixes: - restore the terminal on SIGQUIT and SIGHUP. atexit never runs on an uncaught fatal signal, so Ctrl+\ left the cursor hidden and autowrap disabled. - drop the unconditional newline in restore_tty. Every run emitted a stray blank line, including piped output. - treat EINTR as an interruption rather than a poll() failure, which delayed Ctrl+C by up to one 100ms frame. config-file patch: - make comment stripping quote-aware so a value may contain '#', and strip one matched pair of quotes. bar_fill = "#" was silently dropped, despite '#' being the glyph of the hash style this build ships. - resolve bar_fill/bar_empty inside resolve_bar and clear them on --bar, so command-line flags beat the config file as documented. Previously config glyphs silently overrode --bar. - report unknown keys, unparseable booleans and malformed lines on stderr. flash = enabled silently meant off. - never write to disk. --dump-config prints a commented template on stdout instead of the binary creating ~/.config/ttym/config on first run. Patch set: - fold persist-alert, flash and bar-styles into the base. The base is now terminal-only: it opens /dev/tty, draws and exits, with no file writes and no subprocesses. Everything still in patches/ changes what the program touches. - seven patches become four; config-file's prerequisites drop from six patches to one (notify), so it is regenerable mechanically. - regenerate every patch. All apply with zero fuzz and compile. Source: - rewrite argv in place rather than into a malloc'd copy (C99 5.1.2.2.1), removing die(), four free() calls and . - fold the duplicated elapsed-time expression into elapsed(). - drop dead code: have_any, (void)cfg, and an unreachable branch in parse_duration. - replace hand-counted padding with \033[K. Docs and build: - timer.1: -c is the third hook argument, not the fourth. Add EXIT STATUS (a stopwatch always exits 130 -- it has no natural end) and ENVIRONMENT. Document config precedence and quoting. - wire VERSION into a dist target; it was defined and never used. - track .gitignore, which previously ignored itself, so a fresh clone had no ignore rules. Claude-Session: https://claude.ai/code/session_01APLBs8RB1FcUaC4viVkzbP --- patches/README | 109 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 54 insertions(+), 55 deletions(-) (limited to 'patches/README') diff --git a/patches/README b/patches/README index cd4e12a..51595b9 100644 --- a/patches/README +++ b/patches/README @@ -4,31 +4,48 @@ ttym patches Each patch is a unified diff against the pristine base. Apply with: cd ttym - patch -p1 < patches/ttym--2.0.diff + patch -p1 < patches/ttym--3.0.diff Then rebuild: rm -f config.h ; make To get every feature at once, skip the individual patches entirely and -apply ttym-full-2.0.diff -- one combined diff, no conflicts to resolve. +apply ttym-full-3.0.diff -- one combined diff, no conflicts to resolve. See "The full build" below. +What is in the base +------------------- + +The base build is terminal-only: it opens /dev/tty, draws, and exits. +It does not write files, spawn processes or depend on anything outside +libc. That includes the progress-bar styles, the alert loop and the +terminal flash, which were separate patches through 2.0: + + - countdown and stopwatch, pause/resume, quit + - progress bar with eight built-in styles (--bar, BAR_STYLE) + - alert-until-keypress on completion (--no-persist, ALERT_PERSIST) + - reverse-video flash during that alert (--flash, FLASH) + +Every remaining patch changes what the program *touches* -- the +filesystem, subprocesses, or external binaries. That is the line the +split is drawn on. + + The full build -------------- - ttym-full-2.0.diff - Every feature in a single patch: persist-alert, flash, notify, - logging, hooks, bar-styles and config-file, with all inter-patch - conflicts already resolved. Apply it to the pristine base and - rebuild: + ttym-full-3.0.diff + Every feature in a single patch: notify, logging, hooks and + config-file, with all inter-patch conflicts already resolved. + Apply it to the pristine base and rebuild: - patch -p1 < patches/ttym-full-2.0.diff + patch -p1 < patches/ttym-full-3.0.diff rm -f config.h ; make This is the recommended route to the full-featured build. It is - equivalent to stacking all seven feature patches below and hand- + equivalent to stacking the four feature patches below and hand- resolving every collision, but deterministic and conflict-free. Use the individual patches only when you want a specific subset. @@ -36,86 +53,68 @@ The full build Available patches ----------------- - ttym-persist-alert-2.0.diff - After a countdown completes, keep ringing the bell every ~1.5s - until any key is pressed. Foreground-only; backgrounded runs - fall through. Adds: --no-persist flag, ALERT_PERSIST knob. - - ttym-flash-2.0.diff [requires persist-alert] - Add reverse-video terminal flash to the persistent alert loop. - Adds: --flash on|off flag, FLASH knob. - - ttym-notify-2.0.diff + ttym-notify-3.0.diff One-shot desktop notification and sound on countdown completion, plus a MESSAGE positional argument piped through {title}/{msg} - placeholders. Adds: TITLE, NOTIFY_CMD, SOUND_CMD knobs. + placeholders. Spawns notify-send and a sound player. + Adds: TITLE, NOTIFY_CMD, SOUND_CMD knobs. - ttym-logging-2.0.diff + ttym-logging-3.0.diff TSV log at ~/.config/ttym/ttym.log, +tag extraction, the -r reader (-n, --raw), and --stats (today|week|month|all). Adds one-shot directory migration from ~/.config/timer/. Adds: -c TEXT flag. - ttym-hooks-2.0.diff + ttym-hooks-3.0.diff Optional executables at ~/.config/ttym/hooks/{on_start,on_done, on_quit} invoked with: mode duration_seconds comment. Adds: -c TEXT flag (passed as 3rd hook arg). - ttym-bar-styles-2.0.diff - Eight built-in bar styles (unicode, ascii, hash, dots, line, - block, arrow, minimal) with locale-aware auto-detect. - Adds: --bar STYLE flag, BAR_STYLE knob. - - ttym-config-file-2.0.diff [capstone -- see Dependencies] - Runtime config at ~/.config/ttym/config (key = value), auto- - generated and self-documenting on first run. Exposes eight keys: + ttym-config-file-3.0.diff [requires notify] + Runtime config read from /etc/ttym.conf, then + ~/.config/ttym/config (key = value). Exposes eight keys: bar_fill, bar_empty, bar_style, flash, alert_persist, title, - notify_cmd, sound_cmd. A system-wide /etc/ttym.conf is read - first. Because it wires up knobs owned by other patches, it must - be applied LAST. + notify_cmd, sound_cmd. Unknown keys and unparseable values are + reported on stderr. Nothing is written to disk: --dump-config + prints a commented template on stdout, so a user does + timer --dump-config > ~/.config/ttym/config -Dependencies ------------- + It needs notify only for the title / notify_cmd / sound_cmd + keys; the bar and alert keys it exposes now live in the base. -flash depends on persist-alert. -config-file is the capstone: it reads knobs introduced by persist- -alert, flash, notify, logging and bar-styles, and extends a usage() -line added by hooks -- so all six must be applied before it. Apply -config-file last. +Dependencies +------------ -All other patches are independent of each other and of the base. +config-file requires notify. Everything else is independent of the +base and of each other. Stacking -------- -The simplest route to the full build is ttym-full-2.0.diff (see "The +The simplest route to the full build is ttym-full-3.0.diff (see "The full build" above). The notes here apply only when stacking the feature patches by hand to assemble a subset. Every patch in this directory applies and compiles cleanly against the -pristine base -- except flash, which builds on persist-alert, and -config-file, the capstone, which builds on six other patches (see -Dependencies). That is the property the verification step guarantees. +pristine base -- except config-file, which builds on notify. Combining multiple optional patches will produce conflicts at shared regions: the Config struct, usage(), the getopt() option string, and in some cases #include lines. This is normal suckless territory -- -resolve by hand, or apply ttym-full-2.0.diff instead. The base is laid -out so most patches add their new code in fresh sections (before -usage(), before main()) where they do not collide; the inevitable -collisions are in points where every patch must add a line or field. +resolve by hand, or apply ttym-full-3.0.diff instead. Two known +collisions when stacking logging with hooks: both own the -c flag, so +keep one Config member and one usage() line for it. Recommended order when stacking: - persist-alert -> flash -> notify -> logging -> hooks -> - bar-styles -> config-file + notify -> logging -> hooks -> config-file -config-file must come last -- after bar-styles in particular, since it -overrides the resolved bar glyphs. Apply, resolve any .rej files, -rebuild. +config-file must come last -- it overrides the resolved bar glyphs and +extends usage() with lines the other patches also touch. Apply, +resolve any .rej files, rebuild. Authoring new patches @@ -129,5 +128,5 @@ Match the discipline used here: - Verify with `patch -p1` against a fresh copy of the base -- or, for a patch with prerequisites, the base plus those -- and confirm the result compiles before publishing. - - When the patch set changes, regenerate ttym-full-2.0.diff so the + - When the patch set changes, regenerate ttym-full-3.0.diff so the one-shot full build stays in sync. -- cgit v1.3