mirror of
https://github.com/Nexoniarz/nexoniarz-config-nixos.git
synced 2026-11-03 04:24:27 +00:00
Switch desktop environment from Hyprland to Budgie; app/audio overhaul
Hyprland problems prompted a full switch to Budgie (X11, lightdm). Removes every Hyprland/Quickshell dotfile, theming script, and hypr-specific module (theme.nix, the whole scripts/ theming pipeline, hyprland.conf/hyprpaper.conf, the Quickshell shell QML, kitty/mako/ rofi configs) in favor of Budgie's own panel/session/theming and a GSettings-driven icon/cursor override. App changes: - Konsole/Dolphin replace kitty/Thunar as the default terminal and file manager; dropped xterm too (services.xserver.excludePackages) - Added Okular (PDF/document viewer), KCalc (calculator), Elisa (audio player) -- nothing filled those roles before - Added OBS Studio Audio/voice: - EasyEffects for real-time mic/output effects (creates its own selectable "Easy Effects Source" virtual mic automatically) - A standalone "Virtual Microphone" PipeWire device (loopback module, services.pipewire.extraConfig), fully independent of the real mic, for routing EasyEffects/Soundux output separately from what Discord etc. use by default - qpwgraph for manual patchbay routing into the above - Soundux (soundboard) installed via Flatpak -- no longer packaged in this nixpkgs channel default-apps.nix: Okular wired into mimeapps.list alongside the existing Gwenview/Ark defaults; removed the Thunar-specific "Open Terminal Here" custom-action tmpfiles rule (dead now that Thunar's gone -- Dolphin gets the same via its built-in konsolepart terminal panel for free).
This commit is contained in:
parent
aa89dd729c
commit
d332ed2ef6
45 changed files with 105 additions and 7711 deletions
|
|
@ -1,41 +1,17 @@
|
|||
{ config, pkgs, ... }:
|
||||
|
||||
let
|
||||
# hyprpolkitagent only ships its binary under libexec/, not bin/, so it
|
||||
# never lands on $PATH — wrap it so `exec-once = hyprpolkitagent` in
|
||||
# hyprland.conf can find it.
|
||||
hyprpolkitagentBin = pkgs.writeShellScriptBin "hyprpolkitagent"
|
||||
"exec ${pkgs.hyprpolkitagent}/libexec/hyprpolkitagent";
|
||||
in
|
||||
{
|
||||
# Every installed package — GUI apps, CLI tools, and dev toolchains alike.
|
||||
# If it's "install X", it goes here.
|
||||
users.users."nexoniarz".packages = with pkgs; [
|
||||
# Hyprland desktop toolchain (Wayland-native, lightweight, squared theme)
|
||||
kitty # Terminal
|
||||
thunar # File manager
|
||||
thunar-archive-plugin
|
||||
thunar-volman
|
||||
tumbler # Thumbnails (paired with services.tumbler.enable)
|
||||
rofi # Launcher (Wayland support built in since 2.0)
|
||||
quickshell # Bar / UI shell
|
||||
tumbler # Thumbnails (paired with services.tumbler.enable; also used by Nemo)
|
||||
gnome-screenshot # Screenshot capture (X11)
|
||||
imv # Image viewer
|
||||
mako # Notification daemon
|
||||
grim # Screenshot capture
|
||||
slurp # Screenshot region select
|
||||
wl-clipboard # Wayland clipboard
|
||||
hyprpolkitagentBin # Polkit authentication agent (wrapped, see `let` above)
|
||||
hyprlock # Screen locker (power menu)
|
||||
brightnessctl # Backlight brightness (laptop panels)
|
||||
ddcutil # DDC/CI brightness (external monitors)
|
||||
lm_sensors # sensors-detect helps ddcutil find I2C buses
|
||||
hyprpaper # Wallpaper daemon, driven by the Quickshell picker (static images)
|
||||
mpvpaper # Wallpaper daemon for video-loop (animated) wallpapers
|
||||
networkmanager # nmcli, for the Wi-Fi hotspot toggle
|
||||
xdg-user-dirs # Resolves localized folder names (Obrazy, not Pictures, on pl_PL)
|
||||
bibata-cursors # Cursor theme(s) for the cursor picker
|
||||
xcur2png # Renders Xcursor files to PNG for the cursor picker's previews
|
||||
imagemagick # `identify`, used to pick the right cursor-preview size
|
||||
playerctl # MPRIS media control, for the function-key media binds
|
||||
|
||||
# Apps
|
||||
|
|
@ -48,6 +24,14 @@ in
|
|||
vlc
|
||||
filezilla
|
||||
chromium
|
||||
obs-studio
|
||||
easyeffects # Real-time mic/output effects (pitch, EQ, ...) — auto-creates a
|
||||
# selectable "Easy Effects Source" virtual mic once an Input effect
|
||||
# is added, no manual PipeWire routing needed
|
||||
qpwgraph # Patchbay GUI — wires EasyEffects'/Soundux's output into the
|
||||
# standalone "Virtual Microphone Sink" (see system.nix)
|
||||
# Soundux (soundboard) isn't packaged in this nixpkgs channel anymore —
|
||||
# installed via Flatpak instead (io.github.Soundux).
|
||||
|
||||
# Games
|
||||
prismlauncher
|
||||
|
|
@ -79,6 +63,13 @@ in
|
|||
unzip
|
||||
unrar
|
||||
kdePackages.gwenview
|
||||
kdePackages. kate
|
||||
kdePackages.kate
|
||||
kdePackages.qqc2-desktop-style
|
||||
linphonePackages.linphone-desktop
|
||||
kdePackages.kcalc # Calculator (nothing filled this role before)
|
||||
kdePackages.okular # Document/PDF viewer (nothing filled this role before)
|
||||
kdePackages.elisa # Audio player (vlc/ffmpeg cover video already; nothing dedicated for audio)
|
||||
kdePackages.konsole # Terminal (replaces kitty)
|
||||
kdePackages.dolphin # File manager (replaces Thunar, alongside Budgie's default Nemo)
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,24 +42,19 @@
|
|||
"application/x-7z-compressed" "application/vnd.rar" "application/zip"
|
||||
"application/x-java-archive" "application/x-arj"
|
||||
];
|
||||
# Taken from Okular's own .desktop file, same as the other two lists.
|
||||
okularMimes = [
|
||||
"application/pdf" "application/x-pdf" "image/vnd.djvu" "application/epub+zip"
|
||||
"application/x-fictionbook+xml" "application/x-mobipocket-ebook"
|
||||
"image/x-portable-document" "application/x-cbr" "application/x-cbz"
|
||||
"application/x-cbt" "application/x-cb7" "application/postscript"
|
||||
];
|
||||
line = desktop: mime: "${mime}=${desktop}";
|
||||
in
|
||||
''
|
||||
[Default Applications]
|
||||
${lib.concatMapStringsSep "\n" (line "org.kde.gwenview.desktop") gwenviewMimes}
|
||||
${lib.concatMapStringsSep "\n" (line "org.kde.ark.desktop") arkMimes}
|
||||
${lib.concatMapStringsSep "\n" (line "org.kde.okular.desktop") okularMimes}
|
||||
'';
|
||||
|
||||
# Thunar's own "Open Terminal Here" custom action (Edit > Configure
|
||||
# Custom Actions) shipped pointing at `exo-open --launch
|
||||
# TerminalEmulator` — exo isn't installed on this system at all (no
|
||||
# XFCE session, so its helpers.rc mechanism has nothing to resolve
|
||||
# against), so the action silently did nothing. `f`, not `w`: only
|
||||
# creates this baseline for a fresh account — once it exists, further
|
||||
# edits made through Thunar's own UI (adding more custom actions) are
|
||||
# never overwritten by a rebuild.
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /home/nexoniarz/.config/Thunar 0755 nexoniarz users -"
|
||||
''f /home/nexoniarz/.config/Thunar/uca.xml 0644 nexoniarz users - <?xml version="1.0" encoding="UTF-8"?><actions><action><icon>utilities-terminal</icon><name>Open Terminal Here</name><submenu></submenu><unique-id>1788014825097143-1</unique-id><command>kitty --working-directory %f</command><description>Open a terminal in this folder</description><range></range><patterns>*</patterns><startup-notify/><directories/></action></actions>''
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,101 +1,41 @@
|
|||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
# --- Hyprland: Wayland compositor ---
|
||||
programs.hyprland = {
|
||||
enable = true;
|
||||
xwayland.enable = true;
|
||||
};
|
||||
|
||||
# --- greetd + tuigreet: minimal TUI login manager, launches Hyprland directly ---
|
||||
services.greetd = {
|
||||
enable = true;
|
||||
settings.default_session = {
|
||||
# start-hyprland, not the raw Hyprland binary: it's Hyprland's own
|
||||
# watchdog-wrapped launcher (ships alongside Hyprland in the same
|
||||
# package) that also auto-restarts the compositor if it hangs.
|
||||
# Launching the raw binary directly works but logs a startup warning
|
||||
# ("Hyprland is being launched without start-hyprland. This is highly
|
||||
# advised against.") and skips that recovery.
|
||||
command = "${pkgs.tuigreet}/bin/tuigreet --time --remember --remember-session --cmd start-hyprland";
|
||||
user = "greeter";
|
||||
};
|
||||
};
|
||||
|
||||
# --- XDG portals: screen share, file pickers, etc. ---
|
||||
xdg.portal = {
|
||||
enable = true;
|
||||
extraPortals = [ pkgs.xdg-desktop-portal-hyprland pkgs.xdg-desktop-portal-gtk ];
|
||||
config.common.default = [ "hyprland" "gtk" ];
|
||||
};
|
||||
# --- Budgie: X11 desktop environment, lightdm login manager ---
|
||||
services.xserver.enable = true;
|
||||
services.xserver.displayManager.lightdm.enable = true;
|
||||
services.desktopManager.budgie.enable = true;
|
||||
|
||||
security.polkit.enable = true;
|
||||
|
||||
# Keyboard layout for console/XWayland/greetd; Hyprland's own input block
|
||||
# in hyprland/hyprland.conf mirrors this.
|
||||
# Keyboard layout for console/X11/lightdm
|
||||
services.xserver.xkb = {
|
||||
layout = "pl";
|
||||
variant = "";
|
||||
};
|
||||
|
||||
# Thunar: thumbnails, trash/volume mount integration, settings storage
|
||||
# Thumbnails + trash/volume mount integration for Dolphin and Budgie's
|
||||
# default Nemo file manager alike.
|
||||
services.tumbler.enable = true;
|
||||
services.gvfs.enable = true;
|
||||
programs.xfconf.enable = true;
|
||||
|
||||
# Native Wayland backend for Electron/Chromium-based apps
|
||||
environment.sessionVariables.NIXOS_OZONE_WL = "1";
|
||||
# Drop the xterm fallback terminal xserver.nix installs unconditionally —
|
||||
# Konsole is the real terminal here.
|
||||
services.xserver.excludePackages = [ pkgs.xterm ];
|
||||
|
||||
# --- Dotfiles: symlink tracked configs into the user's home ---
|
||||
# Editing /etc/nixos/hyprland or /etc/nixos/dotfiles takes effect on the
|
||||
# next reload of the respective app (no rebuild needed for config-only
|
||||
# tweaks; a rebuild re-runs the symlink if the target path itself changes).
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /home/nexoniarz/.config/hypr 0755 nexoniarz users -"
|
||||
"L+ /home/nexoniarz/.config/hypr/hyprland.conf - - - - /etc/nixos/hyprland/hyprland.conf"
|
||||
"L+ /home/nexoniarz/.config/hypr/hyprpaper.conf - - - - /etc/nixos/hyprland/hyprpaper.conf"
|
||||
|
||||
"d /home/nexoniarz/.config/kitty 0755 nexoniarz users -"
|
||||
"L+ /home/nexoniarz/.config/kitty/kitty.conf - - - - /etc/nixos/dotfiles/kitty/kitty.conf"
|
||||
# Colors set live via the Quickshell Theme picker — see kitty.conf's
|
||||
# `include theme.conf`. Empty by default; kitty-theme-set writes to it.
|
||||
"f /home/nexoniarz/.config/kitty/theme.conf 0644 nexoniarz users - -"
|
||||
|
||||
"d /home/nexoniarz/.config/rofi 0755 nexoniarz users -"
|
||||
"L+ /home/nexoniarz/.config/rofi/config.rasi - - - - /etc/nixos/dotfiles/rofi/config.rasi"
|
||||
# Colors set live via the Quickshell Theme picker — see config.rasi's
|
||||
# `@import "theme.rasi"`. Empty by default; rofi-theme-set writes to it.
|
||||
"f /home/nexoniarz/.config/rofi/theme.rasi 0644 nexoniarz users - -"
|
||||
|
||||
"d /home/nexoniarz/.config/mako 0755 nexoniarz users -"
|
||||
"L+ /home/nexoniarz/.config/mako/config - - - - /etc/nixos/dotfiles/mako/config"
|
||||
|
||||
# Quickshell is multi-file with relative imports, so the whole directory
|
||||
# is symlinked rather than a single file.
|
||||
"L+ /home/nexoniarz/.config/quickshell - - - - /etc/nixos/dotfiles/quickshell"
|
||||
|
||||
# $HOME/Wallpapers, not $HOME/Pictures/Wallpapers — this system's locale
|
||||
# (pl_PL) means the real XDG Pictures dir is ~/Obrazy, not ~/Pictures.
|
||||
# Living directly under $HOME sidesteps that entirely.
|
||||
"d /home/nexoniarz/Wallpapers 0755 nexoniarz users -"
|
||||
"d /home/nexoniarz/Screenshots 0755 nexoniarz users -"
|
||||
|
||||
# User-editable cursor theme choice (set live via the Quickshell cursor
|
||||
# picker) — created empty if missing, never overwritten if it already
|
||||
# has content, since it's runtime state rather than tracked config.
|
||||
"f /home/nexoniarz/.config/hypr/cursor.conf 0644 nexoniarz users - -"
|
||||
|
||||
# Same deal for display settings (mode/scale/transform per monitor,
|
||||
# set via the Quickshell display picker) — without this file backing
|
||||
# them, they only ever applied live via hyprctl and got silently
|
||||
# reset by hyprland.conf's generic fallback monitor rule any time
|
||||
# Hyprland re-evaluated monitor state.
|
||||
"f /home/nexoniarz/.config/hypr/display.conf 0644 nexoniarz users - -"
|
||||
|
||||
# Same deal for window border colors (accent/border, set live via the
|
||||
# Quickshell Theme picker) — without this file, they only ever applied
|
||||
# via hyprctl and would fall back to hyprland.conf's own general{}
|
||||
# defaults any time Hyprland re-evaluated its config.
|
||||
"f /home/nexoniarz/.config/hypr/theme.conf 0644 nexoniarz users - -"
|
||||
# GTK/Qt themselves stay at their stock defaults (no forced dark mode, no
|
||||
# custom CSS) — only the icon and cursor theme are picked explicitly, via
|
||||
# Budgie's own GSettings-override hook rather than a custom script.
|
||||
# (extraGSettingsOverridePackages is for packages that ship their own
|
||||
# gsettings-schema overrides, e.g. plugins — not theme packages, which
|
||||
# just need to be on the system path for their icons/cursors to resolve.)
|
||||
environment.systemPackages = [
|
||||
pkgs.gruvbox-plus-icons
|
||||
pkgs.bibata-cursors
|
||||
];
|
||||
services.desktopManager.budgie.extraGSettingsOverrides = ''
|
||||
[org.gnome.desktop.interface]
|
||||
icon-theme='Gruvbox-Plus-Dark'
|
||||
cursor-theme='Bibata-Modern-Classic'
|
||||
'';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,14 +10,9 @@
|
|||
#OpenTabletDriver
|
||||
hardware.opentabletdriver.enable = true;
|
||||
hardware.uinput.enable = true;
|
||||
boot.kernelModules = [ "uinput" "v4l2loopback" ];
|
||||
boot.kernelModules = [ "uinput" ];
|
||||
|
||||
# Virtual webcam device so ffmpeg (fed by the GoPro's stream) can appear
|
||||
# as a normal camera to apps like Discord. exclusive_caps=1 is required
|
||||
# for Chromium/WebRTC-based apps to recognize it as a real capture device.
|
||||
boot.extraModulePackages = [ config.boot.kernelPackages.v4l2loopback ];
|
||||
boot.extraModprobeConfig = ''
|
||||
options v4l2loopback devices=1 video_nr=10 card_label="GoPro Webcam" exclusive_caps=1
|
||||
# PS4/PS5 controllers (e.g. DualSense) connect over Bluetooth then
|
||||
# immediately disconnect — a well-known Linux Bluetooth stack issue
|
||||
# where many adapters' Enhanced Retransmission Mode implementation is
|
||||
|
|
|
|||
|
|
@ -5,21 +5,6 @@ let
|
|||
vaultToggle = pkgs.writeShellScriptBin "vault-toggle" (builtins.readFile ./scripts/vault-toggle.sh);
|
||||
stripMetadata = pkgs.writeShellScriptBin "strip-metadata" (builtins.readFile ./scripts/strip-metadata.sh);
|
||||
brightnessCtl = pkgs.writeShellScriptBin "brightness-ctl" (builtins.readFile ./scripts/brightness-ctl.sh);
|
||||
showDesktopToggle = pkgs.writeShellScriptBin "show-desktop-toggle" (builtins.readFile ./scripts/show-desktop-toggle.sh);
|
||||
wallpaperSet = pkgs.writeShellScriptBin "wallpaper-set" (builtins.readFile ./scripts/wallpaper-set.sh);
|
||||
wallpaperDelete = pkgs.writeShellScriptBin "wallpaper-delete" (builtins.readFile ./scripts/wallpaper-delete.sh);
|
||||
wallpaperRestore = pkgs.writeShellScriptBin "wallpaper-restore" (builtins.readFile ./scripts/wallpaper-restore.sh);
|
||||
wallpaperThumbs = pkgs.writeShellScriptBin "wallpaper-thumbs" (builtins.readFile ./scripts/wallpaper-thumbs.sh);
|
||||
displaySet = pkgs.writeShellScriptBin "display-set" (builtins.readFile ./scripts/display-set.sh);
|
||||
hyprThemeSet = pkgs.writeShellScriptBin "hypr-theme-set" (builtins.readFile ./scripts/hypr-theme-set.sh);
|
||||
gtkThemeSet = pkgs.writeShellScriptBin "gtk-theme-set" (builtins.readFile ./scripts/gtk-theme-set.sh);
|
||||
qtThemeSet = pkgs.writeShellScriptBin "qt-theme-set" (builtins.readFile ./scripts/qt-theme-set.sh);
|
||||
kittyThemeSet = pkgs.writeShellScriptBin "kitty-theme-set" (builtins.readFile ./scripts/kitty-theme-set.sh);
|
||||
rofiThemeSet = pkgs.writeShellScriptBin "rofi-theme-set" (builtins.readFile ./scripts/rofi-theme-set.sh);
|
||||
listCursorThemes = pkgs.writeShellScriptBin "list-cursor-themes" (builtins.readFile ./scripts/list-cursor-themes.sh);
|
||||
cursorPreviewAll = pkgs.writeShellScriptBin "cursor-preview-all" (builtins.readFile ./scripts/cursor-preview-all.sh);
|
||||
cursorSet = pkgs.writeShellScriptBin "cursor-set" (builtins.readFile ./scripts/cursor-set.sh);
|
||||
screenshot = pkgs.writeShellScriptBin "screenshot" (builtins.readFile ./scripts/screenshot.sh);
|
||||
hotspotStart = pkgs.writeShellScriptBin "hotspot-start" (builtins.readFile ./scripts/hotspot-start.sh);
|
||||
systemTelemetry = pkgs.writeShellScriptBin "system-telemetry" (builtins.readFile ./scripts/system-telemetry.sh);
|
||||
weatherFetch = pkgs.writeShellScriptBin "weather-fetch" (builtins.readFile ./scripts/weather-fetch.sh);
|
||||
|
|
@ -33,23 +18,8 @@ in
|
|||
vaultToggle
|
||||
stripMetadata
|
||||
brightnessCtl
|
||||
showDesktopToggle
|
||||
wallpaperSet
|
||||
wallpaperDelete
|
||||
wallpaperRestore
|
||||
wallpaperThumbs
|
||||
displaySet
|
||||
hyprThemeSet
|
||||
gtkThemeSet
|
||||
qtThemeSet
|
||||
kittyThemeSet
|
||||
rofiThemeSet
|
||||
listCursorThemes
|
||||
cursorPreviewAll
|
||||
cursorSet
|
||||
screenshot
|
||||
hotspotStart
|
||||
systemTelemetry
|
||||
weatherFetch
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Extracts a small preview PNG (closest to 32px) for every installed
|
||||
# cursor theme's "left_ptr" cursor into a cache directory, one per theme
|
||||
# named "<theme>.png". Xcursor files are a binary format QML's Image
|
||||
# element can't decode directly, so this is a one-time render step —
|
||||
# already-cached themes are skipped.
|
||||
#
|
||||
# usage: cursor-preview-all <cache-dir>
|
||||
set -euo pipefail
|
||||
|
||||
cache_dir="${1:?usage: cursor-preview-all <cache-dir>}"
|
||||
mkdir -p "$cache_dir"
|
||||
|
||||
dirs=(
|
||||
"$HOME/.icons"
|
||||
"$HOME/.local/share/icons"
|
||||
"/run/current-system/sw/share/icons"
|
||||
"/etc/profiles/per-user/$USER/share/icons"
|
||||
)
|
||||
|
||||
render_one() {
|
||||
local theme="$1"
|
||||
local src="$2"
|
||||
local out="$cache_dir/$theme.png"
|
||||
[ -e "$out" ] && return 0
|
||||
|
||||
local tmp
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' RETURN
|
||||
|
||||
xcur2png -q -d "$tmp" -c "$tmp/conf" -i 1 "$src" > /dev/null 2>&1 || return 0
|
||||
|
||||
local best=""
|
||||
local best_diff=999999
|
||||
for png in "$tmp"/*.png; do
|
||||
[ -e "$png" ] || continue
|
||||
local w
|
||||
w=$(identify -format "%w" "$png" 2>/dev/null || echo 0)
|
||||
local diff=$(( w > 32 ? w - 32 : 32 - w ))
|
||||
if [ "$diff" -lt "$best_diff" ]; then
|
||||
best_diff=$diff
|
||||
best="$png"
|
||||
fi
|
||||
done
|
||||
[ -n "$best" ] && cp "$best" "$out"
|
||||
}
|
||||
|
||||
for d in "${dirs[@]}"; do
|
||||
[ -d "$d" ] || continue
|
||||
for theme_dir in "$d"/*/; do
|
||||
[ -f "${theme_dir}cursors/left_ptr" ] || continue
|
||||
render_one "$(basename "$theme_dir")" "${theme_dir}cursors/left_ptr"
|
||||
done
|
||||
done
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Applies a cursor theme immediately (hyprctl) and persists it across
|
||||
# restarts by writing env vars to a file hyprland.conf sources on startup.
|
||||
set -euo pipefail
|
||||
|
||||
theme="${1:?usage: cursor-set <theme-name> [size]}"
|
||||
size="${2:-24}"
|
||||
|
||||
hyprctl setcursor "$theme" "$size" > /dev/null
|
||||
|
||||
mkdir -p "$HOME/.config/hypr"
|
||||
cat > "$HOME/.config/hypr/cursor.conf" <<EOF
|
||||
env = HYPRCURSOR_THEME,$theme
|
||||
env = XCURSOR_THEME,$theme
|
||||
env = XCURSOR_SIZE,$size
|
||||
EOF
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Applies a full monitor descriptor immediately (hyprctl) AND persists it
|
||||
# into ~/.config/hypr/display.conf, which hyprland.conf sources — without
|
||||
# that file, hyprland.conf's generic fallback rule
|
||||
# ("monitor = , preferred, auto, 1") is the only thing on record, and
|
||||
# ANYTHING that makes Hyprland re-evaluate monitor state (observed: even
|
||||
# changing the cursor theme via `hyprctl setcursor` does this) reapplies
|
||||
# that fallback, silently dropping back to "preferred" refresh rate
|
||||
# instead of whatever the user actually picked.
|
||||
#
|
||||
# Takes the monitor name and everything after it as one pre-built
|
||||
# descriptor string (mode,position,scale + optional transform/bitdepth/
|
||||
# cm/sdrbrightness/sdrsaturation/vrr/icc keyword,value pairs) rather than
|
||||
# fixed positional args — Hyprland's own `monitor` keyword syntax grew
|
||||
# past "mode/x/y/scale/transform" (color management, HDR, VRR, ICC), and
|
||||
# the caller (LeftFlyout.qml's Displays panel) already has all of that as
|
||||
# structured state, so it's simpler for it to build the one string than
|
||||
# for this script to accept a growing positional argument list.
|
||||
set -euo pipefail
|
||||
|
||||
mon="${1:?usage: display-set <monitor> <descriptor-tail>}"
|
||||
descriptor_tail="${2:?descriptor required}"
|
||||
|
||||
descriptor="$mon,$descriptor_tail"
|
||||
|
||||
conf="$HOME/.config/hypr/display.conf"
|
||||
mkdir -p "$(dirname "$conf")"
|
||||
touch "$conf"
|
||||
|
||||
line="monitor = $descriptor"
|
||||
if grep -q "^monitor = $mon," "$conf" 2>/dev/null; then
|
||||
# mktemp in the same directory as $conf, not the default /tmp: /tmp is
|
||||
# a different filesystem here, which makes `mv` below a non-atomic
|
||||
# copy+unlink instead of a rename — Hyprland's file watcher (it
|
||||
# watches every `source=`d file, including this one) could catch the
|
||||
# destination mid-replace and its `source=` glob would find nothing,
|
||||
# throwing "globbing error: found no match" (observed live after an
|
||||
# orientation change, which always hits this branch since DP-1
|
||||
# already has an existing line by then).
|
||||
tmp=$(mktemp "$conf.XXXXXX")
|
||||
awk -v prefix="monitor = $mon," -v newline="$line" '
|
||||
index($0, prefix) == 1 { print newline; next } { print }
|
||||
' "$conf" > "$tmp"
|
||||
mv "$tmp" "$conf"
|
||||
else
|
||||
echo "$line" >> "$conf"
|
||||
fi
|
||||
|
||||
hyprctl keyword monitor "$descriptor" > /dev/null
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Regenerates GTK3/4 CSS + settings.ini to match the current Quickshell
|
||||
# theme (accent color + dark/light mode). These used to be static,
|
||||
# Nix-rebuild-only files (symlinked straight into the store) — this makes
|
||||
# them live-editable instead, the same way cursor.conf/display.conf work,
|
||||
# so the Theme picker can actually reach GTK apps like Thunar.
|
||||
set -euo pipefail
|
||||
|
||||
accent="${1:?usage: gtk-theme-set <accent> <bg> <bgAlt> <fg> <fgDim> <border> <dark:1|0>}"
|
||||
bg="${2:?bg required}"
|
||||
bg_alt="${3:?bgAlt required}"
|
||||
fg="${4:?fg required}"
|
||||
fg_dim="${5:?fgDim required}"
|
||||
border="${6:?border required}"
|
||||
dark="${7:?dark required}"
|
||||
|
||||
dir3="$HOME/.config/gtk-3.0"
|
||||
dir4="$HOME/.config/gtk-4.0"
|
||||
mkdir -p "$dir3" "$dir4"
|
||||
|
||||
css_file=$(mktemp)
|
||||
cat > "$css_file" <<EOF
|
||||
/* Squared corners + accent colors, generated by gtk-theme-set from the
|
||||
Quickshell Theme picker — GTK's CSS is a subset of real CSS: no
|
||||
!important, and zero still needs a unit.
|
||||
|
||||
The @define-color block matters more than it looks: plain selector
|
||||
overrides (window, .background, .view, ...) only get you so far —
|
||||
Thunar's icon-view content area and sidebar tree view turned out to
|
||||
pull their actual fill color from these named theme variables
|
||||
internally, not from the generic background-color cascade. Verified
|
||||
live: without redefining theme_base_color specifically, the sidebar
|
||||
and file grid stayed white no matter how broad the selector list got. */
|
||||
@define-color theme_bg_color $bg;
|
||||
@define-color theme_base_color $bg;
|
||||
@define-color theme_fg_color $fg;
|
||||
@define-color theme_text_color $fg;
|
||||
@define-color theme_selected_bg_color $accent;
|
||||
@define-color theme_selected_fg_color $bg;
|
||||
@define-color borders $border;
|
||||
|
||||
* {
|
||||
border-radius: 0px;
|
||||
-gtk-outline-radius: 0px;
|
||||
}
|
||||
window, .background, .view, iconview, treeview, treeview.view, list, row,
|
||||
notebook, notebook > stack, scrolledwindow, viewport {
|
||||
background-color: $bg;
|
||||
color: $fg;
|
||||
}
|
||||
iconview.cell, iconview text {
|
||||
background-color: $bg;
|
||||
color: $fg;
|
||||
}
|
||||
treeview.view:selected, row:selected, iconview:selected {
|
||||
background-color: $accent;
|
||||
color: $bg;
|
||||
}
|
||||
headerbar, .titlebar {
|
||||
background-color: $bg_alt;
|
||||
color: $fg;
|
||||
border-color: $border;
|
||||
box-shadow: none;
|
||||
}
|
||||
button {
|
||||
border-color: $border;
|
||||
}
|
||||
button:hover {
|
||||
background-color: $bg_alt;
|
||||
}
|
||||
selection, *:selected {
|
||||
background-color: $accent;
|
||||
color: $bg;
|
||||
}
|
||||
EOF
|
||||
|
||||
cp "$css_file" "$dir3/gtk.css"
|
||||
cp "$css_file" "$dir4/gtk.css"
|
||||
rm -f "$css_file"
|
||||
|
||||
prefer_dark="false"
|
||||
theme_name="Adwaita"
|
||||
icon_theme="Gruvbox-Plus-Light"
|
||||
if [ "$dark" = "1" ]; then
|
||||
prefer_dark="true"
|
||||
theme_name="Adwaita-dark"
|
||||
icon_theme="Gruvbox-Plus-Dark"
|
||||
fi
|
||||
|
||||
ini_file=$(mktemp)
|
||||
cat > "$ini_file" <<EOF
|
||||
[Settings]
|
||||
gtk-application-prefer-dark-theme=$prefer_dark
|
||||
gtk-theme-name=$theme_name
|
||||
gtk-icon-theme-name=$icon_theme
|
||||
gtk-font-name=Noto Sans, 10
|
||||
gtk-enable-animations=true
|
||||
EOF
|
||||
|
||||
cp "$ini_file" "$dir3/settings.ini"
|
||||
cp "$ini_file" "$dir4/settings.ini"
|
||||
rm -f "$ini_file"
|
||||
|
||||
# settings.ini alone isn't enough: GTK3 binds gtk-theme-name/icon-theme-name
|
||||
# to org.gnome.desktop.interface via GSettings whenever that schema and a
|
||||
# dconf backend are present (they are here — dconf-service was already
|
||||
# running), and that binding wins over settings.ini unconditionally. This
|
||||
# machine's dconf database had hard values left over (gtk-theme='Breeze',
|
||||
# icon-theme='breeze') that silently overrode every settings.ini edit —
|
||||
# which is why neither accent/dark-mode nor the Gruvbox icon switch ever
|
||||
# reached Thunar. Writing the same values to dconf keeps both sources in
|
||||
# sync so it doesn't matter which one GTK actually reads from.
|
||||
if command -v dconf >/dev/null 2>&1; then
|
||||
color_scheme="default"
|
||||
[ "$dark" = "1" ] && color_scheme="prefer-dark"
|
||||
dconf write /org/gnome/desktop/interface/gtk-theme "'$theme_name'"
|
||||
dconf write /org/gnome/desktop/interface/icon-theme "'$icon_theme'"
|
||||
dconf write /org/gnome/desktop/interface/color-scheme "'$color_scheme'"
|
||||
fi
|
||||
|
||||
# GTK2 compat: separate config format (~/.gtkrc-2.0, not settings.ini),
|
||||
# and no live theme-switching mechanism at all — GTK2 apps only read this
|
||||
# at their own startup. Only icon theme and font actually follow the
|
||||
# current theme; gnome-themes-extra's bundled GTK2 Adwaita engine has no
|
||||
# dark variant to switch to (GTK2 predates that concept), so gtk-theme-
|
||||
# name here is always the one real option, not a broken attempt at dark
|
||||
# mode.
|
||||
cat > "$HOME/.gtkrc-2.0" <<EOF
|
||||
gtk-theme-name="Adwaita"
|
||||
gtk-icon-theme-name="$icon_theme"
|
||||
gtk-font-name="Noto Sans 10"
|
||||
EOF
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Applies Hyprland's active/inactive window border colors immediately AND
|
||||
# persists them into ~/.config/hypr/display.conf's sibling, theme.conf
|
||||
# (sourced by hyprland.conf after the static general{} block, so it
|
||||
# overrides those hardcoded defaults) — without that file, a config
|
||||
# reload/restart would silently revert to whatever's hardcoded in
|
||||
# hyprland.conf, same class of bug as the display-settings one.
|
||||
set -euo pipefail
|
||||
|
||||
accent="${1:?usage: hypr-theme-set <accent_hex_no_hash> <border_hex_no_hash>}"
|
||||
border="${2:?border required}"
|
||||
|
||||
hyprctl keyword general:col.active_border "rgba(${accent}ff)" > /dev/null
|
||||
hyprctl keyword general:col.inactive_border "rgba(${border}ff)" > /dev/null
|
||||
|
||||
conf="$HOME/.config/hypr/theme.conf"
|
||||
mkdir -p "$(dirname "$conf")"
|
||||
cat > "$conf" <<EOF
|
||||
general {
|
||||
col.active_border = rgba(${accent}ff)
|
||||
col.inactive_border = rgba(${border}ff)
|
||||
}
|
||||
EOF
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Regenerates ~/.config/kitty/theme.conf (included from kitty.conf) to
|
||||
# match the current Quickshell theme. No explicit reload needed — kitty
|
||||
# has a built-in config-file watcher (visible as `kitten __watch_conf__`
|
||||
# in its process list) that also watches included files, so this takes
|
||||
# effect live in already-open kitty windows.
|
||||
set -euo pipefail
|
||||
|
||||
accent="${1:?usage: kitty-theme-set <accent> <bg> <bgAlt> <fg> <fgDim> <border>}"
|
||||
bg="${2:?bg required}"
|
||||
bg_alt="${3:?bgAlt required}"
|
||||
fg="${4:?fg required}"
|
||||
fg_dim="${5:?fgDim required}"
|
||||
border="${6:?border required}"
|
||||
|
||||
conf="$HOME/.config/kitty/theme.conf"
|
||||
mkdir -p "$(dirname "$conf")"
|
||||
|
||||
cat > "$conf" <<EOF
|
||||
background $bg
|
||||
foreground $fg
|
||||
selection_background $border
|
||||
selection_foreground $fg
|
||||
cursor $fg
|
||||
active_border_color $accent
|
||||
inactive_border_color $border
|
||||
active_tab_background $bg_alt
|
||||
active_tab_foreground $fg
|
||||
inactive_tab_background $bg
|
||||
inactive_tab_foreground $fg_dim
|
||||
EOF
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Lists installed Xcursor themes by scanning the real XCURSOR_PATH locations
|
||||
# for a "cursors" subdirectory — no hardcoded theme names.
|
||||
set -euo pipefail
|
||||
|
||||
dirs=(
|
||||
"$HOME/.icons"
|
||||
"$HOME/.local/share/icons"
|
||||
"/run/current-system/sw/share/icons"
|
||||
# Packages installed via users.users.<name>.packages (as opposed to
|
||||
# environment.systemPackages) land here, not under
|
||||
# /run/current-system/sw — this is where bibata-cursors actually is.
|
||||
"/etc/profiles/per-user/$USER/share/icons"
|
||||
)
|
||||
|
||||
for d in "${dirs[@]}"; do
|
||||
[ -d "$d" ] || continue
|
||||
for theme in "$d"/*/; do
|
||||
[ -d "${theme}cursors" ] || continue
|
||||
basename "$theme"
|
||||
done
|
||||
done | sort -u
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Regenerates qt6ct's config + color scheme to match the current Quickshell
|
||||
# theme, the Qt-side equivalent of gtk-theme-set. Qt apps (Gwenview, Ark,
|
||||
# Kate, ...) had no working platform theme plugin at all before this — the
|
||||
# QT_QPA_PLATFORMTHEME=qt6ct env var (hyprland.conf) was pointing at a
|
||||
# plugin that wasn't even installed, so every Qt app fell back to
|
||||
# whatever default style/palette it shipped with, ignoring the rest of
|
||||
# the desktop entirely. style=Fusion (not a KDE/Breeze style) matters
|
||||
# specifically: Fusion paints straight from QPalette, so a custom color
|
||||
# scheme here is guaranteed to actually show — Breeze-family styles
|
||||
# prefer reading colors from kdeglobals instead, which is a separate,
|
||||
# static, unrelated leftover file on this machine.
|
||||
set -euo pipefail
|
||||
|
||||
accent="${1:?usage: qt-theme-set <accent> <bg> <bgAlt> <fg> <fgDim> <border> <dark:1|0>}"
|
||||
bg="${2:?bg required}"
|
||||
bg_alt="${3:?bgAlt required}"
|
||||
fg="${4:?fg required}"
|
||||
fg_dim="${5:?fgDim required}"
|
||||
border="${6:?border required}"
|
||||
dark="${7:?dark required}"
|
||||
|
||||
dir="$HOME/.config/qt6ct"
|
||||
mkdir -p "$dir/colors"
|
||||
|
||||
# qt6ct color scheme files store QPalette::ColorRole values as a fixed-
|
||||
# order, comma-separated #aarrggbb list — order and format verified
|
||||
# against the example schemes qt6ct itself ships
|
||||
# (share/qt6ct/colors/darker.conf etc.), not guessed: WindowText, Button,
|
||||
# Light, Midlight, Dark, Mid, Text, BrightText, ButtonText, Base, Window,
|
||||
# Shadow, Highlight, HighlightedText, Link, LinkVisited, AlternateBase,
|
||||
# NoRole, ToolTipBase, ToolTipText, PlaceholderText.
|
||||
a() { printf '#ff%s' "${1#\#}"; } # opaque
|
||||
t() { printf '#80%s' "${1#\#}"; } # translucent, for PlaceholderText
|
||||
|
||||
active="$(a "$fg"), $(a "$bg_alt"), $(a "$border"), $(a "$bg_alt"), $(a "$bg"), $(a "$border"), $(a "$fg"), $(a "$fg"), $(a "$fg"), $(a "$bg"), $(a "$bg"), $(a "$border"), $(a "$accent"), $(a "$bg"), $(a "$accent"), $(a "$accent"), $(a "$bg_alt"), $(a "$bg"), $(a "$bg_alt"), $(a "$fg"), $(t "$fg_dim")"
|
||||
disabled="$(a "$fg_dim"), $(a "$bg_alt"), $(a "$border"), $(a "$bg_alt"), $(a "$bg"), $(a "$border"), $(a "$fg_dim"), $(a "$fg_dim"), $(a "$fg_dim"), $(a "$bg"), $(a "$bg"), $(a "$border"), $(a "$accent"), $(a "$fg_dim"), $(a "$accent"), $(a "$accent"), $(a "$bg_alt"), $(a "$bg"), $(a "$bg_alt"), $(a "$fg_dim"), $(t "$fg_dim")"
|
||||
|
||||
color_file="$dir/colors/quickshell-theme.conf"
|
||||
cat > "$color_file" <<EOF
|
||||
[ColorScheme]
|
||||
active_colors=$active
|
||||
disabled_colors=$disabled
|
||||
inactive_colors=$active
|
||||
EOF
|
||||
|
||||
icon_theme="Gruvbox-Plus-Light"
|
||||
[ "$dark" = "1" ] && icon_theme="Gruvbox-Plus-Dark"
|
||||
|
||||
cat > "$dir/qt6ct.conf" <<EOF
|
||||
[Appearance]
|
||||
style=Fusion
|
||||
icon_theme=$icon_theme
|
||||
custom_palette=true
|
||||
color_scheme_path=$color_file
|
||||
standard_dialogs=default
|
||||
EOF
|
||||
|
||||
# qt6ct's palette only reaches classic QWidget UI. Modern KDE apps
|
||||
# (confirmed live: Gwenview) draw their toolbar/chrome with Kirigami/QML
|
||||
# instead, which ignores QPalette entirely and reads kdeglobals' color
|
||||
# groups directly via KColorScheme — so without this, qt6ct alone left
|
||||
# exactly that chrome stuck on whatever this machine's stale leftover
|
||||
# kdeglobals had (unrelated purple KDE colors from some earlier install),
|
||||
# while everything else (dialogs, the actual image viewport, ...)
|
||||
# correctly followed the theme.
|
||||
rgb() {
|
||||
local hex="${1#\#}"
|
||||
printf '%d,%d,%d' "0x${hex:0:2}" "0x${hex:2:2}" "0x${hex:4:2}"
|
||||
}
|
||||
|
||||
cat > "$HOME/.config/kdeglobals" <<EOF
|
||||
[General]
|
||||
ColorScheme=QuickshellTheme
|
||||
widgetStyle=Fusion
|
||||
|
||||
[KDE]
|
||||
widgetStyle=Fusion
|
||||
|
||||
[Colors:Window]
|
||||
BackgroundNormal=$(rgb "$bg_alt")
|
||||
ForegroundNormal=$(rgb "$fg")
|
||||
|
||||
[Colors:View]
|
||||
BackgroundNormal=$(rgb "$bg")
|
||||
ForegroundNormal=$(rgb "$fg")
|
||||
|
||||
[Colors:Button]
|
||||
BackgroundNormal=$(rgb "$bg_alt")
|
||||
ForegroundNormal=$(rgb "$fg")
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundNormal=$(rgb "$accent")
|
||||
ForegroundNormal=$(rgb "$bg")
|
||||
DecorationFocus=$(rgb "$accent")
|
||||
DecorationHover=$(rgb "$accent")
|
||||
|
||||
[Colors:Tooltip]
|
||||
BackgroundNormal=$(rgb "$bg_alt")
|
||||
ForegroundNormal=$(rgb "$fg")
|
||||
|
||||
[Colors:Header]
|
||||
BackgroundNormal=$(rgb "$bg_alt")
|
||||
ForegroundNormal=$(rgb "$fg")
|
||||
EOF
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Regenerates ~/.config/rofi/theme.rasi (imported by config.rasi) to match
|
||||
# the current Quickshell theme. Rofi has no config-file watcher — unlike
|
||||
# kitty, it's launched on demand rather than staying resident, so the next
|
||||
# time it's opened it just reads whatever is on disk. No reload step needed.
|
||||
set -euo pipefail
|
||||
|
||||
accent="${1:?usage: rofi-theme-set <accent> <bg> <bgAlt> <fg> <fgDim> <border> <dark:1|0>}"
|
||||
bg="${2:?bg required}"
|
||||
bg_alt="${3:?bgAlt required}"
|
||||
fg="${4:?fg required}"
|
||||
fg_dim="${5:?fgDim required}"
|
||||
border="${6:?border required}"
|
||||
dark="${7:?dark required}"
|
||||
|
||||
icon_theme="Gruvbox-Plus-Light"
|
||||
if [ "$dark" = "1" ]; then
|
||||
icon_theme="Gruvbox-Plus-Dark"
|
||||
fi
|
||||
|
||||
conf="$HOME/.config/rofi/theme.rasi"
|
||||
mkdir -p "$(dirname "$conf")"
|
||||
|
||||
cat > "$conf" <<EOF
|
||||
* {
|
||||
bg: ${bg}ee;
|
||||
bg-alt: ${bg_alt}ee;
|
||||
fg: ${fg}ff;
|
||||
accent: ${accent}ff;
|
||||
border-color: ${border}ff;
|
||||
placeholder: ${fg_dim}ff;
|
||||
}
|
||||
configuration {
|
||||
icon-theme: "$icon_theme";
|
||||
}
|
||||
EOF
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Unified screenshot helper.
|
||||
# screenshot <region|full|window> <clipboard|file|both>
|
||||
set -euo pipefail
|
||||
|
||||
mode="${1:-region}"
|
||||
action="${2:-both}"
|
||||
|
||||
geom=()
|
||||
case "$mode" in
|
||||
region)
|
||||
sel="$(slurp)" || exit 0
|
||||
[ -z "$sel" ] && exit 0
|
||||
geom=(-g "$sel")
|
||||
;;
|
||||
window)
|
||||
win="$(hyprctl activewindow -j)"
|
||||
at_x=$(echo "$win" | jq -r '.at[0]')
|
||||
at_y=$(echo "$win" | jq -r '.at[1]')
|
||||
w=$(echo "$win" | jq -r '.size[0]')
|
||||
h=$(echo "$win" | jq -r '.size[1]')
|
||||
geom=(-g "${at_x},${at_y} ${w}x${h}")
|
||||
;;
|
||||
full)
|
||||
geom=()
|
||||
;;
|
||||
*)
|
||||
echo "unknown mode: $mode (expected region|full|window)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p "$HOME/Screenshots"
|
||||
file="$HOME/Screenshots/$(date +%Y-%m-%d_%H-%M-%S).png"
|
||||
|
||||
case "$action" in
|
||||
clipboard)
|
||||
grim "${geom[@]}" - | wl-copy
|
||||
;;
|
||||
file)
|
||||
grim "${geom[@]}" "$file"
|
||||
;;
|
||||
both)
|
||||
grim "${geom[@]}" "$file"
|
||||
wl-copy < "$file"
|
||||
;;
|
||||
*)
|
||||
echo "unknown action: $action (expected clipboard|file|both)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# "Show desktop" for Hyprland: stashes every window on the active workspace
|
||||
# into a hidden special workspace, and restores each one to the workspace
|
||||
# it came from on the next press — regardless of which workspace you're on
|
||||
# when you press it again (a single global state file, not one keyed by
|
||||
# "current workspace", which broke the moment you switched workspaces
|
||||
# in between).
|
||||
set -euo pipefail
|
||||
|
||||
state_file="${XDG_RUNTIME_DIR:-/tmp}/hypr-show-desktop.json"
|
||||
|
||||
if [ -s "$state_file" ]; then
|
||||
jq -c '.[]' "$state_file" | while IFS= read -r entry; do
|
||||
addr=$(echo "$entry" | jq -r '.address')
|
||||
ws=$(echo "$entry" | jq -r '.workspace')
|
||||
hyprctl dispatch movetoworkspacesilent "$ws,address:$addr" > /dev/null
|
||||
done
|
||||
rm -f "$state_file"
|
||||
else
|
||||
ws_id=$(hyprctl activeworkspace -j | jq -r '.id')
|
||||
hyprctl clients -j | jq -c --argjson ws "$ws_id" \
|
||||
'[.[] | select(.workspace.id == $ws) | {address: .address, workspace: $ws}]' > "$state_file"
|
||||
jq -r '.[].address' "$state_file" | while IFS= read -r addr; do
|
||||
[ -n "$addr" ] && hyprctl dispatch movetoworkspacesilent "special:showdesktop,address:$addr" > /dev/null
|
||||
done
|
||||
fi
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Removes a wallpaper by moving it to the freedesktop.org trash
|
||||
# (~/.local/share/Trash), the same mechanism Thunar's own delete uses —
|
||||
# so it shows up in Thunar's Trash and can be restored from there,
|
||||
# instead of `rm` permanently destroying it with no way back.
|
||||
set -euo pipefail
|
||||
|
||||
path="${1:?usage: wallpaper-delete <path>}"
|
||||
[ -e "$path" ] || exit 0
|
||||
|
||||
trash_dir="$HOME/.local/share/Trash"
|
||||
mkdir -p "$trash_dir/files" "$trash_dir/info"
|
||||
|
||||
name="$(basename "$path")"
|
||||
dest="$trash_dir/files/$name"
|
||||
info="$trash_dir/info/$name.trashinfo"
|
||||
n=1
|
||||
while [ -e "$dest" ] || [ -e "$info" ]; do
|
||||
dest="$trash_dir/files/${name}.$n"
|
||||
info="$trash_dir/info/${name}.$n.trashinfo"
|
||||
n=$((n + 1))
|
||||
done
|
||||
|
||||
cat > "$info" <<EOF
|
||||
[Trash Info]
|
||||
Path=$path
|
||||
DeletionDate=$(date +%Y-%m-%dT%H:%M:%S)
|
||||
EOF
|
||||
|
||||
mv "$path" "$dest"
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Reapplies the last-set wallpaper on login/reboot. hyprpaper and mpvpaper
|
||||
# both start every session with nothing loaded, and wallpaper-set only ever
|
||||
# ran through live IPC with no persistence of its own — without this the
|
||||
# desktop silently reverted to blank on every fresh login. Run via
|
||||
# exec-once in hyprland.conf, after hyprpaper.
|
||||
set -euo pipefail
|
||||
|
||||
state_file="$HOME/.config/hypr/wallpaper.conf"
|
||||
[ -f "$state_file" ] || exit 0
|
||||
|
||||
# hyprpaper's IPC socket isn't guaranteed ready the instant exec-once fires
|
||||
# it, since exec-once entries all start back-to-back with no ordering
|
||||
# guarantee beyond process spawn order.
|
||||
for _ in $(seq 1 50); do
|
||||
hyprctl hyprpaper listactive > /dev/null 2>&1 && break
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
bash "$state_file"
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Sets the desktop wallpaper — static images go through hyprpaper's IPC,
|
||||
# video files loop via mpvpaper. Only one backend should ever be driving
|
||||
# the background at a time, so switching to one always tears the other
|
||||
# down first.
|
||||
#
|
||||
# usage: wallpaper-set <path> [fit_mode] [muted] [speed] [auto_pause]
|
||||
# fit_mode: cover|contain (static images only, default cover)
|
||||
# muted: 1|0 (animated only, default 1)
|
||||
# speed: playback speed (animated only, default 1.0)
|
||||
# auto_pause: 1|0 (animated only — mpvpaper -p, pauses
|
||||
# playback while the wallpaper is hidden
|
||||
# behind a fullscreen window, default 1)
|
||||
set -euo pipefail
|
||||
|
||||
image="${1:?usage: wallpaper-set <path> [fit_mode] [muted] [speed] [auto_pause]}"
|
||||
fit_mode="${2:-cover}"
|
||||
muted="${3:-1}"
|
||||
speed="${4:-1.0}"
|
||||
auto_pause="${5:-1}"
|
||||
|
||||
case "${image,,}" in
|
||||
*.mp4|*.webm|*.mkv|*.mov|*.gif)
|
||||
# -9: plain SIGTERM doesn't reliably kill mpvpaper (verified live —
|
||||
# it survives pkill -f without -9), which would leave the old
|
||||
# instance still holding the layer-shell surface underneath a new
|
||||
# one. The short sleep gives it a moment to actually release that
|
||||
# surface before a replacement tries to claim it.
|
||||
pkill -9 -f mpvpaper > /dev/null 2>&1 || true
|
||||
sleep 0.3
|
||||
|
||||
mpv_opts="loop-playlist=inf speed=$speed"
|
||||
[ "$muted" = "1" ] && mpv_opts="no-audio $mpv_opts"
|
||||
|
||||
mpvpaper_flags=()
|
||||
[ "$auto_pause" = "1" ] && mpvpaper_flags+=("-p")
|
||||
|
||||
# setsid + disown so mpvpaper (which runs forever, looping the
|
||||
# video) survives after this script exits.
|
||||
setsid mpvpaper "${mpvpaper_flags[@]}" -o "$mpv_opts" '*' "$image" \
|
||||
> /dev/null 2>&1 < /dev/null &
|
||||
disown
|
||||
;;
|
||||
*)
|
||||
# -9: plain SIGTERM doesn't reliably kill mpvpaper (verified live —
|
||||
# it survives pkill -f without -9), which would leave the old
|
||||
# instance still holding the layer-shell surface underneath a new
|
||||
# one. The short sleep gives it a moment to actually release that
|
||||
# surface before a replacement tries to claim it.
|
||||
pkill -9 -f mpvpaper > /dev/null 2>&1 || true
|
||||
sleep 0.3
|
||||
# This hyprpaper version's IPC only exposes a single `wallpaper`
|
||||
# request (confirmed via `hyprctl hyprpaper --help`) — it loads
|
||||
# and swaps the image in one call. preload/unload/listloaded
|
||||
# requests it used to need don't exist anymore and error out with
|
||||
# "invalid hyprpaper request", which — combined with `set -e` —
|
||||
# was aborting this script before it ever got here.
|
||||
hyprctl hyprpaper wallpaper ",$image,$fit_mode" > /dev/null
|
||||
;;
|
||||
esac
|
||||
|
||||
# Persist so the wallpaper survives a reboot/relogin. hyprpaper/mpvpaper
|
||||
# both start every session with nothing loaded — this was previously only
|
||||
# ever set at runtime via IPC, so the desktop silently reverted to blank
|
||||
# every fresh login. wallpaper-restore (exec-once in hyprland.conf, after
|
||||
# hyprpaper starts) re-runs the exact command saved here.
|
||||
state_file="$HOME/.config/hypr/wallpaper.conf"
|
||||
mkdir -p "$(dirname "$state_file")"
|
||||
printf 'wallpaper-set %q %q %q %q %q\n' "$image" "$fit_mode" "$muted" "$speed" "$auto_pause" > "$state_file"
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Generates (or reuses) a cached preview JPEG for each animated wallpaper
|
||||
# — first frame ~1s in, to skip a common black/fade intro on most clips —
|
||||
# so the Wallpapers panel can show a real thumbnail instead of a generic
|
||||
# play icon. Prints "<source path>\t<thumbnail path>" per file on stdout
|
||||
# once it's ready, so the caller (LeftFlyout.qml) never has to
|
||||
# independently reconstruct the cache filename — it's a hash of the
|
||||
# source path, chosen only to keep filenames short/safe, not something
|
||||
# QML needs to know how to derive.
|
||||
set -euo pipefail
|
||||
|
||||
cache_dir="${1:?usage: wallpaper-thumbs <cache-dir> <file...>}"
|
||||
shift
|
||||
mkdir -p "$cache_dir"
|
||||
|
||||
for file in "$@"; do
|
||||
[ -f "$file" ] || continue
|
||||
name="$(printf '%s' "$file" | sha1sum | cut -d' ' -f1)"
|
||||
thumb="$cache_dir/$name.jpg"
|
||||
if [ ! -f "$thumb" ] || [ "$file" -nt "$thumb" ]; then
|
||||
ffmpeg -y -ss 00:00:01 -i "$file" -frames:v 1 -vf "scale=320:-1" "$thumb" \
|
||||
> /dev/null 2>&1 || true
|
||||
fi
|
||||
[ -f "$thumb" ] && printf '%s\t%s\n' "$file" "$thumb"
|
||||
done
|
||||
|
|
@ -58,6 +58,36 @@
|
|||
alsa.enable = true;
|
||||
alsa.support32Bit = true;
|
||||
pulse.enable = true;
|
||||
|
||||
# Standalone "Virtual Microphone" device, fully independent of the real
|
||||
# hardware mic — always present regardless of what's running, so apps
|
||||
# like Discord keep defaulting to the real mic untouched, and only pick
|
||||
# this up if explicitly selected. Feed anything into "Virtual
|
||||
# Microphone Sink" (e.g. via qpwgraph/helvum, routing EasyEffects' or
|
||||
# Soundux's output to it) and it comes out the other side as "Virtual
|
||||
# Microphone", selectable like any other input device.
|
||||
extraConfig.pipewire."99-virtual-microphone" = {
|
||||
"context.modules" = [
|
||||
{
|
||||
name = "libpipewire-module-loopback";
|
||||
args = {
|
||||
"node.description" = "Virtual Microphone";
|
||||
"capture.props" = {
|
||||
"node.name" = "virtual_mic_sink";
|
||||
"node.description" = "Virtual Microphone Sink";
|
||||
"media.class" = "Audio/Sink";
|
||||
"audio.position" = "FL,FR";
|
||||
};
|
||||
"playback.props" = {
|
||||
"node.name" = "virtual_mic_source";
|
||||
"node.description" = "Virtual Microphone";
|
||||
"media.class" = "Audio/Source";
|
||||
"audio.position" = "FL,FR";
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
hardware.bluetooth = {
|
||||
|
|
@ -168,6 +198,15 @@
|
|||
"net.ipv6.conf.default.accept_redirects" = false;
|
||||
"net.ipv4.conf.all.send_redirects" = false;
|
||||
"net.ipv4.conf.default.send_redirects" = false;
|
||||
# Restrict ptrace to a process's own children — blocks a common local
|
||||
# credential-scraping/process-injection vector between unrelated
|
||||
# processes. Only bites ad-hoc `gdb -p <pid>`-style attaches to a
|
||||
# process that isn't your debugger's own child.
|
||||
"kernel.yama.ptrace_scope" = 1;
|
||||
# Drop the kernel-uptime-derived TCP timestamp from outgoing packets
|
||||
# (a passive fingerprinting/uptime-leak vector). Negligible throughput
|
||||
# impact on a normal home connection.
|
||||
"net.ipv4.tcp_timestamps" = 0;
|
||||
};
|
||||
|
||||
# Crash dumps can contain decrypted memory contents, passwords, keys —
|
||||
|
|
@ -201,4 +240,16 @@
|
|||
networking.networkmanager.wifi.macAddress = "random";
|
||||
networking.networkmanager.wifi.scanRandMacAddress = true;
|
||||
networking.networkmanager.ethernet.macAddress = "stable";
|
||||
|
||||
# Disable NetworkManager's periodic external connectivity probe (normally
|
||||
# pings a canary URL to detect captive portals) — one less routine
|
||||
# external phone-home, no functional loss on a directly-connected desktop.
|
||||
networking.networkmanager.settings.connectivity = {
|
||||
uri = "";
|
||||
interval = 0;
|
||||
};
|
||||
|
||||
# Budgie's module turns this on by default (for Budgie Control Center's
|
||||
# Location Services panel) — nothing in this config needs location data.
|
||||
services.geoclue2.enable = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
# Dark GTK theme (built into GTK3/4's Adwaita since 3.20) for Thunar and
|
||||
# other GTK apps, as a starting point. The actual palette (accent color,
|
||||
# dark/light) is no longer static here — it's generated at runtime by
|
||||
# gtk-theme-set (modules/scripts/gtk-theme-set.sh), called from the
|
||||
# Quickshell Theme picker (dotfiles/quickshell/Theme.qml) every time the
|
||||
# theme changes, so switching accent/mode there actually reaches GTK
|
||||
# apps instead of requiring a rebuild.
|
||||
environment.systemPackages = with pkgs; [
|
||||
gnome-themes-extra
|
||||
adwaita-icon-theme
|
||||
gruvbox-plus-icons
|
||||
dconf
|
||||
# Qt's side of the same live-theming pipeline as GTK above — see
|
||||
# qt-theme-set (modules/scripts/qt-theme-set.sh). Without this
|
||||
# installed, QT_QPA_PLATFORMTHEME=qt6ct (hyprland.conf) pointed at a
|
||||
# platform theme plugin that didn't exist, so every Qt app (Gwenview,
|
||||
# Ark, Kate, ...) silently fell back to its own default style/palette
|
||||
# instead of anything resembling the rest of the desktop.
|
||||
qt6Packages.qt6ct
|
||||
kdePackages.qqc2-desktop-style # QQC2 style needed for QT_QUICK_CONTROLS_STYLE=
|
||||
# org.kde.desktop (hyprland.conf) to actually
|
||||
# theme Kirigami-based apps' chrome
|
||||
];
|
||||
|
||||
# No environment.sessionVariables.GTK_THEME here on purpose: GTK_THEME
|
||||
# takes priority over settings.ini's gtk-theme-name for GTK3 apps, and
|
||||
# as a session env var it's fixed at login — it can't be changed live.
|
||||
# A leftover "Adwaita:dark" override here was silently overriding
|
||||
# whatever gtk-theme-set wrote to settings.ini, permanently forcing
|
||||
# dark mode and blocking the Theme picker from ever reaching Thunar.
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /home/nexoniarz/.config/gtk-3.0 0755 nexoniarz users -"
|
||||
"d /home/nexoniarz/.config/gtk-4.0 0755 nexoniarz users -"
|
||||
"d /home/nexoniarz/.config/qt6ct 0755 nexoniarz users -"
|
||||
"d /home/nexoniarz/.config/qt6ct/colors 0755 nexoniarz users -"
|
||||
# `f`, not `L+`: created empty only if missing, never overwritten on
|
||||
# rebuild — gtk-theme-set/qt-theme-set own the actual content from
|
||||
# here on.
|
||||
"f /home/nexoniarz/.config/gtk-3.0/gtk.css 0644 nexoniarz users - -"
|
||||
"f /home/nexoniarz/.config/gtk-4.0/gtk.css 0644 nexoniarz users - -"
|
||||
"f /home/nexoniarz/.config/gtk-3.0/settings.ini 0644 nexoniarz users - -"
|
||||
"f /home/nexoniarz/.config/gtk-4.0/settings.ini 0644 nexoniarz users - -"
|
||||
"f /home/nexoniarz/.gtkrc-2.0 0644 nexoniarz users - -"
|
||||
"f /home/nexoniarz/.config/qt6ct/qt6ct.conf 0644 nexoniarz users - -"
|
||||
];
|
||||
}
|
||||
Loading…
Reference in a new issue