Microsoft Pen Protocol support

I'm running Ubuntu 19.10 on a Dell Inspiron 13 7000 2-in-1 Black Edition (7391). The laptop comes with a really nice stylus, the Dell PN350M Active Pen, which unfortunately does not work under my system at all. A little bit of digging around the web reveals that the pen uses the Microsoft Pen Protocol to pair with a laptop instead of Bluetooth. Are these supported? Google leaves me with no answers.

EDIT:

Below is an updated output of xinput. There's something suspicious here, in particular the "UNKNOWN" thing.

~> xinput list
⎡ Virtual core pointer                      id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ DELL0960:00 04F3:30E4 Touchpad            id=9    [slave  pointer  (2)]
⎜   ↳ CUST0000:00 27C6:0111                     id=10   [slave  pointer  (2)]
⎜   ↳ CUST0000:00 27C6:0111 UNKNOWN             id=11   [slave  pointer  (2)]
⎜   ↳ PS/2 Generic Mouse                        id=16   [slave  pointer  (2)]
⎜   ↳ M585/M590 Mouse                           id=18   [slave  pointer  (2)]
⎣ Virtual core keyboard                     id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
    ↳ Power Button                              id=7    [slave  keyboard (3)]
    ↳ Video Bus                                 id=6    [slave  keyboard (3)]
    ↳ Dell WMI hotkeys                          id=14   [slave  keyboard (3)]
    ↳ Intel HID 5 button array                  id=13   [slave  keyboard (3)]
    ↳ Intel HID events                          id=12   [slave  keyboard (3)]
    ↳ Integrated_Webcam_HD: Integrate           id=8    [slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard              id=15   [slave  keyboard (3)]
    ↳ M585/M590 Keyboard                        id=17   [slave  keyboard (3)]



Solution 1:

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)

Solution 2:

have the same question. I am on the 15" silver. had an idea that you could potentially use wacom drivers that use that protocol? but other than that you would somehow have to get your hands on the mpp drivers, although im not sure where