#!/usr/bin/python # # This file is part of the batocera distribution (https://batocera.org). # Copyright (c) 2022 Nicolas Adenis-Lamarre. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 3. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # YOU MUST KEEP THIS HEADER AS IT IS # # import evdev from evdev import ecodes import select import sys import time import binascii if len(sys.argv) != 3: print("mising device argument") exit(1) bCalibration = ecodes.BTN_MIDDLE # input devpath = sys.argv[1] hidpath = sys.argv[2] input = evdev.InputDevice(devpath) hidin = open(hidpath, "wb") absVals = [[0, 100], [0, 100]] # input rectangle for (code, inf) in input.capabilities()[ecodes.EV_ABS]: if code == ecodes.ABS_X: absVals[0] = [inf.min, inf.max] if code == ecodes.ABS_Y: absVals[1] = [inf.min, inf.max] poll = select.poll() poll.register(input.fd, select.POLLIN) evkeys = [ecodes.KEY_CONFIG] for (code) in input.capabilities()[ecodes.EV_KEY]: evkeys.append(code) target = evdev.UInput(name="Retro Shooter Lightgun", events={ ecodes.EV_ABS: [ (ecodes.ABS_X, evdev.AbsInfo(value=0, min=absVals[0][0], max=absVals[0][1], fuzz=0, flat=0, resolution=0)), (ecodes.ABS_Y, evdev.AbsInfo(value=0, min=absVals[1][0], max=absVals[1][1], fuzz=0, flat=0, resolution=0)) ], ecodes.EV_KEY: evkeys}) calibration_timeChecker = None calibration_mode = False calibration_step = 0 def moveTargetTo(fromPosition, toPosition): currentPosition = fromPosition while fromPosition[0] != toPosition[0] or fromPosition[1] != toPosition[1] : if currentPosition[0] < toPosition[0]: currentPosition[0] = currentPosition[0] +1 if currentPosition[0] > toPosition[0]: currentPosition[0] = currentPosition[0] -1 if currentPosition[1] < toPosition[1]: currentPosition[1] = currentPosition[1] +1 if currentPosition[1] > toPosition[1]: currentPosition[1] = currentPosition[1] -1 target.write(ecodes.EV_ABS, ecodes.ABS_X, currentPosition[0]) target.write(ecodes.EV_ABS, ecodes.ABS_Y, currentPosition[1]) target.syn() time.sleep(0.001) # x1000 = 1 second try: while True: if poll.poll(1000): if calibration_mode: ### # 1. to center : '\xaa\x66' # 2. center point to top left : '\xaa\xb1' # 3. top left to right bottom : '\xaa\xb2' # 4. right bottom to right top : '\xaa\xb3' # 5. right top : '\xaa\xb4' # . commit : '\xaa\xb5' # . rollback : '\xaa\xb6' for event in input.read(): if event.type == ecodes.EV_KEY and event.code == ecodes.BTN_LEFT and event.value == 1: if calibration_step == 1: hidin.write(binascii.unhexlify('AAB1')) hidin.flush() frompos = [ absVals[0][0] + round((absVals[0][1] - absVals[0][0]) * 0.5), absVals[1][0] + round((absVals[1][1] - absVals[1][0]) * 0.5) ] topos = [ absVals[0][0] + round((absVals[0][1] - absVals[0][0]) * 0.1), absVals[1][0] + round((absVals[1][1] - absVals[1][0]) * 0.1) ] moveTargetTo(frompos, topos) calibration_step = 2 elif calibration_step == 2: hidin.write(binascii.unhexlify('AAB2')) hidin.flush() frompos = [ absVals[0][0] + round((absVals[0][1] - absVals[0][0]) * 0.1), absVals[1][0] + round((absVals[1][1] - absVals[1][0]) * 0.1) ] topos = [ absVals[0][0] + round((absVals[0][1] - absVals[0][0]) * 0.9), absVals[1][0] + round((absVals[1][1] - absVals[1][0]) * 0.9) ] moveTargetTo(frompos, topos) calibration_step = 3 elif calibration_step == 3: hidin.write(binascii.unhexlify('AAB3')) hidin.flush() frompos = [ absVals[0][0] + round((absVals[0][1] - absVals[0][0]) * 0.9), absVals[1][0] + round((absVals[1][1] - absVals[1][0]) * 0.9) ] topos = [ absVals[0][0] + round((absVals[0][1] - absVals[0][0]) * 0.9), absVals[1][0] + round((absVals[1][1] - absVals[1][0]) * 0.1) ] moveTargetTo(frompos, topos) calibration_step = 4 elif calibration_step == 4: hidin.write(binascii.unhexlify('AAB4')) hidin.flush() hidin.write(binascii.unhexlify('AAB5')) hidin.flush() calibration_mode= False target.write(ecodes.EV_KEY, ecodes.KEY_CONFIG, 0) target.syn() else: if calibration_timeChecker is not None: if time.time() - calibration_timeChecker > 4: # 4 seconds calibration_mode = True calibration_timeChecker = None calibration_step = 1 target.write(ecodes.EV_KEY, ecodes.KEY_CONFIG, 1) target.syn() frompos = [ absVals[0][0] + round((absVals[0][1] - absVals[0][0]) * 0.5), absVals[1][0] + round((absVals[1][1] - absVals[1][0]) * 0.0) ] topos = [ absVals[0][0] + round((absVals[0][1] - absVals[0][0]) * 0.5), absVals[1][0] + round((absVals[1][1] - absVals[1][0]) * 0.5) ] moveTargetTo(frompos, topos) hidin.write(binascii.unhexlify('AA66')) hidin.flush() # could be enabled by the timeChecker if calibration_mode is False: # normal event read for event in input.read(): if event.type == ecodes.EV_SYN: target.syn() elif event.type == ecodes.EV_KEY and event.code == bCalibration: if event.value == 1: calibration_timeChecker = time.time() target.write(event.type, event.code, event.value) elif event.value == 0: calibration_timeChecker = None target.write(event.type, event.code, event.value) else: target.write(event.type, event.code, event.value) except KeyboardInterrupt: poll.unregister(input.fd) target.close() hidin.close() except Exception as e: import traceback with open('/var/run/gun-retroshooter.crash', 'w') as fd: fd.write(traceback.format_exc()) raise e