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 "$@"

5 Likes

interesting, i was looking for something to improve my creativity workflow within Qubes, even replacing the qubes VM manager by something that looks better and allow me to manage my vm’s more effectively, installing a better window manager in dom0.
Can you share some more screenshots of how your workflow looks like with this new GUI?