-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontroller.py
executable file
·120 lines (101 loc) · 4.08 KB
/
controller.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!/usr/bin/env python3
"""Virtual Gamecube controller"""
__author__="Tyler Westland"
import asyncio
from dataclasses import dataclass, replace
import evdev
from typing import List, Optional
@dataclass(frozen=True)
class Action:
etype: int
code: int
set_value: int
reset_value: int
seconds: float
# 0 is base, 127 is middle, and 255 max for absolutes axises
ACTIONS = {
"a": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_SOUTH, 1, 0, 0.25),
"b": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_EAST, 1, 0, 0.25),
"x": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_NORTH, 1, 0, 0.25),
"y": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_WEST, 1, 0, 0.25),
"z": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_TR, 1, 0, 0.25),
"l": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_TL2, 1, 0, 0.25),
"r": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_TR2, 1, 0, 0.25),
"start": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_START, 1, 0, 0.25),
"up": Action(evdev.events.EV_ABS, evdev.ecodes.ABS_Y, 0, 127, 0.25),
"down": Action(evdev.events.EV_ABS, evdev.ecodes.ABS_Y, 255, 127, 0.25),
"left": Action(evdev.events.EV_ABS, evdev.ecodes.ABS_X, 0, 127, 0.25),
"right": Action(evdev.events.EV_ABS, evdev.ecodes.ABS_X, 255, 127, 0.25),
"cup": Action(evdev.events.EV_ABS, evdev.ecodes.ABS_RY, 0, 127, 0.25),
"cdown": Action(evdev.events.EV_ABS, evdev.ecodes.ABS_RY, 255, 127, 0.25),
"cleft": Action(evdev.events.EV_ABS, evdev.ecodes.ABS_RX, 0, 127, 0.25),
"cright": Action(evdev.events.EV_ABS, evdev.ecodes.ABS_RX, 255, 127, 0.25),
"dup": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_DPAD_UP, 1, 0, 0.25),
"ddown": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_DPAD_DOWN, 1, 0, 0.25),
"dleft": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_DPAD_LEFT, 1, 0, 0.25),
"dright": Action(evdev.events.EV_KEY, evdev.ecodes.BTN_DPAD_RIGHT, 1, 0, 0.25),
}
# Create variations of the actions with shorter or longer time
adjectives = {
"nano_": 1/16,
"n": 1/16,
"tiny_": 1/8,
"t": 1/8,
"short_": 1/8,
"s": 1/4,
"long_": 1.0,
"l": 1.0,
"double_": 2.0,
"d": 2.0
}
variations = dict()
for adjective in adjectives:
for action in ACTIONS:
variations[f"{adjective}{action}"] = replace(
ACTIONS[action], seconds=adjectives[adjective])
for variation in variations:
ACTIONS[variation] = variations[variation]
def expand_adjectives(buttons: List[str]):
def inner():
for button in buttons:
yield button
for adjective in adjectives:
yield f"{adjective}{button}"
return list(inner())
SUGGESTED_BANNED_INPUTS = expand_adjectives(["start", "z"])
class GameCubeController():
def __init__(self, clone_parent:str, controller_name:str=None):
if controller_name is None:
controller_name = "python--GameCube"
self.ui = evdev.uinput.UInput.from_device(
clone_parent,
name=controller_name)
def close(self):
self.ui.close()
def is_open(self):
return evdev.util.is_device(f"/dev/input/{self.ui.name}")
async def perform_actions(self, actions:List[Action]) -> None:
"""Push button for specified time
Parameters
----------
button: List[int] (evdev.ecodes)
Buttons to push down, and then lift up
seconds: float = 0.25
Seconds to hold the button down for
"""
# FIND MAX SECONDS
seconds = max(act.seconds for act in actions)
# Activate the actions
for act in actions:
self.ui.write(act.etype, act.code, act.set_value)
# Synchronize the activations
self.ui.syn()
# Wait
await asyncio.sleep(seconds)
# Reset the actions
for act in actions:
self.ui.write(act.etype, act.code, act.reset_value)
# Synchronize the lifts
self.ui.syn()
# Synchronize the resets
self.ui.syn()