10
10
from adafruit_hid .consumer_control import ConsumerControl
11
11
from adafruit_hid .keyboard import Keyboard
12
12
from adafruit_hid .keyboard import Keycode
13
+ from adafruit_hid .mouse import Mouse
13
14
from adafruit_pioasm import Program
14
15
from adafruit_ticks import ticks_add , ticks_less , ticks_ms
15
16
from next_keycode import (
21
22
shift_modifiers ,
22
23
)
23
24
25
+
26
+ # Compared to a modern mouse, the DPI of the NeXT mouse is low. Increasing this
27
+ # number makes the pointer move further faster, but it also makes moves chunky.
28
+ # Customize this number according to the trade-off you want, but also check
29
+ # whether your operating system can assign a higher "sensitivity" or
30
+ # "acceleration" for the mouse.
31
+ MOUSE_SCALE = 8
32
+
24
33
# Customize the power key's keycode. You can change it to `Keycode.POWER` if
25
34
# you really want to accidentally power off your computer!
26
35
POWER_KEY_SENDS = Keycode .F1
@@ -79,7 +88,8 @@ def set_leds(i):
79
88
return pack_message_str (f"0000000001110{ i :02b} 0000000" )
80
89
81
90
82
- QUERY = pack_message_str ("000001000" , 1 )
91
+ QUERY = pack_message_str ("000001000" , True )
92
+ MOUSEQUERY = pack_message_str ("010001000" , True )
83
93
RESET = pack_message_str ("0111101111110000000000" )
84
94
85
95
BIT_BREAK = 1 << 11
@@ -94,12 +104,19 @@ def is_mod_report(report):
94
104
return not bool (report & BIT_MOD )
95
105
96
106
107
+ def extract_bits (report , * positions ):
108
+ result = 0
109
+ for p in positions :
110
+ result = (result << 1 )
111
+ if report & (1 << p ):
112
+ result |= 1
113
+ #result = (result << 1) | bool(report & (1<<p))
114
+ return result
115
+
97
116
# keycode bits are backwards compared to other information sources
98
117
# (bit 0 is first)
99
118
def keycode (report ):
100
- b = f"{ report >> 12 :07b} "
101
- b = "" .join (reversed (b ))
102
- return int (b , 2 )
119
+ return extract_bits (report , 12 , 13 , 14 , 15 , 16 , 17 , 18 )
103
120
104
121
105
122
def modifiers (report ):
@@ -122,11 +139,18 @@ def modifiers(report):
122
139
)
123
140
124
141
142
+ def signfix (num , sign_pos ):
143
+ """Fix a signed number if the bit with weight `sign_pos` is actually the sign bit"""
144
+ if num & sign_pos :
145
+ return num - 2 * sign_pos
146
+ return num
147
+
125
148
class KeyboardHandler :
126
149
def __init__ (self ):
127
150
self .old_modifiers = 0
128
151
self .cc = ConsumerControl (usb_hid .devices )
129
152
self .kbd = Keyboard (usb_hid .devices )
153
+ self .mouse = Mouse (usb_hid .devices )
130
154
131
155
def set_key_state (self , key , state ):
132
156
if state :
@@ -144,6 +168,27 @@ def set_key_state(self, key, state):
144
168
else :
145
169
self .kbd .release (key )
146
170
171
+ def handle_mouse_report (self , report ):
172
+ if report == 1536 : # the "nothing happened" report
173
+ return
174
+
175
+ dx = extract_bits (report , 11 ,12 ,13 ,14 ,15 ,16 ,17 )
176
+ dx = - signfix (dx , 64 )
177
+ dy = extract_bits (report , 0 ,1 ,2 ,3 ,4 ,5 ,6 )
178
+ dy = - signfix (dy , 64 )
179
+ b1 = not extract_bits (report , 18 )
180
+ b2 = not extract_bits (report , 7 )
181
+
182
+ self .mouse .report [0 ] = (
183
+ Mouse .MIDDLE_BUTTON if (b1 and b2 ) else
184
+ Mouse .LEFT_BUTTON if b1 else
185
+ Mouse .RIGHT_BUTTON if b2
186
+ else 0 )
187
+ if dx or dy :
188
+ self .mouse .move (dx * MOUSE_SCALE , dy * MOUSE_SCALE )
189
+ else :
190
+ self .mouse ._send_no_move () # pylint: disable=protected-access
191
+
147
192
def handle_report (self , report_value ):
148
193
if report_value == 1536 : # the "nothing happened" report
149
194
return
@@ -205,5 +250,19 @@ def handle_report(self, report_value):
205
250
sm .restart ()
206
251
sm .write (RESET )
207
252
time .sleep (0.1 )
253
+
254
+ sm .write (MOUSEQUERY )
255
+ deadline = ticks_add (ticks_ms (), 100 )
256
+ while ticks_less (ticks_ms (), deadline ):
257
+ if sm .in_waiting :
258
+ sm .readinto (recv_buf )
259
+ value = recv_buf [0 ]
260
+ handler .handle_mouse_report (value )
261
+ break
262
+ else :
263
+ print ("keyboard did not respond - resetting" )
264
+ sm .restart ()
265
+ sm .write (RESET )
266
+ time .sleep (0.1 )
208
267
finally : # Release all keys before e.g., code is reloaded
209
268
handler .kbd .release_all ()
0 commit comments