How to calibrate touch screen on Wayland?
6
votes
1
answer
9688
views
I have a Fedora 35 notebook running Gnome and Wayland. It has a built in touch screen that works just fine and never needs calibration, even in a multi-monitor setup.
And I have two external monitors (a normal 4k display and a 1080p USB-C touch screen). When I connect these monitors, I disable the internal notebook screen, as I don't need it in that setup. So I end up with this:
Which is perfectly fine. (The right screen is the external touch screen and the left one is the external 4k monitor.)
The problem is that the touch screen thinks it is 5760x2160 pixels, when it is actually just 1920x1080, resulting in incorrect touch inputs:
In order to fix this, I found this article: Arch Wiki - Calibrating_Touchscreen
But it relies on xinput which doesn't appear to exist on Wayland.
The article does mention a way to persistently calibrate a touch screen using udev though. For that I needed to calculate the transformation matrix, which I did as described in the article:
> Now, calculate these as accurate as possible:
>
> c0 = touch_area_width / total_width
> c2 = touch_area_height / total_height
> c1 = touch_area_x_offset / total_width
> c3 = touch_area_y_offset / total_height
>
> The matrix is
>
> [ c0 0 c1 ] [ 0 c2 c3 ] [ 0 0 1 ]
>
> which is represented as a row-by-row array:
>
> c0 0 c1 0 c2 c3 0 0 1
I ended up with this:
(left to right: External 4k screen, external 1080p touch screen, internal 4k touch screen)
**Edit 2:**
According to this article, the only way to get touchscreens in general to work properly in a multi-monitor setup is to use X11. According to the article


c0 = touch_area_width / total_width | 1920/(3840+1920)=0.333333333
c2 = touch_area_height / total_height | 1080/2160=0.5
c1 = touch_area_x_offset / total_width | 3840/(3840+1920)=0.666666667
c3 = touch_area_y_offset / total_height | 0/2160=0
c0 0 c1 0 c2 c3 0 0 1 | 0.333333333 0 0.666666667 0 0.5 0 0 0 1
The article then goes on and shows an example udev rule using a simple transformation matrix:
> /etc/udev/rules.d/99-acer-touch.rules
>
> ENV{ID_VENDOR_ID}=="2149",ENV{ID_MODEL_ID}=="2703",ENV{WL_OUTPUT}="DVI1",ENV{LIBINPUT_CALIBRATION_MATRIX}="1 0 0 0 1 0"
So it would seem I need to know the vendor ID and model ID (which I think is the same as the product id) and the "WL_OUTPUT" which I'm just gonna call monitor connector.
Getting the vendor id and product id via lsusb
was trivial:
$ lsusb
Bus 005 Device 007: ID 1d5c:7102 Fresco Logic Generic Billboard Device
Bus 005 Device 006: ID 222a:0001 ILI Technology Corp. Multi-Touch Screen
Bus 005 Device 005: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
# ... Those 5 devices show up when I connect the touch screen. I removed the rest from the list.
Okay so now I have the vendor id (222a) and the product id (0001).
I then wrote this ridiculous python script to figure out what the name of the monitor connector would be via dbus because I couldn't figure out how to do this without xrandr --query
:
#!/usr/bin/python3
# https://dbus.freedesktop.org/doc/dbus-python/tutorial.html
# https://github.com/GNOME/mutter/blob/b5f99bd12ebc483e682e39c8126a1b51772bc67d/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml
# https://ask.fedoraproject.org/t/change-scaling-resolution-of-primary-monitor-from-bash-terminal/19892
import dbus
bus = dbus.SessionBus()
display_config_well_known_name = "org.gnome.Mutter.DisplayConfig"
display_config_object_path = "/org/gnome/Mutter/DisplayConfig"
display_config_proxy = bus.get_object(display_config_well_known_name, display_config_object_path)
display_config_interface = dbus.Interface(display_config_proxy, dbus_interface=display_config_well_known_name)
serial, physical_monitors, logical_monitors, properties = display_config_interface.GetCurrentState()
for x, y, scale, transform, primary, linked_monitors_info, props in logical_monitors:
for linked_monitor_connector, linked_monitor_vendor, linked_monitor_product, linked_monitor_serial in linked_monitors_info:
for monitor_info, monitor_modes, monitor_properties in physical_monitors:
monitor_connector, monitor_vendor, monitor_product, monitor_serial = monitor_info
if linked_monitor_connector == monitor_connector:
print("Display: " + monitor_properties.get("display-name") + " - Connector: " + monitor_connector)
The output was:
Display: RTK 22" - Connector: DP-2
Display: LG Electronics 27" - Connector: DP-5
I now know the connector is DP-2.
I then created this simple bash script and ran it with sudo
:
#!/bin/bash
VENDOR_ID=222a
PRODUCT_ID=0001
MONITOR_CONNECTOR=DP-2
CALIBRATION_MATRIX="0.333333333 0 0.666666667 0 0.5 0 0 0 1"
UDEV_RULE="ENV{ID_VENDOR_ID}==\"${VENDOR_ID}\",ENV{ID_MODEL_ID}==\"${PRODUCT_ID}\",ENV{WL_OUTPUT}=\"${MONITOR_CONNECTOR}\",ENV{LIBINPUT_CALIBRATION_MATRIX}=\"${CALIBRATION_MATRIX}\""
UDEV_RULES_FILE="/etc/udev/rules.d/99-touchscreen-cal.rules"
echo "${UDEV_RULE}" > "${UDEV_RULES_FILE}"
udevadm control --reload-rules && udevadm trigger
I got no errors. Then I double checked the generated rules file:
$ cat /etc/udev/rules.d/99-touchscreen-cal.rules
ENV{ID_VENDOR_ID}=="222a",ENV{ID_MODEL_ID}=="0001",ENV{WL_OUTPUT}="DP-2",ENV{LIBINPUT_CALIBRATION_MATRIX}="0.333333333 0 0.666666667 0 0.5 0 0 0 1"
So everything should have worked, right? But it didn't. The touch screen input is still as incorrect as before.
Any ideas how I can fix that?
**Edit:**
When I don't disable the internal touch screen, the external touch screen sends all touch input to that internal touch screen instead of spreading the touch input across all monitors. (If I touch the lower right corner of the external 1080p touch screen, the touch is registered in the lower-right corner of the internal 4k touch screen of the notebook etc...)

libinput currently assumes the touchscreen(s) covers all available monitors.
which I know to be incorrect because my internal (non-usb) touch screen works perfectly fine, even in a multi-monitor setup.
Asked by Forivin
(1193 rep)
Jul 3, 2022, 10:39 AM
Last activity: Jun 28, 2024, 09:14 PM
Last activity: Jun 28, 2024, 09:14 PM