-
Notifications
You must be signed in to change notification settings - Fork 0
/
hid_gamepad.py
94 lines (74 loc) · 2.72 KB
/
hid_gamepad.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
# SPDX-FileCopyrightText: 2018 Dan Halbert for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`Gamepad`
====================================================
* Author(s): Dan Halbert
"""
import struct
import time
from adafruit_hid import find_device
class Gamepad:
def __init__(self, devices):
self._gamepad_device = find_device(devices, usage_page=0x1, usage=0x05)
# Typically controllers start numbering buttons at 1 rather than 0.
# report[0] buttons 1-8
# report[1] buttons 9-16
# report[2] buttons 17-24
# report[3] buttons 25-26
self._report = bytearray(8)
# Remember the last report as well, so we can avoid sending
# duplicate reports.
self._last_report = bytearray(8)
# Store settings separately before putting into report. Saves code
# especially for buttons.
self._buttons_state = 0
# Send an initial report to test if HID device is ready.
# If not, wait a bit and try once more.
try:
self.reset_all()
except OSError:
time.sleep(1)
self.reset_all()
def press_buttons(self, *buttons):
"""Press and hold the given buttons."""
for button in buttons:
self._buttons_state |= 1 << self._validate_button_number(button) - 1
self._send()
def release_buttons(self, *buttons):
"""Release the given buttons."""
for button in buttons:
self._buttons_state &= ~(1 << self._validate_button_number(button) - 1)
self._send()
def release_all_buttons(self):
"""Release all the buttons."""
self._buttons_state = 0
self._send()
def click_buttons(self, *buttons):
"""Press and release the given buttons."""
self.press_buttons(*buttons)
self.release_buttons(*buttons)
def reset_all(self):
"""Release all buttons and set joysticks to zero."""
self._buttons_state = 0
self._send(always=True)
def _send(self, always=False):
"""Send a report with all the existing settings.
If ``always`` is ``False`` (the default), send only if there have been changes.
"""
struct.pack_into(
"<Lbbbb",
self._report,
0,
self._buttons_state,
)
if always or self._last_report != self._report:
self._gamepad_device.send_report(self._report)
# Remember what we sent, without allocating new storage.
self._last_report[:] = self._report
@staticmethod
def _validate_button_number(button):
if not 1 <= button <= 21:
raise ValueError("Button number must in range 1 to 21")
return button