impress_voice_input/package-linux.sh
impressionyang 20bc24a028 feat(linux): 新增多后端快捷键支持和录音指示器
- 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>
2026-06-15 16:43:16 +08:00

219 lines
8.2 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.

#!/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 # 启动应用"