Skip to content

Commit 36a27be

Browse files
authored
Merge pull request #2361 from jepler/next-mouse-daisychain
Add code for mouse support in NeXT keyboard
2 parents f6a77d2 + b580773 commit 36a27be

File tree

1 file changed

+63
-4
lines changed
  • CircuitPython_NeXT_Keyboard_RP2040

1 file changed

+63
-4
lines changed

CircuitPython_NeXT_Keyboard_RP2040/code.py

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from adafruit_hid.consumer_control import ConsumerControl
1111
from adafruit_hid.keyboard import Keyboard
1212
from adafruit_hid.keyboard import Keycode
13+
from adafruit_hid.mouse import Mouse
1314
from adafruit_pioasm import Program
1415
from adafruit_ticks import ticks_add, ticks_less, ticks_ms
1516
from next_keycode import (
@@ -21,6 +22,14 @@
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!
2635
POWER_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)
8393
RESET = pack_message_str("0111101111110000000000")
8494

8595
BIT_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)
99118
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)
103120

104121

105122
def 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+
125148
class 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)
208267
finally: # Release all keys before e.g., code is reloaded
209268
handler.kbd.release_all()

0 commit comments

Comments
 (0)