|
|
|
#!/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<n>'
|
|
|
|
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<n> /dev/input/event<m>")
|
|
|
|
sys.exit(1)
|
|
|
|
main(sys.argv)
|