361 lines
13 KiB
Bash
361 lines
13 KiB
Bash
#!/bin/bash
|
||
# 04c-quickshell-setup.sh
|
||
|
||
# 1. 引用工具库
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
|
||
if [[ -f "$SCRIPT_DIR/00-utils.sh" ]]; then
|
||
source "$SCRIPT_DIR/00-utils.sh"
|
||
else
|
||
echo "Error: 00-utils.sh not found in $SCRIPT_DIR."
|
||
exit 1
|
||
fi
|
||
|
||
# ==============================================================================
|
||
# 核心辅助函数定义
|
||
# ==============================================================================
|
||
|
||
# --- 函数:强制安全拷贝(解决覆盖冲突) ---
|
||
force_copy() {
|
||
local src="$1"
|
||
local target_dir="$2"
|
||
|
||
if [[ -z "$src" || -z "$target_dir" ]]; then
|
||
warn "force_copy: Missing arguments"
|
||
return 1
|
||
fi
|
||
|
||
local item_name
|
||
item_name=$(basename "$src")
|
||
|
||
# 只有当拷贝的不是 "目录下的所有内容" (即路径不以 /. 结尾) 时,才执行精确删除
|
||
if [[ "$src" != */. ]]; then
|
||
# 清理 target_dir 结尾多余的斜杠或 /.
|
||
local clean_target="${target_dir%/}"
|
||
clean_target="${clean_target%/.}"
|
||
|
||
# 先安全删除目标路径的同名内容,防止目录覆盖文件的冲突
|
||
as_user rm -rf "${clean_target}/${item_name}"
|
||
fi
|
||
|
||
# 执行安全拷贝
|
||
exe as_user cp -rf "$src" "$target_dir"
|
||
}
|
||
|
||
# --- 函数:静默删除 niri 绑定 ---
|
||
niri_remove_bind() {
|
||
local target_key="$1"
|
||
local config_file="$HOME_DIR/.config/niri/dms/binds.kdl"
|
||
|
||
if [[ ! -f "$config_file" ]]; then
|
||
return 1
|
||
fi
|
||
|
||
# 使用 Python 处理,无日志,无备份
|
||
python3 -c "
|
||
import sys, re
|
||
|
||
file_path = '$config_file'
|
||
target_key = sys.argv[1]
|
||
|
||
try:
|
||
with open(file_path, 'r') as f:
|
||
content = f.read()
|
||
|
||
pattern = re.compile(r'(?m)^\s*(?!//).*?' + re.escape(target_key) + r'(?=\s|\{)')
|
||
|
||
while True:
|
||
match = pattern.search(content)
|
||
if not match:
|
||
break
|
||
|
||
start_idx = match.start()
|
||
open_brace_idx = content.find('{', start_idx)
|
||
if open_brace_idx == -1:
|
||
break
|
||
|
||
balance = 0
|
||
end_idx = -1
|
||
for i in range(open_brace_idx, len(content)):
|
||
char = content[i]
|
||
if char == '{':
|
||
balance += 1
|
||
elif char == '}':
|
||
balance -= 1
|
||
if balance == 0:
|
||
end_idx = i + 1
|
||
break
|
||
|
||
if end_idx != -1:
|
||
if end_idx < len(content) and content[end_idx] == '\n':
|
||
end_idx += 1
|
||
content = content[:start_idx] + content[end_idx:]
|
||
else:
|
||
break
|
||
|
||
with open(file_path, 'w') as f:
|
||
f.write(content)
|
||
|
||
except Exception:
|
||
pass
|
||
" "$target_key"
|
||
}
|
||
|
||
VERIFY_LIST="/tmp/shorin_install_verify.list"
|
||
rm -f "$VERIFY_LIST"
|
||
|
||
log "Installing DMS..."
|
||
# ==============================================================================
|
||
# Identify User & DM Check
|
||
# ==============================================================================
|
||
log "Identifying user..."
|
||
detect_target_user
|
||
|
||
if [[ -z "$TARGET_USER" || ! -d "$HOME_DIR" ]]; then
|
||
error "Target user invalid or home directory does not exist."
|
||
exit 1
|
||
fi
|
||
|
||
info_kv "Target" "$TARGET_USER"
|
||
|
||
# DM Check
|
||
check_dm_conflict
|
||
|
||
log "Target user for DMS installation: $TARGET_USER"
|
||
|
||
# 下载并执行安装脚本
|
||
INSTALLER_SCRIPT="/tmp/dms_install.sh"
|
||
DMS_URL="https://install.danklinux.com"
|
||
|
||
log "Downloading DMS installer wrapper..."
|
||
if curl -fsSL "$DMS_URL" -o "$INSTALLER_SCRIPT"; then
|
||
chmod +x "$INSTALLER_SCRIPT"
|
||
chown "$TARGET_USER" "$INSTALLER_SCRIPT"
|
||
|
||
log "Executing DMS installer as user ($TARGET_USER)..."
|
||
log "NOTE: If the installer asks for input, this script might hang."
|
||
pacman -S --noconfirm vulkan-headers
|
||
if runuser -u "$TARGET_USER" -- bash -c "cd ~ && $INSTALLER_SCRIPT"; then
|
||
success "DankMaterialShell installed successfully."
|
||
else
|
||
warn "DMS installer returned an error code. You may need to install it manually."
|
||
exit 1
|
||
fi
|
||
rm -f "$INSTALLER_SCRIPT"
|
||
else
|
||
warn "Failed to download DMS installer script from $DMS_URL."
|
||
fi
|
||
|
||
|
||
# ==============================================================================
|
||
# dms 随图形化环境自动启动
|
||
# ==============================================================================
|
||
section "Config" "dms autostart"
|
||
|
||
DMS_AUTOSTART_LINK="$HOME_DIR/.config/systemd/user/niri.service.wants/dms.service"
|
||
DMS_NIRI_CONFIG_FILE="$HOME_DIR/.config/niri/config.kdl"
|
||
DMS_HYPR_CONFIG_FILE="$HOME_DIR/.config/hypr/hyprland.conf"
|
||
|
||
if [[ -L "$DMS_AUTOSTART_LINK" ]]; then
|
||
log "Detect DMS systemd service enabled, disabling ...."
|
||
rm -f "$DMS_AUTOSTART_LINK"
|
||
fi
|
||
|
||
DMS_NIRI_INSTALLED="false"
|
||
DMS_HYPR_INSTALLED="false"
|
||
|
||
if command -v niri &>/dev/null; then
|
||
DMS_NIRI_INSTALLED="true"
|
||
elif command -v hyprland &>/dev/null; then
|
||
DMS_HYPR_INSTALLED="true"
|
||
fi
|
||
|
||
if [[ "$DMS_NIRI_INSTALLED" == "true" ]]; then
|
||
if ! grep -E -q "^[[:space:]]*spawn-at-startup.*dms.*run" "$DMS_NIRI_CONFIG_FILE"; then
|
||
log "Enabling DMS autostart in niri config.kdl..."
|
||
echo 'spawn-at-startup "dms" "run"' >> "$DMS_NIRI_CONFIG_FILE"
|
||
echo 'spawn-at-startup "xhost" "+si:localuser:root"' >> "$DMS_NIRI_CONFIG_FILE"
|
||
else
|
||
log "DMS autostart already exists in niri config.kdl, skipping."
|
||
fi
|
||
|
||
elif [[ "$DMS_HYPR_INSTALLED" == "true" ]]; then
|
||
log "Configuring Hyprland autostart..."
|
||
if ! grep -q "exec-once.*dms run" "$DMS_HYPR_CONFIG_FILE"; then
|
||
log "Adding DMS autostart to hyprland.conf"
|
||
echo 'exec-once = dms run' >> "$DMS_HYPR_CONFIG_FILE"
|
||
echo 'exec-once = xhost +si:localuser:root'>> "$DMS_HYPR_CONFIG_FILE"
|
||
else
|
||
log "DMS autostart already exists in Hyprland config, skipping."
|
||
fi
|
||
fi
|
||
|
||
# ==============================================================================
|
||
# fcitx5 configuration and locale
|
||
# ==============================================================================
|
||
section "Config" "input method"
|
||
|
||
if [[ "$DMS_NIRI_INSTALLED" == "true" ]]; then
|
||
if ! grep -q "fcitx5" "$DMS_NIRI_CONFIG_FILE"; then
|
||
log "Enabling fcitx5 autostart in niri config.kdl..."
|
||
echo 'spawn-at-startup "fcitx5" "-d"' >> "$DMS_NIRI_CONFIG_FILE"
|
||
else
|
||
log "Fcitx5 autostart already exists, skipping."
|
||
fi
|
||
|
||
if grep -q "^[[:space:]]*environment[[:space:]]*{" "$DMS_NIRI_CONFIG_FILE"; then
|
||
log "Existing environment block found. Injecting fcitx variables..."
|
||
if ! grep -q 'XMODIFIERS "@im=fcitx"' "$DMS_NIRI_CONFIG_FILE"; then
|
||
sed -i '/^[[:space:]]*environment[[:space:]]*{/a \ LC_CTYPE "en_US.UTF-8"\n XMODIFIERS "@im=fcitx"\n LANG "zh_CN.UTF-8"' "$DMS_NIRI_CONFIG_FILE"
|
||
else
|
||
log "Environment variables for fcitx already exist, skipping."
|
||
fi
|
||
else
|
||
log "No environment block found. Appending new block..."
|
||
cat << EOT >> "$DMS_NIRI_CONFIG_FILE"
|
||
|
||
environment {
|
||
LC_CTYPE "en_US.UTF-8"
|
||
XMODIFIERS "@im=fcitx"
|
||
LANGUAGE "zh_CN.UTF-8"
|
||
LANG "zh_CN.UTF-8"
|
||
}
|
||
EOT
|
||
fi
|
||
|
||
chown -R "$TARGET_USER:" "$PARENT_DIR/quickshell-dotfiles"
|
||
|
||
# === [ 核心修复点 ] ===
|
||
# 精准清除目标路径中会导致冲突的非目录文件(软链接)
|
||
as_user rm -rf "$HOME_DIR/.local/share/fcitx5"
|
||
as_user rm -rf "$HOME_DIR/.config/fcitx5"
|
||
# =======================
|
||
|
||
force_copy "$PARENT_DIR/quickshell-dotfiles/." "$HOME_DIR/"
|
||
|
||
elif [[ "$DMS_HYPR_INSTALLED" == "true" ]]; then
|
||
if ! grep -q "fcitx5" "$DMS_HYPR_CONFIG_FILE"; then
|
||
log "Adding fcitx5 autostart to hyprland.conf"
|
||
echo 'exec-once = fcitx5 -d' >> "$DMS_HYPR_CONFIG_FILE"
|
||
|
||
cat << EOT >> "$DMS_HYPR_CONFIG_FILE"
|
||
|
||
# --- Added by Shorin-Setup Script ---
|
||
# Fcitx5 Input Method Variables
|
||
env = XMODIFIERS,@im=fcitx
|
||
env = LC_CTYPE,en_US.UTF-8
|
||
# Locale Settings
|
||
env = LANG,zh_CN.UTF-8
|
||
# ----------------------------------
|
||
EOT
|
||
else
|
||
log "Fcitx5 configuration already exists in Hyprland config, skipping."
|
||
fi
|
||
|
||
chown -R "$TARGET_USER:" "$PARENT_DIR/quickshell-dotfiles"
|
||
|
||
# === [ 核心修复点 ] ===
|
||
as_user rm -rf "$HOME_DIR/.local/share/fcitx5"
|
||
as_user rm -rf "$HOME_DIR/.config/fcitx5"
|
||
# 这里我顺手修正了原本脚本的一个小 Bug:
|
||
# 如果 quickshell-dotfiles 包含 .config 和 .local,应复制到 ~ 下,而不是 ~/.config/ 下,否则会变成 ~/.config/.config
|
||
force_copy "$PARENT_DIR/quickshell-dotfiles/." "$HOME_DIR/"
|
||
fi
|
||
# ==============================================================================
|
||
# filemanager
|
||
# ==============================================================================
|
||
section "Config" "file manager"
|
||
|
||
if [[ "$DMS_NIRI_INSTALLED" == "true" ]]; then
|
||
log "DMS niri detected, configuring nautilus"
|
||
FM_PKGS="ffmpegthumbnailer gvfs-smb nautilus-open-any-terminal xdg-terminal-exec file-roller gnome-keyring gst-plugins-base gst-plugins-good gst-libav nautilus"
|
||
echo "$FM_PKGS" >> "$VERIFY_LIST"
|
||
exe as_user paru -S --noconfirm --needed $FM_PKGS
|
||
# 默认终端处理
|
||
if ! grep -q "kitty" "$HOME_DIR/.config/xdg-terminals.list"; then
|
||
echo 'kitty.desktop' >> "$HOME_DIR/.config/xdg-terminals.list"
|
||
fi
|
||
|
||
# if [ ! -f /usr/local/bin/gnome-terminal ] || [ -L /usr/local/bin/gnome-terminal ]; then
|
||
# exe ln -sf /usr/bin/kitty /usr/local/bin/gnome-terminal
|
||
# fi
|
||
sudo -u "$TARGET_USER" dbus-run-session gsettings set com.github.stunkymonkey.nautilus-open-any-terminal terminal kitty
|
||
|
||
as_user mkdir -p "$HOME_DIR/Templates"
|
||
as_user touch "$HOME_DIR/Templates/new"
|
||
as_user touch "$HOME_DIR/Templates/new.sh"
|
||
as_user bash -c "echo '#!/bin/bash' >> '$HOME_DIR/Templates/new.sh'"
|
||
chown -R "$TARGET_USER:" "$HOME_DIR/Templates"
|
||
|
||
configure_nautilus_user
|
||
|
||
|
||
elif [[ "$DMS_HYPR_INSTALLED" == "true" ]]; then
|
||
log "DMS hyprland detected, skipping file manager."
|
||
fi
|
||
|
||
# ==============================================================================
|
||
# screenshare
|
||
# ==============================================================================
|
||
section "Config" "screenshare"
|
||
|
||
if [[ "$DMS_NIRI_INSTALLED" == "true" ]]; then
|
||
log "DMS niri detected, configuring xdg-desktop-portal"
|
||
echo "xdg-desktop-portal-gnome" >> "$VERIFY_LIST"
|
||
exe pacman -S --noconfirm --needed xdg-desktop-portal-gnome
|
||
if ! grep -q '/usr/lib/xdg-desktop-portal-gnome' "$DMS_NIRI_CONFIG_FILE"; then
|
||
log "Configuring environment in niri config.kdl"
|
||
echo 'spawn-sh-at-startup "dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=niri & /usr/lib/xdg-desktop-portal-gnome"' >> "$DMS_NIRI_CONFIG_FILE"
|
||
fi
|
||
|
||
elif [[ "$DMS_HYPR_INSTALLED" == "true" ]]; then
|
||
log "DMS hyprland detected, configuring xdg-desktop-portal"
|
||
echo "xdg-desktop-portal-hyprland" >> "$VERIFY_LIST"
|
||
exe pacman -S --noconfirm --needed xdg-desktop-portal-hyprland
|
||
if ! grep -q '/usr/lib/xdg-desktop-portal-hyprland' "$DMS_HYPR_CONFIG_FILE"; then
|
||
log "Configuring environment in hyprland.conf"
|
||
echo 'exec-once = dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=hyprland & /usr/lib/xdg-desktop-portal-hyprland' >> "$DMS_HYPR_CONFIG_FILE"
|
||
fi
|
||
fi
|
||
|
||
# ==============================================================================
|
||
# Validation Check: DMS & Core Components (Blackbox Audit)
|
||
# ==============================================================================
|
||
section "Config" "components validation"
|
||
log "Verifying DMS and core components installation..."
|
||
|
||
MISSING_COMPONENTS=()
|
||
|
||
if ! command -v dms &>/dev/null ; then
|
||
MISSING_COMPONENTS+=("dms")
|
||
fi
|
||
if ! command -v quickshell &>/dev/null; then
|
||
MISSING_COMPONENTS+=("quickshell")
|
||
fi
|
||
|
||
if [[ ${#MISSING_COMPONENTS[@]} -gt 0 ]]; then
|
||
error "FATAL: Official DMS installer failed to provide core binaries!"
|
||
warn "Missing core commands: ${MISSING_COMPONENTS[*]}"
|
||
write_log "FATAL" "DMS Blackbox installation failed. Missing: ${MISSING_COMPONENTS[*]}"
|
||
echo -e " ${H_YELLOW}>>> Exiting installer. Please check upstream DankLinux repo or network. ${NC}"
|
||
exit 1
|
||
else
|
||
success "Blackbox components validated successfully."
|
||
fi
|
||
|
||
# ==============================================================================
|
||
# Dispaly Manager
|
||
# ==============================================================================
|
||
section "Config" "Dispaly Manager"
|
||
|
||
log "Cleaning up legacy TTY autologin configs..."
|
||
rm -f /etc/systemd/system/getty@tty1.service.d/autologin.conf 2>/dev/null
|
||
|
||
if [ "$SKIP_DM" = true ]; then
|
||
log "Display Manager setup skipped (Conflict found or user opted out)."
|
||
warn "You will need to start your session manually from the TTY."
|
||
else
|
||
setup_ly
|
||
fi
|
||
|
||
log "Module 04c completed." |