Files
niri-beyond-glass/install.sh
T
2026-07-27 16:58:57 +08:00

518 lines
18 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -Eeuo pipefail
readonly PROJECT="niri-beyond-glass"
readonly NIRI_TAG="v26.04"
readonly XWS_TAG="v0.8.2"
readonly RUST_TOOLCHAIN="1.85.1"
readonly DMS_REV="069ddab041c738236a8910e4c39b65d9628d3018"
readonly NIXPKGS_REV="d407951447dcd00442e97087bf374aad70c04cea"
readonly ARCHIVE_URL="https://git.k1412.top/wuyang/niri-beyond-glass/archive/main.tar.gz"
wallpaper=""
config_only=0
dry_run=0
rebuild=0
source_dir="${NBG_SOURCE_DIR:-}"
tmp_dir=""
selected_wallpaper=""
usage() {
cat <<'EOF'
用法:install.sh [选项]
--wallpaper PATH 使用指定的静态壁纸
--config-only 只更新配置(要求 DMS 运行层已经安装)
--rebuild 强制重新编译 Niri 和 Xwayland Satellite
--dry-run 显示将执行的主要步骤,不修改系统
-h, --help 显示帮助
EOF
}
log() {
printf '\033[1;36m[Niri]\033[0m %s\n' "$*"
}
die() {
printf '\033[1;31m错误:\033[0m %s\n' "$*" >&2
exit 1
}
run() {
if (( dry_run )); then
printf '+'
printf ' %q' "$@"
printf '\n'
else
"$@"
fi
}
cleanup() {
if [[ -n "$tmp_dir" && -d "$tmp_dir" ]]; then
rm -rf "$tmp_dir"
fi
}
trap cleanup EXIT
while (($#)); do
case "$1" in
--wallpaper)
(($# >= 2)) || die "--wallpaper 缺少路径"
wallpaper="$2"
shift 2
;;
--config-only)
config_only=1
shift
;;
--rebuild)
rebuild=1
shift
;;
--dry-run)
dry_run=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
die "未知参数:$1"
;;
esac
done
[[ ${EUID:-$(id -u)} -ne 0 ]] || die "请用普通用户运行,脚本会在需要时调用 sudo。"
[[ -z "$wallpaper" || -f "$wallpaper" ]] || die "壁纸不存在:$wallpaper"
if [[ -r /etc/os-release ]]; then
# shellcheck disable=SC1091
. /etc/os-release
else
die "无法识别操作系统。"
fi
if [[ "${ID:-}" != "ubuntu" || "${VERSION_ID:-}" != "24.04" ]]; then
die "只支持 Ubuntu 24.04;当前是 ${PRETTY_NAME:-未知系统}"
fi
case "$(uname -m)" in
x86_64|aarch64) ;;
*) die "目前只支持 x86_64 和 aarch64。" ;;
esac
fetch_source() {
if [[ -n "$source_dir" ]]; then
[[ -f "$source_dir/config/niri/config.kdl" ]] || die "NBG_SOURCE_DIR 不是有效源码目录。"
return
fi
tmp_dir="$(mktemp -d)"
log "下载配置仓库"
curl -fL --retry 3 "$ARCHIVE_URL" -o "$tmp_dir/source.tar.gz"
mkdir -p "$tmp_dir/source"
tar -xzf "$tmp_dir/source.tar.gz" -C "$tmp_dir/source" --strip-components=1
source_dir="$tmp_dir/source"
}
install_packages() {
log "安装 Ubuntu 24.04 依赖"
run sudo apt-get update
run sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y \
build-essential clang git curl ca-certificates pkg-config \
meson ninja-build cmake hwdata \
libudev-dev libgbm-dev libxkbcommon-dev libegl1-mesa-dev \
libwayland-dev libinput-dev libdbus-1-dev libsystemd-dev \
libseat-dev libpipewire-0.3-dev libpango1.0-dev \
libdisplay-info-dev libcairo2-dev libglib2.0-dev \
libxcb1-dev libxcb-composite0-dev libxcb-randr0-dev \
libxcb-res0-dev libxcb-xfixes0-dev libxcb-cursor-dev \
xwayland xdg-desktop-portal-gnome xdg-desktop-portal-gtk \
alacritty \
nautilus policykit-1-gnome pipewire-pulse wireplumber \
brightnessctl playerctl grim slurp wl-clipboard libnotify-bin \
accountsservice jq nix-bin nix-setup-systemd \
fonts-noto-core fonts-noto-cjk fonts-noto-color-emoji fonts-font-awesome \
fonts-inter fonts-jetbrains-mono
}
install_dms_runtime() {
local state_home="${XDG_STATE_HOME:-$HOME/.local/state}"
local profile="$state_home/$PROJECT/nix-profile"
local dms_flake="github:AvengeMedia/DankMaterialShell/$DMS_REV"
local nixpkgs="github:NixOS/nixpkgs/$NIXPKGS_REV"
if [[ -x "$profile/bin/dms" && -x "$profile/bin/quickshell" &&
-x "$profile/bin/matugen" && -x "$profile/bin/dgop" &&
-x "$profile/bin/dsearch" ]]; then
log "DMS 运行层已经安装,跳过"
return
fi
log "安装隔离的 DMS 1.5.3 与 Quickshell 0.3.0"
run sudo usermod -aG nix-users "$USER"
run sudo systemctl enable --now nix-daemon.socket
run mkdir -p "$state_home/$PROJECT"
if (( dry_run )); then
printf '+ sudo -u %q -g nix-users nix profile install --profile %q ...\n' \
"$USER" "$profile"
return
fi
local attempt
for attempt in 1 2 3 4 5; do
if sudo -u "$USER" -g nix-users env \
HOME="$HOME" USER="$USER" \
NIX_CONFIG="experimental-features = nix-command flakes" \
nix profile install --profile "$profile" \
"$dms_flake#dms-shell" \
"$nixpkgs#quickshell" \
"$nixpkgs#matugen" \
"$nixpkgs#dgop" \
"$nixpkgs#dsearch"; then
break
fi
(( attempt < 5 )) || die "DMS 运行层安装失败,请检查网络后重新运行脚本。"
log "下载中断,正在重试($attempt/5"
sleep 3
done
[[ -x "$profile/bin/dms" && -x "$profile/bin/quickshell" ]] ||
die "DMS 或 Quickshell 安装不完整。"
}
install_rust() {
export RUSTUP_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/$PROJECT/rustup"
export CARGO_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/$PROJECT/cargo"
export PATH="$CARGO_HOME/bin:$PATH"
if [[ ! -x "$CARGO_HOME/bin/rustup" ]]; then
log "安装隔离的 Rust $RUST_TOOLCHAIN 工具链"
if (( dry_run )); then
printf '+ curl https://sh.rustup.rs | sh -s -- -y --no-modify-path --profile minimal --default-toolchain %q\n' "$RUST_TOOLCHAIN"
return
fi
local rustup_installer
rustup_installer="$(mktemp)"
curl --proto '=https' --tlsv1.2 -fsSL --retry 5 --retry-all-errors \
https://sh.rustup.rs -o "$rustup_installer"
RUSTUP_MAX_RETRIES=5 sh "$rustup_installer" -y --no-modify-path \
--profile minimal --default-toolchain "$RUST_TOOLCHAIN"
rm -f "$rustup_installer"
elif ! "$CARGO_HOME/bin/rustup" run "$RUST_TOOLCHAIN" rustc --version >/dev/null 2>&1; then
RUSTUP_MAX_RETRIES=5 run "$CARGO_HOME/bin/rustup" toolchain install \
"$RUST_TOOLCHAIN" --profile minimal
fi
run "$CARGO_HOME/bin/rustup" set auto-self-update disable
run "$CARGO_HOME/bin/rustup" default "$RUST_TOOLCHAIN"
}
version_ge() {
[[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n1)" == "$2" ]]
}
install_libdisplay_info() {
local installed=""
installed="$(pkg-config --modversion libdisplay-info 2>/dev/null || true)"
if [[ -n "$installed" ]] && version_ge "$installed" "0.3.0"; then
return
fi
log "编译 libdisplay-info 0.3.0Ubuntu 24.04 自带版本较旧)"
local build_root="${XDG_CACHE_HOME:-$HOME/.cache}/$PROJECT/build"
local src="$build_root/libdisplay-info"
run mkdir -p "$build_root"
if [[ ! -d "$src/.git" ]]; then
run git clone --depth 1 --branch 0.3.0 \
https://gitlab.freedesktop.org/emersion/libdisplay-info.git "$src"
fi
run meson setup "$src/build" "$src" --prefix=/usr/local --buildtype=release --wipe
run ninja -C "$src/build"
run sudo ninja -C "$src/build" install
run sudo ldconfig
}
install_niri() {
local current=""
current="$(niri --version 2>/dev/null || true)"
if (( ! rebuild )) && [[ "$current" == *"26.4"* || "$current" == *"26.04"* ]]; then
log "已安装兼容的 $current,跳过编译"
return
fi
log "编译 Niri $NIRI_TAG"
local build_root="${XDG_CACHE_HOME:-$HOME/.cache}/$PROJECT/build"
local src="$build_root/niri"
run mkdir -p "$build_root"
if [[ ! -d "$src/.git" ]]; then
run git clone --depth 1 --branch "$NIRI_TAG" https://github.com/niri-wm/niri.git "$src"
else
run git -C "$src" fetch --depth 1 origin "refs/tags/$NIRI_TAG:refs/tags/$NIRI_TAG"
run git -C "$src" checkout -f "$NIRI_TAG"
fi
run cargo build --manifest-path "$src/Cargo.toml" --release --locked
run sudo install -Dm755 "$src/target/release/niri" /usr/local/bin/niri
run sudo install -Dm755 "$src/resources/niri-session" /usr/local/bin/niri-session
run sudo install -Dm644 "$src/resources/niri.service" /usr/local/lib/systemd/user/niri.service
run sudo install -Dm644 "$src/resources/niri-shutdown.target" /usr/local/lib/systemd/user/niri-shutdown.target
run sudo install -Dm644 "$src/resources/niri.desktop" /usr/local/share/wayland-sessions/niri.desktop
run sudo install -Dm644 "$src/resources/niri-portals.conf" /usr/local/share/xdg-desktop-portal/niri-portals.conf
}
install_xwayland_satellite() {
if (( ! rebuild )) && command -v xwayland-satellite >/dev/null 2>&1; then
return
fi
log "编译 Xwayland Satellite $XWS_TAG"
local build_root="${XDG_CACHE_HOME:-$HOME/.cache}/$PROJECT/build"
local src="$build_root/xwayland-satellite"
run mkdir -p "$build_root"
if [[ ! -d "$src/.git" ]]; then
run git clone --depth 1 --branch "$XWS_TAG" \
https://github.com/Supreeeme/xwayland-satellite.git "$src"
else
run git -C "$src" fetch --depth 1 origin "refs/tags/$XWS_TAG:refs/tags/$XWS_TAG"
run git -C "$src" checkout -f "$XWS_TAG"
fi
run cargo build --manifest-path "$src/Cargo.toml" --release --locked
run sudo install -Dm755 "$src/target/release/xwayland-satellite" /usr/local/bin/xwayland-satellite
}
backup_and_install_config() {
local config_home="${XDG_CONFIG_HOME:-$HOME/.config}"
local state_home="${XDG_STATE_HOME:-$HOME/.local/state}"
local stamp
stamp="$(date +%Y%m%d-%H%M%S)"
local backup_root="$state_home/$PROJECT/backups/$stamp"
local backup="$backup_root/config"
local original="$state_home/$PROJECT/original"
local names=(niri waybar fuzzel mako swaylock niri-beyond-glass DankMaterialShell alacritty)
select_wallpaper "$config_home/niri-beyond-glass/wallpaper"
log "备份并安装用户配置"
run mkdir -p "$backup"
local name
for name in "${names[@]}"; do
if [[ -e "$config_home/$name" ]]; then
run cp -a "$config_home/$name" "$backup/$name"
fi
done
if [[ -d "$state_home/DankMaterialShell" ]]; then
run mkdir -p "$backup_root/state"
run cp -a "$state_home/DankMaterialShell" "$backup_root/state/DankMaterialShell"
fi
run mkdir -p "$original/config" "$original/absent"
for name in "${names[@]}"; do
if [[ ! -e "$original/config/$name" && ! -e "$original/absent/$name" ]]; then
if [[ -e "$config_home/$name" ]]; then
run cp -a "$config_home/$name" "$original/config/$name"
else
run touch "$original/absent/$name"
fi
fi
done
run touch "$original/.captured"
if (( dry_run )); then
printf '+ install DMS, Niri and Alacritty style while preserving wallpaper %q\n' \
"$selected_wallpaper"
return
fi
mkdir -p \
"$config_home/niri" \
"$config_home/niri/dms" \
"$config_home/niri-beyond-glass" \
"$config_home/DankMaterialShell/themes" \
"$config_home/alacritty" \
"$config_home/systemd/user" \
"$state_home/DankMaterialShell" \
"$HOME/.local/bin" \
"$HOME/Pictures/Screenshots"
install -m644 "$source_dir/config/niri/config.kdl" "$config_home/niri/config.kdl"
sed "s|__HOME__|$HOME|g" "$source_dir/config/niri/beyond-glass.kdl" \
> "$config_home/niri/beyond-glass.kdl"
install -m644 "$source_dir"/config/niri/dms/*.kdl "$config_home/niri/dms/"
local theme_file="$config_home/DankMaterialShell/themes/beyond-glass.json"
install -m644 "$source_dir/config/dms/beyond-glass.json" "$theme_file"
local settings_tmp session_tmp
settings_tmp="$(mktemp)"
session_tmp="$(mktemp)"
jq --arg wallpaper "$selected_wallpaper" --arg theme "$theme_file" '
.customThemeFile = $theme
| .greeterWallpaperPath = $wallpaper
| .lockScreenWallpaperPath = $wallpaper
| .lockScreenVideoPath = ""
' "$source_dir/config/dms/settings.json" > "$settings_tmp"
jq --arg wallpaper "$selected_wallpaper" '
.wallpaperPath = $wallpaper
| .wallpaperPathLight = $wallpaper
| .wallpaperPathDark = $wallpaper
| .monitorWallpapers = {}
| .monitorWallpapersLight = {}
| .monitorWallpapersDark = {}
| .perMonitorWallpaper = false
' "$source_dir/config/dms/session.json" > "$session_tmp"
install -m644 "$settings_tmp" "$config_home/DankMaterialShell/settings.json"
install -m644 "$session_tmp" "$state_home/DankMaterialShell/session.json"
rm -f "$settings_tmp" "$session_tmp"
touch "$config_home/DankMaterialShell/.firstlaunch" \
"$config_home/DankMaterialShell/.changelog-1.5"
install -m644 "$source_dir/config/alacritty/alacritty.toml" \
"$config_home/alacritty/alacritty.toml"
install -m644 "$source_dir/config/systemd/dms.service" \
"$config_home/systemd/user/dms.service"
install -m755 "$source_dir/scripts/niri-beyond-browser" \
"$HOME/.local/bin/niri-beyond-browser"
install -m755 "$source_dir/scripts/niri-beyond-refresh" \
"$HOME/.local/bin/niri-beyond-refresh"
local profile="$state_home/$PROJECT/nix-profile"
local binary
for binary in dms quickshell qs matugen dgop dsearch; do
if [[ -x "$profile/bin/$binary" ]]; then
ln -sfn "$profile/bin/$binary" "$HOME/.local/bin/$binary"
fi
done
printf '%s\n' "$selected_wallpaper" > "$config_home/niri-beyond-glass/wallpaper"
}
select_wallpaper() {
local state_file="$1"
local selected="$wallpaper"
if [[ -z "$selected" ]]; then
local existing=""
if [[ -r "$state_file" ]]; then
existing="$(head -n1 "$state_file")"
fi
if [[ -n "$existing" && -f "$existing" ]]; then
selected="$existing"
fi
fi
if [[ -z "$selected" ]]; then
local session="${XDG_STATE_HOME:-$HOME/.local/state}/DankMaterialShell/session.json"
local existing=""
if [[ -r "$session" ]]; then
existing="$(jq -r '.wallpaperPath // empty' "$session" 2>/dev/null || true)"
fi
if [[ -n "$existing" && -f "$existing" ]]; then
selected="$existing"
fi
fi
if [[ -z "$selected" ]] && command -v gsettings >/dev/null 2>&1; then
local uri=""
uri="$(gsettings get org.gnome.desktop.background picture-uri-dark 2>/dev/null \
| tr -d "'" || true)"
uri="${uri#file://}"
if [[ -n "$uri" && -f "$uri" ]]; then
selected="$uri"
fi
fi
if [[ -z "$selected" ]]; then
local candidates=(
/usr/share/backgrounds/warty-final-ubuntu.png
/usr/share/backgrounds/ubuntu-default-greyscale-wallpaper.png
)
local candidate
for candidate in "${candidates[@]}"; do
if [[ -f "$candidate" ]]; then
selected="$candidate"
break
fi
done
fi
if [[ -z "$selected" ]]; then
selected="$(find /usr/share/backgrounds -maxdepth 2 -type f \
\( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' \) \
-print -quit 2>/dev/null || true)"
fi
[[ -n "$selected" && -f "$selected" ]] ||
die "没有找到可用壁纸;可用 --wallpaper PATH 指定目标机上的图片。"
selected_wallpaper="$selected"
log "保留目标机壁纸:$selected_wallpaper"
}
activate_dms() {
local profile="${XDG_STATE_HOME:-$HOME/.local/state}/$PROJECT/nix-profile"
if (( dry_run )); then
printf '+ stop legacy shell processes and enable dms.service\n'
return
fi
systemctl --user disable --now waybar.service mako.service 2>/dev/null || true
pkill -x waybar 2>/dev/null || true
pkill -x mako 2>/dev/null || true
pkill -x swaybg 2>/dev/null || true
pkill -x swayidle 2>/dev/null || true
systemctl --user reset-failed mako.service dms.service 2>/dev/null || true
if ! systemctl --user daemon-reload 2>/dev/null; then
log "当前没有可用的 systemd 用户会话;DMS 将在下次 Niri 登录时启动"
return
fi
systemctl --user enable dms.service
"$profile/bin/dms" auth resolve-lock --quiet >/dev/null 2>&1 || true
if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then
systemctl --user restart dms.service
fi
}
validate_config() {
if (( dry_run )); then
printf '+ niri validate -c %q\n' "${XDG_CONFIG_HOME:-$HOME/.config}/niri/config.kdl"
return
fi
command -v niri >/dev/null 2>&1 || die "找不到 niri,无法验证配置。"
niri validate -c "${XDG_CONFIG_HOME:-$HOME/.config}/niri/config.kdl"
}
main() {
fetch_source
if (( ! config_only )); then
install_packages
install_rust
install_libdisplay_info
install_niri
install_xwayland_satellite
install_dms_runtime
else
local profile="${XDG_STATE_HOME:-$HOME/.local/state}/$PROJECT/nix-profile"
[[ -x "$profile/bin/dms" && -x "$profile/bin/quickshell" ]] ||
die "--config-only 需要先完成一次完整安装。"
fi
backup_and_install_config
validate_config
activate_dms
log "安装完成"
printf '\nDMS 个性化已迁移,目标机原壁纸和显示器布局未修改。\n'
printf '当前若在 Niri 中,顶栏会自动重启;否则注销后选择“Niri”登录。\n'
printf 'GNOME 和 GDM 均未修改;快捷键说明:Super+Shift+/。\n'
}
main