Skip to content

Instantly share code, notes, and snippets.

@LinusCDE
Created January 6, 2022 17:08
Show Gist options
  • Save LinusCDE/a02cd3f0cec1b997eabf851ec0d59537 to your computer and use it in GitHub Desktop.
Save LinusCDE/a02cd3f0cec1b997eabf851ec0d59537 to your computer and use it in GitHub Desktop.
EvMacros

EvMacros

Basic but really powerful recording and playback of raw ev events. This is useful for short-term recording and playback and especially helpful if your devices are not conventional you want support for relative mouse input which is especially needed in games.

Recording

Adjust the devices to be recorded in macrec.py at the end of the file. Use evtest to see what devices have which number (/dev/input/event<X>).

Then you can just run the script: ./maccrec.py > recording. The recording will automaticially stop if you press the ESC key (can be adjusted in the code as well).

Playback

Feed the contents of maccrec.py into macplay.py: cat recording | ./macplay.py. Please note that depending on what you recorded, it may be hard to stop the playback. Best to run this from a terminal opened somewhere else (e.g. your smartphone).

If you rebooted your machine or replugged a bunch of input-devices, the event id may also not be correct. So not really suited for really long term recording.

#!/usr/bin/env python3
import evdev
from time import sleep, time
def readcommands():
'''Parse all input from stdin (until EOF / CTRL+D) and sort by time.'''
commands = []
while True:
try:
line = input()
except EOFError:
# Sort command by time (multiple recorded devices might not be exactly be in proper order)
commands.sort(key=lambda cmd: cmd[2])
return commands
command = line.split()[0]
args = line.split()[1:]
if command == "Event":
commands.append(("Event", int(args[0]), float(args[1]), int(args[2]), int(args[3]), int(args[4]) ))
commands = readcommands()
#print(commands)
# Find all evdev devices used in the recording and replicate them as new/cloned uinput devices
# (Maybe change to just inject into the orignal devices instead.)
devs = {}
for cmd in filter(lambda cmd: cmd[0] == "Event", commands):
if cmd[1] not in devs:
devs[cmd[1]] = evdev.UInput.from_device("/dev/input/event%d" % cmd[1])
#print(devs)
#print('Running...')
startedAt = time()
for cmd in commands:
command = cmd[0]
if command == "Event":
# Wait until next event is supposed to run
now = time() - startedAt
while now < cmd[2]:
sleep(max(0, cmd[2] - now))
now = time() - startedAt
# Send input
devs[cmd[1]].write(cmd[3], cmd[4], cmd[5])
for dev in devs.values():
dev.close()
#!/usr/bin/env python3
from threading import Thread, Lock
import evdev
from time import time
startedAt = time()
lock = Lock()
def rec(evNum):
global lastEventAt
device = evdev.InputDevice('/dev/input/event%d' % evNum)
for event in device.read_loop():
if event.type == 1 and event.code == 1 and event.value == 1:
# Esc pressed
exit(0)
with lock:
print("Event %d %f %d %d %d" % (evNum, event.timestamp() - startedAt, event.type, event.code, event.value))
# Change these ids to what evdev ids you want recorded!!!
Thread(target=rec, args=(8,), daemon=True).start()
rec(10) # Last id can just be on the current thread
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment