1010from adafruit_hid .consumer_control import ConsumerControl
1111from adafruit_hid .keyboard import Keyboard
1212from adafruit_hid .keyboard import Keycode
13+ from adafruit_hid .mouse import Mouse
1314from adafruit_pioasm import Program
1415from adafruit_ticks import ticks_add , ticks_less , ticks_ms
1516from next_keycode import (
2122 shift_modifiers ,
2223)
2324
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+
2433# Customize the power key's keycode. You can change it to `Keycode.POWER` if
2534# you really want to accidentally power off your computer!
2635POWER_KEY_SENDS = Keycode .F1
@@ -79,7 +88,8 @@ def set_leds(i):
7988 return pack_message_str (f"0000000001110{ i :02b} 0000000" )
8089
8190
82- QUERY = pack_message_str ("000001000" , 1 )
91+ QUERY = pack_message_str ("000001000" , True )
92+ MOUSEQUERY = pack_message_str ("010001000" , True )
8393RESET = pack_message_str ("0111101111110000000000" )
8494
8595BIT_BREAK = 1 << 11
@@ -94,12 +104,19 @@ def is_mod_report(report):
94104 return not bool (report & BIT_MOD )
95105
96106
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+
97116# keycode bits are backwards compared to other information sources
98117# (bit 0 is first)
99118def 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 )
103120
104121
105122def modifiers (report ):
@@ -122,11 +139,18 @@ def modifiers(report):
122139)
123140
124141
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+
125148class KeyboardHandler :
126149 def __init__ (self ):
127150 self .old_modifiers = 0
128151 self .cc = ConsumerControl (usb_hid .devices )
129152 self .kbd = Keyboard (usb_hid .devices )
153+ self .mouse = Mouse (usb_hid .devices )
130154
131155 def set_key_state (self , key , state ):
132156 if state :
@@ -144,6 +168,27 @@ def set_key_state(self, key, state):
144168 else :
145169 self .kbd .release (key )
146170
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+
147192 def handle_report (self , report_value ):
148193 if report_value == 1536 : # the "nothing happened" report
149194 return
@@ -205,5 +250,19 @@ def handle_report(self, report_value):
205250 sm .restart ()
206251 sm .write (RESET )
207252 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 )
208267finally : # Release all keys before e.g., code is reloaded
209268 handler .kbd .release_all ()
0 commit comments