271 lines
14 KiB
Shell
Executable file
271 lines
14 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
# Compose the Souveraine quickshell config (`qs -c souveraine`).
|
|
#
|
|
# Model: ~/.config/quickshell/souveraine is BUILT by this script —
|
|
# - our files (this repo) are symlinked in, repo stays source of truth
|
|
# - untouched upstream directories are borrowed as whole-dir symlinks
|
|
# into the ii tree (so upstream updates flow through)
|
|
# - directories where we override any file are composed file-by-file
|
|
# The ii tree itself is DEPLOYED from this repo (ii-base/, the pinned base —
|
|
# vendored 2026-07-21 after laptop/phone drifted ~900 files): every run
|
|
# rsyncs ii-base -> ~/.config/quickshell/ii, so "borrowed from ii" means
|
|
# borrowed from the same pin on every device. On the phone (aarch64) the
|
|
# ii-phone/ overlay is applied on top — the declared home for phone-only
|
|
# files (Cellular, mobile bar/OSK/wallpaper behavior). Never hand-edit
|
|
# ~/.config/quickshell/ii; change ii-base/ (or ii-phone/) and redeploy.
|
|
#
|
|
# deploy.sh compose ~/.config/quickshell/souveraine
|
|
# deploy.sh -u remove the souveraine config dir (ii untouched)
|
|
# deploy.sh --legacy-clean remove the OLD overlay symlinks from ii and
|
|
# restore its .upstream backups (one-time migration)
|
|
# deploy.sh --phone rsync this surface to the phone and deploy there
|
|
set -euo pipefail
|
|
|
|
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
QS="${HOME}/.config/quickshell"
|
|
II="${QS}/ii"
|
|
SV="${QS}/souveraine"
|
|
TARGET_ARCH="${SOUVERAINE_TARGET_ARCH:-$(uname -m)}"
|
|
|
|
# Manifest: "<repo-relative> <target-relative-to-~/.config/quickshell>"
|
|
MANIFEST="
|
|
shell.qml souveraine/shell.qml
|
|
GlobalStates.qml souveraine/GlobalStates.qml
|
|
settings.qml souveraine/settings.qml
|
|
settings-phone.qml souveraine/settings-phone.qml
|
|
panelFamilies/SouveraineFamily.qml souveraine/panelFamilies/SouveraineFamily.qml
|
|
services/Souveraine.qml souveraine/services/Souveraine.qml
|
|
services/Ai.qml souveraine/services/Ai.qml
|
|
services/Cellular.qml souveraine/services/Cellular.qml
|
|
services/Network.qml souveraine/services/Network.qml
|
|
services/TaskbarApps.qml souveraine/services/TaskbarApps.qml
|
|
services/GlobalFocusGrab.qml souveraine/services/GlobalFocusGrab.qml
|
|
services/Idle.qml souveraine/services/Idle.qml
|
|
services/IdleCoordinator.qml souveraine/services/IdleCoordinator.qml
|
|
services/LockContentPolicy.qml souveraine/services/LockContentPolicy.qml
|
|
services/WallpaperAssets.qml souveraine/services/WallpaperAssets.qml
|
|
services/WallpaperDownload.qml souveraine/services/WallpaperDownload.qml
|
|
scripts/wallpaper/download_wallhaven.sh souveraine/scripts/wallpaper/download_wallhaven.sh
|
|
services/ConflictKiller.qml souveraine/services/ConflictKiller.qml
|
|
services/SessionEvents.qml souveraine/services/SessionEvents.qml
|
|
services/SessiondBridge.qml souveraine/services/SessiondBridge.qml
|
|
services/StepUpAuth.qml souveraine/services/StepUpAuth.qml
|
|
services/SessionAudit.qml souveraine/services/SessionAudit.qml
|
|
services/Speech.qml souveraine/services/Speech.qml
|
|
services/NotifyEvents.qml souveraine/services/NotifyEvents.qml
|
|
services/CrashReporter.qml souveraine/services/CrashReporter.qml
|
|
modules/ii/sidebarLeft/SidebarLeft.qml souveraine/modules/ii/sidebarLeft/SidebarLeft.qml
|
|
modules/ii/sidebarLeft/AiChat.qml souveraine/modules/ii/sidebarLeft/AiChat.qml
|
|
modules/ii/sidebarRight/SidebarRight.qml souveraine/modules/ii/sidebarRight/SidebarRight.qml
|
|
modules/ii/sidebarRight/QuickSliders.qml souveraine/modules/ii/sidebarRight/QuickSliders.qml
|
|
modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml souveraine/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml
|
|
modules/ii/bar/UtilButtons.qml souveraine/modules/ii/bar/UtilButtons.qml
|
|
modules/common/Config.qml souveraine/modules/common/Config.qml
|
|
modules/common/ShellModel.qml souveraine/modules/common/ShellModel.qml
|
|
modules/common/functions/Session.qml souveraine/modules/common/functions/Session.qml
|
|
modules/common/widgets/ContentPage.qml souveraine/modules/common/widgets/ContentPage.qml
|
|
modules/common/widgets/StyledToolTip.qml souveraine/modules/common/widgets/StyledToolTip.qml
|
|
modules/settings/DeviceConfig.qml souveraine/modules/settings/DeviceConfig.qml
|
|
modules/settings/LockConfig.qml souveraine/modules/settings/LockConfig.qml
|
|
modules/settings/WallpaperConfig.qml souveraine/modules/settings/WallpaperConfig.qml
|
|
modules/settings/DockConfig.qml souveraine/modules/settings/DockConfig.qml
|
|
modules/settings/NavigationConfig.qml souveraine/modules/settings/NavigationConfig.qml
|
|
modules/settings/KeyboardConfig.qml souveraine/modules/settings/KeyboardConfig.qml
|
|
modules/settings/IdleConfig.qml souveraine/modules/settings/IdleConfig.qml
|
|
modules/settings/SpeechConfig.qml souveraine/modules/settings/SpeechConfig.qml
|
|
modules/ii/polkit/Polkit.qml souveraine/modules/ii/polkit/Polkit.qml
|
|
modules/ii/dock/Dock.qml souveraine/modules/ii/dock/Dock.qml
|
|
modules/ii/dock/DockManifest.qml souveraine/modules/ii/dock/DockManifest.qml
|
|
modules/ii/dock/DockApps.qml souveraine/modules/ii/dock/DockApps.qml
|
|
modules/ii/dock/DockAppButton.qml souveraine/modules/ii/dock/DockAppButton.qml
|
|
modules/ii/dock/DockButton.qml souveraine/modules/ii/dock/DockButton.qml
|
|
modules/ii/dock/DockSeparator.qml souveraine/modules/ii/dock/DockSeparator.qml
|
|
modules/ii/dock/DockStack.qml souveraine/modules/ii/dock/DockStack.qml
|
|
modules/ii/appInventory/AppInventory.qml souveraine/modules/ii/appInventory/AppInventory.qml
|
|
modules/ii/appInventory/AppInventoryScope.qml souveraine/modules/ii/appInventory/AppInventoryScope.qml
|
|
modules/ii/overview/Overview.qml souveraine/modules/ii/overview/Overview.qml
|
|
modules/ii/screenCorners/ScreenCorners.qml souveraine/modules/ii/screenCorners/ScreenCorners.qml
|
|
modules/ii/onScreenKeyboard/OnScreenKeyboard.qml souveraine/modules/ii/onScreenKeyboard/OnScreenKeyboard.qml
|
|
modules/common/Persistent.qml souveraine/modules/common/Persistent.qml
|
|
modules/common/panels/lock/LockScreen.qml souveraine/modules/common/panels/lock/LockScreen.qml
|
|
modules/ii/lock/Lock.qml souveraine/modules/ii/lock/Lock.qml
|
|
modules/ii/lock/TouchLockSurface.qml souveraine/modules/ii/lock/TouchLockSurface.qml
|
|
modules/souveraine/lock/LockMediaCard.qml souveraine/modules/souveraine/lock/LockMediaCard.qml
|
|
modules/souveraine/lock/LockAgentCard.qml souveraine/modules/souveraine/lock/LockAgentCard.qml
|
|
modules/souveraine/lock/LockNotifyCard.qml souveraine/modules/souveraine/lock/LockNotifyCard.qml
|
|
modules/souveraine/lock/LockSurfaceHost.qml souveraine/modules/souveraine/lock/LockSurfaceHost.qml
|
|
modules/souveraine/lock/qmldir souveraine/modules/souveraine/lock/qmldir
|
|
modules/souveraine/navigation/SystemGestureRail.qml souveraine/modules/souveraine/navigation/SystemGestureRail.qml
|
|
modules/souveraine/navigation/qmldir souveraine/modules/souveraine/navigation/qmldir
|
|
modules/souveraine/boot/BootBloom.qml souveraine/modules/souveraine/boot/BootBloom.qml
|
|
modules/souveraine/boot/BootBloom.frag.qsb souveraine/modules/souveraine/boot/BootBloom.frag.qsb
|
|
modules/souveraine/boot/souvie.png souveraine/modules/souveraine/boot/souvie.png
|
|
modules/souveraine/boot/qmldir souveraine/modules/souveraine/boot/qmldir
|
|
modules/ii/sessionScreen/SessionScreen.qml souveraine/modules/ii/sessionScreen/SessionScreen.qml
|
|
qmldir souveraine/qmldir
|
|
panelFamilies/qmldir souveraine/panelFamilies/qmldir
|
|
services/qmldir souveraine/services/qmldir
|
|
modules/common/qmldir souveraine/modules/common/qmldir
|
|
modules/common/functions/qmldir souveraine/modules/common/functions/qmldir
|
|
modules/common/panels/lock/qmldir souveraine/modules/common/panels/lock/qmldir
|
|
modules/common/widgets/qmldir souveraine/modules/common/widgets/qmldir
|
|
modules/ii/appInventory/qmldir souveraine/modules/ii/appInventory/qmldir
|
|
modules/ii/bar/qmldir souveraine/modules/ii/bar/qmldir
|
|
modules/ii/dock/qmldir souveraine/modules/ii/dock/qmldir
|
|
modules/ii/lock/qmldir souveraine/modules/ii/lock/qmldir
|
|
modules/ii/onScreenKeyboard/qmldir souveraine/modules/ii/onScreenKeyboard/qmldir
|
|
modules/ii/overview/qmldir souveraine/modules/ii/overview/qmldir
|
|
modules/ii/polkit/qmldir souveraine/modules/ii/polkit/qmldir
|
|
modules/ii/screenCorners/qmldir souveraine/modules/ii/screenCorners/qmldir
|
|
modules/ii/sessionScreen/qmldir souveraine/modules/ii/sessionScreen/qmldir
|
|
modules/ii/sidebarLeft/qmldir souveraine/modules/ii/sidebarLeft/qmldir
|
|
modules/ii/sidebarRight/qmldir souveraine/modules/ii/sidebarRight/qmldir
|
|
modules/ii/sidebarRight/volumeMixer/qmldir souveraine/modules/ii/sidebarRight/volumeMixer/qmldir
|
|
modules/settings/qmldir souveraine/modules/settings/qmldir
|
|
"
|
|
|
|
# wifi fallback: PHONE_HOST=casey@10.10.20.234 ./deploy.sh --phone
|
|
PHONE_USB="${PHONE_HOST:-casey@172.16.42.1}"
|
|
PHONE_DEST="souveraine-surfaces/quickshell"
|
|
|
|
manifest_lines() { printf '%s\n' "$MANIFEST" | sed '/^[[:space:]]*$/d'; }
|
|
|
|
# Compile the boot bloom shader to .qsb (Qt RHI) if the source is newer. The
|
|
# .qsb is architecture-independent (SPIR-V + reflection), so we build it once
|
|
# here and the manifest symlink / phone rsync carries it — no qsb on the phone.
|
|
compile_shaders() {
|
|
local frag="$SRC/modules/souveraine/boot/BootBloom.frag"
|
|
local qsb_out="$SRC/modules/souveraine/boot/BootBloom.frag.qsb"
|
|
[[ -f "$frag" ]] || return 0
|
|
if [[ ! -f "$qsb_out" || "$frag" -nt "$qsb_out" ]]; then
|
|
local qsb
|
|
qsb="$(command -v qsb || echo /usr/lib/qt6/bin/qsb)"
|
|
if [[ ! -x "$qsb" ]]; then
|
|
echo "ERROR: qsb not found (need qt6-shadertools) to build BootBloom.frag.qsb" >&2
|
|
exit 1
|
|
fi
|
|
# --glsl 100es,120,150 + --hlsl/--msl targets so it runs on the Adreno
|
|
# GLES backend the phone uses; -O optimizes.
|
|
"$qsb" --glsl "100es,120,150" --hlsl 50 --msl 12 -O -o "$qsb_out" "$frag"
|
|
echo "compiled BootBloom.frag -> BootBloom.frag.qsb"
|
|
fi
|
|
}
|
|
compile_shaders
|
|
|
|
if [[ "${1:-}" == "--manifest" ]]; then
|
|
manifest_lines
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "${1:-}" == "--phone" ]]; then
|
|
ssh_i=(ssh -i "$HOME/.ssh/ani" -o ConnectTimeout=5)
|
|
deployed_revision="unknown"
|
|
if repo_root="$(git -C "$SRC" rev-parse --show-toplevel 2>/dev/null)"; then
|
|
deployed_revision="$(git -C "$repo_root" rev-parse HEAD)"
|
|
if ! git -C "$repo_root" diff --quiet -- "$SRC" \
|
|
|| [[ -n "$(git -C "$repo_root" ls-files --others --exclude-standard -- "$SRC")" ]]; then
|
|
deployed_revision="${deployed_revision}-dirty"
|
|
fi
|
|
fi
|
|
"${ssh_i[@]}" "$PHONE_USB" "mkdir -p ~/$PHONE_DEST"
|
|
rsync -a --delete -e "ssh -i $HOME/.ssh/ani" "$SRC/" "$PHONE_USB:$PHONE_DEST/"
|
|
printf '%s\n' "$deployed_revision" \
|
|
| "${ssh_i[@]}" "$PHONE_USB" "cat > ~/$PHONE_DEST/DEPLOYED-REVISION"
|
|
"${ssh_i[@]}" "$PHONE_USB" "bash ~/$PHONE_DEST/deploy.sh"
|
|
# Phone-only: install the mobile settings launcher into the app grid.
|
|
"${ssh_i[@]}" "$PHONE_USB" "mkdir -p ~/.local/share/applications && ln -sf ~/$PHONE_DEST/souveraine-settings.desktop ~/.local/share/applications/souveraine-settings.desktop"
|
|
echo "Deployed to phone. Hyprland starts qs -c souveraine; no secondary shell is required."
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "${1:-}" == "-u" || "${1:-}" == "--uninstall" ]]; then
|
|
rm -rf "$SV"
|
|
echo "removed $SV (ii tree untouched)"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "${1:-}" == "--legacy-clean" ]]; then
|
|
# One-time migration: strip the old overlay out of ii.
|
|
# 1. remove any symlink in ii that points into a souveraine checkout
|
|
find "$II" -type l | while read -r l; do
|
|
case "$(readlink "$l")" in
|
|
*souveraine*) rm -f "$l"; echo "unlinked ${l#$QS/}" ;;
|
|
esac
|
|
done
|
|
# 2. restore upstream backups
|
|
find "$II" -name '*.upstream' | while read -r u; do
|
|
mv -f "$u" "${u%.upstream}"
|
|
echo "restored ${u%.upstream}"
|
|
done
|
|
exit 0
|
|
fi
|
|
|
|
# --- Sync the pinned ii base --------------------------------------------
|
|
# --exclude .git: the pin carries no VCS dirs, and any nested .git already
|
|
# on a device (e.g. widgets/shapes) is left alone rather than deleted.
|
|
mkdir -p "$QS"
|
|
rsync -a --delete --exclude='.git' "$SRC/ii-base/" "$II/"
|
|
if [[ "$TARGET_ARCH" == "aarch64" ]]; then
|
|
rsync -a "$SRC/ii-phone/" "$II/"
|
|
echo "ii synced from pin + phone overlay"
|
|
else
|
|
echo "ii synced from pin"
|
|
fi
|
|
install -m 0644 "$SRC/ii-base.pin" "$II/.souveraine-upstream-pin"
|
|
|
|
# --- Compose -------------------------------------------------------------
|
|
# Targets under souveraine/, relative to $SV
|
|
SV_TARGETS="$(manifest_lines | awk '$2 ~ /^souveraine\// {sub(/^souveraine\//, "", $2); print $2}')"
|
|
|
|
is_replaced() { # exact file override
|
|
grep -qxF "$1" <<< "$SV_TARGETS"
|
|
}
|
|
is_touched() { # dir contains an override somewhere below
|
|
grep -q "^$1/" <<< "$SV_TARGETS"
|
|
}
|
|
|
|
compose_dir() { # $1 = path relative to ii root ("" for root)
|
|
local rel="$1" entry name erel
|
|
mkdir -p "$SV${rel:+/$rel}"
|
|
for entry in "$II${rel:+/$rel}"/*; do
|
|
[[ -e "$entry" ]] || continue
|
|
name="$(basename "$entry")"
|
|
[[ "$name" == *.upstream ]] && continue
|
|
erel="${rel:+$rel/}$name"
|
|
if [[ -d "$entry" ]]; then
|
|
if is_touched "$erel"; then
|
|
compose_dir "$erel"
|
|
else
|
|
ln -sfn "$II/$erel" "$SV/$erel"
|
|
fi
|
|
else
|
|
# root files and files inside touched dirs: link individually,
|
|
# skipping ones our manifest replaces
|
|
is_replaced "$erel" && continue
|
|
ln -sf "$II/$erel" "$SV/$erel"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Rebuild from scratch each run: cheap, and guarantees no stale links.
|
|
rm -rf "$SV"
|
|
compose_dir ""
|
|
|
|
# Our files on top (this also creates dirs that exist only in our tree,
|
|
# e.g. brand-new settings pages)
|
|
mkdir -p "$QS/pill"
|
|
while read -r rel target; do
|
|
[[ -z "$rel" ]] && continue
|
|
src="$SRC/$rel"; t="$QS/$target"
|
|
[[ -f "$src" ]] || { echo "missing $src" >&2; exit 1; }
|
|
mkdir -p "$(dirname "$t")"
|
|
ln -sf "$src" "$t"
|
|
done < <(manifest_lines)
|
|
|
|
n_ours=$(manifest_lines | grep -c .)
|
|
n_borrowed_dirs=$(find "$SV" -maxdepth 3 -type l -xtype d | wc -l)
|
|
echo "souveraine config composed at $SV ($n_ours files ours, $n_borrowed_dirs dirs borrowed from ii)"
|
|
if grep -q 'qsConfig", "ii"' "$HOME/.config/hypr/hyprland/variables.lua" 2>/dev/null; then
|
|
echo "NOTE: hyprland qsConfig is still 'ii' — flip variables.lua to 'souveraine' to switch."
|
|
fi
|
|
echo "Run with: qs -c souveraine (restart: pkill -f 'qs -c souveraine')"
|