#!/bin/sh # An attempt to unify usage of multiple launchers. # MENUCMD must be set to the name of the lancher # # look at the last line to understand how it works tmp="$(mktemp -u)" export menuopts="" TOFIRC="$HOME/.config/tofi/config" options=' horizontal center input instant dynamic long prompt numbered prefix' cleanup() { rm -f "$tmp"; } logn() { >&2 printf '%s\n' "$@"; } run() { IFS=: set -f find -L $PATH -type f -printf "%f\n" 2> /dev/null | sort -u } help() { cat <<-EOF Usage: commander OPTIONS... An attempt to regroup multiple launchers into one, it detects the value of the MENUCMD variable try: MENUCMD=tofi commander < /etc/passwd Options: -h Display items horizontally -c Display centered -i Take an input and return it to stdout -d Adjust size and style dynamically -p ARG Prompt user with the specified argument -l Display in long list format -n Output the number of matches -r Select an executable from PATH -s Invert instant select on single match -x Invert prefix matching EOF } add_option() { menuopts="$menuopts $*"; } get_prop() { awk -F '=' "/$1 *=*/ {print \$2}" "$TOFIRC"; } trap cleanup EXIT # Helper functions case "$MENUCMD" in "tofi") # $1: items count get_height() { font_size="$(get_prop "font-size")" padding_top="$(get_prop "padding-top")" padding_bottom="$(get_prop "padding-bottom")" printf "%s" "$(( font_size*2 + padding_top + padding_bottom + 2 + ($1)*(font_size*2 - 2) ))" } # $1: items length get_width() { font_size="$(get_prop "font-size")" padding_left="$(get_prop "padding-left")" padding_right="$(get_prop "padding-right")" printf "%s" "$(($1*(font_size-2) + 2 + padding_left + padding_right))" } ;; "dmenu") ;; *) ;; esac main() { # shellcheck disable=SC2317 case "$MENUCMD" in "tofi") center() { add_option "--anchor=center --width=20%"; } horizontal() { add_option "--horizontal=true --height=32" add_option "--result-spacing=12" add_option "--selection-background=#88c0d0" add_option "--selection-background-padding=6,4" add_option "--padding-top=0 --padding-bottom=0" add_option "--margin-top=4" } dynamic() { pre_cmd() { max_height="$(get_height 8)" max_width="$(get_width 48)" tee "$tmp" items_count="$(wc -l < "$tmp")" items_length="$(wc -L < "$tmp")" if [ "$long" -ne 1 ] then height="$(get_height "$items_count")" [ "$height" -gt "$max_height" ] && height="$max_height" add_option "--height=$height" fi width="$(get_width "$items_length")" [ "$width" -gt "$max_width" ] && width="$max_width" if [ "$prompt" -eq 1 ] then prompt_width="$(printf "%s" "$prompt_arg" | wc -c)" width="$((width + $(get_width "$prompt_width")))" fi add_option "--width=$width" } } long() { add_option "--width=100% --height=$(get_height 20)"; } input() { add_option "--height=$(get_height 0) --width=$(get_width 24)" pre_filter() { cat /dev/null; } } prefix() { add_option '--matching-algorithm=normal'; } instant() { add_option "--auto-accept-single=false"; } prompt() { add_option '--prompt-text' "$prompt_arg"; } numbered() { pre_filter() { awk '{print NR, $0}'; } post_filter() { awk '{print $1}'; } } menucmd() { tofi "$@"; } ;; "dmenu") horizontal() { add_option "-l 0"; } center() { add_option "-c"; } long() { add_option "-l 20 -g 1"; } input() { pre_filter() { cat /dev/null; } } dynamic() { pre_cmd() { tee "$tmp" items_count="$(wc -l < "$tmp")" number="$items_count" for i in $(seq $((number/2))) do [ "$((number%i))" -eq 0 ] && factors="$i $factors" done min_diff=9999999999 for factor in $factors do quotient="$((number/factor))" diff=$((quotient - factor)) if [ "$diff" -ge 0 ] && [ "$diff" -lt "$min_diff" ] then columns="$factor" rows="$quotient" min_diff="$diff" fi done add_option "-g $columns" add_option "-l $rows" } } instant() { add_option "-n"; } prefix() { add_option '-x'; } prompt() { add_option '-p' "$prompt_arg"; } numbered() { add_option "-px"; } menucmd() { dmenu "$@"; } ;; *) center() { :; } horizontal() { :; } dynamic() { :; } long() { :; } input() { pre_filter() { cat /dev/null; } add_option '--print-query' } prefix() { :; } prompt() { add_option "--prompt" "$prompt_arg"; } numbered() { pre_filter() { awk '{print NR, $0}'; } add_option '--with-nth' '2..' post_filter() { awk '{print $1}'; } } menucmd() { fzf "$@"; } ;; esac for option in $options do eval "$option=0" done while getopts ":cdhilop:nrsx" opt do # shellcheck disable=SC2034 case $opt in # display horizontal h) horizontal=1 ;; # display centered c) center=1 ;; # take an input and return it on stdout i) input=1 ;; # adjust size and style dynamically d) dynamic=1 ;; p) prompt=1; prompt_arg="$OPTARG";; # long list format l) long=1 ;; # output number of match n) numbered=1 ;; # select an executable from PATH r) shift run | $0 "$@" exit ;; # invert instant select on single match s) instant=1 ;; # invert prefix matching x) prefix=1 ;; :) logn "Option '-$OPTARG' requires an argument" help exit 1 ;; ?) logn "Invalid option: -$OPTARG" help exit 1 ;; esac done # conflicts [ "$input" -eq 1 ] && [ "$dynamic" -eq 1 ] && logn "conflicting options." && help && exit 1 # quit if stdin is empty [ -t 0 ] && [ "$input" -eq 0 ] && logn "Stdin is empty." && help && exit 1 # call options for option in $options do eval "test \"\$$option\" -eq 1" && $option done command -v pre_filter > /dev/null || pre_filter() { cat; } command -v post_filter > /dev/null || post_filter() { cat; } which "${MENUCMD:-fzf}" > /dev/null || exit 1 if command -v pre_cmd > /dev/null then pre_cmd > "$tmp" >&2 printf "menuopts: %s\n" "$menuopts" # shellcheck disable=SC2086 pre_filter < "$tmp" | menucmd $menuopts | post_filter else >&2 printf "menuopts: %s\n" "$menuopts" # shellcheck disable=SC2086 pre_filter | menucmd $menuopts | post_filter fi } main "$@"