#!/bin/bash # Comfortable Swipe # turn on errors per line set -e DIR="$(dirname "$0")" BASENAME="$(basename "$0")" VERSION="$(cat "$DIR/VERSION" | tr -d '[:space:]')" # note: this will be hardcoded upon install # show help function help { cat < [] -h, --help show this help text -v, --version print the program version Commands: start starts 3/4-finger gesture service stop stops 3/4-finger gesture service restart restarts 3/4-finger gesture service autostart [] toggle to automatically run on startup automatically run on startup (toggleable) buffer parses output of libinput debug-events help shows the help dialog config [...] shows the location of the config file debug logs raw output from input events taken from libinput status checks status of program and autostart Report bugs to https://github.com/Hikari9/comfortable-swipe/issues/new EOF } # start comfortable-swipe # internally pipes debug text to the buffer function start { debug | buffer } # stop running comfortable-swipe commands (except self) function stop { function stop_name { pgrep -f "${1:?}" | fgrep -v $$ | xargs kill -- } stop_name "$BASENAME" stop_name "$(which comfortable-swipe)" stop_name "$(which comfortable-swipe-buffer)" } # restart comfortable swipe function restart { stop > /dev/null 2>&1 # restart the server in the background nohup "$BASENAME" start 0<&- &>/dev/null & } # show debug text # internally just calls libinput debug-events function debug { if command -v libinput-debug-events > /dev/null 2>&1; then local DEBUGEVENTS="libinput-debug-events" else local DEBUGEVENTS="libinput debug-events" fi stdbuf -oL -e0 $DEBUGEVENTS 2> >(fgrep -v 'double tracking') } # parse input from a buffer # internally calls comfortable-swipe-main.cpp, which is # installed as: /usr/local/bin/ comfortable-swipe-buffer function buffer { exec comfortable-swipe-buffer "$@" } # get location of configuration file function config { local CONFIG="$HOME/.config/comfortable-swipe.conf" local KEYS="left3|left4|right3|right4|up3|up4|down3|down4|threshold|mouse3|mouse4" local DEPRECATED="hold3|hold4" local USAGE="Usage: $BASENAME config [get|set|delete|list|keys|path]..." # show path to config function path { echo "$CONFIG" } # get all keys from config, without comments # and show only last among duplicates function list { # dispatch subcommands if [[ $# -eq 0 ]]; then # no options; just show path tac "$CONFIG" |\ sed -E "s/[#;].*//g" |\ egrep '\s+' |\ tr -d ' ' |\ egrep "^($KEYS|$DEPRECATED)=" |\ awk '!a[$1]++' |\ tac |\ sed 's/=/ = /g' else # filter list to match arguments as pattern local pattern="$1" for arg in "${@:2}"; do pattern="$pattern|$arg" done list | egrep "($pattern)[^=]* =" fi } # get a list of all valid keys, one per line function keys { echo "$KEYS" | sed 's/|/\n/g' } # Get a specific key in the config # Usage: comfortable-swipe config get function get { # helper function to print usage function usage { echo "Usage: $BASENAME config get " echo -n "Keys: " echo "$KEYS" | sed 's/|/, /g' } # no next argument: show list if [[ $# -eq 0 ]]; then list exit $? fi local KEY="$1" # check if key is valid if ! [[ "$KEY" =~ ^($KEYS|$DEPRECATED)$ ]]; then echo "'$KEY' is an invalid key" >&2 echo >&2 usage >&2 echo >&2 echo "If you want to filter keys, you can instead try:" >&2 echo >&2 echo " $BASENAME config list $@" echo >&2 exit 1 fi # get key from config file list | fgrep -m1 "$KEY =" | sed -E "s/^$KEY = //" } # Deletes a key in the config function delete { # helper function to print usage function usage { echo "Usage: $BASENAME config delete [...]" } # no next argument: show help if [[ $# -eq 0 ]]; then echo "Key is required!" >&2 # no key; show usage usage >&2 exit 1 fi local DELETE="$(list "$@")" # check if there is something to delete if [[ -z "$DELETE" ]]; then echo "No config entry to delete" >&2 exit 1 else echo "Deleted:" echo "$DELETE" local RESULT="$(egrep -v "^\\s*($(echo "$DELETE" | awk '{print $1}' | paste -s -d '|'))\\s*=" "$CONFIG")" echo "$RESULT" > "$CONFIG" # restart comfortable-swipe restart fi } # Set a specific key in the config # Usage: comfortable-swipe config get function set { # helper function to print usage function usage { echo "Usage: $BASENAME config set [=] " echo -n "Valid keys: " echo "$KEYS" | sed 's/|/, /g' } # no next argument: show help if [[ $# -eq 0 ]]; then echo "Key is required!" >&2 echo >&2 # no key; show usage usage >&2 exit 1 fi # parse key and value option local KEYVALUE="${@:1}" local KEY= local VALUE= case "$KEYVALUE" in *=*) # key has equal sign, we split it KEY="${KEYVALUE%%=*}" VALUE="${KEYVALUE#*=}" ;; *) # default: just get from next arguments combined if [[ $# -eq 1 ]]; then echo "Value is required!" >&2 usage >&2 echo >&2 echo "If you want to set value to blank, explicitly pass a blank string:" >&2 echo >&2 echo " $BASENAME config set $@ \"\"" >&2 echo >&2 echo "Or delete explicitly:" >&2 echo >&2 echo " $BASENAME config delete $1" echo >&2 exit 1 fi KEY="$1" VALUE="${@:2}" ;; esac # trim leading and trailing spaces from key and value KEY="$(echo "$KEY" | awk '{$1=$1};1')" VALUE="$(echo "$VALUE" | awk '{$1=$1};1')" # check if key is valid if ! [[ "$KEY" =~ ^($KEYS|$DEPRECATED)$ ]]; then echo "'$KEY' is an invalid key" >&2 echo >&2 usage exit 1 fi # if the key is present in the config, perform a replace # replace the last value from the config with the given key if list | grep -P "^\Q$KEY =" > /dev/null 2>&1; then # apply sed to keep our formatting # \\1 - keep any kind of indentation # \\2 - keep any comments after the value (with the whitespace instact) local RESULT="$(tac "$CONFIG" |\ sed -E "s/^(\\s*)$KEY\\s*=\\s*(\?!(\\s*[#;]))*(.*)\$/\\1$KEY = $VALUE\\2/1" |\ tac)" # make sure we separate piping from outputing echo "$RESULT" > "$CONFIG" else # otherwise, key is not present so we simply append echo "$KEY = $VALUE" >> "$CONFIG" fi # show newly set value echo "$KEY = $(get "$KEY")" restart } # dispatch subcommands if [[ $# -eq 0 ]]; then # no options; just show path path elif declare -f "$1" >/dev/null 2>&1; then # invoke subcommand function, passing arguments through # TODO: unset all nonlocal functions "$@" # same as "$1" "$2" "$3" ... for full argument list else echo "Error: function $1 not recognized" >&2 echo "$USAGE" >&2 exit 1 fi } # enable or disable autostart # you can also set manually by running: gnome-session-properties function autostart { # path to autostart files local AUTOSTART="$HOME/.config/autostart/comfortable-swipe.desktop" local ENABLED="X-GNOME-Autostart-enabled" # show autostart file path function path { echo "$AUTOSTART" } # echo autostart status: ON, OFF, MISSING, INVALID function status { if [[ ! -f "$AUTOSTART" ]]; then echo "MISSING" elif fgrep "$ENABLED=true" < "$AUTOSTART" > /dev/null; then echo "ON" elif fgrep "$ENABLED=false" < "$AUTOSTART" > /dev/null; then echo "OFF" else echo "INVALID" fi } # enable autostart function on { sed -i "s/$ENABLED=false/$ENABLED=true/" "$AUTOSTART" echo "Autostart switched on" } # disable autostart function off { sed -i "s/$ENABLED=true/$ENABLED=false/" "$AUTOSTART" echo "Autostart switched off" } # toggle to opposite autostart status function toggle { [[ $(status) = ON ]] && off || on } # dispatch subcommands if [[ $# -eq 0 ]]; then # default behavior is to toggle toggle elif declare -f "$1" >/dev/null 2>&1; then # invoke subcommand function, passing arguments through # TODO: unset all nonlocal functions "$@" # same as "$1" "$2" "$3" ... for full argument list else echo "Function $1 not recognized" >&2 echo "Usage: $BASENAME autostart [on|off|toggle|status|path]" >&2 exit 1 fi } # verbosely show comfortable-swipe status function status { # TODO: show configuration status as well echo "Autostart is $("$BASENAME" autostart status)" if pgrep -f "$BASENAME" | fgrep -v $$ > /dev/null 2>&1; then echo "comfortable-swipe program is RUNNING" else echo "comfortable-swipe program is STOPPED" fi } #################### # COMMAND OPTIONS # #################### for i in "$@"; do case $i in -h | --help) # eagerly show help help exit 0 ;; -v | --version) # eagerly print version echo "comfortable-swipe $VERSION" exit 0 ;; *) # unknown option if [[ "$i" == -* ]]; then echo "Unknown option: $i" >&2 exit 1 fi ;; esac done # dispatch subcommands if [[ $# -eq 0 ]]; then # no options; just show help help elif declare -f "$1" >/dev/null 2>&1; then # invoke subcommand function, passing arguments through # TODO: unset all nonlocal functions "$@" # same as "$1" "$2" "$3" ... for full argument list else echo "Error: function $1 not recognized" >&2 echo "Usage: $BASENAME [--help|--version] [start|stop|restart|config|autostart|buffer|debug|status] []" >&2 exit 1 fi