#!/usr/bin/env bash # # Use rofi/zenity to change system runstate thanks to systemd. # # Note: this currently relies on associative array support in the shell. # # Inspired from i3pystatus wiki: # https://github.com/enkore/i3pystatus/wiki/Shutdown-Menu # # Copyright 2015 Benjamin Chrétien # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . ####################################################################### # BEGIN CONFIG # ####################################################################### # Use a custom lock script #LOCKSCRIPT="i3lock-extra -m pixelize" # Colors: FG (foreground), BG (background), HL (highlighted) FG_COLOR="${FG_COLOR:-#bbbbbb}" BG_COLOR="${BG_COLOR:-#111111}" HLFG_COLOR="${HLFG_COLOR:-#111111}" HLBG_COLOR="${HLBG_COLOR:-#bbbbbb}" BORDER_COLOR="${BORDER_COLOR:-#222222}" # Options not related to colors ROFI_TEXT="${ROFI_TEXT:-Menu:}" ROFI_OPTIONS=(${ROFI_OPTIONS:--width 11 -location 3 -hide-scrollbar -bw 2}) # Zenity options ZENITY_TITLE="${ZENITY_TITLE:-Menu}" ZENITY_TEXT="${ZENITY_TEXT:-Action:}" ZENITY_OPTIONS=(${ZENITY_OPTIONS:---column= --hide-header}) ####################################################################### # END CONFIG # ####################################################################### # Whether to ask for user's confirmation enable_confirmation=${ENABLE_CONFIRMATIONS:-false} # Preferred launcher if both are available preferred_launcher="${LAUNCHER:-rofi}" usage="$(basename "$0") [-h] [-c] [-p name] -- display a menu for shutdown, reboot, lock etc. where: -h show this help text -c ask for user confirmation -p preferred launcher (rofi or zenity) This script depends on: - systemd, - i3, - rofi or zenity." # Check whether the user-defined launcher is valid launcher_list=(rofi zenity) function check_launcher() { if [[ ! "${launcher_list[@]}" =~ (^|[[:space:]])"$1"($|[[:space:]]) ]]; then echo "Supported launchers: ${launcher_list[*]}" exit 1 else # Get array with unique elements and preferred launcher first # Note: uniq expects a sorted list, so we cannot use it i=1 launcher_list=($(for l in "$1" "${launcher_list[@]}"; do printf "%i %s\n" "$i" "$l"; let i+=1; done \ | sort -uk2 | sort -nk1 | cut -d' ' -f2- | tr '\n' ' ')) fi } # Parse CLI arguments while getopts "hcp:" option; do case "${option}" in h) echo "${usage}" exit 0 ;; c) enable_confirmation=true ;; p) preferred_launcher="${OPTARG}" check_launcher "${preferred_launcher}" ;; *) exit 1 ;; esac done check_launcher "${preferred_launcher}" # Check whether a command exists function command_exists() { command -v "$1" &> /dev/null 2>&1 } # systemctl required if ! command_exists systemctl ; then exit 1 fi # menu defined as an associative array typeset -A menu # Menu with keys/commands menu=( [Shutdown]="systemctl poweroff" [Reboot]="systemctl reboot" [Hibernate]="systemctl hibernate" [Suspend]="systemctl suspend" [Halt]="systemctl halt" [Lock]="${LOCKSCRIPT:-i3lock --color=${BG_COLOR#"#"}}" [Logout]="i3-msg exit" [Cancel]="" ) menu_nrows=${#menu[@]} # Menu entries that may trigger a confirmation message menu_confirm="Shutdown Reboot Hibernate Suspend Halt Logout" launcher_exe="" launcher_options="" rofi_colors="" function prepare_launcher() { if [[ "$1" == "rofi" ]]; then rofi_colors=(-bc "${BORDER_COLOR}" -bg "${BG_COLOR}" -fg "${FG_COLOR}" \ -hlfg "${HLFG_COLOR}" -hlbg "${HLBG_COLOR}") launcher_exe="rofi" launcher_options=(-dmenu -i -lines "${menu_nrows}" -p "${ROFI_TEXT}" \ "${rofi_colors[@]}" "${ROFI_OPTIONS[@]}") elif [[ "$1" == "zenity" ]]; then launcher_exe="zenity" launcher_options=(--list --title="${ZENITY_TITLE}" --text="${ZENITY_TEXT}" \ "${ZENITY_OPTIONS[@]}") fi } for l in "${launcher_list[@]}"; do if command_exists "${l}" ; then prepare_launcher "${l}" break fi done # No launcher available if [[ -z "${launcher_exe}" ]]; then exit 1 fi launcher=(${launcher_exe} "${launcher_options[@]}") selection="$(printf '%s\n' "${!menu[@]}" | sort | "${launcher[@]}")" function ask_confirmation() { if [ "${launcher_exe}" == "rofi" ]; then confirmed=$(echo -e "Yes\nNo" | rofi -dmenu -i -lines 2 -p "${selection}?" \ "${rofi_colors[@]}" "${ROFI_OPTIONS[@]}") [ "${confirmed}" == "Yes" ] && confirmed=0 elif [ "${launcher_exe}" == "zenity" ]; then zenity --question --text "Are you sure you want to ${selection,,}?" confirmed=$? fi if [ "${confirmed}" == 0 ]; then i3-msg -q "exec ${menu[${selection}]}" fi } if [[ $? -eq 0 && ! -z ${selection} ]]; then if [[ "${enable_confirmation}" = true && \ ${menu_confirm} =~ (^|[[:space:]])"${selection}"($|[[:space:]]) ]]; then ask_confirmation else i3-msg -q "exec ${menu[${selection}]}" fi fi