diff --git a/hypr/.config/hypr/hypridle.conf b/hypr/.config/hypr/hypridle.conf deleted file mode 100644 index ac6fd3a..0000000 --- a/hypr/.config/hypr/hypridle.conf +++ /dev/null @@ -1,33 +0,0 @@ -# hypridle.conf — screensaver only. -# Managed by dotfiles (stow hypr). -# -# IDLE OWNERSHIP: Noctalia owns dim / lock / screen-off / suspend via its own -# [idle] block in ~/.config/noctalia/config.toml. hypridle is started only for -# the Noctalia shell (see hyprland.lua) and contributes just the tte -# screensaver. Do not re-add lock/DPMS listeners here — that duplication is -# what made two idle managers fight over the same transitions. -# -# lock_cmd goes through scripts/lock.sh, which resolves the active shell from -# env.lua. That keeps a manually started hypridle under Caelestia from calling -# `noctalia ...` (which would silently do nothing). -# -# WARNING — do not add DPMS dispatchers back to this file. This Hyprland -# (hyprmod Lua runtime, 0.56.x) evaluates `hyprctl dispatch` args as Lua, and -# the dpms dispatcher takes a TABLE, not a boolean: -# WRONG (silently becomes a TOGGLE, ignoring the argument): -# hyprctl dispatch 'hl.dsp.dpms(true)' -# RIGHT: -# hyprctl dispatch 'hl.dsp.dpms({action="enable"})' -# A toggle in after_sleep_cmd turns the display OFF on resume, and with -# misc:key_press_enables_dpms = false nothing could wake it again. - -general { - lock_cmd = ~/.config/hypr/scripts/lock.sh -} - -# Screensaver via backend wrappers (switch backend in modules/screensaver.lua). -listener { - timeout = 300 - on-timeout = ~/.config/hypr/scripts/screensaver-start.sh - on-resume = ~/.config/hypr/scripts/screensaver-stop.sh -} diff --git a/hypr/.config/hypr/hyprland.lua b/hypr/.config/hypr/hyprland.lua index b05ca54..4fe34b5 100644 --- a/hypr/.config/hypr/hyprland.lua +++ b/hypr/.config/hypr/hyprland.lua @@ -40,11 +40,6 @@ hl.on("hyprland.start", function() else -- default: "noctalia" hl.exec_cmd("noctalia &") end - -- activate hypridle when not using Caelestia (has its own idle-ing mechnism) - if env.shell ~= "caelestia" then - hl.exec_cmd("hypridle &") - end - hl.exec_cmd("sh ~/.config/hypr/scripts/portal-host-override.sh") hl.exec_cmd("sh ~/.config/hypr/scripts/ensure-mime-defaults.sh") end) @@ -74,4 +69,3 @@ if fid_gui2 then fid_gui2:close() require("hyprland-gui") end - diff --git a/hypr/.config/hypr/hyprsaver.toml b/hypr/.config/hypr/hyprsaver.toml deleted file mode 100644 index fbadbfa..0000000 --- a/hypr/.config/hypr/hyprsaver.toml +++ /dev/null @@ -1,40 +0,0 @@ -# hyprsaver.toml — chill playlist: calm shaders + cool palettes, cycling. -# Location: ~/.config/hypr/hyprsaver.toml (stowed from dotfiles). -# Names verified against `hyprsaver --list-shaders` / `--list-palettes`. -# Everything else uses hyprsaver's built-in defaults (see -# /usr/share/hyprsaver/examples/config.toml for the full reference). - -[general] -fps = 30 -shader = "cycle" -palette = "cycle" -shader_cycle_interval = 300 -palette_cycle_interval = 60 -cycle_order = "random" -synced = true -shader_playlist = "chill" -palette_playlist = "chill" - -[playlists.chill] -shaders = [ - "plasma", - "marble", - "snowfall", - "aurora", - "clouds", - "planet", - "starfield", - "fireflies", -] -palettes = [ - "vaporwave", - "frost", - "ocean", - "aurora", -] - -[behavior] -fade_in_ms = 800 -fade_out_ms = 400 -dismiss_on = ["key", "mouse_move", "mouse_click", "touch"] -exclusive_keyboard = true diff --git a/hypr/.config/hypr/modules/keybinds.lua b/hypr/.config/hypr/modules/keybinds.lua index 32432f2..e66c0c6 100644 --- a/hypr/.config/hypr/modules/keybinds.lua +++ b/hypr/.config/hypr/modules/keybinds.lua @@ -233,7 +233,6 @@ bind("SHIFT + PRINT", exec("hyprshot -m active --output-folder " .. var.screensh }) bind(var.mainMod .. " + L", exec(command.lock), { locked = true, description = "Lock screen" }) -bind(var.mainMod .. " + SHIFT + L", exec(command.screensaver), { description = "Start screensaver (any key quits)" }) bind(var.mainMod .. " + K", exec(command.keybinds_show), { description = "Show keybind overview" }) bind(var.mainMod .. " + W", exec(command.wallpaper_select), { description = "Wallpaper select" }) diff --git a/hypr/.config/hypr/modules/lock.lua b/hypr/.config/hypr/modules/lock.lua deleted file mode 100644 index f9c4891..0000000 --- a/hypr/.config/hypr/modules/lock.lua +++ /dev/null @@ -1,33 +0,0 @@ --- Single source of truth for "how do I lock this session", keyed on the --- active shell in env.lua. Consumed by two callers: --- * scripts/lock.sh (hypridle's lock_cmd) - loaded via plain dofile --- * modules/shell_commands.lua (SUPER+L) - loaded via require --- --- Both contexts load this file, so it must NOT use require("env"): Lua's --- default package.path is cwd-relative, and that only resolves inside --- ~/.config/hypr. hypridle's cwd is not guaranteed. Resolve by path instead. -local config_home = os.getenv("XDG_CONFIG_HOME") or os.getenv("HOME") .. "/.config" -local prefix = config_home .. "/hypr/" - -local function load_env() - local fid = io.open(prefix .. "env.lua") - if fid then - fid:close() - return dofile(prefix .. "env.lua") - end - return dofile(prefix .. "env_example.lua") -end - -local LOCK = { - noctalia = "noctalia msg session lock", - caelestia = "caelestia shell lock lock", -} - -local env = load_env() - -return { - shell = env.shell, - -- nil for an unknown shell; scripts/lock.sh treats that as a hard error - -- rather than reporting success while nothing is actually locked. - cmd = LOCK[env.shell], -} diff --git a/hypr/.config/hypr/modules/screensaver.lua b/hypr/.config/hypr/modules/screensaver.lua deleted file mode 100644 index f43eb88..0000000 --- a/hypr/.config/hypr/modules/screensaver.lua +++ /dev/null @@ -1,13 +0,0 @@ --- Screensaver commands for idle (hypridle on-timeout/on-resume) and manual --- (SUPER+SHIFT+L) use. scripts/screensaver-start.sh and --- scripts/screensaver-stop.sh exec the .start / .stop command below. --- --- Active backend: tte (fullscreen kitty + terminal-text-effects). -return { - start = "~/.config/hypr/scripts/launch-screensaver.sh", - stop = "~/.config/hypr/scripts/screensaver-stop.sh", - -- Alternative backend: hyprsaver (GPU fractal shaders, wlr-layer-shell). - -- Swap the commented pair in to switch (no other file changes needed). - -- start = "hyprsaver", - -- stop = "hyprsaver --quit", -} diff --git a/hypr/.config/hypr/modules/shell_commands.lua b/hypr/.config/hypr/modules/shell_commands.lua index 57c4174..22faeeb 100644 --- a/hypr/.config/hypr/modules/shell_commands.lua +++ b/hypr/.config/hypr/modules/shell_commands.lua @@ -1,10 +1,6 @@ local env = require("env") --- Shell-agnostic on purpose: the active shell is resolved inside lock.sh from --- env.lua, so hypridle's lock_cmd and this bind cannot drift apart. -local LOCK = "~/.config/hypr/scripts/lock.sh" - local commands = { session = "", logout = "", @@ -13,8 +9,7 @@ local commands = { menu = "", settings = "", control_center = "", - lock = LOCK, - screensaver = "sh ~/.config/hypr/scripts/screensaver-start.sh force", + lock = "", wallpaper_select = "", keybinds_show = "sh ~/.config/hypr/scripts/show-keybinds.sh", clipboard = "", @@ -33,6 +28,7 @@ if env.shell == "noctalia" then commands.settings = "noctalia msg settings-toggle" commands.control_center = "noctalia msg panel-toggle control-center" commands.wallpaper_select = "noctalia msg panel-toggle wallpaper" + commands.lock = "noctalia msg session lock" elseif env.shell == "caelestia" then commands.session = "caelestia shell drawers toggle session" commands.logout = "hyprctl dispatch exit" @@ -47,6 +43,7 @@ elseif env.shell == "caelestia" then commands.emoji = "caelestia emoji -p" commands.show_all = "caelestia shell drawers toggle utilities" commands.clear_notifications = "caelestia shell notifs clear" + commands.lock = "caelestia shell lock lock" end diff --git a/hypr/.config/hypr/modules/windows_and_workspaces.lua b/hypr/.config/hypr/modules/windows_and_workspaces.lua index d3ea078..62865d2 100644 --- a/hypr/.config/hypr/modules/windows_and_workspaces.lua +++ b/hypr/.config/hypr/modules/windows_and_workspaces.lua @@ -101,16 +101,6 @@ hl.window_rule({ border_size = 0, }) --- Manual tte screensaver preview (SUPER+SHIFT+L): fullscreen kitty. -hl.window_rule({ - name = "screensaver-fullscreen", - match = { - class = "org.screensaver", - }, - fullscreen = true, - float = true, -}) - hl.window_rule({ name = "floating-windows", match = { class = "(imv|.*hyprmod)" }, diff --git a/hypr/.config/hypr/screensaver.txt b/hypr/.config/hypr/screensaver.txt deleted file mode 100644 index c74bbcc..0000000 --- a/hypr/.config/hypr/screensaver.txt +++ /dev/null @@ -1,4 +0,0 @@ -hyprland -// idle looks good on you - -ArchTop is relaxing 🧘 diff --git a/hypr/.config/hypr/scripts/launch-screensaver.sh b/hypr/.config/hypr/scripts/launch-screensaver.sh deleted file mode 100755 index 8d7b9f5..0000000 --- a/hypr/.config/hypr/scripts/launch-screensaver.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash -# launch-screensaver.sh — idle-triggered tte screensaver (hypridle on-timeout). -# Spawns one fullscreen kitty running screensaver-tte.sh per monitor. -# Usage: launch-screensaver.sh [force] -# force ignore the screensaver-off toggle file. -# Toggle off: touch ~/.local/state/hypr/screensaver-off -# -# Lessons baked in: no focus-restore after spawn (it raced the new window and -# caused flash-then-nothing), no pkill -f patterns anywhere (teardown is done -# by screensaver-stop.sh via exact client PIDs). - -set -euo pipefail - -SCREENSAVER_CLASS="org.screensaver" -TOGGLE_FILE="$HOME/.local/state/hypr/screensaver-off" - -command -v tte >/dev/null 2>&1 || exit 1 -command -v kitty >/dev/null 2>&1 || exit 1 - -# Already running (precise client check). -if hyprctl clients -j 2>/dev/null | jq -e '.[] | select(.class=="org.screensaver")' >/dev/null 2>&1; then - exit 0 -fi - -# Opt-out toggle (unless forced, e.g. manual invocation). -if [[ -f "$TOGGLE_FILE" ]] && [[ "${1:-}" != "force" ]]; then - exit 1 -fi - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -# hyprctl prints plain-text errors (not JSON) when HYPRLAND_INSTANCE_SIGNATURE -# is stale (e.g. shell/tmux predates the current compositor). Fail clearly -# instead of dumping a jq parse error. -monitors_json="$(hyprctl monitors -j 2>/dev/null)" || { - echo "launch-screensaver: hyprctl failed - is HYPRLAND_INSTANCE_SIGNATURE stale? (try a fresh terminal)" >&2 - exit 1 -} -echo "$monitors_json" | jq -e . >/dev/null 2>&1 || { - echo "launch-screensaver: hyprctl did not return JSON - stale HYPRLAND_INSTANCE_SIGNATURE?" >&2 - exit 1 -} - -# NOTE: hyprmod Lua runtime (Hyprland 0.56.x) evaluates `hyprctl dispatch` args -# as Lua — classic `dispatch focusmonitor eDP-1` fails. Dispatchers must be -# hl.dsp.* expressions. Focus each monitor before exec so the saver kitty -# spawns there; deliberately no focus-restore afterwards (spawn race). -for m in $(echo "$monitors_json" | jq -r '.[] | .name'); do - hyprctl dispatch "hl.dsp.focus({monitor=\"$m\"})" >/dev/null 2>&1 - hyprctl dispatch "hl.dsp.exec_cmd('kitty --class=\"$SCREENSAVER_CLASS\" --override font_size=18 --override window_padding_width=0 --override background_opacity=1 --override mouse_hide_wait=1.0 -e \"$SCRIPT_DIR/screensaver-tte.sh\"')" >/dev/null 2>&1 -done diff --git a/hypr/.config/hypr/scripts/lock.sh b/hypr/.config/hypr/scripts/lock.sh deleted file mode 100755 index ba9c405..0000000 --- a/hypr/.config/hypr/scripts/lock.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -# lock.sh — lock the session with the active shell's own lock command. -# The mapping lives in modules/lock.lua (reads env.lua), so neither hypridle's -# lock_cmd nor the SUPER+L bind hardcodes a shell. This is what keeps a -# manually started hypridle under Caelestia from failing on `noctalia ...`. -# -# Usage: lock.sh (no args; safe to call while locked) - -set -euo pipefail - -# Resolve from this script's own location so it works through the -# ~/.config/hypr symlink and does not depend on XDG_CONFIG_HOME. -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -LOCK_LUA="$(cd "$SCRIPT_DIR/.." && pwd)/modules/lock.lua" - -CMD="$(LOCK_LUA="$LOCK_LUA" lua -e 'print(dofile(os.getenv("LOCK_LUA")).cmd)' 2>/dev/null)" || { - echo "lock: cannot read modules/lock.lua" >&2 - exit 1 -} - -# Empty here means env.lua named a shell with no known lock command. Fail loudly -# instead of exiting 0, or a caller would think the session got locked. -[[ -n "$CMD" ]] || { - echo "lock: no lock command for the active shell (check env.lua)" >&2 - exit 1 -} - -# Word-split on purpose: CMD is a command line, not a single path. -# shellcheck disable=SC2086 -exec $CMD diff --git a/hypr/.config/hypr/scripts/screensaver-start.sh b/hypr/.config/hypr/scripts/screensaver-start.sh deleted file mode 100755 index 8b4f824..0000000 --- a/hypr/.config/hypr/scripts/screensaver-start.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# screensaver-start.sh — exec the start command from modules/screensaver.lua. -# Usage: screensaver-start.sh [force] (forwarded to .sh targets, e.g. the tte -# launcher's toggle bypass; dropped for bare commands like `hyprsaver`). - -set -euo pipefail - -CMD="$(lua -e 'print(dofile(os.getenv("HOME").."/.config/hypr/modules/screensaver.lua").start)' 2>/dev/null)" || { - echo "screensaver-start: cannot read modules/screensaver.lua" >&2 - exit 1 -} -CMD="${CMD/#\~/$HOME}" -[[ -n "$CMD" ]] || { - echo "screensaver-start: empty start command" >&2 - exit 1 -} - -if [[ "$CMD" == *.sh ]]; then - exec "$CMD" "$@" -else - # Bare command, may carry flags (e.g. "hyprsaver --quit"). - # shellcheck disable=SC2086 - exec $CMD -fi diff --git a/hypr/.config/hypr/scripts/screensaver-stop.sh b/hypr/.config/hypr/scripts/screensaver-stop.sh deleted file mode 100755 index 56af40a..0000000 --- a/hypr/.config/hypr/scripts/screensaver-stop.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# screensaver-stop.sh — exec the stop command from modules/screensaver.lua. -# When it points back here (tte backend), run the built-in teardown below: -# kill saver windows by Hyprland client PID (class org.screensaver) plus -# stray tte processes (exact comm match). Never `pkill -f `: a -# full-cmdline pattern also matches bystander shells merely mentioning it. - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ME="$(realpath "$SCRIPT_DIR/screensaver-stop.sh")" - -WANT="$(lua -e 'print(dofile(os.getenv("HOME").."/.config/hypr/modules/screensaver.lua").stop)' 2>/dev/null)" || { - echo "screensaver-stop: cannot read modules/screensaver.lua" >&2 - exit 1 -} -WANT="${WANT/#\~/$HOME}" - -# Alternative backend configured (e.g. "hyprsaver --quit"): run it instead. -if [[ "$(realpath -m "$WANT")" != "$ME" ]]; then - # shellcheck disable=SC2086 - exec $WANT -fi - -# --- tte teardown --- -pids="$(hyprctl clients -j 2>/dev/null | jq -r '.[] | select(.class=="org.screensaver") | .pid' 2>/dev/null)" || pids="" -if [[ -n "${pids:-}" ]]; then - # shellcheck disable=SC2086 - kill $pids 2>/dev/null || true -fi - -# tte runs under comm "tte" (verified); -x matches the comm only, so this -# cannot hit bystander shells. -pkill -x tte 2>/dev/null || true - -exit 0 diff --git a/hypr/.config/hypr/scripts/screensaver-tte.sh b/hypr/.config/hypr/scripts/screensaver-tte.sh deleted file mode 100755 index c7af194..0000000 --- a/hypr/.config/hypr/scripts/screensaver-tte.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# screensaver-tte.sh — runs inside each fullscreen tte kitty. -# Loops random tte effects until any keypress, then exits (kitty closes). -# Child teardown uses the exact PID ($!) — never pkill -f patterns, which -# would also match bystander shells mentioning the class name. - -set -euo pipefail - -SCREENSAVER_TEXT="$HOME/.config/hypr/screensaver.txt" - -trap 'exit 0' SIGINT SIGTERM SIGHUP SIGQUIT - -printf '\033]11;rgb:00/00/00\007' # black background - -input="$SCREENSAVER_TEXT" -[[ -f "$input" ]] || input="/etc/hostname" - -while true; do - tte -i <(fastfetch --pipe) \ - --frame-rate 60 --canvas-width 0 --canvas-height 0 \ - --anchor-canvas c --anchor-text c \ - --random-effect --exclude-effects dev_worm \ - --no-eol --no-restore-cursor & - TPID=$! - # Wait for tte to appear (Python startup) before polling it. - for _ in $(seq 1 10); do - kill -0 "$TPID" 2>/dev/null || break - pgrep -x tte >/dev/null 2>&1 && break - sleep 0.1 - done - while kill -0 "$TPID" 2>/dev/null; do - if read -r -n1 -t 1; then - kill "$TPID" 2>/dev/null || true - wait "$TPID" 2>/dev/null || true - exit 0 - fi - done - wait "$TPID" 2>/dev/null || true - # Throttle respawn if tte exits instantly (e.g. a sub-second effect). - sleep 0.2 -done diff --git a/test/lint.sh b/test/lint.sh index 6662020..8032f26 100755 --- a/test/lint.sh +++ b/test/lint.sh @@ -31,7 +31,9 @@ say_pass() { printf 'ok: %s\n' "$*"; PASS=$((PASS+1)); } # Exclusions: never lint tpm or general vendored plugin trees, whose third-party # scripts may legitimately fail `bash -n` and are not written/maintained here. EXCLUDE_VENDOR='(/tpm/|/plugins/|/plugins$)' -mapfile -t SCRIPTS < <(git ls-files '*.sh' 2>/dev/null | grep -vE "$EXCLUDE_VENDOR") +mapfile -t SCRIPTS < <(git ls-files '*.sh' 2>/dev/null | grep -vE "$EXCLUDE_VENDOR" | while IFS= read -r script; do + [[ -f "$script" ]] && printf '%s\n' "$script" +done) if [[ ${#SCRIPTS[@]} -eq 0 ]]; then mapfile -t SCRIPTS < <(find . -name '*.sh' \ -not -path './.git/*' -not -path '*/.git/*' \