mirror of
https://github.com/ItsDrike/dotfiles.git
synced 2025-06-29 12:10:42 +00:00
Update multiple scripts
This commit is contained in:
parent
542ac098b4
commit
44b010a541
17 changed files with 424 additions and 37 deletions
85
home/.local/bin/scripts/gui/prompt/dmenu-scripts/displayselect
Executable file
85
home/.local/bin/scripts/gui/prompt/dmenu-scripts/displayselect
Executable file
|
@ -0,0 +1,85 @@
|
|||
|
||||
#!/bin/sh
|
||||
|
||||
# A UI for detecting and selecting all displays. Probes xrandr for connected
|
||||
# displays and lets user select one to use. User may also select "manual
|
||||
# selection" which opens arandr.
|
||||
|
||||
twoscreen() { # If multi-monitor is selected and there are two screens.
|
||||
|
||||
mirror=$(printf "no\\nyes" | dmenu -i -p "Mirror displays?")
|
||||
# Mirror displays using native resolution of external display and a scaled
|
||||
# version for the internal display
|
||||
if [ "$mirror" = "yes" ]; then
|
||||
external=$(echo "$screens" | dmenu -i -p "Optimize resolution for:")
|
||||
internal=$(echo "$screens" | grep -v "$external")
|
||||
|
||||
res_external=$(xrandr --query | sed -n "/^$external/,/\+/p" | \
|
||||
tail -n 1 | awk '{print $1}')
|
||||
res_internal=$(xrandr --query | sed -n "/^$internal/,/\+/p" | \
|
||||
tail -n 1 | awk '{print $1}')
|
||||
|
||||
res_ext_x=$(echo "$res_external" | sed 's/x.*//')
|
||||
res_ext_y=$(echo "$res_external" | sed 's/.*x//')
|
||||
res_int_x=$(echo "$res_internal" | sed 's/x.*//')
|
||||
res_int_y=$(echo "$res_internal" | sed 's/.*x//')
|
||||
|
||||
scale_x=$(echo "$res_ext_x / $res_int_x" | bc -l)
|
||||
scale_y=$(echo "$res_ext_y / $res_int_y" | bc -l)
|
||||
|
||||
xrandr --output "$external" --auto --scale 1.0x1.0 \
|
||||
--output "$internal" --auto --same-as "$external" \
|
||||
--scale "$scale_x"x"$scale_y"
|
||||
else
|
||||
|
||||
primary=$(echo "$screens" | dmenu -i -p "Select primary display:")
|
||||
secondary=$(echo "$screens" | grep -v "$primary")
|
||||
direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?")
|
||||
xrandr --output "$primary" --auto --scale 1.0x1.0 --output "$secondary" --"$direction"-of "$primary" --auto --scale 1.0x1.0
|
||||
fi
|
||||
}
|
||||
|
||||
morescreen() { # If multi-monitor is selected and there are more than two screens.
|
||||
primary=$(echo "$screens" | dmenu -i -p "Select primary display:")
|
||||
secondary=$(echo "$screens" | grep -v "$primary" | dmenu -i -p "Select secondary display:")
|
||||
direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?")
|
||||
tertiary=$(echo "$screens" | grep -v "$primary" | grep -v "$secondary" | dmenu -i -p "Select third display:")
|
||||
xrandr --output "$primary" --auto --output "$secondary" --"$direction"-of "$primary" --auto --output "$tertiary" --"$(printf "left\\nright" | grep -v "$direction")"-of "$primary" --auto
|
||||
}
|
||||
|
||||
multimon() { # Multi-monitor handler.
|
||||
case "$(echo "$screens" | wc -l)" in
|
||||
2) twoscreen ;;
|
||||
*) morescreen ;;
|
||||
esac ;}
|
||||
|
||||
onescreen() { # If only one output available or chosen.
|
||||
xrandr --output "$1" --auto --scale 1.0x1.0 $(echo "$allposs" | grep -v "\b$1" | awk '{print "--output", $1, "--off"}' | paste -sd ' ' -)
|
||||
}
|
||||
|
||||
postrun() { # Stuff to run to clean up.
|
||||
command -v setbg >/dev/null && setbg # Fix background if screen size/arangement has changed.
|
||||
}
|
||||
|
||||
# Get all possible displays
|
||||
allposs=$(xrandr -q | grep "connected")
|
||||
|
||||
# Get all connected screens.
|
||||
screens=$(echo "$allposs" | awk '/ connected/ {print $1}')
|
||||
|
||||
# If there's only one screen
|
||||
if [ "$(echo "$screens" | wc -l)" -lt 2 ]; then
|
||||
onescreen "$screens"
|
||||
notify-send "💻 Only one screen detected." "Using it in its optimal settings..."
|
||||
else
|
||||
# Get user choice including multi-monitor and manual selection:
|
||||
chosen=$(printf "%s\\nmulti-monitor\\nmanual selection" "$screens" | dmenu -i -p "Select display arangement:") &&
|
||||
case "$chosen" in
|
||||
"manual selection") arandr ; exit ;;
|
||||
"multi-monitor") multimon ;;
|
||||
*) onescreen "$chosen" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
|
||||
postrun
|
11
home/.local/bin/scripts/gui/prompt/dmenu-scripts/dman
Executable file
11
home/.local/bin/scripts/gui/prompt/dmenu-scripts/dman
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Dmenu prompt to easily search for a man page to open
|
||||
|
||||
page="$(apropos --long "$1" | dmenu -i -l 10 | awk '{print $2, $1}' | tr -d '()')"
|
||||
|
||||
if tty -s; then
|
||||
man $page
|
||||
else
|
||||
$TERMINAL -e man $page
|
||||
fi
|
68
home/.local/bin/scripts/gui/prompt/dmenu-scripts/dmenumount
Executable file
68
home/.local/bin/scripts/gui/prompt/dmenu-scripts/dmenumount
Executable file
|
@ -0,0 +1,68 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Gives dmenu prompt to mount unmounted drives and Android phones.
|
||||
# If they're in /etc/fstab they'll be mounted automatically.
|
||||
# Otherwise, you'll be prompted to give a mountpoint from already
|
||||
# existing directories. If you input a novel directory, it will
|
||||
# prompt you to create that directory.
|
||||
|
||||
getmount() { \
|
||||
[ -z "$chosen" ] && exit 1
|
||||
# shellcheck disable=SC2086
|
||||
mp="$(find $1 2>/dev/null | dmenu -i -p "Type in mount point.")" || exit 1
|
||||
test -z "$mp" && exit 1
|
||||
if [ ! -d "$mp" ]; then
|
||||
mkdiryn=$(printf "No\\nYes" | dmenu -i -p "$mp does not exist. Create it?") || exit 1
|
||||
[ "$mkdiryn" = "Yes" ] && (mkdir -p "$mp" || sudo -A mkdir -p "$mp")
|
||||
fi
|
||||
}
|
||||
|
||||
mountusb() { \
|
||||
chosen="$(echo "$usbdrives" | dmenu -i -p "Mount which drive?")" || exit 1
|
||||
chosen="$(echo "$chosen" | awk '{print $1}')"
|
||||
sudo -A mount "$chosen" 2>/dev/null && notify-send "💻 USB mounting" "$chosen mounted." && exit 0
|
||||
alreadymounted=$(lsblk -nrpo "name,type,mountpoint" | awk '$3!~/\/boot|\/home$|SWAP/&&length($3)>1{printf "-not ( -path *%s -prune ) ",$3}')
|
||||
getmount "/mnt /media /mount /home -maxdepth 5 -type d $alreadymounted"
|
||||
partitiontype="$(lsblk -no "fstype" "$chosen")"
|
||||
case "$partitiontype" in
|
||||
"vfat") sudo -A mount -t vfat "$chosen" "$mp" -o rw,umask=0000;;
|
||||
"exfat") sudo -A mount "$chosen" "$mp" -o uid="$(id -u)",gid="$(id -g)";;
|
||||
*) sudo -A mount "$chosen" "$mp"; user="$(whoami)"; ug="$(groups | awk '{print $1}')"; sudo -A chown "$user":"$ug" "$mp";;
|
||||
esac
|
||||
notify-send "💻 USB mounting" "$chosen mounted to $mp."
|
||||
}
|
||||
|
||||
mountandroid() { \
|
||||
chosen="$(echo "$anddrives" | dmenu -i -p "Which Android device?")" || exit 1
|
||||
chosen="$(echo "$chosen" | cut -d : -f 1)"
|
||||
getmount "$HOME -maxdepth 3 -type d"
|
||||
simple-mtpfs --device "$chosen" "$mp"
|
||||
echo "OK" | dmenu -i -p "Tap Allow on your phone if it asks for permission and then press enter" || exit 1
|
||||
simple-mtpfs --device "$chosen" "$mp"
|
||||
notify-send "🤖 Android Mounting" "Android device mounted to $mp."
|
||||
}
|
||||
|
||||
asktype() { \
|
||||
choice="$(printf "USB\\nAndroid" | dmenu -i -p "Mount a USB drive or Android device?")" || exit 1
|
||||
case $choice in
|
||||
USB) mountusb ;;
|
||||
Android) mountandroid ;;
|
||||
esac
|
||||
}
|
||||
|
||||
anddrives=$(simple-mtpfs -l 2>/dev/null)
|
||||
usbdrives="$(lsblk -rpo "name,type,size,mountpoint" | grep 'part\|rom' | awk '$4==""{printf "%s (%s)\n",$1,$3}')"
|
||||
|
||||
if [ -z "$usbdrives" ]; then
|
||||
[ -z "$anddrives" ] && echo "No USB drive or Android device detected" && exit
|
||||
echo "Android device(s) detected."
|
||||
mountandroid
|
||||
else
|
||||
if [ -z "$anddrives" ]; then
|
||||
echo "USB drive(s) detected."
|
||||
mountusb
|
||||
else
|
||||
echo "Mountable USB drive(s) and Android device(s) detected."
|
||||
asktype
|
||||
fi
|
||||
fi
|
121
home/.local/bin/scripts/gui/prompt/dmenu-scripts/dmenurecord
Executable file
121
home/.local/bin/scripts/gui/prompt/dmenu-scripts/dmenurecord
Executable file
|
@ -0,0 +1,121 @@
|
|||
|
||||
#!/bin/sh
|
||||
|
||||
# Usage:
|
||||
# `$0`: Ask for recording type via dmenu
|
||||
# `$0 screencast`: Record both audio and screen
|
||||
# `$0 video`: Record only screen
|
||||
# `$0 audio`: Record only audio
|
||||
# `$0 kill`: Kill existing recording
|
||||
#
|
||||
# If there is already a running instance, user will be prompted to end it.
|
||||
|
||||
screencast() {
|
||||
ffmpeg -y \
|
||||
-f x11grab \
|
||||
-framerate 60 \
|
||||
-s "$(xdpyinfo | grep dimensions | awk '{print $2;}')" \
|
||||
-i "$DISPLAY" \
|
||||
-f alsa -i default \
|
||||
-r 30 \
|
||||
-c:v h264 -crf 0 -preset ultrafast -c:a aac \
|
||||
"$HOME/screencast-$(date '+%y%m%d-%H%M-%S').mp4" &
|
||||
echo $! > /tmp/recordingpid
|
||||
}
|
||||
|
||||
video() {
|
||||
ffmpeg \
|
||||
-f x11grab \
|
||||
-s "$(xdpyinfo | grep dimensions | awk '{print $2;}')" \
|
||||
-i "$DISPLAY" \
|
||||
-c:v libx264 -qp 0 -r 30 \
|
||||
"$HOME/video-$(date '+%y%m%d-%H%M-%S').mkv" &
|
||||
echo $! > /tmp/recordingpid
|
||||
}
|
||||
|
||||
webcamhidef() {
|
||||
ffmpeg \
|
||||
-f v4l2 \
|
||||
-i /dev/video0 \
|
||||
-video_size 1920x1080 \
|
||||
"$HOME/webcam-$(date '+%y%m%d-%H%M-%S').mkv" &
|
||||
echo $! > /tmp/recordingpid
|
||||
}
|
||||
|
||||
webcam() {
|
||||
ffmpeg \
|
||||
-f v4l2 \
|
||||
-i /dev/video0 \
|
||||
-video_size 640x480 \
|
||||
"$HOME/webcam-$(date '+%y%m%d-%H%M-%S').mkv" &
|
||||
echo $! > /tmp/recordingpid
|
||||
}
|
||||
|
||||
|
||||
audio() {
|
||||
ffmpeg \
|
||||
-f alsa -i default \
|
||||
"$HOME/audio-$(date '+%y%m%d-%H%M-%S').mp3" &
|
||||
echo $! > /tmp/recordingpid
|
||||
}
|
||||
|
||||
asktoend() {
|
||||
response=$(printf "No\\nYes" | dmenu -i -p "Recording still active. End recording?") &&
|
||||
[ "$response" = "Yes" ] && killrecording
|
||||
}
|
||||
|
||||
videoselected()
|
||||
{
|
||||
slop -f "%x %y %w %h" > /tmp/slop
|
||||
read -r X Y W H < /tmp/slop
|
||||
rm /tmp/slop
|
||||
|
||||
ffmpeg \
|
||||
-f x11grab \
|
||||
-framerate 60 \
|
||||
-video_size "$W"x"$H" \
|
||||
-i :0.0+"$X,$Y" \
|
||||
-c:v libx264 -qp 0 -r 30 \
|
||||
"$HOME/box-$(date '+%y%m%d-%H%M-%S').mkv" &
|
||||
echo $! > /tmp/recordingpid
|
||||
}
|
||||
|
||||
killrecording() {
|
||||
recpid="$(cat /tmp/recordingpid)"
|
||||
# kill with SIGTERM, allowing finishing touches.
|
||||
kill -15 "$recpid" 2>/dev/null
|
||||
rm -f /tmp/recordingpid
|
||||
# even after SIGTERM, ffmpeg may still run, so SIGKILL it.
|
||||
sleep 3
|
||||
kill -9 "$recpid" 2>/dev/null
|
||||
exit
|
||||
}
|
||||
|
||||
askrecording() { \
|
||||
choice=$(printf "screencast\\nvideo\\nvideo selected\\naudio\\nwebcam\\nwebcam (hi-def)" | dmenu -i -p "Select recording style:")
|
||||
case "$choice" in
|
||||
screencast) screencast;;
|
||||
audio) audio;;
|
||||
video) video;;
|
||||
*selected) videoselected;;
|
||||
webcam) webcam;;
|
||||
"webcam (hi-def)") webcamhidef;;
|
||||
esac
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
screencast) screencast;;
|
||||
audio) audio;;
|
||||
video) video;;
|
||||
*selected) videoselected;;
|
||||
kill) killrecording;;
|
||||
*)
|
||||
if [ -f /tmp/recordingpid ]; then
|
||||
recpid="$(cat /tmp/recordingpid)"
|
||||
if ps -p $recpid > /dev/null; then
|
||||
asktoend
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
askrecording;;
|
||||
esac
|
44
home/.local/bin/scripts/gui/prompt/dmenu-scripts/dmenuumount
Executable file
44
home/.local/bin/scripts/gui/prompt/dmenu-scripts/dmenuumount
Executable file
|
@ -0,0 +1,44 @@
|
|||
#!/bin/sh
|
||||
|
||||
# A dmenu prompt to unmount drives.
|
||||
# Provides you with mounted partitions, select one to unmount.
|
||||
# Drives mounted at /, /boot and /home will not be options to unmount.
|
||||
|
||||
unmountusb() {
|
||||
[ -z "$drives" ] && exit
|
||||
chosen="$(echo "$drives" | dmenu -i -p "Unmount which drive?")" || exit 1
|
||||
chosen="$(echo "$chosen" | awk '{print $1}')"
|
||||
[ -z "$chosen" ] && exit
|
||||
sudo -A umount "$chosen" && notify-send "💻 USB unmounting" "$chosen unmounted."
|
||||
}
|
||||
|
||||
unmountandroid() { \
|
||||
chosen="$(awk '/simple-mtpfs/ {print $2}' /etc/mtab | dmenu -i -p "Unmount which device?")" || exit 1
|
||||
[ -z "$chosen" ] && exit
|
||||
sudo -A umount -l "$chosen" && notify-send "🤖 Android unmounting" "$chosen unmounted."
|
||||
}
|
||||
|
||||
asktype() { \
|
||||
choice="$(printf "USB\\nAndroid" | dmenu -i -p "Unmount a USB drive or Android device?")" || exit 1
|
||||
case "$choice" in
|
||||
USB) unmountusb ;;
|
||||
Android) unmountandroid ;;
|
||||
esac
|
||||
}
|
||||
|
||||
drives=$(lsblk -nrpo "name,type,size,mountpoint" | awk '$4!~/\/boot|\/home$|SWAP/&&length($4)>1{printf "%s (%s)\n",$4,$3}')
|
||||
|
||||
if ! grep simple-mtpfs /etc/mtab; then
|
||||
[ -z "$drives" ] && echo "No drives to unmount." && exit
|
||||
echo "Unmountable USB drive detected."
|
||||
unmountusb
|
||||
else
|
||||
if [ -z "$drives" ]
|
||||
then
|
||||
echo "Unmountable Android device detected."
|
||||
unmountandroid
|
||||
else
|
||||
echo "Unmountable USB drive(s) and Android device(s) detected."
|
||||
asktype
|
||||
fi
|
||||
fi
|
8
home/.local/bin/scripts/gui/prompt/dmenu-scripts/pyrun
Executable file
8
home/.local/bin/scripts/gui/prompt/dmenu-scripts/pyrun
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Simple wrapper to quickly execute a simple python command and have it printed out.
|
||||
# The output is then sent back via notification.
|
||||
#
|
||||
# This already has random imported, since that's a common use-case for this script.
|
||||
|
||||
echo "" | dmenu -i -p "Python" | xargs -0 -I % python -c "import random;print(%)" | xargs -I % notify-send "Pyrun output:" "%"
|
36
home/.local/bin/scripts/gui/prompt/dmenu-scripts/todo
Executable file
36
home/.local/bin/scripts/gui/prompt/dmenu-scripts/todo
Executable file
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
# This is inspired by dmenu's todo script made by suckless
|
||||
#
|
||||
# Manage TODO tasks in dmenu by writing them, remove by selecting
|
||||
# an existing entry
|
||||
#
|
||||
# Configurable variables
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
FILE="${XDG_DATA_HOME:-$HOME/.local/share}/todos"
|
||||
PROMPT="Add/delete a task: "
|
||||
|
||||
# Logic
|
||||
# ---------------------------------------------------------------------
|
||||
mkdir -p "$(dirname $FILE)"
|
||||
touch "$FILE"
|
||||
|
||||
height=$(wc -l "$FILE" | awk '{print $1}')
|
||||
|
||||
# Run dmenu and keep restarting it until it returns an empty output
|
||||
cmd=$(dmenu -l "$height" -p "$PROMPT" "$@" < "$FILE")
|
||||
while [ -n "$cmd" ]; do
|
||||
# If the output matched an existing TODO, remove it
|
||||
if grep -q "^$cmd\$" "$FILE"; then
|
||||
grep -v "^$cmd\$" "$FILE" > "$FILE.$$"
|
||||
mv "$FILE.$$" "$FILE"
|
||||
height=$(( height - 1 ))
|
||||
# If the output didn't match an existing TODO, it's a new one, add it
|
||||
else
|
||||
echo "$cmd" >> "$FILE"
|
||||
height=$(( height + 1 ))
|
||||
fi
|
||||
|
||||
# Keep restarting until empty output
|
||||
cmd=$(dmenu -l "$height" -p "$PROMPT" "$@" < "$FILE")
|
||||
done
|
23
home/.local/bin/scripts/gui/prompt/dmenu-scripts/website-link
Executable file
23
home/.local/bin/scripts/gui/prompt/dmenu-scripts/website-link
Executable file
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env python
|
||||
import feedparser
|
||||
import subprocess
|
||||
|
||||
|
||||
URL = "https://itsdrike.com/posts/index.xml"
|
||||
|
||||
|
||||
def main():
|
||||
feed = feedparser.parse(URL)
|
||||
titles = {entry['title']: entry['link'] for entry in feed['entries']}
|
||||
|
||||
selected_page = subprocess.check_output(
|
||||
["dmenu", "-i", "-p", "Post"],
|
||||
input="\n".join(titles.keys()), universal_newlines=True
|
||||
)
|
||||
link = titles[selected_page.strip()]
|
||||
|
||||
subprocess.check_output(["xsel", "-bi"], input=link, universal_newlines=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
11
home/.local/bin/scripts/gui/prompt/menuman
Executable file
11
home/.local/bin/scripts/gui/prompt/menuman
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Menu prompt to easily search for a man page to open
|
||||
|
||||
page="$(apropos --long "$1" | menuprompt --choose -p "Pick man page" | awk '{print $2, $1}' | tr -d '()')"
|
||||
|
||||
if tty -s; then
|
||||
man "$page"
|
||||
else
|
||||
$TERMINAL -e man "$page"
|
||||
fi
|
20
home/.local/bin/scripts/gui/prompt/menupass
Executable file
20
home/.local/bin/scripts/gui/prompt/menupass
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This script is the a wrapper around menuprompt automatically passing options
|
||||
# to prompt for a password. This is necessary for SUDO_ASKPASS variable, as it
|
||||
# expects a path to a single executable.
|
||||
#
|
||||
# SUDO_ASKPASS is an environment variable that can be set to a path of an executable,
|
||||
# which will run if sudo can't obtain password from terminal (stdin), i.e. running in
|
||||
# a graphical application. This script will use menuprompt to obtain the plaintext
|
||||
# password and which sudo will then read and verify.
|
||||
#
|
||||
# This is as safe as any other SUDO_ASKPASS program, and it needs to return the final
|
||||
# password in plaintext for sudo to read it, however pipes retain the same filesystem user
|
||||
# permissions and shouldn't be accessible to other non-priviledged users, so it's not
|
||||
# a huge security issue, but know that it will deal with plaintext passwords.
|
||||
#
|
||||
# You can test whether this worked by running sudo -A echo "test", making sudo use the
|
||||
# SUDO_ASKPASS program to obtain the password.
|
||||
|
||||
menuprompt -P -p "$1" 2>/dev/null
|
338
home/.local/bin/scripts/gui/prompt/menuprompt
Executable file
338
home/.local/bin/scripts/gui/prompt/menuprompt
Executable file
|
@ -0,0 +1,338 @@
|
|||
#!/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
|
36
home/.local/bin/scripts/gui/prompt/menutodo
Normal file
36
home/.local/bin/scripts/gui/prompt/menutodo
Normal file
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
# This is inspired by dmenu's todo script made by suckless
|
||||
#
|
||||
# Manage TODO tasks in dmenu by writing them, remove by selecting
|
||||
# an existing entry
|
||||
#
|
||||
# Configurable variables
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
FILE="${XDG_DATA_HOME:-$HOME/.local/share}/todos"
|
||||
PROMPT="Add/delete a task: "
|
||||
|
||||
# Logic
|
||||
# ---------------------------------------------------------------------
|
||||
mkdir -p "$(dirname $FILE)"
|
||||
touch "$FILE"
|
||||
|
||||
height=$(wc -l "$FILE" | awk '{print $1}')
|
||||
|
||||
# Run dmenu and keep restarting it until it returns an empty output
|
||||
cmd=$(dmenu -l "$height" -p "$PROMPT" "$@" < "$FILE")
|
||||
while [ -n "$cmd" ]; do
|
||||
# If the output matched an existing TODO, remove it
|
||||
if grep -q "^$cmd\$" "$FILE"; then
|
||||
grep -v "^$cmd\$" "$FILE" > "$FILE.$$"
|
||||
mv "$FILE.$$" "$FILE"
|
||||
height=$(( height - 1 ))
|
||||
# If the output didn't match an existing TODO, it's a new one, add it
|
||||
else
|
||||
echo "$cmd" >> "$FILE"
|
||||
height=$(( height + 1 ))
|
||||
fi
|
||||
|
||||
# Keep restarting until empty output
|
||||
cmd=$(dmenu -l "$height" -p "$PROMPT" "$@" < "$FILE")
|
||||
done
|
Loading…
Add table
Add a link
Reference in a new issue