#!/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