2

I'm running Ubuntu 20.04 on a Dell Inspiron 15 7591 2-in-1. The laptop comes with a stylus, the Dell PN350M Active Pen, which unfortunately does not work properly under my system. I found out that the pen uses Microsoft Pen Protocol to pair with my device. Below are some of the outputs that might help with solving this issue.

I found out that sudo evtest shows the pressure readings perfectly however, xinput --test doesn't show the pressure readings at all and the tip is considered as Button 1.

sudo evtest
[sudo] password for parth: 
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0:  Lid Switch
/dev/input/event1:  Power Button
/dev/input/event2:  Sleep Button
/dev/input/event3:  AT Translated Set 2 keyboard
/dev/input/event4:  Integrated_Webcam_HD: Integrate
/dev/input/event5:  DELL0908:00 04F3:30E3 Mouse
/dev/input/event6:  Dell WMI hotkeys
/dev/input/event7:  Intel HID events
/dev/input/event8:  Intel HID 5 button array
/dev/input/event9:  PS/2 Generic Mouse
/dev/input/event10: 2.4G Mouse
/dev/input/event11: 2.4G Mouse
/dev/input/event12: DELL0908:00 04F3:30E3 Touchpad
/dev/input/event13: Video Bus
/dev/input/event14: Video Bus
/dev/input/event15: CUST0000:00 27C6:0118
/dev/input/event16: CUST0000:00 27C6:0118 Stylus
/dev/input/event17: CUST0000:00 27C6:0118 UNKNOWN
/dev/input/event18: sof-hda-dsp Headphone Mic
/dev/input/event19: sof-hda-dsp HDMI/DP,pcm=3
/dev/input/event20: sof-hda-dsp HDMI/DP,pcm=4
/dev/input/event21: sof-hda-dsp HDMI/DP,pcm=5
Select the device event number [0-21]: 16
Input driver version is 1.0.1
Input device ID: bus 0x18 vendor 0x27c6 product 0x118 version 0x100
Input device name: "CUST0000:00 27C6:0118 Stylus"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
  Event type 3 (EV_ABS)
    Event code 47 (ABS_MT_SLOT)
      Value      0
      Min        0
      Max        9
    Event code 53 (ABS_MT_POSITION_X)
      Value      0
      Min        0
      Max     5760
      Resolution      17
    Event code 54 (ABS_MT_POSITION_Y)
      Value      0
      Min        0
      Max     3240
      Resolution      17
    Event code 57 (ABS_MT_TRACKING_ID)
      Value      0
      Min        0
      Max    65535
    Event code 58 (ABS_MT_PRESSURE)
      Value      0
      Min        0
      Max     1023
    Event code 59 (ABS_MT_DISTANCE)
      Value      0
      Min        0
      Max        1
Properties:
  Property type 1 (INPUT_PROP_DIRECT)
Testing ... (interrupt to exit)
Event: time 1599396868.187587, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value 43
Event: time 1599396868.187587, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 3767
Event: time 1599396868.187587, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 1597
Event: time 1599396868.187587, -------------- SYN_REPORT ------------
Event: time 1599396868.189591, type 3 (EV_ABS), code 59 (ABS_MT_DISTANCE), value 0
Event: time 1599396868.189591, type 3 (EV_ABS), code 58 (ABS_MT_PRESSURE), value 11
Event: time 1599396868.189591, -------------- SYN_REPORT ------------
xinput --test 15
motion a[0]=44467 a[1]=32959 
button press   1 a[0]=44467 a[1]=32959 
motion a[0]=44467 a[1]=32939 
motion a[0]=44490 a[1]=32919 
motion a[0]=44501 a[1]=32878 
motion a[0]=44524 a[1]=32858 
motion a[0]=44546 a[1]=32818 
motion a[0]=44592 a[1]=32757 
motion a[0]=44626 a[1]=32716 
motion a[0]=44672 a[1]=32676 
motion a[0]=44717 a[1]=32656 
motion a[0]=44774 a[1]=32615 
motion a[0]=44831 a[1]=32595 
motion a[0]=44888 a[1]=32575 
motion a[0]=44956 a[1]=32575 
motion a[0]=45013 a[1]=32575 
motion a[0]=45070 a[1]=32575 
motion a[0]=45127 a[1]=32555 
motion a[0]=45195 a[1]=32555 
motion a[0]=45252 a[1]=32534 
motion a[0]=45309 a[1]=32514 
motion a[0]=45366 a[1]=32494 
motion a[0]=45422 a[1]=32454 
motion a[0]=45491 a[1]=32433 
motion a[0]=45548 a[1]=32413 
motion a[0]=45616 a[1]=32393 
motion a[0]=45695 a[1]=32373 
motion a[0]=45775 a[1]=32332 
motion a[0]=45832 a[1]=32312 
motion a[0]=45877 a[1]=32292 
motion a[0]=45912 a[1]=32292 
motion a[0]=45934 a[1]=32272 
motion a[0]=45934 a[1]=32251 
motion a[0]=45946 a[1]=32231 
motion a[0]=45946 a[1]=32211 
motion a[0]=45946 a[1]=32191 
motion a[0]=45957 a[1]=32150 
motion a[0]=45957 a[1]=32110 
motion a[0]=45968 a[1]=32049 
motion a[0]=45980 a[1]=32009 
motion a[0]=45991 a[1]=31928 
motion a[0]=46048 a[1]=31665 
motion a[0]=46037 a[1]=31705 
motion a[0]=46105 a[1]=31422 
motion a[0]=46094 a[1]=31443 
motion a[0]=46105 a[1]=31402 
motion a[0]=46116 a[1]=31341 
motion a[0]=46116 a[1]=31341 
button release 1 a[0]=46116 a[1]=31341

Any help would be appreciated!

2
  • Related to askubuntu.com/q/1189075/26246 if not a duplicate.
    – user.dz
    Jun 16, 2021 at 10:34
  • I have the Inspiron 15 7590 for almost two years, and had the same problem with no resolution. BUT now, at last (!!!), on Fedora+KDE with Kernel 5.12 it works perfectly with no hacks or tricks
    – Zeev
    Jun 28, 2021 at 22:51

1 Answer 1

3

I managed to create a python driver using the evdev and libevdev libraries. I'm creating a virtual device and then simply passing the events I found from the stylus. It's working with pressure events and tracking however I couldn't get the buttons to work.

Just copy this and mark it as executable and you shall see a device called "Custom Stylus" that should work. Futher to make it seamless I added it to the gnome-session-properties so that it runs the script on startup.

Hope this helps!

#!/usr/bin/env python3
import sys
import libevdev
import time
import evdev
from evdev import UInput, AbsInfo, ecodes
import os


def main(args):
    p = 0
    i = 0
    devices = evdev.list_devices()

    for dev in devices:
        # print('%-12i%s' % (p, evdev.InputDevice(dev).name))
        if evdev.InputDevice(dev).name == 'CUST0000:00 27C6:0118 Stylus':
            i = p
        p += 1

    # i=int(input("Enter number: "))

    device = evdev.InputDevice(devices[i])

    print(device)

    # print(mouse.capabilities(verbose=True))

    device.grab()

    x, y = 0, 0

    dev = libevdev.Device()
    dev.name = "Custom Stylus"

    dev.enable(libevdev.INPUT_PROP_DIRECT)
    dev.enable(libevdev.EV_KEY.BTN_TOOL_PEN)
    dev.enable(libevdev.EV_KEY.BTN_TOOL_RUBBER)
    # Click
    dev.enable(libevdev.EV_KEY.BTN_TOUCH)
    # Press button 1 on pen
    dev.enable(libevdev.EV_KEY.BTN_STYLUS)
    # Press button 2 on pen, see great doc
    dev.enable(libevdev.EV_KEY.BTN_STYLUS2)
    # Send absolute X coordinate
    dev.enable(libevdev.EV_ABS.ABS_X,
               libevdev.InputAbsInfo(minimum=0, maximum=5760, resolution=17))
    # Send absolute Y coordinate
    dev.enable(libevdev.EV_ABS.ABS_Y,
               libevdev.InputAbsInfo(minimum=0, maximum=3240, resolution=17))
    # Send absolute pressure
    dev.enable(libevdev.EV_ABS.ABS_PRESSURE,
               libevdev.InputAbsInfo(minimum=0, maximum=1023))
    dev.enable(libevdev.EV_SYN.SYN_REPORT)
    dev.enable(libevdev.EV_SYN.SYN_DROPPED)
    try:
        uinput = dev.create_uinput_device()
        print("New device at {} ({})".format(uinput.devnode, uinput.syspath))
        # Sleep for a bit so udev, libinput, Xorg, Wayland, ...
        # all have had a chance to see the device and initialize
        # it. Otherwise the event will be sent by the kernel but
        # nothing is ready to listen to the device yet. And it
        # will never be detected in the futur ;-)
        time.sleep(1)
        # Reports that the PEN is close to the surface
        # Important to make sure xinput can detect (and list)
        # the pen. Otherwise, it won't write anything in gimp.
        uinput.send_events([
            libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
                                value=0),
            libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
                                value=1),
            libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                                value=0),
        ])
        # Says that the pen it out of range of the tablet. Useful
        # to make sure you can move your mouse, and to avoid
        # strange things during the first draw.
        uinput.send_events([
            libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
                                value=0),
            libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
                                value=0),
            libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                                value=0),
        ])

        for event in device.read_loop():
            code, val = event.code, event.value

            if code == ecodes.ABS_MT_POSITION_X:
                uinput.send_events([
                    libevdev.InputEvent(libevdev.EV_ABS.ABS_X,
                                        value=int(val)),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
                                        value=1),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS,
                                        value=0),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2,
                                        value=0),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
                                        value=1),
                    libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                                        value=0)])
                # vpen.write(ecodes.EV_ABS, ecodes.ABS_X, int(val))

            if code == ecodes.ABS_MT_POSITION_Y:
                uinput.send_events([
                    libevdev.InputEvent(libevdev.EV_ABS.ABS_Y,
                                        value=int(val)),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
                                        value=1),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS,
                                        value=0),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2,
                                        value=0),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
                                        value=1),
                    libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                                        value=0)])

            if code == ecodes.ABS_MT_PRESSURE:
                uinput.send_events([
                    libevdev.InputEvent(libevdev.EV_ABS.ABS_PRESSURE,
                                        value=int(val)),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
                                        value=1),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS,
                                        value=0),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2,
                                        value=0),
                    libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
                                        value=1),
                    libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                                        value=0)])

        device.ungrab()

    except KeyboardInterrupt:
        pass


if __name__ == "__main__":
    main(sys.argv)

1
  • This is a nice approach. Although I wish there were official Ubuntu support for Dell's active pen. I was considering purchasing the same or similar laptop, but now I'm not sure... If I do, I'll try your script and let you know if it works for me. Jun 17, 2021 at 3:14

You must log in to answer this question.

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