|
5 | 5 | import rp2pio |
6 | 6 | import usb_hid |
7 | 7 | from adafruit_hid.consumer_control import ConsumerControl |
8 | | -from adafruit_hid.consumer_control_code import ConsumerControlCode |
9 | 8 | from adafruit_hid.keyboard import Keyboard |
10 | 9 | from adafruit_pioasm import Program |
11 | 10 | from adafruit_ticks import ticks_add, ticks_less, ticks_ms |
12 | 11 |
|
13 | | -from next_keycode import cc_value, is_cc, next_modifiers, next_scancodes |
| 12 | +from next_keycode import ( |
| 13 | + cc_value, |
| 14 | + is_cc, |
| 15 | + next_modifiers, |
| 16 | + next_scancodes, |
| 17 | + shifted_codes, |
| 18 | + shift_modifiers, |
| 19 | +) |
14 | 20 |
|
15 | 21 | NEXT_SERIAL_BUS_FREQUENCY = ( |
16 | 22 | 18958 # 455kHz/24 https://journal.spencerwnelson.com/entries/nextkb.html |
|
19 | 25 | pio_program = Program( |
20 | 26 | """ |
21 | 27 | top: |
| 28 | + set pins, 1 |
22 | 29 | pull block ; wait for send request |
23 | 30 | out x, 1 ; trigger receive? |
24 | 31 | out y, 7 ; get count of bits to transmit (minus 1) |
25 | 32 |
|
26 | | - set pins, 1 |
27 | | -bitloop: |
| 33 | +bitloop: |
28 | 34 | out pins, 1 [7] ; send next bit |
29 | 35 | jmp y--, bitloop [7] ; loop if bits left to send |
30 | 36 |
|
31 | 37 | set pins, 1 ; idle the bus after last bit |
32 | 38 | jmp !x, top ; to top if no scancode expected |
33 | 39 |
|
| 40 | + set pins, 1 ; mark bus as idle so keyboard will send |
34 | 41 | set y, 19 ; 20 bits to receive |
35 | 42 |
|
36 | 43 | wait 0, pin 0 [7] ; wait for falling edge plus half bit time |
|
42 | 49 | """ |
43 | 50 | ) |
44 | 51 |
|
| 52 | + |
45 | 53 | def pack_message(bitcount, data, trigger_receive=False): |
46 | 54 | if bitcount > 24: |
47 | 55 | raise ValueError("too many bits in message") |
@@ -76,7 +84,7 @@ def is_make(report): |
76 | 84 |
|
77 | 85 |
|
78 | 86 | def is_mod_report(report): |
79 | | - return not (report & 1) |
| 87 | + return not report & 1 |
80 | 88 |
|
81 | 89 |
|
82 | 90 | # keycode bits are backwards compared to other information sources |
@@ -107,64 +115,85 @@ def modifiers(report): |
107 | 115 | **pio_program.pio_kwargs, |
108 | 116 | ) |
109 | 117 |
|
| 118 | + |
110 | 119 | class KeyboardHandler: |
111 | 120 | def __init__(self): |
112 | 121 | self.old_modifiers = 0 |
113 | 122 | self.cc = ConsumerControl(usb_hid.devices) |
114 | 123 | self.kbd = Keyboard(usb_hid.devices) |
115 | 124 |
|
116 | 125 | def set_key_state(self, key, state): |
117 | | - print("set_key_state", key, state) |
118 | 126 | if state: |
119 | | - self.kbd.press(key) |
| 127 | + if isinstance(key, tuple): |
| 128 | + old_report_modifier = self.kbd.report_modifier[0] |
| 129 | + self.kbd.report_modifier[0] = 0 |
| 130 | + self.kbd.press(*key) |
| 131 | + self.kbd.release_all() |
| 132 | + self.kbd.report_modifier[0] = old_report_modifier |
| 133 | + else: |
| 134 | + self.kbd.press(key) |
120 | 135 | else: |
121 | | - self.kbd.release(key) |
| 136 | + if isinstance(key, tuple): |
| 137 | + pass |
| 138 | + else: |
| 139 | + self.kbd.release(key) |
122 | 140 |
|
123 | | - def handle_report(self, value): |
124 | | - if value == 1536: |
| 141 | + def handle_report(self, report_value): |
| 142 | + if report_value == 1536: # the "nothing happened" report |
125 | 143 | return |
126 | 144 |
|
127 | | - if is_mod_report(value): |
128 | | - mods = modifiers(value) |
129 | | - changes = self.old_modifiers ^ mods |
130 | | - self.old_modifiers = mods |
131 | | - for i in range(7): |
132 | | - bit = 1 << i |
133 | | - if changes & bit: # Modifier key pressed or released |
134 | | - self.set_key_state(next_modifiers[i], mods & bit) |
135 | | - else: |
136 | | - code = next_scancodes.get(keycode(value)) |
137 | | - make = is_make(value) |
138 | | - if code: |
139 | | - if is_cc(code): |
140 | | - if make: |
141 | | - self.cc.send(cc_value(code)) |
142 | | - else: |
143 | | - self.set_key_state(code, make) |
| 145 | + # Handle modifier changes |
| 146 | + mods = modifiers(report_value) |
| 147 | + changes = self.old_modifiers ^ mods |
| 148 | + self.old_modifiers = mods |
| 149 | + for i in range(7): |
| 150 | + bit = 1 << i |
| 151 | + if changes & bit: # Modifier key pressed or released |
| 152 | + self.set_key_state(next_modifiers[i], mods & bit) |
| 153 | + |
| 154 | + # Handle key press/release |
| 155 | + code = next_scancodes.get(keycode(report_value)) |
| 156 | + if mods & shift_modifiers: |
| 157 | + code = shifted_codes.get(keycode(report_value), code) |
| 158 | + make = is_make(report_value) |
| 159 | + if code: |
| 160 | + if is_cc(code): |
| 161 | + if make: |
| 162 | + self.cc.send(cc_value(code)) |
| 163 | + else: |
| 164 | + self.set_key_state(code, make) |
144 | 165 |
|
145 | 166 |
|
146 | 167 | handler = KeyboardHandler() |
147 | 168 |
|
148 | 169 | recv_buf = array.array("I", [0]) |
149 | 170 |
|
150 | | -sm.write(RESET) |
151 | 171 | time.sleep(0.1) |
152 | | -sm.write(set_leds(0)) |
| 172 | +sm.write(RESET) |
153 | 173 | time.sleep(0.1) |
154 | 174 |
|
| 175 | +for _ in range(4): |
| 176 | + sm.write(set_leds(3)) |
| 177 | + time.sleep(0.1) |
| 178 | + sm.write(set_leds(0)) |
| 179 | + time.sleep(0.1) |
| 180 | + |
155 | 181 | print("Keyboard ready!") |
156 | 182 |
|
157 | | -while True: |
158 | | - sm.write(QUERY) |
159 | | - deadline = ticks_add(ticks_ms(), 100) |
160 | | - while ticks_less(ticks_ms(), deadline): |
161 | | - if sm.in_waiting: |
162 | | - sm.readinto(recv_buf) |
163 | | - value = recv_buf[0] |
164 | | - handler.handle_report(value) |
165 | | - break |
166 | | - else: |
167 | | - print("keyboard did not respond - resetting") |
168 | | - sm.restart() |
169 | | - sm.write(RESET) |
170 | | - time.sleep(0.1) |
| 183 | +try: |
| 184 | + while True: |
| 185 | + sm.write(QUERY) |
| 186 | + deadline = ticks_add(ticks_ms(), 100) |
| 187 | + while ticks_less(ticks_ms(), deadline): |
| 188 | + if sm.in_waiting: |
| 189 | + sm.readinto(recv_buf) |
| 190 | + value = recv_buf[0] |
| 191 | + handler.handle_report(value) |
| 192 | + break |
| 193 | + else: |
| 194 | + print("keyboard did not respond - resetting") |
| 195 | + sm.restart() |
| 196 | + sm.write(RESET) |
| 197 | + time.sleep(0.1) |
| 198 | +finally: # Release all keys before e.g., code is reloaded |
| 199 | + handler.kbd.release_all() |
0 commit comments