From d947956270b092df10637bb3531441caca698b86 Mon Sep 17 00:00:00 2001 From: Suleyman Farajli Date: Tue, 25 Nov 2025 18:47:27 +0400 Subject: feat: new api for scripts --- scripts/cli/noc | 15 +++- scripts/cli/nospac | 11 +-- scripts/cli/sdev | 80 --------------------- scripts/cli/slight | 27 +++---- scripts/cli/svol | 30 +++----- scripts/daemons/bat-checkd | 16 +++++ scripts/daemons/mail-syncd | 6 ++ scripts/daemons/notifd | 35 +++++++++ scripts/gui/br | 32 ++++----- scripts/gui/nsend | 56 +++++++++------ scripts/gui/sclip | 69 +++++++++--------- scripts/gui/sdev | 49 +++++++++++++ scripts/gui/shot | 37 +++++----- scripts/gui/sslock | 67 +++++++----------- scripts/gui/swall | 56 +++++++-------- scripts/lib/lib_common.sh | 134 +++++++++++++++++++++++++++++++++++ scripts/lib/lib_handle.sh | 140 ++++++++++++++++++++++++++++++++++++ scripts/slib | 172 --------------------------------------------- 18 files changed, 567 insertions(+), 465 deletions(-) delete mode 100755 scripts/cli/sdev create mode 100755 scripts/daemons/bat-checkd create mode 100755 scripts/daemons/mail-syncd create mode 100755 scripts/daemons/notifd create mode 100755 scripts/gui/sdev create mode 100644 scripts/lib/lib_common.sh create mode 100644 scripts/lib/lib_handle.sh delete mode 100755 scripts/slib diff --git a/scripts/cli/noc b/scripts/cli/noc index 575566c..63bee86 100755 --- a/scripts/cli/noc +++ b/scripts/cli/noc @@ -1,6 +1,19 @@ #!/bin/sh -# Remove empty lines and all the comments starting with "#" +. "lib_common.sh" + +help() { +cat << EOF +${0}: Remove empty lines and all the comments starting with '#'. +options: + -h Print this message and exit +EOF +exit 0 +} + +[ "${1}" = "-h" ] && help + +[ ${#} -gt 0 ] && invalid_use for file in "${@}"; do sed -i "s/\s*#.*//g; /^$/ d" "${file}" diff --git a/scripts/cli/nospac b/scripts/cli/nospac index 6ba466a..7ee5b36 100755 --- a/scripts/cli/nospac +++ b/scripts/cli/nospac @@ -1,5 +1,7 @@ #!/bin/sh +. "lib_common.sh" + help() { cat << EOF ${0}: Replace all the spaces in file and directory @@ -7,13 +9,12 @@ names with "_" in in the current directory. options: -h Print this message and exit EOF +exit 0 } -if [ ${#} -gt 0 ]; then - [ ${#} = 1 ] && [ "${1}" = "-h" ] && help && exit 0 - printf "%s: Invalid usage\nTry '%s -h' for help.\n" "${0}" "${0}" - exit 1 -fi +[ "${1}" = "-h" ] && help + +[ ${#} -gt 0 ] && invalid_use for file in ./*; do newfile=$(echo "${file}" | tr ' ' '_') diff --git a/scripts/cli/sdev b/scripts/cli/sdev deleted file mode 100755 index c227771..0000000 --- a/scripts/cli/sdev +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/sh - -. slib - -help() { -cat << EOF -${0}: Disable and Enable devices -options: - -e [dev] Enable dev - -d [dev] Disable dev - -t [dev] Toggle dev - -l List devices - -h Print this message and exit - -Note: Nondescriptive inputs may disable/enable unwanted devices. -EOF - -exit 0 -} - -get_id() { - if ! dev=$(xinput list --name-only | grep -i -m1 "${1}" ); then - err "Couldn't get device" - fi - id="${dev#∼ }" -} - -xenable() { - echo "${id}" - - if ! xinput enable "${1}" > /dev/null 2>&1; then - err "Failed to enable dev." - else - exit 0 - fi -} - -xdisable() { - echo "${id}" - - if ! xinput disable "${1}" > /dev/null 2>&1; then - err "Failed to disable dev." - else - exit 0 - fi -} - -check_program "xinput" - -while getopts "e:d:t:lh" option; do - case "${option}" in - e) - get_id "${OPTARG}" - xenable "${id}" - ;; - d) - get_id "${OPTARG}" - xdisable "${id}" - ;; - t) - get_id "${OPTARG}" - - [ "${id}" = "${dev}" ] && xdisable "${id}" - xenable "${id}" - ;; - l) - if ! xinput list 2>/dev/null; then - err "Listing Failed" - else - exit 0 - fi - ;; - h) help ;; - - *) invalid_use -h ;; - - esac -done - -invalid_use diff --git a/scripts/cli/slight b/scripts/cli/slight index 54edbcd..4efbf19 100755 --- a/scripts/cli/slight +++ b/scripts/cli/slight @@ -1,6 +1,7 @@ #!/bin/sh -. slib +. "lib_common.sh" +. "lib_handle.sh" help() { cat << EOF @@ -14,34 +15,22 @@ options: NOTE: Script interprets values as percentages EOF - exit 0 } -check_program "brightnessctl" - +brightness_handle check_program [ ${#} != 1 ] && [ ${#} != 2 ] && invalid_use while getopts "i:d:s:ph" option; do case "${option}" in - i) run --reload-status "brightnessctl set +${OPTARG}%" ;; - - d) run --reload-status "brightnessctl set ${OPTARG}-%" ;; - - s) run --reload-status "brightnessctl set ${OPTARG}%" ;; - - p) - if ! echo $(( ($(brightnessctl g) * 100) / $(brightnessctl m) )); then - err "Failed to get current brightness" - else - exit 0 - fi - ;; + i) run --reload-status "brightness_handle up ${OPTARG}" ;; + d) run --reload-status "brightness_handle down ${OPTARG}" ;; + s) run --reload-status "brightness_handle set ${OPTARG}" ;; + t) run --reload-status "brightness_handle toggle" ;; + p) brightness_handle get-current ;; h) help ;; *) invalid_use -h ;; - esac done - invalid_use diff --git a/scripts/cli/svol b/scripts/cli/svol index 0488633..5ce6848 100755 --- a/scripts/cli/svol +++ b/scripts/cli/svol @@ -1,10 +1,11 @@ #!/bin/sh -. slib +. "lib_common.sh" +. "lib_handle.sh" help() { cat << EOF -${0}: Wrapper script to change volume +${0}: Change volume options: -i [Vol] Increase volume by Vol -d [Vol] Decrease volume by Vol @@ -13,37 +14,24 @@ options: -t Toggle between mute and unmute -h Print this message and exit EOF - exit 0 } -check_program "pactl" "pulseaudio must be installed" +volume_handle check_program [ $# != 1 ] && [ $# != 2 ] && invalid_use while getopts "i:d:s:pth" option; do case "${option}" in - i) run --reload-status "pactl set-sink-volume @DEFAULT_SINK@ +${OPTARG}%" ;; - - d) run --reload-status "pactl set-sink-volume @DEFAULT_SINK@ -${OPTARG}%" ;; - - s) run --reload-status "pactl set-sink-volume @DEFAULT_SINK@ ${OPTARG}%" ;; - - t) run --reload-status "pactl set-sink-mute @DEFAULT_SINK@ toggle" ;; - - p) - if ! pactl get-sink-volume @DEFAULT_SINK@ \ - | sed -e 's,.* \([0-9][0-9]*\)%.*,\1,' | head -n1 2>/dev/null; then - err "Failed to get current volume" - else - exit 0 - fi - ;; + i) run --reload-status "volume_handle up ${OPTARG}" ;; + d) run --reload-status "volume_handle down ${OPTARG}" ;; + s) run --reload-status "volume_handle set ${OPTARG}" ;; + t) run --reload-status "volume_handle toggle" ;; + p) volume_handle get-current ;; h) help ;; *) invalid_use -h ;; esac done - invalid_use diff --git a/scripts/daemons/bat-checkd b/scripts/daemons/bat-checkd new file mode 100755 index 0000000..2c272b8 --- /dev/null +++ b/scripts/daemons/bat-checkd @@ -0,0 +1,16 @@ +#!/bin/sh + +# FIXME: hardcoded battery +BAT="/sys/class/power_supply/BAT0" + +while true; do + percent=$(cat "${BAT}/capacity") + status=$(cat "${BAT}/status") + + # Only notify if battery is low AND not charging + if [ "${status}" = "Discharging" ] && [ "${percent}" -lt 7 ]; then + nsend -s "Battery" "Critically low: ${percent}%" + fi + + sleep 120 +done diff --git a/scripts/daemons/mail-syncd b/scripts/daemons/mail-syncd new file mode 100755 index 0000000..e317ace --- /dev/null +++ b/scripts/daemons/mail-syncd @@ -0,0 +1,6 @@ +#!/bin/sh + +while true; do + mbsync -a + sleep 300 +done diff --git a/scripts/daemons/notifd b/scripts/daemons/notifd new file mode 100755 index 0000000..9ad5697 --- /dev/null +++ b/scripts/daemons/notifd @@ -0,0 +1,35 @@ +#!/bin/sh + +. "lib_handle.sh" + +FIFO=/tmp/noti.fifo +FILE=/tmp/noti.txt + +[ -p "${FIFO}" ] || mkfifo "${FIFO}" + +exec 3<"${FIFO}" +exec 4>"${FIFO}" + +tpid="" + +cancel() { + [ -n "${tpid}" ] && kill "${tpid}" 2>/dev/null + tpid="" +} + +while read n <&3; do + [ "${n}" = "-1" ] && { cancel; continue; } + + case "${n}" in ''|*[!0-9]*) continue ;; esac + + cancel + + [ "${n}" -gt 0 ] || { :>"${FILE}"; notify_handle reload; continue; } + + ( + sleep "${n}" + :>"${FILE}" + notify_handle reload + ) & + tpid=$! +done diff --git a/scripts/gui/br b/scripts/gui/br index 474b1b1..428e24b 100755 --- a/scripts/gui/br +++ b/scripts/gui/br @@ -1,6 +1,6 @@ #!/bin/sh -. slib +. "lib_common.sh" help() { cat << EOF @@ -9,33 +9,25 @@ options: [link] Open link -h Print this message and exit -NOTE: bookmarks file is located at ~/.config/sites/bookmarks +NOTE: bookmarks file is located at ~/.config/sites/bookmarks.txt EOF - exit 0 } -alias dmenucmd="dmenu -bw 1 -c -g 1 -l 25" - -[ -z "${BROWSER}" ] && browser="qutebrowser" || browser="${BROWSER}" +[ -z "${BROWSER}" ] && BROWSER="firefox" if [ "${#}" -eq 0 ]; then + bookmark_path="${XDG_CONFIG_HOME:-$HOME/.config}/sites/bookmarks.txt" + [ -e "${bookmark_path}" ] || err "Couldn't find bookmarks file" - link_file="${XDG_CONFIG_HOME:-$HOME/.config}/sites/bookmarks.txt" - - check_program "dmenu" + . "lib_handle.sh" - [ -z "${XDG_HOME_CONFIG}" ] || link_file="${XDG_HOME_CONFIG}"/sites/bookmarks.txt - [ -e "${link_file}" ] || err "Couldn't find bookmarks file" + link=$(< "${bookmark_path}" menu_handle center) - link=$(< "${link_file}" dmenucmd) - - [ -z "${link}" ] || "${browser}" "${link}" + [ -z "${link}" ] || "${BROWSER}" "${link}" +fi -elif [ "${#}" -eq 1 ]; then - [ "${1}" = "-h" ] && help +[ "${#}" -ne 1 ] && invalid_use +[ "${1}" = "-h" ] && help - "${browser}" "${1}" -else - invalid_use -fi +"${BROWSER}" "${1}" diff --git a/scripts/gui/nsend b/scripts/gui/nsend index 1bfc1f4..e1cbd78 100755 --- a/scripts/gui/nsend +++ b/scripts/gui/nsend @@ -1,34 +1,44 @@ #!/bin/sh -. slib +. "lib_common.sh" +. "lib_handle.sh" help() { cat << EOF -${0}: Wrapper script to send notifications +${argv0}: Send notifications options: - -s [Name] [Msg] Send Msg with Name - -s [Msg] Send Msg Without Name + -s [Msg] Send Msg + -t [Time] Disappear after Time (in second) -h Print this message and exit -EOF +Note: time must be between 0 and 300, +by default time is set to unlimited. +EOF exit 0 } -case "${#}" in -1) - [ "${1}" = "-h" ] && help - invalid_use -;; -2) - [ "${1}" = "-s" ] || invalid_use - notification_string="${2}" -;; -3) - [ "${1}" = "-s" ] || invalid_use - notification_string="${2}: ${3}" -;; -*) invalid_use ;; -esac - - -send_notification "${notification_string}" +[ "${#}" -eq 0 ] && invalid_use + +time=-1 +while getopts "s:t:h" option; do + case "${option}" in + s) msg="${OPTARG}" ;; + t) + time="${OPTARG}" + + case "${time}" in + ''|*[!0-9]*) err "Not a number: ${time}" ;; + esac + + [ "${time}" -gt 300 ] && err "Out of range: ${time}" + ;; + h) help ;; + + *) invalid_use -h ;; + esac +done + +shift $((OPTIND - 1)) +[ ${#} != 0 ] && invalid_use + +notify_handle send "${time}" "${msg}" diff --git a/scripts/gui/sclip b/scripts/gui/sclip index 4854aa1..56ea57f 100755 --- a/scripts/gui/sclip +++ b/scripts/gui/sclip @@ -1,71 +1,72 @@ #!/bin/sh -. slib +. "lib_common.sh" +. "lib_handle.sh" help() { cat << EOF -${0}: print the clipboard content +${progname}: print the clipboard content options: - -h Print this message and exit -s Do not send notifications -c [text] Copy text to clipboard -f [file] Copy content of file into clipboard -i Copy standart input into clipboard + -h Print this message and exit EOF - exit 0 } -check_program "xclip" +clipboard_handle check_program + +if [ "${#}" -eq 0 ]; then + clipboard_handle get-clipboard + exit $? +fi -i_flg=0 +read_stdin=0 silent=0 while getopts "f:c:sih" option; do case "${option}" in f) filepath="${OPTARG}" ;; - c) input="${OPTARG}" ;; - s) silent=1 ;; - - i) i_flg=1 ;; - + i) read_stdin=1 ;; h) help ;; *) invalid_use -h ;; esac done - shift $((OPTIND - 1)) [ "${#}" -ne 0 ] && invalid_use if [ -n "${filepath}" ]; then - mime_type=$(file --mime-type -b "${filepath}") - - if [ "${silent}" -eq 1 ]; then - run "xclip -selection clipboard -t ${mime_type} -i ${filepath}" - fi + mime_type=$(file_handle get-mime_type "$filepath") || { + err "Failed to detect mime type"; exit 1;} - run --success-notify "${filepath} copied" \ - --failure-notify "${filepath} failed to copy" \ - "xclip -selection clipboard -t ${mime_type} -i ${filepath}" -fi + clipboard_handle file "${mime_type}" "${filepath}" + status=$? -[ "${i_flg}" -eq 1 ] && input=$(cat) +else + [ -z "$input" ] && [ "$read_stdin" -eq 1 ] && input=$(cat) + [ -z "$input" ] && err "Empty input" -if [ -n "${input}" ]; then - # Don't use `run` since pipes are used - printf '%s' "${input}" | xclip -selection clipboard + printf '%s' "${input}" | clipboard_handle text + status=$? +fi - if [ $? -eq 0 ]; then - [ "${silent}" -eq 0 ] && send_notification "${argv0}:" "'${input}' copied" - exit 0 +if [ "${silent}" -eq 0 ]; then + if [ "${status}" -eq 0 ]; then + if [ -n "${filepath}" ]; then + notify_handle "Copied file: ${filepath}" + else + notify_handle "Copied text" + fi + else + if [ -n "${filepath}" ]; then + notify_handle "Failed to copy file: ${filepath}" + else + notify_handle "Failed to copy text" + fi fi - - [ "${silent}" -eq 0 ] && send_notification "${argv0}:" "'${input}' failed to copy" - - exit 1 fi - -run "xclip -o -selection clipboard" diff --git a/scripts/gui/sdev b/scripts/gui/sdev new file mode 100755 index 0000000..340bdb1 --- /dev/null +++ b/scripts/gui/sdev @@ -0,0 +1,49 @@ +#!/bin/sh + +. "lib_common.sh" +. "lib_handle.sh" + +help() { +cat << EOF +${0}: Disable and Enable devices +options: + -e [dev] Enable dev + -d [dev] Disable dev + -t [dev] Toggle dev + -l List devices + -h Print this message and exit + +Note: Nondescriptive inputs may disable/enable unwanted devices. +EOF +exit 0 +} + +input_device_handle check_program + +get_id() { + id=$(input_device_handle get-id "$1") || exit 1 + printf '%s\n' "$id" +} + +while getopts "e:d:t:lh" option; do + case "${option}" in + e) run "input_device_handle enable $(get_id ${OPTARG})" ;; + d) run "input_device_handle disable $(get_id ${OPTARG})" ;; + t) + id=$(get_id ${OPTARG}) + if input_device_handle is_enabled "${id}"; then + input_device_handle disable "${id}" + else + input_device_handle enable "${id}" + fi + exit $? + ;; + l) run "input_device_handle list" ;; + h) help ;; + + *) invalid_use -h ;; + + esac +done + +invalid_use diff --git a/scripts/gui/shot b/scripts/gui/shot index 712f60b..a133c16 100755 --- a/scripts/gui/shot +++ b/scripts/gui/shot @@ -1,6 +1,7 @@ #!/bin/sh -. slib +. "lib_common.sh" +. "lib_handle.sh" help() { cat << EOF @@ -8,29 +9,26 @@ ${0}: Wrapper script to take screenshots options: -d [File] Write to File -s Select the area with the cursor + -w Select the area with the cursor -h Print this message and exit -NOTE: save directory is ~/pics/screenshot +NOTE: save directory is ~/media/photo/screenshot EOF - exit 0 } -check_program "scrot" - +screenshot_handle check_program default_dir="${XDG_SCREENSHOT_DIR:-${HOME}/media/photo/screenshot}" -s_flg="" -while getopts "d:sh" option; do +mode="fullscreen" +while getopts "d:swh" option; do case "${option}" in - d) input="${OPTARG}" ;; - - s) s_flg="1" ;; - - h) help ;; - - *) invalid_use -h ;; + d) input="${OPTARG}" ;; + s) mode="select" ;; + w) mode="focused-window" ;; + h) help ;; + *) invalid_use -h ;; esac done @@ -48,12 +46,13 @@ outfile="${input}" [ -d "${input}" ] && outfile="${input}/$(date '+%b%d::%H%M%S').png" if [ ! -e "$(dirname "${outfile}")" ]; then - mkdir -p "$(dirname "${outfile}")" > /dev/null 2>&1 \ - || err "Failed to create directory" + mkdir -p "$(dirname "${outfile}")" || + err "Failed to create directory ${outfile}" fi -if [ "${s_flg}" = "1" ]; then - run --reload-compositor "scrot -zs ${outfile}" "${outfile}" +if [ "${mode}" = "select" ]; then + run --no-exit --reload-compositor \ + "screenshot_handle ${mode} ${outfile}" && echo "${outfile}" else - run "scrot -z ${outfile}" "${outfile}" + screenshot_handle "${mode}" "${outfile}" && echo "${outfile}" fi diff --git a/scripts/gui/sslock b/scripts/gui/sslock index 26ba2cf..8a3284e 100755 --- a/scripts/gui/sslock +++ b/scripts/gui/sslock @@ -1,59 +1,44 @@ #!/bin/sh -. slib +. "lib_common.sh" +. "lib_handle.sh" help() { cat << EOF -${0}: Wrapper script to take screenshots +${0}: Lock screen options: - -n Black screen - -b Blur - -c Take the current state of the desktop + -n Black screen lock -h Print this message and exit EOF - exit 0 } -check_program "slock" - -blur=0 -set_current_state=0 -current_wallpaper="${XDG_CACHE_HOME:-$HOME/.cache}/wallpaper/current" -while getopts "bcnh" option; do - case "${option}" in - b) - check_program "mogrify" "imagemagick must be installed" - blur=1 ;; - - c) - check_program "scrot" - set_current_state=1 - ;; - n) - slock - exit 0;; - - h) help ;; - - *) invalid_use -h ;; +trap 'rm -f "${lock_image}"' EXIT INT TERM HUP - esac -done - -shift $((OPTIND - 1)) +if [ "${#}" -eq 1 ]; then + [ "${1}" = "-h" ] && help + [ "${1}" = "-n" ] || invalid_use + screenlock_handle + exit 0 +fi -[ "${#}" -eq 0 ] || invalid_use +[ "${#}" -ne 0 ] && invalid_use -background="/tmp/$(date '+%b%d::%H%M%S').png" +mode=$(printf "Blank\nBlur\nWallpaper" | menu_handle default) +if [ "${mode}" = "Blank" ]; then + screenlock_handle + exit 0 +fi -if [ "$set_current_state" -eq 0 ]; then - # Avoid distorting the wallpaper when calling `mogrify` - cp "${current_wallpaper}" "${background}" -else - scrot -z "${background}" +lock_image=$(get_random_filename .png) +if [ "${mode}" = "Blur" ]; then + screenshot_handle fullscreen "${lock_image}" + image_handle blur "${lock_image}" fi -[ "${blur}" -eq 1 ] && mogrify -blur 0x8 "${background}" +if [ "${mode}" = "Wallpaper" ]; then + current_wallpaper="${XDG_CACHE_HOME:-$HOME/.cache}/wallpaper/current" + cp "${current_wallpaper}" "${lock_image}" +fi -slock -f "${background}" +screenlock_handle "${lock_image}" diff --git a/scripts/gui/swall b/scripts/gui/swall index a225481..5f22ec8 100755 --- a/scripts/gui/swall +++ b/scripts/gui/swall @@ -1,6 +1,7 @@ #!/bin/sh -. slib +. "lib_common.sh" +. "lib_handle.sh" help() { cat << EOF @@ -12,58 +13,53 @@ options: NOTE: default directory is ~/.local/share/wallpapers A symlink at ~/.cache/wallpapers/current is created pointing to the selected wallpaper. - EOF - exit 0 } -check_program "xwallpaper" - +wallpaper_handle check_program SYMLINK="${XDG_CACHE_HOME:-$HOME/.cache}/wallpaper/current" -if [ "${#}" = 0 ]; then - input="${XDG_DATA_HOME:-$HOME/.local/share}/wallpapers" -elif [ "${#}" != 1 ] && [ "${#}" != 2 ]; then - invalid_use -fi +[ "${#}" -eq 0 ] && input="${XDG_DATA_HOME:-$HOME/.local/share}/wallpapers" +[ "${#}" -gt 2 ] && invalid_use -while getopts "hcld:" option; do +while getopts "cd:h" option; do case "${option}" in c) - unlink "${SYMLINK}" - run --reload-compositor "xwallpaper --clear" ;; - + rm -f "${SYMLINK}" + run --reload-compositor "wallpaper_handle clear" + ;; d) input="${OPTARG}" ;; - h) help ;; *) invalid_use -h ;; - esac done - shift $((OPTIND - 1)) [ "${#}" != 0 ] && invalid_use if [ -n "${input}" ]; then - case $(file -L -b --mime-type "${input}") in - image/*) image="${input}" ;; - - inode/directory) waldir="${input}" ;; - - *) err "Couldn't read given file" ;; - esac + if [ -d "${input}" ]; then + waldir="${input}" + elif [ -f "${input}" ]; then + case "${input}" in + *.jpg|*.JPG|*.jpeg|*.JPEG|*.png|*.PNG) image="${input}" ;; + *) err "Unsupported file type" ;; + esac + else + err "Couldn't read given file" + fi fi -if [ -n "${waldir}" ]; then - image=$(find "${waldir}" -iregex '.*.\(jpg\|jpeg\|png\)' 2>/dev/null \ - | shuf -n 1 ) +if [ -d "$waldir" ]; then + image=$( + find "$waldir" \( -name '*.jpg' -o -name '*.jpeg' -o -name '*.png' \) 2>/dev/null | + awk 'BEGIN{srand()} {a[NR]=$0} END{if(NR>0) print a[int(rand()*NR)+1]}' + ) fi [ -z "${image}" ] && err "No image file found" -mkdir -p "$(dirname "${SYMLINK}")" && \ - ln -sf "${image}" "${SYMLINK}" && \ - run "xwallpaper --zoom ${image}" "${image}" +mkdir -p "$(dirname "${SYMLINK}")" && ln -sf "${image}" "${SYMLINK}" +wallpaper_handle set "${image}" && echo "${image}" diff --git a/scripts/lib/lib_common.sh b/scripts/lib/lib_common.sh new file mode 100644 index 0000000..58ee5ba --- /dev/null +++ b/scripts/lib/lib_common.sh @@ -0,0 +1,134 @@ +#!/bin/sh + +progname=$(basename "${0}") + +# @FUNCTION: err +# @USAGE: [-x] ... +# @DESCRIPTION: +# Print given messages to stderr line by line and exit with status 1. +# +# If "-x" is specified, suppress the program prefix (`program: `) +# from the output. Otherwise, the first line is prefixed with "program: ". +# +# This function is intended for fatal errors; it always exits the script. +# @EXAMPLE: +# err "Invalid usage" "Try '${progname} -h' for help." +err() { + if [ "${1}" != "-x" ]; then + printf "%s: " "${progname}" + else + shift + fi + + for line in "${@}"; do + echo "${line}" >&2 + done + exit 1 +} + +# @FUNCTION: invalid_use +# USAGE: [-h] +# @DESCRIPTION: +# Output a usage error message. If `-h` is not specified output: +# ": Invalid usage " +# "Try 'program -h' for help." +# else output only: +# "Try 'program -h' for help." + +invalid_use() { + [ "${1}" = "-h" ] && err -x "Try '${progname} -h' for help." + err "Invalid usage" "Try '${progname} -h' for help." +} + +# @FUNCTION: check_program +# USAGE: [error-msg] +# @DESCRIPTION: +# Check if command exists on the system. +# If not, print an error message to stderr and exit with status 1. +# The default error message is "`command` must be installed" +# but can optionally be overwritten. +# +# @EXAMPLE: +# Check if pulseaudio is installed. +# +# check_program "pactl" "pulseaudio must be installed" + +check_program() { + command -v "${1}" > /dev/null 2>&1 && return 0 + [ -n "${2}" ] && err "${2}" + err "${1} must be installed" +} + +# @FUNCTION: get_random_filename +# @USAGE: get_random_filename [parentdir] +# @DESCRIPTION: +# Write a random file path to stdout, under parentdir, (if provided else under /tmp) with the extension. +# +# @EXAMPLE: +# Get a filepath in the `/var` directory with the extension `.png` +# +# get_random_filename .png /var + +get_random_filename() { + [ "${#}" -eq 2 ] && parentdir="${2}" || parentdir="/tmp" + + extension="${1}" + + echo "${parentdir}/$(date '+%b%d::%H%M%S')${extension}" +} + +# @FUNCTION: run +# @USAGE: [--reload-status] [--reload-compositor] [--success-notify ] [--failure-notify ] [success-msg] [failure-msg] +# @DESCRIPTION: +# Safely execute a simple shell command, print optional success/failure messages, +# and optionally reload the status bar or restart the compositor. +# +# Success messages are printed to stdout; failure messages are printed to stderr. +# Optional desktop notifications can be sent using --success-notify and --failure-notify. +# Command output is not suppressed. +# +# Options: +# --reload-status Reload the status bar (via `slreload`). +# --reload-compositor Restart the compositor (kills and restarts `picom`). +# --success-notify Send a desktop notification on success. +# --failure-notify Send a desktop notification on failure. +# +# Restrictions: +# - Does NOT use `eval`; only simple commands and arguments are supported. +# - Shell operators like `&&`, `||`, `|`, `>`, `>>`, etc. will NOT work. +# - This design prevents unintended execution and makes the function safe in scripts. +# +# Return value: +# Exits with 0 if the command succeeds, 1 otherwise. +# +# @EXAMPLES: +# # Run xwallpaper and print the image path on success +# run "xwallpaper --zoom ${image}" "${image}" +# +# # Restart compositor and show a success message +# run --reload-compositor "xwallpaper --zoom ${image}" "Wallpaper updated" "Wallpaper failed" +# +# # With notification hooks +# run --success-notify "Wallpaper set" \ +# --failure-notify "Wallpaper failed" \ +# "xwallpaper --zoom ${image}" "Wallpaper updated" "Wallpaper failed" + +run() { + no_exit=0 + reload_status=0 + reload_compositor=0 + + [ "${1}" = "--no-exit" ] && no_exit=1 && shift + [ "${1}" = "--reload-status" ] && reload_status=1 && shift + [ "${1}" = "--reload-compositor" ] && reload_compositor=1 && shift + + trap ' + [ "${reload_status}" -eq 1 ] && status_handle reload + [ "${reload_compositor}" -eq 1 ] && compositor_handle start + ' EXIT + + [ "${reload_compositor}" -eq 1 ] && compositor_handle stop + + eval "${@}" + [ "${no_exit}" -eq 1 ] || exit "${?}" +} diff --git a/scripts/lib/lib_handle.sh b/scripts/lib/lib_handle.sh new file mode 100644 index 0000000..d4524d9 --- /dev/null +++ b/scripts/lib/lib_handle.sh @@ -0,0 +1,140 @@ +#!/bin/sh +. "lib_common.sh" + +volume_handle() { + case "${1}" in + "up") pactl set-sink-volume @DEFAULT_SINK@ +"${2}"% ;; + "down") pactl set-sink-volume @DEFAULT_SINK@ -"${2}"% ;; + "set") pactl set-sink-volume @DEFAULT_SINK@ "${2}"% ;; + "toggle") pactl set-sink-mute @DEFAULT_SINK@ toggle;; + "get-current") + pactl get-sink-volume @DEFAULT_SINK@ | + sed -n 's/.* \([0-9][0-9]*%\).*/\1/p' + ;; + "check_program") + check_program "pactl" "pulseaudio must be installed" ;; + + esac +} + +brightness_handle() { + case "${1}" in + "up") brightnessctl set +"${2}"% ;; + "down") brightnessctl set "${2}"-% ;; + "set") brightnessctl set "${2}"% ;; + "get-current") + printf '%s\n' \ + $(( ($(brightnessctl g) * 100) / $(brightnessctl m) )) + ;; + "check_program") check_program "brightnessctl" + esac +} + +screenshot_handle() { + case "${1}" in + "fullscreen") flags="-z" ;; + "select") flags="-zs" ;; + "focused-window") flags="-zu" ;; + "check_program") + check_program "scrot" + return + ;; + esac + shift + scrot "${flags}" "${1}" +} + +wallpaper_handle() { + case "${1}" in + "clear") xwallpaper --clear ;; + "set") xwallpaper --zoom "${2}" ;; + "check_program") check_program "xwallpaper" ;; + esac + +} + +screenlock_handle() { + if [ "${#}" -eq 0 ]; then + slock + else + slock -f "${1}" + fi +} + +input_device_handle() { + case "$1" in + "enable") xinput enable "${2}";; + "disable") xinput disable "${2}";; + "list") xinput list ;; + "get-id") + dev=$(xinput list --name-only | grep -i -m1 "${1}" ) || return 1 + printf "${dev#∼ }" + ;; + "is_enabled") + xinput list-props "${2}" | grep -q "Device Enabled.*1$" + return $? + ;; + "check_program") check_program "xclip" ;; + esac +} + +clipboard_handle() { + case "${1}" in + "text") xclip -selection clipboard ;; + "file") xclip -selection clipboard -t "${2}" -i "${3}" ;; + "get-clipboard") xclip -o -selection clipboard ;; + esac +} + +image_handle() { + case "${1}" in + "blur") mogrify -blur 0x8 "${2}" ;; + "check_program") + check_program "mogrify" "imagemagick must be installed" + ;; + esac +} + +file_handle() { + file=$2 + case $1 in + get-mime_type) + m=$(file --mime-type -b "$file" 2>/dev/null) && { echo "$m"; return; } + m=$(file -I "$file" 2>/dev/null) && { m=${m#*: }; m=${m%%;*}; echo "$m"; return; } + m=$(file -i "$file" 2>/dev/null) && { m=${m#*: }; m=${m%%;*}; echo "$m"; return; } + return 1 + ;; + esac +} + +notify_handle() { + case "${1}" in + "reload") xsetroot -name "fsignal:1" ;; + "send") + echo "${2}" > /tmp/noti.fifo + shift 2 + printf "%s\n" "$*" > /tmp/noti.txt + xsetroot -name "fsignal:1" + ;; + esac +} + +compositor_handle() { + case "${1}" in + "start") pgrep -x picom >/dev/null || picom -b & ;; + "stop") pgrep -x picom >/dev/null && pkill -x picom ;; + esac +} + +status_handle() { + [ "${1}" = "reload" ] && slreload +} + +menu_handle() { + case "${1}" in + "default") flags="" ;; + "center") flags="-bw 1 -c -g 1 -l 25" ;; + esac + shift + dmenu ${flags} +} diff --git a/scripts/slib b/scripts/slib deleted file mode 100755 index 0df4a50..0000000 --- a/scripts/slib +++ /dev/null @@ -1,172 +0,0 @@ -#!/bin/sh - -argv0=$(basename "${0}") - -# @FUNCTION: err -# @USAGE: [-x] ... -# @DESCRIPTION: -# Print given messages to stderr line by line and exit with status 1. -# -# If "-x" is specified, suppress the program prefix (`program: `) -# from the output. Otherwise, the first line is prefixed with "program: ". -# -# This function is intended for fatal errors; it always exits the script. -# @EXAMPLE: -# err "Invalid usage" "Try '${argv0} -h' for help." -err() { - if [ "${1}" != "-x" ]; then - printf "%s: " "${argv0}" - else - shift - fi - - for line in "${@}"; do - echo "${line}" >&2 - done - exit 1 -} - -# @FUNCTION: invalid_use -# USAGE: [-h] -# @DESCRIPTION: -# Output a usage error message. If `-h` is not specified output: -# ": Invalid usage " -# "Try 'program -h' for help." -# else output only: -# "Try 'program -h' for help." - -invalid_use() { - [ "${1}" = "-h" ] && err -x "Try '${argv0} -h' for help." - err "Invalid usage" "Try '${argv0} -h' for help." -} - -# @FUNCTION: check_program -# USAGE: [error-msg] -# @DESCRIPTION: -# Check if command exists on the system. -# If not, print an error message to stderr and exit with status 1. -# The default error message is "`command` must be installed" -# but can optionally be overwritten. -# -# @EXAMPLE: -# Check if pulseaudio is installed. -# -# check_program "pactl" "pulseaudio must be installed" - -check_program() { - command -v "${1}" > /dev/null 2>&1 && return 0 - [ -n "${2}" ] && err "${2}" - err "${1} must be installed" -} - -# @FUNCTION: send_notification -# @USAGE: send_notification ... -# @DESCRIPTION: -# Write a notification message to a temporary file and trigger a dwm notification signal. -# The message is written to /tmp/noti.txt. If writing fails, a warning is printed to stderr. -# After writing, a dwm signal is sent using `xsetroot -name "fsignal:1"`. -# -# @EXAMPLE: -# Send a desktop notification with the message "Backup complete". -# -# send_notification "Backup complete" - -send_notification() { - echo "$@" > /tmp/noti.txt || echo "Warning: failed to write to notification file" >&2 - xsetroot -name "fsignal:1" -} - -# @FUNCTION: get_random_filename -# @USAGE: get_random_filename [parentdir] -# @DESCRIPTION: -# Write a random file path to stdout, under parentdir, (if provided else under /tmp) with the extension. -# -# @EXAMPLE: -# Get a filepath in the `/var` directory with the extension `.png` -# -# get_random_filename /var .png - -get_random_filename() { - [ "${#}" -eq 2 ] && parentdir="${2}" || parentdir="/tmp" - - extension="${1}" - - echo "${parentdir}/$(date '+%b%d::%H%M%S')${extension}" -} - -# @FUNCTION: run -# @USAGE: [--reload-status] [--reload-compositor] [--success-notify ] [--failure-notify ] [success-msg] [failure-msg] -# @DESCRIPTION: -# Safely execute a simple shell command, print optional success/failure messages, -# and optionally reload the status bar or restart the compositor. -# -# Success messages are printed to stdout; failure messages are printed to stderr. -# Optional desktop notifications can be sent using --success-notify and --failure-notify. -# Command output is not suppressed. -# -# Options: -# --reload-status Reload the status bar (via `slreload`). -# --reload-compositor Restart the compositor (kills and restarts `picom`). -# --success-notify Send a desktop notification on success. -# --failure-notify Send a desktop notification on failure. -# -# Restrictions: -# - Does NOT use `eval`; only simple commands and arguments are supported. -# - Shell operators like `&&`, `||`, `|`, `>`, `>>`, etc. will NOT work. -# - This design prevents unintended execution and makes the function safe in scripts. -# -# Return value: -# Exits with 0 if the command succeeds, 1 otherwise. -# -# @EXAMPLES: -# # Run xwallpaper and print the image path on success -# run "xwallpaper --zoom ${image}" "${image}" -# -# # Restart compositor and show a success message -# run --reload-compositor "xwallpaper --zoom ${image}" "Wallpaper updated" "Wallpaper failed" -# -# # With notification hooks -# run --success-notify "Wallpaper set" \ -# --failure-notify "Wallpaper failed" \ -# "xwallpaper --zoom ${image}" "Wallpaper updated" "Wallpaper failed" - -run() { - relstat=0 - compstat=0 - exitval=0 - - while [ $# -gt 0 ]; do - case "$1" in - --reload-status) relstat=1 ;; - --reload-compositor) compstat=1 ;; - --success-notify) success_msg="${2}"; shift ;; - --failure-notify) failure_msg="${2}"; shift ;; - *) break; - esac - shift - done - - if [ "${compstat}" -eq 1 ]; then - pgrep -x picom > /dev/null && killall picom - fi - - if ${1}; then - [ -n "${2}" ] && echo "${2}" - [ -n "${success_msg}" ] && send_notification "${argv0}:" "${success_msg}" - else - [ -n "${3}" ] && err "${3}" - [ -n "${failure_msg}" ] && send_notification "${argv0}:" "${failure_msg}" - - exitval=1 - fi - - if [ "${relstat}" -eq 1 ]; then - slreload || echo "Warning: Failed to reload slstatus" >&2 - fi - - if [ "${compstat}" -eq 1 ]; then - picom -b || echo "Warning: Failed to start picom" >&2 - fi - - exit "${exitval}" -} -- cgit v1.2.3