Add a settings screen so nothing needs a text editor

All eighteen options are now reachable from the menu as a cursor list: arrows
select, Space or Right cycles a value, q goes back, changes are written
immediately. A numbered menu would have run out of digits.

Three real bugs surfaced while building it, each found by testing against an
actual pty rather than a pipe:

- Labels went through printf as format strings, so the percent sign in
  "Minimum battery level (%)" was an invalid conversion. cau_msg_in now only
  treats a message as a format string when arguments were actually passed -
  otherwise any literal % a translator writes is a trap.

- Mixing bash's line-mode read into a single-key interface left the following
  read -sn1 receiving nothing at all, reproducibly, so the screen froze after
  editing the package list. Replaced with a small line editor built on the same
  single-character reader.

- Backspace was being swallowed: in canonical mode DEL is the ERASE character
  and the line discipline consumes it, and bash returns to canonical mode
  between each read -sn1. The interface now holds non-canonical mode for its
  whole lifetime and hands the terminal back only around actions that print or
  prompt, with a trap restoring it on Ctrl-C.

Translations and config reads are memoized; the screen redraws every label on
every keypress and a fork per lookup was the reason the redraw was slow enough
to matter.
This commit is contained in:
Felitendo committed 2026-08-08 16:18:59 +02:00
1 parent 2eff62f9fd
commit c308b7e286
8 files changed
+491 -26

No files matched your search

+23 -3
View File
@@ -6,14 +6,33 @@
# root-run daemon, and sourcing it would turn a stray line into arbitrary code
# execution. The format is one "Key=Value" per line, '#' starts a comment.
# The file is cached and parsed in-process rather than shelled out to sed on
# every lookup. The settings screen reads every key on every redraw, and a fork
# per key made the redraw slow enough to swallow keystrokes.
CAU_CONFIG_CACHE=''
CAU_CONFIG_CACHED=0
_cau_config_slurp() {
(( CAU_CONFIG_CACHED )) && return 0
CAU_CONFIG_CACHE=''
[[ -r $CAU_CONFIG ]] && CAU_CONFIG_CACHE="$(< "$CAU_CONFIG")"
CAU_CONFIG_CACHED=1
return 0
}
# cau_config_get <Key> [default]
cau_config_get() {
local key="$1" default="${2:-}" val
local key="$1" default="${2:-}" val='' line
[[ -r $CAU_CONFIG ]] || { printf '%s\n' "$default"; return; }
_cau_config_slurp
val="$(sed -nE "s/^[[:space:]]*${key}[[:space:]]*=[[:space:]]*(.*)$/\\1/p" \
"$CAU_CONFIG" 2>/dev/null | tail -n1)"
# last assignment wins, matching the previous sed|tail behaviour
while IFS= read -r line; do
[[ $line == *"$key"* ]] || continue
[[ $line =~ ^[[:space:]]*"$key"[[:space:]]*=(.*)$ ]] || continue
val="${BASH_REMATCH[1]}"
done <<< "$CAU_CONFIG_CACHE"
# strip a trailing comment and surrounding whitespace/quotes
val="${val%%#*}"
@@ -78,6 +97,7 @@ cau_config_set() {
fi
mv -f "$tmp" "$CAU_CONFIG"
CAU_CONFIG_CACHED=0
}
# ---------------------------------------------------------------------------