commit 098cbd6ac34a1eec13ba5cb077b467ba19644372
Author: MrJensK <jens.se@icloud.com>
AuthorDate: Sat Aug 8 16:02:09 2026 +0200
Commit: MrJensK <jens.se@icloud.com>
CommitDate: Sat Aug 8 16:02:09 2026 +0200
new
---
README.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
screensaver | 38 +++++++++++++++++++++++++++++
screensaver-launch | 61 +++++++++++++++++++++++++++++++++++++++++++++++
screensaver.txt | 5 ++++
screensaverd | 38 +++++++++++++++++++++++++++++
5 files changed, 212 insertions(+)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..20c57cd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+# screensaver
+
+En egen variant av [Omarchy](https://github.com/basecamp/omarchy)s terminal-screensaver
+(samma idé: [tte](https://github.com/ChrisBuilds/terminaltexteffects) animerar en
+ASCII-konstfil i en fullskärms-terminal), anpassad för X11/[sxwm](https://github.com/uint23/sxwm)
+istället för Hyprland.
+
+## Filer
+
+- `screensaver.txt` – ASCII-konsten/loggan som animeras. Byt ut mot vad du vill
+ (vilken textfil som helst funkar, tte bryr sig inte om innehållet).
+- `screensaver` – körs inuti terminalfönstret: loopar tte med slumpmässig effekt,
+ avslutar sig själv vid tangenttryck eller om fönstret tappar fokus.
+- `screensaver-launch` – hittar alla anslutna skärmar (`xrandr`) och öppnar ett
+ fullskärms-kitty-fönster med `screensaver` på var och en.
+- `screensaverd` – bevakar inaktivitet via `xssstate` och startar/dödar
+ screensavern automatiskt.
+
+## Installation
+
+Kräver `tte`, `xdotool`, `wmctrl`, `xssstate` (paketet `x11-utils` eller
+`suckless-tools` beroende på distro) och `kitty`.
+
+```sh
+pipx install terminaltexteffects # installerar `tte` i din användarmiljö, ingen sudo
+```
+
+## Använda manuellt
+
+```sh
+./screensaver-launch # starta screensavern direkt, på alla skärmar
+```
+
+Avsluta genom att röra musen eller trycka en tangent.
+
+## Automatisk aktivering vid inaktivitet
+
+Filerna installeras i `~/.local/bin` (som ligger i `PATH`, se `~/.xinitrc`).
+Starta `screensaverd` när sxwm startar genom att lägga till en rad i
+`~/.config/sxwmrc`:
+
+```
+exec : "screensaverd"
+```
+
+Vill du justera tiden till skärmsläckaren (standard 150 sekunder) utan att
+ändra scriptet, gör en liten wrapper eftersom `exec` inte går genom en shell
+och därmed inte kan sätta miljövariabler direkt:
+
+```sh
+# ~/.local/bin/screensaverd-300 (chmod +x)
+#!/bin/sh
+SCREENSAVER_IDLE_SECONDS=300 exec "$HOME/.local/bin/screensaverd"
+```
+
+```
+exec : "screensaverd-300"
+```
+
+Efter ändring i `sxwmrc`: logga ut och in igen (eller kör
+`call : mod + r : reload_config` — observera att `reload_config` läser om
+bindningar/inställningar men `exec`-raderna körs bara vid sxwm-start).
+
+## Byta ASCII-konst
+
+Peka `screensaver` mot en annan fil genom att sätta miljövariabeln innan start:
+
+```sh
+SCREENSAVER_ART=~/min-logga.txt ./screensaver-launch
+```
diff --git a/screensaver b/screensaver
new file mode 100755
index 0000000..e8ae213
--- /dev/null
+++ b/screensaver
@@ -0,0 +1,38 @@
+#!/bin/bash
+# Runs random TTE (terminal text effects) animations in a loop.
+# Meant to be launched inside a fullscreen terminal by screensaver-launch.
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+ART_FILE="${SCREENSAVER_ART:-$SCRIPT_DIR/screensaver.txt}"
+WIN_CLASS="screensaver"
+
+screensaver_in_focus() {
+ local id
+ id=$(xdotool getactivewindow 2>/dev/null) || return 1
+ xprop -id "$id" WM_CLASS 2>/dev/null | grep -q "\"$WIN_CLASS\""
+}
+
+exit_screensaver() {
+ tput cnorm 2>/dev/null
+ pkill -x tte 2>/dev/null
+ exit 0
+}
+
+trap exit_screensaver SIGINT SIGTERM SIGHUP SIGQUIT
+
+printf '\033]11;rgb:00/00/00\007' # black background
+tput civis 2>/dev/null # hide text cursor
+
+tty=$(tty 2>/dev/null)
+
+while true; do
+ tte -i "$ART_FILE" \
+ --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
+ if read -n1 -t 1 || ! screensaver_in_focus; then
+ exit_screensaver
+ fi
+ done
+done
diff --git a/screensaver-launch b/screensaver-launch
new file mode 100755
index 0000000..7c4e5b4
--- /dev/null
+++ b/screensaver-launch
@@ -0,0 +1,61 @@
+#!/bin/bash
+# Launches the screensaver fullscreen on every connected monitor (X11 / kitty).
+set -u
+
+export PATH="$HOME/.local/bin:$PATH"
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+WIN_CLASS="screensaver"
+
+command -v tte >/dev/null || { echo "tte not found (pipx install terminaltexteffects)" >&2; exit 1; }
+
+# Already running? Don't stack another instance.
+if xdotool search --class "^${WIN_CLASS}\$" >/dev/null 2>&1; then
+ exit 0
+fi
+
+for mon in $(xrandr --listmonitors | awk 'NR>1 {print $3}'); do
+ # mon looks like 3440/798x1440/334+0+0
+ geom="${mon%%+*}" # 3440/798x1440/334
+ offsets="+${mon#*+}" # +0+0
+ w="${geom%%/*}"
+ h_part="${geom#*x}"
+ h="${h_part%%/*}"
+ x="$(echo "$offsets" | cut -d+ -f2)"
+ y="$(echo "$offsets" | cut -d+ -f3)"
+
+ kitty --class="$WIN_CLASS" \
+ --override font_size=18 \
+ --override window_padding_width=0 \
+ --override background_opacity=1 \
+ -e "$SCRIPT_DIR/screensaver" &
+ kpid=$!
+
+ # Wait for the window owned by this kitty process to appear, then pin it
+ # to this monitor and fullscreen it.
+ newwin=""
+ for _ in $(seq 1 50); do
+ for win in $(xdotool search --class "^${WIN_CLASS}\$" 2>/dev/null); do
+ if [[ "$(xdotool getwindowpid "$win" 2>/dev/null)" == "$kpid" ]]; then
+ newwin="$win"
+ break 2
+ fi
+ done
+ sleep 0.1
+ done
+
+ if [[ -n "$newwin" ]]; then
+ # The window may not be fully mapped/reparented yet right after it
+ # appears in `xdotool search`, so retry the placement a few times to
+ # ride out the occasional BadWindow race with the window manager.
+ for attempt in 1 2 3; do
+ xdotool windowactivate --sync "$newwin" 2>/dev/null
+ if xdotool windowmove "$newwin" "$x" "$y" 2>/dev/null \
+ && xdotool windowsize "$newwin" "$w" "$h" 2>/dev/null \
+ && wmctrl -ir "$newwin" -b add,fullscreen 2>/dev/null; then
+ break
+ fi
+ sleep 0.2
+ done
+ fi
+done
diff --git a/screensaver.txt b/screensaver.txt
new file mode 100644
index 0000000..2e96315
--- /dev/null
+++ b/screensaver.txt
@@ -0,0 +1,5 @@
+·▄▄▄ ▐▄• ▄ ▄· ▄▌ ·▄▄▄▄ ▄▄▄ ..▄▄ · ▄ •▄ ▄▄▄▄▄ ▄▄▄·
+█ · ▄█▀▄ █▌█▌▪▐█▪██▌ ██· ██ ▀▄.▀·▐█ ▀. █▌▄▌▪•██ ▄█▀▄ ▐█ ▄█
+█▀▀▪▐█▌.▐▌ ·██· ▐█▌▐█▪ ▐█▪ ▐█▌▐▀▀▪▄▄▀▀▀█▄▐▀▀▄· ▐█.▪▐█▌.▐▌ ██▀·
+██ .▐█▌.▐▌▪▐█·█▌ ▐█▀·. ██. ██ ▐█▄▄▌▐█▄▪▐█▐█.█▌ ▐█▌·▐█▌.▐▌▐█▪·•
+▀▀▀ ▀█▄▀▪•▀▀ ▀▀ ▀ • ▀▀▀▀▀• ▀▀▀ ▀▀▀▀ ·▀ ▀ ▀▀▀ ▀█▄▀▪.▀
diff --git a/screensaverd b/screensaverd
new file mode 100755
index 0000000..0d20a4e
--- /dev/null
+++ b/screensaverd
@@ -0,0 +1,38 @@
+#!/bin/bash
+# Idle watcher: launches the screensaver after IDLE_SECONDS of inactivity
+# and kills it the moment real activity resumes.
+set -u
+
+# sxwm's `exec` runs commands via execvp with whatever PATH the X session
+# started with, which may not include a pipx-installed `tte`. Make sure it's
+# found regardless.
+export PATH="$HOME/.local/bin:$PATH"
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+IDLE_SECONDS="${SCREENSAVER_IDLE_SECONDS:-150}"
+WIN_CLASS="screensaver"
+
+is_running() {
+ xdotool search --class "^${WIN_CLASS}\$" >/dev/null 2>&1
+}
+
+stop_screensaver() {
+ pkill -f "$SCRIPT_DIR/screensaver\$" 2>/dev/null
+ pkill -x tte 2>/dev/null
+ for win in $(xdotool search --class "^${WIN_CLASS}\$" 2>/dev/null); do
+ xdotool windowkill "$win" 2>/dev/null
+ done
+}
+
+while true; do
+ idle_ms=$(xssstate -i)
+ idle_s=$((idle_ms / 1000))
+
+ if (( idle_s >= IDLE_SECONDS )) && ! is_running; then
+ "$SCRIPT_DIR/screensaver-launch"
+ elif (( idle_s < 1 )) && is_running; then
+ stop_screensaver
+ fi
+
+ sleep 1
+done