Graphics tablet configuration on Linux
Following on from my previous article about configuring my mildly cursed WP8060U graphics tablet, I finally got around to setting it up for real use on my desktop.
The situation
The WP8060U is a 4:3 graphics tablet from the early 2000s. My desktop, on the other hand, has two portrait 1440x900 (16:10) displays and a landscape 1680x1050 display. This combination does not mesh well. Without configuration, the horizontal axis of the graphics tablet represents the full 3480px width of my displays, and the vertical axis the 1440px of the portrait displays on either side, stretching the 4:3 input area of the tablet to 29:12.
Input considerations
Essentially, I want the tablet's area to cover a 4:3 area in the centre of the middle monitor, to use with Krita. What we need to do is:
- Figure out the resolution of the display
- Divide the smaller axis by the relevant part of the tablet's aspect ratio
- Multiply the result by the other part of the aspect ratio
- Figure out the in-display offset for the area we want
- Calculate the total offset for the whole display server for the area
So, let's take a look at the output of xrandr, ignoring the different mode lines:
DisplayPort-1 connected primary 1680x1050+900+0 (normal left inverted right x axis y axis) 473mm x 296mm HDMI-A-1 connected 900x1440+0+0 left (normal left inverted right x axis y axis) 410mm x 256mm DVI-D-0 connected 900x1440+2580+0 left (normal left inverted right x axis y axis) 410mm x 260mm
My middle display is hooked up to the DisplayPort output of my graphics card, making it DisplayPort-1. It's resolution is 1680x1050, and it's positioned 900px from the left and 0px from the top.
First, the working area.
We take the smaller half of the resolution - 1050, in this case - and divide that by the relevant half of the tablet's aspect ratio - the 3 in 4:3:
1050 ÷ 3 = 350
Now we multiply that by the other half of the aspect ratio - 4 - to get the width of the area:
350 × 4 = 1400
This means our working area is going to be 1400x1050.
Next, the internal offset:
We take the total width of the display and subtract the working area's width from it, then divide it by two, to get the margin on either side:
1680 - 1400 = 280
280 ÷ 2 = 140
If the smaller side was the width rather than the height, like if you wanted to use your graphics tablet on a portrait display, you'd be doing these two in reverse.
Finally, the total offsets. We're going to gloss over the vertical offset because my display has no vertical offset, but essentially, all you need to do is add together the display's offset and the internal offset you just worked out. xrandr said the display's offset was +900+0, so it's 900px from the left and 0px from the top of the display server.
900 + 140 = 1040
To summarise:
- Input area is 1400x1050
- Input area offset is +1040+0
Pixels to floats
The xinput property we're going to modify takes floating point numbers - that is, non-integer numbers - between 0 and 1 for mapping, so we need to convert the pixel positions we just worked out into floats to feed into the command. You'll want a calculator for this one.
Working area width (c0): Divide the working area width by the total display server width:
1400 ÷ 3480 = 0.40229885057471
Working area height (c2): Divide the working area height by the total display server height:
1050 ÷ 1440 = 0.72916666666667
Working area horizontal offset (c1): Divide the working area horizontal offset by the total display server width:
1040 ÷ 3480 = 0.29885057471264
Working area vertical offset (c3): Divide the working area offset by the total display server height: 0 ÷ 1440 = 0
Finding the device
Before we can configure the device, we need to find out it's True Name - or at least, it's xinput name. If you run xinput, it will tell you all the input devices attached to your X session. Here's mine:
[izaya@nagato ~]$ xinput ⎡ Virtual core pointer id=2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] ⎜ ↳ Logitech MX518 Gaming Mouse id=13 [slave pointer (2)] ⎜ ↳ Logitech MX518 Gaming Mouse Consumer Control id=15 [slave pointer (2)] ⎜ ↳ Valve Software Wired Controller id=19 [slave pointer (2)] ⎜ ↳ UC-LOGIC Tablet WP8060U Mouse id=22 [slave pointer (2)] ⎜ ↳ UC-LOGIC Tablet WP8060U Pen Pen (0) id=23 [slave pointer (2)] ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] ↳ Power Button id=6 [slave keyboard (3)] ↳ Power Button id=7 [slave keyboard (3)] ↳ Sleep Button id=8 [slave keyboard (3)] ↳ Logitech MX518 Gaming Mouse Keyboard id=14 [slave keyboard (3)] ↳ Dell Dell USB Keyboard System Control id=11 [slave keyboard (3)] ↳ HD Webcam C615 id=12 [slave keyboard (3)] ↳ Dell Dell USB Keyboard id=9 [slave keyboard (3)] ↳ USB 2.0 Camera: HD 720P Webcam id=17 [slave keyboard (3)] ↳ Logitech MX518 Gaming Mouse System Control id=16 [slave keyboard (3)] ↳ Valve Software Wired Controller id=18 [slave keyboard (3)] ↳ Logitech MX518 Gaming Mouse Consumer Control id=20 [slave keyboard (3)] ↳ Dell Dell USB Keyboard Consumer Control id=10 [slave keyboard (3)] ↳ UC-LOGIC Tablet WP8060U Pen id=21 [slave keyboard (3)]
The relevant input device in this case is the "UC-LOGIC Tablet WP8060U Pen Pen (0)", device ID 23. The device ID changes depending on the order devices are initialised, so the name is the most reliable way to configure the device.
Application
Now we can put together our command. The structure is:
xinput set-prop "Device name" --type float "Coordinate Transformation Matrix" c0 0 c1 0 c2 c3 0 0 1
All you need to do is substitute in the device name and the numbers you worked out earlier; for my setup, the command is:
xinput set-prop "UC-LOGIC Tablet WP8060U Pen Pen (0)" --type=float "Coordinate Transformation Matrix" 0.40229885057471 0 0.29885057471264 0 0.72916666666667 0 0 0 1
I suspect this could be automated with udev but I haven't experimented with that yet. Perhaps for a part 3?
Bonus: Changing mapping of buttons
The pen on my graphics tablet has two buttons, which are mapped to middle and right click, in addition to the main left-clicking pressure sensitive input. The right click is further up the pen and is as such, inconvenient to press. In Krita, however, right click is used to open the brushes and colour selection dialog. As such, I switched them with the command:
xinput --set-button-map "UC-LOGIC Tablet WP8060U Pen Pen (0)" 1 3 2
You can get the current button map with
xinput --get-button-map "Device name"
and re-order it to your preferences.