Swap terminaltexteffects for ttfx (#6670)

* Swap terminaltexteffects for ttfx

ttfx is a Rust port of terminaltexteffects that renders byte-identical
frames as a single dependency-free binary. Same option names, defaults,
and exit codes, so every invocation here is unchanged apart from the
command name.

The screensaver runs at --frame-rate 120 with --random-effect. On a
fullscreen canvas Python cannot hold that for the heavier effects
(beams: 14.1 ms/frame against an 8.3 ms budget, so ~71fps); ttfx renders
the same effect at 564fps. Startup drops from ~107 ms to ~1 ms, and the
base image no longer needs Python for the screensaver.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Need a migration

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier Hansson
2026-08-10 11:59:39 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent 7644b56d4e
commit 4e31b61af0
6 changed files with 19 additions and 15 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ journalctl -t omarchy-shell -n "$lines" --no-pager --quiet 2>/dev/null || true
section "Relevant processes"
ps -eo pid=,args= \
| grep -E 'quickshell -n -p|omarchy-system-sleep-monitor|systemd-inhibit.*Lock screen before suspend|org\.omarchy\.screensaver|omarchy-screensaver|(^|/| )tte( |$)' \
| grep -E 'quickshell -n -p|omarchy-system-sleep-monitor|systemd-inhibit.*Lock screen before suspend|org\.omarchy\.screensaver|omarchy-screensaver|(^|/| )ttfx( |$)' \
| grep -v grep || true
section "Sleep lock service"
+1 -1
View File
@@ -2,7 +2,7 @@
# omarchy:summary=Launch the Omarchy screensaver in the default terminal on the system with the correct font configuration.
if omarchy-cmd-missing tte; then
if omarchy-cmd-missing ttfx; then
exit 1
fi
+9 -9
View File
@@ -412,7 +412,7 @@ greeter_screen() {
# nested function so the wait loop below can repaint it verbatim if the VT
# resizes out from under us: a late resize (virtio-gpu KMS handoff, or the SDL
# window's size arriving) scrolls the old frame into the top-left and resets
# the DEC saved cursor tte paints from — exactly the tiled/garbled logo we saw
# the DEC saved cursor ttfx paints from — exactly the tiled/garbled logo we saw
# on a fresh first boot. Redrawing on every resize beats any fixed-length
# startup wait, which can only ever guess when the console has stopped moving.
_greeter_draw() {
@@ -464,7 +464,7 @@ greeter_screen() {
# ColorShift the logo: a green base (indexed color 2) with a cyan accent (6)
# drifting through, settling on green. Indexed ANSI colors, not hex — the
# framebuffer console can't render tte's truecolor faithfully (it crushes the
# framebuffer console can't render ttfx's truecolor faithfully (it crushes the
# palette to a muddy lavender), but the 16 indexed colors map to the Tokyo
# Night palette and render true. One long-running invocation (many cycles) so
# it never restarts — a restart is what flashed. The effect reads from
@@ -472,13 +472,13 @@ greeter_screen() {
# --reuse-canvas paints upward from the saved cursor, anchored one row below
# the logo to repaint exactly the rows drawn above. --canvas-width is cols-1
# to stay off the autowrap column.
if command -v tte >/dev/null 2>&1; then
if command -v ttfx >/dev/null 2>&1; then
printf '%s%d;1H\0337' "$CSI" "$((logo_row + LOGO_HEIGHT))"
# Run tte directly (not inside a `while` subshell) so $anim is tte's own
# PID: killing a wrapping subshell would orphan tte, which then keeps
# Run ttfx directly (not inside a `while` subshell) so $anim is ttfx's own
# PID: killing a wrapping subshell would orphan ttfx, which then keeps
# painting the logo over the keyboard step. --cycles is high enough that it
# never ends on its own before Return.
tte -i "$LOGO_PATH" \
ttfx -i "$LOGO_PATH" \
--canvas-width "$((cols - 1))" \
--anchor-text c \
--frame-rate 60 \
@@ -495,8 +495,8 @@ greeter_screen() {
_greeter_kill_anim() {
[[ -n ${anim:-} ]] || return 0
# Guard both: `kill` returns non-zero if tte already exited (crash, or a
# resize race), and `wait` reports tte's kill signal (143) — either would
# Guard both: `kill` returns non-zero if ttfx already exited (crash, or a
# resize race), and `wait` reports ttfx's kill signal (143) — either would
# abort provisioning under `set -e` and drop straight to the login screen.
kill "$anim" 2>/dev/null || true
wait "$anim" 2>/dev/null || true
@@ -537,7 +537,7 @@ greeter_screen() {
trap - WINCH
_greeter_kill_anim
# tte leaves the tty in raw/no-echo mode when killed; restore it or the gum
# ttfx leaves the tty in raw/no-echo mode when killed; restore it or the gum
# prompts in the keyboard step that follows silently die. Then clear the
# leftover animation frame.
stty sane </dev/tty 2>/dev/null || true
+3 -3
View File
@@ -8,7 +8,7 @@ screensaver_in_focus() {
exit_screensaver() {
hyprctl eval 'hl.config({ cursor = { invisible = false } })' &>/dev/null || hyprctl keyword cursor:invisible false &>/dev/null || true
pkill -x tte 2>/dev/null
pkill -x ttfx 2>/dev/null
pkill -f '[o]rg.omarchy.screensaver' 2>/dev/null
exit 0
}
@@ -23,11 +23,11 @@ hyprctl eval 'hl.config({ cursor = { invisible = true } })' &>/dev/null || hyprc
tty=$(tty 2>/dev/null)
while true; do
tte -i ~/.config/omarchy/branding/screensaver.txt \
ttfx -i ~/.config/omarchy/branding/screensaver.txt \
--frame-rate 120 --canvas-width 0 --canvas-height 0 --reuse-canvas --anchor-canvas c --anchor-text c\
--random-effect --no-eol --no-restore-cursor &
while pgrep -t "${tty#/dev/}" -x tte >/dev/null; do
while pgrep -t "${tty#/dev/}" -x ttfx >/dev/null; do
if read -n1 -t 1 || ! screensaver_in_focus; then
exit_screensaver
fi
+1 -1
View File
@@ -104,7 +104,7 @@ postgresql-libs
power-profiles-daemon
python-gobject
python-poetry-core
python-terminaltexteffects
ttfx
qemu-user-static-binfmt
qrencode
quickshell-git
+4
View File
@@ -0,0 +1,4 @@
echo "Replace terminaltexteffects with ttfx"
omarchy-pkg-add ttfx
omarchy-pkg-drop python-terminaltexteffects