Amir Alexander Abdelbaki 2026-06-11 21:29:39 +02:00
commit 325f5fcc1a
36 changed files with 689 additions and 94 deletions

View File

@ -3,6 +3,14 @@
# #
# Config: ~/.config/config-updater/updater.conf # Config: ~/.config/config-updater/updater.conf
# Manifest: ~/.config/config-updater/manifest # 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 set -euo pipefail
@ -23,6 +31,7 @@ die() { err "$*"; exit 1; }
# ── parse updater.conf ──────────────────────────────────────────────────────── # ── parse updater.conf ────────────────────────────────────────────────────────
SOURCE_BASE="" SOURCE_BASE=""
declare -A ENTRY_TYPE # name → config | flat | ignore 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 while IFS= read -r line; do
line="${line%%#*}" line="${line%%#*}"
@ -36,10 +45,19 @@ while IFS= read -r line; do
continue continue
fi fi
read -r type name _rest <<< "$line" read -r type name rest <<< "$line"
[[ -z "${name:-}" ]] && continue [[ -z "${name:-}" ]] && continue
case "$type" in 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" ;; *) warn "Unknown type '$type' for '$name' — skipping" ;;
esac esac
done < "$CONF_FILE" done < "$CONF_FILE"
@ -91,11 +109,35 @@ for name in "${!ENTRY_TYPE[@]}"; do
continue continue
fi fi
excl="${ENTRY_EXCL[$name]:-}"
case "$type" in case "$type" in
config) config)
rm -rf "${TARGET:?}/${name}" if [[ -z "$excl" ]]; then
cp -r "$src" "$TARGET/$name" rm -rf "${TARGET:?}/${name}"
ok "config $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) flat)
[[ -d "$src" ]] || { [[ -d "$src" ]] || {

View File

@ -7,14 +7,14 @@
# (for installation-specific files that live at the ~/.config root) # (for installation-specific files that live at the ~/.config root)
# ignore <name> present in source but intentionally not managed here # ignore <name> present in source but intentionally not managed here
SOURCE_BASE = ~/Dotfiles/desktopenvs/hyprland SOURCE_BASE = ~/Dotfiles/desktopenvs/hyprlua
# ── deployed as ~/.config/<name> ───────────────────────────────────────────── # ── deployed as ~/.config/<name> ─────────────────────────────────────────────
config alacritty config alacritty
config btop config btop
config dunst config dunst
config gtk-3.0 config gtk-3.0
config hypr config hypr except usr
config kitty config kitty
config mimeapps.list config mimeapps.list
config nwg-dock-hyprland config nwg-dock-hyprland
@ -27,9 +27,6 @@ config walker
config wofi config wofi
config xfce4 config xfce4
# ── flat: directory contents copied directly into ~/.config/ ──────────────────
flat hypr-usr # installation-specific: binds, monitors, autostart, etc.
# ── intentionally not managed here ─────────────────────────────────────────── # ── intentionally not managed here ───────────────────────────────────────────
ignore config-updater # the updater itself ignore config-updater # the updater itself
ignore CRT # referenced from dotfiles path directly in binds.conf 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 spicetify # managed separately (spicetify handles its own config)
ignore Vencord # managed separately ignore Vencord # managed separately
ignore waybar # present but inactive; eww bar is used instead 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/

View File

@ -1,22 +1,19 @@
#source ~/.config/idle.conf
general { general {
lock_cmd = pidof hyprlock || hyprlock # avoid starting multiple hyprlock instances. lock_cmd = pidof hyprlock || hyprlock
before_sleep_cmd = loginctl lock-session # lock before suspend. before_sleep_cmd = loginctl lock-session
after_sleep_cmd = systemctl restart fprintd.service ;; hyprctl dispatch dpms on # to avoid having to press a key twice to turn on the display. # 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 { listener {
timeout = 120 timeout = 180 # 3 min — lock screen
on-timeout = loginctl lock-session # lock screen when timeout has passed on-timeout = loginctl lock-session
} }
listener { listener {
timeout = 600 #10min timeout = 600 # 10 min — suspend
on-timeout = systemctl suspend-then-hibernate # suspend pc on-timeout = systemctl suspend-then-hibernate
} }
#listener {
# timeout = 18000 #5h
# on-timeout = /usr/bin/reboot #reboot
#}

View File

@ -1,12 +1,12 @@
-- Hyprland Lua config — https://wiki.hypr.land/Configuring/Start/ -- 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("usr.monitors")
require("envvars") require("usr.envvars")
require("input") require("usr.input")
require("binds") require("usr.binds")
require("windowrules") require("usr.windowrules")
require("autostart") require("usr.autostart")
-------------------- --------------------
---- MY PROGRAMS --- ---- MY PROGRAMS ---

View File

@ -8,7 +8,7 @@ general {
} }
auth { auth {
pam:module = sudo pam:module = hyprlock
fingerprint:enabled = true fingerprint:enabled = true
} }

View File

@ -8,4 +8,4 @@ wallpaper {
} }
# per-monitor state, written by ~/.config/scripts/wallpaper-picker # per-monitor state, written by ~/.config/scripts/wallpaper-picker
source = ~/.config/wallpaper.conf source = ~/.config/hypr/usr/wallpaper.conf

View File

@ -20,4 +20,6 @@ hl.on("hyprland.start", function()
hl.exec_cmd("blueman-applet") hl.exec_cmd("blueman-applet")
hl.exec_cmd("blueman-tray") hl.exec_cmd("blueman-tray")
hl.exec_cmd("hypridle") hl.exec_cmd("hypridle")
hl.exec_cmd("bash ~/Dotfiles/desktopenvs/hyprlua/scripts/presence-detect.sh")
hl.exec_cmd("chamel")
end) end)

View File

@ -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 .. " + 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 .. " + 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 .. " + 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")) 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 + C", hl.dsp.exec_cmd("dunstctl close-all"))
hl.bind(mainMod .. " + CTRL + G", hl.dsp.exec_cmd("~/.config/scripts/onscreenkb.sh")) 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 + 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 .. " + 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")) hl.bind(mainMod .. " + CTRL + E", hl.dsp.exec_cmd("~/.config/scripts/screenrotationwcw.sh"))

View File

@ -15,4 +15,4 @@ hl.config({
}, },
}) })
require("input-device-exceptions") require("usr.input-device-exceptions")

View File

@ -1,17 +1,17 @@
#!/bin/bash #!/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]" ) if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
#echo $statecon kill "$(cat "$PID_FILE")"
if [ "$statecon" != "" ]; then rm -f "$PID_FILE"
notify-send -t 2000 "Caffeine" "Idle inhibit OFF"
notify-send -t 1000 "caffeine mode on"
killall hypridle
else else
systemd-inhibit --what=idle:sleep \
notify-send -t 1000 "caffeine mode off" --who="caffeine" \
hypridle & --why="Caffeine mode active" \
--mode=block \
sleep infinity &
echo $! > "$PID_FILE"
notify-send -t 2000 "Caffeine" "Idle inhibited"
fi fi

View File

@ -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

View File

@ -1,24 +1,41 @@
wg-quick up wg0 poweroff
wg-quick down wg0 reboot
hyprshutdown hyprshutdown
hyprshutdown && reboot hyprshutdown && reboot
poweroff
hyprctl kill
reboot
systemctl soft-reboot systemctl soft-reboot
systemctl suspend-then-hibernate systemctl suspend-then-hibernate
systemctl hibernate systemctl hibernate
systemctl sleep systemctl sleep
systemctl restart fprintd.service
systemctl hybrid-sleep systemctl hybrid-sleep
hyprctl dispatch exit hyprctl dispatch exit
hyprlock hyprlock
hyprctl kill
systemctl restart fprintd.service
wg-quick up wg0
wg-quick down wg0
hyprctl reload 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 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 "#00BFFF"
chamel stroke-color "#000000" chamel stroke-color "#000000"
chamel stroke-color "#FFFFFF" chamel stroke-color "#FFFFFF"

View File

@ -1,3 +1,3 @@
#!/bin/bash #!/bin/bash
cat ~/Dotfiles/desktopenvs/hyprlua/hypr-usr/binds.lua | less cat ~/Dotfiles/desktopenvs/hyprlua/hypr/usr/binds.lua | less

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/bin/bash
hyprpaper -c ~/.config/wallpaper.conf hyprpaper -c ~/.config/hypr/usr/wallpaper.conf
~/Dotfiles/desktopenvs/hyprland/scripts/ewwstart.sh ~/Dotfiles/desktopenvs/hyprlua/scripts/ewwstart.sh

View File

@ -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

View File

@ -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))

View File

@ -6,7 +6,7 @@ set -u
VERBOSE=0 VERBOSE=0
DIR="" DIR=""
STATE_FILE="${HOME}/.config/wallpaper.conf" STATE_FILE="${HOME}/.config/hypr/usr/wallpaper.conf"
HYPRLOCK_BG_FILE="${HOME}/.config/hypr/hyprlock-backgrounds.conf" HYPRLOCK_BG_FILE="${HOME}/.config/hypr/hyprlock-backgrounds.conf"
FIT_MODE="cover" FIT_MODE="cover"

View File

@ -99,8 +99,8 @@ done
# ── swap hypr-usr (the part that actually differs) ──────────────────────────── # ── swap hypr-usr (the part that actually differs) ────────────────────────────
# hyprland: *.conf files live at ~/.config/ root (sourced by hyprland.conf) # hyprland: *.conf files live at ~/.config/ root (sourced by hyprland.conf)
# hyprlua: *.lua files live at ~/.config/hypr/ (required by hyprland.lua) # hyprlua: device-specific files live at ~/.config/hypr/usr/
# wallpaper.conf stays at ~/.config/ root (hyprpaper still reads it) # (required via require("usr.*") in hyprland.lua)
printf '\n' printf '\n'
@ -108,20 +108,17 @@ printf '\n'
for f in "${HYPR_USR_CONFS[@]}"; do for f in "${HYPR_USR_CONFS[@]}"; do
[[ -f "$TARGET/$f" ]] && run "rm '$TARGET/$f'" && note "removed ~/.config/$f" [[ -f "$TARGET/$f" ]] && run "rm '$TARGET/$f'" && note "removed ~/.config/$f"
done 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/ # Deploy device-specific files into ~/.config/hypr/usr/
if [[ -d "$SRC/hypr-usr" ]]; then if [[ -d "$SRC/hypr/usr" ]]; then
run "mkdir -p '$TARGET/hypr'" run "mkdir -p '$TARGET/hypr/usr'"
for lua in "$SRC/hypr-usr"/*.lua; do for f in "$SRC/hypr/usr"/*; do
[[ -e "$lua" ]] || continue [[ -e "$f" ]] || continue
run "cp '$lua' '$TARGET/hypr/'" run "cp '$f' '$TARGET/hypr/usr/'"
ok "hypr-usr $(basename "$lua") → ~/.config/hypr/" ok "hypr/usr $(basename "$f") → ~/.config/hypr/usr/"
done 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 fi
# ── update config-updater ───────────────────────────────────────────────────── # ── update config-updater ─────────────────────────────────────────────────────

View File

@ -9,4 +9,5 @@ spawn-at-startup "bash" "-c" "gammastep -O 4500"
spawn-at-startup "nm-applet" spawn-at-startup "nm-applet"
spawn-at-startup "dunst" 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" "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" spawn-at-startup "blueman-applet"

View File

@ -204,6 +204,7 @@ binds {
Mod+Ctrl+C { spawn "dunstctl" "close-all"; } Mod+Ctrl+C { spawn "dunstctl" "close-all"; }
Mod+Ctrl+G { spawn "bash" "-c" "~/.config/scripts/onscreenkb.sh"; } Mod+Ctrl+G { spawn "bash" "-c" "~/.config/scripts/onscreenkb.sh"; }
Mod+Shift+C { spawn "bash" "-c" "~/.config/scripts/caffeine.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+Shift+X { spawn "bash" "-c" "~/.config/scripts/niri-toggle-touchpad.sh"; }
Mod+Ctrl+E { spawn "bash" "-c" "~/.config/scripts/screenrotationwcw.sh"; } Mod+Ctrl+E { spawn "bash" "-c" "~/.config/scripts/screenrotationwcw.sh"; }

View File

@ -1,14 +1,17 @@
#!/bin/bash #!/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 [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
kill "$(cat "$PID_FILE")"
if [ "$statecon" != "" ]; then rm -f "$PID_FILE"
notify-send -t 1000 "Caffeine mode ON — idle inhibited" notify-send -t 2000 "Caffeine" "Idle inhibit OFF"
killall swayidle
else else
notify-send -t 1000 "Caffeine mode OFF — idle active" systemd-inhibit --what=idle:sleep \
swayidle -w \ --who="caffeine" \
timeout 300 'swaylock -f' \ --why="Caffeine mode active" \
timeout 600 'systemctl suspend' \ --mode=block \
before-sleep 'swaylock -f' & sleep infinity &
echo $! > "$PID_FILE"
notify-send -t 2000 "Caffeine" "Idle inhibited"
fi fi

View File

@ -0,0 +1 @@
../../hyprlua/scripts/enroll-biometrics.sh

View File

@ -0,0 +1 @@
../../hyprlua/scripts/presence-detect.sh

View File

@ -26,7 +26,8 @@ sudo pacman -Syu --noconfirm --needed \
cool-retro-term qalculate-gtk iwd dbus \ cool-retro-term qalculate-gtk iwd dbus \
thunar tumbler thunar-archive-plugin thunar-shares-plugin thunar-volman \ thunar tumbler thunar-archive-plugin thunar-shares-plugin thunar-volman \
hyprpicker pcmanfm-qt udisks2 ly kew \ 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 # 3. Enable essential services
log "Enabling essential services..." log "Enabling essential services..."
@ -102,11 +103,8 @@ for cfg in "${CONFIGS[@]}"; do
rm -rf ~/.config/"$cfg" rm -rf ~/.config/"$cfg"
cp -r ~/Dotfiles/desktopenvs/hyprlua/"$cfg" ~/.config/ cp -r ~/Dotfiles/desktopenvs/hyprlua/"$cfg" ~/.config/
done done
# hypr/usr/ (device-specific) is already inside hypr/ and copied above.
# User-side Lua files live inside ~/.config/hypr/ so require() finds them # Customise ~/.config/hypr/usr/ per device after install.
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/
cp ~/Dotfiles/colors.conf ~/.config/colors.conf cp ~/Dotfiles/colors.conf ~/.config/colors.conf

View File

@ -27,7 +27,7 @@ sudo pacman -Syu --noconfirm --needed \
thunar tumbler thunar-archive-plugin thunar-shares-plugin thunar-volman \ thunar tumbler thunar-archive-plugin thunar-shares-plugin thunar-volman \
pcmanfm-qt udisks2 ly kew \ pcmanfm-qt udisks2 ly kew \
pavucontrol playerctl wf-recorder sound-theme-freedesktop \ pavucontrol playerctl wf-recorder sound-theme-freedesktop \
xinput jq xinput jq python-opencv v4l-utils
# 3. Enable essential services # 3. Enable essential services
log "Enabling essential services..." log "Enabling essential services..."

View File

@ -14,6 +14,7 @@ set -uo pipefail
readonly STATE_FILE="/updatestate" readonly STATE_FILE="/updatestate"
readonly SCRIPT_PREFIX="/updatescript" readonly SCRIPT_PREFIX="/updatescript"
readonly NEWS_FEED="https://archlinux.org/feeds/news/" readonly NEWS_FEED="https://archlinux.org/feeds/news/"
DOTFILES="${DOTFILES:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
AI_MODE=false AI_MODE=false
for _arg in "$@"; do [[ "$_arg" == "--AI" ]] && AI_MODE=true; done for _arg in "$@"; do [[ "$_arg" == "--AI" ]] && AI_MODE=true; done
@ -485,6 +486,169 @@ PROMPT_EOF
fi 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 # MAIN
# ═══════════════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════════════
@ -526,6 +690,10 @@ main() {
echo echo
ok "${BO}System is up to date.${RS}" ok "${BO}System is up to date.${RS}"
write_state write_state
echo
if ask "Sync dotfiles configs to ~/.config?"; then
sync_configs
fi
rm -f "$_NEWS_PY" rm -f "$_NEWS_PY"
exit 0 exit 0
fi fi
@ -678,6 +846,11 @@ main() {
fi fi
echo echo
# ── Config sync ──────────────────────────────────────────────────────────
if ask "Sync dotfiles configs to ~/.config?"; then
sync_configs
fi
rm -f "$_NEWS_PY" rm -f "$_NEWS_PY"
} }

4
wgq-projekt.sh Executable file
View File

@ -0,0 +1,4 @@
#/bin/bash
wg-quick up Projekt-new
sudo ip a add 10.0.10.173/24 dev Projekt-new