Merge branch 'main' of https://git.abdelbaki.eu/The_miro/Dotfiles
commit
325f5fcc1a
|
|
@ -3,6 +3,14 @@
|
|||
#
|
||||
# Config: ~/.config/config-updater/updater.conf
|
||||
# Manifest: ~/.config/config-updater/manifest
|
||||
#
|
||||
# Syntax in updater.conf:
|
||||
# config <name> [except <sub1> <sub2> ...]
|
||||
# copy SOURCE_BASE/<name> to ~/.config/<name>, optionally skipping subdirs
|
||||
# flat <name>
|
||||
# copy contents of SOURCE_BASE/<name> directly into ~/.config/
|
||||
# ignore <name>
|
||||
# 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)
|
||||
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" ]] || {
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@
|
|||
# (for installation-specific files that live at the ~/.config root)
|
||||
# ignore <name> present in source but intentionally not managed here
|
||||
|
||||
SOURCE_BASE = ~/Dotfiles/desktopenvs/hyprland
|
||||
SOURCE_BASE = ~/Dotfiles/desktopenvs/hyprlua
|
||||
|
||||
# ── deployed as ~/.config/<name> ─────────────────────────────────────────────
|
||||
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/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#}
|
||||
|
|
|
|||
|
|
@ -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 ---
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ general {
|
|||
}
|
||||
|
||||
auth {
|
||||
pam:module = sudo
|
||||
pam:module = hyprlock
|
||||
fingerprint:enabled = true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ wallpaper {
|
|||
}
|
||||
|
||||
# per-monitor state, written by ~/.config/scripts/wallpaper-picker
|
||||
source = ~/.config/wallpaper.conf
|
||||
source = ~/.config/hypr/usr/wallpaper.conf
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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"))
|
||||
|
|
@ -15,4 +15,4 @@ hl.config({
|
|||
},
|
||||
})
|
||||
|
||||
require("input-device-exceptions")
|
||||
require("usr.input-device-exceptions")
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
#!/bin/bash
|
||||
cat ~/Dotfiles/desktopenvs/hyprlua/hypr-usr/binds.lua | less
|
||||
cat ~/Dotfiles/desktopenvs/hyprlua/hypr/usr/binds.lua | less
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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=<id> 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
|
||||
|
|
@ -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))
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ─────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"; }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
../../hyprlua/scripts/enroll-biometrics.sh
|
||||
|
|
@ -0,0 +1 @@
|
|||
../../hyprlua/scripts/presence-detect.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
|
||||
|
||||
|
|
|
|||
|
|
@ -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..."
|
||||
|
|
|
|||
173
sysupdate.sh
173
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"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
#/bin/bash
|
||||
|
||||
wg-quick up Projekt-new
|
||||
sudo ip a add 10.0.10.173/24 dev Projekt-new
|
||||
Loading…
Reference in New Issue