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.

A bounding ball animation
The classic bouncing ball that everyone animates. It showcases a few techniques that I learned from The Animator's Survival Kit. Note the "Squash and Stretch", the ball stretches as it gains speed when approaching the ground and squishes as it hits the ground. It also has "Slow In, Slow Out" where the ball slows as it is about to reach the peak of its bounce and slowly accelerates as it starts to fall back to earth.

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.

Description=Wacom Plugin Service



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.

  1. 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.
  2. Run systemctl --user enable wacom.service
  3. Run systemctl --user start wacom.service
  4. Run systemctl --user status wacom.service to monitor the health of the service
  5. We can stop the service with systemctl --user stop wacom.serivce, disable it with systemctl --user disable wacom.service, and if we change the service definition file we can reload the service with systemctl --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.

An animation of a guy rolling his eyes
A rolling eyes reaction I make with OpenToonz. It's the open source version of the software Studio Ghibli uses. I think I'm pretty close to Hayao Miyazaki level tbh.

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".