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" section "Relevant processes"
ps -eo pid=,args= \ 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 | grep -v grep || true
section "Sleep lock service" 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. # 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 exit 1
fi 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 # 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 # 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 # 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 # 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. # startup wait, which can only ever guess when the console has stopped moving.
_greeter_draw() { _greeter_draw() {
@@ -464,7 +464,7 @@ greeter_screen() {
# ColorShift the logo: a green base (indexed color 2) with a cyan accent (6) # 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 # 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 # 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 # Night palette and render true. One long-running invocation (many cycles) so
# it never restarts — a restart is what flashed. The effect reads from # 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 # --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 # the logo to repaint exactly the rows drawn above. --canvas-width is cols-1
# to stay off the autowrap column. # 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))" printf '%s%d;1H\0337' "$CSI" "$((logo_row + LOGO_HEIGHT))"
# Run tte directly (not inside a `while` subshell) so $anim is tte's own # Run ttfx directly (not inside a `while` subshell) so $anim is ttfx's own
# PID: killing a wrapping subshell would orphan tte, which then keeps # PID: killing a wrapping subshell would orphan ttfx, which then keeps
# painting the logo over the keyboard step. --cycles is high enough that it # painting the logo over the keyboard step. --cycles is high enough that it
# never ends on its own before Return. # never ends on its own before Return.
tte -i "$LOGO_PATH" \ ttfx -i "$LOGO_PATH" \
--canvas-width "$((cols - 1))" \ --canvas-width "$((cols - 1))" \
--anchor-text c \ --anchor-text c \
--frame-rate 60 \ --frame-rate 60 \
@@ -495,8 +495,8 @@ greeter_screen() {
_greeter_kill_anim() { _greeter_kill_anim() {
[[ -n ${anim:-} ]] || return 0 [[ -n ${anim:-} ]] || return 0
# Guard both: `kill` returns non-zero if tte already exited (crash, or a # Guard both: `kill` returns non-zero if ttfx already exited (crash, or a
# resize race), and `wait` reports tte's kill signal (143) — either would # resize race), and `wait` reports ttfx's kill signal (143) — either would
# abort provisioning under `set -e` and drop straight to the login screen. # abort provisioning under `set -e` and drop straight to the login screen.
kill "$anim" 2>/dev/null || true kill "$anim" 2>/dev/null || true
wait "$anim" 2>/dev/null || true wait "$anim" 2>/dev/null || true
@@ -537,7 +537,7 @@ greeter_screen() {
trap - WINCH trap - WINCH
_greeter_kill_anim _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 # prompts in the keyboard step that follows silently die. Then clear the
# leftover animation frame. # leftover animation frame.
stty sane </dev/tty 2>/dev/null || true stty sane </dev/tty 2>/dev/null || true
+3 -3
View File
@@ -8,7 +8,7 @@ screensaver_in_focus() {
exit_screensaver() { exit_screensaver() {
hyprctl eval 'hl.config({ cursor = { invisible = false } })' &>/dev/null || hyprctl keyword cursor:invisible false &>/dev/null || true 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 pkill -f '[o]rg.omarchy.screensaver' 2>/dev/null
exit 0 exit 0
} }
@@ -23,11 +23,11 @@ hyprctl eval 'hl.config({ cursor = { invisible = true } })' &>/dev/null || hyprc
tty=$(tty 2>/dev/null) tty=$(tty 2>/dev/null)
while true; do 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\ --frame-rate 120 --canvas-width 0 --canvas-height 0 --reuse-canvas --anchor-canvas c --anchor-text c\
--random-effect --no-eol --no-restore-cursor & --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 if read -n1 -t 1 || ! screensaver_in_focus; then
exit_screensaver exit_screensaver
fi fi
+1 -1
View File
@@ -104,7 +104,7 @@ postgresql-libs
power-profiles-daemon power-profiles-daemon
python-gobject python-gobject
python-poetry-core python-poetry-core
python-terminaltexteffects ttfx
qemu-user-static-binfmt qemu-user-static-binfmt
qrencode qrencode
quickshell-git quickshell-git
+4
View File
@@ -0,0 +1,4 @@
echo "Replace terminaltexteffects with ttfx"
omarchy-pkg-add ttfx
omarchy-pkg-drop python-terminaltexteffects