├── COPYING ├── README.md ├── dmenu-power-menu ├── rofi-power-menu └── screenshot.png /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2020 Jaakko Luttinen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rofi Power Menu Mode 2 | 3 | 4 | Rofi Power Menu provides a mode for offering basic power menu operations such as 5 | shutting down, logging out, rebooting and suspending. By default, it shows all 6 | choices and asks for confirmation for irreversible actions. The choices, their 7 | order and whether they require confirmation, can be all configured with 8 | command-line options. It also shows symbols by default, but this requires a 9 | monospace font with good support for symbols, so it can be disabled with 10 | `--no-symbols`. 11 | 12 | In contrast to other similar solutions I've found, the power menu is implemented 13 | as a rofi mode, not as a stand-alone executable that launches rofi by itself. 14 | This makes it possible to combine the script with the full power of how rofi can 15 | use modi. For instance, you can have multiple modi available (`-modi`) or 16 | combine multiple modi in one mode (`-combi-modi`), pass your own themes 17 | (`-theme`) and configurations as CLI flags (e.g., `-fullscreen`, 18 | `-sidebar-mode`, `-matching fuzzy`, `-location`). 19 | 20 | There's also a stand-alone script which uses dmenu (or rofi in dmenu mode). It's 21 | also a bit easier to use as you don't need to type the small amount of rofi 22 | "boilerplate". 23 | 24 | Just to give an example, the screenshot below shows Rofi Power Menu launched as: 25 | 26 | ``` 27 | rofi \ 28 | -show p \ 29 | -modi p:'rofi-power-menu --symbols-font "Symbols Nerd Font Mono"' \ 30 | -font "JetBrains Mono NF 16" \ 31 | -theme Paper \ 32 | -theme-str 'window {width: 8em;} listview {lines: 6;}' 33 | ``` 34 | 35 | ![Screenshot of Rofi Power Menu](./screenshot.png) 36 | 37 | 38 | ## Install 39 | 40 | You can use the script directly from this directory without needing to install 41 | it at all. If you want rofi to find it more easily, the script needs to be found 42 | in `PATH`. If you have `~/.local/bin` in `PATH`, you can just copy the script 43 | there: 44 | 45 | ``` 46 | cp rofi-power-menu ~/.local/bin/ 47 | ``` 48 | 49 | 50 | ## Usage 51 | 52 | A simple example showing how to launch the power menu: 53 | 54 | ``` 55 | rofi -show power-menu -modi power-menu:rofi-power-menu 56 | ``` 57 | 58 | If you didn't install the script in `PATH`, you need to give the path to the 59 | script. If you're running rofi under this directory where the script is, you can 60 | run it as follows: 61 | 62 | ``` 63 | rofi -show power-menu -modi power-menu:./rofi-power-menu 64 | ``` 65 | 66 | 67 | ### `--help` 68 | 69 | ``` 70 | rofi-power-menu - a power menu mode for Rofi 71 | 72 | Usage: rofi-power-menu [--choices CHOICES] [--confirm CHOICES] 73 | [--choose CHOICE] [--dry-run] [--symbols|--no-symbols] 74 | 75 | Use with Rofi in script mode. For instance, to ask for shutdown or reboot: 76 | 77 | rofi -show menu -modi "menu:rofi-power-menu --choices=shutdown/reboot" 78 | 79 | Available options: 80 | --dry-run Don't perform the selected action but print it to stderr. 81 | --choices CHOICES Show only the selected choices in the given order. Use / 82 | as the separator. Available choices are lockscreen, 83 | logout,suspend, hibernate, reboot and shutdown. By 84 | default, all available choices are shown. 85 | --confirm CHOICES Require confirmation for the gives choices only. Use / as 86 | the separator. Available choices are lockscreen, logout, 87 | suspend, hibernate, reboot and shutdown. By default, only 88 | irreversible actions logout, reboot and shutdown require 89 | confirmation. 90 | --choose CHOICE Preselect the given choice and only ask for a 91 | confirmation (if confirmation is set to be requested). It 92 | is strongly recommended to combine this option with 93 | --confirm=CHOICE if the choice wouldn't require 94 | confirmation by default. Available choices are 95 | lockscreen, logout, suspend, hibernate, reboot and 96 | shutdown. 97 | --[no-]symbols Show Unicode symbols or not. Requires a font with support 98 | for the symbols. Use, for instance, fonts from the 99 | Nerdfonts collection. By default, they are shown 100 | --[no-]text Show text description or not. 101 | --symbols-font FONT Use the given font for symbols. By default, the symbols 102 | use the same font as the text. That font is configured 103 | with rofi. 104 | -h,--help Show this help text. 105 | ``` 106 | 107 | 108 | ### `--choices=CHOICE1/CHOICE2/...` 109 | 110 | By default, the menu shows all available choices in a particular order. You can 111 | control the shown choices and their order by using `--choices` and listing the 112 | desired choices with `/` as the separator. Available choices are: 113 | 114 | - `lockscreen`: Lock screen 115 | - `logout`: Log out (confirmation asked by default) 116 | - `suspend`: Suspend 117 | - `hibernate`: Hibernate 118 | - `reboot`: Reboot (confirmation asked by default) 119 | - `shutdown`: Shutdown (confirmation asked by default) 120 | 121 | For instance, to show only `shutdown` and `reboot` choices: 122 | 123 | ``` 124 | rofi -show power-menu -modi "power-menu:./rofi-power-menu --choices=shutdown/reboot" 125 | ``` 126 | 127 | Or if you want a typical session menu: 128 | 129 | ``` 130 | rofi -show session-menu -modi "session-menu:./rofi-power-menu --choices=logout/lockscreen" 131 | ``` 132 | 133 | ### `--confirm=CHOICE1/CHOICE2/...` 134 | 135 | By default, confirmation is asked for irreversible actions `logout`, `reboot` 136 | and `shutdown`. You can choose for which actions you want confirmation (if any) 137 | by listing them with `--confirm` option. For instance, confirmation can be asked 138 | only for `reboot` and `shutdown`: 139 | 140 | 141 | ``` 142 | rofi -show power-menu -modi "power-menu:./rofi-power-menu --confirm=reboot/shutdown" 143 | ``` 144 | 145 | If you don't want confirmations for any actions, just give an empty string: 146 | 147 | ``` 148 | rofi -show power-menu -modi "power-menu:./rofi-power-menu --confirm=''" 149 | ``` 150 | 151 | 152 | ### `--choose=CHOICE` 153 | 154 | To open just a confirmation dialog for some fixed choice, you can use 155 | `--choose=CHOICE`, where `CHOICE` can again be one of the choices listed above. 156 | You should also require confirmation for that choice if that isn't done by 157 | default. For instance, a simple logout confirmation: 158 | 159 | ``` 160 | rofi -show logout -modi "logout:./rofi-power-menu --choose=logout" 161 | ``` 162 | 163 | For some choices (e.g., `hibernate`), confirmation isn't asked by default, so 164 | you probably want to ask that in this case: 165 | 166 | ``` 167 | rofi -show hibernate -modi "hibernate:./rofi-power-menu --choose=hibernate --confirm=hibernate" 168 | ``` 169 | 170 | If confirmation isn't asked, the action is performed immediately. Although, 171 | that's probably not useful, it is possible. However, note that Rofi will still 172 | pop up a menu with no options available. It would be nice if Rofi would not 173 | appear at all if it wasn't given any choices. This works when running the 174 | accompanied stand-alone script `dmenu-power-menu`. 175 | 176 | 177 | ### `--[no-]symbols` 178 | 179 | Disable or enable Unicode symbols/icons/glyphs. They are enabled by default. In 180 | order for them to show up correctly, you need a font that supports the used 181 | glyphs. It is recommended to use fonts from the [Nerdfonts 182 | collection](https://www.nerdfonts.com/). In addition, it is recommended to use a 183 | monospace font, otherwise the symbols widths might be messed up. So, for 184 | instance, "Symbols Nerd Font Mono", "Iosevka Nerd Font Mono" or "JetBrainsMono 185 | NF" are good options. 186 | 187 | 188 | ### `--dry-run` 189 | 190 | For debugging and development purposes, you can pass `--dry-run` flag. Then, the 191 | selected action isn't performed but only printed to stderr. 192 | 193 | 194 | ### dmenu 195 | 196 | There's a stand-alone script `dmenu-power-menu` that can be used to run the 197 | power menu with dmenu (or rofi in dmenu mode if dmenu isn't found). That script 198 | takes the same command-line arguments as listed above for the main script 199 | `rofi-power-menu`. The stand-alone script might be easier to use but you cannot 200 | pass arguments to dmenu/rofi so their configuration is hardcoded. Also, you need 201 | to install 202 | [rofi-script-to-dmenu](https://github.com/jluttine/rofi-script-to-dmenu). 203 | 204 | 205 | ## Copyright 206 | 207 | Copyright (c) 2020 Jaakko Luttinen 208 | 209 | MIT License 210 | -------------------------------------------------------------------------------- /dmenu-power-menu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Simple stand-alone wrapper for rofi-power-menu. 4 | # 5 | # This script takes the same CLI arguments as rofi-power-menu. 6 | # 7 | # rofi-script-to-dmenu needs to be installed 8 | 9 | # Use local rofi-power-menu if present. This makes developing easier. 10 | command -v ./rofi-power-menu >/dev/null 11 | powermenu_exists=$? 12 | if [ $powermenu_exists -eq 0 ] 13 | then 14 | powermenu="./rofi-power-menu" 15 | else 16 | powermenu="rofi-power-menu" 17 | fi 18 | 19 | cmd="$powermenu $@" 20 | rofi-script-to-dmenu "$cmd" 21 | -------------------------------------------------------------------------------- /rofi-power-menu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script defines just a mode for rofi instead of being a self-contained 4 | # executable that launches rofi by itself. This makes it more flexible than 5 | # running rofi inside this script as now the user can call rofi as one pleases. 6 | # For instance: 7 | # 8 | # rofi -show powermenu -modi powermenu:./rofi-power-menu 9 | # 10 | # See README.md for more information. 11 | 12 | set -e 13 | set -u 14 | 15 | # All supported choices 16 | all=(shutdown reboot suspend hibernate logout lockscreen) 17 | 18 | # By default, show all (i.e., just copy the array) 19 | show=("${all[@]}") 20 | 21 | declare -A texts 22 | texts[lockscreen]="lock screen" 23 | texts[switchuser]="switch user" 24 | texts[logout]="log out" 25 | texts[suspend]="suspend" 26 | texts[hibernate]="hibernate" 27 | texts[reboot]="reboot" 28 | texts[shutdown]="shut down" 29 | 30 | declare -A icons 31 | icons[lockscreen]="\Uf033e" 32 | icons[switchuser]="\Uf0019" 33 | icons[logout]="\Uf0343" 34 | icons[suspend]="\Uf04b2" 35 | icons[hibernate]="\Uf02ca" 36 | icons[reboot]="\Uf0709" 37 | icons[shutdown]="\Uf0425" 38 | icons[cancel]="\Uf0156" 39 | 40 | declare -A actions 41 | actions[lockscreen]="loginctl lock-session ${XDG_SESSION_ID-}" 42 | #actions[switchuser]="???" 43 | actions[logout]="loginctl terminate-session ${XDG_SESSION_ID-}" 44 | actions[suspend]="systemctl suspend" 45 | actions[hibernate]="systemctl hibernate" 46 | actions[reboot]="systemctl reboot" 47 | actions[shutdown]="systemctl poweroff" 48 | 49 | # By default, ask for confirmation for actions that are irreversible 50 | confirmations=(reboot shutdown logout) 51 | 52 | # By default, no dry run 53 | dryrun=false 54 | showsymbols=true 55 | showtext=true 56 | 57 | function check_valid { 58 | option="$1" 59 | shift 1 60 | for entry in "${@}" 61 | do 62 | if [ -z "${actions[$entry]+x}" ] 63 | then 64 | echo "Invalid choice in $1: $entry" >&2 65 | exit 1 66 | fi 67 | done 68 | } 69 | 70 | # Parse command-line options 71 | parsed=$(getopt --options=h --longoptions=help,dry-run,confirm:,choices:,choose:,symbols,no-symbols,text,no-text,symbols-font: --name "$0" -- "$@") 72 | if [ $? -ne 0 ]; then 73 | echo 'Terminating...' >&2 74 | exit 1 75 | fi 76 | eval set -- "$parsed" 77 | unset parsed 78 | while true; do 79 | case "$1" in 80 | "-h"|"--help") 81 | echo "rofi-power-menu - a power menu mode for Rofi" 82 | echo 83 | echo "Usage: rofi-power-menu [--choices CHOICES] [--confirm CHOICES]" 84 | echo " [--choose CHOICE] [--dry-run] [--symbols|--no-symbols]" 85 | echo 86 | echo "Use with Rofi in script mode. For instance, to ask for shutdown or reboot:" 87 | echo 88 | echo " rofi -show menu -modi \"menu:rofi-power-menu --choices=shutdown/reboot\"" 89 | echo 90 | echo "Available options:" 91 | echo " --dry-run Don't perform the selected action but print it to stderr." 92 | echo " --choices CHOICES Show only the selected choices in the given order. Use /" 93 | echo " as the separator. Available choices are lockscreen," 94 | echo " logout,suspend, hibernate, reboot and shutdown. By" 95 | echo " default, all available choices are shown." 96 | echo " --confirm CHOICES Require confirmation for the gives choices only. Use / as" 97 | echo " the separator. Available choices are lockscreen, logout," 98 | echo " suspend, hibernate, reboot and shutdown. By default, only" 99 | echo " irreversible actions logout, reboot and shutdown require" 100 | echo " confirmation." 101 | echo " --choose CHOICE Preselect the given choice and only ask for a" 102 | echo " confirmation (if confirmation is set to be requested). It" 103 | echo " is strongly recommended to combine this option with" 104 | echo " --confirm=CHOICE if the choice wouldn't require" 105 | echo " confirmation by default. Available choices are" 106 | echo " lockscreen, logout, suspend, hibernate, reboot and" 107 | echo " shutdown." 108 | echo " --[no-]symbols Show Unicode symbols or not. Requires a font with support" 109 | echo " for the symbols. Use, for instance, fonts from the" 110 | echo " Nerdfonts collection. By default, they are shown" 111 | echo " --[no-]text Show text description or not." 112 | echo " --symbols-font FONT Use the given font for symbols. By default, the symbols" 113 | echo " use the same font as the text. That font is configured" 114 | echo " with rofi." 115 | echo " -h,--help Show this help text." 116 | exit 0 117 | ;; 118 | "--dry-run") 119 | dryrun=true 120 | shift 1 121 | ;; 122 | "--confirm") 123 | IFS='/' read -ra confirmations <<< "$2" 124 | check_valid "$1" "${confirmations[@]}" 125 | shift 2 126 | ;; 127 | "--choices") 128 | IFS='/' read -ra show <<< "$2" 129 | check_valid "$1" "${show[@]}" 130 | shift 2 131 | ;; 132 | "--choose") 133 | # Check that the choice is valid 134 | check_valid "$1" "$2" 135 | selectionID="$2" 136 | shift 2 137 | ;; 138 | "--symbols") 139 | showsymbols=true 140 | shift 1 141 | ;; 142 | "--no-symbols") 143 | showsymbols=false 144 | shift 1 145 | ;; 146 | "--text") 147 | showtext=true 148 | shift 1 149 | ;; 150 | "--no-text") 151 | showtext=false 152 | shift 1 153 | ;; 154 | "--symbols-font") 155 | symbols_font="$2" 156 | shift 2 157 | ;; 158 | "--") 159 | shift 160 | break 161 | ;; 162 | *) 163 | echo "Internal error" >&2 164 | exit 1 165 | ;; 166 | esac 167 | done 168 | 169 | if [ "$showsymbols" = "false" -a "$showtext" = "false" ] 170 | then 171 | echo "Invalid options: cannot have --no-symbols and --no-text enabled at the same time." >&2 172 | exit 1 173 | fi 174 | 175 | # Define the messages after parsing the CLI options so that it is possible to 176 | # configure them in the future. 177 | 178 | function write_message { 179 | if [ -z ${symbols_font+x} ]; 180 | then 181 | icon="$1" 182 | else 183 | icon="$1" 184 | fi 185 | text="$2" 186 | if [ "$showsymbols" = "true" ] 187 | then 188 | if [ "$showtext" = "true" ] 189 | then 190 | echo -n "\u200e$icon \u2068$text\u2069" 191 | else 192 | echo -n "\u200e$icon" 193 | fi 194 | else 195 | echo -n "$text" 196 | fi 197 | } 198 | 199 | function print_selection { 200 | echo -e "$1" | $(read -r -d '' entry; echo "echo $entry") 201 | } 202 | 203 | declare -A messages 204 | declare -A confirmationMessages 205 | for entry in "${all[@]}" 206 | do 207 | messages[$entry]=$(write_message "${icons[$entry]}" "${texts[$entry]^}") 208 | done 209 | for entry in "${all[@]}" 210 | do 211 | # Add zero-width space character (\u200b) to icon to ensure confirmation- 212 | # and regular messages never collide. 213 | confirmationMessages[$entry]=$(write_message "${icons[$entry]}\u200b" "Yes, ${texts[$entry]}") 214 | done 215 | confirmationMessages[cancel]=$(write_message "${icons[cancel]}" "No, cancel") 216 | 217 | if [ $# -gt 0 ] 218 | then 219 | # If arguments given, use those as the selection 220 | selection="${@}" 221 | else 222 | # Otherwise, use the CLI passed choice if given 223 | if [ -n "${selectionID+x}" ] 224 | then 225 | selection="${messages[$selectionID]}" 226 | fi 227 | fi 228 | 229 | # Don't allow custom entries 230 | echo -e "\0no-custom\x1ftrue" 231 | # Use markup 232 | echo -e "\0markup-rows\x1ftrue" 233 | 234 | if [ -z "${selection+x}" ] 235 | then 236 | echo -e "\0prompt\x1fPower menu" 237 | for entry in "${show[@]}" 238 | do 239 | echo -e "${messages[$entry]}\0icon\x1f${icons[$entry]}" 240 | done 241 | else 242 | for entry in "${show[@]}" 243 | do 244 | if [ "$selection" = "$(print_selection "${messages[$entry]}")" ] 245 | then 246 | # Check if the selected entry is listed in confirmation requirements 247 | for confirmation in "${confirmations[@]}" 248 | do 249 | if [ "$entry" = "$confirmation" ] 250 | then 251 | # Ask for confirmation 252 | echo -e "\0prompt\x1fAre you sure" 253 | echo -e "${confirmationMessages[$entry]}\0icon\x1f${icons[$entry]}" 254 | echo -e "${confirmationMessages[cancel]}\0icon\x1f${icons[cancel]}" 255 | exit 0 256 | fi 257 | done 258 | # If not, then no confirmation is required, so mark confirmed 259 | selection=$(print_selection "${confirmationMessages[$entry]}") 260 | fi 261 | if [ "$selection" = "$(print_selection "${confirmationMessages[$entry]}")" ] 262 | then 263 | if [ $dryrun = true ] 264 | then 265 | # Tell what would have been done 266 | echo "Selected: $entry" >&2 267 | else 268 | # Perform the action 269 | ${actions[$entry]} 270 | fi 271 | exit 0 272 | fi 273 | if [ "$selection" = "$(print_selection "${confirmationMessages[cancel]}")" ] 274 | then 275 | # Do nothing 276 | exit 0 277 | fi 278 | done 279 | # The selection didn't match anything, so raise an error 280 | echo "Invalid selection: $selection" >&2 281 | exit 1 282 | fi 283 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jluttine/rofi-power-menu/700fb6c778d7a4a75f505e02b466bd60143f9d5c/screenshot.png --------------------------------------------------------------------------------