-
Notifications
You must be signed in to change notification settings - Fork 0
/
keyboardext.py
244 lines (192 loc) · 7.91 KB
/
keyboardext.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# SPDX-FileCopyrightText: 2017 Dan Halbert for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_hid.keyboard.Keyboard`
====================================================
* Author(s): Scott Shawcroft, Dan Halbert
"""
import time
from micropython import const
from adafruit_hid.keycode import Keycode
from adafruit_hid import find_device
_MAX_KEYPRESSES = const(6)
class Keyboardext:
"""Send HID keyboard reports."""
LED_NUM_LOCK = 0x01
"""LED Usage ID for Num Lock"""
LED_CAPS_LOCK = 0x02
"""LED Usage ID for Caps Lock"""
LED_SCROLL_LOCK = 0x04
"""LED Usage ID for Scroll Lock"""
LED_COMPOSE = 0x08
"""LED Usage ID for Compose"""
# No more than _MAX_KEYPRESSES regular keys may be pressed at once.
def __init__(self, devices):
"""Create a Keyboard object that will send keyboard HID reports.
Devices can be a list of devices that includes a keyboard device or a keyboard device
itself. A device is any object that implements ``send_report()``, ``usage_page`` and
``usage``.
"""
self._keyboard_device = find_device(devices, usage_page=0x1, usage=0x06)
# Reuse this bytearray to send keyboard reports.
self.report = bytearray(8)
# report[0] modifiers
# report[1] unused
# report[2:8] regular key presses
# View onto byte 0 in report.
self.report_modifier = memoryview(self.report)[0:1]
# List of regular keys currently pressed.
# View onto bytes 2-7 in report.
self.report_keys = memoryview(self.report)[2:]
# Do a no-op to test if HID device is ready.
# If not, wait a bit and try once more.
try:
self.release_all()
except OSError:
time.sleep(1)
self.release_all()
self._layout = ""
# Set default layout
# self._layout = "en_us"
# self._asciimap = __import__("keyboardext/" + self._layout)
def set_layout(self, layout):
"""Sets the layout of the keyboard object. The string must match
the name of the mapping file.
"""
self._asciimap = __import__("keyboardext/" + layout)
self._layout = layout
def get_layout(self):
"""Returns the selected layout as string
"""
return self._layout
def write(self, string):
"""Type the string by pressing and releasing keys on my keyboard.
:param string: A string of ASCII characters.
Example::
# Write abc to the keyboard
keyboard.write('abc')
"""
if self._layout == "":
print("Error: No layout selected.")
return
if isinstance(self._asciimap.asciiToKeycode, list):
isList = True
elif isinstance(self._asciimap.asciiToKeycode, dict):
isList = False
for char in string:
if isList:
ascii_num = ord(str(char, "ascii"))
if (ascii_num > 255): continue # in case the character is not part of latin-1 charset
keycodes = self._asciimap.asciiToKeycode[ascii_num]
else:
keycodes = self._asciimap.asciiToKeycode[char]
if isinstance(keycodes, list): # list of tuples = DEADKEYS
for item in keycodes:
if isinstance(item, tuple):
self.send(*item)
else:
self.send(item)
elif isinstance(keycodes, tuple):
self.send(*keycodes) # tuples needs to be unpacked by *
else:
self.send(keycodes) # send a single keycode as int
def writeln(self, string):
"""Type the string by pressing and releasing keys on my keyboard.
:param string: A string of ASCII characters.
Example::
# Write abc followed by RETURN to the keyboard
keyboard.writeln('abc')
"""
self.write(string)
self.send(Keycode.ENTER)
def press(self, *keycodes):
"""Send a report indicating that the given keys have been pressed.
:param keycodes: Press these keycodes all at once.
:raises ValueError: if more than six regular keys are pressed.
Keycodes may be modifiers or regular keys.
No more than six regular keys may be pressed simultaneously.
Examples::
from adafruit_hid.keycode import Keycode
# Press ctrl-x.
kbd.press(Keycode.LEFT_CONTROL, Keycode.X)
# Or, more conveniently, use the CONTROL alias for LEFT_CONTROL:
kbd.press(Keycode.CONTROL, Keycode.X)
# Press a, b, c keys all at once.
kbd.press(Keycode.A, Keycode.B, Keycode.C)
"""
for keycode in keycodes:
self._add_keycode_to_report(keycode)
self._keyboard_device.send_report(self.report)
def release(self, *keycodes):
"""Send a USB HID report indicating that the given keys have been released.
:param keycodes: Release these keycodes all at once.
If a keycode to be released was not pressed, it is ignored.
Example::
# release SHIFT key
kbd.release(Keycode.SHIFT)
"""
for keycode in keycodes:
self._remove_keycode_from_report(keycode)
self._keyboard_device.send_report(self.report)
def release_all(self):
"""Release all pressed keys."""
for i in range(8):
self.report[i] = 0
self._keyboard_device.send_report(self.report)
def send(self, *keycodes):
"""Press the given keycodes and then release all pressed keys.
:param keycodes: keycodes to send together
"""
self.press(*keycodes)
self.release_all()
def _add_keycode_to_report(self, keycode):
"""Add a single keycode to the USB HID report."""
modifier = Keycode.modifier_bit(keycode)
if modifier:
# Set bit for this modifier.
self.report_modifier[0] |= modifier
else:
# Don't press twice.
# (I'd like to use 'not in self.report_keys' here, but that's not implemented.)
for i in range(_MAX_KEYPRESSES):
if self.report_keys[i] == keycode:
# Already pressed.
return
# Put keycode in first empty slot.
for i in range(_MAX_KEYPRESSES):
if self.report_keys[i] == 0:
self.report_keys[i] = keycode
return
# All slots are filled.
raise ValueError("Trying to press more than six keys at once.")
def _remove_keycode_from_report(self, keycode):
"""Remove a single keycode from the report."""
modifier = Keycode.modifier_bit(keycode)
if modifier:
# Turn off the bit for this modifier.
self.report_modifier[0] &= ~modifier
else:
# Check all the slots, just in case there's a duplicate. (There should not be.)
for i in range(_MAX_KEYPRESSES):
if self.report_keys[i] == keycode:
self.report_keys[i] = 0
@property
def led_status(self):
"""Returns the last received report"""
return self._keyboard_device.last_received_report
def led_on(self, led_code):
"""Returns whether an LED is on based on the led code
Examples::
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
import time
# Press and release CapsLock.
kbd.press(Keycode.CAPS_LOCK)
time.sleep(.09)
kbd.release(Keycode.CAPS_LOCK)
# Check status of the LED_CAPS_LOCK
print(kbd.led_on(Keyboard.LED_CAPS_LOCK))
"""
return bool(self.led_status[0] & led_code)