diff --git a/README.md b/README.md index 5e314b9..8b2f385 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,20 @@ This is a monorepo containing a collection of GitHub Actions maintained by Lizar ## actions -| Action | Description | Type | Language | -|-------------------------------------------------------|----------------------------------------------|-----------|------------------| -| [audit_repos](actions/audit_repos#readme) | Audit repositories in an organization | composite | javascript | -| [facebook_post](actions/facebook_post#readme) | Post to Facebook page/group using Graph API | docker | python | -| [monitor_space](actions/monitor_space#readme) | Monitor and track minimum free disk space | composite | bash | -| [more_space](actions/more_space#readme) | Free up disk space in GitHub Actions runners | composite | bash | -| [release_changelog](actions/release_changelog#readme) | Generate a changelog for the latest release | composite | javascript | -| [release_create](actions/release_create#readme) | Create a new release | composite | bash, javascript | -| [release_homebrew](actions/release_homebrew#readme) | Validate and update Homebrew formula | composite | bash, python | -| [release_setup](actions/release_setup#readme) | Prepare a release | docker | python | -| [setup_cuda](actions/setup_cuda#readme) | Set up NVIDIA CUDA Toolkit on Linux runners | composite | bash | -| [setup_python](actions/setup_python#readme) | Set up Python environment | composite | bash | +| Action | Description | Type | Language | +|---------------------------------------------------------------|----------------------------------------------|-----------|------------------| +| [audit_repos](actions/audit_repos#readme) | Audit repositories in an organization | composite | javascript | +| [facebook_post](actions/facebook_post#readme) | Post to Facebook page/group using Graph API | docker | python | +| [monitor_space](actions/monitor_space#readme) | Monitor and track minimum free disk space | composite | bash | +| [more_space](actions/more_space#readme) | Free up disk space in GitHub Actions runners | composite | bash | +| [release_changelog](actions/release_changelog#readme) | Generate a changelog for the latest release | composite | javascript | +| [release_create](actions/release_create#readme) | Create a new release | composite | bash, javascript | +| [release_homebrew](actions/release_homebrew#readme) | Validate and update Homebrew formula | composite | bash, python | +| [release_setup](actions/release_setup#readme) | Prepare a release | docker | python | +| [screenshot](actions/screenshot#readme) | Setup cross-platform screenshot CLI tool | composite | bash | +| [setup_cuda](actions/setup_cuda#readme) | Set up NVIDIA CUDA Toolkit on Linux runners | composite | bash | +| [setup_python](actions/setup_python#readme) | Set up Python environment | composite | bash | +| [setup_virtual_desktop](actions/setup_virtual_desktop#readme) | Setup virtual desktop for GUI apps on Linux | composite | bash | ## Contributions diff --git a/actions/screenshot/README.md b/actions/screenshot/README.md index f9763bf..ca5fef3 100644 --- a/actions/screenshot/README.md +++ b/actions/screenshot/README.md @@ -12,11 +12,13 @@ points in time or for debugging purposes. No preparation needed! These platforms have built-in screenshot capabilities. ### Linux -Linux requires a display server (X11 or Wayland) to be running. Use a display setup action before this one: +Linux requires a display server (X11 or Wayland) to be running. Use the virtual desktop setup action before this one: ```yaml - name: Setup virtual display - uses: aganders3/headless-gui@v2 + uses: LizardByte/actions/actions/setup_virtual_desktop@master + with: + environment: xfce ``` ## 🚀 Basic Usage @@ -189,22 +191,25 @@ jobs: screenshot: runs-on: ubuntu-latest steps: + - name: Setup virtual desktop + uses: LizardByte/actions/actions/setup_virtual_desktop@master + with: + environment: xfce + - name: Setup screenshot tool id: screenshot uses: LizardByte/actions/actions/screenshot@master - - name: Setup and use headless display - uses: aganders3/headless-gui@v2 - with: - run: | - # Launch something visual - xeyes & - sleep 2 - - # Take multiple screenshots - ${{ steps.screenshot.outputs.tool-path }} --output-path=screenshot1.png - sleep 1 - ${{ steps.screenshot.outputs.tool-path }} --output-path=screenshot2.png --delay=500 + - name: Take screenshots + run: | + # Launch something visual + xeyes & + sleep 2 + + # Take multiple screenshots + ${{ steps.screenshot.outputs.tool-path }} --output-path=screenshot1.png + sleep 1 + ${{ steps.screenshot.outputs.tool-path }} --output-path=screenshot2.png --delay=500 - name: Upload screenshots uses: actions/upload-artifact@v6 diff --git a/actions/screenshot/action.yml b/actions/screenshot/action.yml index ac0cbcc..059952b 100644 --- a/actions/screenshot/action.yml +++ b/actions/screenshot/action.yml @@ -72,3 +72,4 @@ runs: with: name: screenshot-${{ runner.os }} path: screenshot.png + if-no-files-found: error diff --git a/actions/screenshot/post-ci.sh b/actions/screenshot/post-ci.sh index 56985de..4ee5562 100644 --- a/actions/screenshot/post-ci.sh +++ b/actions/screenshot/post-ci.sh @@ -39,15 +39,3 @@ fi echo "" echo "Screenshot validation successful!" - -# Cleanup Xvfb if running on Linux -if [[ "$OSTYPE" == "linux-gnu"* && -f /tmp/xvfb_screenshot.pid ]]; then - XVFB_PID=$(cat /tmp/xvfb_screenshot.pid) - if ps -p "$XVFB_PID" > /dev/null 2>&1; then - echo "" - echo "Cleaning up Xvfb (PID: $XVFB_PID)..." - kill "$XVFB_PID" || true - rm -f /tmp/xvfb_screenshot.pid - echo "✓ Xvfb stopped" - fi -fi diff --git a/actions/screenshot/pre-ci.sh b/actions/screenshot/pre-ci.sh index 0c967c8..f521481 100644 --- a/actions/screenshot/pre-ci.sh +++ b/actions/screenshot/pre-ci.sh @@ -6,47 +6,13 @@ echo "Pre-CI: Setting up display for screenshot tests..." # Only need to setup display on Linux if [[ "$OSTYPE" == "linux-gnu"* ]]; then - echo "Detected Linux - setting up virtual display..." + echo "Detected Linux - setting up virtual desktop..." - # Install xvfb if not already present - if ! command -v Xvfb &> /dev/null; then - echo "Installing Xvfb..." - sudo apt-get update -qq - sudo apt-get install -y -qq xvfb - sudo apt-get clean - sudo rm -rf /var/lib/apt/lists/* - fi - - # Start Xvfb on display :99 - echo "Starting Xvfb virtual display on :99..." - Xvfb :99 -screen 0 1024x768x24 & - XVFB_PID=$! - - # Save PID for cleanup in post-ci - echo "$XVFB_PID" > /tmp/xvfb_screenshot.pid - - # Set DISPLAY environment variable - export DISPLAY=:99 - echo "DISPLAY=$DISPLAY" >> "$GITHUB_ENV" - - # Wait a moment for Xvfb to start - sleep 2 - - # Verify Xvfb is running - if ps -p $XVFB_PID > /dev/null; then - echo "✓ Xvfb is running (PID: $XVFB_PID)" - echo "✓ DISPLAY set to :99" - else - echo "✗ Failed to start Xvfb" - exit 1 - fi - - # Optional: Start a simple window manager for more realistic testing - if command -v fluxbox &> /dev/null; then - echo "Starting fluxbox window manager..." - fluxbox & - sleep 1 - fi + # Call the setup_virtual_desktop action's setup script + chmod +x ./actions/setup_virtual_desktop/setup_desktop.sh + ./actions/setup_virtual_desktop/setup_desktop.sh \ + --environment=xfce \ + --display-size=1280x720 else echo "Not Linux - no display setup needed" @@ -54,4 +20,4 @@ else fi echo "" -echo "Display setup complete!" +echo "Pre-CI setup complete!" diff --git a/actions/setup_virtual_desktop/README.md b/actions/setup_virtual_desktop/README.md new file mode 100644 index 0000000..a64bd02 --- /dev/null +++ b/actions/setup_virtual_desktop/README.md @@ -0,0 +1,352 @@ +# setup_virtual_desktop + +A reusable action to set up a virtual desktop environment on Linux GitHub Actions runners for GUI applications, +system tray icons, and notifications. + +This action configures a headless X11 server (Xvfb) with a full desktop environment, enabling you to run and test GUI +applications that require a display, system tray support, or notification capabilities. + +## 🛠️ Prep Work + +**Linux Only** - This action only works on Linux runners and will fail if run on Windows or macOS. + +## 🚀 Basic Usage + +See [action.yml](action.yml) + +```yaml +steps: + - name: Setup virtual desktop + uses: LizardByte/actions/actions/setup_virtual_desktop@master + with: + appindicator-version: ayatana + display-size: 1280x720 + environment: xfce +``` + +## 📥 Inputs + +| Name | Description | Default | Required | +|-----------------------|-----------------------------------------------------------------------|------------|----------| +| appindicator-version | AppIndicator version (ayatana, legacy). Only applies to mate and xfce | `ayatana` | `false` | +| display-size | Display resolution in WIDTHxHEIGHT format (e.g., 1920x1080) | `1280x720` | `false` | +| environment | Desktop environment (fluxbox, lxde, mate, openbox, xfce) | `xfce` | `false` | + +## 📤 Outputs + +| Name | Description | +|-----------|---------------------------------------| +| display | DISPLAY environment variable value | +| xvfb-pid | Process ID of the Xvfb server | + +## 🖥️ Desktop Environments + +### Fluxbox + +![Fluxbox Desktop](docs/images/screenshot-fluxbox.png) + +**Best for:** Lightweight standalone window manager + +- **Tray Support:** ✅ Good (stalonetray) +- **Notifications:** ✅ Full support (dunst) +- **Footprint:** Very small (~50MB) +- **Features:** Built-in toolbar, minimal dependencies + +```yaml +with: + environment: fluxbox +``` + +### LXDE + +![LXDE Desktop](docs/images/screenshot-lxde.png) + +**Best for:** Lightweight option with basic desktop features + +- **Tray Support:** ✅ Good (lxpanel) +- **Notifications:** ✅ Full support (notification-daemon) +- **Footprint:** Small (~80MB) +- **Features:** Minimal resource usage, fast startup + +```yaml +with: + environment: lxde +``` + +### MATE + +![MATE Desktop](docs/images/screenshot-mate.png) + +**Best for:** Traditional desktop experience with excellent compatibility + +- **Tray Support:** ✅ Excellent (mate-indicator-applet) +- **AppIndicator:** ✅ Full support (ayatana or legacy) +- **Notifications:** ✅ Full support (mate-notification-daemon) +- **Footprint:** Medium (~160MB) +- **Features:** Fork of GNOME 2, traditional desktop layout, high application compatibility + +```yaml +with: + appindicator-version: ayatana # or 'legacy' + environment: mate +``` + +### Openbox + +![Openbox Desktop](docs/images/screenshot-openbox.png) + +**Best for:** Minimal window manager with tray via tint2 + +- **Tray Support:** ✅ Good (tint2 panel) +- **Notifications:** ✅ Full support (dunst) +- **Footprint:** Very small (~50MB) +- **Features:** Standalone window manager, highly customizable + +```yaml +with: + environment: openbox +``` + +### XFCE + +![XFCE Desktop](docs/images/screenshot-xfce.png) + +**Best for:** Full-featured desktop with excellent tray support + +- **Tray Support:** ✅ Excellent (xfce4-indicator-plugin) +- **AppIndicator:** ✅ Full support (ayatana or legacy) +- **Notifications:** ✅ Full support (xfce4-notifyd) +- **Footprint:** Medium (~150MB) +- **Features:** Complete desktop environment with panels, system tray, window decorations + +```yaml +with: + appindicator-version: ayatana # or 'legacy' + environment: xfce +``` + +## 🖥 Example Workflows + +### Basic - Run GUI Application + +```yaml +name: Test GUI App +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup virtual desktop + uses: LizardByte/actions/actions/setup_virtual_desktop@master + with: + environment: xfce + + - name: Run GUI application + run: | + # DISPLAY is already set by the action + python my_gui_app.py & + sleep 5 +``` + +### Testing System Tray Application + +```yaml +name: Test Tray Icon +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup virtual desktop + id: desktop + uses: LizardByte/actions/actions/setup_virtual_desktop@master + with: + appindicator-version: ayatana + display-size: 1920x1080 + environment: xfce + + - name: Install app dependencies + run: | + sudo apt-get update + sudo apt-get install -y python3-gi gir1.2-ayatanaappindicator3-0.1 + + - name: Run tray application + run: | + echo "Display: ${{ steps.desktop.outputs.display }}" + python3 tray_app.py & + APP_PID=$! + sleep 5 + + # Verify app is running + if ps -p $APP_PID > /dev/null; then + echo "Tray app is running" + fi +``` + +### Testing Notifications + +```yaml +name: Test Notifications +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Setup virtual desktop + uses: LizardByte/actions/actions/setup_virtual_desktop@master + with: + environment: xfce + + - name: Send test notification + run: | + notify-send "Test Title" "Test message body" + + - name: Test Python notifications + run: | + sudo apt-get update + sudo apt-get install -y python3-gi gir1.2-notify-0.7 + python3 -c " + from gi.repository import Notify + Notify.init('Test') + n = Notify.Notification.new('Python Notification', 'From Python!') + n.show() + " +``` + +### With Screenshots + +```yaml +name: Test with Screenshots +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Setup virtual desktop + uses: LizardByte/actions/actions/setup_virtual_desktop@master + with: + environment: openbox + display-size: 1024x768 + + - name: Setup screenshot tool + id: screenshot + uses: LizardByte/actions/actions/screenshot@master + + - name: Run GUI and capture + run: | + xterm & + sleep 2 + ${{ steps.screenshot.outputs.tool-path }} --output-path=desktop.png + + - name: Upload screenshot + uses: actions/upload-artifact@v4 + with: + name: desktop-screenshot + path: desktop.png +``` + +### High Resolution Testing + +```yaml +name: High-DPI Testing +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Setup 4K virtual desktop + uses: LizardByte/actions/actions/setup_virtual_desktop@master + with: + environment: lxde + display-size: 3840x2160 + + - name: Run high-DPI tests + run: pytest tests/test_hidpi.py +``` + +### Matrix Testing Multiple Environments + +```yaml +name: Test All Environments +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + desktop: [xfce, lxde, openbox, fluxbox] + steps: + - name: Setup ${{ matrix.desktop }} + uses: LizardByte/actions/actions/setup_virtual_desktop@master + with: + environment: ${{ matrix.desktop }} + + - name: Run tests + run: pytest tests/ +``` + +## 📝 Notes + +- **Linux Only:** This action will fail with an error if run on Windows or macOS runners +- **Display Variable:** The `DISPLAY` environment variable is automatically set for subsequent steps +- **Process Management:** Xvfb and the desktop environment run in the background for the duration of the job +- **Resource Usage:** Desktop environments vary in size: + - Minimal (openbox, fluxbox): ~50MB + - Lightweight (lxde): ~80MB + - Full-featured (xfce): ~150MB +- **Tray Icons:** All environments support system tray icons through their respective panel implementations +- **AppIndicator Support:** + - Only available on MATE and XFCE environments + - **Ayatana** (recommended): Modern fork with active development + - **Legacy**: Original Ubuntu implementation (deprecated but still available) + - Python apps need `gir1.2-ayatanaappindicator3-0.1` or `gir1.2-appindicator3-0.1` +- **Notifications:** All environments include notification daemon support (`notify-send` command available) +- **Common Resolutions:** + - HD: 1280x720 + - Full HD: 1920x1080 + - 4K: 3840x2160 + - Custom: Any WIDTHxHEIGHT format + +## 🔧 Troubleshooting + +### GUI Application Won't Start +```yaml +- name: Debug display + run: | + echo "DISPLAY=$DISPLAY" + xdpyinfo | grep dimensions +``` + +### Tray Icon Not Appearing +Different environments have different tray implementations. Try XFCE for the most reliable tray support. + +### Out of Memory +Use a lighter environment (openbox, fluxbox, or lxde) if you encounter memory issues. + +## 🔗 See Also + +This action works well with: +- [screenshot](../screenshot) - Capture screenshots of the virtual desktop +- [setup_python](../setup_python) - Set up Python for GUI testing + +## 💡 Common Use Cases + +- Testing GUI applications in CI/CD +- Running applications that require system tray support +- Testing notification functionality +- Automated screenshot capture of applications +- Integration testing for desktop applications +- AppIndicator testing (Ubuntu/GNOME style tray icons) +- Qt/GTK application testing diff --git a/actions/setup_virtual_desktop/action.yml b/actions/setup_virtual_desktop/action.yml new file mode 100644 index 0000000..17b8b2b --- /dev/null +++ b/actions/setup_virtual_desktop/action.yml @@ -0,0 +1,194 @@ +--- +name: "Setup Virtual Desktop" +description: "Setup a virtual desktop environment on Linux for GUI applications, tray icons, and notifications." +author: "LizardByte" + +branding: + icon: monitor + color: green + +inputs: + appindicator-version: + description: 'AppIndicator version to use (ayatana, legacy). Only applies to mate and xfce environments.' + required: false + default: 'ayatana' + display-size: + description: 'Display resolution (e.g., 1280x720, 1920x1080)' + required: false + default: '1280x720' + environment: + description: 'Desktop environment to setup (fluxbox, lxde, mate, openbox, xfce)' + required: false + default: 'xfce' + +outputs: + display: + description: 'DISPLAY environment variable value' + value: ${{ steps.setup.outputs.display }} + xvfb-pid: + description: 'Process ID of the Xvfb server' + value: ${{ steps.setup.outputs.xvfb-pid }} + +runs: + using: "composite" + steps: + - name: Verify Linux platform + shell: bash + run: | + if [[ "$RUNNER_OS" != "Linux" ]]; then + echo "Error: This action only works on Linux runners" + echo "Current runner OS: $RUNNER_OS" + exit 1 + fi + + - name: Make script executable + shell: bash + run: chmod +x "${GITHUB_ACTION_PATH}/setup_desktop.sh" + + - name: Setup virtual desktop + id: setup + shell: bash + env: + INPUT_APPINDICATOR_VERSION: ${{ inputs.appindicator-version }} + INPUT_DISPLAY_SIZE: ${{ inputs.display-size }} + INPUT_ENVIRONMENT: ${{ inputs.environment }} + run: | + "${GITHUB_ACTION_PATH}/setup_desktop.sh" \ + --appindicator-version="${INPUT_APPINDICATOR_VERSION}" \ + --display-size="${INPUT_DISPLAY_SIZE}" \ + --environment="${INPUT_ENVIRONMENT}" + + # For PR testing only + - name: Checkout + if: github.repository == 'LizardByte/actions' + uses: actions/checkout@v6 + + - name: Tests + if: github.repository == 'LizardByte/actions' + env: + INPUT_APPINDICATOR_VERSION: ${{ inputs.appindicator-version }} + INPUT_ENVIRONMENT: ${{ inputs.environment }} + SCREENSHOT_PATH: ${{ github.workspace }}/actions/screenshot/screenshot.sh + shell: bash + run: | + echo "::group::Setup directories" + mkdir -p tests/screenshots + cd tests/screenshots + echo "::endgroup::" + + echo "::group::Script setup" + chmod +x ${SCREENSHOT_PATH} + echo "::endgroup::" + + echo "::group::Install test dependencies" + sudo apt-get update -qq + sudo apt-get install -y -qq \ + imagemagick \ + python3-gi \ + xterm \ + yad + echo "::endgroup::" + + echo "::group::Baseline screenshot" + ${SCREENSHOT_PATH} --output-path=00-baseline.png + echo "::endgroup::" + + echo "::group::Test notification" + notify-send \ + --icon=dialog-information \ + --expire-time=5000 \ + "Test Notification" \ + "This is a test notification." + ${SCREENSHOT_PATH} --output-path=01-notification.png --delay=2000 + echo "::endgroup::" + + echo "::group::Test tray icon" + # Create a simple tray icon using yad + yad --notification \ + --image=info \ + --text="Test Tray Icon" \ + --command="echo 'Tray icon clicked'" & + YAD_PID=$! + ${SCREENSHOT_PATH} --output-path=02-tray-icon.png --delay=3000 + kill $YAD_PID 2>/dev/null || true + echo "::endgroup::" + + echo "::group::Test window manager" + # Open a simple terminal window to verify WM is working + xterm -T "Test Window" -e "echo 'Test window'; sleep 2" & + XTERM_PID=$! + ${SCREENSHOT_PATH} --output-path=03-window.png --delay=1000 + kill $XTERM_PID 2>/dev/null || true + echo "::endgroup::" + + echo "::group::Test multiple tray icons" + # Create multiple tray icons to verify tray is working properly + yad --notification \ + --image=preferences-system \ + --text="Settings Icon" & + YAD1=$! + + yad --notification \ + --image=dialog-warning \ + --text="Warning Icon" & + YAD2=$! + + ${SCREENSHOT_PATH} --output-path=04-multiple-tray.png --delay=2000 + kill $YAD1 $YAD2 2>/dev/null || true + echo "::endgroup::" + + echo "::group::Test AppIndicator" + # Test ayatana-appindicator (only on MATE/XFCE which have indicator support) + if [[ "$INPUT_ENVIRONMENT" == "mate" ]] || [[ "$INPUT_ENVIRONMENT" == "xfce" ]]; then + echo "::group::AppIndicator Debug Info" + bash "${GITHUB_ACTION_PATH}/debug_indicators.sh" + echo "::endgroup::" + + echo "Testing AppIndicator support (${INPUT_APPINDICATOR_VERSION})..." + # Run the test in the background so we can screenshot while it's active + python3 "${GITHUB_ACTION_PATH}/test_appindicator.py" "${INPUT_APPINDICATOR_VERSION}" & + APPINDICATOR_PID=$! + + # Wait a moment for the indicator to appear, then take screenshot + sleep 1 + ${SCREENSHOT_PATH} --output-path=05-appindicator.png --delay=500 + + # Wait for the test to complete + if wait $APPINDICATOR_PID; then + echo "AppIndicator test completed successfully" + else + echo "::error::AppIndicator test failed" + exit 1 + fi + else + echo "Skipping AppIndicator test (not supported on ${INPUT_ENVIRONMENT})" + fi + echo "::endgroup::" + + ls -lh + + - name: Set artifact name + if: github.repository == 'LizardByte/actions' + id: artifact-name + shell: bash + env: + INPUT_APPINDICATOR_VERSION: ${{ inputs.appindicator-version }} + INPUT_ENVIRONMENT: ${{ inputs.environment }} + run: | + case "$INPUT_ENVIRONMENT" in + mate|xfce) + name="virtual-desktop-screenshots-${INPUT_ENVIRONMENT}-${INPUT_APPINDICATOR_VERSION}" + ;; + *) + name="virtual-desktop-screenshots-${INPUT_ENVIRONMENT}" + ;; + esac + echo "name=${name}" >> "${GITHUB_OUTPUT}" + + - name: Upload screenshots + if: github.repository == 'LizardByte/actions' + uses: actions/upload-artifact@v6 + with: + name: ${{ steps.artifact-name.outputs.name }} + path: tests/screenshots/*.png + if-no-files-found: error diff --git a/actions/setup_virtual_desktop/ci-matrix.json b/actions/setup_virtual_desktop/ci-matrix.json new file mode 100644 index 0000000..fb248ff --- /dev/null +++ b/actions/setup_virtual_desktop/ci-matrix.json @@ -0,0 +1,48 @@ +[ + { + "runs-on": "ubuntu-latest", + "with": { + "environment": "fluxbox" + } + }, + { + "runs-on": "ubuntu-latest", + "with": { + "environment": "lxde" + } + }, + { + "runs-on": "ubuntu-latest", + "with": { + "environment": "mate", + "appindicator-version": "ayatana" + } + }, + { + "runs-on": "ubuntu-latest", + "with": { + "environment": "mate", + "appindicator-version": "legacy" + } + }, + { + "runs-on": "ubuntu-latest", + "with": { + "environment": "openbox" + } + }, + { + "runs-on": "ubuntu-latest", + "with": { + "environment": "xfce", + "appindicator-version": "ayatana" + } + }, + { + "runs-on": "ubuntu-latest", + "with": { + "environment": "xfce", + "appindicator-version": "legacy" + } + } +] diff --git a/actions/setup_virtual_desktop/debug_indicators.sh b/actions/setup_virtual_desktop/debug_indicators.sh new file mode 100644 index 0000000..0699647 --- /dev/null +++ b/actions/setup_virtual_desktop/debug_indicators.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Debug script to check AppIndicator environment + +echo "=== AppIndicator Debug Information ===" +echo "" + +echo "1. Checking if indicator service is running:" +pgrep -fa "(ayatana-indicator|indicator-application)" || echo " No indicator service found" +echo "" + +echo "2. Checking D-Bus session:" +echo " DBUS_SESSION_BUS_ADDRESS: ${DBUS_SESSION_BUS_ADDRESS:-NOT SET}" +echo "" + +echo "3. Checking if indicator service is on D-Bus:" +dbus-send --session --dest=org.freedesktop.DBus --print-reply /org/freedesktop/DBus \ + org.freedesktop.DBus.ListNames 2>/dev/null | grep -i indicator || echo " No indicator service registered on D-Bus" +echo "" + +echo "4. Checking GObject Introspection typelibs:" +ls -lh /usr/lib/x86_64-linux-gnu/girepository-1.0/*AppIndicator* 2>/dev/null || echo " No AppIndicator typelibs found" +echo "" + +echo "5. Checking panel processes:" +pgrep -fa "(mate-panel|xfce4-panel)" || echo " No panel found" +echo "" + +echo "6. Checking DISPLAY:" +echo " DISPLAY: ${DISPLAY:-NOT SET}" +echo "" + +echo "7. Installed AppIndicator packages:" +dpkg -l | grep -E "(ayatana-indicator|libayatana-appindicator|gir1.2-ayatanaappindicator|gir1.2-appindicator|indicator-application)" || echo " No AppIndicator packages installed" +echo "" + +echo "8. Testing simple indicator registration:" +python3 -c " +import gi +try: + gi.require_version('AyatanaAppIndicator3', '0.1') + from gi.repository import AyatanaAppIndicator3 as AppIndicator + print(' ✓ Python can import AyatanaAppIndicator3 (Ayatana)') + indicator = AppIndicator.Indicator.new('test', 'mail-message-new', AppIndicator.IndicatorCategory.APPLICATION_STATUS) + print(' ✓ Created Ayatana indicator object') + indicator.set_status(AppIndicator.IndicatorStatus.ACTIVE) + print(' ✓ Set Ayatana indicator status to ACTIVE') +except Exception as e: + print(f' ✗ Ayatana AppIndicator failed: {e}') + +try: + gi.require_version('AppIndicator3', '0.1') + from gi.repository import AppIndicator3 as AppIndicator + print(' ✓ Python can import AppIndicator3 (Legacy)') + indicator = AppIndicator.Indicator.new('test', 'mail-message-new', AppIndicator.IndicatorCategory.APPLICATION_STATUS) + print(' ✓ Created Legacy indicator object') + indicator.set_status(AppIndicator.IndicatorStatus.ACTIVE) + print(' ✓ Set Legacy indicator status to ACTIVE') +except Exception as e: + print(f' ✗ Legacy AppIndicator failed: {e}') +" 2>&1 + +echo "" +echo "=== End Debug Information ===" diff --git a/actions/setup_virtual_desktop/docs/images/screenshot-fluxbox.png b/actions/setup_virtual_desktop/docs/images/screenshot-fluxbox.png new file mode 100644 index 0000000..ea87ea1 Binary files /dev/null and b/actions/setup_virtual_desktop/docs/images/screenshot-fluxbox.png differ diff --git a/actions/setup_virtual_desktop/docs/images/screenshot-lxde.png b/actions/setup_virtual_desktop/docs/images/screenshot-lxde.png new file mode 100644 index 0000000..88c848c Binary files /dev/null and b/actions/setup_virtual_desktop/docs/images/screenshot-lxde.png differ diff --git a/actions/setup_virtual_desktop/docs/images/screenshot-mate.png b/actions/setup_virtual_desktop/docs/images/screenshot-mate.png new file mode 100644 index 0000000..102cf3a Binary files /dev/null and b/actions/setup_virtual_desktop/docs/images/screenshot-mate.png differ diff --git a/actions/setup_virtual_desktop/docs/images/screenshot-openbox.png b/actions/setup_virtual_desktop/docs/images/screenshot-openbox.png new file mode 100644 index 0000000..72e52ec Binary files /dev/null and b/actions/setup_virtual_desktop/docs/images/screenshot-openbox.png differ diff --git a/actions/setup_virtual_desktop/docs/images/screenshot-xfce.png b/actions/setup_virtual_desktop/docs/images/screenshot-xfce.png new file mode 100644 index 0000000..23e7fc9 Binary files /dev/null and b/actions/setup_virtual_desktop/docs/images/screenshot-xfce.png differ diff --git a/actions/setup_virtual_desktop/post-ci.sh b/actions/setup_virtual_desktop/post-ci.sh new file mode 100644 index 0000000..1dee478 --- /dev/null +++ b/actions/setup_virtual_desktop/post-ci.sh @@ -0,0 +1,154 @@ +#!/bin/bash + +set -euo pipefail + +echo "Verifying virtual desktop setup..." + +# Check if DISPLAY is set +if [[ -z "${DISPLAY:-}" ]]; then + echo "✗ DISPLAY environment variable not set" + exit 1 +fi + +echo "✓ DISPLAY is set to: $DISPLAY" + +# Check D-Bus session address is set +echo "" +echo "Checking D-Bus session..." +if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then + echo "✗ DBUS_SESSION_BUS_ADDRESS is not set" + exit 1 +fi + +echo "✓ DBUS_SESSION_BUS_ADDRESS is set to: $DBUS_SESSION_BUS_ADDRESS" + +# Verify D-Bus is working +if dbus-send --session --dest=org.freedesktop.DBus --print-reply /org/freedesktop/DBus \ + org.freedesktop.DBus.ListNames >/dev/null 2>&1; then + echo "✓ D-Bus session is responding" +else + echo "✗ D-Bus session is not responding" + exit 1 +fi + +# Check if X server is responding +if xdpyinfo > /dev/null 2>&1; then + echo "✓ X server is responding" +else + echo "✗ X server is not responding" + exit 1 +fi + +# Get display information +echo "" +echo "Display information:" +xdpyinfo | grep -E "dimensions|resolution" || true + +# Check if notify-send is available +if command -v notify-send &> /dev/null; then + echo "✓ notify-send is available" + + # Try sending a test notification + if notify-send "Test Notification" "Virtual desktop is working!" 2>/dev/null; then + echo "✓ Notification sent successfully" + else + echo "✗ Notification command ran but may not be visible" + exit 1 + fi +else + echo "✗ notify-send not found" + exit 1 +fi + +# Check for running window manager +echo "" +echo "Checking for window manager..." +WM_FOUND=false + +for wm in fluxbox lxsession mate-session openbox xfce4-session; do + if pgrep -x "$wm" > /dev/null 2>&1; then + echo "✓ Found window manager: $wm" + WM_FOUND=true + break + fi +done + +if [[ "$WM_FOUND" = false ]]; then + echo "✗ No window manager found running (may have daemonized)" + exit 1 +fi + +# Check for panel/tray +echo "" +echo "Checking for panel/tray..." +PANEL_FOUND=false + +for panel in lxpanel mate-panel stalonetray tint2 xfce4-panel; do + if pgrep -x "$panel" > /dev/null 2>&1; then + echo "✓ Found panel: $panel" + PANEL_FOUND=true + break + fi +done + +if [[ "$PANEL_FOUND" = false ]]; then + echo "✗ No panel found running (may have daemonized or integrated)" + exit 1 +fi + +# Check for AppIndicator service (MATE/XFCE only, except MATE with legacy) +echo "" +echo "Checking for AppIndicator service..." + +# Check what environment and version we're using +IS_MATE=false +IS_XFCE=false +if pgrep -x "mate-session" > /dev/null 2>&1; then + IS_MATE=true +elif pgrep -x "xfce4-session" > /dev/null 2>&1; then + IS_XFCE=true +fi + +if pgrep -f "ayatana-indicator-application-service" > /dev/null 2>&1; then + echo "✓ Ayatana AppIndicator service is running" + + # Verify it's registered on D-Bus (ayatana uses org.ayatana.indicator.application) + if dbus-send --session --dest=org.freedesktop.DBus --print-reply /org/freedesktop/DBus \ + org.freedesktop.DBus.ListNames 2>/dev/null | grep -q "org.ayatana.indicator.application"; then + echo "✓ Ayatana AppIndicator service is registered on D-Bus" + else + echo "✗ Ayatana AppIndicator service running but not registered on D-Bus" + exit 1 + fi +elif pgrep -f "indicator-application-service" > /dev/null 2>&1; then + echo "✓ Legacy AppIndicator service is running" + + # Verify it's registered on D-Bus (legacy uses com.canonical.indicator.application) + if dbus-send --session --dest=org.freedesktop.DBus --print-reply /org/freedesktop/DBus \ + org.freedesktop.DBus.ListNames 2>/dev/null | grep -q "com.canonical.indicator.application"; then + echo "✓ Legacy AppIndicator service is registered on D-Bus" + else + echo "✗ Legacy AppIndicator service running but not registered on D-Bus" + exit 1 + fi +elif [[ "$IS_MATE" == "true" ]] || [[ "$IS_XFCE" == "true" ]]; then + # For MATE with legacy indicators, the notification area is used instead of indicator service + if [[ "$IS_MATE" == "true" ]]; then + # Check if mate-indicator-applet is running (only for Ayatana) + if pgrep -f "mate-indicator-applet" > /dev/null 2>&1; then + echo "⚠ MATE with Ayatana indicators detected but service not running" + echo " This is expected if using the 'legacy' appindicator-version input" + else + echo "ℹ MATE session detected - using notification area for indicators" + echo " (Legacy indicators use the system tray, not the indicator service)" + fi + else + echo "✗ XFCE session detected but AppIndicator service not running" + exit 1 + fi +else + echo "ℹ AppIndicator service not expected (not MATE/XFCE environment)" +fi + +echo "" +echo "Virtual desktop validation successful!" diff --git a/actions/setup_virtual_desktop/requirements.txt b/actions/setup_virtual_desktop/requirements.txt new file mode 100644 index 0000000..92bc560 --- /dev/null +++ b/actions/setup_virtual_desktop/requirements.txt @@ -0,0 +1,4 @@ +# Python dependencies for test_appindicator.py +# PyGObject provides Python bindings for GObject-based libraries like GTK and AppIndicator +# Only install on Linux where AppIndicator/Ayatana is available +pygobject==3.54.5; sys_platform == 'linux' diff --git a/actions/setup_virtual_desktop/setup_desktop.sh b/actions/setup_virtual_desktop/setup_desktop.sh new file mode 100644 index 0000000..11be4d6 --- /dev/null +++ b/actions/setup_virtual_desktop/setup_desktop.sh @@ -0,0 +1,431 @@ +#!/bin/bash + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +RESET='\033[0m' + +# Constants +AYATANA="ayatana" +LEGACY="legacy" + +# Default values +APPINDICATOR_VERSION="$AYATANA" +DISPLAY_NUM=99 +DISPLAY_SIZE="1280x720" +ENVIRONMENT="" + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --appindicator-version=*) + APPINDICATOR_VERSION="${1#*=}" + shift + ;; + --display-size=*) + DISPLAY_SIZE="${1#*=}" + shift + ;; + --environment=*) + ENVIRONMENT="${1#*=}" + shift + ;; + *) + echo -e "${RED}Error: Unknown parameter: $1${RESET}" >&2 + exit 1 + ;; + esac +done + +# Validate environment +VALID_ENVIRONMENTS=("fluxbox" "lxde" "mate" "openbox" "xfce") +VALID=false +for env in "${VALID_ENVIRONMENTS[@]}"; do + if [[ "$ENVIRONMENT" == "$env" ]]; then + VALID=true + break + fi +done + +if [[ "$VALID" == "false" ]]; then + echo -e "${RED}Error: Invalid environment '${ENVIRONMENT}'${RESET}" >&2 + echo -e "${YELLOW}Valid options: ${VALID_ENVIRONMENTS[*]}${RESET}" >&2 + exit 1 +fi + +# Validate appindicator-version +VALID_APPINDICATOR_VERSIONS=("${AYATANA}" "${LEGACY}") +VALID=false +for version in "${VALID_APPINDICATOR_VERSIONS[@]}"; do + if [[ "$APPINDICATOR_VERSION" == "$version" ]]; then + VALID=true + break + fi +done + +if [[ "$VALID" == "false" ]]; then + echo -e "${RED}Error: Invalid appindicator-version '${APPINDICATOR_VERSION}'${RESET}" >&2 + echo -e "${YELLOW}Valid options: ${VALID_APPINDICATOR_VERSIONS[*]}${RESET}" >&2 + exit 1 +fi + +# Parse display size +if [[ ! "$DISPLAY_SIZE" =~ ^[0-9]+x[0-9]+$ ]]; then + echo -e "${RED}Error: Invalid display size format '${DISPLAY_SIZE}'${RESET}" >&2 + echo -e "${YELLOW}Expected format: WIDTHxHEIGHT (e.g., 1280x720)${RESET}" >&2 + exit 1 +fi + +WIDTH=$(echo "$DISPLAY_SIZE" | cut -d'x' -f1) +HEIGHT=$(echo "$DISPLAY_SIZE" | cut -d'x' -f2) + +echo -e "${CYAN}Setting up virtual desktop environment...${RESET}" +echo -e "${CYAN} Environment: ${ENVIRONMENT}${RESET}" +echo -e "${CYAN} Display size: ${DISPLAY_SIZE}${RESET}" +echo -e "${CYAN} Display: :${DISPLAY_NUM}${RESET}" + +# Base dependencies for all environments +BASE_DEPS=( + adwaita-icon-theme + at-spi2-core + dbus-x11 + gnome-icon-theme + libnotify-bin + libpulse0 + pulseaudio-utils + x11-utils + x11-xserver-utils + xfonts-75dpi + xfonts-100dpi + xfonts-base + xvfb +) + +# Function to configure AppIndicator packages based on version +configure_appindicator() { + local version="$1" + + if [[ "$version" == "${AYATANA}" ]]; then + ENV_DEPS+=( + ayatana-indicator-application + gir1.2-ayatanaappindicator3-0.1 + ) + INDICATOR_SERVICE="/usr/libexec/ayatana-indicator-application/ayatana-indicator-application-service" + elif [[ "$version" == "${LEGACY}" ]]; then + ENV_DEPS+=( + gir1.2-appindicator3-0.1 + indicator-application + ) + INDICATOR_SERVICE="/usr/lib/x86_64-linux-gnu/indicator-application/indicator-application-service" + fi + + return 0 +} + +# Desktop environment specific packages +echo -e "${BLUE}Configuring ${ENVIRONMENT} desktop environment...${RESET}" + +case "$ENVIRONMENT" in + fluxbox) + # Fluxbox - lightweight window manager with built-in toolbar + ENV_DEPS=( + dunst + fluxbox + stalonetray + ) + WM_COMMAND="fluxbox" + PANEL_COMMAND="stalonetray" + NOTIF_COMMAND="dunst" + INDICATOR_SERVICE="" + ;; + + lxde) + # LXDE - very lightweight, basic tray support + ENV_DEPS=( + lxde + lxsession + notification-daemon + policykit-1 + policykit-1-gnome + ) + WM_COMMAND="startlxde" + PANEL_COMMAND="" # lxsession starts lxpanel automatically + NOTIF_COMMAND="/usr/lib/notification-daemon/notification-daemon" + INDICATOR_SERVICE="" + ;; + + mate) + # MATE - full-featured desktop, fork of GNOME 2, excellent tray support + ENV_DEPS=( + mate-applet-brisk-menu + mate-applets + mate-desktop-environment-core + mate-icon-theme + mate-indicator-applet + mate-notification-daemon + mate-panel + mate-themes + ) + + # Add AppIndicator packages based on version + configure_appindicator "$APPINDICATOR_VERSION" + + WM_COMMAND="mate-session" + PANEL_COMMAND="" # mate-session starts panel automatically + NOTIF_COMMAND="/usr/libexec/mate-notification-daemon/mate-notification-daemon" + ;; + + openbox) + # Openbox - minimal window manager with tint2 panel for tray + ENV_DEPS=( + dunst + openbox + tint2 + ) + WM_COMMAND="openbox" + PANEL_COMMAND="tint2" + NOTIF_COMMAND="dunst" + INDICATOR_SERVICE="" + ;; + + xfce) + # XFCE - lightweight, full-featured, excellent tray support + ENV_DEPS=( + xfce4 + xfce4-indicator-plugin + xfce4-notifyd + ) + + # Add AppIndicator packages based on version + configure_appindicator "$APPINDICATOR_VERSION" + + WM_COMMAND="xfce4-session" + PANEL_COMMAND="" # xfce4-session starts panel automatically + NOTIF_COMMAND="/usr/lib/x86_64-linux-gnu/xfce4/notifyd/xfce4-notifyd" + ;; + + *) + echo -e "${RED}Error: Unsupported environment '${ENVIRONMENT}'${RESET}" >&2 + exit 1 + ;; +esac + +# Combine base and environment-specific dependencies +ALL_DEPS=("${BASE_DEPS[@]}" "${ENV_DEPS[@]}") + +# Install all dependencies in one command +echo -e "${BLUE}Installing all dependencies (${#ALL_DEPS[@]} packages)...${RESET}" +echo "::group::Installing dependencies" +sudo apt-get update -qq +sudo apt-get install -y -qq "${ALL_DEPS[@]}" +echo "::endgroup::" + +# Clean up apt cache +echo -e "${BLUE}Cleaning up apt cache...${RESET}" +sudo apt-get clean +sudo rm -rf /var/lib/apt/lists/* + +# Start Xvfb +echo -e "${BLUE}Starting Xvfb on :${DISPLAY_NUM}...${RESET}" +Xvfb ":${DISPLAY_NUM}" -screen 0 "${WIDTH}x${HEIGHT}x24" -ac +extension GLX +render -noreset & +XVFB_PID=$! + +# Wait for Xvfb to start +sleep 2 + +# Verify Xvfb is running +if ! ps -p $XVFB_PID > /dev/null; then + echo -e "${RED}Error: Failed to start Xvfb${RESET}" >&2 + exit 1 +fi + +echo -e "${GREEN}✓ Xvfb started (PID: ${XVFB_PID})${RESET}" + +# Set DISPLAY environment variable +export DISPLAY=":${DISPLAY_NUM}" +echo "DISPLAY=:${DISPLAY_NUM}" >> "$GITHUB_ENV" + +# Start D-Bus session (required for notifications and accessibility) +echo -e "${BLUE}Starting D-Bus session...${RESET}" + +# Always start a new D-Bus session for this workflow +# Don't try to reuse existing dbus-daemon as it may not have the correct session address +eval "$(dbus-launch --sh-syntax)" + +if [[ -n "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then + echo -e "${GREEN}✓ D-Bus session started${RESET}" + echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" >> "$GITHUB_ENV" +else + echo -e "${RED}Error: Failed to start D-Bus session${RESET}" >&2 + exit 1 +fi + +# Start AT-SPI bus to fix accessibility warnings +echo -e "${BLUE}Starting accessibility bus...${RESET}" +if [[ -x "/usr/libexec/at-spi-bus-launcher" ]]; then + /usr/libexec/at-spi-bus-launcher --launch-immediately & + sleep 1 +elif [[ -x "/usr/lib/at-spi2-core/at-spi-bus-launcher" ]]; then + /usr/lib/at-spi2-core/at-spi-bus-launcher --launch-immediately & + sleep 1 +else + echo -e "${YELLOW}⚠ AT-SPI bus launcher not found, accessibility may be limited${RESET}" +fi + +# Start PulseAudio with dummy sink to fix audio warnings +echo -e "${BLUE}Starting PulseAudio with dummy sink...${RESET}" +pulseaudio --start --exit-idle-time=-1 > /dev/null 2>&1 || true +pactl load-module module-null-sink sink_name=dummy > /dev/null 2>&1 || true + +# Start gnome-keyring for credential management +if [[ "$ENVIRONMENT" == "lxde" ]]; then + echo -e "${BLUE}Starting gnome-keyring daemon...${RESET}" + eval "$(gnome-keyring-daemon --start --components=pkcs11,secrets,ssh 2>/dev/null)" + export SSH_AUTH_SOCK + echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" >> "$GITHUB_ENV" +fi + +# Start window manager in background +echo -e "${BLUE}Starting ${ENVIRONMENT} window manager...${RESET}" + +$WM_COMMAND & +WM_PID=$! + +# Give session managers more time to start all components +if [[ "$ENVIRONMENT" == "xfce" ]] || [[ "$ENVIRONMENT" == "mate" ]] || [[ "$ENVIRONMENT" == "lxde" ]]; then + sleep 5 # Session managers need more time to start panels, notification daemons, etc. +else + sleep 2 +fi + +# Verify window manager is running +if ps -p $WM_PID > /dev/null 2>&1; then + echo -e "${GREEN}✓ Window manager started (PID: ${WM_PID})${RESET}" +else + echo -e "${YELLOW}⚠ Window manager may have daemonized or exited${RESET}" +fi + +# Configure XFCE panel for indicator support +if [[ "$ENVIRONMENT" == "xfce" ]] && [[ -n "${INDICATOR_SERVICE:-}" ]]; then + echo -e "${BLUE}Configuring XFCE panel for AppIndicator support...${RESET}" + + # Wait for panel to be fully initialized + sleep 2 + + # Add the indicator plugin to XFCE panel + # The plugin ID is usually "indicator" and we add it to panel-1 (default panel) + xfconf-query -c xfce4-panel -p /panels/panel-1/plugin-ids -t int -s 1 -t int -s 2 -t int -s 3 -t int -s 4 -t int -s 5 -t int -s 99 --create 2>/dev/null || true + xfconf-query -c xfce4-panel -p /plugins/plugin-99 -t string -s indicator --create 2>/dev/null || true + + # Restart xfce4-panel to apply changes + xfce4-panel -r 2>/dev/null || true + sleep 2 + + echo -e "${GREEN}✓ XFCE panel configured for indicators${RESET}" +fi + +# Start panel/tray (if different from WM) +if [[ "$PANEL_COMMAND" != "$WM_COMMAND" ]] && [[ -n "$PANEL_COMMAND" ]]; then + echo -e "${BLUE}Starting panel/tray...${RESET}" + $PANEL_COMMAND & + PANEL_PID=$! + sleep 1 + + if ps -p $PANEL_PID > /dev/null 2>&1; then + echo -e "${GREEN}✓ Panel started (PID: ${PANEL_PID})${RESET}" + else + echo -e "${YELLOW}⚠ Panel may have daemonized or exited${RESET}" + fi +fi + +# Start notification daemon +if [[ -n "$NOTIF_COMMAND" ]]; then + echo -e "${BLUE}Starting notification daemon...${RESET}" + + # Check if the command exists first + if command -v "$NOTIF_COMMAND" &> /dev/null || [[ -x "$NOTIF_COMMAND" ]]; then + $NOTIF_COMMAND & + NOTIF_PID=$! + sleep 2 + + if ps -p $NOTIF_PID > /dev/null 2>&1; then + echo -e "${GREEN}✓ Notification daemon started (PID: ${NOTIF_PID})${RESET}" + else + echo -e "${YELLOW}⚠ Notification daemon may have daemonized or exited${RESET}" + fi + else + echo -e "${YELLOW}⚠ Notification daemon not found: ${NOTIF_COMMAND}${RESET}" + fi +fi + +# Start indicator application service (for AppIndicator support) +# In CI environments, D-Bus activation doesn't always work reliably, so we start the service manually +# after giving the panel time to initialize its indicator applet +# Note: For MATE with legacy indicators, we skip this as they use the notification area instead +if [[ -n "${INDICATOR_SERVICE:-}" ]]; then + # Skip for MATE + legacy combination (uses notification area instead) + if [[ "$ENVIRONMENT" == "mate" ]] && [[ "$APPINDICATOR_VERSION" == "${LEGACY}" ]]; then + echo -e "${BLUE}Legacy indicators for MATE will use the notification area (system tray)${RESET}" + echo -e "${CYAN} No indicator service needed${RESET}" + else + echo -e "${BLUE}Starting indicator application service...${RESET}" + + if [[ -x "$INDICATOR_SERVICE" ]]; then + # Wait for the panel's indicator applet to be ready + sleep 3 + + # Start the service + $INDICATOR_SERVICE & + INDICATOR_PID=$! + sleep 3 + + if ps -p $INDICATOR_PID > /dev/null 2>&1; then + echo -e "${GREEN}✓ Indicator service started (PID: ${INDICATOR_PID})${RESET}" + else + # Service may have daemonized, check if it's running by name + if pgrep -f "$(basename "$INDICATOR_SERVICE")" > /dev/null 2>&1; then + echo -e "${GREEN}✓ Indicator service is running (daemonized)${RESET}" + else + echo -e "${YELLOW}⚠ Indicator service may have exited${RESET}" + exit 1 + fi + fi + else + echo -e "${RED}✗ Indicator service not found or not executable: ${INDICATOR_SERVICE}${RESET}" >&2 + echo -e "${YELLOW} Checking if file exists...${RESET}" + ls -la "$(dirname "$INDICATOR_SERVICE")" 2>&1 || true + exit 1 + fi + fi +fi + +# Verify X server is responding +echo -e "${BLUE}Verifying X server...${RESET}" +if xdpyinfo -display ":${DISPLAY_NUM}" > /dev/null 2>&1; then + echo -e "${GREEN}✓ X server is responding${RESET}" +else + echo -e "${RED}Error: X server is not responding${RESET}" >&2 + exit 1 +fi + + +# Output information +echo "" +echo -e "${GREEN}Virtual desktop setup complete!${RESET}" +echo -e "${CYAN}Configuration:${RESET}" +echo -e "${CYAN} Display: :${DISPLAY_NUM}${RESET}" +echo -e "${CYAN} Resolution: ${WIDTH}x${HEIGHT}x24${RESET}" +echo -e "${CYAN} Environment: ${ENVIRONMENT}${RESET}" +echo -e "${CYAN} Xvfb PID: ${XVFB_PID}${RESET}" + +# Output to GitHub Actions +echo "display=:${DISPLAY_NUM}" >> "${GITHUB_OUTPUT}" +echo "xvfb-pid=${XVFB_PID}" >> "${GITHUB_OUTPUT}" + +echo "" +echo -e "${CYAN}You can now run GUI applications with DISPLAY=:${DISPLAY_NUM}${RESET}" diff --git a/actions/setup_virtual_desktop/test_appindicator.py b/actions/setup_virtual_desktop/test_appindicator.py new file mode 100644 index 0000000..bba135f --- /dev/null +++ b/actions/setup_virtual_desktop/test_appindicator.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Test script for AppIndicator/Ayatana support in virtual desktop environments.""" +import signal +import sys + + +def main(): + """Create a test AppIndicator with a built-in icon.""" + # AppIndicator is only available on Linux + if sys.platform != 'linux': + print(f"Skipping AppIndicator test on {sys.platform} (only supported on Linux)") + return + + # Get appindicator version from command line argument (default: ayatana) + appindicator_version = sys.argv[1] if len(sys.argv) > 1 else 'ayatana' + + # Import gi modules only on Linux to avoid import errors on other platforms + import gi + + if appindicator_version == 'ayatana': + gi.require_version('AyatanaAppIndicator3', '0.1') + from gi.repository import AyatanaAppIndicator3 as AppIndicator + print("Using Ayatana AppIndicator") + elif appindicator_version == 'legacy': + gi.require_version('AppIndicator3', '0.1') + from gi.repository import AppIndicator3 as AppIndicator + print("Using Legacy AppIndicator") + else: + print(f"Error: Invalid appindicator version '{appindicator_version}'") + sys.exit(1) + + from gi.repository import GLib + from gi.repository import Gtk + + indicator = AppIndicator.Indicator.new( + "test-indicator", + "mail-message-new", + AppIndicator.IndicatorCategory.APPLICATION_STATUS + ) + indicator.set_status(AppIndicator.IndicatorStatus.ACTIVE) + indicator.set_title("Test AppIndicator") + + # Create a simple menu + menu = Gtk.Menu() + item = Gtk.MenuItem(label="Test Item") + menu.append(item) + item.show() + indicator.set_menu(menu) + + # Exit after 3 seconds + GLib.timeout_add_seconds(3, Gtk.main_quit) + + Gtk.main() + + +if __name__ == "__main__": + signal.signal(signal.SIGINT, signal.SIG_DFL) + main()