diff --git a/bin/blob-launch-screensaver b/bin/blob-launch-screensaver index 51a03f1..65f4408 100755 --- a/bin/blob-launch-screensaver +++ b/bin/blob-launch-screensaver @@ -2,7 +2,8 @@ # blob:summary=Launch the Blob screensaver in the default terminal on the system with the correct font configuration. -if blob-cmd-missing ttfx; then +if ! blob-screensaver-effect >/dev/null; then + blob-notify-send -g ✋ "Screensaver needs ttfx or tte" "Install python-terminaltexteffects." exit 1 fi diff --git a/bin/blob-screensaver b/bin/blob-screensaver index 3349511..a1273e8 100755 --- a/bin/blob-screensaver +++ b/bin/blob-screensaver @@ -2,13 +2,37 @@ # blob:summary=Run the Blob screensaver using random effects from TTE. +effect_command=$(blob-screensaver-effect) || exit 1 + +effect_args() { + local input="$HOME/.config/blob/branding/screensaver.txt" + + if [[ $effect_command == "ttfx" ]]; then + printf '%s\n' -i "$input" --frame-rate 120 --canvas-width 0 --canvas-height 0 \ + --reuse-canvas --anchor-canvas c --anchor-text c --random-effect --no-eol \ + --no-restore-cursor + return 0 + fi + + local effects=( + beams binarypath blackhole bouncyballs bubbles burn colorshift crumble + decrypt errorcorrect expand fireworks middleout orbittingvolley overflow + pour print rain randomsequence rings scattered slice slide spotlights + spray swarm sweep synthgrid unstable vhstape waves wipe + ) + + printf '%s\n' -i "$input" --frame-rate 120 --canvas-width 0 --canvas-height 0 \ + --anchor-canvas c --anchor-text c --no-color \ + "${effects[RANDOM % ${#effects[@]}]}" +} + screensaver_in_focus() { hyprctl activewindow -j | jq -e '.class == "org.blob.screensaver"' >/dev/null 2>&1 } exit_screensaver() { hyprctl eval 'hl.config({ cursor = { invisible = false } })' &>/dev/null || hyprctl keyword cursor:invisible false &>/dev/null || true - pkill -x ttfx 2>/dev/null + pkill -x "$effect_command" 2>/dev/null pkill -f '[o]rg.blob.screensaver' 2>/dev/null exit 0 } @@ -36,11 +60,10 @@ wait_for_terminal_resize() { wait_for_terminal_resize while true; do - ttfx -i ~/.config/blob/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 & + mapfile -t effect_arguments < <(effect_args) + "$effect_command" "${effect_arguments[@]}" & - while pgrep -t "${tty#/dev/}" -x ttfx >/dev/null; do + while pgrep -t "${tty#/dev/}" -x "$effect_command" >/dev/null; do if read -n1 -t 1 || ! screensaver_in_focus; then exit_screensaver fi diff --git a/bin/blob-screensaver-effect b/bin/blob-screensaver-effect new file mode 100755 index 0000000..73f2937 --- /dev/null +++ b/bin/blob-screensaver-effect @@ -0,0 +1,13 @@ +#!/bin/bash + +# blob:summary=Print the terminal text effects command the screensaver can use +# blob:hidden=true + +for candidate in ttfx tte; do + if command -v "$candidate" &>/dev/null; then + printf '%s\n' "$candidate" + exit 0 + fi +done + +exit 1 diff --git a/bin/blob-system-lock b/bin/blob-system-lock index c271f63..866e84b 100755 --- a/bin/blob-system-lock +++ b/bin/blob-system-lock @@ -20,7 +20,8 @@ if pgrep -x "1password" >/dev/null && blob-cmd-present 1password; then fi # Avoid running screensaver when locked -pkill -x ttfx 2>/dev/null || true -# ttfx handles SIGTERM asynchronously, so keep its terminal alive until it exits. -timeout 1s pidwait -x ttfx 2>/dev/null || true +effect_command=$(blob-screensaver-effect 2>/dev/null) && { + pkill -x "$effect_command" 2>/dev/null || true + timeout 1s pidwait -x "$effect_command" 2>/dev/null || true +} pkill -f '[o]rg.blob.screensaver' 2>/dev/null || true diff --git a/default/pam.d/blob-lock-password b/default/pam.d/blob-lock-password new file mode 100644 index 0000000..c8c39f6 --- /dev/null +++ b/default/pam.d/blob-lock-password @@ -0,0 +1,2 @@ +#%PAM-1.0 +auth include system-auth diff --git a/docs/install.md b/docs/install.md index 2533294..263f6b5 100644 --- a/docs/install.md +++ b/docs/install.md @@ -97,6 +97,7 @@ with `--check`. | `/etc/sddm.conf.d/zz-blob.conf` | Wayland greeter, run on Hyprland, stock theme, remember the last session | | `/etc/sddm/hyprland-greeter.lua` | the greeter's own tiny Hyprland config | | `/usr/local/bin/blob-hw-gpu-accelerated` | the renderer check that config runs, where SDDM's user can read it | + | `/etc/pam.d/blob-lock-password` | the PAM stack the lock screen authenticates against | The greeter config matters more than it looks: SDDM's stock Wayland greeter runs on `weston`, which Blob does not install, so a default SDDM would fail @@ -145,6 +146,18 @@ with `--check`. | `--autologin` | skip the SDDM prompt for this user | | `--no-launch` | leave the login screen for the next reboot | +## The lock screen + +The lock plugin watches `/etc/pam.d/blob-lock-password` and refuses to lock at +all when it is not there - `blob system lock` returns `missing-pam` and nothing +appears, which reads as a keybinding that does nothing. The installer writes it; +it is one line including `system-auth`, the same stack every other Wayland +locker on Arch authenticates against. + +Fingerprint unlock is separate and opt-in: `blob setup-security-fingerprint` +writes `/etc/pam.d/blob-lock-fingerprint`, and the lock only offers fingerprint +when that file and a reader are both present. + ## Choosing a renderer Hyprland renders through Mesa, and on an adapter Mesa has no driver for it exits diff --git a/docs/packages.md b/docs/packages.md index 1a5bc02..5e3514b 100644 --- a/docs/packages.md +++ b/docs/packages.md @@ -70,7 +70,7 @@ Two are worth knowing about: ``` packages/blob.packages 121 packages from core, extra, multilib -packages/blob-aur.packages 10 packages with no official equivalent +packages/blob-aur.packages 11 packages with no official equivalent ``` Only missing packages are touched, through `pacman -S --needed` and @@ -127,6 +127,7 @@ Omarchy's own package list, because they were installed by hand on this machine: | Package | Needed by | | --- | --- | | `python-pywal16` | `blob-wallpaper-set`, palette extraction | +| `python-terminaltexteffects` | `blob-screensaver`, the effects it draws | | `awww` | the wallpaper daemon `blob-wallpaper-set` and `blob-theme-menu` drive | | `playerctl` | the quick settings media row | diff --git a/install/steps/login.sh b/install/steps/login.sh index fa299fb..b3d6106 100644 --- a/install/steps/login.sh +++ b/install/steps/login.sh @@ -21,6 +21,8 @@ install_login_config() { "$blob_greeter_conf" "sddm.conf.d/zz-blob.conf" install_root_file "$repo_dir/bin/blob-hw-gpu-accelerated" \ "$greeter_gpu_detector" "blob-hw-gpu-accelerated" 0755 + install_root_file "$repo_dir/default/pam.d/blob-lock-password" \ + /etc/pam.d/blob-lock-password "pam.d/blob-lock-password" } install_session_entry() { diff --git a/packages/blob-aur.packages b/packages/blob-aur.packages index 80aedab..6ddd777 100644 --- a/packages/blob-aur.packages +++ b/packages/blob-aur.packages @@ -6,6 +6,9 @@ # Palette extraction behind blob-wallpaper-set; pywal16 is the maintained fork python-pywal16|python-pywal +# Terminal text effects behind the screensaver; ttfx was the Omarchy build +python-terminaltexteffects + # Terminal dispatch; every TUI launcher's Exec= line calls it xdg-terminal-exec diff --git a/uninstall.sh b/uninstall.sh index e536323..9fc0682 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -9,6 +9,7 @@ session_entry="/usr/local/share/wayland-sessions/blob.desktop" root_files=( /etc/profile.d/blob.sh /usr/local/bin/blob-hw-gpu-accelerated + /etc/pam.d/blob-lock-password /etc/sddm.conf.d/zz-blob.conf /etc/sddm.conf.d/zzz-blob-autologin.conf /etc/sddm/hyprland-greeter.conf