foxygit / screensaver Log in
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