Why doesn't xsetwacom work from udev?

5,683

Solution 1

There is a fairly easy workaround, you can add something like this to your xorg.conf (or a file in xorg.conf.d, as I've done):

anthony@Watt:/etc/X11/xorg.conf.d$ cat 55-local-wacom.conf 
Section "InputClass"
       Identifier "Wacom Left Handed"
       MatchDriver "wacom"
       Option "Rotate" "half"
EndSection

Check the wacom(4) manpage for details of all the options you can set.

(In theory, you can use MatchProduct to individually configure the touchpad, pen, eraser, etc., but when I tried that a while back, it caused Xorg to segfault. Same if I tried to float them. But you're not doing any of that... and maybe the bug is fixed now.)

Solution 2

It works if you create two files, one wrapper script being called by udev, which in turns calls the actual configuration script in the background. The configuration script needs to sleep for a short while, so that X11 has time to do its job. Here's the setup I use:

Wrapper script called by udev (/usr/local/bin/setupwacom.sh):

#!/usr/bin/env bash
/usr/local/bin/setupwacom-post-X11.sh &

Configuration script called by the wrapper script (/usr/local/bin/setupwacom-post-X11.sh):

#!/usr/bin/env bash
sleep 2
export XAUTHORITY=/home/adrian/.Xauthority
export DISPLAY=:0
# Put your xsetwacom commands here, for example: 
xsetwacom --set "Wacom Intuos S Pad pad" Button 1 "key +ctrl +shift e"

Solution 3

None of the answers here worked for me, and the options I wanted to set could not be specified in xorg.conf:

$ xsetwacom -x get 'Wacom Intuos PT S Pad pad' button 1 
Button: Actions are not supported by xorg.conf. Try shell format (-s) instead.

I ended up having to start the script with a systemd service triggered by a udev rule:

$ cat /etc/udev/rules.d/99-wacom.rules
SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="056a", ENV{ID_MODEL_ID}=="0302", TAG+="systemd"

Vendor and model id can be found running lsusb with the device plugged in.

To reload udev rules:

$ udevadm control --reload-rules
$ udevadm trigger

The TAG+="systemd" enables other systemd services (system or user) to depend on the device (registers it as a device unit, see man systemd.device). To find the name of the device unit run udevadm monitor and plug in the tablet. I get

UDEV  [2918.098423] add      /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3 (usb)
...

To check that systemd is picking it up do

$ systemctl status /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/
● sys-devices-pci0000:00-0000:00:1d.0-usb2-2\x2d1-2\x2d1.3.device - CTH-480 [Intuos Pen & Touch (S)]
   Loaded: loaded
   Active: active (plugged) since Mon 2016-06-20 11:14:20 UYT; 29min ago
   Device: /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3

So the device unit is sys-devices-pci0000:00-0000:00:1d.0-usb2-2\x2d1-2\x2d1.3.device, and it can be used in the systemd service unit

 $ cat .config/systemd/user/wacom.service    
[Service]
Type=forking
Restart=no
ExecStart=/path/to/wacom-pad-button-setup

[Install]
WantedBy=default.target
WantedBy=sys-devices-pci0000:00-0000:00:1d.0-usb2-2\x2d1-2\x2d1.1.device
WantedBy=sys-devices-pci0000:00-0000:00:1d.0-usb2-2\x2d1-2\x2d1.2.device
WantedBy=sys-devices-pci0000:00-0000:00:1d.0-usb2-2\x2d1-2\x2d1.3.device

There is one device unit per usb port.

Then enable and reload the unit with systemctl --user enable wacom.service and systemctl --user daemon-reload.

The script still needs to sleep a bit for xsetwacom to find the device, and to set $DISPLAY and $XAUTHORITY. Type=oneshot works fine when plugging it in, but it doesn't run it if the device was already plugged in when booting the computer. That is why I needed to use a user service instead of a system one, and why the unit also has WantedBy=default.target. The problem with oneshot is that it was blocking startx. Type=forking and Restart=no tells systemd not to wait for the script's forked process to exit, so the script can then sleep in the background waiting for Xorg to start.

$ cat bin/wacom-pad-button-setup
#!/bin/rc
{
    sleep 2

    if (~ $DISPLAY ()) {
        DISPLAY=:0
        XAUTHORITY=/home/spelufo/.Xauthority
    }

    xsetwacom set 'Wacom Intuos PT S Pad pad' button 9 'button +3 -3'
    xsetwacom set 'Wacom Intuos PT S Pad pad' button 8 'button +4 -4'
    xsetwacom set 'Wacom Intuos PT S Pad pad' button 3 'button +1 -1'
    xsetwacom set 'Wacom Intuos PT S Pad pad' button 1 'button +2 -2'
} &

Solution 4

The workaround by derobert is not suitable for all situations (if you can't use the xorg.conf).

The proposed wrap and sleep solution by Adrian is somehow not working for me (ubuntu 16.04).

If you add this to the top of your xsetwacom script:

exec > /tmp/debug-my-script.txt 2>&1
xinput --list

You can see from the output that the xsetwacom script is somehow still executed before xinput is aware of the wacom. No matter how long you make the sleep.

What I propose here is another solution/workaround using the little program at which is simpler than the solution by spelufo (which I didn't try) but only requires to install the at program. (sudo apt install at for debian users).

Now change your wrapper script (Adrian's answer) to something like this:

#!/usr/bin/env bash
at now -f /usr/local/bin/setupwacom-post-X11.sh

at is normally used to schedule a command once, you can for example schedule one hour ahead by at now +1 hours -f yourscript.sh. But since you can only add minutes/hours/days/weeks I used now without addition but rely on the sleep inside the xsetwacom script.

Solution 5

When you plug in the device:

  1. Linux detects the device and creates a device entry based on udev rules.
  2. The X server detects the device.

You cannot run xsetwacom before stage 2. Your script is failing because you're running it at stage 1, when X doesn't know the device yet.

You can set some settings with gnome-settings-daemon. I believe it gets its notification of the new device through D-Bus, but I don't know what the D-Bus event looks like. Try spying on the bus with dbus-monitor.

Share:
5,683
Redsandro
Author by

Redsandro

Lazy Media Creator #SOreadytohelp

Updated on September 18, 2022

Comments

  • Redsandro
    Redsandro over 1 year

    I made a script to rotate my Wacom Bamboo tablet 180 degrees. It works fine when I execute it as myself (user) or root, but when started from udev (i.e. when plugging the tablet into a usb port) it won't work.

    Udev rules:

    SUBSYSTEMS=="usb", ATTRS{idVendor}=="056a", ATTRS{idProduct}=="00d1", ATTRS{manufacturer}=="Wacom Co.,Ltd.", RUN+="/usr/local/bin/red-wacom-bamboo.sh"
    

    Wacom script /usr/local/bin/red-wacom-bamboo.sh:

    #!/usr/bin/env bash
    
    exec > /tmp/red-wacom.log
    exec 2>&1
    
    # I had to do this otherwise xsetwacom would say:
    # "Failed to open Display ."
    # Is there a way to do this without using my username?
    export XAUTHORITY=/home/redsandro/.Xauthority
    export DISPLAY=:0
    
    /usr/bin/xsetwacom set "Wacom Bamboo 2FG 4x5 Pen stylus" Rotate half
    /usr/bin/xsetwacom set "Wacom Bamboo 2FG 4x5 Finger touch" Rotate half
    

    Result in /tmp/red-wacom.log:

    Cannot find device 'Wacom Bamboo 2FG 4x5 Pen stylus'.
    Cannot find device 'Wacom Bamboo 2FG 4x5 Finger touch'.
    

    (Note that the error in the log means the udev rule itself is not the problem.)

    I tried setting a sleep in the script, maybe it needs a few ms. But that doesn't help.

    • Why doesn't this script work when called directly from udev?
      • How do I fix this?
    • Can I call a script from udev as a specific user? (e.g. sync /home to external backup drive - /home/ is only visible to it's user)
  • Redsandro
    Redsandro about 11 years
    I upvoted your answer for the details, but I am not sure this is correct for the following reason: I tried using sleep with a bunch of seconds. When plugging in, the tablet works after less than a second, so by the time the commands get executed, the device is already detected and in use by X. But still it doesn't work?
  • derobert
    derobert about 11 years
    @Redsandro /usr/share/X11/xorg.conf.d/50-wacom.conf is 50 on my system, so I picked 55 to come after it. Not sure that even matters.
  • Lqueryvg
    Lqueryvg over 8 years
    This answer provides useful information but doesn't answer the original question. What if you plug in your USB Wacom device after the X server has started ?
  • derobert
    derobert over 8 years
    @Lqueryvg InputClass applies to hotplugged devices, so it should work then too.
  • Lqueryvg
    Lqueryvg over 8 years
    @derobert, thanks for responding. I didn't realise InputClass worked for hotplug too. I have some button events which I map using xsetwacom and I'd like to trigger them if I hotplug my tablet after X has started. I will give this a try. Thanks !
  • Lqueryvg
    Lqueryvg over 8 years
    Works a treat. Whether the Wacom is plugged in before or after X is started. And saves having a convoluted approach combining xinitrc scripts and udev. No need to use xsetwacom any more. The only (small) downside is that it's not user specific, i.e. the setting applies for all users. But for a single user's PC this is perfect.
  • Jan
    Jan over 7 years
    I dont know why it was not the case in your scenario, but I had to escape at least the \x to \\x in the service file. My WantedBy's looked like this in the end: WantedBy=sys-devices-pci0000\:00-0000\:00\:14.0-usb1-1\\x2d4‌​.device and now they are triggered... before they didnt.
  • Gaurav
    Gaurav about 6 years
    Faced the same issue with Adrian's suggestion and this fixed it for me on 16.04 Ubuntu. I baffles me how it started working with at now without any forking. Any reason for that? Actually with the at method it doesn't even need the wrapper script. You can directly add it as .., RUN+="/usr/bin/at now -f script-path" :)
  • BozanicJosip
    BozanicJosip almost 4 years
    This is so crazy since I didn't even have the sleep in the script and it's working without it. I wish I know what's happening in the background so that it's working with at