diff --git a/hyprland/.config/hypr/env.lua b/hyprland/.config/hypr/env.lua index d107e6d..ca4f03d 100644 --- a/hyprland/.config/hypr/env.lua +++ b/hyprland/.config/hypr/env.lua @@ -1,3 +1,4 @@ -- See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Environment-variables/ hl.env("XCURSOR_SIZE", "24") hl.env("QT_QPA_PLATFORMTHEME", "qt5ct") -- change to qt6ct if you have that +hl.env("SUDO_ASKPASS", os.getenv("HOME") .. "/.local/bin/sudo-askpass.sh") diff --git a/hyprland/.config/hypr/exec.lua b/hyprland/.config/hypr/exec.lua index 23f6028..92927ca 100644 --- a/hyprland/.config/hypr/exec.lua +++ b/hyprland/.config/hypr/exec.lua @@ -1,11 +1,14 @@ -- Autostart. See https://wiki.hypr.land/Configuring/Basics/Autostart/ hl.on("hyprland.start", function() hl.exec_cmd("hyprctl setcursor catppuccin-mocha-dark-cursors 28 &") - hl.exec_cmd("systemctl --user start hyprpolkitagent &") + hl.exec_cmd("systemctl --user start plasma-polkit-agent &") hl.exec_cmd("waybar & mako & hyprpaper & hypridle & wluma & hyprshell run &") - hl.exec_cmd("systemctl --user start hypr-lid.service &") - hl.exec_cmd("blueman-applet &") hl.exec_cmd("1password --silent &") - hl.exec_cmd("op signin &") + hl.exec_cmd("systemctl --user start hyprland-session.target &") + hl.exec_cmd("blueman-applet &") hl.exec_cmd("sunsetr &") end) + +hl.on("hyprland.shutdown", function() + os.execute("systemctl --user stop hyprland-session.target && sleep 0.1") +end) diff --git a/hyprland/.config/hypr/hyprland.lua b/hyprland/.config/hypr/hyprland.lua index 5b43983..62cb745 100644 --- a/hyprland/.config/hypr/hyprland.lua +++ b/hyprland/.config/hypr/hyprland.lua @@ -67,7 +67,9 @@ hl.config({ }) hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } }) -hl.animation({ leaf = "borderangle", enabled = true, speed = 50, bezier = "linear", style = "loop" }) +-- borderangle disabled: the old "loop" style animated continuously, forcing +-- constant redraws and spiking idle CPU usage even with VFR enabled. +hl.animation({ leaf = "borderangle", enabled = false, speed = 50, bezier = "linear", style = "loop" }) hl.animation({ leaf = "workspaces", enabled = true, speed = 0.5, bezier = "default" }) hl.animation({ leaf = "windows", enabled = false }) hl.animation({ leaf = "fade", enabled = false }) @@ -83,9 +85,23 @@ hl.config({ -- new_is_master = true -- } +-- CPU/power efficiency. See https://wiki.hypr.land/Configuring/Variables/#misc hl.config({ misc = { force_default_wallpaper = 0, -- Set to 0 or 1 to disable the anime mascot wallpapers middle_click_paste = false, + -- Don't wake/re-render just because the cursor crossed a monitor edge. + mouse_move_focuses_monitor = false, + -- Skip drawing the Hyprland logo in the corner of empty workspaces. + disable_hyprland_logo = true, + }, +}) + +-- Variable framerate: only redraw when the scene actually changes instead of +-- running at a fixed rate. This is the single biggest idle-CPU saver; it is +-- true by default, but set explicitly (note it lives under `debug:`). +hl.config({ + debug = { + vfr = true, }, }) diff --git a/hyprland/.config/systemd/user/hyprland-session.target b/hyprland/.config/systemd/user/hyprland-session.target new file mode 100644 index 0000000..be479fb --- /dev/null +++ b/hyprland/.config/systemd/user/hyprland-session.target @@ -0,0 +1,6 @@ +[Unit] +Description=Hyprland session +BindsTo=graphical-session.target +Wants=graphical-session-pre.target +After=graphical-session-pre.target +PropagatesStopTo=graphical-session.target diff --git a/hyprland/.local/bin/btop.sh b/hyprland/.local/bin/btop.sh new file mode 100755 index 0000000..59379f4 --- /dev/null +++ b/hyprland/.local/bin/btop.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Open btop fullscreen in a ghostty window; focus it if it is already open. +# Intended as the on-click action for the waybar cpu/memory modules. +set -uo pipefail + +if hyprctl clients -j 2>/dev/null | jq -e 'any(.[]; ((.title // "") + " " + (.class // "")) | ascii_downcase | contains("btop"))' >/dev/null 2>&1; then + hyprctl dispatch 'hl.dsp.focus({ window = "title:btop" })' 2>/dev/null || true + exit 0 +fi + +ghostty -e btop & +sleep 0.5 +hyprctl dispatch 'hl.dsp.window.fullscreen({ action = "set" })' 2>/dev/null || true diff --git a/hyprland/.local/bin/sudo-askpass.sh b/hyprland/.local/bin/sudo-askpass.sh new file mode 100755 index 0000000..8185a96 --- /dev/null +++ b/hyprland/.local/bin/sudo-askpass.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# sudo-askpass: GUI password prompt for sudo via $SUDO_ASKPASS. +# +# sudo calls the program pointed to by $SUDO_ASKPASS when it cannot prompt on a +# terminal (or with `sudo -A`) and reads the password from its stdout. zenity's +# --password dialog does exactly that. If the dialog is cancelled or times out +# zenity exits non-zero and sudo aborts. + +set -uo pipefail + +exec zenity --title="sudo authentication" \ + --password \ + --text="Enter password for $(id -un):" \ + --timeout=120 2>/dev/null diff --git a/mako/.config/mako/config b/mako/.config/mako/config index 75ec128..5d06d52 100644 --- a/mako/.config/mako/config +++ b/mako/.config/mako/config @@ -1,5 +1,35 @@ +# https://github.com/emersion/mako/wiki/Configuration + max-history=10 # Style default-timeout=5000 layer=overlay +anchor=top-right +border-radius=10 +font="MesloLGS Nerd Font Mono Bold 12" +icons=1 +max-icon-size=48 + +# Catppuccin Mocha palette (matches waybar mocha.css) +background-color=#1e1e2e +text-color=#cdd6f4 +border-color=#cba6f7 +progress-color=#a6e3a1 + +# Grouping +group-by=app-name +sort=-time + +[grouped] +format=(%g) %s\n%b + +[urgency=low] +border-color=#94e2d5 +default-timeout=8000 + +[urgency=normal] + +[urgency=critical] +border-color=#f38ba8 +default-timeout=0 diff --git a/systemd/.config/systemd/user/cloudflared-k8s.service b/systemd/.config/systemd/user/cloudflared-k8s.service index 0d1931b..c9f1184 100644 --- a/systemd/.config/systemd/user/cloudflared-k8s.service +++ b/systemd/.config/systemd/user/cloudflared-k8s.service @@ -2,22 +2,29 @@ # systemctl --user daemon-reload # systemctl --user enable --now cloudflared-k8s.service # systemctl --user status cloudflared-k8s.service +# +# The Cloudflare Access service token for k8s-api lives in the "Homelab" +# 1Password vault (item "K8s API Service Token", section "Service Token") and +# is injected at runtime via `op run` -- the secret is never written to disk. [Unit] Description=Cloudflare Tunnel to K8s API -After=network-online.target graphical-session.target -BindsTo=graphical-session.target +After=network-online.target graphical-session.target op-cli-signin.service Wants=network-online.target systemd-networkd-wait-online.service +BindsTo=graphical-session.target StartLimitIntervalSec=500 StartLimitBurst=5 [Service] PassEnvironment=DISPLAY WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR -ExecStart=uwsm app -- /usr/bin/cloudflared access tcp --hostname k8s.samoneal.io --url localhost:6443 +Environment="OP_BIOMETRIC_UNLOCK_ENABLED=true" +Environment="CF_ACCESS_CLIENT_ID=op://Homelab/K8s API Service Token/Service Token/client-id" +Environment="CF_ACCESS_CLIENT_SECRET=op://Homelab/K8s API Service Token/Service Token/client-secret" +ExecStart=/usr/bin/op run -- /usr/bin/cloudflared access tcp --hostname k8s.samoneal.io --url localhost:6443 Restart=on-failure RestartSec=60 [Install] -WantedBy=default.target +WantedBy=graphical-session.target diff --git a/systemd/.config/systemd/user/gdrive-rclone.service b/systemd/.config/systemd/user/gdrive-rclone.service index 0fc38b7..062de2e 100644 --- a/systemd/.config/systemd/user/gdrive-rclone.service +++ b/systemd/.config/systemd/user/gdrive-rclone.service @@ -1,8 +1,8 @@ [Unit] Description=Connect to GDrive via RClone After=network-online.target graphical-session.target +Wants=network-online.target BindsTo=graphical-session.target -Wants=network-online.target graphical-session.target [Service] PassEnvironment=DISPLAY WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR @@ -11,4 +11,4 @@ Restart=on-failure RestartSec=60 [Install] -WantedBy=default.target +WantedBy=graphical-session.target diff --git a/systemd/.config/systemd/user/mnt-nas-credentials.service b/systemd/.config/systemd/user/mnt-nas-credentials.service index 45a99ed..ce16755 100644 --- a/systemd/.config/systemd/user/mnt-nas-credentials.service +++ b/systemd/.config/systemd/user/mnt-nas-credentials.service @@ -1,17 +1,18 @@ [Unit] Description=Fetch NAS credentials from 1Password -After=op-cli-signin.service +After=graphical-session.target op-cli-signin.service +BindsTo=graphical-session.target [Service] Type=oneshot -Environment="DISPLAY=:0" -Environment="WAYLAND_DISPLAY=wayland-0" +PassEnvironment=DISPLAY WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR Environment="OP_BIOMETRIC_UNLOCK_ENABLED=true" -ExecStart=sudo -u sammieo -E /home/sammieo/.local/bin/fetch-nas-credentials.sh -ExecStartPost=sudo -u sammieo /usr/bin/systemctl start nas-mounts.target +Environment="SUDO_ASKPASS=/home/sammieo/.local/bin/sudo-askpass.sh" +ExecStart=sudo -A -u sammieo -E /home/sammieo/.local/bin/fetch-nas-credentials.sh +ExecStartPost=sudo -A -u sammieo /usr/bin/systemctl start nas-mounts.target RemainAfterExit=yes StandardOutput=journal StandardError=journal [Install] -WantedBy=default.target +WantedBy=graphical-session.target diff --git a/systemd/.config/systemd/user/op-cli-signin.service b/systemd/.config/systemd/user/op-cli-signin.service index 95e6948..61c1d66 100644 --- a/systemd/.config/systemd/user/op-cli-signin.service +++ b/systemd/.config/systemd/user/op-cli-signin.service @@ -1,13 +1,19 @@ [Unit] Description=1Password CLI SignIn After=graphical-session.target +BindsTo=graphical-session.target + +StartLimitIntervalSec=300 +StartLimitBurst=10 [Service] Type=oneshot Environment="DISPLAY=:0" Environment="WAYLAND_DISPLAY=wayland-0" Environment="OP_BIOMETRIC_UNLOCK_ENABLED=true" -ExecStart=/bin/sh op signin +ExecStart=/usr/bin/op signin +Restart=on-failure +RestartSec=5 [Install] -WantedBy=default.target +WantedBy=graphical-session.target diff --git a/waybar/.config/waybar/config.jsonc b/waybar/.config/waybar/config.jsonc index 885ff20..ff2a4de 100644 --- a/waybar/.config/waybar/config.jsonc +++ b/waybar/.config/waybar/config.jsonc @@ -2,7 +2,7 @@ "position": "top", "modules-left": ["hyprland/workspaces", "mpris"], "modules-center": ["hyprland/window"], - "modules-right": ["cpu", "memory", "bluetooth", "network", "pulseaudio", "battery", "clock", "power-profiles-daemon", "custom/power"], + "modules-right": ["cpu", "memory", "tray", "bluetooth", "network", "pulseaudio", "battery", "clock", "power-profiles-daemon", "custom/power"], "clock": { "format": "{:%a %d %H:%M}", "tooltip-format": "{:%Y %B}\n{calendar}" @@ -22,6 +22,24 @@ "format-icons": ["", "", "", "", ""], "tooltip-format": "{time}" }, + "cpu": { + "on-click": "~/.local/bin/btop.sh" + }, + "memory": { + "on-click": "~/.local/bin/btop.sh" + }, + "tray": { + "icon-size": 16, + "icon-theme": "Papirus-Dark", + "spacing": 8, + "tooltip": true, + "menu": { + "enabled": true, + "arrow": false, + "separator": true, + "size": 16 + } + }, "network": { "format-wifi": "{essid}", "format-ethernet": "󰤭 Disconnected", diff --git a/waybar/.config/waybar/style.css b/waybar/.config/waybar/style.css index 3b8b9ce..c3d9c2d 100644 --- a/waybar/.config/waybar/style.css +++ b/waybar/.config/waybar/style.css @@ -122,6 +122,20 @@ button:hover { border-bottom: 2px solid @mauve; } +#tray { + color: @lavender; +} + +#tray > .passive { + opacity: 0.7; +} + +#tray > .needs-attention { + background-color: rgba(0, 0, 0, 0.3); + color: @red; + border-bottom: 2px solid @red; +} + #custom-power { color: #ec9974; }