dotfiles/bin/xinput-toggle
2017-07-14 20:28:45 -07:00

181 lines
4.5 KiB
Bash
Executable file

#!/bin/bash
debug() {
# DEBUG comes from the environment
if (( DEBUG )); then
printf '%s\n' "$@" >&2
fi
}
is_enabled() {
local id="${1?}"
enabled=$(
xinput list-props "$id" |
grep '\bDevice Enabled\b' | sed 's/.*\(.\)$/\1/'
)
# xinput returns 0 for disabled and 1 for enabled, so we invert since we
# pass on 0
return "$(( !enabled ))"
}
should_disable() {
local id="${1?}"
local force_enable="${2?}"
local force_disable="${3?}"
if (( force_enable )) && (( force_disable )); then
echo '-d and -e make no sense together' >&2
exit 3
fi
if (( force_enable )); then
return 1
elif (( force_disable )); then
return 0
elif is_enabled "$id"; then
return 0
else
return 1
fi
}
get_id_for_device_name() {
local name="${1?}"
xinput list "$name" | sed -n 's/.*id=\([0-9]\+\).*/\1/p'
}
show_help() {
cat << EOF
Usage: ${0##*/} [-n]
Enable and disable xinput devices.
-d disable only, do not toggle
-e enable only, do not toggle
-h show this help page
-i XID only operate on device with xinput id XID
-r REGEX only operate on devices matching name REGEX
-n show results using notify-send in addition to stdout
-p print what we would do, but don't actually do it
-t SECONDS revert enable/disable after SECONDS seconds, ie. if you were
enabling, after SECONDS seconds it will be disabled again.
SECONDS must be an integer greater than 0
EOF
}
act_on_device() {
local print="${1?}"
local notify="${2?}"
local timeout="${3?}"
local xinput_action="${4?}"
local our_next_flag="${5?}"
local id="${6?}"
if (( print )); then
echo "Would $xinput_action device with id $id"
else
xinput -"$xinput_action" "$id"
if (( notify )); then
notify-send "${xinput_action^}d device with id $id"
fi
if (( timeout )); then
debug "Added timeout $timeout"
{
sleep "$timeout"
# TODO: Make args_without_timeour_or_force non-global (sigh,
# shell makes this difficult due to pass by value)
"$0" "${args_without_timeout_or_force[@]}" \
"$our_next_flag" -i "$id"
} &
fi
fi
}
notify=0
force_enable=0
force_disable=0
timeout=0
print=0
only_xid=0
input_ids=()
args_without_timeout_or_force=()
while getopts dehi:npr:t: opt; do
# We need to get a list of args without timeout/force for when we execute
# timeout reversions. This is a blacklist of things we should not put in
# the array, since they force enable/disable/name or do the timeout itself.
case "$opt" in
d|e|t|r)
: # Blacklisted
;;
*)
args_without_timeout_or_force+=( -"$opt" )
if [[ $OPTARG ]]; then
args_without_timeout_or_force+=( "$OPTARG" )
fi
;;
esac
case "$opt" in
'd') force_disable=1 ;;
'e') force_enable=1 ;;
'n') notify=1 ;;
'i') only_xid="$OPTARG" ;;
'h')
show_help
exit 0
;;
'p') print=1 ;;
'r') regex="$OPTARG" ;;
't') timeout="$OPTARG" ;;
'?')
show_help >&2
exit 1
;;
esac
done
if (( only_xid )) && [[ "$regex" ]]; then
echo '-r and -i cannot be currently used together' >&2
exit 4
elif ! (( only_xid )) && ! [[ "$regex" ]]; then
echo 'either -r or -i must be passed to filter devices' >&2
exit 5
fi
if (( only_xid )); then
input_ids=( "$only_xid" )
else
mapfile -t matched_device_names < <(
xinput list |
sed -n 's/.*↳ \(.\+\)id=.*\[slave.*/\1/p' |
sed 's/[\t ]*$//' |
grep -i -- "$regex"
)
debug 'Matched device names:' "${matched_device_names[@]}"
for name in "${matched_device_names[@]}"; do
input_ids+=( "$(get_id_for_device_name "$name")" )
done
fi
if (( "${#input_ids[@]}" == 0 )); then
msg='No matching devices found'
echo "$msg" >&2
(( notify )) && notify-send "$msg"
exit 2
fi
for id in "${input_ids[@]}"; do
if should_disable "$id" "$force_enable" "$force_disable"; then
act_on_device "$print" "$notify" "$timeout" disable -e "$id"
else
act_on_device "$print" "$notify" "$timeout" enable -d "$id"
fi
done
# We may have queued some timeout reversion jobs
wait