Graphic Tablet VEIKK: pressure sensitivity on LinuxSimulate graphics tablet using libeventDoes anyone know how to configure a Wacom Bamboo tablet to work left-handled?Linux on a tegra tablet for programmingBuild an Affordable Linux TabletTablet evdev button event calibrationInstalling Linux on a tabletNative Linux installation on Allwinner tabletMatch graphic tablet active area to monitor subarea in a dual monitor setupDesktop Linux on LG V500 android tabletRestrict graphic tablet to primary displayGraphic tablet buttons

How do I go from 300 unfinished/half written blog posts, to published posts?

How to run a prison with the smallest amount of guards?

How can a function with a hole (removable discontinuity) equal a function with no hole?

How to write papers efficiently when English isn't my first language?

How can we prove that any integral in the set of non-elementary integrals cannot be expressed in the form of elementary functions?

What does "I’d sit this one out, Cap," imply or mean in the context?

Detecting if an element is found inside a container

Is HostGator storing my password in plaintext?

Trouble understanding the speech of overseas colleagues

What does the word "Atten" mean?

How do I extract a value from a time formatted value in excel?

How to be diplomatic in refusing to write code that breaches the privacy of our users

when is out of tune ok?

How do I find the solutions of the following equation?

Purchasing a ticket for someone else in another country?

Implement the Thanos sorting algorithm

Go Pregnant or Go Home

Would a high gravity rocky planet be guaranteed to have an atmosphere?

CREATE opcode: what does it really do?

Was Spock the First Vulcan in Starfleet?

How does the UK government determine the size of a mandate?

Why didn't Theresa May consult with Parliament before negotiating a deal with the EU?

How to safely derail a train during transit?

How long to clear the 'suck zone' of a turbofan after start is initiated?



Graphic Tablet VEIKK: pressure sensitivity on Linux


Simulate graphics tablet using libeventDoes anyone know how to configure a Wacom Bamboo tablet to work left-handled?Linux on a tegra tablet for programmingBuild an Affordable Linux TabletTablet evdev button event calibrationInstalling Linux on a tabletNative Linux installation on Allwinner tabletMatch graphic tablet active area to monitor subarea in a dual monitor setupDesktop Linux on LG V500 android tabletRestrict graphic tablet to primary displayGraphic tablet buttons













0















I just bought the graphical tablet VEIKK A30. The mouse is recognized directly. Unfortunately, I can't find how to enable pressure sensitivity on Linux. I saw one guy reporting that with some tweaking it was possible to make it work, but can't find any reference on that.



Edit: here is the output of dmesg



[mars19 01:15] usb 2-1: new full-speed USB device number 10 using xhci_hcd
[ +0,153026] usb 2-1: New USB device found, idVendor=2feb, idProduct=0002, bcdDevice= 0.00
[ +0,000006] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ +0,000004] usb 2-1: Product: A30
[ +0,000003] usb 2-1: Manufacturer: VEIKK.INC
[ +0,000004] usb 2-1: SerialNumber: 0000001
[ +0,003052] input: VEIKK.INC A30 Mouse as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.0/0003:2FEB:0002.000C/input/input64
[ +0,064250] input: VEIKK.INC A30 as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.0/0003:2FEB:0002.000C/input/input65
[ +0,000429] hid-generic 0003:2FEB:0002.000C: input,hidraw0: USB HID v1.00 Mouse [VEIKK.INC A30] on usb-0000:00:14.0-1/input0
[ +0,001079] input: VEIKK.INC A30 as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.1/0003:2FEB:0002.000D/input/input66
[ +0,062599] hid-generic 0003:2FEB:0002.000D: input,hidraw1: USB HID v1.00 Keyboard [VEIKK.INC A30] on usb-0000:00:14.0-1/input1
[ +0,001208] hid-generic 0003:2FEB:0002.000E: hiddev0,hidraw2: USB HID v1.00 Device [VEIKK.INC A30] on usb-0000:00:14.0-1/input2


Gimp display:
enter image description here



Edit2: hum, it seems that the device that sends the pressure can be read in sudo cat /dev/hidraw0 as the patterns differs depending the the pressure I apply. I don't know how to read this binary though ^^ If I can, maybe I can use uinput to map it to a new device? ^^ Note that /dev/input contains files like mouseX, eventY, and mouse1 is correlated with the tablet, but not with the pressure, and the only file in this folder that displays lot's of information linked with the tablet is the file /dev/input/by-id/usb-VEIKK.INC_A30_0000001-event-mouse. But it's less clear that the patterns match the pressure, too many information are sent here. If you know how to parse them let me know!



Edit3:
So I don't yet have a driver, but at least it seems to be easy to read the input from the device raw communication. I made this python script as a proof of concept:



#!/usr/bin/env python3
import struct
PRINT_TIMESTAMP = True
# Open the file in the read-binary mode
f = open("/dev/input/by-id/usb-VEIKK.INC_A30_0000001-event-mouse", "rb" )

while 1:
data = f.read(24)
# print struct.unpack('4IHHI',data)
###### FORMAT = ( Time Stamp_INT , 0 , Time Stamp_DEC , 0 ,
###### type , code ( key pressed ) , value (press/release) )
time_int, _, time_dec, _, ev_type, ev_code, ev_val = struct.unpack('4IHHI',data)
t = (ev_type, ev_code)
if ((t == (0,0) and ev_val == 0)
or (t == (4, 4) and ev_val >= 589825 and ev_val <= 589827)):
# Redundant as it's for normal/bottom/top clicks
# (same code for press/release), or just garbage 0,0,0
continue
if PRINT_TIMESTAMP:
print("[:.2f] ".format(time_int + time_dec/1e6),
end="", flush=True)
if t == (3,0):
print("Pos x: (:.2f%)".format(ev_val, 100*ev_val/32767), flush=True)
elif t == (3,1):
print("Pos y: (:.2f%)".format(ev_val, 100*ev_val/32767), flush=True)
elif t == (3,24):
print("Pression: (:.2f%)".format(ev_val, 100*ev_val/8191), flush=True)
elif t == (1,272):
print("Normal click ()".format("press" if ev_val else "release"), flush=True)
elif t == (1,273):
print("click button 2 (bottom) ()".format("press" if ev_val else "release"), flush=True)
elif t == (1,274):
print("click button 3 (top) ()".format("press" if ev_val else "release"), flush=True)
else:
print("Unknow: type=, code=, value=".format(ev_type, ev_code, ev_val), flush=True)


Démo:



[1553182025.55] Pos y: 11458 (34.97%)
[1553182025.55] Pos x: 14310 (43.67%)
[1553182025.56] Pos x: 14314 (43.68%)
[1553182025.56] Pos x: 14318 (43.70%)
[1553182025.57] Pos x: 14321 (43.71%)
[1553182025.57] Normal click (press)
[1553182025.57] Pos x: 14323 (43.71%)
[1553182025.57] Pression: 1122 (13.70%)
[1553182025.57] Pos x: 14326 (43.72%)
[1553182025.57] Pos y: 11466 (34.99%)
[1553182025.57] Pression: 1260 (15.38%)
[1553182025.58] Pos x: 14329 (43.73%)
[1553182025.58] Pression: 1337 (16.32%)
[1553182025.58] Pos x: 14330 (43.73%)
[1553182025.58] Pos y: 11494 (35.08%)
[1553182025.58] Pression: 1515 (18.50%)
[1553182025.59] Pos y: 11506 (35.11%)
[1553182025.59] Pression: 1687 (20.60%)
[1553182025.59] Pos y: 11517 (35.15%)
[1553182025.59] Pression: 1689 (20.62%)
[1553182025.59] Pos y: 11529 (35.18%)
[1553182025.59] Pression: 1789 (21.84%)
[1553182025.60] Pos y: 11536 (35.21%)
[1553182025.60] Pression: 1829 (22.33%)
[1553182025.60] Pos y: 11542 (35.22%)
[1553182025.60] Pression: 1907 (23.28%)
[1553182025.61] Pression: 2031 (24.80%)
[1553182025.61] Pos y: 11549 (35.25%)
[1553182025.61] Pression: 2140 (26.13%)


Edit 4:
Amazing page: https://digimend.github.io/support/howto/trbl/locating_failure/ However, everything works here... except at the very last step when I want to test it. I tried to test with MyPaint, and it does not detect pressure.



I also tried to do my own code that basically copies the input from the event file into a new device like that:



#!/usr/bin/env python3
import sys
import libevdev
import time

def print_capabilities(l):
v = l.driver_version
print("Input driver version is ..".format(v >> 16, (v >> 8) & 0xff, v & 0xff))
id = l.id
print("Input device ID: bus :#x vendor :#x product :#x version :#x".format(
id["bustype"],
id["vendor"],
id["product"],
id["version"],
))
print("Input device name: ".format(l.name))
print("Supported events:")

for t, cs in l.evbits.items():
print(" Event type ()".format(t.value, t.name))

for c in cs:
if t in [libevdev.EV_LED, libevdev.EV_SND, libevdev.EV_SW]:
v = l.value[c]
print(" Event code () state ".format(c.value, c.name, v))
else:
print(" Event code ()".format(c.value, c.name))

if t == libevdev.EV_ABS:
a = l.absinfo[c]
print(" :10s :6d".format('Value', a.value))
print(" :10s :6d".format('Minimum', a.minimum))
print(" :10s :6d".format('Maximum', a.maximum))
print(" :10s :6d".format('Fuzz', a.fuzz))
print(" :10s :6d".format('Flat', a.flat))
print(" :10s :6d".format('Resolution', a.resolution))

print("Properties:")
for p in l.properties:
print(" Property type ()".format(p.value, p.name))


def print_event(e):
print("Event: time .:06d, ".format(e.sec, e.usec), end='')
if e.matches(libevdev.EV_SYN):
if e.matches(libevdev.EV_SYN.SYN_MT_REPORT):
print("++++++++++++++ ++++++++++++".format(e.code.name))
elif e.matches(libevdev.EV_SYN.SYN_DROPPED):
print(">>>>>>>>>>>>>> >>>>>>>>>>>>".format(e.code.name))
else:
print("-------------- ------------".format(e.code.name))
else:
print("type :02x code :03x :20s value :4d".format(e.type.value, e.type.name, e.code.value, e.code.name, e.value))


def main(args):
path = args[1]
dev = libevdev.Device()
dev.name = "Combined Both Devices"
dev.enable(libevdev.EV_ABS.ABS_X,
libevdev.InputAbsInfo(minimum=0, maximum=32767))
dev.enable(libevdev.EV_ABS.ABS_Y,
libevdev.InputAbsInfo(minimum=0, maximum=32767))
dev.enable(libevdev.EV_ABS.ABS_Z,
libevdev.InputAbsInfo(minimum=0, maximum=8191))
dev.enable(libevdev.EV_ABS.ABS_0B,
libevdev.InputAbsInfo(minimum=0, maximum=8191))
dev.enable(libevdev.EV_ABS.ABS_DISTANCE,
libevdev.InputAbsInfo(minimum=0, maximum=8191))
dev.enable(libevdev.EV_ABS.ABS_PRESSURE,
libevdev.InputAbsInfo(minimum=0, maximum=8191))
dev.enable(libevdev.EV_MSC.MSC_SCAN)
dev.enable(libevdev.EV_KEY.BTN_LEFT)
dev.enable(libevdev.EV_KEY.BTN_RIGHT)
dev.enable(libevdev.EV_KEY.BTN_MIDDLE)
dev.enable(libevdev.EV_KEY.BTN_TOUCH)
dev.enable(libevdev.EV_SYN.SYN_REPORT)
dev.enable(libevdev.EV_SYN.SYN_CONFIG)
dev.enable(libevdev.EV_SYN.SYN_MT_REPORT)
dev.enable(libevdev.EV_SYN.SYN_DROPPED)
dev.enable(libevdev.EV_SYN.SYN_04)
dev.enable(libevdev.EV_SYN.SYN_05)
dev.enable(libevdev.EV_SYN.SYN_06)
dev.enable(libevdev.EV_SYN.SYN_07)
dev.enable(libevdev.EV_SYN.SYN_08)
dev.enable(libevdev.EV_SYN.SYN_09)
dev.enable(libevdev.EV_SYN.SYN_0A)
dev.enable(libevdev.EV_SYN.SYN_0B)
dev.enable(libevdev.EV_SYN.SYN_0C)
dev.enable(libevdev.EV_SYN.SYN_0D)
dev.enable(libevdev.EV_SYN.SYN_0E)
dev.enable(libevdev.EV_SYN.SYN_MAX)
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.
time.sleep(1)

with open(path, "rb") as fd:
l = libevdev.Device(fd)
print_capabilities(l)
print("################################n"
"# Waiting for events #n"
"################################")
while True:
try:
ev = l.events()
for e in ev:
uinput.send_events([e])
print_event(e)
if e.matches(libevdev.EV_ABS.ABS_PRESSURE):
print("Pressure! Will send another packeton Z axis!")
uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_Z, e.value)])
uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_0B, e.value)])
uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_DISTANCE, e.value)])

except libevdev.EventsDroppedException:
for e in l.sync():
print_event(e)
uinput.send_events([e])
except KeyboardInterrupt:
pass
except IOError as e:
import errno
if e.errno == errno.EACCES:
print("Insufficient permissions to access ".format(path))
elif e.errno == errno.ENOENT:
print("Device does not exist".format(path))
else:
raise e
except OSError as e:
print(e)


if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: /dev/input/eventX".format(sys.argv[0]))
sys.exit(1)
main(sys.argv)


and I realized that it's more or less the same as the Veikk mouse, and it does not work better.










share|improve this question




























    0















    I just bought the graphical tablet VEIKK A30. The mouse is recognized directly. Unfortunately, I can't find how to enable pressure sensitivity on Linux. I saw one guy reporting that with some tweaking it was possible to make it work, but can't find any reference on that.



    Edit: here is the output of dmesg



    [mars19 01:15] usb 2-1: new full-speed USB device number 10 using xhci_hcd
    [ +0,153026] usb 2-1: New USB device found, idVendor=2feb, idProduct=0002, bcdDevice= 0.00
    [ +0,000006] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
    [ +0,000004] usb 2-1: Product: A30
    [ +0,000003] usb 2-1: Manufacturer: VEIKK.INC
    [ +0,000004] usb 2-1: SerialNumber: 0000001
    [ +0,003052] input: VEIKK.INC A30 Mouse as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.0/0003:2FEB:0002.000C/input/input64
    [ +0,064250] input: VEIKK.INC A30 as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.0/0003:2FEB:0002.000C/input/input65
    [ +0,000429] hid-generic 0003:2FEB:0002.000C: input,hidraw0: USB HID v1.00 Mouse [VEIKK.INC A30] on usb-0000:00:14.0-1/input0
    [ +0,001079] input: VEIKK.INC A30 as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.1/0003:2FEB:0002.000D/input/input66
    [ +0,062599] hid-generic 0003:2FEB:0002.000D: input,hidraw1: USB HID v1.00 Keyboard [VEIKK.INC A30] on usb-0000:00:14.0-1/input1
    [ +0,001208] hid-generic 0003:2FEB:0002.000E: hiddev0,hidraw2: USB HID v1.00 Device [VEIKK.INC A30] on usb-0000:00:14.0-1/input2


    Gimp display:
    enter image description here



    Edit2: hum, it seems that the device that sends the pressure can be read in sudo cat /dev/hidraw0 as the patterns differs depending the the pressure I apply. I don't know how to read this binary though ^^ If I can, maybe I can use uinput to map it to a new device? ^^ Note that /dev/input contains files like mouseX, eventY, and mouse1 is correlated with the tablet, but not with the pressure, and the only file in this folder that displays lot's of information linked with the tablet is the file /dev/input/by-id/usb-VEIKK.INC_A30_0000001-event-mouse. But it's less clear that the patterns match the pressure, too many information are sent here. If you know how to parse them let me know!



    Edit3:
    So I don't yet have a driver, but at least it seems to be easy to read the input from the device raw communication. I made this python script as a proof of concept:



    #!/usr/bin/env python3
    import struct
    PRINT_TIMESTAMP = True
    # Open the file in the read-binary mode
    f = open("/dev/input/by-id/usb-VEIKK.INC_A30_0000001-event-mouse", "rb" )

    while 1:
    data = f.read(24)
    # print struct.unpack('4IHHI',data)
    ###### FORMAT = ( Time Stamp_INT , 0 , Time Stamp_DEC , 0 ,
    ###### type , code ( key pressed ) , value (press/release) )
    time_int, _, time_dec, _, ev_type, ev_code, ev_val = struct.unpack('4IHHI',data)
    t = (ev_type, ev_code)
    if ((t == (0,0) and ev_val == 0)
    or (t == (4, 4) and ev_val >= 589825 and ev_val <= 589827)):
    # Redundant as it's for normal/bottom/top clicks
    # (same code for press/release), or just garbage 0,0,0
    continue
    if PRINT_TIMESTAMP:
    print("[:.2f] ".format(time_int + time_dec/1e6),
    end="", flush=True)
    if t == (3,0):
    print("Pos x: (:.2f%)".format(ev_val, 100*ev_val/32767), flush=True)
    elif t == (3,1):
    print("Pos y: (:.2f%)".format(ev_val, 100*ev_val/32767), flush=True)
    elif t == (3,24):
    print("Pression: (:.2f%)".format(ev_val, 100*ev_val/8191), flush=True)
    elif t == (1,272):
    print("Normal click ()".format("press" if ev_val else "release"), flush=True)
    elif t == (1,273):
    print("click button 2 (bottom) ()".format("press" if ev_val else "release"), flush=True)
    elif t == (1,274):
    print("click button 3 (top) ()".format("press" if ev_val else "release"), flush=True)
    else:
    print("Unknow: type=, code=, value=".format(ev_type, ev_code, ev_val), flush=True)


    Démo:



    [1553182025.55] Pos y: 11458 (34.97%)
    [1553182025.55] Pos x: 14310 (43.67%)
    [1553182025.56] Pos x: 14314 (43.68%)
    [1553182025.56] Pos x: 14318 (43.70%)
    [1553182025.57] Pos x: 14321 (43.71%)
    [1553182025.57] Normal click (press)
    [1553182025.57] Pos x: 14323 (43.71%)
    [1553182025.57] Pression: 1122 (13.70%)
    [1553182025.57] Pos x: 14326 (43.72%)
    [1553182025.57] Pos y: 11466 (34.99%)
    [1553182025.57] Pression: 1260 (15.38%)
    [1553182025.58] Pos x: 14329 (43.73%)
    [1553182025.58] Pression: 1337 (16.32%)
    [1553182025.58] Pos x: 14330 (43.73%)
    [1553182025.58] Pos y: 11494 (35.08%)
    [1553182025.58] Pression: 1515 (18.50%)
    [1553182025.59] Pos y: 11506 (35.11%)
    [1553182025.59] Pression: 1687 (20.60%)
    [1553182025.59] Pos y: 11517 (35.15%)
    [1553182025.59] Pression: 1689 (20.62%)
    [1553182025.59] Pos y: 11529 (35.18%)
    [1553182025.59] Pression: 1789 (21.84%)
    [1553182025.60] Pos y: 11536 (35.21%)
    [1553182025.60] Pression: 1829 (22.33%)
    [1553182025.60] Pos y: 11542 (35.22%)
    [1553182025.60] Pression: 1907 (23.28%)
    [1553182025.61] Pression: 2031 (24.80%)
    [1553182025.61] Pos y: 11549 (35.25%)
    [1553182025.61] Pression: 2140 (26.13%)


    Edit 4:
    Amazing page: https://digimend.github.io/support/howto/trbl/locating_failure/ However, everything works here... except at the very last step when I want to test it. I tried to test with MyPaint, and it does not detect pressure.



    I also tried to do my own code that basically copies the input from the event file into a new device like that:



    #!/usr/bin/env python3
    import sys
    import libevdev
    import time

    def print_capabilities(l):
    v = l.driver_version
    print("Input driver version is ..".format(v >> 16, (v >> 8) & 0xff, v & 0xff))
    id = l.id
    print("Input device ID: bus :#x vendor :#x product :#x version :#x".format(
    id["bustype"],
    id["vendor"],
    id["product"],
    id["version"],
    ))
    print("Input device name: ".format(l.name))
    print("Supported events:")

    for t, cs in l.evbits.items():
    print(" Event type ()".format(t.value, t.name))

    for c in cs:
    if t in [libevdev.EV_LED, libevdev.EV_SND, libevdev.EV_SW]:
    v = l.value[c]
    print(" Event code () state ".format(c.value, c.name, v))
    else:
    print(" Event code ()".format(c.value, c.name))

    if t == libevdev.EV_ABS:
    a = l.absinfo[c]
    print(" :10s :6d".format('Value', a.value))
    print(" :10s :6d".format('Minimum', a.minimum))
    print(" :10s :6d".format('Maximum', a.maximum))
    print(" :10s :6d".format('Fuzz', a.fuzz))
    print(" :10s :6d".format('Flat', a.flat))
    print(" :10s :6d".format('Resolution', a.resolution))

    print("Properties:")
    for p in l.properties:
    print(" Property type ()".format(p.value, p.name))


    def print_event(e):
    print("Event: time .:06d, ".format(e.sec, e.usec), end='')
    if e.matches(libevdev.EV_SYN):
    if e.matches(libevdev.EV_SYN.SYN_MT_REPORT):
    print("++++++++++++++ ++++++++++++".format(e.code.name))
    elif e.matches(libevdev.EV_SYN.SYN_DROPPED):
    print(">>>>>>>>>>>>>> >>>>>>>>>>>>".format(e.code.name))
    else:
    print("-------------- ------------".format(e.code.name))
    else:
    print("type :02x code :03x :20s value :4d".format(e.type.value, e.type.name, e.code.value, e.code.name, e.value))


    def main(args):
    path = args[1]
    dev = libevdev.Device()
    dev.name = "Combined Both Devices"
    dev.enable(libevdev.EV_ABS.ABS_X,
    libevdev.InputAbsInfo(minimum=0, maximum=32767))
    dev.enable(libevdev.EV_ABS.ABS_Y,
    libevdev.InputAbsInfo(minimum=0, maximum=32767))
    dev.enable(libevdev.EV_ABS.ABS_Z,
    libevdev.InputAbsInfo(minimum=0, maximum=8191))
    dev.enable(libevdev.EV_ABS.ABS_0B,
    libevdev.InputAbsInfo(minimum=0, maximum=8191))
    dev.enable(libevdev.EV_ABS.ABS_DISTANCE,
    libevdev.InputAbsInfo(minimum=0, maximum=8191))
    dev.enable(libevdev.EV_ABS.ABS_PRESSURE,
    libevdev.InputAbsInfo(minimum=0, maximum=8191))
    dev.enable(libevdev.EV_MSC.MSC_SCAN)
    dev.enable(libevdev.EV_KEY.BTN_LEFT)
    dev.enable(libevdev.EV_KEY.BTN_RIGHT)
    dev.enable(libevdev.EV_KEY.BTN_MIDDLE)
    dev.enable(libevdev.EV_KEY.BTN_TOUCH)
    dev.enable(libevdev.EV_SYN.SYN_REPORT)
    dev.enable(libevdev.EV_SYN.SYN_CONFIG)
    dev.enable(libevdev.EV_SYN.SYN_MT_REPORT)
    dev.enable(libevdev.EV_SYN.SYN_DROPPED)
    dev.enable(libevdev.EV_SYN.SYN_04)
    dev.enable(libevdev.EV_SYN.SYN_05)
    dev.enable(libevdev.EV_SYN.SYN_06)
    dev.enable(libevdev.EV_SYN.SYN_07)
    dev.enable(libevdev.EV_SYN.SYN_08)
    dev.enable(libevdev.EV_SYN.SYN_09)
    dev.enable(libevdev.EV_SYN.SYN_0A)
    dev.enable(libevdev.EV_SYN.SYN_0B)
    dev.enable(libevdev.EV_SYN.SYN_0C)
    dev.enable(libevdev.EV_SYN.SYN_0D)
    dev.enable(libevdev.EV_SYN.SYN_0E)
    dev.enable(libevdev.EV_SYN.SYN_MAX)
    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.
    time.sleep(1)

    with open(path, "rb") as fd:
    l = libevdev.Device(fd)
    print_capabilities(l)
    print("################################n"
    "# Waiting for events #n"
    "################################")
    while True:
    try:
    ev = l.events()
    for e in ev:
    uinput.send_events([e])
    print_event(e)
    if e.matches(libevdev.EV_ABS.ABS_PRESSURE):
    print("Pressure! Will send another packeton Z axis!")
    uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_Z, e.value)])
    uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_0B, e.value)])
    uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_DISTANCE, e.value)])

    except libevdev.EventsDroppedException:
    for e in l.sync():
    print_event(e)
    uinput.send_events([e])
    except KeyboardInterrupt:
    pass
    except IOError as e:
    import errno
    if e.errno == errno.EACCES:
    print("Insufficient permissions to access ".format(path))
    elif e.errno == errno.ENOENT:
    print("Device does not exist".format(path))
    else:
    raise e
    except OSError as e:
    print(e)


    if __name__ == "__main__":
    if len(sys.argv) < 2:
    print("Usage: /dev/input/eventX".format(sys.argv[0]))
    sys.exit(1)
    main(sys.argv)


    and I realized that it's more or less the same as the Veikk mouse, and it does not work better.










    share|improve this question


























      0












      0








      0








      I just bought the graphical tablet VEIKK A30. The mouse is recognized directly. Unfortunately, I can't find how to enable pressure sensitivity on Linux. I saw one guy reporting that with some tweaking it was possible to make it work, but can't find any reference on that.



      Edit: here is the output of dmesg



      [mars19 01:15] usb 2-1: new full-speed USB device number 10 using xhci_hcd
      [ +0,153026] usb 2-1: New USB device found, idVendor=2feb, idProduct=0002, bcdDevice= 0.00
      [ +0,000006] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
      [ +0,000004] usb 2-1: Product: A30
      [ +0,000003] usb 2-1: Manufacturer: VEIKK.INC
      [ +0,000004] usb 2-1: SerialNumber: 0000001
      [ +0,003052] input: VEIKK.INC A30 Mouse as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.0/0003:2FEB:0002.000C/input/input64
      [ +0,064250] input: VEIKK.INC A30 as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.0/0003:2FEB:0002.000C/input/input65
      [ +0,000429] hid-generic 0003:2FEB:0002.000C: input,hidraw0: USB HID v1.00 Mouse [VEIKK.INC A30] on usb-0000:00:14.0-1/input0
      [ +0,001079] input: VEIKK.INC A30 as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.1/0003:2FEB:0002.000D/input/input66
      [ +0,062599] hid-generic 0003:2FEB:0002.000D: input,hidraw1: USB HID v1.00 Keyboard [VEIKK.INC A30] on usb-0000:00:14.0-1/input1
      [ +0,001208] hid-generic 0003:2FEB:0002.000E: hiddev0,hidraw2: USB HID v1.00 Device [VEIKK.INC A30] on usb-0000:00:14.0-1/input2


      Gimp display:
      enter image description here



      Edit2: hum, it seems that the device that sends the pressure can be read in sudo cat /dev/hidraw0 as the patterns differs depending the the pressure I apply. I don't know how to read this binary though ^^ If I can, maybe I can use uinput to map it to a new device? ^^ Note that /dev/input contains files like mouseX, eventY, and mouse1 is correlated with the tablet, but not with the pressure, and the only file in this folder that displays lot's of information linked with the tablet is the file /dev/input/by-id/usb-VEIKK.INC_A30_0000001-event-mouse. But it's less clear that the patterns match the pressure, too many information are sent here. If you know how to parse them let me know!



      Edit3:
      So I don't yet have a driver, but at least it seems to be easy to read the input from the device raw communication. I made this python script as a proof of concept:



      #!/usr/bin/env python3
      import struct
      PRINT_TIMESTAMP = True
      # Open the file in the read-binary mode
      f = open("/dev/input/by-id/usb-VEIKK.INC_A30_0000001-event-mouse", "rb" )

      while 1:
      data = f.read(24)
      # print struct.unpack('4IHHI',data)
      ###### FORMAT = ( Time Stamp_INT , 0 , Time Stamp_DEC , 0 ,
      ###### type , code ( key pressed ) , value (press/release) )
      time_int, _, time_dec, _, ev_type, ev_code, ev_val = struct.unpack('4IHHI',data)
      t = (ev_type, ev_code)
      if ((t == (0,0) and ev_val == 0)
      or (t == (4, 4) and ev_val >= 589825 and ev_val <= 589827)):
      # Redundant as it's for normal/bottom/top clicks
      # (same code for press/release), or just garbage 0,0,0
      continue
      if PRINT_TIMESTAMP:
      print("[:.2f] ".format(time_int + time_dec/1e6),
      end="", flush=True)
      if t == (3,0):
      print("Pos x: (:.2f%)".format(ev_val, 100*ev_val/32767), flush=True)
      elif t == (3,1):
      print("Pos y: (:.2f%)".format(ev_val, 100*ev_val/32767), flush=True)
      elif t == (3,24):
      print("Pression: (:.2f%)".format(ev_val, 100*ev_val/8191), flush=True)
      elif t == (1,272):
      print("Normal click ()".format("press" if ev_val else "release"), flush=True)
      elif t == (1,273):
      print("click button 2 (bottom) ()".format("press" if ev_val else "release"), flush=True)
      elif t == (1,274):
      print("click button 3 (top) ()".format("press" if ev_val else "release"), flush=True)
      else:
      print("Unknow: type=, code=, value=".format(ev_type, ev_code, ev_val), flush=True)


      Démo:



      [1553182025.55] Pos y: 11458 (34.97%)
      [1553182025.55] Pos x: 14310 (43.67%)
      [1553182025.56] Pos x: 14314 (43.68%)
      [1553182025.56] Pos x: 14318 (43.70%)
      [1553182025.57] Pos x: 14321 (43.71%)
      [1553182025.57] Normal click (press)
      [1553182025.57] Pos x: 14323 (43.71%)
      [1553182025.57] Pression: 1122 (13.70%)
      [1553182025.57] Pos x: 14326 (43.72%)
      [1553182025.57] Pos y: 11466 (34.99%)
      [1553182025.57] Pression: 1260 (15.38%)
      [1553182025.58] Pos x: 14329 (43.73%)
      [1553182025.58] Pression: 1337 (16.32%)
      [1553182025.58] Pos x: 14330 (43.73%)
      [1553182025.58] Pos y: 11494 (35.08%)
      [1553182025.58] Pression: 1515 (18.50%)
      [1553182025.59] Pos y: 11506 (35.11%)
      [1553182025.59] Pression: 1687 (20.60%)
      [1553182025.59] Pos y: 11517 (35.15%)
      [1553182025.59] Pression: 1689 (20.62%)
      [1553182025.59] Pos y: 11529 (35.18%)
      [1553182025.59] Pression: 1789 (21.84%)
      [1553182025.60] Pos y: 11536 (35.21%)
      [1553182025.60] Pression: 1829 (22.33%)
      [1553182025.60] Pos y: 11542 (35.22%)
      [1553182025.60] Pression: 1907 (23.28%)
      [1553182025.61] Pression: 2031 (24.80%)
      [1553182025.61] Pos y: 11549 (35.25%)
      [1553182025.61] Pression: 2140 (26.13%)


      Edit 4:
      Amazing page: https://digimend.github.io/support/howto/trbl/locating_failure/ However, everything works here... except at the very last step when I want to test it. I tried to test with MyPaint, and it does not detect pressure.



      I also tried to do my own code that basically copies the input from the event file into a new device like that:



      #!/usr/bin/env python3
      import sys
      import libevdev
      import time

      def print_capabilities(l):
      v = l.driver_version
      print("Input driver version is ..".format(v >> 16, (v >> 8) & 0xff, v & 0xff))
      id = l.id
      print("Input device ID: bus :#x vendor :#x product :#x version :#x".format(
      id["bustype"],
      id["vendor"],
      id["product"],
      id["version"],
      ))
      print("Input device name: ".format(l.name))
      print("Supported events:")

      for t, cs in l.evbits.items():
      print(" Event type ()".format(t.value, t.name))

      for c in cs:
      if t in [libevdev.EV_LED, libevdev.EV_SND, libevdev.EV_SW]:
      v = l.value[c]
      print(" Event code () state ".format(c.value, c.name, v))
      else:
      print(" Event code ()".format(c.value, c.name))

      if t == libevdev.EV_ABS:
      a = l.absinfo[c]
      print(" :10s :6d".format('Value', a.value))
      print(" :10s :6d".format('Minimum', a.minimum))
      print(" :10s :6d".format('Maximum', a.maximum))
      print(" :10s :6d".format('Fuzz', a.fuzz))
      print(" :10s :6d".format('Flat', a.flat))
      print(" :10s :6d".format('Resolution', a.resolution))

      print("Properties:")
      for p in l.properties:
      print(" Property type ()".format(p.value, p.name))


      def print_event(e):
      print("Event: time .:06d, ".format(e.sec, e.usec), end='')
      if e.matches(libevdev.EV_SYN):
      if e.matches(libevdev.EV_SYN.SYN_MT_REPORT):
      print("++++++++++++++ ++++++++++++".format(e.code.name))
      elif e.matches(libevdev.EV_SYN.SYN_DROPPED):
      print(">>>>>>>>>>>>>> >>>>>>>>>>>>".format(e.code.name))
      else:
      print("-------------- ------------".format(e.code.name))
      else:
      print("type :02x code :03x :20s value :4d".format(e.type.value, e.type.name, e.code.value, e.code.name, e.value))


      def main(args):
      path = args[1]
      dev = libevdev.Device()
      dev.name = "Combined Both Devices"
      dev.enable(libevdev.EV_ABS.ABS_X,
      libevdev.InputAbsInfo(minimum=0, maximum=32767))
      dev.enable(libevdev.EV_ABS.ABS_Y,
      libevdev.InputAbsInfo(minimum=0, maximum=32767))
      dev.enable(libevdev.EV_ABS.ABS_Z,
      libevdev.InputAbsInfo(minimum=0, maximum=8191))
      dev.enable(libevdev.EV_ABS.ABS_0B,
      libevdev.InputAbsInfo(minimum=0, maximum=8191))
      dev.enable(libevdev.EV_ABS.ABS_DISTANCE,
      libevdev.InputAbsInfo(minimum=0, maximum=8191))
      dev.enable(libevdev.EV_ABS.ABS_PRESSURE,
      libevdev.InputAbsInfo(minimum=0, maximum=8191))
      dev.enable(libevdev.EV_MSC.MSC_SCAN)
      dev.enable(libevdev.EV_KEY.BTN_LEFT)
      dev.enable(libevdev.EV_KEY.BTN_RIGHT)
      dev.enable(libevdev.EV_KEY.BTN_MIDDLE)
      dev.enable(libevdev.EV_KEY.BTN_TOUCH)
      dev.enable(libevdev.EV_SYN.SYN_REPORT)
      dev.enable(libevdev.EV_SYN.SYN_CONFIG)
      dev.enable(libevdev.EV_SYN.SYN_MT_REPORT)
      dev.enable(libevdev.EV_SYN.SYN_DROPPED)
      dev.enable(libevdev.EV_SYN.SYN_04)
      dev.enable(libevdev.EV_SYN.SYN_05)
      dev.enable(libevdev.EV_SYN.SYN_06)
      dev.enable(libevdev.EV_SYN.SYN_07)
      dev.enable(libevdev.EV_SYN.SYN_08)
      dev.enable(libevdev.EV_SYN.SYN_09)
      dev.enable(libevdev.EV_SYN.SYN_0A)
      dev.enable(libevdev.EV_SYN.SYN_0B)
      dev.enable(libevdev.EV_SYN.SYN_0C)
      dev.enable(libevdev.EV_SYN.SYN_0D)
      dev.enable(libevdev.EV_SYN.SYN_0E)
      dev.enable(libevdev.EV_SYN.SYN_MAX)
      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.
      time.sleep(1)

      with open(path, "rb") as fd:
      l = libevdev.Device(fd)
      print_capabilities(l)
      print("################################n"
      "# Waiting for events #n"
      "################################")
      while True:
      try:
      ev = l.events()
      for e in ev:
      uinput.send_events([e])
      print_event(e)
      if e.matches(libevdev.EV_ABS.ABS_PRESSURE):
      print("Pressure! Will send another packeton Z axis!")
      uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_Z, e.value)])
      uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_0B, e.value)])
      uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_DISTANCE, e.value)])

      except libevdev.EventsDroppedException:
      for e in l.sync():
      print_event(e)
      uinput.send_events([e])
      except KeyboardInterrupt:
      pass
      except IOError as e:
      import errno
      if e.errno == errno.EACCES:
      print("Insufficient permissions to access ".format(path))
      elif e.errno == errno.ENOENT:
      print("Device does not exist".format(path))
      else:
      raise e
      except OSError as e:
      print(e)


      if __name__ == "__main__":
      if len(sys.argv) < 2:
      print("Usage: /dev/input/eventX".format(sys.argv[0]))
      sys.exit(1)
      main(sys.argv)


      and I realized that it's more or less the same as the Veikk mouse, and it does not work better.










      share|improve this question
















      I just bought the graphical tablet VEIKK A30. The mouse is recognized directly. Unfortunately, I can't find how to enable pressure sensitivity on Linux. I saw one guy reporting that with some tweaking it was possible to make it work, but can't find any reference on that.



      Edit: here is the output of dmesg



      [mars19 01:15] usb 2-1: new full-speed USB device number 10 using xhci_hcd
      [ +0,153026] usb 2-1: New USB device found, idVendor=2feb, idProduct=0002, bcdDevice= 0.00
      [ +0,000006] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
      [ +0,000004] usb 2-1: Product: A30
      [ +0,000003] usb 2-1: Manufacturer: VEIKK.INC
      [ +0,000004] usb 2-1: SerialNumber: 0000001
      [ +0,003052] input: VEIKK.INC A30 Mouse as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.0/0003:2FEB:0002.000C/input/input64
      [ +0,064250] input: VEIKK.INC A30 as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.0/0003:2FEB:0002.000C/input/input65
      [ +0,000429] hid-generic 0003:2FEB:0002.000C: input,hidraw0: USB HID v1.00 Mouse [VEIKK.INC A30] on usb-0000:00:14.0-1/input0
      [ +0,001079] input: VEIKK.INC A30 as /devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.1/0003:2FEB:0002.000D/input/input66
      [ +0,062599] hid-generic 0003:2FEB:0002.000D: input,hidraw1: USB HID v1.00 Keyboard [VEIKK.INC A30] on usb-0000:00:14.0-1/input1
      [ +0,001208] hid-generic 0003:2FEB:0002.000E: hiddev0,hidraw2: USB HID v1.00 Device [VEIKK.INC A30] on usb-0000:00:14.0-1/input2


      Gimp display:
      enter image description here



      Edit2: hum, it seems that the device that sends the pressure can be read in sudo cat /dev/hidraw0 as the patterns differs depending the the pressure I apply. I don't know how to read this binary though ^^ If I can, maybe I can use uinput to map it to a new device? ^^ Note that /dev/input contains files like mouseX, eventY, and mouse1 is correlated with the tablet, but not with the pressure, and the only file in this folder that displays lot's of information linked with the tablet is the file /dev/input/by-id/usb-VEIKK.INC_A30_0000001-event-mouse. But it's less clear that the patterns match the pressure, too many information are sent here. If you know how to parse them let me know!



      Edit3:
      So I don't yet have a driver, but at least it seems to be easy to read the input from the device raw communication. I made this python script as a proof of concept:



      #!/usr/bin/env python3
      import struct
      PRINT_TIMESTAMP = True
      # Open the file in the read-binary mode
      f = open("/dev/input/by-id/usb-VEIKK.INC_A30_0000001-event-mouse", "rb" )

      while 1:
      data = f.read(24)
      # print struct.unpack('4IHHI',data)
      ###### FORMAT = ( Time Stamp_INT , 0 , Time Stamp_DEC , 0 ,
      ###### type , code ( key pressed ) , value (press/release) )
      time_int, _, time_dec, _, ev_type, ev_code, ev_val = struct.unpack('4IHHI',data)
      t = (ev_type, ev_code)
      if ((t == (0,0) and ev_val == 0)
      or (t == (4, 4) and ev_val >= 589825 and ev_val <= 589827)):
      # Redundant as it's for normal/bottom/top clicks
      # (same code for press/release), or just garbage 0,0,0
      continue
      if PRINT_TIMESTAMP:
      print("[:.2f] ".format(time_int + time_dec/1e6),
      end="", flush=True)
      if t == (3,0):
      print("Pos x: (:.2f%)".format(ev_val, 100*ev_val/32767), flush=True)
      elif t == (3,1):
      print("Pos y: (:.2f%)".format(ev_val, 100*ev_val/32767), flush=True)
      elif t == (3,24):
      print("Pression: (:.2f%)".format(ev_val, 100*ev_val/8191), flush=True)
      elif t == (1,272):
      print("Normal click ()".format("press" if ev_val else "release"), flush=True)
      elif t == (1,273):
      print("click button 2 (bottom) ()".format("press" if ev_val else "release"), flush=True)
      elif t == (1,274):
      print("click button 3 (top) ()".format("press" if ev_val else "release"), flush=True)
      else:
      print("Unknow: type=, code=, value=".format(ev_type, ev_code, ev_val), flush=True)


      Démo:



      [1553182025.55] Pos y: 11458 (34.97%)
      [1553182025.55] Pos x: 14310 (43.67%)
      [1553182025.56] Pos x: 14314 (43.68%)
      [1553182025.56] Pos x: 14318 (43.70%)
      [1553182025.57] Pos x: 14321 (43.71%)
      [1553182025.57] Normal click (press)
      [1553182025.57] Pos x: 14323 (43.71%)
      [1553182025.57] Pression: 1122 (13.70%)
      [1553182025.57] Pos x: 14326 (43.72%)
      [1553182025.57] Pos y: 11466 (34.99%)
      [1553182025.57] Pression: 1260 (15.38%)
      [1553182025.58] Pos x: 14329 (43.73%)
      [1553182025.58] Pression: 1337 (16.32%)
      [1553182025.58] Pos x: 14330 (43.73%)
      [1553182025.58] Pos y: 11494 (35.08%)
      [1553182025.58] Pression: 1515 (18.50%)
      [1553182025.59] Pos y: 11506 (35.11%)
      [1553182025.59] Pression: 1687 (20.60%)
      [1553182025.59] Pos y: 11517 (35.15%)
      [1553182025.59] Pression: 1689 (20.62%)
      [1553182025.59] Pos y: 11529 (35.18%)
      [1553182025.59] Pression: 1789 (21.84%)
      [1553182025.60] Pos y: 11536 (35.21%)
      [1553182025.60] Pression: 1829 (22.33%)
      [1553182025.60] Pos y: 11542 (35.22%)
      [1553182025.60] Pression: 1907 (23.28%)
      [1553182025.61] Pression: 2031 (24.80%)
      [1553182025.61] Pos y: 11549 (35.25%)
      [1553182025.61] Pression: 2140 (26.13%)


      Edit 4:
      Amazing page: https://digimend.github.io/support/howto/trbl/locating_failure/ However, everything works here... except at the very last step when I want to test it. I tried to test with MyPaint, and it does not detect pressure.



      I also tried to do my own code that basically copies the input from the event file into a new device like that:



      #!/usr/bin/env python3
      import sys
      import libevdev
      import time

      def print_capabilities(l):
      v = l.driver_version
      print("Input driver version is ..".format(v >> 16, (v >> 8) & 0xff, v & 0xff))
      id = l.id
      print("Input device ID: bus :#x vendor :#x product :#x version :#x".format(
      id["bustype"],
      id["vendor"],
      id["product"],
      id["version"],
      ))
      print("Input device name: ".format(l.name))
      print("Supported events:")

      for t, cs in l.evbits.items():
      print(" Event type ()".format(t.value, t.name))

      for c in cs:
      if t in [libevdev.EV_LED, libevdev.EV_SND, libevdev.EV_SW]:
      v = l.value[c]
      print(" Event code () state ".format(c.value, c.name, v))
      else:
      print(" Event code ()".format(c.value, c.name))

      if t == libevdev.EV_ABS:
      a = l.absinfo[c]
      print(" :10s :6d".format('Value', a.value))
      print(" :10s :6d".format('Minimum', a.minimum))
      print(" :10s :6d".format('Maximum', a.maximum))
      print(" :10s :6d".format('Fuzz', a.fuzz))
      print(" :10s :6d".format('Flat', a.flat))
      print(" :10s :6d".format('Resolution', a.resolution))

      print("Properties:")
      for p in l.properties:
      print(" Property type ()".format(p.value, p.name))


      def print_event(e):
      print("Event: time .:06d, ".format(e.sec, e.usec), end='')
      if e.matches(libevdev.EV_SYN):
      if e.matches(libevdev.EV_SYN.SYN_MT_REPORT):
      print("++++++++++++++ ++++++++++++".format(e.code.name))
      elif e.matches(libevdev.EV_SYN.SYN_DROPPED):
      print(">>>>>>>>>>>>>> >>>>>>>>>>>>".format(e.code.name))
      else:
      print("-------------- ------------".format(e.code.name))
      else:
      print("type :02x code :03x :20s value :4d".format(e.type.value, e.type.name, e.code.value, e.code.name, e.value))


      def main(args):
      path = args[1]
      dev = libevdev.Device()
      dev.name = "Combined Both Devices"
      dev.enable(libevdev.EV_ABS.ABS_X,
      libevdev.InputAbsInfo(minimum=0, maximum=32767))
      dev.enable(libevdev.EV_ABS.ABS_Y,
      libevdev.InputAbsInfo(minimum=0, maximum=32767))
      dev.enable(libevdev.EV_ABS.ABS_Z,
      libevdev.InputAbsInfo(minimum=0, maximum=8191))
      dev.enable(libevdev.EV_ABS.ABS_0B,
      libevdev.InputAbsInfo(minimum=0, maximum=8191))
      dev.enable(libevdev.EV_ABS.ABS_DISTANCE,
      libevdev.InputAbsInfo(minimum=0, maximum=8191))
      dev.enable(libevdev.EV_ABS.ABS_PRESSURE,
      libevdev.InputAbsInfo(minimum=0, maximum=8191))
      dev.enable(libevdev.EV_MSC.MSC_SCAN)
      dev.enable(libevdev.EV_KEY.BTN_LEFT)
      dev.enable(libevdev.EV_KEY.BTN_RIGHT)
      dev.enable(libevdev.EV_KEY.BTN_MIDDLE)
      dev.enable(libevdev.EV_KEY.BTN_TOUCH)
      dev.enable(libevdev.EV_SYN.SYN_REPORT)
      dev.enable(libevdev.EV_SYN.SYN_CONFIG)
      dev.enable(libevdev.EV_SYN.SYN_MT_REPORT)
      dev.enable(libevdev.EV_SYN.SYN_DROPPED)
      dev.enable(libevdev.EV_SYN.SYN_04)
      dev.enable(libevdev.EV_SYN.SYN_05)
      dev.enable(libevdev.EV_SYN.SYN_06)
      dev.enable(libevdev.EV_SYN.SYN_07)
      dev.enable(libevdev.EV_SYN.SYN_08)
      dev.enable(libevdev.EV_SYN.SYN_09)
      dev.enable(libevdev.EV_SYN.SYN_0A)
      dev.enable(libevdev.EV_SYN.SYN_0B)
      dev.enable(libevdev.EV_SYN.SYN_0C)
      dev.enable(libevdev.EV_SYN.SYN_0D)
      dev.enable(libevdev.EV_SYN.SYN_0E)
      dev.enable(libevdev.EV_SYN.SYN_MAX)
      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.
      time.sleep(1)

      with open(path, "rb") as fd:
      l = libevdev.Device(fd)
      print_capabilities(l)
      print("################################n"
      "# Waiting for events #n"
      "################################")
      while True:
      try:
      ev = l.events()
      for e in ev:
      uinput.send_events([e])
      print_event(e)
      if e.matches(libevdev.EV_ABS.ABS_PRESSURE):
      print("Pressure! Will send another packeton Z axis!")
      uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_Z, e.value)])
      uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_0B, e.value)])
      uinput.send_events([libevdev.InputEvent(libevdev.EV_ABS.ABS_DISTANCE, e.value)])

      except libevdev.EventsDroppedException:
      for e in l.sync():
      print_event(e)
      uinput.send_events([e])
      except KeyboardInterrupt:
      pass
      except IOError as e:
      import errno
      if e.errno == errno.EACCES:
      print("Insufficient permissions to access ".format(path))
      elif e.errno == errno.ENOENT:
      print("Device does not exist".format(path))
      else:
      raise e
      except OSError as e:
      print(e)


      if __name__ == "__main__":
      if len(sys.argv) < 2:
      print("Usage: /dev/input/eventX".format(sys.argv[0]))
      sys.exit(1)
      main(sys.argv)


      and I realized that it's more or less the same as the Veikk mouse, and it does not work better.







      tablet






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Mar 22 at 21:33









      Rui F Ribeiro

      41.8k1483142




      41.8k1483142










      asked Mar 21 at 10:48









      tobiasBoratobiasBora

      264312




      264312




















          1 Answer
          1






          active

          oldest

          votes


















          0














          Youou!!! I managed to make a python script that gives me back the pressure on a new virtual device :-D Now I can use it with Gimp/Krita/... The only remaining part to do is to write a proper C driver and load it directly in the kernel... If you have some idea let me know!



          Proof:



          enter image description here



          So the idea of the script is to read the input from the event file of the graphics tablet, and then create the good events (no more BTN_LEFT, but BTN_TOUCH instead... see my other question/answer here for more details).



          To run the script (see below), save it under combine_both.py, and make it executable/install deps:



          $ chmod +x combine_both.py
          $ sudo pip3 install libevdev


          Then, check out the available input devices:



          $ xinput list


          This should give you several entries, among them one entry like



          VEIKK.INC A30 Mouse id=12 [slave pointer (2)]


          Note the id (noted as <id veikk> later), as well as the id of the virtual core pointer <id core>:



          Virtual core pointer id=2 [master pointer (3)]


          Then, you need to know which /dev/input/eventX file to pick, the easiest is to run sudo evtest and read the name of the file corresponding to the VEIKK.INC A30 Mouse. Then run the script with this file in argument like in:



          sudo ./combine_both.py /dev/input/event7


          This script should output stuff when you try to click/move on the device. Moreover xinput list should also list this device with name Tablet alone Pen (0) with an id <id fake tablet>, and xinput test <id fake tablet> should give you something like (note the 3 columns for x/y/pressure):



          $ xinput test 21
          [...]
          motion a[0]=4151295 a[1]=4151295 a[2]=241
          motion a[0]=4060671 a[1]=4060671 a[2]=226
          motion a[0]=3969535 a[1]=3969535 a[2]=211
          motion a[0]=3878399 a[1]=3878399 a[2]=196
          motion a[0]=3787775 a[1]=3787775 a[2]=181
          motion a[0]=3696639 a[1]=3696639 a[2]=166
          motion a[0]=3605503 a[1]=3605503 a[2]=151
          motion a[0]=3514879 a[1]=3514879 a[2]=137
          motion a[0]=3423743 a[1]=3423743 a[2]=122
          motion a[0]=3332607 a[1]=3332607 a[2]=107
          motion a[0]=3241983 a[1]=3241983 a[2]=92
          motion a[0]=3150847 a[1]=3150847 a[2]=77
          motion a[0]=3059711 a[1]=3059711 a[2]=62
          motion a[0]=2969087 a[1]=2969087 a[2]=47
          motion a[0]=2877951 a[1]=2877951 a[2]=32
          motion a[0]=2650623 a[1]=2650623 a[2]=17
          button release 1


          Now, to make it work with Gimp/Krita, you want to disable the real mouse (otherwise you have conflicts between the real and fake tablet), with



          xinput float <id veikk>


          and when you are done you can reattach the real device with



          xinput reattach <id veikk> <id core>


          In gimp, don't forget to setup Tablet alone Pen (0) with mode=screen in Edit/input devices, and make sure the real tablet VEIKK.INC A30 Mouse is disabled. Finally, chose a good dynamic, like Pencil Generic to test!



          Enjoy! (and I'll let you know if I write the C driver at some points)



          Script:



          #!/usr/bin/env python3
          import sys
          import libevdev
          import datetime
          import time


          def print_capabilities(l):
          v = l.driver_version
          print("Input driver version is ..".format(v >> 16, (v >> 8) & 0xff, v & 0xff))
          id = l.id
          print("Input device ID: bus :#x vendor :#x product :#x version :#x".format(
          id["bustype"],
          id["vendor"],
          id["product"],
          id["version"],
          ))
          print("Input device name: ".format(l.name))
          print("Supported events:")

          for t, cs in l.evbits.items():
          print(" Event type ()".format(t.value, t.name))

          for c in cs:
          if t in [libevdev.EV_LED, libevdev.EV_SND, libevdev.EV_SW]:
          v = l.value[c]
          print(" Event code () state ".format(c.value, c.name, v))
          else:
          print(" Event code ()".format(c.value, c.name))

          if t == libevdev.EV_ABS:
          a = l.absinfo[c]
          print(" :10s :6d".format('Value', a.value))
          print(" :10s :6d".format('Minimum', a.minimum))
          print(" :10s :6d".format('Maximum', a.maximum))
          print(" :10s :6d".format('Fuzz', a.fuzz))
          print(" :10s :6d".format('Flat', a.flat))
          print(" :10s :6d".format('Resolution', a.resolution))

          print("Properties:")
          for p in l.properties:
          print(" Property type ()".format(p.value, p.name))


          def print_event(e):
          print("Event: time .:06d, ".format(e.sec, e.usec), end='')
          if e.matches(libevdev.EV_SYN):
          if e.matches(libevdev.EV_SYN.SYN_MT_REPORT):
          print("++++++++++++++ ++++++++++++".format(e.code.name))
          elif e.matches(libevdev.EV_SYN.SYN_DROPPED):
          print(">>>>>>>>>>>>>> >>>>>>>>>>>>".format(e.code.name))
          else:
          print("-------------- ------------".format(e.code.name))
          else:
          print("type :02x code :03x :20s value :4d".format(e.type.value, e.type.name, e.code.value, e.code.name, e.value))


          class Tablet():
          def __init__(self, tablet_name):
          self.tablet_name = tablet_name

          def __enter__(self):
          self.dev = libevdev.Device()
          self.dev.name = "Tablet alone"
          ### NB: all the following information needs to be enabled
          ### in order to recognize the device as a tablet.
          # Say that the device will send "absolute" values
          self.dev.enable(libevdev.INPUT_PROP_DIRECT)
          # Say that we are using the pen (not the erasor), and should be set to 1 when we are at proximity to the device.
          # See http://www.infradead.org/~mchehab/kernel_docs_pdf/linux-input.pdf page 9 (=13) and guidelines page 12 (=16), or the https://github.com/linuxwacom/input-wacom/blob/master/4.5/wacom_w8001.c (rdy=proximity)
          self.dev.enable(libevdev.EV_KEY.BTN_TOOL_PEN)
          self.dev.enable(libevdev.EV_KEY.BTN_TOOL_RUBBER)
          # Click
          self.dev.enable(libevdev.EV_KEY.BTN_TOUCH)
          # Press button 1 on pen
          self.dev.enable(libevdev.EV_KEY.BTN_STYLUS)
          # Press button 2 on pen, see great doc
          self.dev.enable(libevdev.EV_KEY.BTN_STYLUS2)
          # Send absolute X coordinate
          self.dev.enable(libevdev.EV_ABS.ABS_X,
          libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
          # Send absolute Y coordinate
          self.dev.enable(libevdev.EV_ABS.ABS_Y,
          libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
          # Send absolute pressure
          self.dev.enable(libevdev.EV_ABS.ABS_PRESSURE,
          libevdev.InputAbsInfo(minimum=0, maximum=8191))
          # Use to confirm that we finished to send the informations
          # (to be sent after every burst of information, otherwise
          # the kernel does not proceed the information)
          self.dev.enable(libevdev.EV_SYN.SYN_REPORT)
          # Report buffer overflow
          self.dev.enable(libevdev.EV_SYN.SYN_DROPPED)
          self.uinput = self.dev.create_uinput_device()
          print("New device at ()".format(self.uinput.devnode, self.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)
          # self.simulate_first_click()
          self.reset_state()
          return self

          def __exit__(self, type, value, traceback):
          pass

          def reset_state(self):
          self.is_away = True
          self.is_touching = False
          self.pressed_button_1 = False
          self.pressed_button_2 = False
          self.lastmodif = datetime.datetime.now()

          def send_events(self, events, is_away=False):
          self.lastmodif = datetime.datetime.now()
          self.is_away = is_away
          self.uinput.send_events(events)

          def simulate_first_click(self):
          """Useful only the first time to make sure
          xinput detected the input"""
          # 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.
          self.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.
          self.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),
          ])


          def send_state_no_pos(self, is_away=False):
          self.lastmodif = datetime.datetime.now()
          self.is_away = is_away
          print("Away: , Touching: ".format(self.is_away, self.is_touching))
          self.uinput.send_events([
          libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
          value=1 if self.is_touching else 0),
          libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
          value=1 if not self.is_away else 0),
          libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS,
          value=1 if self.pressed_button_1 else 0),
          libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2,
          value=1 if self.pressed_button_2 else 0),
          libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
          value=0),
          ])


          def touch_press(self):
          self.is_touching = True
          self.send_state_no_pos()

          def touch_release(self):
          self.is_touching = False
          self.send_state_no_pos()

          def button_1_press(self):
          self.pressed_button_1 = True
          self.send_state_no_pos()

          def button_1_release(self):
          self.pressed_button_1 = False
          self.send_state_no_pos()

          def button_2_press(self):
          self.pressed_button_2 = True
          self.send_state_no_pos()

          def button_2_release(self):
          self.pressed_button_2 = False
          self.send_state_no_pos()

          def move_x(self, abs_x):
          self.send_events([
          libevdev.InputEvent(libevdev.EV_ABS.ABS_X,
          value=abs_x),
          libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
          value=0),
          ])

          def move_y(self, abs_y):
          self.send_events([
          libevdev.InputEvent(libevdev.EV_ABS.ABS_Y,
          value=abs_y),
          libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
          value=0),
          ])

          def change_pressure(self, pressure):
          self.send_events([
          libevdev.InputEvent(libevdev.EV_ABS.ABS_PRESSURE,
          value=pressure),
          libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
          value=0),
          ])

          def handle_event(self, e):
          if e.matches(libevdev.EV_ABS.ABS_PRESSURE):
          self.change_pressure(e.value)
          elif e.matches(libevdev.EV_ABS.ABS_X):
          self.move_x(e.value)
          elif e.matches(libevdev.EV_ABS.ABS_Y):
          self.move_y(e.value)
          elif e.matches(libevdev.EV_KEY.BTN_LEFT):
          if e.value == 1:
          self.touch_press()
          else:
          self.touch_release()
          elif e.matches(libevdev.EV_SYN.SYN_REPORT):
          pass
          else:
          print("Unkown event:")
          print_event(e)



          def main(args):
          path = args[1]
          try:
          with Tablet("Tablet alone") as tablet:
          ### Read the events from real graphics tablet
          with open(path, "rb") as fd:
          l = libevdev.Device(fd)
          print_capabilities(l)
          print("################################n"
          "# Waiting for events #n"
          "################################")
          while True:
          try:
          ev = l.events()
          for e in ev:
          print_event(e)
          tablet.handle_event(e)
          except libevdev.EventsDroppedException:
          for e in l.sync():
          print_event(e)
          tablet.handle_event(e)
          except KeyboardInterrupt:
          pass
          except IOError as e:
          import errno
          if e.errno == errno.EACCES:
          print("Insufficient permissions to access ".format(path))
          elif e.errno == errno.ENOENT:
          print("Device does not exist".format(path))
          else:
          raise e
          except OSError as e:
          print(e)


          if __name__ == "__main__":
          if len(sys.argv) != 2:
          print("Usage: sudo /dev/input/eventX".format(sys.argv[0]))
          print(" $ sudo evtest")
          print("can help you to know which file to use.")
          sys.exit(1)
          main(sys.argv)





          share|improve this answer






















            Your Answer








            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "106"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader:
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            ,
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );













            draft saved

            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f507687%2fgraphic-tablet-veikk-pressure-sensitivity-on-linux%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            Youou!!! I managed to make a python script that gives me back the pressure on a new virtual device :-D Now I can use it with Gimp/Krita/... The only remaining part to do is to write a proper C driver and load it directly in the kernel... If you have some idea let me know!



            Proof:



            enter image description here



            So the idea of the script is to read the input from the event file of the graphics tablet, and then create the good events (no more BTN_LEFT, but BTN_TOUCH instead... see my other question/answer here for more details).



            To run the script (see below), save it under combine_both.py, and make it executable/install deps:



            $ chmod +x combine_both.py
            $ sudo pip3 install libevdev


            Then, check out the available input devices:



            $ xinput list


            This should give you several entries, among them one entry like



            VEIKK.INC A30 Mouse id=12 [slave pointer (2)]


            Note the id (noted as <id veikk> later), as well as the id of the virtual core pointer <id core>:



            Virtual core pointer id=2 [master pointer (3)]


            Then, you need to know which /dev/input/eventX file to pick, the easiest is to run sudo evtest and read the name of the file corresponding to the VEIKK.INC A30 Mouse. Then run the script with this file in argument like in:



            sudo ./combine_both.py /dev/input/event7


            This script should output stuff when you try to click/move on the device. Moreover xinput list should also list this device with name Tablet alone Pen (0) with an id <id fake tablet>, and xinput test <id fake tablet> should give you something like (note the 3 columns for x/y/pressure):



            $ xinput test 21
            [...]
            motion a[0]=4151295 a[1]=4151295 a[2]=241
            motion a[0]=4060671 a[1]=4060671 a[2]=226
            motion a[0]=3969535 a[1]=3969535 a[2]=211
            motion a[0]=3878399 a[1]=3878399 a[2]=196
            motion a[0]=3787775 a[1]=3787775 a[2]=181
            motion a[0]=3696639 a[1]=3696639 a[2]=166
            motion a[0]=3605503 a[1]=3605503 a[2]=151
            motion a[0]=3514879 a[1]=3514879 a[2]=137
            motion a[0]=3423743 a[1]=3423743 a[2]=122
            motion a[0]=3332607 a[1]=3332607 a[2]=107
            motion a[0]=3241983 a[1]=3241983 a[2]=92
            motion a[0]=3150847 a[1]=3150847 a[2]=77
            motion a[0]=3059711 a[1]=3059711 a[2]=62
            motion a[0]=2969087 a[1]=2969087 a[2]=47
            motion a[0]=2877951 a[1]=2877951 a[2]=32
            motion a[0]=2650623 a[1]=2650623 a[2]=17
            button release 1


            Now, to make it work with Gimp/Krita, you want to disable the real mouse (otherwise you have conflicts between the real and fake tablet), with



            xinput float <id veikk>


            and when you are done you can reattach the real device with



            xinput reattach <id veikk> <id core>


            In gimp, don't forget to setup Tablet alone Pen (0) with mode=screen in Edit/input devices, and make sure the real tablet VEIKK.INC A30 Mouse is disabled. Finally, chose a good dynamic, like Pencil Generic to test!



            Enjoy! (and I'll let you know if I write the C driver at some points)



            Script:



            #!/usr/bin/env python3
            import sys
            import libevdev
            import datetime
            import time


            def print_capabilities(l):
            v = l.driver_version
            print("Input driver version is ..".format(v >> 16, (v >> 8) & 0xff, v & 0xff))
            id = l.id
            print("Input device ID: bus :#x vendor :#x product :#x version :#x".format(
            id["bustype"],
            id["vendor"],
            id["product"],
            id["version"],
            ))
            print("Input device name: ".format(l.name))
            print("Supported events:")

            for t, cs in l.evbits.items():
            print(" Event type ()".format(t.value, t.name))

            for c in cs:
            if t in [libevdev.EV_LED, libevdev.EV_SND, libevdev.EV_SW]:
            v = l.value[c]
            print(" Event code () state ".format(c.value, c.name, v))
            else:
            print(" Event code ()".format(c.value, c.name))

            if t == libevdev.EV_ABS:
            a = l.absinfo[c]
            print(" :10s :6d".format('Value', a.value))
            print(" :10s :6d".format('Minimum', a.minimum))
            print(" :10s :6d".format('Maximum', a.maximum))
            print(" :10s :6d".format('Fuzz', a.fuzz))
            print(" :10s :6d".format('Flat', a.flat))
            print(" :10s :6d".format('Resolution', a.resolution))

            print("Properties:")
            for p in l.properties:
            print(" Property type ()".format(p.value, p.name))


            def print_event(e):
            print("Event: time .:06d, ".format(e.sec, e.usec), end='')
            if e.matches(libevdev.EV_SYN):
            if e.matches(libevdev.EV_SYN.SYN_MT_REPORT):
            print("++++++++++++++ ++++++++++++".format(e.code.name))
            elif e.matches(libevdev.EV_SYN.SYN_DROPPED):
            print(">>>>>>>>>>>>>> >>>>>>>>>>>>".format(e.code.name))
            else:
            print("-------------- ------------".format(e.code.name))
            else:
            print("type :02x code :03x :20s value :4d".format(e.type.value, e.type.name, e.code.value, e.code.name, e.value))


            class Tablet():
            def __init__(self, tablet_name):
            self.tablet_name = tablet_name

            def __enter__(self):
            self.dev = libevdev.Device()
            self.dev.name = "Tablet alone"
            ### NB: all the following information needs to be enabled
            ### in order to recognize the device as a tablet.
            # Say that the device will send "absolute" values
            self.dev.enable(libevdev.INPUT_PROP_DIRECT)
            # Say that we are using the pen (not the erasor), and should be set to 1 when we are at proximity to the device.
            # See http://www.infradead.org/~mchehab/kernel_docs_pdf/linux-input.pdf page 9 (=13) and guidelines page 12 (=16), or the https://github.com/linuxwacom/input-wacom/blob/master/4.5/wacom_w8001.c (rdy=proximity)
            self.dev.enable(libevdev.EV_KEY.BTN_TOOL_PEN)
            self.dev.enable(libevdev.EV_KEY.BTN_TOOL_RUBBER)
            # Click
            self.dev.enable(libevdev.EV_KEY.BTN_TOUCH)
            # Press button 1 on pen
            self.dev.enable(libevdev.EV_KEY.BTN_STYLUS)
            # Press button 2 on pen, see great doc
            self.dev.enable(libevdev.EV_KEY.BTN_STYLUS2)
            # Send absolute X coordinate
            self.dev.enable(libevdev.EV_ABS.ABS_X,
            libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
            # Send absolute Y coordinate
            self.dev.enable(libevdev.EV_ABS.ABS_Y,
            libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
            # Send absolute pressure
            self.dev.enable(libevdev.EV_ABS.ABS_PRESSURE,
            libevdev.InputAbsInfo(minimum=0, maximum=8191))
            # Use to confirm that we finished to send the informations
            # (to be sent after every burst of information, otherwise
            # the kernel does not proceed the information)
            self.dev.enable(libevdev.EV_SYN.SYN_REPORT)
            # Report buffer overflow
            self.dev.enable(libevdev.EV_SYN.SYN_DROPPED)
            self.uinput = self.dev.create_uinput_device()
            print("New device at ()".format(self.uinput.devnode, self.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)
            # self.simulate_first_click()
            self.reset_state()
            return self

            def __exit__(self, type, value, traceback):
            pass

            def reset_state(self):
            self.is_away = True
            self.is_touching = False
            self.pressed_button_1 = False
            self.pressed_button_2 = False
            self.lastmodif = datetime.datetime.now()

            def send_events(self, events, is_away=False):
            self.lastmodif = datetime.datetime.now()
            self.is_away = is_away
            self.uinput.send_events(events)

            def simulate_first_click(self):
            """Useful only the first time to make sure
            xinput detected the input"""
            # 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.
            self.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.
            self.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),
            ])


            def send_state_no_pos(self, is_away=False):
            self.lastmodif = datetime.datetime.now()
            self.is_away = is_away
            print("Away: , Touching: ".format(self.is_away, self.is_touching))
            self.uinput.send_events([
            libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
            value=1 if self.is_touching else 0),
            libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
            value=1 if not self.is_away else 0),
            libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS,
            value=1 if self.pressed_button_1 else 0),
            libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2,
            value=1 if self.pressed_button_2 else 0),
            libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
            value=0),
            ])


            def touch_press(self):
            self.is_touching = True
            self.send_state_no_pos()

            def touch_release(self):
            self.is_touching = False
            self.send_state_no_pos()

            def button_1_press(self):
            self.pressed_button_1 = True
            self.send_state_no_pos()

            def button_1_release(self):
            self.pressed_button_1 = False
            self.send_state_no_pos()

            def button_2_press(self):
            self.pressed_button_2 = True
            self.send_state_no_pos()

            def button_2_release(self):
            self.pressed_button_2 = False
            self.send_state_no_pos()

            def move_x(self, abs_x):
            self.send_events([
            libevdev.InputEvent(libevdev.EV_ABS.ABS_X,
            value=abs_x),
            libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
            value=0),
            ])

            def move_y(self, abs_y):
            self.send_events([
            libevdev.InputEvent(libevdev.EV_ABS.ABS_Y,
            value=abs_y),
            libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
            value=0),
            ])

            def change_pressure(self, pressure):
            self.send_events([
            libevdev.InputEvent(libevdev.EV_ABS.ABS_PRESSURE,
            value=pressure),
            libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
            value=0),
            ])

            def handle_event(self, e):
            if e.matches(libevdev.EV_ABS.ABS_PRESSURE):
            self.change_pressure(e.value)
            elif e.matches(libevdev.EV_ABS.ABS_X):
            self.move_x(e.value)
            elif e.matches(libevdev.EV_ABS.ABS_Y):
            self.move_y(e.value)
            elif e.matches(libevdev.EV_KEY.BTN_LEFT):
            if e.value == 1:
            self.touch_press()
            else:
            self.touch_release()
            elif e.matches(libevdev.EV_SYN.SYN_REPORT):
            pass
            else:
            print("Unkown event:")
            print_event(e)



            def main(args):
            path = args[1]
            try:
            with Tablet("Tablet alone") as tablet:
            ### Read the events from real graphics tablet
            with open(path, "rb") as fd:
            l = libevdev.Device(fd)
            print_capabilities(l)
            print("################################n"
            "# Waiting for events #n"
            "################################")
            while True:
            try:
            ev = l.events()
            for e in ev:
            print_event(e)
            tablet.handle_event(e)
            except libevdev.EventsDroppedException:
            for e in l.sync():
            print_event(e)
            tablet.handle_event(e)
            except KeyboardInterrupt:
            pass
            except IOError as e:
            import errno
            if e.errno == errno.EACCES:
            print("Insufficient permissions to access ".format(path))
            elif e.errno == errno.ENOENT:
            print("Device does not exist".format(path))
            else:
            raise e
            except OSError as e:
            print(e)


            if __name__ == "__main__":
            if len(sys.argv) != 2:
            print("Usage: sudo /dev/input/eventX".format(sys.argv[0]))
            print(" $ sudo evtest")
            print("can help you to know which file to use.")
            sys.exit(1)
            main(sys.argv)





            share|improve this answer



























              0














              Youou!!! I managed to make a python script that gives me back the pressure on a new virtual device :-D Now I can use it with Gimp/Krita/... The only remaining part to do is to write a proper C driver and load it directly in the kernel... If you have some idea let me know!



              Proof:



              enter image description here



              So the idea of the script is to read the input from the event file of the graphics tablet, and then create the good events (no more BTN_LEFT, but BTN_TOUCH instead... see my other question/answer here for more details).



              To run the script (see below), save it under combine_both.py, and make it executable/install deps:



              $ chmod +x combine_both.py
              $ sudo pip3 install libevdev


              Then, check out the available input devices:



              $ xinput list


              This should give you several entries, among them one entry like



              VEIKK.INC A30 Mouse id=12 [slave pointer (2)]


              Note the id (noted as <id veikk> later), as well as the id of the virtual core pointer <id core>:



              Virtual core pointer id=2 [master pointer (3)]


              Then, you need to know which /dev/input/eventX file to pick, the easiest is to run sudo evtest and read the name of the file corresponding to the VEIKK.INC A30 Mouse. Then run the script with this file in argument like in:



              sudo ./combine_both.py /dev/input/event7


              This script should output stuff when you try to click/move on the device. Moreover xinput list should also list this device with name Tablet alone Pen (0) with an id <id fake tablet>, and xinput test <id fake tablet> should give you something like (note the 3 columns for x/y/pressure):



              $ xinput test 21
              [...]
              motion a[0]=4151295 a[1]=4151295 a[2]=241
              motion a[0]=4060671 a[1]=4060671 a[2]=226
              motion a[0]=3969535 a[1]=3969535 a[2]=211
              motion a[0]=3878399 a[1]=3878399 a[2]=196
              motion a[0]=3787775 a[1]=3787775 a[2]=181
              motion a[0]=3696639 a[1]=3696639 a[2]=166
              motion a[0]=3605503 a[1]=3605503 a[2]=151
              motion a[0]=3514879 a[1]=3514879 a[2]=137
              motion a[0]=3423743 a[1]=3423743 a[2]=122
              motion a[0]=3332607 a[1]=3332607 a[2]=107
              motion a[0]=3241983 a[1]=3241983 a[2]=92
              motion a[0]=3150847 a[1]=3150847 a[2]=77
              motion a[0]=3059711 a[1]=3059711 a[2]=62
              motion a[0]=2969087 a[1]=2969087 a[2]=47
              motion a[0]=2877951 a[1]=2877951 a[2]=32
              motion a[0]=2650623 a[1]=2650623 a[2]=17
              button release 1


              Now, to make it work with Gimp/Krita, you want to disable the real mouse (otherwise you have conflicts between the real and fake tablet), with



              xinput float <id veikk>


              and when you are done you can reattach the real device with



              xinput reattach <id veikk> <id core>


              In gimp, don't forget to setup Tablet alone Pen (0) with mode=screen in Edit/input devices, and make sure the real tablet VEIKK.INC A30 Mouse is disabled. Finally, chose a good dynamic, like Pencil Generic to test!



              Enjoy! (and I'll let you know if I write the C driver at some points)



              Script:



              #!/usr/bin/env python3
              import sys
              import libevdev
              import datetime
              import time


              def print_capabilities(l):
              v = l.driver_version
              print("Input driver version is ..".format(v >> 16, (v >> 8) & 0xff, v & 0xff))
              id = l.id
              print("Input device ID: bus :#x vendor :#x product :#x version :#x".format(
              id["bustype"],
              id["vendor"],
              id["product"],
              id["version"],
              ))
              print("Input device name: ".format(l.name))
              print("Supported events:")

              for t, cs in l.evbits.items():
              print(" Event type ()".format(t.value, t.name))

              for c in cs:
              if t in [libevdev.EV_LED, libevdev.EV_SND, libevdev.EV_SW]:
              v = l.value[c]
              print(" Event code () state ".format(c.value, c.name, v))
              else:
              print(" Event code ()".format(c.value, c.name))

              if t == libevdev.EV_ABS:
              a = l.absinfo[c]
              print(" :10s :6d".format('Value', a.value))
              print(" :10s :6d".format('Minimum', a.minimum))
              print(" :10s :6d".format('Maximum', a.maximum))
              print(" :10s :6d".format('Fuzz', a.fuzz))
              print(" :10s :6d".format('Flat', a.flat))
              print(" :10s :6d".format('Resolution', a.resolution))

              print("Properties:")
              for p in l.properties:
              print(" Property type ()".format(p.value, p.name))


              def print_event(e):
              print("Event: time .:06d, ".format(e.sec, e.usec), end='')
              if e.matches(libevdev.EV_SYN):
              if e.matches(libevdev.EV_SYN.SYN_MT_REPORT):
              print("++++++++++++++ ++++++++++++".format(e.code.name))
              elif e.matches(libevdev.EV_SYN.SYN_DROPPED):
              print(">>>>>>>>>>>>>> >>>>>>>>>>>>".format(e.code.name))
              else:
              print("-------------- ------------".format(e.code.name))
              else:
              print("type :02x code :03x :20s value :4d".format(e.type.value, e.type.name, e.code.value, e.code.name, e.value))


              class Tablet():
              def __init__(self, tablet_name):
              self.tablet_name = tablet_name

              def __enter__(self):
              self.dev = libevdev.Device()
              self.dev.name = "Tablet alone"
              ### NB: all the following information needs to be enabled
              ### in order to recognize the device as a tablet.
              # Say that the device will send "absolute" values
              self.dev.enable(libevdev.INPUT_PROP_DIRECT)
              # Say that we are using the pen (not the erasor), and should be set to 1 when we are at proximity to the device.
              # See http://www.infradead.org/~mchehab/kernel_docs_pdf/linux-input.pdf page 9 (=13) and guidelines page 12 (=16), or the https://github.com/linuxwacom/input-wacom/blob/master/4.5/wacom_w8001.c (rdy=proximity)
              self.dev.enable(libevdev.EV_KEY.BTN_TOOL_PEN)
              self.dev.enable(libevdev.EV_KEY.BTN_TOOL_RUBBER)
              # Click
              self.dev.enable(libevdev.EV_KEY.BTN_TOUCH)
              # Press button 1 on pen
              self.dev.enable(libevdev.EV_KEY.BTN_STYLUS)
              # Press button 2 on pen, see great doc
              self.dev.enable(libevdev.EV_KEY.BTN_STYLUS2)
              # Send absolute X coordinate
              self.dev.enable(libevdev.EV_ABS.ABS_X,
              libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
              # Send absolute Y coordinate
              self.dev.enable(libevdev.EV_ABS.ABS_Y,
              libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
              # Send absolute pressure
              self.dev.enable(libevdev.EV_ABS.ABS_PRESSURE,
              libevdev.InputAbsInfo(minimum=0, maximum=8191))
              # Use to confirm that we finished to send the informations
              # (to be sent after every burst of information, otherwise
              # the kernel does not proceed the information)
              self.dev.enable(libevdev.EV_SYN.SYN_REPORT)
              # Report buffer overflow
              self.dev.enable(libevdev.EV_SYN.SYN_DROPPED)
              self.uinput = self.dev.create_uinput_device()
              print("New device at ()".format(self.uinput.devnode, self.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)
              # self.simulate_first_click()
              self.reset_state()
              return self

              def __exit__(self, type, value, traceback):
              pass

              def reset_state(self):
              self.is_away = True
              self.is_touching = False
              self.pressed_button_1 = False
              self.pressed_button_2 = False
              self.lastmodif = datetime.datetime.now()

              def send_events(self, events, is_away=False):
              self.lastmodif = datetime.datetime.now()
              self.is_away = is_away
              self.uinput.send_events(events)

              def simulate_first_click(self):
              """Useful only the first time to make sure
              xinput detected the input"""
              # 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.
              self.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.
              self.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),
              ])


              def send_state_no_pos(self, is_away=False):
              self.lastmodif = datetime.datetime.now()
              self.is_away = is_away
              print("Away: , Touching: ".format(self.is_away, self.is_touching))
              self.uinput.send_events([
              libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
              value=1 if self.is_touching else 0),
              libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
              value=1 if not self.is_away else 0),
              libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS,
              value=1 if self.pressed_button_1 else 0),
              libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2,
              value=1 if self.pressed_button_2 else 0),
              libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
              value=0),
              ])


              def touch_press(self):
              self.is_touching = True
              self.send_state_no_pos()

              def touch_release(self):
              self.is_touching = False
              self.send_state_no_pos()

              def button_1_press(self):
              self.pressed_button_1 = True
              self.send_state_no_pos()

              def button_1_release(self):
              self.pressed_button_1 = False
              self.send_state_no_pos()

              def button_2_press(self):
              self.pressed_button_2 = True
              self.send_state_no_pos()

              def button_2_release(self):
              self.pressed_button_2 = False
              self.send_state_no_pos()

              def move_x(self, abs_x):
              self.send_events([
              libevdev.InputEvent(libevdev.EV_ABS.ABS_X,
              value=abs_x),
              libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
              value=0),
              ])

              def move_y(self, abs_y):
              self.send_events([
              libevdev.InputEvent(libevdev.EV_ABS.ABS_Y,
              value=abs_y),
              libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
              value=0),
              ])

              def change_pressure(self, pressure):
              self.send_events([
              libevdev.InputEvent(libevdev.EV_ABS.ABS_PRESSURE,
              value=pressure),
              libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
              value=0),
              ])

              def handle_event(self, e):
              if e.matches(libevdev.EV_ABS.ABS_PRESSURE):
              self.change_pressure(e.value)
              elif e.matches(libevdev.EV_ABS.ABS_X):
              self.move_x(e.value)
              elif e.matches(libevdev.EV_ABS.ABS_Y):
              self.move_y(e.value)
              elif e.matches(libevdev.EV_KEY.BTN_LEFT):
              if e.value == 1:
              self.touch_press()
              else:
              self.touch_release()
              elif e.matches(libevdev.EV_SYN.SYN_REPORT):
              pass
              else:
              print("Unkown event:")
              print_event(e)



              def main(args):
              path = args[1]
              try:
              with Tablet("Tablet alone") as tablet:
              ### Read the events from real graphics tablet
              with open(path, "rb") as fd:
              l = libevdev.Device(fd)
              print_capabilities(l)
              print("################################n"
              "# Waiting for events #n"
              "################################")
              while True:
              try:
              ev = l.events()
              for e in ev:
              print_event(e)
              tablet.handle_event(e)
              except libevdev.EventsDroppedException:
              for e in l.sync():
              print_event(e)
              tablet.handle_event(e)
              except KeyboardInterrupt:
              pass
              except IOError as e:
              import errno
              if e.errno == errno.EACCES:
              print("Insufficient permissions to access ".format(path))
              elif e.errno == errno.ENOENT:
              print("Device does not exist".format(path))
              else:
              raise e
              except OSError as e:
              print(e)


              if __name__ == "__main__":
              if len(sys.argv) != 2:
              print("Usage: sudo /dev/input/eventX".format(sys.argv[0]))
              print(" $ sudo evtest")
              print("can help you to know which file to use.")
              sys.exit(1)
              main(sys.argv)





              share|improve this answer

























                0












                0








                0







                Youou!!! I managed to make a python script that gives me back the pressure on a new virtual device :-D Now I can use it with Gimp/Krita/... The only remaining part to do is to write a proper C driver and load it directly in the kernel... If you have some idea let me know!



                Proof:



                enter image description here



                So the idea of the script is to read the input from the event file of the graphics tablet, and then create the good events (no more BTN_LEFT, but BTN_TOUCH instead... see my other question/answer here for more details).



                To run the script (see below), save it under combine_both.py, and make it executable/install deps:



                $ chmod +x combine_both.py
                $ sudo pip3 install libevdev


                Then, check out the available input devices:



                $ xinput list


                This should give you several entries, among them one entry like



                VEIKK.INC A30 Mouse id=12 [slave pointer (2)]


                Note the id (noted as <id veikk> later), as well as the id of the virtual core pointer <id core>:



                Virtual core pointer id=2 [master pointer (3)]


                Then, you need to know which /dev/input/eventX file to pick, the easiest is to run sudo evtest and read the name of the file corresponding to the VEIKK.INC A30 Mouse. Then run the script with this file in argument like in:



                sudo ./combine_both.py /dev/input/event7


                This script should output stuff when you try to click/move on the device. Moreover xinput list should also list this device with name Tablet alone Pen (0) with an id <id fake tablet>, and xinput test <id fake tablet> should give you something like (note the 3 columns for x/y/pressure):



                $ xinput test 21
                [...]
                motion a[0]=4151295 a[1]=4151295 a[2]=241
                motion a[0]=4060671 a[1]=4060671 a[2]=226
                motion a[0]=3969535 a[1]=3969535 a[2]=211
                motion a[0]=3878399 a[1]=3878399 a[2]=196
                motion a[0]=3787775 a[1]=3787775 a[2]=181
                motion a[0]=3696639 a[1]=3696639 a[2]=166
                motion a[0]=3605503 a[1]=3605503 a[2]=151
                motion a[0]=3514879 a[1]=3514879 a[2]=137
                motion a[0]=3423743 a[1]=3423743 a[2]=122
                motion a[0]=3332607 a[1]=3332607 a[2]=107
                motion a[0]=3241983 a[1]=3241983 a[2]=92
                motion a[0]=3150847 a[1]=3150847 a[2]=77
                motion a[0]=3059711 a[1]=3059711 a[2]=62
                motion a[0]=2969087 a[1]=2969087 a[2]=47
                motion a[0]=2877951 a[1]=2877951 a[2]=32
                motion a[0]=2650623 a[1]=2650623 a[2]=17
                button release 1


                Now, to make it work with Gimp/Krita, you want to disable the real mouse (otherwise you have conflicts between the real and fake tablet), with



                xinput float <id veikk>


                and when you are done you can reattach the real device with



                xinput reattach <id veikk> <id core>


                In gimp, don't forget to setup Tablet alone Pen (0) with mode=screen in Edit/input devices, and make sure the real tablet VEIKK.INC A30 Mouse is disabled. Finally, chose a good dynamic, like Pencil Generic to test!



                Enjoy! (and I'll let you know if I write the C driver at some points)



                Script:



                #!/usr/bin/env python3
                import sys
                import libevdev
                import datetime
                import time


                def print_capabilities(l):
                v = l.driver_version
                print("Input driver version is ..".format(v >> 16, (v >> 8) & 0xff, v & 0xff))
                id = l.id
                print("Input device ID: bus :#x vendor :#x product :#x version :#x".format(
                id["bustype"],
                id["vendor"],
                id["product"],
                id["version"],
                ))
                print("Input device name: ".format(l.name))
                print("Supported events:")

                for t, cs in l.evbits.items():
                print(" Event type ()".format(t.value, t.name))

                for c in cs:
                if t in [libevdev.EV_LED, libevdev.EV_SND, libevdev.EV_SW]:
                v = l.value[c]
                print(" Event code () state ".format(c.value, c.name, v))
                else:
                print(" Event code ()".format(c.value, c.name))

                if t == libevdev.EV_ABS:
                a = l.absinfo[c]
                print(" :10s :6d".format('Value', a.value))
                print(" :10s :6d".format('Minimum', a.minimum))
                print(" :10s :6d".format('Maximum', a.maximum))
                print(" :10s :6d".format('Fuzz', a.fuzz))
                print(" :10s :6d".format('Flat', a.flat))
                print(" :10s :6d".format('Resolution', a.resolution))

                print("Properties:")
                for p in l.properties:
                print(" Property type ()".format(p.value, p.name))


                def print_event(e):
                print("Event: time .:06d, ".format(e.sec, e.usec), end='')
                if e.matches(libevdev.EV_SYN):
                if e.matches(libevdev.EV_SYN.SYN_MT_REPORT):
                print("++++++++++++++ ++++++++++++".format(e.code.name))
                elif e.matches(libevdev.EV_SYN.SYN_DROPPED):
                print(">>>>>>>>>>>>>> >>>>>>>>>>>>".format(e.code.name))
                else:
                print("-------------- ------------".format(e.code.name))
                else:
                print("type :02x code :03x :20s value :4d".format(e.type.value, e.type.name, e.code.value, e.code.name, e.value))


                class Tablet():
                def __init__(self, tablet_name):
                self.tablet_name = tablet_name

                def __enter__(self):
                self.dev = libevdev.Device()
                self.dev.name = "Tablet alone"
                ### NB: all the following information needs to be enabled
                ### in order to recognize the device as a tablet.
                # Say that the device will send "absolute" values
                self.dev.enable(libevdev.INPUT_PROP_DIRECT)
                # Say that we are using the pen (not the erasor), and should be set to 1 when we are at proximity to the device.
                # See http://www.infradead.org/~mchehab/kernel_docs_pdf/linux-input.pdf page 9 (=13) and guidelines page 12 (=16), or the https://github.com/linuxwacom/input-wacom/blob/master/4.5/wacom_w8001.c (rdy=proximity)
                self.dev.enable(libevdev.EV_KEY.BTN_TOOL_PEN)
                self.dev.enable(libevdev.EV_KEY.BTN_TOOL_RUBBER)
                # Click
                self.dev.enable(libevdev.EV_KEY.BTN_TOUCH)
                # Press button 1 on pen
                self.dev.enable(libevdev.EV_KEY.BTN_STYLUS)
                # Press button 2 on pen, see great doc
                self.dev.enable(libevdev.EV_KEY.BTN_STYLUS2)
                # Send absolute X coordinate
                self.dev.enable(libevdev.EV_ABS.ABS_X,
                libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
                # Send absolute Y coordinate
                self.dev.enable(libevdev.EV_ABS.ABS_Y,
                libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
                # Send absolute pressure
                self.dev.enable(libevdev.EV_ABS.ABS_PRESSURE,
                libevdev.InputAbsInfo(minimum=0, maximum=8191))
                # Use to confirm that we finished to send the informations
                # (to be sent after every burst of information, otherwise
                # the kernel does not proceed the information)
                self.dev.enable(libevdev.EV_SYN.SYN_REPORT)
                # Report buffer overflow
                self.dev.enable(libevdev.EV_SYN.SYN_DROPPED)
                self.uinput = self.dev.create_uinput_device()
                print("New device at ()".format(self.uinput.devnode, self.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)
                # self.simulate_first_click()
                self.reset_state()
                return self

                def __exit__(self, type, value, traceback):
                pass

                def reset_state(self):
                self.is_away = True
                self.is_touching = False
                self.pressed_button_1 = False
                self.pressed_button_2 = False
                self.lastmodif = datetime.datetime.now()

                def send_events(self, events, is_away=False):
                self.lastmodif = datetime.datetime.now()
                self.is_away = is_away
                self.uinput.send_events(events)

                def simulate_first_click(self):
                """Useful only the first time to make sure
                xinput detected the input"""
                # 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.
                self.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.
                self.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),
                ])


                def send_state_no_pos(self, is_away=False):
                self.lastmodif = datetime.datetime.now()
                self.is_away = is_away
                print("Away: , Touching: ".format(self.is_away, self.is_touching))
                self.uinput.send_events([
                libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
                value=1 if self.is_touching else 0),
                libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
                value=1 if not self.is_away else 0),
                libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS,
                value=1 if self.pressed_button_1 else 0),
                libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2,
                value=1 if self.pressed_button_2 else 0),
                libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                value=0),
                ])


                def touch_press(self):
                self.is_touching = True
                self.send_state_no_pos()

                def touch_release(self):
                self.is_touching = False
                self.send_state_no_pos()

                def button_1_press(self):
                self.pressed_button_1 = True
                self.send_state_no_pos()

                def button_1_release(self):
                self.pressed_button_1 = False
                self.send_state_no_pos()

                def button_2_press(self):
                self.pressed_button_2 = True
                self.send_state_no_pos()

                def button_2_release(self):
                self.pressed_button_2 = False
                self.send_state_no_pos()

                def move_x(self, abs_x):
                self.send_events([
                libevdev.InputEvent(libevdev.EV_ABS.ABS_X,
                value=abs_x),
                libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                value=0),
                ])

                def move_y(self, abs_y):
                self.send_events([
                libevdev.InputEvent(libevdev.EV_ABS.ABS_Y,
                value=abs_y),
                libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                value=0),
                ])

                def change_pressure(self, pressure):
                self.send_events([
                libevdev.InputEvent(libevdev.EV_ABS.ABS_PRESSURE,
                value=pressure),
                libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                value=0),
                ])

                def handle_event(self, e):
                if e.matches(libevdev.EV_ABS.ABS_PRESSURE):
                self.change_pressure(e.value)
                elif e.matches(libevdev.EV_ABS.ABS_X):
                self.move_x(e.value)
                elif e.matches(libevdev.EV_ABS.ABS_Y):
                self.move_y(e.value)
                elif e.matches(libevdev.EV_KEY.BTN_LEFT):
                if e.value == 1:
                self.touch_press()
                else:
                self.touch_release()
                elif e.matches(libevdev.EV_SYN.SYN_REPORT):
                pass
                else:
                print("Unkown event:")
                print_event(e)



                def main(args):
                path = args[1]
                try:
                with Tablet("Tablet alone") as tablet:
                ### Read the events from real graphics tablet
                with open(path, "rb") as fd:
                l = libevdev.Device(fd)
                print_capabilities(l)
                print("################################n"
                "# Waiting for events #n"
                "################################")
                while True:
                try:
                ev = l.events()
                for e in ev:
                print_event(e)
                tablet.handle_event(e)
                except libevdev.EventsDroppedException:
                for e in l.sync():
                print_event(e)
                tablet.handle_event(e)
                except KeyboardInterrupt:
                pass
                except IOError as e:
                import errno
                if e.errno == errno.EACCES:
                print("Insufficient permissions to access ".format(path))
                elif e.errno == errno.ENOENT:
                print("Device does not exist".format(path))
                else:
                raise e
                except OSError as e:
                print(e)


                if __name__ == "__main__":
                if len(sys.argv) != 2:
                print("Usage: sudo /dev/input/eventX".format(sys.argv[0]))
                print(" $ sudo evtest")
                print("can help you to know which file to use.")
                sys.exit(1)
                main(sys.argv)





                share|improve this answer













                Youou!!! I managed to make a python script that gives me back the pressure on a new virtual device :-D Now I can use it with Gimp/Krita/... The only remaining part to do is to write a proper C driver and load it directly in the kernel... If you have some idea let me know!



                Proof:



                enter image description here



                So the idea of the script is to read the input from the event file of the graphics tablet, and then create the good events (no more BTN_LEFT, but BTN_TOUCH instead... see my other question/answer here for more details).



                To run the script (see below), save it under combine_both.py, and make it executable/install deps:



                $ chmod +x combine_both.py
                $ sudo pip3 install libevdev


                Then, check out the available input devices:



                $ xinput list


                This should give you several entries, among them one entry like



                VEIKK.INC A30 Mouse id=12 [slave pointer (2)]


                Note the id (noted as <id veikk> later), as well as the id of the virtual core pointer <id core>:



                Virtual core pointer id=2 [master pointer (3)]


                Then, you need to know which /dev/input/eventX file to pick, the easiest is to run sudo evtest and read the name of the file corresponding to the VEIKK.INC A30 Mouse. Then run the script with this file in argument like in:



                sudo ./combine_both.py /dev/input/event7


                This script should output stuff when you try to click/move on the device. Moreover xinput list should also list this device with name Tablet alone Pen (0) with an id <id fake tablet>, and xinput test <id fake tablet> should give you something like (note the 3 columns for x/y/pressure):



                $ xinput test 21
                [...]
                motion a[0]=4151295 a[1]=4151295 a[2]=241
                motion a[0]=4060671 a[1]=4060671 a[2]=226
                motion a[0]=3969535 a[1]=3969535 a[2]=211
                motion a[0]=3878399 a[1]=3878399 a[2]=196
                motion a[0]=3787775 a[1]=3787775 a[2]=181
                motion a[0]=3696639 a[1]=3696639 a[2]=166
                motion a[0]=3605503 a[1]=3605503 a[2]=151
                motion a[0]=3514879 a[1]=3514879 a[2]=137
                motion a[0]=3423743 a[1]=3423743 a[2]=122
                motion a[0]=3332607 a[1]=3332607 a[2]=107
                motion a[0]=3241983 a[1]=3241983 a[2]=92
                motion a[0]=3150847 a[1]=3150847 a[2]=77
                motion a[0]=3059711 a[1]=3059711 a[2]=62
                motion a[0]=2969087 a[1]=2969087 a[2]=47
                motion a[0]=2877951 a[1]=2877951 a[2]=32
                motion a[0]=2650623 a[1]=2650623 a[2]=17
                button release 1


                Now, to make it work with Gimp/Krita, you want to disable the real mouse (otherwise you have conflicts between the real and fake tablet), with



                xinput float <id veikk>


                and when you are done you can reattach the real device with



                xinput reattach <id veikk> <id core>


                In gimp, don't forget to setup Tablet alone Pen (0) with mode=screen in Edit/input devices, and make sure the real tablet VEIKK.INC A30 Mouse is disabled. Finally, chose a good dynamic, like Pencil Generic to test!



                Enjoy! (and I'll let you know if I write the C driver at some points)



                Script:



                #!/usr/bin/env python3
                import sys
                import libevdev
                import datetime
                import time


                def print_capabilities(l):
                v = l.driver_version
                print("Input driver version is ..".format(v >> 16, (v >> 8) & 0xff, v & 0xff))
                id = l.id
                print("Input device ID: bus :#x vendor :#x product :#x version :#x".format(
                id["bustype"],
                id["vendor"],
                id["product"],
                id["version"],
                ))
                print("Input device name: ".format(l.name))
                print("Supported events:")

                for t, cs in l.evbits.items():
                print(" Event type ()".format(t.value, t.name))

                for c in cs:
                if t in [libevdev.EV_LED, libevdev.EV_SND, libevdev.EV_SW]:
                v = l.value[c]
                print(" Event code () state ".format(c.value, c.name, v))
                else:
                print(" Event code ()".format(c.value, c.name))

                if t == libevdev.EV_ABS:
                a = l.absinfo[c]
                print(" :10s :6d".format('Value', a.value))
                print(" :10s :6d".format('Minimum', a.minimum))
                print(" :10s :6d".format('Maximum', a.maximum))
                print(" :10s :6d".format('Fuzz', a.fuzz))
                print(" :10s :6d".format('Flat', a.flat))
                print(" :10s :6d".format('Resolution', a.resolution))

                print("Properties:")
                for p in l.properties:
                print(" Property type ()".format(p.value, p.name))


                def print_event(e):
                print("Event: time .:06d, ".format(e.sec, e.usec), end='')
                if e.matches(libevdev.EV_SYN):
                if e.matches(libevdev.EV_SYN.SYN_MT_REPORT):
                print("++++++++++++++ ++++++++++++".format(e.code.name))
                elif e.matches(libevdev.EV_SYN.SYN_DROPPED):
                print(">>>>>>>>>>>>>> >>>>>>>>>>>>".format(e.code.name))
                else:
                print("-------------- ------------".format(e.code.name))
                else:
                print("type :02x code :03x :20s value :4d".format(e.type.value, e.type.name, e.code.value, e.code.name, e.value))


                class Tablet():
                def __init__(self, tablet_name):
                self.tablet_name = tablet_name

                def __enter__(self):
                self.dev = libevdev.Device()
                self.dev.name = "Tablet alone"
                ### NB: all the following information needs to be enabled
                ### in order to recognize the device as a tablet.
                # Say that the device will send "absolute" values
                self.dev.enable(libevdev.INPUT_PROP_DIRECT)
                # Say that we are using the pen (not the erasor), and should be set to 1 when we are at proximity to the device.
                # See http://www.infradead.org/~mchehab/kernel_docs_pdf/linux-input.pdf page 9 (=13) and guidelines page 12 (=16), or the https://github.com/linuxwacom/input-wacom/blob/master/4.5/wacom_w8001.c (rdy=proximity)
                self.dev.enable(libevdev.EV_KEY.BTN_TOOL_PEN)
                self.dev.enable(libevdev.EV_KEY.BTN_TOOL_RUBBER)
                # Click
                self.dev.enable(libevdev.EV_KEY.BTN_TOUCH)
                # Press button 1 on pen
                self.dev.enable(libevdev.EV_KEY.BTN_STYLUS)
                # Press button 2 on pen, see great doc
                self.dev.enable(libevdev.EV_KEY.BTN_STYLUS2)
                # Send absolute X coordinate
                self.dev.enable(libevdev.EV_ABS.ABS_X,
                libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
                # Send absolute Y coordinate
                self.dev.enable(libevdev.EV_ABS.ABS_Y,
                libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
                # Send absolute pressure
                self.dev.enable(libevdev.EV_ABS.ABS_PRESSURE,
                libevdev.InputAbsInfo(minimum=0, maximum=8191))
                # Use to confirm that we finished to send the informations
                # (to be sent after every burst of information, otherwise
                # the kernel does not proceed the information)
                self.dev.enable(libevdev.EV_SYN.SYN_REPORT)
                # Report buffer overflow
                self.dev.enable(libevdev.EV_SYN.SYN_DROPPED)
                self.uinput = self.dev.create_uinput_device()
                print("New device at ()".format(self.uinput.devnode, self.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)
                # self.simulate_first_click()
                self.reset_state()
                return self

                def __exit__(self, type, value, traceback):
                pass

                def reset_state(self):
                self.is_away = True
                self.is_touching = False
                self.pressed_button_1 = False
                self.pressed_button_2 = False
                self.lastmodif = datetime.datetime.now()

                def send_events(self, events, is_away=False):
                self.lastmodif = datetime.datetime.now()
                self.is_away = is_away
                self.uinput.send_events(events)

                def simulate_first_click(self):
                """Useful only the first time to make sure
                xinput detected the input"""
                # 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.
                self.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.
                self.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),
                ])


                def send_state_no_pos(self, is_away=False):
                self.lastmodif = datetime.datetime.now()
                self.is_away = is_away
                print("Away: , Touching: ".format(self.is_away, self.is_touching))
                self.uinput.send_events([
                libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
                value=1 if self.is_touching else 0),
                libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
                value=1 if not self.is_away else 0),
                libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS,
                value=1 if self.pressed_button_1 else 0),
                libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2,
                value=1 if self.pressed_button_2 else 0),
                libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                value=0),
                ])


                def touch_press(self):
                self.is_touching = True
                self.send_state_no_pos()

                def touch_release(self):
                self.is_touching = False
                self.send_state_no_pos()

                def button_1_press(self):
                self.pressed_button_1 = True
                self.send_state_no_pos()

                def button_1_release(self):
                self.pressed_button_1 = False
                self.send_state_no_pos()

                def button_2_press(self):
                self.pressed_button_2 = True
                self.send_state_no_pos()

                def button_2_release(self):
                self.pressed_button_2 = False
                self.send_state_no_pos()

                def move_x(self, abs_x):
                self.send_events([
                libevdev.InputEvent(libevdev.EV_ABS.ABS_X,
                value=abs_x),
                libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                value=0),
                ])

                def move_y(self, abs_y):
                self.send_events([
                libevdev.InputEvent(libevdev.EV_ABS.ABS_Y,
                value=abs_y),
                libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                value=0),
                ])

                def change_pressure(self, pressure):
                self.send_events([
                libevdev.InputEvent(libevdev.EV_ABS.ABS_PRESSURE,
                value=pressure),
                libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
                value=0),
                ])

                def handle_event(self, e):
                if e.matches(libevdev.EV_ABS.ABS_PRESSURE):
                self.change_pressure(e.value)
                elif e.matches(libevdev.EV_ABS.ABS_X):
                self.move_x(e.value)
                elif e.matches(libevdev.EV_ABS.ABS_Y):
                self.move_y(e.value)
                elif e.matches(libevdev.EV_KEY.BTN_LEFT):
                if e.value == 1:
                self.touch_press()
                else:
                self.touch_release()
                elif e.matches(libevdev.EV_SYN.SYN_REPORT):
                pass
                else:
                print("Unkown event:")
                print_event(e)



                def main(args):
                path = args[1]
                try:
                with Tablet("Tablet alone") as tablet:
                ### Read the events from real graphics tablet
                with open(path, "rb") as fd:
                l = libevdev.Device(fd)
                print_capabilities(l)
                print("################################n"
                "# Waiting for events #n"
                "################################")
                while True:
                try:
                ev = l.events()
                for e in ev:
                print_event(e)
                tablet.handle_event(e)
                except libevdev.EventsDroppedException:
                for e in l.sync():
                print_event(e)
                tablet.handle_event(e)
                except KeyboardInterrupt:
                pass
                except IOError as e:
                import errno
                if e.errno == errno.EACCES:
                print("Insufficient permissions to access ".format(path))
                elif e.errno == errno.ENOENT:
                print("Device does not exist".format(path))
                else:
                raise e
                except OSError as e:
                print(e)


                if __name__ == "__main__":
                if len(sys.argv) != 2:
                print("Usage: sudo /dev/input/eventX".format(sys.argv[0]))
                print(" $ sudo evtest")
                print("can help you to know which file to use.")
                sys.exit(1)
                main(sys.argv)






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered yesterday









                tobiasBoratobiasBora

                264312




                264312



























                    draft saved

                    draft discarded
















































                    Thanks for contributing an answer to Unix & Linux Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid


                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.

                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f507687%2fgraphic-tablet-veikk-pressure-sensitivity-on-linux%23new-answer', 'question_page');

                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    -tablet

                    Popular posts from this blog

                    Creating 100m^2 grid automatically using QGIS?Creating grid constrained within polygon in QGIS?Createing polygon layer from point data using QGIS?Creating vector grid using QGIS?Creating grid polygons from coordinates using R or PythonCreating grid from spatio temporal point data?Creating fields in attributes table using other layers using QGISCreate .shp vector grid in QGISQGIS Creating 4km point grid within polygonsCreate a vector grid over a raster layerVector Grid Creates just one grid

                    What is this called? Old film camera viewer?What makes a good film camera?What to do with an old film camera?What should one look for when buying a used film camera?What is the value and age of this pre-1967 Ricoh 35 mm camera?DSLR recommendation, question about old Canon 35mm film Camera & lensesCan anyone identify the silver rangefinder-style camera in this advertisement?What kind of a Polaroid 600-camera is this?Will an old film camera still work even when not used in a very long time?What is this camera / Can I develop the film?How to fit an action camera into antique (bellows) housing?What to check when buying used and old film bodies?

                    Why is this plane circling around the Lucknow airport every day?Why do aircraft on Flight Radar 24 jump around randomly sometimes?What airport has this walkway over a taxiway?How does Chicago O'Hare's tower sequence aircraft at peak capacity?Which airport is featured in this Delta commercial?After a crash, for how long is the airport closed?Can a passenger plane stand still in the air, or hover at a fixed location above a ground?What are those trucks towing around, and why?What is this airport outside of Cairo, Egypt?Which US airport has the lowest circling MDH?What is this airport video?