dotfiles/home/.local/bin/scripts/gui/prompt/menuprompt
2022-11-20 03:15:56 +01:00

339 lines
8.8 KiB
Bash
Executable file

#!/bin/bash
# Compatibility executable script for applications running dmenu, which
# runs wofi in dmenu mode instead. Note: In many cases the arguments won't
# be compatible and this will fail, however the primary way scripts use
# dmenu is just for simple prompts, using the `-p` flag. and wofi does fully
# support this usage.
# Wrapper script for menu application independent prompts. This script will pick
# the appropriate installed prompt tool (dmenu/rofi/wofi) and use run it accordingly
# to picked usage option(s).
help() {
cat << EOF
usage: menuprompt
Menuprompt is a wrapper script for menu application independent prompt (dmenu/rofi/wofi)
options:
-h, --help show this help message and exit
-p, --prompt [MESSAGE] Prompt to be displayed in the menu
-a, --app [MENU_APP] Overrides the app picked by default, must be one of supported apps
-v, --verbose Show debug information (printed to stderr to retain pipeable stdout output)
-vv, --very-verbose Show even more debug information
--dry-run Don't actually run the final menu command (only useful with --verbose)
actions:
-c, --choose
Basic pick prompt, picking from piped newline separated options
The picked option will be printed to stdout.
usage: printf 'No\nYes' | menuprompt -c -p 'Pick one: '
-yn, --yesno
Essentially an alias for printf 'No\nYes' | menuprompt -p "\$1", however instead of printing the result
to stdout, this instead exits with code 0 on success, 1 on failure, or 2 on getting other input. This
allows for easily combining it with other applications through chaining.
example: menuprompt --yesno && poweroff or menuprompt -yn -p "Do you really want to quit?" && killall Hyprland
-P, --password
Password prompt - hides the input characters
example: menuprompt -P -p "Enter password:" | xargs -I {} echo "Hah, got your password: {}"
supported menu applications and their actions:
dmenu:
- choose: supported
- yesno: supported
- password: supported, but requires password patch for -P argument
wofi:
- choose: supported
- yesno: supported
- password: supported, however setting --prompt won't have any effect
rofi:
- choose: supported
- yesno: supported
- password: not supported - no option for hidden input
passing custom arguments to menu app:
Note that when passing extra arguments, you should always explicity specify the menu application to use
as it's not guaranteed that all supported args will support given extra argument(s), nor that if they do
their support is consistent and the same argument does the same thing.
examples:
Make dmenu run on the bottom of the screen:
printf "A\nB\nC" menuprompt --app dmenu --choose -- -b
Run wofi with 20% width on the yesno prompt:
menuprompt --yesno -p "Do you really want to quit?" --app wofi -- --width=20%
exit codes:
0: Menu app ran successfully, action completed
1-2: Action specific
3: User error - invalid usage
4: Unable to pick menu app (no usable menu app installed?)
5: Internal menuprompt error, report this!
EOF
}
pick_menu_app() {
if [ -n "$MENU_APP" ]; then
if command -v "$MENU_APP" >/dev/null; then
echo "$MENU_APP"
return 0
else
>&2 echo "Specified menu app by (set by \$MENU_APP) isn't a valid command \`$MENU_APP\`"
exit 3
fi
fi
if [ "$XDG_SESSION_TYPE" = "x11" ]; then
if command -v dmenu >/dev/null; then
echo "dmenu"
elif command -v rofi >/dev/null; then
echo "rofi"
else
>&2 echo "Menu application for x11 not found, neither dmenu nor rofi available."
exit 3
fi
elif [ "$XDG_SESSION_TYPE" = "wayland" ]; then
if command -v wofi >/dev/null; then
echo "wofi"
else
>&2 echo "Menu application for wayland not found, wofi not available."
exit 3
fi
else
>&2 echo "Unknown session type, expected \$XDG_SESSION_TYPE to be \`wayland\` or \`x11\`, got \`$XDG_SESSION_TYPE\`."
exit 3
fi
}
# Runs picked menu application with
# $1 pipe STDIN if set to "1", runs normally if "0"
# $2 app to use (result of pick_menu_app)
# $3 prompt to be shown (can be empty)
# $4- extra args to pass to given menu app
runmenu() {
pipe_stdin="$1"
app="$2"
prompt="$3"
shift 3
if [ "$VERBOSITY" -ge 2 ]; then
if [ $# -gt 0 ]; then
>&2 echo "Making menu command for app: \`$app\`, with prompt: \`$prompt\` and extra args: \`$*\`"
else
>&2 echo "Making menu command for app: \`$app\`, with prompt: \`$prompt\` (no extra args)"
fi
fi
if [ "$app" = "dmenu" ] && [ "$pipe_stdin" -eq 0 ]; then
>&2 echo "Dmenu can only be used with piped STDIN (forgot --choose?)"
exit 3
fi
args=()
case "$app" in
dmenu)
args+=("-i")
;;
wofi)
args+=("--dmenu")
;;
rofi)
args+=("-dmenu")
;;
*)
>&2 echo "Unsupported menu app: $app"
exit 4
;;
esac
if [ -n "$prompt" ]; then
if [ "$app" = "wofi" ] && [ "$pipe_stdin" -eq 0 ]; then
>&2 echo "WARNING: wofi prompt can only be displayed with piped STDIN (forgot --choose?)"
fi
args+=("-p" "$prompt")
fi
if [ "$VERBOSITY" -ge 1 ]; then
>&2 echo "Generated command: \`$app ${args[*]} $*\`"
fi
if [ "$DRY_RUN" -ne 1 ]; then
if [ "$pipe_stdin" -eq 1 ]; then
cat | $app "${args[@]}" "$@"
elif [ "$pipe_stdin" -eq 0 ]; then
$app "${args[@]}" "$@"
return $?
else
>&2 echo "Internal error, pipe_stdin set to invalid value: \`$pipe_stdin\`"
exit 5
fi
else
return 0
fi
}
# $1 app to use (result of pick_menu_app)
# $2 prompt to be shown (can be empty)
# $2- extra arguments to pass to given menu app
password() {
app="$1"
prompt="$2"
shift 2
if [ "$VERBOSITY" -ge 2 ]; then
>&2 echo "Prompting for password..."
fi
if [ "$app" = "dmenu" ]; then
if ! runmenu "0" "$app" "$prompt" -P "$@"; then
>&2 echo "To use dmenu for passwords, you need to apply the password patch."
exit 3
fi
elif [ "$app" = "wofi" ]; then
runmenu "0" "$app" "$prompt" -P "$@"
elif [ "$app" = "rofi" ]; then
>&2 echo "Rofi doesn't support password/hidden input."
exit 3
else
>&2 echo "Unsupported menu application picked: \`$app\`"
exit 4
fi
}
# $1 app to use (result of pick_menu_app)
# $2 prompt to be shown (can be empty)
# $3- extra arguments to pass to the menu app
yesno() {
app="$1"
prompt="$2"
shift 2
if [ "$VERBOSITY" -ge 2 ]; then
>&2 echo "Prompting for yesno..."
fi
temp="$(printf "No\nYes" | runmenu "1" "$app" "$prompt" "$@")"
if [ "$VERBOSITY" -ge 2 ]; then
>&2 echo "Yesno prompt answer: \`$temp\`"
fi
if [ "$temp" = "Yes" ]; then
return 0
elif [ "$temp" = "No" ]; then
return 1
else
return 2
fi
}
#@ - extra args to pass to picked menu app
run() {
app="$(pick_menu_app)"
if [ "$VERBOSITY" -ge 2 ]; then
>&2 echo "ACTION: \`$ACTION\`"
>&2 echo "PROMPT_ARG: \`$PROMPT_ARG\`"
>&2 echo "picekd app: \`$app\`"
>&2 echo "Extra args: \`$*\`"
fi
if [ -z "$ACTION" ]; then
runmenu "0" "$app" "$PROMPT_ARG" "$@"
return $?
elif [ "$ACTION" = "choose" ]; then
runmenu "1" "$app" "$PROMPT_ARG" "$@"
return $?
elif [ "$ACTION" = "yesno" ]; then
yesno "$app" "$PROMPT_ARG" "$@"
return $?
elif [ "$ACTION" = "password" ]; then
password "$app" "$PROMPT_ARG" "$@"
return $?
fi
>&2 echo "Huh? How did you get here?"
>&2 echo "Rerun this command with the --verbose flag and submit a report with the captured output."
exit 5
}
ACTION=""
PROMPT_ARG=""
MENU_APP="${MENU_APP-""}"
VERBOSITY=0
DRY_RUN=0
while [ "$1" ]; do
case "$1" in
# options
-h|--help)
help
exit 0
;;
-a|--app)
MENU_APP="$2"
shift 2
;;
-p|--prompt)
PROMPT_ARG="$2"
shift 2
;;
-v|--verbose)
VERBOSITY=1
shift
;;
-vv|--very-verbose)
VERBOSITY=2
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
# actions
-c|--choose)
if [ -n "$ACTION" ]; then
>&2 echo "Action already set, can't specify multiple actions"
exit 3
fi
ACTION="choose"
shift
;;
-yn|--yesno)
if [ -n "$ACTION" ]; then
>&2 echo "Action already set, can't specify multiple actions"
exit 3
fi
ACTION="yesno"
shift
;;
-P|--password)
if [ -n "$ACTION" ]; then
>&2 echo "Action already set, can't specify multiple actions"
exit 3
fi
ACTION="password"
shift
;;
--)
shift
run "$@"
exit $?
;;
# err
*)
>&2 echo "Unrecognized argument: $1"
>&2 echo "use -h or --help for help"
exit 4
;;
esac
done
run