Gentoo Forums
Gentoo Forums
Gentoo Forums
Quick Search: in
TIP: Multiple Button Profiles for Wacom Cintiq
View unanswered posts
View posts from last 24 hours

 
Reply to topic    Gentoo Forums Forum Index Documentation, Tips & Tricks
View previous topic :: View next topic  
Author Message
VinzC
Advocate
Advocate


Joined: 17 Apr 2004
Posts: 4630
Location: Theux (Belgium)

PostPosted: Tue Feb 26, 2013 12:42 am    Post subject: TIP: Multiple Button Profiles for Wacom Cintiq Reply with quote

Hi guys.

I own a Wacom Cintiq 22HD tablet. It has an embedded monitor. I'm using NVidia and TwinView with my dual monitor configuration in above-below disposition. With more than one monitor you need to map the tablet monitor to the tablet area the stylus and eraser browse otherwise you end up browsing the entire area covered by all your plugged-in monitors.

The Cintiq also comes with no less than 18 buttons. Eventually I noticed that under Windows pressing the right-most middle button cycles between 4 programmable configurations. That makes no less than 64 possible shortcuts.

Of course if is possible to achieve the same level of functionality under your favourite GNU desktop environment. I came up with an even more flexible solution:
  • there is no limitation on the number of configurations;
  • you may assign one or more profiles to each one of your favourite drawing applications.
I hence wrote a shell script that potentially handles all your tablets. I have tried it with both my tablets (Cintiq and Bamboo Fun Pen & Touch). The script works with multiple monitors and multiple tablets.

Here's the script:
/usr/local/bin/wacom.sh:
#!/bin/sh

# This script auto-configures Wacom tablets: buttons and area.
#
# By Vince C., jan. 2013

CINTIQ_AREA="500 100 95240 54160"
CINTIQ_OUT="HEAD-1" # $(xrandr | sed -ne '/DVI/s/^\(\(\w\|[-.]\)\+\)\s\+connected.*$\+/\1/gp')

BAMBOO_OUT="HEAD-1" # $(xrandr | sed -ne '/DVI/!s/^\(\(\w\|[-.]\)\+\)\s\+connected.*$\+/\1/gp' | head -n 1)


# Application profile directory
# Typically ~/.local/share/wacom-profiles/<application>/<profile>
WACOM_PROFILE_DATA_DIR=${XDG_DATA_HOME:-$HOME/.local/share}/wacom-profiles

# Directory for storing the last application active profile
# Typically ~/.cache/wacom-profiles/<application>
WACOM_PROFILE_CACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/wacom-profiles

# Temporary storage space for keeping the last selected application
# Tries $XDG_RUNTIME_DIR, /run/users/<user>, /tmp/.private/<user> and
# falls back to /tmp/.wacom-profile-<user>.
if [ -n "$XDG_RUNTIME_DIR" ] && [ -d $XDG_RUNTIME_DIR ]; then
   WACOM_PROFILE_RUN_DIR=${XDG_RUNTIME_DIR}
elif [ -d /run/users/$USER ]; then
   WACOM_PROFILE_RUN_DIR=/run/users/$USER
elif [ -d /tmp/.private/$USER ]; then
   WACOM_PROFILE_RUN_DIR=/tmp/.private/$USER
fi
if [ -z "$WACOM_PROFILE_RUN_DIR" ]; then
   WACOM_PROFILE_RUN_DATA=/tmp/.wacom-profile-instance-$USER
else
   WACOM_PROFILE_RUN_DATA=${WACOM_PROFILE_RUN_DIR}/wacom-profile-instance
fi
# Create the file if it doesn't exist
if [ ! -f $WACOM_PROFILE_RUN_DATA ]; then
   touch $WACOM_PROFILE_RUN_DATA
   chmod 600 $WACOM_PROFILE_RUN_DATA
fi

# Show a notification if library libnotify is installed. Do nothing otherwise.
# Set label to the detected Wacom hardware.
notify()
{
   [ -x /usr/bin/notify-send ] || return 0
   /usr/bin/notify-send "$WACOM_DEV_NAME" "$@"
}

# Show a notification with the information icon.
notify_info()
{
   notify --expire-time=3000 -i dialog-information "$@"
}

# Show a notification with the warning icon.
notify_warning()
{
   notify -i dialog-warning "$@"
}

# Retrieve the ID of the window beyond the mouse pointer.
# See http://unix.stackexchange.com/questions/16131/how-do-i-find-the-x-window-id-under-the-mouse-pointer-in-bash
find_focused_window()
{
   cat <<-EOS | python
   import sys
   from ctypes import *

   # Load X library
   Xlib = CDLL( "libX11.so.6" )
   display = Xlib.XOpenDisplay( "${DISPLAY:-:0}" )
   if display == 0: sys.exit(2)

   # Open root window
   w = Xlib.XRootWindow( display, c_int(0) )

   # Query window under the mouse pointer, exit if unable to guess
   mask = c_uint()
   ( root_id, child_id ) = ( c_uint32(), c_uint32() )
   ( root_x, root_y, win_x, win_y ) = ( c_int(), c_int(), c_int(), c_int() )
   if Xlib.XQueryPointer(
       display, c_uint32(w), byref(root_id), byref(child_id),
       byref(root_x), byref(root_y),
       byref(win_x), byref(win_y), byref(mask)
   ) == 0: sys.exit(0)

   # Print out the window ID (in decimal)
   print child_id.value
EOS
}

# Select the application whose instance is given as argument $1.
# Return 1 if the active window has changed since the last time.
select_instance()
{
   local -r instance=$1
   if ! grep -sq "$instance" $WACOM_PROFILE_RUN_DATA; then
      echo $instance > $WACOM_PROFILE_RUN_DATA
      return 1
   fi
}

# List registered profiles for a given application.
list_profiles()
{
   local -r instance=$1   # Window instance name
   [ -d "${WACOM_PROFILE_DATA_DIR}/$instance" ] && \
      ls -1 ${WACOM_PROFILE_DATA_DIR}/$instance
}

# Select and activate a profile for the given application.
# Arguments:
# $1   Application (instance) name
# $2   Profile name
# $3   Notification message body
select_profile()
{
   local -r instance=$1   # Window instance name
   local -r profile=$2   # Profile [file] name
   local -r message=$3   # Message if profile found

   [ -f ${WACOM_PROFILE_DATA_DIR}/${instance}/$profile ] && \
      mkdir -p ${WACOM_PROFILE_CACHE_DIR} && \
      . ${WACOM_PROFILE_DATA_DIR}/${instance}/$profile && \
      echo $profile > ${WACOM_PROFILE_CACHE_DIR}/$instance && \
      notify_info "$message"
}

# Main routine. Determine the application under the pointer and activate/reload
# the next/last profile for that application.
# Arguments:
# $1   Window ID
# $2   Window title
# $3   Appliation (instance) name
check_profile()
{
   local -r wid=$1      # Window ID under the mouse pointer
   local -r title=$2   # Window title
   local -r instance=$3   # Window instance name

   # Make sure there's at least one profile for the selected app
   [ -d "$WACOM_PROFILE_DATA_DIR" ] && list_profiles $instance | grep -sq [[:alnum:]] || return 0

   # Make sure the selected profile exists
   #CURRENT_PROFILE="$(cat ${WACOM_PROFILE_CACHE_DIR}/$instance)"

   # Check previous application under the pointer. If it is
   # the same window as when the script ran the last time,
   # then find and apply the next profile in the list. Load
   # the last profile if the application changed.
   # Note : application profile storage is made persistent.
   if select_instance $instance; then
      # Read the current profile for the running application
      # then get the next one
      profile=$(
         (list_profiles $instance; list_profiles $instance) | \
         (grep -A1 -f ${WACOM_PROFILE_CACHE_DIR}/$instance 2>/dev/null || head -n1) | \
         head -n2 | tail -n1
      )
      select_profile $instance $profile "Activation du profil $profile pour \"$title\""
   else
      # Just read the current application profile name
      # Use the first one if none was set yet
      profile=$(
         cat ${WACOM_PROFILE_CACHE_DIR}/$instance 2>/dev/null || \
         list_profiles $instance | head -n1
      )
      select_profile $instance $profile "Chargement du profil $profile pour \"$title\""
   fi
}

# Wacom Cintiq 22HD buttons are as follows:
#
#   Left              Right
# +-----------+     +-----------+
# | Button 2  |     | Button 15 |
# +-----------+     +-----------+
# | Button 3  |     | Button 16 |
# +-----------+     +-----------+
# | Button 8  |     | Button 17 |
# +-----------+     +-----------+
# | Button 9  |     | Button 18 |
# +-----------+     +-----------+
# +-----------+     +-----------+
# | Button 1  |     | Button 14 |
# +-----------+     +-----------+
# +-----------+     +-----------+
# | Button 10 |     | Button 19 |
# +-----------+     +-----------+
# | Button 11 |     | Button 20 |
# +-----------+     +-----------+
# | Button 12 |     | Button 21 |
# +-----------+     +-----------+
# | Button 13 |     | Button 22 |
# +-----------+     +-----------+
#
# See https://github.com/stevvvn/dotfiles/blob/master/cintiq/.xsetwacom.sh

# Apply the default configuration for the currently inspected Wacom tablet.
# Arguments:
# $1   Wacom device name (e.g.: "Wacom Cintiq 22HD pad")
cintiq_set_defaults()
{
   # Top-most left buttons
   xsetwacom set "$1" Button 2 "key ctrl z"      # Undo
   xsetwacom set "$1" Button 3 "key ctrl y"      # Redo
   xsetwacom set "$1" Button 8 "key ctrl pgup"      # Next tab (generic)
   xsetwacom set "$1" Button 9 "key ctrl pgdn"   # Previous tab (generic)

   # Central button (Full Screen)
   xsetwacom set "$1" Button 1 "key f11"
}

# Load a Wacom tablet profile.
# Arguments:
# $1   Wacom device name (e.g.: "Wacom Cintiq 22HD pad")
# Keeps the profile switch button (Button 14) from being overriden.
cintiq_setup_pad()
{
   # First load default configuration
   WACOM_PAD="$@"
   cintiq_set_defaults "$WACOM_PAD"

   # Then find the window under the pointer and apply its profile
   local WID=$(find_focused_window)
   if [ -z "$WID" ]; then
      notify_warning "Impossible de déterminer l'application active."
   else
      # xwininfo renvoie quand c'est possible le titre de la fenêtre,
      # son instance et sa classe selon le format suivant:
      # ID "titre": ("instance" "classe") ...
      # Seuls l'ID, le titre et l'instance sont utiles ici.
      eval check_profile $(xwininfo -int -tree -id $WID | \
         sed -ne 's/\([0-9]\+\)\s\+\("[^"]\+"\):\s\+(\("[^"]\+"\)\s\+"[^"]\+").*/\1 \2 \3/p')
   fi

   # Override button 14, the profile switch key (defaults to Shift+Super+F12)
   xsetwacom set "$WACOM_PAD" Button 14 "key super shift f12"

}

# Set the tablet area for Wacom tablets. This function is called once for
# each device (i.e. stylus and eraser).
# Argument:
# $1   Wacom device name (e.g.: "Wacom Cintiq 22HD stylus")
cintiq_setup_area()
{
   xsetwacom --set "$1" Area "$CINTIQ_AREA"
}

# Map a wacom tablet to a specifc monitro (HEAD if nVidia adapter present,
# not yet detected automatically)
map_to_output()
{
   local OUT=$1; shift
   local DEV="$@"
   xsetwacom set "$DEV" MapToOutput $OUT
}

# Command xsetwacom returns "Device Name <spaces> id: <id>  type: <type> <spaces>"
# so we transform it into "Device Name,<id>,<type>", easier to parse.
xsetwacom list devices | sed -e 's/\s\+\(\w\+\):\s\+\(\w\+\)/,\2/g' -e 's/\s\+$//g' | \
while read WACOM_DEV; do
   WACOM_DEV_NAME="${WACOM_DEV%%,*}"
   case $WACOM_DEV_NAME in
      "Wacom Bamboo Comic"*|"Wacom Bamboo Fun"*)
         # Map wacom tablet to the desired output
         [ -n "$BAMBOO_OUT" ] && map_to_output $BAMBOO_OUT "$WACOM_DEV_NAME"
         # Disable the touch feature!
         [ "${WACOM_DEV%%touch*}" = "${WACOM_DEV}" ] && \
            xsetwacom set "$WACOM_DEV_NAME" touch off ;;

      "Wacom Cintiq 22HD"*)
         # Map output to Cintiq screen, then map buttons to keys
         [ -n "$CINTIQ_OUT" ] && map_to_output $CINTIQ_OUT "$WACOM_DEV_NAME"
         # Set eraser and stylus area and Map button keys with the pad
         [ "${WACOM_DEV%%pad*}" = "${WACOM_DEV}" ] && \
            cintiq_setup_area "$WACOM_DEV_NAME" || \
            cintiq_setup_pad "$WACOM_DEV_NAME" ;;
   esac
done

How it works

Store the prose as /usr/local/bin/wacom.sh .

Each time it is run the script
  1. Sets the area dimensions of tablets it detects
  2. Selectively maps detected tablets to the monitor you want (requires tuning)
With Cintiq tablets each time you press the middle button on the right, the script:
  1. Detects which application the mouse pointer is hovering
  2. Cycles through profiles or
  3. If you switch to another drawing application restores the previous profile for that application;
  4. Shows a notification bubble with the tablet name and the selected profile.
Typically profiles are stored in the user's home directory in ~/.local/share/wacom-profiles/<application>/ . They are simple bash scripts, which don't need to be executable as they are sourced from the main script. The script also maintains a last profile per application file in ~/.cache/wacom-profiles/<application> .

An embedded pyton script is used to detect the window under the mouse pointer. I found that script there and decided to embed it in the shell script so that only one tool does everything (not very UNIX, I know :lol:).

Configuration prerequisites

You need to make the script run each time you plug a tablet. My desktop environment is Xfce and I typed /usr/local/bin/wacom.sh (the script full path) into Parameters > Removable Disks & Media > Input Devices > Tablets.

Since tablet buttons may not run scripts, you need to assign the script to a keyboard shortcut in your desktop environment. I used Shift+Super+F12 but you may use whatever suits your needs. Therefore button 14 on the Cintiq tablet is also mapped to that keyboard shortcut so as to run the script each time you press the button.

Here's a sample profile for Gimp:
.local/share/wacom-profiles/gimp-2.8/page1:
#!/bin/sh

[ -n "$WACOM_PAD" ] || exit 1

# Left strip
xsetwacom set "$WACOM_PAD" StripLeftUp "key super pgup"      # brush radius (must be mapped in GIMP)
xsetwacom set "$WACOM_PAD" StripLeftDown "key super pgdn"   # +ctrl = faster

# Right strip
xsetwacom set "$WACOM_PAD" StripRightUp "key shift plus"   # zoom in
xsetwacom set "$WACOM_PAD" StripRightDown "key minus"      # zoom out

# Left buttons (1-3, 8-13)
xsetwacom set "$WACOM_PAD" Button 8 "key ctrl pgup"      # next tab (generic)
xsetwacom set "$WACOM_PAD" Button 9 "key ctrl shift tab"   # previous layer (up in stack)

xsetwacom set "$WACOM_PAD" Button 1 "key ctrl"

xsetwacom set "$WACOM_PAD" Button 10 "key ctrl tab"      # next layer (dn in stack)
xsetwacom set "$WACOM_PAD" Button 11 "key ctrl pgdn"      # previous tab (generic)
xsetwacom set "$WACOM_PAD" Button 12 "key ctrl shift a"      # select none
xsetwacom set "$WACOM_PAD" Button 13 "key x"         # swap colours

# Right buttons (14-22)
xsetwacom set "$WACOM_PAD" Button 15 "key p"         # paint
xsetwacom set "$WACOM_PAD" Button 16 "key a"         # air brush
xsetwacom set "$WACOM_PAD" Button 17 "key n"         # pencil
xsetwacom set "$WACOM_PAD" Button 18 "key shift d"      # dodge

#xsetwacom set "$WACOM_PAD" Button 19 ""
xsetwacom set "$WACOM_PAD" Button 20 "key shift 1"      # normal size
xsetwacom set "$WACOM_PAD" Button 21 "key ctrl shift j"      # fit window
xsetwacom set "$WACOM_PAD" Button 22 "key tab"         # toggle docks

and a sample for MyPaint:
.local/share/wacom-profiles/mypaint/page1:
#!/bin/sh

[ -n "$WACOM_PAD" ] || exit 1

# Left strip
xsetwacom set "$WACOM_PAD" StripLeftDown "key minus"  # zoom out
xsetwacom set "$WACOM_PAD" StripLeftUp "key shift plus"  # zoom in

# Right strip
#xsetwacom set "$WACOM_PAD" StripRightDown "key shift down"  # "key alt down"  # brush radius (must be mapped in GIMP)
#xsetwacom set "$WACOM_PAD" StripRightUp "key shift up" # "key alt up"

# Left buttons (1-3, 4-13)
xsetwacom set "$WACOM_PAD" Button 10 "key pgup"   # previous layer
xsetwacom set "$WACOM_PAD" Button 11 "key pgdn"      # next layer

# Right buttons (14-22)
xsetwacom set "$WACOM_PAD" Button 18 "key tab"  # toggle docks

You need to have libnotify installed if you want the eye-candy notification but it's not necessary. It is ignored by the script if not installed.

Now start Gimp and MyPaint. Suppose MyPaint is active. Press the middle button on the right side of the tablet (Button 14), you'll see a notification popup telling you which profile is active. Switch to Gimp and press the same button again. The profile that's been loaded is now Gimp's profile. If you have more than one profile they will cycle each time you press the middle button. Switch back to MyPaint, press the middle button again and you'll reload the profile that was last active when you left MyPaint.

Last note: the association application/profile survives a reboot.

Enjoy 8) .

UPDATE: I tuned my Gimp profile to minimize keyboard and mouse usage. Button 1 now acts like the Ctrl key. When in paint mode, press button 1 to switch to colour picker. Also note that you need to map 4 keys in Gimp:
  1. Context, Brush Radius Increase (1 point step) to Super+PgUp
  2. Context, Brush Radius Increase more (10 points step) to Super+Ctrl+PgUp
  3. Context, Brush Radius Decrease to Super+PgDown
  4. Context, Brush Radius Decrease more to Super+Ctrl+PgDown
I also wanted to control opacity with the strips but Gimp has too many key combinations for that (up to 4 increase rates). The slow rate is too slow and the faster rate offers not enough granularity with small brush sizes to I had to make a compromise. That's also the purpose of button 1 being mapped to the Ctrl key. Press it while using the left strip for coarse tuning of the brush size. In short, use the left strip for fine tuning and press button 1 when you need to set big brush sizes.

If you don't need to switch between images with the tablet buttons then you may assign buttons 8 and 11 to brush opacity control. For my own usage I found that I didn't need a finer tuning than stepping 10 points.

In the end I don't need my mouse (don't laugh) nor my keyboard anymore as all of my needs are covered by the Cintiq buttons. And I only needed one profile while one button is left unassigned. I think I might assign it to Alt+Tab for I sometimes need to switch to viewing a picture, fullscreen, that I use as a reference when painting; even though the dual monitor is perfect for that, the secondary monitor is too far from my sight (it's a TV screen) to spot details so I sometimes need to see the reference picture right under my nose :D . But that's my own requirements YMMV.
_________________
Gentoo addict: tomorrow I quit, I promise!... Just one more emerge...
GNU/Linux user #369763
“Wow! I feel root”


Last edited by VinzC on Tue Jul 23, 2013 5:01 pm; edited 5 times in total
Back to top
View user's profile Send private message
gnosti
n00b
n00b


Joined: 17 Aug 2006
Posts: 4

PostPosted: Sun Mar 03, 2013 6:37 pm    Post subject: Reply with quote

This is awesome. Makes things much easier.

Thank you.
Back to top
View user's profile Send private message
VinzC
Advocate
Advocate


Joined: 17 Apr 2004
Posts: 4630
Location: Theux (Belgium)

PostPosted: Mon Mar 04, 2013 8:25 am    Post subject: Reply with quote

You're most welcome. I have since updated Gimp and MyPaint profiles, will post them here.
_________________
Gentoo addict: tomorrow I quit, I promise!... Just one more emerge...
GNU/Linux user #369763
“Wow! I feel root”
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    Gentoo Forums Forum Index Documentation, Tips & Tricks All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum