Improve your workflow using Zenity-based GUI

Let me update here, sorry about the delay to answer you. i will share here a script for an easy app workflow. Just bind a key to run the script and make your life easy.

Problem: In Qubes, launching apps in specific qubes requires either:

  • Memorizing commands like qvm-run -a appvm app.
  • Manually opening a terminal in the target qube first.

Solution: This script provides a GUI menu (via Zenity) to:

  • Detect the active qube automatically (no need to type its name).
  • Launch apps with one click (e.g., Browser, Terminal, File Manager)

Secure and easy way to screenshot sharing between Qubes

Problem: Sharing screenshots between qubes is combersome

  • Manual steps: save → qvm-copy → Open in target qube.

Solution: Automated Screenshot feature:

  • Captures a region (xfce4-screenshooter -r).
  • Automatically sends to a user-selected qube (e.g., work or personal).
  • Opens the file manager in the target qube for instant access.

This script can reduce human error, avoid mistakes like:

  • Launching apps in the wrong qube (e.g., opening a browser in personal instead of work vm)
  • Typos in commands

It validates the qube name and uses a predefined list of trusted apps (no accidental risky commands). it makes your workflow faster, you can save time, no need to switch to dom0 terminal to run qvm-run, that way the user has no need to remember app-specific commands (e.g., qubes.StartApp for browsers).

Example: Launching a browser in work-vm takes 2 clicks instead of 5+ keystrokes.

Flexibility:

  • Easily add/remove apps by editing the APPS dictionary in source code (e.g., add ["Signal"]="signal-desktop").
  • Extend with new functions (e.g., _open_vpn to start a VPN in a qube).

Ideal for Non-Technical Users (everybody needs a start, we can make it easy for then so they can have a better experience trying Qubes)

Example Workflow:

  1. You’re in a work qube editing a document.

  2. Need to open a browser in work-web?

     Run script → select "Browser" → done.
    
  3. Need to send a screenshot to personal qube?

     Run script → "Automated Screenshot" → select personal → crop region → file appears in personal’s QubesIncoming.
    

This script simplifies secure, compartmentalized workflows — perfect for Qubes OS’s philosophy.

To make sure everything works

  1. dom0: sudo qubes-dom0-update zenity xorg-xprop xfce4-screenshooter

  2. in VMs: you need the apps and your prefered browser

sudo dnf install xfce4-terminal thunar geany yourbrowser  # Fedora
sudo apt install xfce4-terminal thunar geany yourbrowser # Debian

Menu images:

Install example:

You are in vm personal and saved the script in /Downloads/ named as action-flow.sh

Go to dom0 terminal and type:

mkdir -p ~/scripts
cd scripts/
qvm-run -u root -p personal 'cat /home/Downloads/action-flow.sh' >> action-flow.sh
chmod +x action-flow.sh

bind a key to run the script:

> 1. Start Menu -> System Settings -> Keyboard
> 2. Select "Application Shortcuts" menu
> 3. Click "+Add"
> 4. Browse to Scripts/action-flow.sh
> 5. Bind your shortcut key to run the script
Done!

SOURCE CODE: action-flow.sh

#!/bin/bash

declare -r LOG_FILE="/var/log/qubes/app_launcher.log"
declare -A APPS=(
    ["XTerm"]="xterm"
    ["Terminal"]="xfce4-terminal"
    ["Geany"]="geany"
    ["Thunar"]="thunar"
    ["Browser"]="qubes.StartApp+xfce4-web-browser"
    ["Automated Screenshot"]="_take_screenshot"
)


_get_qube_name() {
    local active_win=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)
    xprop -id "$active_win" _QUBES_VMNAME 2>/dev/null | awk -F'"' '{print $2}'
}

_validate_qube() {
    [[ "$1" =~ ^[a-zA-Z0-9_-]+$ ]] && qvm-check "$1" &>/dev/null
}

_launch_app() {
    local qube="$1" app="$2"

    if [[ "$app" == "Browser" ]]; then
        qvm-run -q -a --service "$qube" "${APPS[$app]}" >/dev/null 2>&1 &
    elif [[ "${APPS[$app]}" == _* ]]; then
        "${APPS[$app]}"
    else
        qvm-run -q -a "$qube" "${APPS[$app]}" >/dev/null 2>&1 &
    fi
}

_take_screenshot() {
    local RUNNING_VMS=$(qvm-ls --running --raw-list | grep -v "dom0" | grep -v "sys" | grep -v "vpn")
    
    if [[ -z "$RUNNING_VMS" ]]; then
        zenity --error --text="No VMs are running!" --width=300
        return 1
    fi

    local VM_LIST=$(echo "$RUNNING_VMS" | tr ' ' '\n')

    local TARGET_VM=$(zenity --list \
        --title="Automated screenshot" \
        --text="Which VM would you like to send the file to:" \
        --column="Active VMs" $VM_LIST \
        --width=400 --height=300 2>/dev/null
    ) || return 1

    if [[ -z "$TARGET_VM" ]]; then
        zenity --error --text="No VM selected!" --width=300
        return 1
    fi

    xfce4-screenshooter -r -o "qvm-copy-to-vm $TARGET_VM" || {
        zenity --error --text="Failed to capture/send to $TARGET_VM!" --width=300
        return 1
    }

    zenity --notification --text="Screenshot sent to $TARGET_VM!" 2>/dev/null
    qvm-run -q -a "$TARGET_VM" 'thunar ~/QubesIncoming/dom0/' &
}

main() {
    echo "$(date) - Starting app launcher" >> "$LOG_FILE"

    qube_name=$(_get_qube_name) || {
        zenity --error --text="Cannot identify Qube!" --width=300
        exit 1
    }

    _validate_qube "$qube_name" || {
        zenity --error --text="Invalid Qube: $qube_name" --width=300
        exit 1
    }

    IFS=$'\n' sorted_apps=($(sort <<<"${!APPS[*]}"))
    selected_app=$(zenity --list \
        --title="Apps - $qube_name" \
        --text="Select an app:" \
        --column="Applications" \
        "${sorted_apps[@]}" \
        --width=350 --height=300 2>/dev/null
    ) || exit 0

    _launch_app "$qube_name" "$selected_app"
    zenity --notification --text="Launched $selected_app in $qube_name" 2>/dev/null &
}

main "$@"

3 Likes