From e18821f9175ac9beff27c67fb2d32fb2a75294bc Mon Sep 17 00:00:00 2001 From: Nick007 Date: Sat, 28 Feb 2026 10:06:08 +0800 Subject: [PATCH] feat: auto-refresh menu on app install/uninstall and add third-party app docs - Extract menu generation logic into standalone refresh-menu.sh script - Use inotifywait to watch ~/Desktop/ for .desktop file changes and auto-refresh the openbox right-click menu on app install/uninstall - Add inotify-tools to Dockerfile dependencies - Add 'Installing Third-Party Applications' section to both READMEs documenting how to install apps like Telegram via sidebar panel --- Dockerfile | 2 +- README.md | 13 ++++++++ README_en.md | 13 ++++++++ root/scripts/refresh-menu.sh | 57 ++++++++++++++++++++++++++++++++++++ root/scripts/start.sh | 55 ++++++---------------------------- 5 files changed, 93 insertions(+), 47 deletions(-) create mode 100755 root/scripts/refresh-menu.sh diff --git a/Dockerfile b/Dockerfile index d6ae87a..4a7ef4f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,7 +27,7 @@ RUN apt-get update && \ libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \ libxcomposite1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 \ libxss1 libxtst6 libatomic1 libxcomposite1 libxrender1 libxrandr2 libxkbcommon-x11-0 \ - libfontconfig1 libdbus-1-3 libnss3 libx11-xcb1 python3-tk stalonetray + libfontconfig1 libdbus-1-3 libnss3 libx11-xcb1 python3-tk stalonetray inotify-tools RUN pip install --no-cache-dir python-xlib diff --git a/README.md b/README.md index fc65a21..e341e2a 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,19 @@ docker run -it -p 3001:3001 -v ./config:/config --device /dev/dri:/dev/dri nickr > **注意:** 如果升级后右键菜单缺少 `WeChat` 相关选项,请先清空本地挂载目录下的openbox目录(如`./config/.config/openbox`)。 +## 安装第三方应用(如 Telegram) + +本项目支持通过 [proot-apps](https://github.com/linuxserver/proot-apps) 安装第三方 Linux 应用。以 Telegram 为例: + +1. 在浏览器中打开容器桌面 +2. 点击左侧 **侧边栏** → **应用程序**(Applications) +3. 在应用列表中找到 **Telegram** +4. 点击 **安装**(Install)按钮,等待安装完成 + +安装完成后,应用快捷方式会自动出现在 `~/Desktop/` 目录下,**右键菜单会自动刷新**,无需重启容器即可从菜单中启动该应用。 + +> **提示:** 如需卸载应用,同样通过侧边栏 → 应用程序,选中对应应用后点击 **卸载**(Uninstall)即可,右键菜单会自动更新。 + ## 高级配置 ### 硬件加速 diff --git a/README_en.md b/README_en.md index 889a8d0..e58b29b 100644 --- a/README_en.md +++ b/README_en.md @@ -158,6 +158,19 @@ Configure the following environment variables in `docker-compose.yml`: > **Note:** If the right-click menu lacks `WeChat` related options after an upgrade, please clear the `openbox` directory in the local mounted directory (e.g., `./config/.config/openbox`). +## Installing Third-Party Applications (e.g., Telegram) + +This project supports installing third-party Linux applications via [proot-apps](https://github.com/linuxserver/proot-apps). Here's how to install Telegram as an example: + +1. Open the container desktop in your browser +2. Click the **Sidebar** on the left → **Applications** +3. Find **Telegram** in the application list +4. Click the **Install** button and wait for the installation to complete + +Once installed, the application shortcut will automatically appear in the `~/Desktop/` directory, and the **right-click menu will auto-refresh** — no container restart needed to launch the app from the menu. + +> **Tip:** To uninstall an application, go to Sidebar → Applications, select the app, and click **Uninstall**. The right-click menu will update automatically. + ## Advanced Configuration ### Hardware Acceleration diff --git a/root/scripts/refresh-menu.sh b/root/scripts/refresh-menu.sh new file mode 100755 index 0000000..1026da8 --- /dev/null +++ b/root/scripts/refresh-menu.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Regenerate openbox right-click menu from defaults + ~/Desktop/*.desktop files + +MENU_DEFAULT="/defaults/menu.xml" +MENU_TARGET="/config/.config/openbox/menu.xml" +MENU_TMP="/tmp/menu.xml" + +mkdir -p /config/.config/openbox +cp "$MENU_DEFAULT" "$MENU_TMP" + +if ls "$HOME/Desktop/"*.desktop >/dev/null 2>&1; then + for desktop_file in "$HOME/Desktop/"*.desktop; do + name=$(grep -E "^Name=" "$desktop_file" | head -n 1 | cut -d "=" -f 2-) + exec_cmd=$(grep -E "^Exec=" "$desktop_file" | head -n 1 | cut -d "=" -f 2-) + icon=$(grep -E "^Icon=" "$desktop_file" | head -n 1 | cut -d "=" -f 2-) + + # skip entries without a name or exec command + [ -z "$name" ] || [ -z "$exec_cmd" ] && continue + + # strip %U, %u, %F, %f field codes + exec_cmd=$(echo "$exec_cmd" | sed 's/ %[fFuU]//g') + + # resolve icon path if it's just a name (not an absolute path) + if [ -n "$icon" ] && [ "${icon#/}" = "$icon" ]; then + icon_resolved="" + # search in proot-apps icons (prefer 256x256) + for size in 256x256 512x512 128x128 64x64 48x48 scalable; do + found=$(find /config/proot-apps/*/usr/share/icons/hicolor/"$size"/apps/"$icon".* 2>/dev/null | head -n 1) + if [ -n "$found" ]; then + icon_resolved="$found" + break + fi + done + # fallback: search system icons + if [ -z "$icon_resolved" ]; then + found=$(find /usr/share/icons/hicolor/*/apps/"$icon".* 2>/dev/null | head -n 1) + [ -n "$found" ] && icon_resolved="$found" + fi + [ -n "$icon_resolved" ] && icon="$icon_resolved" + fi + + # handle missing icon + [ -z "$icon" ] && icon="/usr/share/pixmaps/xterm-color_48x48.xpm" + + # escape XML entities + exec_cmd=$(echo "$exec_cmd" | sed 's/&/\&/g; s//\>/g') + name=$(echo "$name" | sed 's/&/\&/g; s//\>/g; s/"/\"/g') + icon=$(echo "$icon" | sed 's/&/\&/g; s//\>/g; s/"/\"/g') + + sed -i "//a \\${exec_cmd}" "$MENU_TMP" + done +fi + +if [ ! -f "$MENU_TARGET" ] || ! cmp -s "$MENU_TMP" "$MENU_TARGET"; then + cp "$MENU_TMP" "$MENU_TARGET" + openbox --reconfigure 2>/dev/null || true +fi diff --git a/root/scripts/start.sh b/root/scripts/start.sh index d253be7..66e26f3 100755 --- a/root/scripts/start.sh +++ b/root/scripts/start.sh @@ -8,53 +8,16 @@ if [ ! -f /config/.config/openbox/rc.xml ] || grep -A20 "" /config/.config openbox --reconfigure fi -# generate openbox menu -mkdir -p /config/.config/openbox -cp /defaults/menu.xml /tmp/menu.xml +# generate openbox menu from defaults + ~/Desktop/*.desktop files +/scripts/refresh-menu.sh -if ls "$HOME/Desktop/"*.desktop >/dev/null 2>&1; then - for desktop_file in "$HOME/Desktop/"*.desktop; do - name=$(grep -E "^Name=" "$desktop_file" | head -n 1 | cut -d "=" -f 2-) - exec_cmd=$(grep -E "^Exec=" "$desktop_file" | head -n 1 | cut -d "=" -f 2-) - icon=$(grep -E "^Icon=" "$desktop_file" | head -n 1 | cut -d "=" -f 2-) - - # strip %U, %u, %F, %f - exec_cmd=$(echo "$exec_cmd" | sed 's/ %[fFuU]//g') - - # resolve icon path if it's just a name (not an absolute path) - if [ -n "$icon" ] && [ "${icon#/}" = "$icon" ]; then - icon_resolved="" - # search in proot-apps icons (prefer 256x256) - for size in 256x256 512x512 128x128 64x64 48x48 scalable; do - found=$(find /config/proot-apps/*/usr/share/icons/hicolor/"$size"/apps/"$icon".* 2>/dev/null | head -n 1) - if [ -n "$found" ]; then - icon_resolved="$found" - break - fi - done - # fallback: search system icons - if [ -z "$icon_resolved" ]; then - found=$(find /usr/share/icons/hicolor/*/apps/"$icon".* 2>/dev/null | head -n 1) - [ -n "$found" ] && icon_resolved="$found" - fi - [ -n "$icon_resolved" ] && icon="$icon_resolved" - fi - - # handle missing icon - [ -z "$icon" ] && icon="/usr/share/pixmaps/xterm-color_48x48.xpm" - - # escape XML entities - exec_cmd=$(echo "$exec_cmd" | sed 's/&/\&/g; s//\>/g') - name=$(echo "$name" | sed 's/&/\&/g; s//\>/g; s/"/\"/g') - icon=$(echo "$icon" | sed 's/&/\&/g; s//\>/g; s/"/\"/g') - - sed -i "//a \\${exec_cmd}" /tmp/menu.xml - done -fi - -if [ ! -f /config/.config/openbox/menu.xml ] || ! cmp /tmp/menu.xml /config/.config/openbox/menu.xml; then - cp /tmp/menu.xml /config/.config/openbox/menu.xml - openbox --reconfigure +# watch ~/Desktop/ for .desktop file changes and auto-refresh menu +mkdir -p "$HOME/Desktop" +if command -v inotifywait >/dev/null 2>&1; then + (while inotifywait -q -e create -e delete -e modify "$HOME/Desktop/" --include '\.desktop$'; do + sleep 1 + /scripts/refresh-menu.sh + done) >/dev/null 2>&1 & fi nohup stalonetray --dockapp-mode simple > /dev/null 2>&1 &