#!/usr/bin/env bash # This script defines just a mode for rofi instead of being a self-contained # executable that launches rofi by itself. This makes it more flexible than # running rofi inside this script as now the user can call rofi as one pleases. # For instance: # # rofi -show powermenu -modi powermenu:./rofi-power-menu # # See README.md for more information. set -e set -u # All supported choices all=(shutdown reboot suspend hibernate logout lockscreen) # By default, show all (i.e., just copy the array) show=("${all[@]}") declare -A texts texts[lockscreen]="lock screen" texts[switchuser]="switch user" texts[logout]="log out" texts[suspend]="suspend" texts[hibernate]="hibernate" texts[reboot]="reboot" texts[shutdown]="shut down" declare -A icons icons[lockscreen]="\Uf033e" icons[switchuser]="\Uf0019" icons[logout]="\Uf0343" icons[suspend]="\Uf04b2" icons[hibernate]="\Uf02ca" icons[reboot]="\Uf0709" icons[shutdown]="\Uf0425" icons[cancel]="\Uf0156" declare -A actions actions[lockscreen]="loginctl lock-session ${XDG_SESSION_ID-}" #actions[switchuser]="???" actions[logout]="loginctl terminate-session ${XDG_SESSION_ID-}" actions[suspend]="systemctl suspend" actions[hibernate]="systemctl hibernate" actions[reboot]="systemctl reboot" actions[shutdown]="systemctl poweroff" # By default, ask for confirmation for actions that are irreversible confirmations=(reboot shutdown logout) # By default, no dry run dryrun=false showsymbols=true showtext=true function check_valid { option="$1" shift 1 for entry in "${@}" do if [ -z "${actions[$entry]+x}" ] then echo "Invalid choice in $1: $entry" >&2 exit 1 fi done } # Parse command-line options parsed=$(getopt --options=h --longoptions=help,dry-run,confirm:,choices:,choose:,symbols,no-symbols,text,no-text,symbols-font: --name "$0" -- "$@") if [ $? -ne 0 ]; then echo 'Terminating...' >&2 exit 1 fi eval set -- "$parsed" unset parsed while true; do case "$1" in "-h"|"--help") echo "rofi-power-menu - a power menu mode for Rofi" echo echo "Usage: rofi-power-menu [--choices CHOICES] [--confirm CHOICES]" echo " [--choose CHOICE] [--dry-run] [--symbols|--no-symbols]" echo echo "Use with Rofi in script mode. For instance, to ask for shutdown or reboot:" echo echo " rofi -show menu -modi \"menu:rofi-power-menu --choices=shutdown/reboot\"" echo echo "Available options:" echo " --dry-run Don't perform the selected action but print it to stderr." echo " --choices CHOICES Show only the selected choices in the given order. Use /" echo " as the separator. Available choices are lockscreen," echo " logout,suspend, hibernate, reboot and shutdown. By" echo " default, all available choices are shown." echo " --confirm CHOICES Require confirmation for the gives choices only. Use / as" echo " the separator. Available choices are lockscreen, logout," echo " suspend, hibernate, reboot and shutdown. By default, only" echo " irreversible actions logout, reboot and shutdown require" echo " confirmation." echo " --choose CHOICE Preselect the given choice and only ask for a" echo " confirmation (if confirmation is set to be requested). It" echo " is strongly recommended to combine this option with" echo " --confirm=CHOICE if the choice wouldn't require" echo " confirmation by default. Available choices are" echo " lockscreen, logout, suspend, hibernate, reboot and" echo " shutdown." echo " --[no-]symbols Show Unicode symbols or not. Requires a font with support" echo " for the symbols. Use, for instance, fonts from the" echo " Nerdfonts collection. By default, they are shown" echo " --[no-]text Show text description or not." echo " --symbols-font FONT Use the given font for symbols. By default, the symbols" echo " use the same font as the text. That font is configured" echo " with rofi." echo " -h,--help Show this help text." exit 0 ;; "--dry-run") dryrun=true shift 1 ;; "--confirm") IFS='/' read -ra confirmations <<< "$2" check_valid "$1" "${confirmations[@]}" shift 2 ;; "--choices") IFS='/' read -ra show <<< "$2" check_valid "$1" "${show[@]}" shift 2 ;; "--choose") # Check that the choice is valid check_valid "$1" "$2" selectionID="$2" shift 2 ;; "--symbols") showsymbols=true shift 1 ;; "--no-symbols") showsymbols=false shift 1 ;; "--text") showtext=true shift 1 ;; "--no-text") showtext=false shift 1 ;; "--symbols-font") symbols_font="$2" shift 2 ;; "--") shift break ;; *) echo "Internal error" >&2 exit 1 ;; esac done if [ "$showsymbols" = "false" -a "$showtext" = "false" ] then echo "Invalid options: cannot have --no-symbols and --no-text enabled at the same time." >&2 exit 1 fi # Define the messages after parsing the CLI options so that it is possible to # configure them in the future. function write_message { if [ -z ${symbols_font+x} ]; then icon="$1" else icon="$1" fi text="$2" if [ "$showsymbols" = "true" ] then if [ "$showtext" = "true" ] then echo -n "\u200e$icon \u2068$text\u2069" else echo -n "\u200e$icon" fi else echo -n "$text" fi } function print_selection { echo -e "$1" | $(read -r -d '' entry; echo "echo $entry") } declare -A messages declare -A confirmationMessages for entry in "${all[@]}" do messages[$entry]=$(write_message "${icons[$entry]}" "${texts[$entry]^}") done for entry in "${all[@]}" do confirmationMessages[$entry]=$(write_message "${icons[$entry]}" "Yes, ${texts[$entry]}") done confirmationMessages[cancel]=$(write_message "${icons[cancel]}" "No, cancel") if [ $# -gt 0 ] then # If arguments given, use those as the selection selection="${@}" else # Otherwise, use the CLI passed choice if given if [ -n "${selectionID+x}" ] then selection="${messages[$selectionID]}" fi fi # Don't allow custom entries echo -e "\0no-custom\x1ftrue" # Use markup echo -e "\0markup-rows\x1ftrue" if [ -z "${selection+x}" ] then echo -e "\0prompt\x1fPower menu" for entry in "${show[@]}" do echo -e "${messages[$entry]}\0icon\x1f${icons[$entry]}" done else for entry in "${show[@]}" do if [ "$selection" = "$(print_selection "${messages[$entry]}")" ] then # Check if the selected entry is listed in confirmation requirements for confirmation in "${confirmations[@]}" do if [ "$entry" = "$confirmation" ] then # Ask for confirmation echo -e "\0prompt\x1fAre you sure" echo -e "${confirmationMessages[$entry]}\0icon\x1f${icons[$entry]}" echo -e "${confirmationMessages[cancel]}\0icon\x1f${icons[cancel]}" exit 0 fi done # If not, then no confirmation is required, so mark confirmed selection=$(print_selection "${confirmationMessages[$entry]}") fi if [ "$selection" = "$(print_selection "${confirmationMessages[$entry]}")" ] then if [ $dryrun = true ] then # Tell what would have been done echo "Selected: $entry" >&2 else # Perform the action ${actions[$entry]} fi exit 0 fi if [ "$selection" = "$(print_selection "${confirmationMessages[cancel]}")" ] then # Do nothing exit 0 fi done # The selection didn't match anything, so raise an error echo "Invalid selection: $selection" >&2 exit 1 fi