Skip to content

Commit 76d2a5a

Browse files
committed
two mice code
1 parent 608253e commit 76d2a5a

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
import array
5+
import supervisor
6+
import terminalio
7+
import usb.core
8+
from adafruit_display_text.bitmap_label import Label
9+
from displayio import Group, OnDiskBitmap, TileGrid, Palette, ColorConverter
10+
11+
import adafruit_usb_host_descriptors
12+
13+
# use the default built-in display,
14+
# the HSTX / PicoDVI display for the Metro RP2350
15+
display = supervisor.runtime.display
16+
17+
# a group to hold all other visual elements
18+
main_group = Group()
19+
20+
# set the main group to show on the display
21+
display.root_group = main_group
22+
23+
# load the cursor bitmap file
24+
mouse_bmp = OnDiskBitmap("mouse_cursor.bmp")
25+
26+
# lists for labels, mouse tilegrids, and palettes.
27+
# each mouse will get 1 of each item. All lists
28+
# will end up with length 2.
29+
output_lbls = []
30+
mouse_tgs = []
31+
palettes = []
32+
33+
# the different colors to use for each mouse cursor
34+
# and labels
35+
colors = [0xFF00FF, 0x00FF00]
36+
37+
for i in range(2):
38+
# create a palette for this mouse
39+
mouse_palette = Palette(3)
40+
# index zero is used for transparency
41+
mouse_palette.make_transparent(0)
42+
# add the palette to the list of palettes
43+
palettes.append(mouse_palette)
44+
45+
# copy the first two colors from mouse palette
46+
for palette_color_index in range(2):
47+
mouse_palette[palette_color_index] = mouse_bmp.pixel_shader[palette_color_index]
48+
49+
# replace the last color with different color for each mouse
50+
mouse_palette[2] = colors[i]
51+
52+
# create a TileGrid for this mouse cursor.
53+
# use the palette created above
54+
mouse_tg = TileGrid(mouse_bmp, pixel_shader=mouse_palette)
55+
56+
# move the mouse tilegrid to near the center of the display
57+
mouse_tg.x = display.width // 2 - (i * 12)
58+
mouse_tg.y = display.height // 2
59+
60+
# add this mouse tilegrid to the list of mouse tilegrids
61+
mouse_tgs.append(mouse_tg)
62+
63+
# add this mouse tilegrid to the main group so it will show
64+
# on the display
65+
main_group.append(mouse_tg)
66+
67+
# create a label for this mouse
68+
output_lbl = Label(terminalio.FONT, text=f"{mouse_tg.x},{mouse_tg.y}", color=colors[i], scale=1)
69+
# anchored to the top left corner of the label
70+
output_lbl.anchor_point = (0, 0)
71+
72+
# move to op left corner of the display, moving
73+
# down by a static amount to static the two labels
74+
# one below the other
75+
output_lbl.anchored_position = (1, 1 + i * 13)
76+
77+
# add the label to the list of labels
78+
output_lbls.append(output_lbl)
79+
80+
# add the label to the main group so it will show
81+
# on the display
82+
main_group.append(output_lbl)
83+
84+
# lists for mouse interface indexes, endpoint addresses, and USB Device instances
85+
# each of these will end up with length 2 once we find both mice
86+
mouse_interface_indexes = []
87+
mouse_endpoint_addresses = []
88+
mice = []
89+
90+
# scan for connected USB devices
91+
for device in usb.core.find(find_all=True):
92+
# check for boot mouse endpoints on this device
93+
mouse_interface_index, mouse_endpoint_address = (
94+
adafruit_usb_host_descriptors.find_boot_mouse_endpoint(device)
95+
)
96+
# if a boot mouse interface index and endpoint address were found
97+
if mouse_interface_index is not None and mouse_endpoint_address is not None:
98+
# add the interface index to the list of indexes
99+
mouse_interface_indexes.append(mouse_interface_index)
100+
# add the endpoint address to the list of addresses
101+
mouse_endpoint_addresses.append(mouse_endpoint_address)
102+
# add the device instance to the list of mice
103+
mice.append(device)
104+
105+
# print details to the console
106+
print(f"mouse interface: {mouse_interface_index} ", end="")
107+
print(f"endpoint_address: {hex(mouse_endpoint_address)}")
108+
109+
# detach device from kernel if needed
110+
if device.is_kernel_driver_active(0):
111+
device.detach_kernel_driver(0)
112+
113+
# set the mouse configuration so it can be used
114+
device.set_configuration()
115+
116+
# This is ordered by bit position.
117+
BUTTONS = ["left", "right", "middle"]
118+
119+
# list of buffers, will hold one buffer for each mouse
120+
mouse_bufs = []
121+
for i in range(2):
122+
# Buffer to hold data read from the mouse
123+
mouse_bufs.append(array.array("b", [0] * 8))
124+
125+
126+
def get_mouse_deltas(buffer, read_count):
127+
"""
128+
Given a buffer and read_count return the x and y delta values
129+
:param buffer: A buffer containing data read from the mouse
130+
:param read_count: How many bytes of data were read from the mouse
131+
:return: tuple x,y delta values
132+
"""
133+
if read_count == 4:
134+
delta_x = buffer[1]
135+
delta_y = buffer[2]
136+
elif read_count == 8:
137+
delta_x = buffer[2]
138+
delta_y = buffer[4]
139+
else:
140+
raise ValueError(f"Unsupported mouse packet size: {read_count}, must be 4 or 8")
141+
return delta_x, delta_y
142+
143+
# main loop
144+
while True:
145+
# for each mouse instance
146+
for mouse_index, mouse in enumerate(mice):
147+
# try to read data from the mouse
148+
try:
149+
count = mouse.read(
150+
mouse_endpoint_addresses[mouse_index], mouse_bufs[mouse_index], timeout=10
151+
)
152+
153+
# if there is no data it will raise USBTimeoutError
154+
except usb.core.USBTimeoutError:
155+
# Nothing to do if there is no data for this mouse
156+
continue
157+
158+
# there was mouse data, so get the delta x and y values from it
159+
mouse_deltas = get_mouse_deltas(mouse_bufs[mouse_index], count)
160+
161+
# update the x position of this mouse cursor using the delta value
162+
# clamped to the display size
163+
mouse_tgs[mouse_index].x = max(
164+
0, min(display.width - 1, mouse_tgs[mouse_index].x + mouse_deltas[0])
165+
)
166+
# update the y position of this mouse cursor using the delta value
167+
# clamped to the display size
168+
mouse_tgs[mouse_index].y = max(
169+
0, min(display.height - 1, mouse_tgs[mouse_index].y + mouse_deltas[1])
170+
)
171+
172+
# output string with the new cursor position
173+
out_str = f"{mouse_tgs[mouse_index].x},{mouse_tgs[mouse_index].y}"
174+
175+
# loop over possible button bit indexes
176+
for i, button in enumerate(BUTTONS):
177+
# check each bit index to determin if the button was pressed
178+
if mouse_bufs[mouse_index][0] & (1 << i) != 0:
179+
# if it was pressed, add the button to the output string
180+
out_str += f" {button}"
181+
182+
# set the output string into text of the label
183+
# to show it on the display
184+
output_lbls[mouse_index].text = out_str
Binary file not shown.

0 commit comments

Comments
 (0)