- XCB 后端:X11 下 XGrabKey 零权限全局快捷键 - evdev 后端:Wayland 下 EVIOCGRAB + uinput 键盘事件拦截与重放 - GNOME 扩展 D-Bus 后端:GNOME Wayland 备用方案 - Portal GlobalShortcuts 后端:标准 Portal 快捷键 - libei 后端:GNOME 47+/KDE 实验性支持 - 后端工厂:根据会话类型和权限自动选择最优后端 - 录音指示器:浮动窗口在光标位置显示录音状态 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
219 lines
8.2 KiB
Bash
Executable File
219 lines
8.2 KiB
Bash
Executable File
#!/bin/bash
|
||
# ============================================================================
|
||
# Impress Voice Input — Linux 发布包打包脚本
|
||
# 用法: ./package-linux.sh [--clean]
|
||
# ============================================================================
|
||
set -e
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
BUILD_DIR="${SCRIPT_DIR}/build_linux"
|
||
DIST_DIR="${SCRIPT_DIR}/dist"
|
||
PKG_NAME="impress_voice_input_linux"
|
||
PKG_DIR="${DIST_DIR}/${PKG_NAME}"
|
||
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--clean)
|
||
echo "清理发布包目录..."
|
||
rm -rf "${PKG_DIR}" "${DIST_DIR}/${PKG_NAME}.tar.gz"
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# 1. 确保已编译
|
||
if [ ! -f "${BUILD_DIR}/impress_voice_input" ]; then
|
||
echo "错误:未找到可执行文件,请先运行 ./build-linux.sh"
|
||
exit 1
|
||
fi
|
||
|
||
# 2. 创建发布目录结构
|
||
echo "[1/6] 创建发布目录..."
|
||
rm -rf "${PKG_DIR}"
|
||
mkdir -p "${PKG_DIR}/lib"
|
||
mkdir -p "${PKG_DIR}/platforms"
|
||
mkdir -p "${PKG_DIR}/styles"
|
||
mkdir -p "${PKG_DIR}/gnome-extension/io.impress.voice-input-hotkey@impress"
|
||
|
||
# 3. 复制核心文件
|
||
echo "[2/6] 复制可执行文件和资源..."
|
||
cp "${BUILD_DIR}/impress_voice_input" "${PKG_DIR}/"
|
||
cp "${SCRIPT_DIR}/configs/default_config.json" "${PKG_DIR}/default_config.json"
|
||
cp "${SCRIPT_DIR}/src/ui/resources/styles/main.qss" "${PKG_DIR}/styles/" 2>/dev/null || true
|
||
cp "${SCRIPT_DIR}/src/ui/resources/styles/main_dark.qss" "${PKG_DIR}/styles/" 2>/dev/null || true
|
||
|
||
# 4. 复制依赖库
|
||
echo "[3/6] 复制依赖库..."
|
||
cp "${SCRIPT_DIR}/third_party/onnxruntime/lib/libonnxruntime.so"* "${PKG_DIR}/lib/"
|
||
cp "${SCRIPT_DIR}/third_party/portaudio/lib/libportaudio.so"* "${PKG_DIR}/lib/" 2>/dev/null || true
|
||
|
||
for lib in libQt6Core.so.6 libQt6Widgets.so.6 libQt6Gui.so.6 libQt6Concurrent.so.6 libQt6Network.so.6; do
|
||
src=$(find /home/alvin/Qt/6.11.1/gcc_64/lib -name "${lib}" -o -name "${lib}.*" 2>/dev/null | head -1)
|
||
[ -n "$src" ] && cp -L "$src" "${PKG_DIR}/lib/" || true
|
||
done
|
||
|
||
for lib in libicui18n.so.77 libicuuc.so.77 libicudata.so.77; do
|
||
src=$(find /home/alvin/Qt/6.11.1/gcc_64/lib -name "${lib}" -o -name "${lib}.*" 2>/dev/null | head -1)
|
||
[ -n "$src" ] && cp -L "$src" "${PKG_DIR}/lib/" || true
|
||
done
|
||
|
||
for lib in libpcre2-16.so.0 libfontconfig.so.1 libfreetype.so.6; do
|
||
src=$(find /usr/lib64 /usr/lib -name "${lib}" -o -name "${lib}.*" 2>/dev/null | head -1)
|
||
[ -n "$src" ] && cp -L "$src" "${PKG_DIR}/lib/" || true
|
||
done
|
||
|
||
for lib in libei.so.1 libeis.so.1; do
|
||
src=$(find /usr/lib64 /usr/lib -name "${lib}" -o -name "${lib}.*" 2>/dev/null | head -1)
|
||
[ -n "$src" ] && cp -L "$src" "${PKG_DIR}/lib/" || true
|
||
done
|
||
|
||
QT_PLUGIN_DIR=$(find /home/alvin/Qt -path "*/plugins/platforms" -type d 2>/dev/null | head -1)
|
||
if [ -n "$QT_PLUGIN_DIR" ]; then
|
||
cp "${QT_PLUGIN_DIR}"/libq*.so "${PKG_DIR}/platforms/" 2>/dev/null || true
|
||
fi
|
||
|
||
# 5. 复制 GNOME 扩展
|
||
echo "[4/6] 复制 GNOME Shell 扩展..."
|
||
cp "${SCRIPT_DIR}/gnome-extension/io.impress.voice-input-hotkey@impress/extension.js" \
|
||
"${PKG_DIR}/gnome-extension/io.impress.voice-input-hotkey@impress/"
|
||
cp "${SCRIPT_DIR}/gnome-extension/io.impress.voice-input-hotkey@impress/metadata.json" \
|
||
"${PKG_DIR}/gnome-extension/io.impress.voice-input-hotkey@impress/"
|
||
|
||
# 6. 生成脚本和桌面文件
|
||
echo "[5/6] 生成安装脚本和启动脚本..."
|
||
|
||
# --- install.sh ---
|
||
cat > "${PKG_DIR}/install.sh" << 'INSTALLSH'
|
||
#!/bin/bash
|
||
set -e
|
||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
APP_NAME="impress_voice_input"
|
||
GNOME_EXT_UUID="io.impress.voice-input-hotkey@impress"
|
||
INSTALL_MODE="user"
|
||
for arg in "$@"; do case "$arg" in --system) INSTALL_MODE="system" ;; --user) INSTALL_MODE="user" ;; esac; done
|
||
|
||
echo "============================================"
|
||
echo " Impress Voice Input — 安装"
|
||
echo " 模式: ${INSTALL_MODE}"
|
||
echo "============================================"
|
||
|
||
echo ""
|
||
echo "[1/3] GNOME Shell 扩展(GNOME 用户可选)..."
|
||
if [ -d "${SCRIPT_DIR}/gnome-extension/${GNOME_EXT_UUID}" ]; then
|
||
if [ "$INSTALL_MODE" = "system" ]; then
|
||
EXT_DST="/usr/share/gnome-shell/extensions/${GNOME_EXT_UUID}"
|
||
echo " 系统安装: ${EXT_DST}"
|
||
sudo mkdir -p "$(dirname "${EXT_DST}")"
|
||
sudo rm -rf "${EXT_DST}"
|
||
sudo cp -r "${SCRIPT_DIR}/gnome-extension/${GNOME_EXT_UUID}" "${EXT_DST}"
|
||
sudo chmod -R o+r "${EXT_DST}"
|
||
else
|
||
EXT_DST="${HOME}/.local/share/gnome-shell/extensions/${GNOME_EXT_UUID}"
|
||
echo " 用户安装: ${EXT_DST}"
|
||
mkdir -p "$(dirname "${EXT_DST}")"
|
||
rm -rf "${EXT_DST}"
|
||
cp -r "${SCRIPT_DIR}/gnome-extension/${GNOME_EXT_UUID}" "${EXT_DST}"
|
||
fi
|
||
echo " ✓ GNOME 扩展已安装"
|
||
|
||
ENABLED=$(gsettings get org.gnome.shell enabled-extensions 2>/dev/null || echo "[]")
|
||
if echo "${ENABLED}" | grep -q "${GNOME_EXT_UUID}"; then
|
||
echo " ✓ 扩展已在启用列表中"
|
||
else
|
||
echo " 添加到 enabled-extensions..."
|
||
NEW_LIST=$(echo "${ENABLED}" | sed 's/\]$/,"'"${GNOME_EXT_UUID}"'"]/')
|
||
gsettings set org.gnome.shell enabled-extensions "${NEW_LIST}" 2>/dev/null || {
|
||
echo " ⚠ 无法自动启用,请在「扩展」应用中手动启用"
|
||
}
|
||
fi
|
||
else
|
||
echo " ⚠ 未找到扩展目录,跳过"
|
||
fi
|
||
|
||
echo ""
|
||
echo "[2/3] Wayland evdev 权限(Wayland 用户可选)..."
|
||
SESSION_TYPE="${XDG_SESSION_TYPE:-}"
|
||
if [ "$SESSION_TYPE" = "wayland" ] && [ ! -w /dev/uinput ]; then
|
||
echo " 当前为 Wayland 会话,evdev 快捷键需要 input 组权限"
|
||
echo " 执行以下命令并重新登录:"
|
||
echo " sudo usermod -aG input $USER"
|
||
echo " 或创建 udev 规则:"
|
||
echo ' KERNEL=="uinput", MODE="0660", GROUP="input"'
|
||
echo ' KERNEL=="event*", SUBSYSTEM=="input", MODE="0660", GROUP="input"'
|
||
else
|
||
echo " ✓ evdev 权限正常(X11 或已有 input 组访问)"
|
||
fi
|
||
|
||
echo ""
|
||
echo "[3/3] Desktop Entry..."
|
||
if [ -f "${SCRIPT_DIR}/${APP_NAME}.desktop" ]; then
|
||
if [ "$INSTALL_MODE" = "system" ]; then
|
||
DST="/usr/local/share/applications/${APP_NAME}.desktop"
|
||
sudo mkdir -p "$(dirname "${DST}")"
|
||
sudo cp "${SCRIPT_DIR}/${APP_NAME}.desktop" "${DST}"
|
||
sudo sed -i "s|^Exec=.*|Exec=${SCRIPT_DIR}/run.sh|" "${DST}"
|
||
echo " ✓ 已安装到 ${DST}"
|
||
else
|
||
DST="${HOME}/.local/share/applications/${APP_NAME}.desktop"
|
||
mkdir -p "$(dirname "${DST}")"
|
||
cp "${SCRIPT_DIR}/${APP_NAME}.desktop" "${DST}"
|
||
sed -i "s|^Exec=.*|Exec=${SCRIPT_DIR}/run.sh|" "${DST}"
|
||
echo " ✓ 已安装到 ${DST}"
|
||
fi
|
||
else
|
||
echo " ⚠ 未找到 .desktop 文件"
|
||
fi
|
||
|
||
echo ""
|
||
echo "============================================"
|
||
echo " 安装完成"
|
||
echo "============================================"
|
||
echo ""
|
||
echo "启动: ${SCRIPT_DIR}/run.sh 或从应用菜单搜索"
|
||
echo ""
|
||
echo "快捷键方案(自动检测,默认 Ctrl+Alt+C 可自定义):"
|
||
echo " 1. X11: XGrabKey(零权限)"
|
||
echo " 2. Wayland: evdev(需 input 组)"
|
||
echo " 3. GNOME 扩展: D-Bus 信号(备用)"
|
||
if echo "${ENABLED:-}" | grep -q "${GNOME_EXT_UUID}" 2>/dev/null; then
|
||
echo " 4. GNOME 扩展已启用"
|
||
fi
|
||
INSTALLSH
|
||
chmod +x "${PKG_DIR}/install.sh"
|
||
|
||
# --- run.sh ---
|
||
cat > "${PKG_DIR}/run.sh" << 'RUNSH'
|
||
#!/bin/bash
|
||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
export LD_LIBRARY_PATH="${SCRIPT_DIR}/lib:${LD_LIBRARY_PATH}"
|
||
export QT_QPA_PLATFORM_PLUGIN_PATH="${SCRIPT_DIR}/platforms"
|
||
export XDG_DESKTOP_PORTAL_APP_ID="io.impress.voice-input"
|
||
|
||
if [ -d "${SCRIPT_DIR}/gnome-extension" ] && \
|
||
[ ! -d "${HOME}/.local/share/gnome-shell/extensions/io.impress.voice-input-hotkey@impress" ]; then
|
||
echo "GNOME 用户推荐安装 CapsLock 快捷键扩展(一次即可):"
|
||
echo " ./install.sh"
|
||
echo ""
|
||
fi
|
||
|
||
exec "${SCRIPT_DIR}/impress_voice_input" "$@"
|
||
RUNSH
|
||
chmod +x "${PKG_DIR}/run.sh"
|
||
|
||
# desktop entry
|
||
cp "${DIST_DIR}/${PKG_NAME}/io.impress.voice-input.desktop" "${PKG_DIR}/" 2>/dev/null || true
|
||
|
||
# 7. 打包
|
||
echo "[6/6] 打包..."
|
||
cd "${DIST_DIR}"
|
||
tar czf "${PKG_NAME}.tar.gz" "${PKG_NAME}/"
|
||
|
||
echo ""
|
||
echo "发布包已生成: ${DIST_DIR}/${PKG_NAME}.tar.gz"
|
||
echo "大小: $(du -h "${DIST_DIR}/${PKG_NAME}.tar.gz" | cut -f1)"
|
||
echo ""
|
||
echo "使用方式:"
|
||
echo " tar xzf impress_voice_input_linux.tar.gz"
|
||
echo " cd impress_voice_input_linux"
|
||
echo " ./install.sh # 安装 GNOME 扩展 + 桌面入口"
|
||
echo " ./run.sh # 启动应用"
|