Hot-swapping a Wacom Tablet on Linux
Inspired by the very good (but incredibly weird) Smiling Friends, I decided to try my hand at animation. It is difficult to overstate just how bizarre the show is, but you can't help but watch and see how they really pulled out all the stops. There is normal 2D animation, 3D animation, a rotoscoped section, and even one of those super gross, painted, close-up shots you see sometimes in SpongeBob. There was even a character watching a live action clip on a tablet. I really recommend the show, but I totally understand if it turns out to be too weird for you.
I've never been a very good artist and I definitely have never been able to draw consistently or quickly. Given that these are some of the first skills that come to mind mind when you think about animation, you might be wondering what is wrong with me. My real goal is just to be able to make something interesting and creative. I also hope that by picking 2D animation as a medium I'll be forced to learn these skills. At a minimum, I expect to get better at drawing circles, squares, and straight lines which should make the white board diagrams I make when explaining a complex algorithm a bit (or a lot?) easier to read.
To facilitate this, I got a drawing tablet. I went with a Wacom Intuos Small. Given my skills on display here, it should be clear I'm not sponsored, this is just the tablet I got. It seems to work well as a first tablet that is on the cheaper side. The problem is that I use Linux as my daily driver and Wacom tablets aren't supported. I've put a decent amount of work into getting everything running smoothly and I wanted to share what I did here.

Setting up the Wacom Drivers
Unfortunately, Wacom tablets aren't supported on Linux. This means that there aren't official drivers published by Wacom to facilitate communication between the tablet and your computer. Luckily for us, there is a great community of people creating open source drivers for these tablets. Often these drivers ship with newer versions of Linux. I personally only had to install the libwacom package. I installed from source and their documentation was pretty detailed and made the process easy.
Configuring the Tablet with xsetwacom
The nice people over at The Linux Wacom Project, who make the open source drivers, provide a handy tool, xsetwacom, designed to facilitate things like the configuration of the buttons that are commonly found on both the tablets and on the styluses themselves.
Configuring the Buttons
xsetwacom configures parts of the Wacom
eco-system by natural name, rather than by an id or the like. The
command xsetwacom list
will return the name, id, and
type (ERASER,
STYLUS, PAD, etc.)
of all the detected Wacom devices. We can use the
get command to see the value of a setting.
We normally see calls like
xsetwacom get "{device name}" {parameter}
. We can use
xsetwacom list parameters to see all the
set-able values. Once we decide on the value we set it like so
xsetwacom set "{device name}" {parameter} {value}
, for
example,
xsetwacom set "Wacom Intuos S Pad pad" Button 1 "key ctrl
z"
sets button one on the tablet so the "undo" hotkey.
Configuring the Available Writing Space
One quirk of Wacom tablets is that when you have multiple monitors
the drawing surface defaults to mapping across all the available
monitors. Touching on the left edge of the tablet jumps to the left
edge of the left-most monitor, while the right edge would be all the
way over on the right side of the right monitor. This often causes
tiny movements on the pad to be a huge jump on the screen. We use
the
MapToOutput command to limit the drawing
surface to a particular screen. You are supposed to be able to use
the name of the display that you get back from
xrandr, but that never worked for me. When
this fails, you can use the index (starting from zero) of the screen
when you consider the active screens. For example, I use
xsetwacom set "Wacom Intuos S Pen stylus" MapToOutput
Head-0
.
Re-configuration as you Remove and Plug-In the Tablet
The problem is that you'll need to re-configure your tablet each time you plug in your tablet or restart your computer. You can wrap up all your configuration in a script and run that like I did. However, this is still a manual process that I would personally forget to do all the time. Now we are going to look into how to do this automatically.
A Failed Attempt with udev Rules
Linux has a feature called udev rules. These
are user defined actions that are triggered when new devices are
plugged in. We can write a rule like
SUBSYTEM=="usb", ATTRS{idVendor}=="XXXX", ATTRS{idProduct} ==
"YYYY", RUN+="/path/to/my/script"
to run a script when a usb device is plugged in with an vendor id of
XXXX and a product id of
YYYY.
My first thought was to run my setup script each time it detected that my tablet was plugged in. The problem was that for xsetwacom to work, the X server needs to have already detected and processed the tablet. This happens long after the udev rule fires. People online were working on trying to use sleeps to handle this. Your script would simply wait until the X server would have had time to find the tablet before we try to configure it. The problem was that you needed a wrapper around the configuration script. The wrapper would need to trigger the configuration script and run it in the background. This wrapper would then need to sleep and then configure the tablet. This extra layer of indirection is needed because if the configuration script was to sleep in the foreground it would block other things from running. If our script sleeping blocks the X server then it would have never detected our tablet and configuration would fail. This was getting far too complex and it seemed too brittle, so I want with a different solution.
Finding the vendor and product ids
Both the udev rule and my eventual solution
require the vendor id and product id of the table. This can be
gotten with lsusb
. The output will look like
Bus 001 Device 006: ID 056a:0374 Wacom Co., Ltd
which
you can interpret like so
Bus {bus_number} Device {device_number}: ID
{vendor_id}:{product_id} {product_name}
.
A Long Running udev Listener Service
Instead of using udev rules, I wrote a python script that listens to udev events. This is makes it easier to have sleeps to wait for X to detect the tablet. This script simply sits there and when a new device comes in, with a matching vendor id and product id, we trigger our configuration script. This script makes the tablet responsive to unplugging. Once you plug the tablet back in, it will get re-configured.
A Note on Dependency Management
My long running listener uses the
pyudev library. There are a few ways to make
this library accessible to the system service. I chose to install
the library in a pyenv environment. In my
listener script I point the #!
line to the python
binary for this environment. When we execute this script it uses
this python interpreter. Other solutions would be to call the script
through that interpreter manually.
systemd For Automatic Starts and Reloads on Failure
The long-running server method works well, but there is still the manual step of starting it every time you boot your computer and restarting it if it ever crashes. We can use systend to manage this service for us.
[Unit] Description=Wacom Plugin Service [Service] ExecStart=/home/brian/.shellrc/wacom/wacom-listener.py Restart=on-failure RestartSec=10 StartLimitBurst=10 Type=simple Environment=PYTHONUNBUFFERED=1 [Install] WantedBy=default.target
This is our service configuration file—I've called it
wacom.service
. There are obvious values like
Description which is the natural language
summary of what your service does.
ExecStart tells us how to run the service,
while keys like Restart and
RestartSec control the settings for which to
restart the service.
One thing to point out is the line
Environment=PYTHONUNBUFFERED=1
. This sets an
environment variable in the service. In this particular case, we are
making the python output unbuffered. Normally python doesn't
actually print the output(or write it to a file) until a
\n
is printed or the buffer files up. This environment
variable makes it so we always output. This is needed to make the
logging in the modules show up with
journalctl.
The following steps are needed to enable the service.
-
Copy the service file into
~/.config/systemd/user/
. Note that this needs to be a copy and not a soft link. When enabling the service a soft link will be created and will complain about having too many levels of soft lengths. - Run
systemctl --user enable wacom.service
- Run
systemctl --user start wacom.service
-
Run
systemctl --user status wacom.service
to monitor the health of the service -
We can stop the service with
systemctl --user stop wacom.serivce
, disable it withsystemctl --user disable wacom.service
, and if we change the service definition file we can reload the service withsystemctl --user daemond-reload && systemctl --user restart wacom.service
We can use journalctl --user-unit wacom.service
to get
the logs from the service.
Now systemd should manage starting our
service on boot and restarting it should it crash. We can simulate a
crash with
systemctl --user --signal=SIGKILL kill wacom.service
and see how systemd will bring it back up.
Note: For most of these commands we should be able to drop the
.service suffix and just call is wacom when
using systemctl
commands.
Now this listener service should make it so that when we plug in the tablet our defaults should be loaded.

Hopefully this post can help someone who is trying setup their Wacom tablet on Ubuntu Linux. I'm still drawing poorly, but at least I can create my own reaction gifs now. I'm think I'm going to go draw a flower pot sliding across the floor to work on "overlapping action", "drag", and "follow though".