612 lines
20 KiB
Bash
612 lines
20 KiB
Bash
|
|
#!/bin/bash
|
|||
|
|
|
|||
|
|
# ==============================================================================
|
|||
|
|
# 00-utils.sh - The "TUI" Visual Engine (v4.0)
|
|||
|
|
# ==============================================================================
|
|||
|
|
|
|||
|
|
# --- 1. 颜色与样式定义 (ANSI) ---
|
|||
|
|
# 注意:这里定义的是字面量字符串,需要 echo -e 来解析
|
|||
|
|
export NC='\033[0m'
|
|||
|
|
export BOLD='\033[1m'
|
|||
|
|
export DIM='\033[2m'
|
|||
|
|
export ITALIC='\033[3m'
|
|||
|
|
export UNDER='\033[4m'
|
|||
|
|
export H_MAGENTA='\033[1;35m'
|
|||
|
|
# 常用高亮色
|
|||
|
|
export H_RED='\033[1;31m'
|
|||
|
|
export H_GREEN='\033[1;32m'
|
|||
|
|
export H_YELLOW='\033[1;33m'
|
|||
|
|
export H_BLUE='\033[1;34m'
|
|||
|
|
export H_PURPLE='\033[1;35m'
|
|||
|
|
export H_CYAN='\033[1;36m'
|
|||
|
|
export H_WHITE='\033[1;37m'
|
|||
|
|
export H_GRAY='\033[1;90m'
|
|||
|
|
|
|||
|
|
# 背景色 (用于标题栏)
|
|||
|
|
export BG_BLUE='\033[44m'
|
|||
|
|
export BG_PURPLE='\033[45m'
|
|||
|
|
|
|||
|
|
# 符号定义
|
|||
|
|
export TICK="${H_GREEN}✔${NC}"
|
|||
|
|
export CROSS="${H_RED}✘${NC}"
|
|||
|
|
export INFO="${H_BLUE}ℹ${NC}"
|
|||
|
|
export WARN="${H_YELLOW}⚠${NC}"
|
|||
|
|
export ARROW="${H_CYAN}➜${NC}"
|
|||
|
|
|
|||
|
|
|
|||
|
|
check_root() {
|
|||
|
|
if [ "$EUID" -ne 0 ]; then
|
|||
|
|
echo -e "${H_RED} $CROSS CRITICAL ERROR: Script must be run as root.${NC}"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
check_root
|
|||
|
|
|
|||
|
|
# ==============================================================================
|
|||
|
|
# detect_target_user - 识别目标用户 (支持 1-based 序号与回车默认选择)
|
|||
|
|
# ==============================================================================
|
|||
|
|
detect_target_user() {
|
|||
|
|
# 1. 缓存检查
|
|||
|
|
if [[ -f "/tmp/shorin_install_user" ]]; then
|
|||
|
|
TARGET_USER=$(cat "/tmp/shorin_install_user")
|
|||
|
|
HOME_DIR="/home/$TARGET_USER"
|
|||
|
|
export TARGET_USER HOME_DIR
|
|||
|
|
return 0
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
log "Detecting system users..."
|
|||
|
|
|
|||
|
|
# 2. 提取系统中所有普通用户 (UID 1000-60000)
|
|||
|
|
mapfile -t HUMAN_USERS < <(awk -F: '$3 >= 1000 && $3 < 60000 {print $1}' /etc/passwd)
|
|||
|
|
|
|||
|
|
# 3. 核心决策逻辑
|
|||
|
|
if [[ ${#HUMAN_USERS[@]} -gt 1 ]]; then
|
|||
|
|
echo -e " ${H_YELLOW}>>> Multiple users detected. Who is the target?${NC}"
|
|||
|
|
|
|||
|
|
local default_user=""
|
|||
|
|
local default_idx=""
|
|||
|
|
|
|||
|
|
# 遍历用户,生成 1 开始的序号,并捕获当前 Sudo 用户作为默认值
|
|||
|
|
for i in "${!HUMAN_USERS[@]}"; do
|
|||
|
|
local mark=""
|
|||
|
|
local display_idx=$((i + 1))
|
|||
|
|
|
|||
|
|
if [[ "${HUMAN_USERS[$i]}" == "${SUDO_USER:-}" ]]; then
|
|||
|
|
mark="${H_CYAN}*${NC}"
|
|||
|
|
default_user="${HUMAN_USERS[$i]}"
|
|||
|
|
default_idx="$display_idx"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo -e " [${display_idx}] ${mark}${HUMAN_USERS[$i]}"
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
while true; do
|
|||
|
|
# 动态生成提示词
|
|||
|
|
if [[ -n "$default_user" ]]; then
|
|||
|
|
echo -ne " ${H_CYAN}Select user ID [1-${#HUMAN_USERS[@]}] (Default ${default_idx}): ${NC}"
|
|||
|
|
else
|
|||
|
|
echo -ne " ${H_CYAN}Select user ID [1-${#HUMAN_USERS[@]}]: ${NC}"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
read -r idx
|
|||
|
|
|
|||
|
|
# 处理直接回车:如果有默认用户,直接采纳
|
|||
|
|
if [[ -z "$idx" && -n "$default_user" ]]; then
|
|||
|
|
TARGET_USER="$default_user"
|
|||
|
|
log "Defaulting to current user: ${H_CYAN}${TARGET_USER}${NC}"
|
|||
|
|
break
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 验证输入是否为合法数字 (1 到 数组长度)
|
|||
|
|
if [[ "$idx" =~ ^[0-9]+$ ]] && [ "$idx" -ge 1 ] && [ "$idx" -le "${#HUMAN_USERS[@]}" ]; then
|
|||
|
|
# 数组索引需要减 1 还原
|
|||
|
|
TARGET_USER="${HUMAN_USERS[$((idx - 1))]}"
|
|||
|
|
break
|
|||
|
|
else
|
|||
|
|
warn "Invalid selection. Please enter a valid number or press Enter for default."
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
elif [[ ${#HUMAN_USERS[@]} -eq 1 ]]; then
|
|||
|
|
TARGET_USER="${HUMAN_USERS[0]}"
|
|||
|
|
log "Single user detected: ${H_CYAN}${TARGET_USER}${NC}"
|
|||
|
|
|
|||
|
|
else
|
|||
|
|
if [[ -n "${SUDO_USER:-}" && "$SUDO_USER" != "root" ]]; then
|
|||
|
|
TARGET_USER="$SUDO_USER"
|
|||
|
|
else
|
|||
|
|
echo -ne " ${H_YELLOW}No standard user found. Enter intended username:${NC} "
|
|||
|
|
read -r TARGET_USER
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 4. 最终验证与持久化
|
|||
|
|
if [[ -z "$TARGET_USER" ]]; then
|
|||
|
|
error "Target user cannot be empty."
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo "$TARGET_USER" > "/tmp/shorin_install_user"
|
|||
|
|
HOME_DIR="/home/$TARGET_USER"
|
|||
|
|
export TARGET_USER HOME_DIR
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 日志文件
|
|||
|
|
export TEMP_LOG_FILE="/tmp/log-shorin-arch-setup.txt"
|
|||
|
|
[ ! -f "$TEMP_LOG_FILE" ] && touch "$TEMP_LOG_FILE" && chmod 666 "$TEMP_LOG_FILE"
|
|||
|
|
|
|||
|
|
# --- 2. 基础工具 ---
|
|||
|
|
write_log() {
|
|||
|
|
# Strip ANSI colors for log file
|
|||
|
|
local clean_msg=$(echo -e "$2" | sed 's/\x1b\[[0-9;]*m//g')
|
|||
|
|
echo "[$(date '+%H:%M:%S')] [$1] $clean_msg" >> "$TEMP_LOG_FILE"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# --- 3. 视觉组件 (TUI Style) ---
|
|||
|
|
|
|||
|
|
# 绘制分割线
|
|||
|
|
hr() {
|
|||
|
|
printf "${H_GRAY}%*s${NC}\n" "${COLUMNS:-80}" '' | tr ' ' '─'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 绘制大标题 (Section)
|
|||
|
|
section() {
|
|||
|
|
local title="$1"
|
|||
|
|
local subtitle="$2"
|
|||
|
|
echo ""
|
|||
|
|
echo -e "${H_PURPLE}╭──────────────────────────────────────────────────────────────────────────────╮${NC}"
|
|||
|
|
echo -e "${H_PURPLE}│${NC} ${BOLD}${H_WHITE}$title${NC}"
|
|||
|
|
echo -e "${H_PURPLE}│${NC} ${H_CYAN}$subtitle${NC}"
|
|||
|
|
echo -e "${H_PURPLE}╰──────────────────────────────────────────────────────────────────────────────╯${NC}"
|
|||
|
|
write_log "SECTION" "$title - $subtitle"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 绘制键值对信息
|
|||
|
|
info_kv() {
|
|||
|
|
local key="$1"
|
|||
|
|
local val="$2"
|
|||
|
|
local extra="$3"
|
|||
|
|
printf " ${H_BLUE}●${NC} %-15s : ${BOLD}%s${NC} ${DIM}%s${NC}\n" "$key" "$val" "$extra"
|
|||
|
|
write_log "INFO" "$key=$val"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 普通日志
|
|||
|
|
log() {
|
|||
|
|
echo -e " $ARROW $1"
|
|||
|
|
write_log "LOG" "$1"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 成功日志
|
|||
|
|
success() {
|
|||
|
|
echo -e " $TICK ${H_GREEN}$1${NC}"
|
|||
|
|
write_log "SUCCESS" "$1"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 警告日志 (突出显示)
|
|||
|
|
warn() {
|
|||
|
|
echo -e " $WARN ${H_YELLOW}${BOLD}WARNING:${NC} ${H_YELLOW}$1${NC}"
|
|||
|
|
write_log "WARN" "$1"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 错误日志 (非常突出)
|
|||
|
|
error() {
|
|||
|
|
echo -e ""
|
|||
|
|
echo -e "${H_RED} ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓${NC}"
|
|||
|
|
echo -e "${H_RED} ┃ ERROR: $1${NC}"
|
|||
|
|
echo -e "${H_RED} ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛${NC}"
|
|||
|
|
echo -e ""
|
|||
|
|
write_log "ERROR" "$1"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# --- 4. 核心:命令执行器 (Command Exec) ---
|
|||
|
|
exe() {
|
|||
|
|
local full_command="$*"
|
|||
|
|
|
|||
|
|
# Visual: 显示正在运行的命令
|
|||
|
|
echo -e " ${H_GRAY}┌──[ ${H_MAGENTA}EXEC${H_GRAY} ]────────────────────────────────────────────────────${NC}"
|
|||
|
|
echo -e " ${H_GRAY}│${NC} ${H_CYAN}$ ${NC}${BOLD}$full_command${NC}"
|
|||
|
|
|
|||
|
|
write_log "EXEC" "$full_command"
|
|||
|
|
|
|||
|
|
# Run the command
|
|||
|
|
"$@"
|
|||
|
|
local status=$?
|
|||
|
|
|
|||
|
|
if [ $status -eq 0 ]; then
|
|||
|
|
echo -e " ${H_GRAY}└──────────────────────────────────────────────────────── ${H_GREEN}OK${H_GRAY} ─┘${NC}"
|
|||
|
|
else
|
|||
|
|
echo -e " ${H_GRAY}└────────────────────────────────────────────────────── ${H_RED}FAIL${H_GRAY} ─┘${NC}"
|
|||
|
|
write_log "FAIL" "Exit Code: $status"
|
|||
|
|
return $status
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 静默执行
|
|||
|
|
exe_silent() {
|
|||
|
|
"$@" > /dev/null 2>&1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# --- 5. 可复用逻辑块 ---
|
|||
|
|
|
|||
|
|
# 动态选择 Flathub 镜像源 (修复版:使用 echo -e 处理颜色变量)
|
|||
|
|
select_flathub_mirror() {
|
|||
|
|
# 1. 索引数组保证顺序
|
|||
|
|
local names=(
|
|||
|
|
"SJTU (Shanghai Jiao Tong)"
|
|||
|
|
"USTC (Univ of Sci & Tech of China)"
|
|||
|
|
"FlatHub Offical"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
local urls=(
|
|||
|
|
"https://mirror.sjtu.edu.cn/flathub"
|
|||
|
|
"https://mirrors.ustc.edu.cn/flathub"
|
|||
|
|
"https://dl.flathub.org/repo/"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 2. 动态计算菜单宽度 (基于无颜色的纯文本)
|
|||
|
|
local max_len=0
|
|||
|
|
local title_text="Select Flathub Mirror (60s Timeout)"
|
|||
|
|
|
|||
|
|
max_len=${#title_text}
|
|||
|
|
|
|||
|
|
for name in "${names[@]}"; do
|
|||
|
|
# 预估显示长度:"[x] Name - Recommended"
|
|||
|
|
local item_len=$((${#name} + 4 + 14))
|
|||
|
|
if (( item_len > max_len )); then
|
|||
|
|
max_len=$item_len
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
# 菜单总宽度
|
|||
|
|
local menu_width=$((max_len + 4))
|
|||
|
|
|
|||
|
|
# --- 3. 渲染菜单 (使用 echo -e 确保颜色变量被解析) ---
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
# 生成横线
|
|||
|
|
local line_str=""
|
|||
|
|
printf -v line_str "%*s" "$menu_width" ""
|
|||
|
|
line_str=${line_str// /─}
|
|||
|
|
|
|||
|
|
# 打印顶部边框
|
|||
|
|
echo -e "${H_PURPLE}╭${line_str}╮${NC}"
|
|||
|
|
|
|||
|
|
# 打印标题 (计算居中填充)
|
|||
|
|
local title_padding_len=$(( (menu_width - ${#title_text}) / 2 ))
|
|||
|
|
local right_padding_len=$((menu_width - ${#title_text} - title_padding_len))
|
|||
|
|
|
|||
|
|
# 生成填充空格
|
|||
|
|
local t_pad_l=""; printf -v t_pad_l "%*s" "$title_padding_len" ""
|
|||
|
|
local t_pad_r=""; printf -v t_pad_r "%*s" "$right_padding_len" ""
|
|||
|
|
|
|||
|
|
echo -e "${H_PURPLE}│${NC}${t_pad_l}${BOLD}${title_text}${NC}${t_pad_r}${H_PURPLE}│${NC}"
|
|||
|
|
|
|||
|
|
# 打印中间分隔线
|
|||
|
|
echo -e "${H_PURPLE}├${line_str}┤${NC}"
|
|||
|
|
|
|||
|
|
# 打印选项
|
|||
|
|
for i in "${!names[@]}"; do
|
|||
|
|
local name="${names[$i]}"
|
|||
|
|
local display_idx=$((i+1))
|
|||
|
|
|
|||
|
|
# 1. 构造用于显示的带颜色字符串
|
|||
|
|
local color_str=""
|
|||
|
|
# 2. 构造用于计算长度的无颜色字符串
|
|||
|
|
local raw_str=""
|
|||
|
|
|
|||
|
|
if [ "$i" -eq 0 ]; then
|
|||
|
|
raw_str=" [$display_idx] $name - Recommended"
|
|||
|
|
color_str=" ${H_CYAN}[$display_idx]${NC} ${name} - ${H_GREEN}Recommended${NC}"
|
|||
|
|
else
|
|||
|
|
raw_str=" [$display_idx] $name"
|
|||
|
|
color_str=" ${H_CYAN}[$display_idx]${NC} ${name}"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 计算右侧填充空格
|
|||
|
|
local padding=$((menu_width - ${#raw_str}))
|
|||
|
|
local pad_str="";
|
|||
|
|
if [ "$padding" -gt 0 ]; then
|
|||
|
|
printf -v pad_str "%*s" "$padding" ""
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 打印:边框 + 内容 + 填充 + 边框
|
|||
|
|
echo -e "${H_PURPLE}│${NC}${color_str}${pad_str}${H_PURPLE}│${NC}"
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
# 打印底部边框
|
|||
|
|
echo -e "${H_PURPLE}╰${line_str}╯${NC}"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
# --- 4. 用户交互 ---
|
|||
|
|
local choice
|
|||
|
|
# 提示符
|
|||
|
|
read -t 60 -p "$(echo -e " ${H_YELLOW}Enter choice [1-${#names[@]}]: ${NC}")" choice
|
|||
|
|
if [ $? -ne 0 ]; then echo ""; fi
|
|||
|
|
choice=${choice:-1}
|
|||
|
|
|
|||
|
|
if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt "${#names[@]}" ]; then
|
|||
|
|
log "Invalid choice or timeout. Defaulting to SJTU..."
|
|||
|
|
choice=1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
local index=$((choice-1))
|
|||
|
|
local selected_name="${names[$index]}"
|
|||
|
|
local selected_url="${urls[$index]}"
|
|||
|
|
|
|||
|
|
log "Setting Flathub mirror to: ${H_GREEN}$selected_name${NC}"
|
|||
|
|
|
|||
|
|
# 执行修改 (仅修改 flathub,不涉及 github)
|
|||
|
|
if exe flatpak remote-modify flathub --url="$selected_url"; then
|
|||
|
|
success "Mirror updated."
|
|||
|
|
else
|
|||
|
|
error "Failed to update mirror."
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
as_user() {
|
|||
|
|
runuser -u "$TARGET_USER" -- "$@"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
hide_desktop_file() {
|
|||
|
|
local source_file="$1"
|
|||
|
|
local filename=$(basename "$source_file")
|
|||
|
|
local user_dir="$HOME_DIR/.local/share/applications"
|
|||
|
|
local target_file="$user_dir/$filename"
|
|||
|
|
|
|||
|
|
mkdir -p "$user_dir"
|
|||
|
|
|
|||
|
|
if [[ -f "$source_file" ]]; then
|
|||
|
|
cp -fv "$source_file" "$target_file"
|
|||
|
|
if grep -q "^NoDisplay=" "$target_file"; then
|
|||
|
|
sed -i 's/^NoDisplay=.*/NoDisplay=true/' "$target_file"
|
|||
|
|
else
|
|||
|
|
echo "NoDisplay=true" >> "$target_file"
|
|||
|
|
fi
|
|||
|
|
chown "$TARGET_USER:" "$target_file"
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 批量执行
|
|||
|
|
run_hide_desktop_file() {
|
|||
|
|
|
|||
|
|
local apps_to_hide=(
|
|||
|
|
"avahi-discover.desktop"
|
|||
|
|
"qv4l2.desktop"
|
|||
|
|
"qvidcap.desktop"
|
|||
|
|
"bssh.desktop"
|
|||
|
|
"org.fcitx.Fcitx5.desktop"
|
|||
|
|
"org.fcitx.fcitx5-migrator.desktop"
|
|||
|
|
"xgps.desktop"
|
|||
|
|
"xgpsspeed.desktop"
|
|||
|
|
"gvim.desktop"
|
|||
|
|
"kbd-layout-viewer5.desktop"
|
|||
|
|
"bvnc.desktop"
|
|||
|
|
"yazi.desktop"
|
|||
|
|
"btop.desktop"
|
|||
|
|
"vim.desktop"
|
|||
|
|
"nvim.desktop"
|
|||
|
|
"nvtop.desktop"
|
|||
|
|
"mpv.desktop"
|
|||
|
|
"org.gnome.Settings.desktop"
|
|||
|
|
"thunar-settings.desktop"
|
|||
|
|
"thunar-bulk-rename.desktop"
|
|||
|
|
"thunar-volman-settings.desktop"
|
|||
|
|
"clipse-gui.desktop"
|
|||
|
|
"waypaper.desktop"
|
|||
|
|
"xfce4-about.desktop"
|
|||
|
|
"cmake-gui.desktop"
|
|||
|
|
"assistant.desktop"
|
|||
|
|
"qdbusviewer.desktop"
|
|||
|
|
"linguist.desktop"
|
|||
|
|
"designer.desktop"
|
|||
|
|
"org.kde.drkonqi.coredump.gui.desktop"
|
|||
|
|
"org.kde.kwrite.desktop"
|
|||
|
|
"org.freedesktop.MalcontentControl.desktop"
|
|||
|
|
"org.gnome.Nautilus.desktop"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
echo "正在隐藏不需要的桌面图标..."
|
|||
|
|
|
|||
|
|
# 用一个 for 循环搞定所有调用
|
|||
|
|
for app in "${apps_to_hide[@]}"; do
|
|||
|
|
hide_desktop_file "/usr/share/applications/$app"
|
|||
|
|
done
|
|||
|
|
chown -R "$TARGET_USER:" "$HOME_DIR/.local/share/applications"
|
|||
|
|
|
|||
|
|
echo "图标隐藏完成!"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
configure_nautilus_user() {
|
|||
|
|
local sys_file="/usr/share/applications/org.gnome.Nautilus.desktop"
|
|||
|
|
local user_dir="$HOME_DIR/.local/share/applications"
|
|||
|
|
local user_file="$user_dir/org.gnome.Nautilus.desktop"
|
|||
|
|
|
|||
|
|
# 1. 检查系统文件是否存在
|
|||
|
|
if [ -f "$sys_file" ]; then
|
|||
|
|
|
|||
|
|
local need_modify=0
|
|||
|
|
local env_vars="env"
|
|||
|
|
|
|||
|
|
# --- 逻辑 1: Niri 检测 (输入法修复) ---
|
|||
|
|
if command -v niri >/dev/null 2>&1; then
|
|||
|
|
# 只要有 niri,就强制使用 fcitx 模块
|
|||
|
|
env_vars="$env_vars GTK_IM_MODULE=fcitx"
|
|||
|
|
need_modify=1
|
|||
|
|
log "检测到 Niri 环境,准备注入 GTK_IM_MODULE=fcitx"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# --- 逻辑 2: 双显卡 NVIDIA 检测 (GSK 渲染修复) ---
|
|||
|
|
local gpu_count=$(lspci | grep -E -i "vga|3d" | wc -l)
|
|||
|
|
local has_nvidia=$(lspci | grep -E -i "nvidia" | wc -l)
|
|||
|
|
|
|||
|
|
if [ "$gpu_count" -gt 1 ] && [ "$has_nvidia" -gt 0 ]; then
|
|||
|
|
# 叠加 GSK 渲染变量
|
|||
|
|
env_vars="$env_vars GSK_RENDERER=gl"
|
|||
|
|
need_modify=1
|
|||
|
|
log "检测到双显卡 NVIDIA,准备注入 GSK_RENDERER=gl"
|
|||
|
|
|
|||
|
|
# 额外操作: 创建 gsk.conf
|
|||
|
|
local env_conf_dir="$HOME_DIR/.config/environment.d"
|
|||
|
|
if [ ! -f "$env_conf_dir/gsk.conf" ]; then
|
|||
|
|
mkdir -p "$env_conf_dir"
|
|||
|
|
echo "GSK_RENDERER=gl" > "$env_conf_dir/gsk.conf"
|
|||
|
|
# 修复权限
|
|||
|
|
if [ -n "$TARGET_USER" ]; then
|
|||
|
|
chown -R "$TARGET_USER" "$env_conf_dir"
|
|||
|
|
fi
|
|||
|
|
log "已添加用户级环境变量配置: $env_conf_dir/gsk.conf"
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# --- 3. 执行修改 (如果命中了任意一个逻辑) ---
|
|||
|
|
if [ "$need_modify" -eq 1 ]; then
|
|||
|
|
|
|||
|
|
# 准备目录并复制
|
|||
|
|
mkdir -p "$user_dir"
|
|||
|
|
cp "$sys_file" "$user_file"
|
|||
|
|
|
|||
|
|
# 修复所有者
|
|||
|
|
if [ -n "$TARGET_USER" ]; then
|
|||
|
|
chown "$TARGET_USER" "$user_file"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 修改 Desktop 文件
|
|||
|
|
# env_vars 此时可能是:
|
|||
|
|
# - "env GTK_IM_MODULE=fcitx" (仅Niri)
|
|||
|
|
# - "env GSK_RENDERER=gl" (仅双显卡)
|
|||
|
|
# - "env GTK_IM_MODULE=fcitx GSK_RENDERER=gl" (两者都有)
|
|||
|
|
sed -i "s|^Exec=|Exec=$env_vars |" "$user_file"
|
|||
|
|
|
|||
|
|
log "已生成 Nautilus 用户配置: $user_file (参数: $env_vars)"
|
|||
|
|
|
|||
|
|
fi
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
if [[ -d "${src%/}" ]]; then
|
|||
|
|
(cd "$src" && find . -type d) | while read -r d; do
|
|||
|
|
as_user rm -f "$target_dir/$d" 2>/dev/null
|
|||
|
|
done
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
exe as_user cp -rf "$src" "$target_dir"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ==============================================================================
|
|||
|
|
# check_dm_conflict - 检测现有的显示管理器冲突,并让用户选择是否启用新 DM
|
|||
|
|
# ==============================================================================
|
|||
|
|
# 使用方法: check_dm_conflict
|
|||
|
|
# 结果: 设置全局变量 $SKIP_DM (true/false)
|
|||
|
|
check_dm_conflict() {
|
|||
|
|
local KNOWN_DMS=(
|
|||
|
|
"cdm" "console-tdm" "emptty" "lemurs" "lidm" "loginx" "ly" "nodm" "tbsm"
|
|||
|
|
"entrance-git" "gdm" "lightdm" "lxdm" "plasma-login-manager" "sddm"
|
|||
|
|
"slim" "xorg-xdm" "greetd"
|
|||
|
|
)
|
|||
|
|
SKIP_DM=false
|
|||
|
|
local DM_FOUND=""
|
|||
|
|
|
|||
|
|
for dm in "${KNOWN_DMS[@]}"; do
|
|||
|
|
if pacman -Q "$dm" &>/dev/null; then
|
|||
|
|
DM_FOUND="$dm"
|
|||
|
|
break
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
if [ -n "$DM_FOUND" ]; then
|
|||
|
|
info_kv "Conflict" "${H_RED}$DM_FOUND${NC}"
|
|||
|
|
SKIP_DM=true
|
|||
|
|
else
|
|||
|
|
# read -t 20 等待 20 秒,超时默认 Y
|
|||
|
|
read -t 20 -p "$(echo -e " ${H_CYAN}Enable Display Manager ? [Y/n] (Default Y): ${NC}")" choice || true
|
|||
|
|
if [[ "${choice:-Y}" =~ ^[Yy]$ ]]; then
|
|||
|
|
SKIP_DM=false
|
|||
|
|
else
|
|||
|
|
SKIP_DM=true
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ==============================================================================
|
|||
|
|
# setup_greetd_tuigreet - 安装并配置 greetd + tuigreet
|
|||
|
|
# ==============================================================================
|
|||
|
|
# 使用方法: setup_greetd_tuigreet
|
|||
|
|
setup_greetd_tuigreet() {
|
|||
|
|
log "Installing greetd and tuigreet..."
|
|||
|
|
exe pacman -S --noconfirm --needed greetd greetd-tuigreet
|
|||
|
|
|
|||
|
|
# 禁用可能存在的默认 getty@tty1,把 TTY1 彻底让给 greetd
|
|||
|
|
systemctl disable getty@tty1.service 2>/dev/null
|
|||
|
|
|
|||
|
|
# 配置 greetd (覆盖写入 config.toml)
|
|||
|
|
log "Configuring /etc/greetd/config.toml..."
|
|||
|
|
local GREETD_CONF="/etc/greetd/config.toml"
|
|||
|
|
|
|||
|
|
cat <<EOF > "$GREETD_CONF"
|
|||
|
|
[terminal]
|
|||
|
|
# 绑定到 TTY1
|
|||
|
|
vt = 1
|
|||
|
|
|
|||
|
|
[default_session]
|
|||
|
|
# 使用 tuigreet 作为前端
|
|||
|
|
# 自动扫描 /usr/share/wayland-sessions/,支持时间显示、密码星号、记住上次选择
|
|||
|
|
command = "tuigreet --time --user-menu --remember --remember-user-session --asterisks"
|
|||
|
|
user = "greeter"
|
|||
|
|
EOF
|
|||
|
|
|
|||
|
|
# 修复 tuigreet 的 --remember 缓存目录权限
|
|||
|
|
log "Ensuring cache directory permissions for tuigreet..."
|
|||
|
|
mkdir -p /var/cache/tuigreet
|
|||
|
|
chown -R greeter:greeter /var/cache/tuigreet
|
|||
|
|
chmod 755 /var/cache/tuigreet
|
|||
|
|
|
|||
|
|
# 启用服务
|
|||
|
|
log "Enabling greetd service..."
|
|||
|
|
systemctl enable greetd.service
|
|||
|
|
|
|||
|
|
success "greetd with tuigreet frontend has been successfully configured!"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# ==============================================================================
|
|||
|
|
# setup_ly - 安装并配置 ly 显示管理器
|
|||
|
|
# ==============================================================================
|
|||
|
|
# 功能列表:
|
|||
|
|
# 1. 安装 ly 软件包
|
|||
|
|
# 2. 禁用其他可能冲突的 TTY 登录服务 (getty/greetd)
|
|||
|
|
# 3. 编辑 /etc/ly/config.ini,开启 Matrix (代码雨) 背景动画
|
|||
|
|
# 4. 启用 ly.service 开机自启
|
|||
|
|
# 使用方法: setup_ly
|
|||
|
|
setup_ly() {
|
|||
|
|
log "Installing ly display manager..."
|
|||
|
|
exe pacman -S --noconfirm --needed ly
|
|||
|
|
|
|||
|
|
# # 配置 ly (非破坏性修改 config.ini)
|
|||
|
|
# log "Configuring /etc/ly/config.ini for Matrix animation..."
|
|||
|
|
# local LY_CONF="/etc/ly/config.ini"
|
|||
|
|
|
|||
|
|
# if [[ -f "$LY_CONF" ]]; then
|
|||
|
|
# # 使用 sed 精准替换:
|
|||
|
|
# # 1. 将注释掉的或现有的 animation = none 替换为 animation = matrix
|
|||
|
|
# sed -i 's/^[#[:space:]]*animation[[:space:]]*=.*/animation = matrix/' "$LY_CONF"
|
|||
|
|
# else
|
|||
|
|
# log "Warning: $LY_CONF not found! Please check ly installation."
|
|||
|
|
# fi
|
|||
|
|
|
|||
|
|
# 启用服务
|
|||
|
|
log "Enabling ly service..."
|
|||
|
|
systemctl enable ly@tty1
|
|||
|
|
|
|||
|
|
success "ly display manager with Matrix animation has been successfully configured!"
|
|||
|
|
}
|