6

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:

monitor setup

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:

incorrect touch input 2

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:

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...) 3 displays
(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 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.

1
  • Have you ever found a working solution for wayland? Commented Jun 28, 2024 at 18:22

1 Answer 1

3

My google-fu failed me, until I found this on Peter Hutterer's blog:

https://who-t.blogspot.com/2024/03/enforcing-touchscreen-mapping-in-gnome.html

You can configure this with gsetting:

gsettings set org.gnome.desktop.peripherals.touchscreen:/org/gnome/desktop/peripherals/touchscreens/<vendor_id>:<product_id>/ output "['<monitor_vendor>', '<monitor_product>', '<monitor_serial>']" 

where <vendor_id>:<product_id> are the device USB vendor ID and USB product ID and <monitor_vendor>', '<monitor_product>', '<monitor_serial>' are coming from your .config/monitor.xml, in the monitorspec node for your monitor.

1
  • Oh damn, that's great to hear, I gotta try this when I get a chance. It would be nice to turn this into a script, maybe even a gnome extension that automatically detects and configures touch screen devices based on their virtual position. Commented Jul 16, 2024 at 18:40

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.