diff --git a/desktopenvs/hyprlua/config-updater/update-configs.sh b/desktopenvs/hyprlua/config-updater/update-configs.sh index 5321428..b2b27fd 100755 --- a/desktopenvs/hyprlua/config-updater/update-configs.sh +++ b/desktopenvs/hyprlua/config-updater/update-configs.sh @@ -3,6 +3,14 @@ # # Config: ~/.config/config-updater/updater.conf # Manifest: ~/.config/config-updater/manifest +# +# Syntax in updater.conf: +# config [except ...] +# copy SOURCE_BASE/ to ~/.config/, optionally skipping subdirs +# flat +# copy contents of SOURCE_BASE/ directly into ~/.config/ +# ignore +# present in source but intentionally not managed here set -euo pipefail @@ -23,6 +31,7 @@ die() { err "$*"; exit 1; } # ── parse updater.conf ──────────────────────────────────────────────────────── SOURCE_BASE="" declare -A ENTRY_TYPE # name → config | flat | ignore +declare -A ENTRY_EXCL # name → space-separated list of excluded subdirs while IFS= read -r line; do line="${line%%#*}" @@ -36,10 +45,19 @@ while IFS= read -r line; do continue fi - read -r type name _rest <<< "$line" + read -r type name rest <<< "$line" [[ -z "${name:-}" ]] && continue + case "$type" in - config|flat|ignore) ENTRY_TYPE["$name"]="$type" ;; + config|flat|ignore) + ENTRY_TYPE["$name"]="$type" + # Parse optional "except sub1 sub2 ..." + if [[ "${rest:-}" =~ ^except[[:space:]]+(.+)$ ]]; then + ENTRY_EXCL["$name"]="${BASH_REMATCH[1]}" + else + ENTRY_EXCL["$name"]="" + fi + ;; *) warn "Unknown type '$type' for '$name' — skipping" ;; esac done < "$CONF_FILE" @@ -91,11 +109,35 @@ for name in "${!ENTRY_TYPE[@]}"; do continue fi + excl="${ENTRY_EXCL[$name]:-}" + case "$type" in config) - rm -rf "${TARGET:?}/${name}" - cp -r "$src" "$TARGET/$name" - ok "config $name" + if [[ -z "$excl" ]]; then + rm -rf "${TARGET:?}/${name}" + cp -r "$src" "$TARGET/$name" + ok "config $name" + else + # Copy all top-level items of $src except excluded subdirs + mkdir -p "${TARGET}/${name}" + item_errors=0 + while IFS= read -r -d '' item; do + item_name="$(basename "$item")" + skip_item=false + for e in $excl; do + [[ "$item_name" == "$e" ]] && skip_item=true && break + done + [[ "$skip_item" == true ]] && continue + rm -rf "${TARGET:?}/${name}/${item_name}" + cp -r "$item" "${TARGET}/${name}/" || (( item_errors++ )) || true + done < <(find "$src" -maxdepth 1 -mindepth 1 -print0 | sort -z) + if (( item_errors > 0 )); then + err "config $name (${item_errors} error(s))" + (( errors++ )) || true + else + ok "config $name (excl: $excl)" + fi + fi ;; flat) [[ -d "$src" ]] || { diff --git a/desktopenvs/hyprlua/config-updater/updater.conf b/desktopenvs/hyprlua/config-updater/updater.conf index 203880b..db58e9e 100644 --- a/desktopenvs/hyprlua/config-updater/updater.conf +++ b/desktopenvs/hyprlua/config-updater/updater.conf @@ -7,14 +7,14 @@ # (for installation-specific files that live at the ~/.config root) # ignore present in source but intentionally not managed here -SOURCE_BASE = ~/Dotfiles/desktopenvs/hyprland +SOURCE_BASE = ~/Dotfiles/desktopenvs/hyprlua # ── deployed as ~/.config/ ───────────────────────────────────────────── config alacritty config btop config dunst config gtk-3.0 -config hypr +config hypr except usr config kitty config mimeapps.list config nwg-dock-hyprland @@ -27,9 +27,6 @@ config walker config wofi config xfce4 -# ── flat: directory contents copied directly into ~/.config/ ────────────────── -flat hypr-usr # installation-specific: binds, monitors, autostart, etc. - # ── intentionally not managed here ─────────────────────────────────────────── ignore config-updater # the updater itself ignore CRT # referenced from dotfiles path directly in binds.conf @@ -40,3 +37,7 @@ ignore greetd-tuigreet # deployed to /etc/greetd/ at install time ignore spicetify # managed separately (spicetify handles its own config) ignore Vencord # managed separately ignore waybar # present but inactive; eww bar is used instead +# hypr/usr/ contains device-specific lua files (monitors, binds, input, etc.). +# They are excluded from automated syncs to preserve per-device customisations. +# Deploy manually on a new device: +# cp -r ~/Dotfiles/desktopenvs/hyprlua/hypr/usr/ ~/.config/hypr/ diff --git a/desktopenvs/hyprlua/hypr/hypridle.conf b/desktopenvs/hyprlua/hypr/hypridle.conf index dcc57d1..fbf07ba 100644 --- a/desktopenvs/hyprlua/hypr/hypridle.conf +++ b/desktopenvs/hyprlua/hypr/hypridle.conf @@ -1,22 +1,19 @@ -#source ~/.config/idle.conf general { - lock_cmd = pidof hyprlock || hyprlock # avoid starting multiple hyprlock instances. - before_sleep_cmd = loginctl lock-session # lock before suspend. - after_sleep_cmd = systemctl restart fprintd.service ;; hyprctl dispatch dpms on # to avoid having to press a key twice to turn on the display. - + lock_cmd = pidof hyprlock || hyprlock + before_sleep_cmd = loginctl lock-session + # fprintd restart ensures fingerprint sensor is ready after resume + after_sleep_cmd = systemctl restart fprintd.service ; hyprctl dispatch dpms on + ignore_dbus_inhibit = false # respect systemd-inhibit locks (presence-detect, caffeine) } + +# Presence detection resets the idle timer every 2 minutes while you're visible, +# so these timeouts only run when you've actually stepped away. listener { - timeout = 120 - on-timeout = loginctl lock-session # lock screen when timeout has passed + timeout = 180 # 3 min — lock screen + on-timeout = loginctl lock-session } listener { - timeout = 600 #10min - on-timeout = systemctl suspend-then-hibernate # suspend pc + timeout = 600 # 10 min — suspend + on-timeout = systemctl suspend-then-hibernate } - - -#listener { -# timeout = 18000 #5h -# on-timeout = /usr/bin/reboot #reboot -#} diff --git a/desktopenvs/hyprlua/hypr/hyprland.lua b/desktopenvs/hyprlua/hypr/hyprland.lua index d1a05ce..40a1a1d 100644 --- a/desktopenvs/hyprlua/hypr/hyprland.lua +++ b/desktopenvs/hyprlua/hypr/hyprland.lua @@ -1,12 +1,12 @@ -- Hyprland Lua config — https://wiki.hypr.land/Configuring/Start/ --- User-side files are required from ~/.config/hypr/ alongside this file. +-- Device-specific files live in ~/.config/hypr/usr/ (deployed from hypr/usr/). -require("monitors") -require("envvars") -require("input") -require("binds") -require("windowrules") -require("autostart") +require("usr.monitors") +require("usr.envvars") +require("usr.input") +require("usr.binds") +require("usr.windowrules") +require("usr.autostart") -------------------- ---- MY PROGRAMS --- diff --git a/desktopenvs/hyprlua/hypr/hyprlock.conf b/desktopenvs/hyprlua/hypr/hyprlock.conf index 918cd2e..0e61ac2 100644 --- a/desktopenvs/hyprlua/hypr/hyprlock.conf +++ b/desktopenvs/hyprlua/hypr/hyprlock.conf @@ -8,7 +8,7 @@ general { } auth { - pam:module = sudo + pam:module = hyprlock fingerprint:enabled = true } diff --git a/desktopenvs/hyprlua/hypr/hyprpaper.conf b/desktopenvs/hyprlua/hypr/hyprpaper.conf index 22a683b..74fbbc1 100644 --- a/desktopenvs/hyprlua/hypr/hyprpaper.conf +++ b/desktopenvs/hyprlua/hypr/hyprpaper.conf @@ -8,4 +8,4 @@ wallpaper { } # per-monitor state, written by ~/.config/scripts/wallpaper-picker -source = ~/.config/wallpaper.conf +source = ~/.config/hypr/usr/wallpaper.conf diff --git a/desktopenvs/hyprlua/hypr-usr/autostart.lua b/desktopenvs/hyprlua/hypr/usr/autostart.lua similarity index 91% rename from desktopenvs/hyprlua/hypr-usr/autostart.lua rename to desktopenvs/hyprlua/hypr/usr/autostart.lua index d4c948c..09beef1 100644 --- a/desktopenvs/hyprlua/hypr-usr/autostart.lua +++ b/desktopenvs/hyprlua/hypr/usr/autostart.lua @@ -20,4 +20,6 @@ hl.on("hyprland.start", function() hl.exec_cmd("blueman-applet") hl.exec_cmd("blueman-tray") hl.exec_cmd("hypridle") + hl.exec_cmd("bash ~/Dotfiles/desktopenvs/hyprlua/scripts/presence-detect.sh") + hl.exec_cmd("chamel") end) diff --git a/desktopenvs/hyprlua/hypr-usr/binds.lua b/desktopenvs/hyprlua/hypr/usr/binds.lua similarity index 98% rename from desktopenvs/hyprlua/hypr-usr/binds.lua rename to desktopenvs/hyprlua/hypr/usr/binds.lua index e5fb31b..359d046 100644 --- a/desktopenvs/hyprlua/hypr-usr/binds.lua +++ b/desktopenvs/hyprlua/hypr/usr/binds.lua @@ -61,7 +61,7 @@ hl.bind(mainMod .. " + W", hl.dsp.exec_cmd("[tag +centered-L] kitt hl.bind(mainMod .. " + CTRL + R", hl.dsp.exec_cmd("[tag +centered-L] kitty -e ~/.config/scripts/amssh")) hl.bind(mainMod .. " + F1", hl.dsp.exec_cmd("[tag +centered] kitty ~/.config/scripts/helpmenu.sh")) hl.bind(mainMod .. " + CTRL + T", hl.dsp.exec_cmd("[tag +centered-S] kitty bash ~/.config/scripts/timer-pick")) -hl.bind(mainMod .. " + SHIFT + F1", hl.dsp.exec_cmd("[tag +centered-L] kitty nvim ~/.config/binds.lua")) +hl.bind(mainMod .. " + SHIFT + F1", hl.dsp.exec_cmd("[tag +centered-L] kitty nvim ~/.config/hypr/usr/binds.lua")) hl.bind(mainMod .. " + CTRL + P", hl.dsp.exec_cmd("~/.config/scripts/screenrec.sh")) @@ -260,6 +260,7 @@ hl.bind(mainMod .. " + CTRL + Z", hl.dsp.exec_cmd("chamel clear-and-deactivate") hl.bind(mainMod .. " + CTRL + C", hl.dsp.exec_cmd("dunstctl close-all")) hl.bind(mainMod .. " + CTRL + G", hl.dsp.exec_cmd("~/.config/scripts/onscreenkb.sh")) hl.bind(mainMod .. " + SHIFT + C", hl.dsp.exec_cmd("~/.config/scripts/caffeine.sh")) +hl.bind(mainMod .. " + SHIFT + B", hl.dsp.exec_cmd("[tag +centered-S] kitty bash ~/.config/scripts/enroll-biometrics.sh")) hl.bind(mainMod .. " + SHIFT + X", hl.dsp.exec_cmd("~/.config/scripts/hyprland-toggle-touchpad.sh")) hl.bind(mainMod .. " + CTRL + E", hl.dsp.exec_cmd("~/.config/scripts/screenrotationwcw.sh")) diff --git a/desktopenvs/hyprlua/hypr-usr/envvars.lua b/desktopenvs/hyprlua/hypr/usr/envvars.lua similarity index 100% rename from desktopenvs/hyprlua/hypr-usr/envvars.lua rename to desktopenvs/hyprlua/hypr/usr/envvars.lua diff --git a/desktopenvs/hyprlua/hypr-usr/input-device-exceptions.lua b/desktopenvs/hyprlua/hypr/usr/input-device-exceptions.lua similarity index 100% rename from desktopenvs/hyprlua/hypr-usr/input-device-exceptions.lua rename to desktopenvs/hyprlua/hypr/usr/input-device-exceptions.lua diff --git a/desktopenvs/hyprlua/hypr-usr/input.lua b/desktopenvs/hyprlua/hypr/usr/input.lua similarity index 90% rename from desktopenvs/hyprlua/hypr-usr/input.lua rename to desktopenvs/hyprlua/hypr/usr/input.lua index 618c643..95928a5 100644 --- a/desktopenvs/hyprlua/hypr-usr/input.lua +++ b/desktopenvs/hyprlua/hypr/usr/input.lua @@ -15,4 +15,4 @@ hl.config({ }, }) -require("input-device-exceptions") +require("usr.input-device-exceptions") diff --git a/desktopenvs/hyprlua/hypr-usr/mk-device-block.sh b/desktopenvs/hyprlua/hypr/usr/mk-device-block.sh similarity index 100% rename from desktopenvs/hyprlua/hypr-usr/mk-device-block.sh rename to desktopenvs/hyprlua/hypr/usr/mk-device-block.sh diff --git a/desktopenvs/hyprlua/hypr-usr/mk-device-exception.sh b/desktopenvs/hyprlua/hypr/usr/mk-device-exception.sh similarity index 100% rename from desktopenvs/hyprlua/hypr-usr/mk-device-exception.sh rename to desktopenvs/hyprlua/hypr/usr/mk-device-exception.sh diff --git a/desktopenvs/hyprlua/hypr-usr/monitors.lua b/desktopenvs/hyprlua/hypr/usr/monitors.lua similarity index 100% rename from desktopenvs/hyprlua/hypr-usr/monitors.lua rename to desktopenvs/hyprlua/hypr/usr/monitors.lua diff --git a/desktopenvs/hyprlua/hypr-usr/wallpaper.conf b/desktopenvs/hyprlua/hypr/usr/wallpaper.conf similarity index 100% rename from desktopenvs/hyprlua/hypr-usr/wallpaper.conf rename to desktopenvs/hyprlua/hypr/usr/wallpaper.conf diff --git a/desktopenvs/hyprlua/hypr-usr/windowrules.conf.old b/desktopenvs/hyprlua/hypr/usr/windowrules.conf.old similarity index 100% rename from desktopenvs/hyprlua/hypr-usr/windowrules.conf.old rename to desktopenvs/hyprlua/hypr/usr/windowrules.conf.old diff --git a/desktopenvs/hyprlua/hypr-usr/windowrules.lua b/desktopenvs/hyprlua/hypr/usr/windowrules.lua similarity index 100% rename from desktopenvs/hyprlua/hypr-usr/windowrules.lua rename to desktopenvs/hyprlua/hypr/usr/windowrules.lua diff --git a/desktopenvs/hyprlua/scripts/caffeine.sh b/desktopenvs/hyprlua/scripts/caffeine.sh index b6d40e6..b21cc27 100755 --- a/desktopenvs/hyprlua/scripts/caffeine.sh +++ b/desktopenvs/hyprlua/scripts/caffeine.sh @@ -1,17 +1,17 @@ #!/bin/bash +# Toggle idle inhibit via systemd-inhibit (hypridle respects the logind idle hint). +PID_FILE="/tmp/caffeine-inhibit.pid" -statecon=$( pidof hypridle | grep "[1234567890]" ) -#echo $statecon -if [ "$statecon" != "" ]; then - - notify-send -t 1000 "caffeine mode on" - killall hypridle - +if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then + kill "$(cat "$PID_FILE")" + rm -f "$PID_FILE" + notify-send -t 2000 "Caffeine" "Idle inhibit OFF" else - - notify-send -t 1000 "caffeine mode off" - hypridle & - + systemd-inhibit --what=idle:sleep \ + --who="caffeine" \ + --why="Caffeine mode active" \ + --mode=block \ + sleep infinity & + echo $! > "$PID_FILE" + notify-send -t 2000 "Caffeine" "Idle inhibited" fi - - diff --git a/desktopenvs/hyprlua/scripts/enroll-biometrics.sh b/desktopenvs/hyprlua/scripts/enroll-biometrics.sh new file mode 100755 index 0000000..5aa2b73 --- /dev/null +++ b/desktopenvs/hyprlua/scripts/enroll-biometrics.sh @@ -0,0 +1,251 @@ +#!/bin/bash +# enroll-biometrics.sh — TUI for face biometric setup. +# +# Two subsystems: +# 1. Presence detection — configure/test the webcam used by presence-detect.sh +# 2. Howdy face auth — enroll/manage/test face models for PAM authentication + +BACKTITLE="Biometric Enrollment" +PRESENCE_CFG="${XDG_CONFIG_HOME:-$HOME/.config}/presence-detect.conf" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PYTHON_DETECT="$SCRIPT_DIR/python/presence_detect.py" + +# ── Dialog theme (Cyberqueer) ───────────────────────────────────────────────── +TMP_D=$(mktemp -d) +trap 'rm -rf "$TMP_D"' EXIT INT TERM + +export DIALOGRC="$TMP_D/dialogrc" +cat > "$DIALOGRC" << 'RCEOF' +use_shadow = ON +use_colors = ON +screen_color = (BLACK,BLACK,ON) +shadow_color = (BLACK,BLACK,ON) +title_color = (MAGENTA,BLACK,ON) +border_color = (MAGENTA,BLACK,ON) +button_active_color = (BLACK,MAGENTA,ON) +button_inactive_color = (WHITE,BLACK,OFF) +button_key_active_color = (BLACK,CYAN,ON) +button_key_inactive_color = (CYAN,BLACK,ON) +button_label_active_color = (BLACK,MAGENTA,ON) +button_label_inactive_color = (WHITE,BLACK,OFF) +inputbox_color = (WHITE,BLACK,OFF) +inputbox_border_color = (MAGENTA,BLACK,ON) +menubox_color = (WHITE,BLACK,OFF) +menubox_border_color = (MAGENTA,BLACK,ON) +item_color = (WHITE,BLACK,OFF) +item_selected_color = (BLACK,MAGENTA,ON) +tag_color = (CYAN,BLACK,ON) +tag_selected_color = (BLACK,CYAN,ON) +check_color = (WHITE,BLACK,OFF) +check_selected_color = (BLACK,MAGENTA,ON) +uarrow_color = (MAGENTA,BLACK,ON) +darrow_color = (MAGENTA,BLACK,ON) +RCEOF + +# ── Helpers ─────────────────────────────────────────────────────────────────── +die() { clear; printf "\nError: %s\n\n" "$1" >&2; exit 1; } + +msg() { + dialog --backtitle "$BACKTITLE" --title " $1 " --msgbox "\n$2\n" "$3" "$4" +} + +require_dialog() { + command -v dialog &>/dev/null && return + sudo pacman -S --noconfirm dialog || die "dialog not found" +} + +# ── Camera helpers ───────────────────────────────────────────────────────────── +list_cameras() { + for dev in /dev/video*; do + [[ -c "$dev" ]] || continue + local id="${dev#/dev/video}" + local name + name=$(v4l2-ctl --device="$dev" --info 2>/dev/null \ + | awk -F': ' '/Card type/{print $2}' | head -1) + [[ -z "$name" ]] && name="Camera $id" + printf '%s\n%s\n' "$id" "$name" + done +} + +get_camera_id() { + if [[ -n "$PRESENCE_DETECT_CAMERA" ]]; then + echo "$PRESENCE_DETECT_CAMERA" + elif [[ -f "$PRESENCE_CFG" ]]; then + grep -oP 'CAMERA=\K[0-9]+' "$PRESENCE_CFG" 2>/dev/null || echo 0 + else + echo 0 + fi +} + +set_camera_id() { + mkdir -p "$(dirname "$PRESENCE_CFG")" + printf 'CAMERA=%s\n' "$1" > "$PRESENCE_CFG" +} + +# ── Presence detection ──────────────────────────────────────────────────────── +presence_configure_camera() { + local -a cam_items + mapfile -t cam_items < <(list_cameras) + + if [[ ${#cam_items[@]} -eq 0 ]]; then + msg "No Cameras Found" \ + "No video devices found at /dev/video*.\n\nMake sure your webcam is connected." \ + 10 55 + return + fi + + local current; current=$(get_camera_id) + local choice + choice=$(dialog --backtitle "$BACKTITLE" \ + --title " Select Camera " \ + --menu "\nCurrent: /dev/video${current}\n\nSelect camera to use for presence detection:" \ + 16 62 6 \ + "${cam_items[@]}" \ + 3>&1 1>&2 2>&3) || return + + set_camera_id "$choice" + msg "Camera Set" \ + "Presence detection will use /dev/video${choice}.\n\nRestart presence-detect.sh for the change to take effect." \ + 9 62 +} + +presence_test_camera() { + local cam; cam=$(get_camera_id) + + clear + printf "\n\033[1;35m Testing presence detection on /dev/video%s...\033[0m\n" "$cam" + printf "\033[35m ─────────────────────────────────────────\033[0m\n\n" + printf " Look at the camera and stay still.\n\n" + + python3 "$PYTHON_DETECT" "$cam" 2>/dev/null + local rc=$? + + case $rc in + 0) msg "Test Result" "Face detected!\n\nPresence detection is working correctly." 8 52 ;; + 1) msg "Test Result" \ + "No face detected.\n\nMake sure you are in front of the camera\nand there is adequate lighting." \ + 10 56 ;; + 2) msg "Camera Error" \ + "Could not open /dev/video${cam}.\n\nTry configuring a different camera first." \ + 9 56 ;; + *) msg "Error" "Unexpected exit code ($rc) from detection script." 7 52 ;; + esac +} + +# ── Howdy helpers ───────────────────────────────────────────────────────────── +howdy_installed() { command -v howdy &>/dev/null; } + +howdy_require() { + howdy_installed && return 0 + dialog --backtitle "$BACKTITLE" --title " Howdy Not Found " \ + --yesno "\nhowdy is not installed.\n\nInstall it now via yay?" 8 48 || return 1 + clear + printf "\nInstalling howdy...\n\n" + if command -v yay &>/dev/null; then + yay -S --noconfirm --needed howdy + else + sudo pacman -S --noconfirm --needed howdy + fi + howdy_installed || { msg "Install Failed" "howdy installation failed.\nInstall it manually: yay -S howdy" 8 52; return 1; } +} + +# ── Howdy operations ────────────────────────────────────────────────────────── +howdy_add() { + howdy_require || return + + local name + name=$(dialog --backtitle "$BACKTITLE" \ + --title " Add Face Model " \ + --inputbox "\nEnter a label for this face model:" 8 52 "$USER" \ + 3>&1 1>&2 2>&3) || return + [[ -z "$name" ]] && return + + clear + printf "\n\033[1;35m Enrolling face model '%s'...\033[0m\n" "$name" + printf "\033[35m ─────────────────────────────────────────\033[0m\n\n" + printf " Look at the camera and hold still.\n\n" + sudo howdy add -n "$name" + local rc=$? + + if [[ $rc -eq 0 ]]; then + msg "Enrolled" \ + "Face model '$name' added.\n\nYou may need to relogin for PAM changes to take effect." \ + 9 58 + else + msg "Failed" "Enrollment failed (code $rc).\n\nCheck camera and lighting." 8 52 + fi +} + +howdy_list() { + howdy_require || return + local out; out=$(sudo howdy list 2>&1) + msg "Enrolled Face Models" "$out" 20 64 +} + +howdy_remove() { + howdy_require || return + + local models; models=$(sudo howdy list 2>&1) + if [[ -z "$models" || "$models" == *"no models"* ]]; then + msg "No Models" "No face models are currently enrolled." 7 44 + return + fi + + local id + id=$(dialog --backtitle "$BACKTITLE" \ + --title " Remove Face Model " \ + --inputbox "\nCurrent models:\n\n${models}\n\nEnter model ID to remove:" 20 64 \ + 3>&1 1>&2 2>&3) || return + [[ -z "$id" || ! "$id" =~ ^[0-9]+$ ]] && { + msg "Invalid ID" "Please enter a numeric model ID." 6 36; return + } + + dialog --backtitle "$BACKTITLE" --title " Confirm " \ + --yesno "\nRemove face model ID $id?" 7 40 || return + + sudo howdy remove -I "$id" + msg "Done" "Face model $id removed." 6 38 +} + +howdy_test() { + howdy_require || return + clear + printf "\n\033[1;35m Testing howdy authentication...\033[0m\n" + printf "\033[35m ─────────────────────────────────────────\033[0m\n\n" + printf " Look at the camera.\n\n" + sudo howdy test + local rc=$? + if [[ $rc -eq 0 ]]; then + msg "Result" "Authentication successful." 6 38 + else + msg "Result" "Authentication failed (code $rc).\n\nCheck enrolled models and camera setup." 9 52 + fi +} + +# ── Main menu ───────────────────────────────────────────────────────────────── +main_menu() { + local choice + choice=$(dialog --backtitle "$BACKTITLE" \ + --title " Biometric Enrollment " \ + --menu "\nSelect an option:" 20 66 8 \ + "1" "Presence detection — configure camera" \ + "2" "Presence detection — test detection" \ + "3" "Howdy face auth — add face model" \ + "4" "Howdy face auth — list enrolled models" \ + "5" "Howdy face auth — remove face model" \ + "6" "Howdy face auth — test authentication" \ + 3>&1 1>&2 2>&3) || { clear; exit 0; } + + case "$choice" in + 1) presence_configure_camera ;; + 2) presence_test_camera ;; + 3) howdy_add ;; + 4) howdy_list ;; + 5) howdy_remove ;; + 6) howdy_test ;; + esac + main_menu +} + +require_dialog +main_menu diff --git a/desktopenvs/hyprlua/scripts/frequentcommands.list b/desktopenvs/hyprlua/scripts/frequentcommands.list index 5cfc086..236cef7 100755 --- a/desktopenvs/hyprlua/scripts/frequentcommands.list +++ b/desktopenvs/hyprlua/scripts/frequentcommands.list @@ -1,24 +1,41 @@ -wg-quick up wg0 -wg-quick down wg0 +poweroff +reboot hyprshutdown hyprshutdown && reboot -poweroff -hyprctl kill -reboot systemctl soft-reboot systemctl suspend-then-hibernate systemctl hibernate systemctl sleep -systemctl restart fprintd.service systemctl hybrid-sleep hyprctl dispatch exit hyprlock +hyprctl kill +systemctl restart fprintd.service +wg-quick up wg0 +wg-quick down wg0 hyprctl reload -kitty ~/Dotfiles/desktopenvs/hyprland/scripts/helpmenu.sh -kitty nvim ~/.config/binds.conf -~/Dotfiles/desktopenvs/hyprland/scripts/caffeine.sh -~/Dotfiles/desktopenvs/hyprland/scripts/hyprland-toggle-touchpad.sh eww reload +~/.config/scripts/screenshot.sh +~/.config/scripts/screenrec.sh +~/.config/scripts/caffeine.sh +~/.config/scripts/hyprland-toggle-touchpad.sh +~/.config/scripts/onscreenkb.sh +~/.config/scripts/togglebar.sh +~/.config/scripts/toggle-layout.sh +~/.config/scripts/togglewinbars.sh +~/.config/scripts/screenrotationacw.sh +~/.config/scripts/screenrotationwcw.sh +~/.config/scripts/unified-rotate.sh cw +~/.config/scripts/unified-rotate.sh ccw +~/.config/scripts/pwr-dmenu.sh +kitty ~/.config/scripts/helpmenu.sh +kitty -e ~/.config/scripts/wallpaper-picker ~/Pictures +kitty -e ~/.config/scripts/amssh +kitty bash ~/.config/scripts/enroll-biometrics.sh +kitty nvim ~/.config/hypr/usr/binds.lua +chamel toggle +chamel clear +chamel clear-and-deactivate chamel stroke-color "#00BFFF" chamel stroke-color "#000000" chamel stroke-color "#FFFFFF" diff --git a/desktopenvs/hyprlua/scripts/helpmenu.sh b/desktopenvs/hyprlua/scripts/helpmenu.sh index 5db8c81..02d4c6f 100755 --- a/desktopenvs/hyprlua/scripts/helpmenu.sh +++ b/desktopenvs/hyprlua/scripts/helpmenu.sh @@ -1,3 +1,3 @@ #!/bin/bash -cat ~/Dotfiles/desktopenvs/hyprlua/hypr-usr/binds.lua | less +cat ~/Dotfiles/desktopenvs/hyprlua/hypr/usr/binds.lua | less diff --git a/desktopenvs/hyprlua/scripts/monitorhandler.sh b/desktopenvs/hyprlua/scripts/monitorhandler.sh index 1ddc98d..c0deca0 100755 --- a/desktopenvs/hyprlua/scripts/monitorhandler.sh +++ b/desktopenvs/hyprlua/scripts/monitorhandler.sh @@ -1,4 +1,4 @@ #!/bin/bash -hyprpaper -c ~/.config/wallpaper.conf -~/Dotfiles/desktopenvs/hyprland/scripts/ewwstart.sh +hyprpaper -c ~/.config/hypr/usr/wallpaper.conf +~/Dotfiles/desktopenvs/hyprlua/scripts/ewwstart.sh diff --git a/desktopenvs/hyprlua/scripts/presence-detect.sh b/desktopenvs/hyprlua/scripts/presence-detect.sh new file mode 100755 index 0000000..387a4f4 --- /dev/null +++ b/desktopenvs/hyprlua/scripts/presence-detect.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Webcam presence detection daemon. +# Checks for a face every 2 minutes; holds a systemd-inhibit idle lock while +# the user is detected so hypridle never fires during an active session. +# +# Camera selection: set PRESENCE_DETECT_CAMERA env var or write +# CAMERA= to ~/.config/presence-detect.conf +# +# Exit codes from python helper: 0=face, 1=no face, 2=camera error + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PYTHON_DETECT="$SCRIPT_DIR/python/presence_detect.py" +INHIBIT_PID_FILE="/tmp/presence-inhibit.pid" +PRESENCE_CFG="${XDG_CONFIG_HOME:-$HOME/.config}/presence-detect.conf" +INTERVAL=120 # seconds between checks + +_camera_id() { + if [[ -n "$PRESENCE_DETECT_CAMERA" ]]; then + echo "$PRESENCE_DETECT_CAMERA" + elif [[ -f "$PRESENCE_CFG" ]]; then + grep -oP 'CAMERA=\K[0-9]+' "$PRESENCE_CFG" 2>/dev/null || echo 0 + else + echo 0 + fi +} + +_inhibit_running() { + [[ -f "$INHIBIT_PID_FILE" ]] && kill -0 "$(cat "$INHIBIT_PID_FILE")" 2>/dev/null +} + +_start_inhibit() { + _inhibit_running && return + systemd-inhibit --what=idle --who="presence-detect" \ + --why="User presence detected" --mode=block \ + sleep infinity & + echo $! > "$INHIBIT_PID_FILE" + logger -t presence-detect "Presence detected — idle inhibited" +} + +_stop_inhibit() { + _inhibit_running || return + kill "$(cat "$INHIBIT_PID_FILE")" 2>/dev/null + rm -f "$INHIBIT_PID_FILE" + logger -t presence-detect "No presence — idle inhibit released" +} + +_cleanup() { + _stop_inhibit + exit 0 +} +trap _cleanup SIGTERM SIGINT SIGHUP + +while true; do + CAMERA="$(_camera_id)" + python3 "$PYTHON_DETECT" "$CAMERA" 2>/dev/null + rc=$? + case $rc in + 0) _start_inhibit ;; + 1) _stop_inhibit ;; + # rc=2 = camera unavailable — leave current inhibit state unchanged + esac + sleep "$INTERVAL" +done diff --git a/desktopenvs/hyprlua/scripts/python/presence_detect.py b/desktopenvs/hyprlua/scripts/python/presence_detect.py new file mode 100755 index 0000000..9cb06d5 --- /dev/null +++ b/desktopenvs/hyprlua/scripts/python/presence_detect.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +""" +Webcam face presence detector using OpenCV haar cascades. +Exit codes: 0 = face detected, 1 = no face, 2 = camera error +Usage: presence_detect.py [camera_id] +""" +import sys +import cv2 + +FRAMES_TO_CHECK = 6 +FACES_NEEDED = 2 # require face in at least N frames to count as present +SCALE_FACTOR = 1.1 +MIN_NEIGHBORS = 4 + + +def detect(camera_id: int) -> int: + cap = cv2.VideoCapture(camera_id) + if not cap.isOpened(): + return 2 + + cascade = cv2.CascadeClassifier( + cv2.data.haarcascades + "haarcascade_frontalface_default.xml" + ) + detected = 0 + try: + for _ in range(FRAMES_TO_CHECK): + ok, frame = cap.read() + if not ok: + continue + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + faces = cascade.detectMultiScale(gray, SCALE_FACTOR, MIN_NEIGHBORS) + if len(faces) > 0: + detected += 1 + finally: + cap.release() + + return 0 if detected >= FACES_NEEDED else 1 + + +if __name__ == "__main__": + camera_id = int(sys.argv[1]) if len(sys.argv) > 1 else 0 + sys.exit(detect(camera_id)) diff --git a/desktopenvs/hyprlua/scripts/wallpaper-picker b/desktopenvs/hyprlua/scripts/wallpaper-picker index 66cab3c..1ded82a 100755 --- a/desktopenvs/hyprlua/scripts/wallpaper-picker +++ b/desktopenvs/hyprlua/scripts/wallpaper-picker @@ -6,7 +6,7 @@ set -u VERBOSE=0 DIR="" -STATE_FILE="${HOME}/.config/wallpaper.conf" +STATE_FILE="${HOME}/.config/hypr/usr/wallpaper.conf" HYPRLOCK_BG_FILE="${HOME}/.config/hypr/hyprlock-backgrounds.conf" FIT_MODE="cover" diff --git a/desktopenvs/migrate-hyprland-to-hyprlua.sh b/desktopenvs/migrate-hyprland-to-hyprlua.sh index b188a65..c651558 100755 --- a/desktopenvs/migrate-hyprland-to-hyprlua.sh +++ b/desktopenvs/migrate-hyprland-to-hyprlua.sh @@ -99,8 +99,8 @@ done # ── swap hypr-usr (the part that actually differs) ──────────────────────────── # hyprland: *.conf files live at ~/.config/ root (sourced by hyprland.conf) -# hyprlua: *.lua files live at ~/.config/hypr/ (required by hyprland.lua) -# wallpaper.conf stays at ~/.config/ root (hyprpaper still reads it) +# hyprlua: device-specific files live at ~/.config/hypr/usr/ +# (required via require("usr.*") in hyprland.lua) printf '\n' @@ -108,20 +108,17 @@ printf '\n' for f in "${HYPR_USR_CONFS[@]}"; do [[ -f "$TARGET/$f" ]] && run "rm '$TARGET/$f'" && note "removed ~/.config/$f" done +# Remove wallpaper.conf from ~/.config/ root if present (old location) +[[ -f "$TARGET/wallpaper.conf" ]] && run "rm '$TARGET/wallpaper.conf'" && note "removed ~/.config/wallpaper.conf" -# Deploy new .lua files into ~/.config/hypr/ -if [[ -d "$SRC/hypr-usr" ]]; then - run "mkdir -p '$TARGET/hypr'" - for lua in "$SRC/hypr-usr"/*.lua; do - [[ -e "$lua" ]] || continue - run "cp '$lua' '$TARGET/hypr/'" - ok "hypr-usr $(basename "$lua") → ~/.config/hypr/" +# Deploy device-specific files into ~/.config/hypr/usr/ +if [[ -d "$SRC/hypr/usr" ]]; then + run "mkdir -p '$TARGET/hypr/usr'" + for f in "$SRC/hypr/usr"/*; do + [[ -e "$f" ]] || continue + run "cp '$f' '$TARGET/hypr/usr/'" + ok "hypr/usr $(basename "$f") → ~/.config/hypr/usr/" done - # wallpaper.conf stays at ~/.config/ root - if [[ -f "$SRC/hypr-usr/wallpaper.conf" ]]; then - run "cp '$SRC/hypr-usr/wallpaper.conf' '$TARGET/'" - ok "hypr-usr wallpaper.conf → ~/.config/" - fi fi # ── update config-updater ───────────────────────────────────────────────────── diff --git a/desktopenvs/niri/niri/modules/autostart.kdl b/desktopenvs/niri/niri/modules/autostart.kdl index f89ff45..9e82ad4 100644 --- a/desktopenvs/niri/niri/modules/autostart.kdl +++ b/desktopenvs/niri/niri/modules/autostart.kdl @@ -9,4 +9,5 @@ spawn-at-startup "bash" "-c" "gammastep -O 4500" spawn-at-startup "nm-applet" spawn-at-startup "dunst" spawn-at-startup "bash" "-c" "swayidle -w timeout 300 'swaylock -f' timeout 600 'systemctl suspend' before-sleep 'swaylock -f'" +spawn-at-startup "bash" "-c" "~/.config/scripts/presence-detect.sh" spawn-at-startup "blueman-applet" diff --git a/desktopenvs/niri/niri/modules/binds.kdl b/desktopenvs/niri/niri/modules/binds.kdl index b497589..e871560 100644 --- a/desktopenvs/niri/niri/modules/binds.kdl +++ b/desktopenvs/niri/niri/modules/binds.kdl @@ -204,6 +204,7 @@ binds { Mod+Ctrl+C { spawn "dunstctl" "close-all"; } Mod+Ctrl+G { spawn "bash" "-c" "~/.config/scripts/onscreenkb.sh"; } Mod+Shift+C { spawn "bash" "-c" "~/.config/scripts/caffeine.sh"; } + Mod+Shift+B { spawn "bash" "-c" "kitty -e ~/.config/scripts/enroll-biometrics.sh"; } Mod+Shift+X { spawn "bash" "-c" "~/.config/scripts/niri-toggle-touchpad.sh"; } Mod+Ctrl+E { spawn "bash" "-c" "~/.config/scripts/screenrotationwcw.sh"; } diff --git a/desktopenvs/niri/scripts/caffeine.sh b/desktopenvs/niri/scripts/caffeine.sh index 6fb2c12..db3b021 100755 --- a/desktopenvs/niri/scripts/caffeine.sh +++ b/desktopenvs/niri/scripts/caffeine.sh @@ -1,14 +1,17 @@ #!/bin/bash +# Toggle idle inhibit via systemd-inhibit (swayidle respects the logind idle hint). +PID_FILE="/tmp/caffeine-inhibit.pid" -statecon=$(pidof swayidle | grep "[1234567890]") - -if [ "$statecon" != "" ]; then - notify-send -t 1000 "Caffeine mode ON — idle inhibited" - killall swayidle +if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then + kill "$(cat "$PID_FILE")" + rm -f "$PID_FILE" + notify-send -t 2000 "Caffeine" "Idle inhibit OFF" else - notify-send -t 1000 "Caffeine mode OFF — idle active" - swayidle -w \ - timeout 300 'swaylock -f' \ - timeout 600 'systemctl suspend' \ - before-sleep 'swaylock -f' & + systemd-inhibit --what=idle:sleep \ + --who="caffeine" \ + --why="Caffeine mode active" \ + --mode=block \ + sleep infinity & + echo $! > "$PID_FILE" + notify-send -t 2000 "Caffeine" "Idle inhibited" fi diff --git a/desktopenvs/niri/scripts/enroll-biometrics.sh b/desktopenvs/niri/scripts/enroll-biometrics.sh new file mode 120000 index 0000000..0c6ed96 --- /dev/null +++ b/desktopenvs/niri/scripts/enroll-biometrics.sh @@ -0,0 +1 @@ +../../hyprlua/scripts/enroll-biometrics.sh \ No newline at end of file diff --git a/desktopenvs/niri/scripts/presence-detect.sh b/desktopenvs/niri/scripts/presence-detect.sh new file mode 120000 index 0000000..472cbb6 --- /dev/null +++ b/desktopenvs/niri/scripts/presence-detect.sh @@ -0,0 +1 @@ +../../hyprlua/scripts/presence-detect.sh \ No newline at end of file diff --git a/desktopenvs/niri/scripts/python/python b/desktopenvs/niri/scripts/python similarity index 100% rename from desktopenvs/niri/scripts/python/python rename to desktopenvs/niri/scripts/python diff --git a/setup/modules/Desktop-Environments/hyprlua.sh b/setup/modules/Desktop-Environments/hyprlua.sh index b68e3a5..5d44571 100755 --- a/setup/modules/Desktop-Environments/hyprlua.sh +++ b/setup/modules/Desktop-Environments/hyprlua.sh @@ -26,7 +26,8 @@ sudo pacman -Syu --noconfirm --needed \ cool-retro-term qalculate-gtk iwd dbus \ thunar tumbler thunar-archive-plugin thunar-shares-plugin thunar-volman \ hyprpicker pcmanfm-qt udisks2 ly kew \ - hyprpolkitagent pavucontrol playerctl wf-recorder sound-theme-freedesktop + hyprpolkitagent pavucontrol playerctl wf-recorder sound-theme-freedesktop \ + python-opencv v4l-utils # 3. Enable essential services log "Enabling essential services..." @@ -102,11 +103,8 @@ for cfg in "${CONFIGS[@]}"; do rm -rf ~/.config/"$cfg" cp -r ~/Dotfiles/desktopenvs/hyprlua/"$cfg" ~/.config/ done - -# User-side Lua files live inside ~/.config/hypr/ so require() finds them -cp ~/Dotfiles/desktopenvs/hyprlua/hypr-usr/*.lua ~/.config/hypr/ -# wallpaper.conf stays at ~/.config/ for hyprpaper -cp ~/Dotfiles/desktopenvs/hyprlua/hypr-usr/wallpaper.conf ~/.config/ +# hypr/usr/ (device-specific) is already inside hypr/ and copied above. +# Customise ~/.config/hypr/usr/ per device after install. cp ~/Dotfiles/colors.conf ~/.config/colors.conf diff --git a/setup/modules/Desktop-Environments/niri.sh b/setup/modules/Desktop-Environments/niri.sh index f7a9d46..b3c2278 100755 --- a/setup/modules/Desktop-Environments/niri.sh +++ b/setup/modules/Desktop-Environments/niri.sh @@ -27,7 +27,7 @@ sudo pacman -Syu --noconfirm --needed \ thunar tumbler thunar-archive-plugin thunar-shares-plugin thunar-volman \ pcmanfm-qt udisks2 ly kew \ pavucontrol playerctl wf-recorder sound-theme-freedesktop \ - xinput jq + xinput jq python-opencv v4l-utils # 3. Enable essential services log "Enabling essential services..." diff --git a/sysupdate.sh b/sysupdate.sh index 945151b..615a18b 100755 --- a/sysupdate.sh +++ b/sysupdate.sh @@ -14,6 +14,7 @@ set -uo pipefail readonly STATE_FILE="/updatestate" readonly SCRIPT_PREFIX="/updatescript" readonly NEWS_FEED="https://archlinux.org/feeds/news/" +DOTFILES="${DOTFILES:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}" AI_MODE=false for _arg in "$@"; do [[ "$_arg" == "--AI" ]] && AI_MODE=true; done @@ -485,6 +486,169 @@ PROMPT_EOF fi } +# ═══════════════════════════════════════════════════════════════════════════════ +# CONFIG SYNC +# ═══════════════════════════════════════════════════════════════════════════════ + +# Items always skipped in the direct-copy fallback (managed by separate +# installers, or intentionally not auto-synced). hypr/usr/ (device-specific) +# is handled separately inside the copy loop rather than via this list. +declare -a _CFG_SKIP=( + config-updater # meta: the updater script itself + CRT eww eww-nobattery eww-touch greetd-tuigreet spicetify Vencord waybar +) + +# Resolve the dotfiles DE source directory for the current machine. +# Priority: installed updater.conf SOURCE_BASE → running session detection. +_de_source_dir() { + local conf="${XDG_CONFIG_HOME:-$HOME/.config}/config-updater/updater.conf" + if [[ -f "$conf" ]]; then + local src + src=$(awk -F'[[:space:]]*=[[:space:]]*' '/^SOURCE_BASE/{print $2; exit}' "$conf") + src="${src/#\~/$HOME}" + [[ -d "${src:-}" ]] && printf '%s' "$src" && return + fi + if pgrep -x Hyprland &>/dev/null || [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then + printf '%s' "$DOTFILES/desktopenvs/hyprlua" + elif pgrep -x niri &>/dev/null || [[ -n "${NIRI_SOCKET:-}" ]]; then + printf '%s' "$DOTFILES/desktopenvs/niri" + fi +} + +# Detect and offer to migrate the old hyprlua flat layout: +# old: ~/.config/hypr/monitors.lua etc. (pre-usr/ era) +# new: ~/.config/hypr/usr/monitors.lua +_migrate_hypr_usr() { + local target="${XDG_CONFIG_HOME:-$HOME/.config}" + local hypr_dir="$target/hypr" + local usr_dir="$hypr_dir/usr" + + # Already in the right place — nothing to do + [[ -d "$usr_dir" ]] && return 0 + + # ── Case 1: old flat layout — lua files at ~/.config/hypr/ root ────────── + if [[ -d "$hypr_dir" ]]; then + local -a old_files=() + for f in monitors.lua envvars.lua input.lua binds.lua windowrules.lua \ + autostart.lua input-device-exceptions.lua; do + [[ -f "$hypr_dir/$f" ]] && old_files+=("$f") + done + + if [[ ${#old_files[@]} -gt 0 ]]; then + echo + warn "Old hyprlua layout: device-specific files at ${BO}~/.config/hypr/${RS} root." + warn "New location: ${BO}~/.config/hypr/usr/${RS}" + echo + if ! ask "Migrate now?"; then + warn "Skipped — hyprland.lua will fail to start until migrated." + return 1 + fi + mkdir -p "$usr_dir" + for f in "${old_files[@]}"; do + mv "$hypr_dir/$f" "$usr_dir/" + ok "moved hypr/$f → hypr/usr/$f" + done + if [[ -f "$target/wallpaper.conf" && ! -f "$usr_dir/wallpaper.conf" ]]; then + mv "$target/wallpaper.conf" "$usr_dir/wallpaper.conf" + ok "moved wallpaper.conf → hypr/usr/wallpaper.conf" + fi + echo + ok "Migration complete — device configs now at ${BO}~/.config/hypr/usr/${RS}" + return 0 + fi + fi + + # ── Case 2: no usr/ and no old files — copy template from dotfiles ─────── + local de_dir; de_dir=$(_de_source_dir) + local src_usr="${de_dir:+$de_dir/hypr/usr}" + if [[ -n "${src_usr:-}" && -d "$src_usr" ]]; then + mkdir -p "$usr_dir" + cp -r "$src_usr/." "$usr_dir/" + ok "Copied ${BO}hypr/usr/${RS} from dotfiles ${DI}(no local config found)${RS}" + fi +} + +sync_configs() { + section "Config Sync" + + # ── Migration: old flat layout → hypr/usr/ ─────────────────────────────── + _migrate_hypr_usr + + # ── Preferred: use the installed update-configs.sh ─────────────────────── + local cfg_script + for cfg_script in "$HOME/update-configs.sh" \ + "${XDG_CONFIG_HOME:-$HOME/.config}/config-updater/update-configs.sh"; do + [[ -x "$cfg_script" ]] && break || cfg_script="" + done + + if [[ -n "${cfg_script:-}" ]]; then + log "Using installed config-updater (${DI}${cfg_script}${RS})" + echo + if "$cfg_script"; then + echo + ok "Config sync complete" + else + echo + warn "Config sync completed with errors" + fi + return + fi + + # ── Fallback: direct copy from dotfiles ────────────────────────────────── + local de_dir; de_dir=$(_de_source_dir) + if [[ -z "${de_dir:-}" || ! -d "$de_dir" ]]; then + warn "Cannot determine DE source directory — config sync skipped" + warn "Hint: deploy config-updater or ensure a supported DE session is running" + return + fi + + local target="${XDG_CONFIG_HOME:-$HOME/.config}" + log "Source → ${BO}${de_dir}${RS}" + log "Target → ${BO}${target}${RS}" + warn "${BO}hypr/usr/${RS} preserved (device-specific — customise manually)" + echo + + local synced=0 errs=0 + while IFS= read -r -d '' item; do + local name; name="$(basename "$item")" + local skip=false + for s in "${_CFG_SKIP[@]}"; do [[ "$name" == "$s" ]] && skip=true && break; done + [[ "$skip" == true ]] && continue + + if [[ "$name" == "hypr" ]]; then + # Copy hypr/ contents but preserve ~/.config/hypr/usr/ + mkdir -p "$target/hypr" + local hypr_ok=true + while IFS= read -r -d '' hitem; do + local hname; hname="$(basename "$hitem")" + [[ "$hname" == "usr" ]] && continue + rm -rf "${target}/hypr/${hname}" && cp -r "$hitem" "$target/hypr/" \ + || hypr_ok=false + done < <(find "$item" -maxdepth 1 -mindepth 1 -print0 | sort -z) + if $hypr_ok; then + ok "synced hypr ${DI}(usr/ preserved)${RS}" + (( synced++ )) || true + else + err "failed hypr" + (( errs++ )) || true + fi + elif rm -rf "${target:?}/${name}" && cp -r "$item" "$target/"; then + ok "synced ${name}" + (( synced++ )) || true + else + err "failed ${name}" + (( errs++ )) || true + fi + done < <(find "$de_dir" -maxdepth 1 -mindepth 1 -not -name '.*' -print0 | sort -z) + + echo + if (( errs > 0 )); then + warn "Config sync: ${synced} synced, ${errs} error(s)" + else + ok "Config sync: ${BO}${synced}${RS} config(s) synced" + fi +} + # ═══════════════════════════════════════════════════════════════════════════════ # MAIN # ═══════════════════════════════════════════════════════════════════════════════ @@ -526,6 +690,10 @@ main() { echo ok "${BO}System is up to date.${RS}" write_state + echo + if ask "Sync dotfiles configs to ~/.config?"; then + sync_configs + fi rm -f "$_NEWS_PY" exit 0 fi @@ -678,6 +846,11 @@ main() { fi echo + # ── Config sync ────────────────────────────────────────────────────────── + if ask "Sync dotfiles configs to ~/.config?"; then + sync_configs + fi + rm -f "$_NEWS_PY" } diff --git a/wgq-projekt.sh b/wgq-projekt.sh new file mode 100755 index 0000000..7592cc6 --- /dev/null +++ b/wgq-projekt.sh @@ -0,0 +1,4 @@ +#/bin/bash + +wg-quick up Projekt-new +sudo ip a add 10.0.10.173/24 dev Projekt-new