Skip to content

Instantly share code, notes, and snippets.

@Konfekt
Last active April 20, 2024 05:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Konfekt/d57567b84b264e37e62bd5c001e76457 to your computer and use it in GitHub Desktop.
Save Konfekt/d57567b84b264e37e62bd5c001e76457 to your computer and use it in GitHub Desktop.
A shell script to map all Wacom devices to a given monitor, in particular to switch through all available monitors by repeatedly calling it with the parameter NEXT
#!/usr/bin/env bash
# exit on error or on use of undeclared variable or pipe error:
set -o errexit -o errtrace -o nounset -o pipefail
# optionally debug output by supplying TRACE=1
[[ "${TRACE:-0}" == "1" ]] && set -o xtrace
shopt -s inherit_errexit
PS4='+\t '
IFS=$'\n\t'
error_handler() {
echo >&2 "Error: In ${BASH_SOURCE[0]}, Lines $1 and $2, Command $3 exited with Status $4"
pr -tn "${BASH_SOURCE[0]}" | tail -n+$(($1 - 3)) | head -n7 | sed '4s/^\s*/>> /' >&2
exit "$4"
}
trap 'error_handler $LINENO "$BASH_LINENO" "$BASH_COMMAND" $?' ERR
usage() {
cat << EOF
Usage: $0 <Monitor>
Map all Wacom Devices to Monitor <Monitor>.
For example, to map to Monitor VGA-1:
$0 VGA-1
If <Monitor> is NEXT, then to the next monitor
as listed by xrandr. Useful, for example, to bind,
say by xbindkeys, a key to
notify-send "\$($0 NEXT)"
EOF
exit 1
}
next() {
[ -n "$TMPDIR" ] || TMPDIR=/tmp
WFILE="$TMPDIR/wacom2mon"
# Since
# xsetwacom --get "Wacom Pad pad" MapToOutput
# returns
# 'MapToOutput' is a write-only option.
# we store the output device in a temp file.
# This, woefully, cannot detect output device changes;
# for example, after a reboot.
if [ -f "$WFILE" ]; then
current_monitor="$(cat "$WFILE")"
else
current_monitor=""
fi
if [ -z "$current_monitor" ]; then
# no mapping known, just use the first monitor
next_monitor=$(xrandr --listactivemonitors | sed -n 2p | awk '{print $4}')
else
# duplicate the output in case the current monitor is the last one, so we automatically cycle
# through. head after grep takes the first match and sed afterwards drops the match and keeps
# only the next monitor
next_monitor=$(xrandr --listactivemonitors | sed 1d | awk '{print $4}' | tee /dev/stdout | grep -A 1 "$current_monitor" | sed -n 2p)
fi
echo "$next_monitor" >$WFILE
echo "$next_monitor"
}
command -v xsetwacom >/dev/null 2>&1 || exit 1
command -v xrandr >/dev/null 2>&1 || exit 1
if [[ $# == 0 ]]; then
usage
else
screen="$1"
fi
[ "$screen" = "NEXT" ] && screen="$(next)"
devices="$(xsetwacom --list devices | cut -f 1)"
[ -z "$devices" ] && exit 1
while read -r d; do
# instead of `while IFS=$' \n\t'`; from https://stackoverflow.com/a/15398846
d=$(echo $d | xargs echo -n)
xsetwacom --set "$d" MapToOutput "$screen"
echo "$d was mapped to: $screen"
done <<< $devices
@j-hap
Copy link

j-hap commented Sep 3, 2023

Thanks for the script! Here's some suggestions: instead of taking the last two entries from the xrandr output, you can keep them all if you just delete the first line with sed 1d. I also took the liberty of changing the way a current mapping is found, so it's not limited to bash and its arrays

next() {
  [ -n "$TMPDIR" ] || TMPDIR=/tmp
  WFILE="$TMPDIR/wacom2mon"

  # Since
  #   xsetwacom --get "Wacom Pad pad" MapToOutput
  # returns
  #   'MapToOutput' is a write-only option.
  # we store the output device in a temp file.
  # This, woefully, cannot detect output device changes;
  # for example, after a reboot.

  if [ -f "$WFILE" ]; then
    current_monitor="$(cat "$WFILE")"
  else
    current_monitor=""
  fi

  if [ -z current_monitor ]; then
    # no mapping known, just use the first monitor
    next_monitor=$(xrandr --listactivemonitors | sed -n 2p | awk '{print $4}')
  else
    # duplicate the output in case the current monitor is the last one, so we automatically cycle
    # through. head after grep takes the first match and sed afterwards drops the match and keeps
    # only the next monitor
    next_monitor=$(xrandr --listactivemonitors | sed 1d | awk '{print $4}' | tee /dev/stdout | grep -A 1 "$current_monitor" | sed -n 2p)
  fi

  echo "$next_monitor" >$WFILE
  echo "$next_monitor"
}

this still fails if the stored monitor is not found anymore though

@Konfekt
Copy link
Author

Konfekt commented Sep 3, 2023

Glad to have been of help. Thank you for your improvements as well, which have just been added where [ -z current_monitor ] was replaced by [ -z $current_monitor ].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment