0

I'm using an application with two identical (ELO branded) touch screens on xorg. I would like to automatically assign the correct touch panel to the corresponding monitor, even if usb cables or monitor cables got re-plugged. (I would have to run a setup utility once, to check which panel belongs to which monitor, but that's not the problem).

Both monitors and touch panels have serial numbers and preferrably, I'd put these in a configuration file, look them up at startup and run the correct xinput commands from there. Preferrably this should be done through udev, as that is probably the correct way.

What I did before is:

  • run xinput to see the inputs:
$ xinput 
⎡ Virtual core pointer                      id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ Logitech USB-PS/2 Optical Mouse           id=9    [slave  pointer  (2)]
⎜   ↳ Atmel Atmel maXTouch Digitizer            id=10   [slave  pointer  (2)]
⎜   ↳ Atmel Atmel maXTouch Digitizer            id=11   [slave  pointer  (2)]
⎜   ↳ Atmel Atmel maXTouch Digitizer            id=15   [slave  pointer  (2)]
⎜   ↳ Atmel Atmel maXTouch Digitizer            id=16   [slave  pointer  (2)]
⎣ Virtual core keyboard                     id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard    ... etcetera

Then list the devices for the Atmel panels: xinput --list-props 10|grep Device.Node|cut -f2 -d'"', which gives me /dev/input/event11 (do this for the other 3 Atmel ids as well). Then run udevadm to see which of these are touch panels and read the serial ID: udevadm info /dev/input/event11 gives me

N: input/event11
L: 0
S: input/by-path/pci-0000:00:14.0-usb-0:7:1.1-event
S: input/by-id/usb-Atmel_Atmel_maXTouch_Digitizer_E21R077282-event-if01
E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7:1.1/0003:03EB:8A6E.0003/input/input12/event11
E: DEVNAME=/dev/input/event11
E: MAJOR=13
E: MINOR=75
E: SUBSYSTEM=input
E: USEC_INITIALIZED=5462678
E: ID_INPUT=1
E: ID_INPUT_TOUCHSCREEN=1
E: ID_INPUT_WIDTH_MM=341
E: ID_INPUT_HEIGHT_MM=273
E: ID_VENDOR=Atmel
E: ID_VENDOR_ENC=Atmel
E: ID_VENDOR_ID=03eb
E: ID_MODEL=Atmel_maXTouch_Digitizer
E: ID_MODEL_ENC=Atmel\x20maXTouch\x20Digitizer
E: ID_MODEL_ID=8a6e
E: ID_REVISION=1034
E: ID_SERIAL=Atmel_Atmel_maXTouch_Digitizer_E21R077282
E: ID_SERIAL_SHORT=E21R077282
E: ID_TYPE=hid
E: ID_BUS=usb
E: ID_USB_INTERFACES=:030000:
E: ID_USB_INTERFACE_NUM=01
E: ID_USB_DRIVER=usbhid
E: ID_PATH=pci-0000:00:14.0-usb-0:7:1.1
E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_7_1_1
E: LIBINPUT_DEVICE_GROUP=3/3eb/8a6e:usb-0000:00:14.0-7
E: DEVLINKS=/dev/input/by-path/pci-0000:00:14.0-usb-0:7:1.1-event /dev/input/by-id/usb-Atmel_Atmel_maXTouch_Digitizer_E21R077282-event-if01

So now I have the serial ID for the panel.

Then I need the displays that are connected: xrandr -q --verbose shows monitors and the edid-block. Because the edid information is read only and immutable, I can use that as a fixed identifier for now. (No need to run edid-decode although I could).

I'm leaving out the scripting that fetches the monitor names from the xrandr output; but finally, I can connect the Atmel Digitizer that has ID_SERIAL_SHORT=E21R077282 to monitor DP-4, with xinput --map-to-output 10 DP-4; and add the other Atmel panel to DP-6.

But this feels like a whole lot of number crunching, to go from id=10 to /dev/input/event11 to the serial number.

  • Can this be done with udev rules?
  • Should I make a udev rule that distinguishes on ID_SERIAL_SHORT?
  • But then, what should it do with that information?
  • I.e. how can I transfer the information from udev to xorg?
  • And is udev able to fetch the monitor information from xorg as well? Or how should I build the connection between the two?

1 Answer 1

0

The short answer: you cannot (update: you can, see below)

udev is all about USB-device-handling. At the time of plugging something into a USB port, a udev ACTION takes place and you can associate a number of things, including the running of any custom script in a udev rule.

However, there is (a) no guarantee an X11 server is running at that time, and (b) even if it is running, it may be owned by a different user, and it would be a security breach if you could sneak something into the X11 server through a privileged script invoked by the udev system.

For example, you cannot legitimately invoke an xinput command as a part of the udev rule, it should not be able to do anything, even though the very same command would run just fine if invoked by a user who is logged in on the desktop.

There are lots of suggestions out there for rules like (example is for a Wacom touch tablet):

ATTRS{idVendor}=="056a", ACTION=="add", ENV{DISPLAY}=":0.0", ENV{XAUTHORITY}="/home/someusername/.Xauthority", RUN+="/usr/local/sbin/wacom.sh $env{DEVNAME}"

where the wacom.sh script in turn make appropriate xinput calls, but they assume a number of things which are not necessarily true: that DISPLAY is, in fact, :0.0, or that root has access to the specified .Xauthority file (not always true, depends on how the home directory is mounted) or that the username is always the same on a multi-user system.

The only legitimate way out is to run a script by the user, manually, after plugging in the USB touch device. You can associate it with a button on the panel/desktop, but it cannot be invoked by the udev system.

Here's an example I use to map two identical touchscreen monitors arranged side-by-side into one extended desktop. I use xinput/udevadm (there are different detection schemes depending on the device) and hard-coded serial numbers of all screens and devices to map the left stylus/touch screen onto the left monitor, and the right ones onto the right monitor. The exact search strings in parsing out EDID depend very much on the OS and edid-decode versions, so may need adjusting to your situation, I could not figure out a universal way of extracting EDID information.

#!/bin/bash

LEFT_USB=XXXXXXX
LEFT_HDMI=YYYYYYY
RIGHT_USB=ZZZZZZZ
RIGHT_HDMI=TTTTTTT

### all xinput devices
ids=`xinput list --id-only`

left_ids=""
right_ids=""

for i in $ids
do
  node=`xinput list-props $i | grep Device\ Node | cut -f2 -d\"`
  if [ ! -z "$node" ]; then
    serial=`udevadm info $node | grep ID_SERIAL_SHORT | cut -f2 -d=`
    touch_id=`xinput list $i | grep "TouchClass" | sed -r 's/[^0-9]*([0-9]+).*/\1/'`
    pen_id=`xinput list $i | grep Pen | cut -f2 -d= | cut -f1`
    if [ "$serial" == "$LEFT_USB" ]; then 
      left_ids+="$pen_id $touch_id "
    elif [ "$serial" == "$RIGHT_USB" ]; then 
      right_ids+="$pen_id $touch_id "
    fi
  fi
done

left_ids=`echo $left_ids | uniq | xargs`
right_ids=`echo $right_ids | uniq | xargs`

### all connected displays
DISPLAYS=`xrandr | grep \ connected | cut -f1 -d\  `
for d in $DISPLAYS
do
  serial=`xrandr -q --verbose | sed -n  "/$d connected/,/HDCP/p" | edid-decode | grep '\ \ Serial\ Number:' | cut -f2 -d: | xargs`
  if   [ "$serial" == "$LEFT_HDMI" ]; then
    for i in $left_ids
    do
      xinput map-to-output $i $d
    done
  elif [ "$serial" == "$RIGHT_HDMI" ]; then
    for i in $right_ids
    do
      xinput map-to-output $i $d
    done
  fi
done

exit 0

Edit:

An interesting alternative is proposed by imdn in the Arch Linux System Administration Forum.  The basic idea is to make use of a semaphore file which gets created by the udev rule, and add an inotifywait to the X configuration (start a file-inotify service from ~/.xprofile, for example) to monitor any changes to that lock file, and executing an appropriate xinput command when that happens.  Seems workable in principle, but I could not get it going.

The only proper solution is to change the X configuration itself, and invoke setting of appropriate options upon configuration of a new pointer device. If the device name is unique, adding a rule in /etc/X11/xorg.d/* based on MatchProduct is all that is needed. This example is for a Wacom Cintiq tablet, tested on Ubuntu 23.04, xfce4:

## this file is /etc/X11/xorg.conf.d/50-wacom.conf
# xrandr: 
#   DVI-0 connected primary 2560x1600+0+0 (main monitor)
#   DisplayPort-0 connected 1920x1080+2560+0 (Wacom Cintiq tablet)
# Wacom tablet can be configured to be --right-of 1920x1080+2560+0 or --below 1920x1080+0+1600
#   --right-of means x-shift=2560/(2560+1920)=0.571429, x-scale=1920/(2560+1920)=0.428571, yscale=1080/1600=0.675
#    Option "TransformationMatrix" "0.428571 0 0.571429 0 0.675 0 0 0 1"
#   --below means    y-shift=1600/(1600+1080)=0.597015, y-scale=1080/(1600+1080)=0.402985, xscale=1920/2560=0.75
#    Option "TransformationMatrix" "0.75 0 0 0 0.402985 0.597015 0 0 1"
# Refs:
# https://unix.stackexchange.com/questions/58117/determine-xinput-device-manufacturer-and-model
# https://askubuntu.com/questions/178140/xinput-coordinate-transformation-matrix-in-xorg-conf
# https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations
#
# Can match by a common substring for all Wacom devices (as reported by xinput):
#    MatchProduct "Wacom ISDv5 307 Pen stylus"
#    MatchProduct "Wacom ISDv5 307 Pen eraser"
#    MatchProduct "Wacom ISDv5 309 Finger touch"

Section "InputClass"
    Identifier "Wacom Cintiq"
    Driver "wacom"
    MatchProduct "Wacom ISDv5"
    Option "TransformationMatrix" "0.428571 0 0.571429 0 0.675 0 0 0 1"
EndSection

Things get more complicated if there are two identical devices that have the same product ids, the only unique identifier being the serial number. Unfortunately, MatchSerial options does not exist in xorg, but one can set a TAG in udev rules, and then use MatchTag in xorg.

Edit:

Finally, this is the proper solution. The following example is for my two FlatFrog panels side-by-side. The beauty of this particular solution is that all the subdevices inherit these scaling properties when they get activated, so no need to chase touch/stylus/eraser separately.

### this is /etc/udev/rules.d/69-flatfrog.rules
ACTION=="remove", GOTO="flatfrog_end"
KERNEL!="event[0-9]*", GOTO="flatfrog_end"

ATTRS{idVendor}=="25b5", ATTRS{idProduct}="0082", ENV{ID_SERIAL_SHORT}=="XXXXXXXXX", ENV{ID_INPUT.tags}="FlatFrogRHS"
ATTRS{idVendor}=="25b5", ATTRS{idProduct}="0082", ENV{ID_SERIAL_SHORT}=="YYYYYYYYY", ENV{ID_INPUT.tags}="FlatFrogLHS"

LABEL="flatfrog_end"
### this is /etc/X11/xorg.conf.d/10-flatfrog.conf
Section "InputClass"
    Identifier "RHstylus"
    MatchTag   "FlatFrogRHS"
    Option    "TransformationMatrix" "0.5 0 0.5 0 1 0 0 0 1"
EndSection
Section "InputClass"
    Identifier "LHstylus"
    MatchTag   "FlatFrogLHS"
    Option     "TransformationMatrix" "0.5 0 0 0 1 0 0 0 1"
EndSection

Ref: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1592

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .