Linux & Multi-Screen Touch Screen Setups
While working on the Zyn-Fusion UI I ended up getting a touch screen to help with the testing process. After getting the screen, buying several incorrect HDMI cables, and setting up the screen I found out that the touch events weren’t working as expected. In fact they were often showing up on the wrong screen. If I disabled my primary monitor and only used the touch screen, then events were spot on, so this was only a multi-monitor setup issue.
So, what caused the problem and how can it be fixed?
Well, by default the mouse/touch events which were emitted by the new screen were scaled to the total available area treating multiple screens as a single larger screen. Fortunately X11 provides one solution through xinput. Just running the xinput tool lists out a collection of devices which provide mouse and keyboard events to X11.
mark@cvar:~$ xinput | Virtual core pointer id=2 [master pointer (3)] | > Virtual core XTEST pointer id=4 [slave pointer (2)] | > PixArt USB Optical Mouse id=8 [slave pointer (2)] | > ILITEK Multi-Touch-V3004 id=11 [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)] > AT Translated Set 2 keyboard id=9 [slave keyboard (3)] > Speakup id=10 [slave keyboard (3)]
In this case the monitor is device 11 which has it’s own set of properties.
mark@cvar:~$ xinput list-props 11 Device 'ILITEK Multi-Touch-V3004': Device Enabled (152): 1 Coordinate Transformation Matrix (154): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000 Device Accel Profile (282): 0 Device Accel Constant Deceleration (283): 1.000000 Device Accel Adaptive Deceleration (284): 1.000000 Device Accel Velocity Scaling (285): 10.000000 Device Product ID (272): 8746, 136 Device Node (273): "/dev/input/event13" Evdev Axis Inversion (286): 0, 0 Evdev Axis Calibration (287): <no items> Evdev Axes Swap (288): 0 Axis Labels (289): "Abs MT Position X" (689), "Abs MT Position Y" (690), "None" (0), "None" (0) Button Labels (290): "Button Unknown" (275), "Button Unknown" (275), "Button Unknown" (275), "Button Wheel Up" (158), "Button Wheel Down" (159) Evdev Scrolling Distance (291): 0, 0, 0 Evdev Middle Button Emulation (292): 0 Evdev Middle Button Timeout (293): 50 Evdev Third Button Emulation (294): 0 Evdev Third Button Emulation Timeout (295): 1000 Evdev Third Button Emulation Button (296): 3 Evdev Third Button Emulation Threshold (297): 20 Evdev Wheel Emulation (298): 0 Evdev Wheel Emulation Axes (299): 0, 0, 4, 5 Evdev Wheel Emulation Inertia (300): 10 Evdev Wheel Emulation Timeout (301): 200 Evdev Wheel Emulation Button (302): 4 Evdev Drag Lock Buttons (303): 0
Notably xinput provides a property to describe a coordinate transformation which can be used to remap the x and y values of the cursor events. The transformation matrix here is a 3x3 matrix used to transform 2D coordinates and is a fairly common sight in computer graphics. It translates from \((x,y)\) to \((x',y')\) as defined by:
The transformation matrix allows for stretching, shearing, translation, flipping, scaling, etc. For the sorts of problems you may see introduced by a multi-monitor setup I would only expect people to care about translating (\(t\)) the events and then re-scaling (\(s\)) them to the offset area. Using these two parameters, the transformation matrix equation is simplified to:
Or without the matrix representation:
With that background out of the way, let’s see how this applied to my specific monitor setup:
As I mentioned earlier the touch events were scaled to the dimensions of the larger virtual screen. Since the touch screen is larger this means the y axis is mapped correctly and the x axis is mapped for pixels 0..3200 (both screens) instead of pixels 1281..3200 (left screen only). Since the xinput scales theses parameters based upon the total screen size, we can divide by the total x size (3200) to learn that the x axis maps to 0..1 rather than 0.4..1.0. Solving the above equations we can remap the touch events using \(s_x=0.6\) and \(t_x=0.4\). This results in the transformation matrix:
The last step is to provide the new transformation matrix to xinput:
xinput set-prop 11 'Coordinate Transformation Matrix' 0.6 0 0.4 0 1 0 0 0 1
Now cursor events map onto the correct screen accurately and the code to change the xinput properties can be easily put into a shell script.