#!/usr/bin/env python3 # # evalgamepad.py # Reading two USB gamepads & making sense of it (try pong)... # #----------------------------------------------------------------------- # 'lsusb' ouput: Bus 002 Device 003: ID 0079:0011 DragonRise Inc. Gamepad # Bus 002 Device 002: ID 0079:0011 DragonRise Inc. Gamepad # # input devices: /dev/input/event6 # /dev/input/event7 # import sys, os, fcntl, time import libevdev # Buttons in 'state' BTN_TRIGGER = 1 # [Blue/X] BTN_THUMB = 2 # [Red/A] BTN_THUMB2 = 4 # [Yellow/B] BTN_TOP = 8 # [Green/Y] BTN_TOP2 = 16 # [Frontal left] BTN_PINKIE = 32 # [Frontal right] BTN_BASE3 = 64 # [Centre left] BTN_BASE4 = 128 # [Centre right] GAMEAREA_MIN_X = 0 # Game area definition GAMEAREA_MAX_X = 1920 # Assume FHD resolution GAMEAREA_MIN_Y = 0 GAMEAREA_MAX_Y = 1080 def init_player(path): # Gamepad init. fd = open(path, "rb") fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) dev = libevdev.Device(fd) return { "fd" : fd, # File descriptor to close (later ...) "dev" : dev, # Actual gamepad of this player "state" : 0, # Button mask (as above) "delta_x" : 0, # Current delta setting "delta_y" : 0, "x" : GAMEAREA_MAX_X/2, # Current actual position "y" : GAMEAREA_MAX_Y/2, # Start at center "w" : 10, "h" : 100 } def exit_player(player): player["fd"].close() def init_object(x, y, w, h, delta_x, delta_y): return { "delta_x" : delta_x, # Current movement "delta_y" : delta_y, "x" : x, # Current position "y" : y, "w" : w, # Size of object "h" : h, } 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 eval_gamepad(player): bChanged = False try: for e in player["dev"].events(): #print_event(e) # Event type 1 (EV_KEY) # Event code 288 (BTN_TRIGGER) <- 1=[Blue/X] # Event code 289 (BTN_THUMB) <- 1=[Red/A] # Event code 290 (BTN_THUMB2) <- 1=[Yellow/B] # Event code 291 (BTN_TOP) <- 1=[Green/Y] # Event code 292 (BTN_TOP2) <- 1=[Frontal left] # Event code 293 (BTN_PINKIE) <- 1=[Frontal right] # Event code 294 (BTN_BASE) # Event code 295 (BTN_BASE2) # Event code 296 (BTN_BASE3) <- 1=[Centre left] # Event code 297 (BTN_BASE4) <- 1=[Centre right] if e.type.value == 1: # Non-Cross events if e.value == 1: # Simple buttons may act as toggles if e.code.value == 288: # BTN_TRIGGER player["state"] = player["state"] ^ BTN_TRIGGER elif e.code.value == 289: # BTN_THUMB player["state"] = player["state"] ^ BTN_THUMB elif e.code.value == 290: # BTN_THUMB2 player["state"] = player["state"] ^ BTN_THUMB2 elif e.code.value == 291: # BTN_TOP player["state"] = player["state"] ^ BTN_TOP elif e.code.value == 292: # BTN_TOP2 player["state"] = player["state"] ^ BTN_TOP2 elif e.code.value == 293: # BTN_PINKIE player["state"] = player["state"] ^ BTN_PINKIE elif e.code.value == 296: # BTN_BASE3 player["state"] = player["state"] ^ BTN_BASE3 elif e.code.value == 297: # BTN_BASE4 player["state"] = player["state"] ^ BTN_BASE4 bChanged = True # Event type 3 (EV_ABS) # Event code 0 (ABS_X) <- Cross: 127=Neutral, 0=[Left], 255=[Right] # Event code 1 (ABS_Y) <- Cross: 127=Neutral, 0=[Top], 255=[Down] elif e.type.value == 3: # EV_ABS if e.code.value == 0: # ABS_X if e.value == 0: player["delta_x"] = player["delta_x"] - 1 elif e.value == 255: player["delta_x"] = player["delta_x"] + 1 else: # 127/Neutral player["delta_x"] = 0 else: # ABS_Y if e.value == 0: player["delta_y"] = player["delta_y"] + 1 elif e.value == 255: player["delta_y"] = player["delta_y"] - 1 else: # 127/Neutral player["delta_y"] = 0 bChanged = True #if bChanged: # print("State=",player["state"], "X=", player["delta_x"], "Y=", player["delta_y"]) except libevdev.EventsDroppedException: print("Dropped!") for e in player["dev"].sync(): print_event(e) return bChanged def eval_position(player): bChanged = False sum_x = player["x"] + player["delta_x"]/255 sum_y = player["y"] + player["delta_y"]/255 if sum_x < GAMEAREA_MIN_X: sum_x = GAMEAREA_MIN_X elif sum_x + player["w"] > GAMEAREA_MAX_X: sum_x = GAMEAREA_MAX_X - player["w"] if sum_y < GAMEAREA_MIN_Y: sum_y = GAMEAREA_MIN_Y elif sum_y + player["h"] > GAMEAREA_MAX_Y: sum_y = GAMEAREA_MAX_Y - player["h"] if (sum_x != player["x"]) or (sum_y != player["y"]): bChanged = True player["x"] = sum_x player["y"] = sum_y return bChanged def eval_object(obj): sum_x = obj["x"] + obj["delta_x"] sum_y = obj["y"] + obj["delta_y"] if sum_x < GAMEAREA_MIN_X: sum_x = GAMEAREA_MIN_X obj["delta_x"] = -obj["delta_x"] elif sum_x + obj["w"] > GAMEAREA_MAX_X: sum_x = GAMEAREA_MAX_X - obj["w"] obj["delta_x"] = -obj["delta_x"] if sum_y < GAMEAREA_MIN_Y: sum_y = GAMEAREA_MIN_Y obj["delta_y"] = -obj["delta_y"] elif sum_y + obj["h"] > GAMEAREA_MAX_Y: sum_y = GAMEAREA_MAX_Y - obj["h"] obj["delta_y"] = -obj["delta_y"] obj["x"] = sum_x obj["y"] = sum_y def draw_objects(ball, player1, player2): # Update objects on playground print("\rB: ", ball["x"], "/", ball["y"], end="") print(" P1: ", player1["x"], "/", player1["y"], " S:", hex(player1["state"]), " ", end="") print(" P2: ", player2["x"], "/", player2["y"], " S:", hex(player2["state"]), " ", end="") def gameloop(ball, player1, player2): while True: # 1. Retrieve user entries bChanged = eval_gamepad(player1) if bChanged: if (player1["state"] & BTN_BASE4) > 0: # Exit? print("Player #1 aborted game") break else: print("Player #1: {}h".format(hex(player1["state"]))) bChanged = eval_gamepad(player2) # Exit? if bChanged: if (player2["state"] & BTN_BASE4) > 0: print("Player #2 aborted game") break else: print("Player #2: {}h".format(hex(player2["state"]))) # 2. Adjust player positions bChanged = eval_position(player1) if bChanged: print("Player #1: ", player1["x"], " / ", player1["y"]) bChanged = eval_position(player2) if bChanged: print("Player #2: ", player2["x"], " / ", player2["y"]) # 3. Adjust object positions eval_object(ball) # 4. Refresh game area drawing draw_objects(ball, player1, player2) # 5. Little delay for testing time.sleep(0.1) def init_gamearea(): # Create base playground pass def main(args): try: # Initialize init_gamearea() ball = init_object(10, 10, 15, 15, 2.0, 1.5) player1 = init_player(args[1]) player2 = init_player(args[2]) # Work ... gameloop(ball, player1, player2) # Quit gracefully exit_player(player2) exit_player(player1) except KeyboardInterrupt: # Never comes ... (?!) pass except IOError as e: import errno if e.errno == errno.EACCES: print("Insufficient permissions to access {} or {}".format(path1, path2)) elif e.errno == errno.ENOENT: print("Device {} or {} does not exist".format(path1, path2)) else: raise e if __name__ == "__main__": if len(sys.argv) < 3: print("usage: ./evalgamepad.py /dev/input/event /dev/input/event") sys.exit(1) main(sys.argv)