Recently I obtained an XP-Pen 4x3" Graphics Tablet for a very low price (about 
$15).
The intended usage is to make handritten notes using Xournal, Xournalpp or 
similar, rather than for illustration work. 

I was not very hopeful of it being supported in OpenBSD, but I am pleased to 
report that it is mostly functional and usable including Pressure Sensitivity. 

The configuration discovery process was a little involved, so I am posting it 
here in the hope the information will be useful to others trying to get a USB 
tablet to work.

The tablet connects via USB and has a single-ended passive stylus. 
I used OpenBSD 7.6-release. The kernel has been patched to make the (PS/2) 
Touchpad work on this particular machine but I don't think that will change how 
a USB tablet works.

In dmesg it attaches as:

uhidev0 at uhub0 port 4 configuration 1 interface 0 "UGTABLET 4 inch PenTablet" 
rev 2.00/0.00 addr 2
uhidev0: iclass 3/1, 9 report ids
ukbd0 at uhidev0 reportid 6: 8 variable keys, 6 key codes
wskbd1 at ukbd0 mux 1
ums0 at uhidev0 reportid 9: 3 buttons
wsmouse1 at ums0 mux 0
uhidev1 at uhub0 port 4 configuration 1 interface 1 "UGTABLET 4 inch PenTablet" 
rev 2.00/0.00 addr 2
uhidev1: iclass 3/1, 7 report ids
uhid0 at uhidev1 reportid 7: input=9, output=0, feature=0
uhidev2 at uhub0 port 4 configuration 1 interface 2 "UGTABLET 4 inch PenTablet" 
rev 2.00/0.00 addr 2
uhidev2: iclass 3/0, 2 report ids
uhid1 at uhidev2 reportid 2: input=11, output=9, feature=0

That is, it appears as a secondary mouse with 3 buttons, and two uhid devices 
uhid0 and uhid1.

Before the tablet is configured to be claimed by a driver such as usbtablet(4), 
it is possible to dump the USB HID report descriptor for each of the uhid  
devices:

usbhidctl -f 0 -r:
Report descriptor:
Collection page=Digitizer usage=Pen
Collection page=Digitizer usage=Stylus
Input   size=1 count=1 page=Digitizer usage=Tip_Switch, logical range 0..1
Input   size=1 count=1 page=Digitizer usage=Barrel_Switch, logical range 0..1
Input   size=1 count=1 page=Digitizer usage=Eraser, logical range 0..1
Input   size=1 count=1 page=Digitizer usage=Invert, logical range 0..1
Input   size=1 count=1 page=Digitizer usage=In_Range, logical range 0..1
Input   size=16 count=1 page=Generic_Desktop usage=X, logical range 0..32767, 
physical range 0..4799, unit=
Input   size=16 count=1 page=Generic_Desktop usage=Y, logical range 0..32767, 
physical range 0..3000, unit=
Input   size=16 count=1 page=Digitizer usage=Tip_Pressure, logical range 0..8191
Input   size=8 count=1 page=Digitizer usage=X_Tilt, logical range -127..127
Input   size=8 count=1 page=Digitizer usage=Y_Tilt, logical range -127..127
End collection
End collection
Total   input size 9 bytes
Total  output size 0 bytes
Total feature size 0 bytes

usbhidctl -f 1 -r:
Report descriptor:
Collection page=0xff0a usage=0x0001
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Input   size=8 count=1 page=0xff0a usage=0x0002, logical range 0..255
Output  size=8 count=1 page=0xff0a usage=0x0003, logical range 0..255
Output  size=8 count=1 page=0xff0a usage=0x0003, logical range 0..255
Output  size=8 count=1 page=0xff0a usage=0x0003, logical range 0..255
Output  size=8 count=1 page=0xff0a usage=0x0003, logical range 0..255
Output  size=8 count=1 page=0xff0a usage=0x0003, logical range 0..255
Output  size=8 count=1 page=0xff0a usage=0x0003, logical range 0..255
Output  size=8 count=1 page=0xff0a usage=0x0003, logical range 0..255
Output  size=8 count=1 page=0xff0a usage=0x0003, logical range 0..255
Output  size=8 count=1 page=0xff0a usage=0x0003, logical range 0..255
End collection
Total   input size 11 bytes
Total  output size 9 bytes
Total feature size 0 bytes

The report for /dev/uhid1 seems to be a vendor custom page, which does not 
appear in  /usr/share/misc/usb_hid_usages  (the default HID usage table), so I 
was not able to make use of this interface.

The report for /dev/uhid0 includes capabilities which this unit does not in 
fact have: the pen does not have an Eraser end, does not work inverted for 
erasing, and doesn't support 'pen tilt'. I put this down to a vendor firmware 
error, maybe using the same firmware as a more capable model in the range.

I created a minimal configuration file for Xenocara in 
/etc/X11/xorg.conf.d/70-usbtablet.conf:

Section "InputDevice"
        Identifier "stylus"
        Driver "usbtablet"
        Option "Device" "/dev/uhid0"
        Option "Type" "stylus"
        Option "Mode" "Absolute"
        Option "AccelerationProfile" "-1" # no acceleration
        Option "AdaptiveDeceleration" "2.0" # deceleration factor when moving 
pointer slowly
#     Option "DebugLevel" "0"
EndSection

# Available if the Tablet Pen has an 'Eraser" end
#Section "InputDevice"
#        Identifier "eraser"
#        Driver "usbtablet"
#        Option "Device" "/dev/uhid0"
#        Option "Type" "eraser"
#        Option "Mode" "Absolute"
#        Option "DebugLevel" "1"
#EndSection

Section "Serverlayout"
        Identifier "Default Layout"
        InputDevice "stylus"
#      InputDevice "eraser"
EndSection

# required to avoid "No Screens found" error
Section "Screen"
        Identifier "Default Screen"
EndSection

Section "ServerFlags"
#       Option "DontZap" "true" # Prevent Ctrl-Alt-Backspace Xserver zap for 
security
        Option "AllowMouseOpenFail" "true" # Don't report failure to open a 
Mouse device
EndSection

Notes:
1. It was not obvious from the usbtablet(4) Manpage that the ServerLayout and 
Screen sections were necessary (to bind the InputDevice and to avoid a 'no 
Screens found' error respectively). 
I realise this is standard xorg.conf configuration procedure, but it took some 
digging in the xorg.conf(5) Manpage to realise that.

2. The "eraser" InputDevice isn't necessary (doesn't appear to add any 
functionality) for this particular device. But if the tablet used actually has 
an Eraser function (rather than just reporting that it has it) then it may be 
useful.

3. With this manual configuration, hotplugging doesn't work for the tablet, 
though other USB devices such as mice can still be hotplugged. So it's 
necessary to either 'pkill Xorg' or reboot to make the tablet work

4. ServerFlags AllowMouseOpenFail stops Xenocara from complaining if the tablet 
isn't connected when it starts. DontZap is to prevent the screenlocker being 
killed.

5. the DebugLevel option is useful to see what events the tablet actually 
produces, rather than what it claims in the HID Report Descriptor. For example, 
DebugLevel 1 shows pen pressure and driver default parameters. I didn't check 
what Debug levels are valid, suspect anything >0 is 'on'.

5. The Threshold Option referred to in the usbtablet(4) Manpage, though an 
integer, turns out to be a percentage of the maximum pressure parameter. So 
with this tablet (pressure range 0-8191), the threshold is set to 419 by 
default.

With the above configuration and 'XP-Pen Deco Fun XS' 4x3" tablet, I got the 
following functionality:

xinput list:
⎡ Virtual core pointer                        id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                    id=4    [slave  pointer  
(2)]
⎜   ↳ Stylus                                        id=6    [slave  pointer  
(2)]
⎜   ↳ /dev/wsmouse0                                 id=8    [slave  pointer  
(2)]
⎜   ↳ /dev/wsmouse1                                 id=9    [slave  pointer  
(2)]
⎜   ↳ /dev/wsmouse                                  id=10   [slave  pointer  
(2)]
⎣ Virtual core keyboard                       id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard                     id=5    [slave  keyboard 
(3)]
    ↳ /dev/wskbd                                      id=7    [slave  keyboard 
(3)]

xinput list 6:
Stylus                                          id=6    [slave  pointer  (2)]
        Reporting 6 classes:
                Class originated from: 6. Type: XIButtonClass
                Buttons supported: 4
                Button labels: None None None None
                Button state:
                Class originated from: 6. Type: XIValuatorClass
                Detail for Valuator 0:
                  Label: None
                  Range: 0.000000 - 1920.000000
                  Resolution: 393700 units/m
                  Mode: absolute
                  Current value: 1920.000000
                Class originated from: 6. Type: XIValuatorClass
                Detail for Valuator 1:
                  Label: None
                  Range: 0.000000 - 1200.000000
                  Resolution: 393700 units/m
                  Mode: absolute
                  Current value: 1200.000000
                Class originated from: 6. Type: XIValuatorClass
                Detail for Valuator 2:
                  Label: None
                  Range: 0.000000 - 8191.000000
                  Resolution: 393700 units/m
                  Mode: absolute
                  Current value: 0.000000
                Class originated from: 6. Type: XIValuatorClass
                Detail for Valuator 3:
                  Label: None
                  Range: 0.000000 - 0.000000
                  Resolution: 393700 units/m
                  Mode: absolute
                  Current value: 0.000000
                Class originated from: 6. Type: XIValuatorClass
                Detail for Valuator 4:
                  Label: None
                  Range: 0.000000 - 0.000000
                  Resolution: 393700 units/m
                  Mode: absolute
                  Current value: 0.000000
 
xinput list 9:
/dev/wsmouse1                                   id=9    [slave  pointer  (2)]
        Reporting 7 classes:
                Class originated from: 9. Type: XIButtonClass
                Buttons supported: 7
                Button labels: "Button Left" "Button Middle" "Button Right" 
None None None None
                Button state:
                Class originated from: 9. Type: XIValuatorClass
                Detail for Valuator 0:
                  Label: Abs X
                  Range: 0.000000 - 32767.000000
                  Resolution: 1 units/m
                  Mode: absolute
                  Current value: 960.000000
                Class originated from: 9. Type: XIValuatorClass
                Detail for Valuator 1:
                  Label: Abs Y
                  Range: 0.000000 - 32767.000000
                  Resolution: 1 units/m
                  Mode: absolute
                  Current value: 600.000000
                Class originated from: 9. Type: XIValuatorClass
                Detail for Valuator 2:
                  Label: Rel Horiz Scroll
                  Range: 0.000000 - -1.000000
                  Resolution: 0 units/m
                  Mode: relative
                Class originated from: 9. Type: XIValuatorClass
                Detail for Valuator 3:
                  Label: Rel Vert Scroll
                  Range: 0.000000 - -1.000000
                  Resolution: 0 units/m
                  Mode: relative
                Class originated from: 9. Type: XIScrollClass
                Scroll info for Valuator 2
                  type: 2 (horizontal)
                  increment: 4096.000000
                  flags: 0x0
                Class originated from: 9. Type: XIScrollClass
                Scroll info for Valuator 3
                  type: 1 (vertical)
                  increment: 4096.000000
                  flags: 0x0

- Drawing with pressure sensitivity works in Xournal, Inkscape and LibreOffice 
Draw (and maybe other applications that use Xinput)
- Pressure sensitivity doesn't work in Xournal++. I tried adjusting the 
pressure scaling but with no result. Maybe it doesn't use Xinput?
- Only one of the two 'barrel buttons' on the pen works. The tip is button1, 
the lower button is button2. Button1 needs to be inactive (pen lifted out of 
contact with the tablet a little) for button2 to be read. Button3 is absent. 
This was tested using 
'xev -event button'.

Perhaps the 70-usbtablet.conf above would be a candidate for inclusion in 
/etc/examples? 
Alternatively, a line could be added to the usbtablet(4) Manpage to say that 
ServerLayout and Screen sections are necessary in the configuration file to 
make the driver bind.

Comments and corrections to the above are welcome. Does anyone have any idea, 
for example, why the second barrel button does not appear to work, while 
/dev/wsmouse1 reports 3 buttons?

Chris Billington

Reply via email to