После некоторого времени и чтения doc / code я наконец нашел решение.
Если вы просто хотите протестировать скрипт (в конце этого ответа ), просто запустите его от имени пользователя root с:
$ chmod +x completely_fake_tablet.py
$ sudo pip3 install libevdev
$ sudo./completely_fake_tablet.py
, затем откройте gimp, добавьте устройство в качестве устройства ввода, выберите кисть, например Pencil Generic
, и наслаждайтесь. Если вы хотите получить более подробную информацию, прочитайте следующее:
Подводя итог, система очень придирчива, и вы хотите убедиться,:
libevdev.INPUT_PROP_DIRECT
, чтобы сказать, что у вас есть планшет -как устройство (см. ссылку на документ выше)Вы включаете все планшеты -, например такие инструменты, как:
libevdev.EV_KEY.BTN_TOOL_PEN
будет использоваться, чтобы сказать, когда перо находится близко к планшету или нет, libevdev.EV_KEY.BTN_TOUCH
чтобы сказать, когда вы нажимаете libevdev.EV_KEY.BTN_STYLUS
/ libevdev.EV_KEY.BTN_STYLUS2
, что соответствует кнопке на стилусе libevdev.EV_ABS.ABS_{X,Y}
для позиции (обязательно укажите минимальное значение, максимальное значение, и разрешение:без разрешения, устройство никогда не будет обнаружено!)libevdev.EV_ABS.ABS_PRESSURE
для давления libevdev.EV_SYN.SYN_REPORT
, которые должны отправляться каждый раз, когда вы отправляете блок информации. Если вы не отправите это, ядро не будет обрабатывать события или с очень низкой скоростью, например, 1/с. Вы также должны убедиться, что вы ждете не менее одной секунды после включения устройства перед отправкой любого события, иначе планшет не будет распознан в будущем. Я также заметил, что часть мыши с пером не указана в xinput
, пока я не отправлю первое событие. Обратите внимание, что xinput
будет перечислять два устройства, одно для кнопок, которые я предполагаю как клавиатуру, и одно как перо (с именами Tablet alone Pen (0)
и Tablet alone
:
.
$ xinput list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ ETPS/2 Elantech Touchpad id=17 [slave pointer (2)]
⎜ ↳ lircd-uinput id=18 [slave pointer (2)]
⎜ ↳ Tablet alone Pen (0) id=12 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Asus Wireless Radio Control id=7 [slave keyboard (3)]
↳ Video Bus id=8 [slave keyboard (3)]
↳ Video Bus id=9 [slave keyboard (3)]
↳ Sleep Button id=10 [slave keyboard (3)]
↳ USB2.0 HD UVC WebCam: USB2.0 HD id=14 [slave keyboard (3)]
↳ Asus WMI hotkeys id=15 [slave keyboard (3)]
↳ AT Translated Set 2 keyboard id=16 [slave keyboard (3)]
↳ lircd-uinput id=19 [slave keyboard (3)]
↳ Tablet alone id=11 [slave keyboard (3)]
Если вы тестируете с помощью Gimp, убедитесь, что вы открываете свое программное обеспечение после того, какxinput
сможет его перечислить, иначе оно не будет отображаться в списке устройств ввода, и вам нужно будет перезапустить gimp (обратите внимание, что затем вы можете перезапустить скрипт без перезапуска gimp ).Вы также хотите установить в Edit/input device
устройство Tablet alone
на Screen
и выбрать кисть, динамика которой установлена примерно на Pencil Generic
, как на скриншоте. Чтобы скрипт работал, вы также можете нажать Tab
, чтобы снова увеличить область рисования (Tab
, чтобы вернуться к обычному окну ), и масштабировать, пока вся область не будет покрыта.
Скрипт:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import libevdev
import time
## Some doc needed for this project
# http://www.infradead.org/~mchehab/kernel_docs_pdf/linux-input.pdf
## Some code to get inspiration from
# https://github.com/linuxwacom/input-wacom/blob/master/4.5/wacom_w8001.c
## Some doc to read at some point in my life:
# https://lwn.net/Kernel/LDD3/
# https://www.kernel.org/doc/html/v4.11/driver-api/index.html
def main(args):
dev = libevdev.Device()
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
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)
dev.enable(libevdev.EV_KEY.BTN_TOOL_PEN)
dev.enable(libevdev.EV_KEY.BTN_TOOL_RUBBER)
# Click
dev.enable(libevdev.EV_KEY.BTN_TOUCH)
# Press button 1 on pen
dev.enable(libevdev.EV_KEY.BTN_STYLUS)
# Press button 2 on pen, see great doc
dev.enable(libevdev.EV_KEY.BTN_STYLUS2)
# Send absolute X coordinate
dev.enable(libevdev.EV_ABS.ABS_X,
libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
# Send absolute Y coordinate
dev.enable(libevdev.EV_ABS.ABS_Y,
libevdev.InputAbsInfo(minimum=0, maximum=32767, resolution=100))
# Send absolute pressure
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)
dev.enable(libevdev.EV_SYN.SYN_REPORT)
# Report buffer overflow
dev.enable(libevdev.EV_SYN.SYN_DROPPED)
try:
uinput = dev.create_uinput_device()
print("New device at {} ({})".format(uinput.devnode, uinput.syspath))
# Sleep for a bit so udev, libinput, Xorg, Wayland,...
# all have had a chance to see the device and initialize
# it. Otherwise the event will be sent by the kernel but
# nothing is ready to listen to the device yet. And it
# will never be detected in the futur ;-)
time.sleep(1)
# Reports that the PEN is close to the surface
# Important to make sure xinput can detect (and list)
# the pen. Otherwise, it won't write anything in gimp.
uinput.send_events([
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
value=0),
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
value=1),
libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
value=0),
])
# Says that the pen it out of range of the tablet. Useful
# to make sure you can move your mouse, and to avoid
# strange things during the first draw.
uinput.send_events([
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
value=0),
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
value=0),
libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
value=0),
])
print("Waiting 30s to let you:")
print("1) open Gimp")
print("2) Go to 'Edit/Input device' and configure the device 'Tablet alone' to 'Screen'.")
print("3) Save and close the pop up")
print("4) Create a new file (Ctrl-N)")
print("5) Zoom and press 'tab' to have a drawing surface coverint most of the screen.")
print("6) Switch to brush using 'p' key.")
time.sleep(25)
pc = 0
direc = +1
already_pressed_one = False
for i in range(250):
pc_ = pc/100
val_x = int(pc_*10000 + (1-pc_)*17767)
val_y = int(pc_*5000 + (1-pc_)*22767)
val_pres = int(pc_*10 + (1-pc_)*6000)
print("Will send: x={}, y={}, press={} (pc={})".format(
val_x,
val_y,
val_pres,
pc))
uinput.send_events([
libevdev.InputEvent(libevdev.EV_ABS.ABS_X,
value=val_y),
libevdev.InputEvent(libevdev.EV_ABS.ABS_Y,
value=val_y),
libevdev.InputEvent(libevdev.EV_ABS.ABS_PRESSURE,
value=val_pres),
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
value=1),
libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS,
value=0),
libevdev.InputEvent(libevdev.EV_KEY.BTN_STYLUS2,
value=0),
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
value=1),
libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
value=0),
])
pc += direc
if not already_pressed_one:
print("Press!")
uinput.send_events([
# Pen close to device
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
value=1),
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
value=1),
libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
value=0),
])
already_pressed_one = True
if pc >= 100 or pc <=0 :
print("Release click.")
uinput.send_events([
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
value=0),
# Pen outside of the position
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
value=0),
libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
value=0),
])
if pc >= 100:
pc = 100
direc = -1
if pc <= 0:
pc = 0
direc = +1
time.sleep(5)
print("Press!")
uinput.send_events([
# Pen close to device
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOOL_PEN,
value=1),
libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH,
value=1),
libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT,
value=0),
])
already_pressed_one = True
time.sleep(0.1)
except KeyboardInterrupt:
pass
except OSError as e:
print(e)
if __name__ == "__main__":
if len(sys.argv) > 2:
print("Usage: {}")
sys.exit(1)
main(sys.argv)
Обратите внимание, что теперь xinput test
также отображает давление:
$ xinput test 12
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
Простой и краткий ответ заключается в том, что если вы не сделаете history -c
перед history -r
, вы прочтете историю дважды .
Последняя команда (и ее номер )задаются с помощьюfc -l -1
:
$ fc -l -1
19131 clear
Если вы снова прочитаете файл истории , вы продублируете большую часть истории (в памяти, а не в файле):
$ history -r; fc -l -1
38319 fc -l -1
Это причина иметь и использовать history -c
.
What then happens is that a new pane in tmux has completely empty history. The workaround for that must be very simple: just execute
history -r
И вся история в файле истории снова появится в памяти.