HOW-TO: Rofi - XFCE-like menu

EDIT: Whole post has been edited to add the new really functional version.

So I recently installed i3wm in my Qubes desktop because it feels better for me.
I’ve coded a bash script to help me organize the rofi menu, because dmenu sucks for me.
In dom0:

  • Install rofi in dom0 with sudo qubes-dom0-update rofi
  • Create the folder ~/.config/rofi
  • Create the file rofi_qubes_os_menu.sh inside with this content:
#!/bin/bash

app_menu () {
	qube_name="$1"
	running_status="$2"
	last_qube_menu="$3"

	# Create an option to boot/shutdown/kill VM
	if [ "$running_status" = "bold" ]; then
		change_status_option='Shutdown\nKill'
	else
		change_status_option='Power on'
	fi


	# Rebuild the name format used by the .desktop files
	qube=$(echo "$qube_name" | sed -r 's/-/_d/g')

	# Get the name of available apps
	apps=$(grep -h "^Name=" ~/.local/share/applications/org.qubes-os.vm._$qube.* | sed 's/^Name=//')

	# Let the user select the app. Also, make a settings option available
	chosen=$(echo -e "Back\n$apps\n$change_status_option\nSettings" | rofi -dmenu -p "$1 - Select app")

	if [ "$chosen" = "Back" ]; then
		show_qubes "$last_qube_menu"
	elif [ "$chosen" = "Power on" ]; then
		qvm-start $qube_name
       	elif [ "$chosen" = "Shutdown" ]; then
		qvm-shutdown $qube_name
       	elif [ "$chosen" = "Kill" ]; then
		qvm-kill $qube_name
	elif [ "$chosen" = "Settings" ]; then
		exo-open ~/.local/share/applications/org.qubes-os.qubes-vm-settings._$qube.desktop
	else
		$(grep -h "^Exec=" $(grep -rl "^Name=$chosen" ~/.local/share/applications/org.qubes-os.vm._$qube.* | head -n1) | sed 's/^Exec=//')
	fi
}

show_qubes () {
	# Get a list of all available qubes VMs
	options=$(qvm-ls --fields NAME,STATE,CLASS,LABEL,PROVIDES_NETWORK --raw-data | grep "$1" | \
	while IFS='|' read -r name state class label net; do
		if [ "$state" = "Halted" ]; then
			is_running_weight="normal"
			is_running_icon="\U25C9"
		else
			is_running_weight="bold"
			is_running_icon="\U25CF"
		fi

		echo -e "<span foreground='$label'>$is_running_icon </span><span weight='$is_running_weight'>$name</span>"
	done)

	chosen=$(echo -e "<span>Back</span>\n$options" | rofi -dmenu -markup-rows -p "Select qube")

	# Depending on the chosen one, do things
	if [ "$chosen" = "<span>Back</span>" ]; then
		main_menu
	else
		app_menu $(echo -e "$chosen" | cut -d '>' -f 4 | cut -d '<' -f 1) $(echo -e "$chosen" | cut -d "'" -f 4) "$1"
	fi
}


disp_app_menu () {
	# Rebuild the name format used by the .desktop files
	qube=$(echo "$1" | sed -r 's/-/_d/g')

	# Get the name of available apps
	apps=$(grep -h "^Name=" ~/.local/share/applications/org.qubes-os.dispvm._$qube.* | sed 's/^Name=//')

	# Let the user select the app. Also, make a settings option available
	chosen=$(echo -e "Back\n$apps\nSettings" | rofi -dmenu -p "$1 - Select app")

	if [[ "$chosen" = "Back" ]]; then
		show_disp
	elif [[ "$chosen" = "Settings" ]]; then
		exo-open ~/.local/share/applications/org.qubes-os.qubes-vm-settings._$qube.desktop
	else
		$(grep -h "^Exec=" $(grep -rl "^Name=$chosen" ~/.local/share/applications/org.qubes-os.dispvm._$qube.* | head -n1) | sed 's/^Exec=//')
	fi
}


show_disp () {
	# Get a list of all available AppVMs that do not provide network (candidates for Diposable VMs)
	options=$(qvm-ls --fields NAME,CLASS,LABEL,PROVIDES_NETWORK --raw-data | grep "|AppVM|.*|False$" | \
	while IFS='|' read -r name class label net; do
		is_disposable=$(qvm-prefs "$name" template_for_dispvms 2>/dev/null)
		if [ "$is_disposable" = "True" ]; then
			echo -e "<span foreground='$label'>\U25CF </span><span>$name</span>"
		fi
	done)

	chosen=$(echo -e "<span>Back</span>\n$options" | rofi -dmenu -markup-rows -p "Select qube")

	# Depending on the chosen one, do things
	if [ "$chosen" = "<span>Back</span>" ]; then
		main_menu
	else
		disp_app_menu $(echo -e "$chosen" | cut -d '>' -f 4 | cut -d '<' -f 1)
	fi
}


dom0_menu () {
	# Get the name of available apps
	apps=$(grep -h "^Name=" /usr/share/applications/* | sed 's/^Name=//')

	# Let the user select the app. Also, make a settings option available
	chosen=$(echo -e "Back\n$apps" | rofi -dmenu -p "dom0 - Select app")

	if [ "$chosen" = "Back" ]; then
		main_menu
	else
		$(grep -h "^Exec=" $(grep -rl "^Name=$chosen" /usr/share/applications/ | head -n1) | sed 's/^Exec=//')
	fi
}


main_menu () {
	chosen=$(echo -e "Exit\nApp VMs\nActive Disposable VMs\nSpawn disposable VM\nStandalone VMs\nTemplates\nServices\ndom0" | rofi -dmenu -markup-rows -p "Select option")

	if [ "$chosen" = "Exit" ]; then
		exit

	elif [ "$chosen" = "dom0" ]; then
		dom0_menu

	elif [ "$chosen" = "Services" ]; then
		show_qubes '|True$'

	elif [ "$chosen" = "Templates" ]; then
		show_qubes '|TemplateVM|.*|False$'

       	elif [ "$chosen" = "Standalone VMs" ]; then
               	show_qubes '|StandaloneVM|.*|False$'

       	elif [ "$chosen" = "Active Disposable VMs" ]; then
               	show_qubes '|DispVM|.*|False$'

	elif [ "$chosen" = "Spawn disposable VM" ]; then
		show_disp

       	elif [ "$chosen" = "App VMs" ]; then
               	show_qubes '|AppVM|.*|False$'

	# User presses Esc or clicks away
	#elif [ $? -eq 1 ]; then
	#	exit
	fi
}

main_menu
  • Give it execution permissions: chmod u+x ~/.config/rofi/rofi_qubes_os_menu.sh
  • Last, create a keybind in ~/.config/i3/config to launch rofi (or replace the one used by dmenu). Example: bindsym $mod+d exec --no-startup-id ~/.config/rofi/rofi_qubes_os_menu.sh

Now everything works perfectly. It’s only left to do this things:

  • If user presses “Esc”: go back to the previous section
  • If use clicks away from rofi: Exit rofi

This rofi menu tries to be somewhat similar to the one we have in XFCE. You can view AppVMs, disposable VMs, etc… Spawn apps inside them, start those machines when they are not running, shutdown/kill them when they are running, open their settings, etc…
Also, running VMs appear in bold text so you know wether they are running or not.
Also, a circle icon at the left of the VM appears with the color it has been assigned, so everything is easier to do in this menu.

You will also have a menu for dom0 apps.
Some back buttons and an exit button has been added, so you can navigate easily through the menu.

bump
demo video: