├── 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 | 
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
--------------------------------------------------------------------------------