3
3
# SPDX-License-Identifier: MIT
4
4
import array
5
5
6
- import displayio
7
6
import supervisor
8
7
import terminalio
9
8
import usb .core
10
9
from adafruit_display_text .bitmap_label import Label
11
- from displayio import Group , OnDiskBitmap , TileGrid
12
- from tilepalettemapper import TilePaletteMapper
10
+ from displayio import ColorConverter , Group , OnDiskBitmap , Palette , TileGrid
13
11
14
12
import adafruit_usb_host_descriptors
15
13
14
+ # use the default built-in display,
16
15
display = supervisor .runtime .display
17
16
17
+ # a group to hold all other visual elements
18
18
main_group = Group ()
19
+
20
+ # set the main group to show on the display
19
21
display .root_group = main_group
20
22
23
+ # load the cursor bitmap file
21
24
mouse_bmp = OnDiskBitmap ("mouse_cursor.bmp" )
22
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.
23
29
output_lbls = []
24
30
mouse_tgs = []
25
- palette_mappers = []
26
- color_converter = displayio .ColorConverter ()
27
- colors = [0xFF00FF , 0x00FF00 ]
28
- remap_palette = displayio .Palette (3 + len (colors ))
29
- remap_palette .make_transparent (0 )
31
+ palettes = []
30
32
31
- # copy the 3 colors from mouse palette
32
- for i in range ( 3 ):
33
- remap_palette [ i ] = mouse_bmp . pixel_shader [ i ]
33
+ # the different colors to use for each mouse cursor
34
+ # and labels
35
+ colors = [ 0xFF00FF , 0x00FF00 ]
34
36
35
- # add the two extra colors to the palette
36
37
for i in range (2 ):
37
- remap_palette [i + 3 ] = colors [i ]
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 )
38
44
39
- for i in range (2 ):
40
- palette_mapper = TilePaletteMapper (remap_palette , 3 , 1 , 1 )
41
- palette_mapper [0 ] = [0 , 1 , i + 3 ]
42
- palette_mappers .append (palette_mapper )
43
- mouse_tg = TileGrid (mouse_bmp , pixel_shader = palette_mapper )
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
44
57
mouse_tg .x = display .width // 2 - (i * 12 )
45
58
mouse_tg .y = display .height // 2
59
+
60
+ # add this mouse tilegrid to the list of mouse tilegrids
46
61
mouse_tgs .append (mouse_tg )
62
+
63
+ # add this mouse tilegrid to the main group so it will show
64
+ # on the display
47
65
main_group .append (mouse_tg )
48
66
67
+ # create a label for this mouse
49
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
50
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
51
75
output_lbl .anchored_position = (1 , 1 + i * 13 )
76
+
77
+ # add the label to the list of labels
52
78
output_lbls .append (output_lbl )
79
+
80
+ # add the label to the main group so it will show
81
+ # on the display
53
82
main_group .append (output_lbl )
54
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
55
86
mouse_interface_indexes = []
56
87
mouse_endpoint_addresses = []
57
88
mice = []
58
89
59
90
# scan for connected USB devices
60
91
for device in usb .core .find (find_all = True ):
92
+ # check for boot mouse endpoints on this device
61
93
mouse_interface_index , mouse_endpoint_address = (
62
94
adafruit_usb_host_descriptors .find_boot_mouse_endpoint (device )
63
95
)
96
+ # if a boot mouse interface index and endpoint address were found
64
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
65
99
mouse_interface_indexes .append (mouse_interface_index )
100
+ # add the endpoint address to the list of addresses
66
101
mouse_endpoint_addresses .append (mouse_endpoint_address )
67
-
102
+ # add the device instance to the list of mice
68
103
mice .append (device )
104
+
105
+ # print details to the console
69
106
print (f"mouse interface: { mouse_interface_index } " , end = "" )
70
107
print (f"endpoint_address: { hex (mouse_endpoint_address )} " )
108
+
109
+ # detach device from kernel if needed
71
110
if device .is_kernel_driver_active (0 ):
72
111
device .detach_kernel_driver (0 )
73
112
77
116
# This is ordered by bit position.
78
117
BUTTONS = ["left" , "right" , "middle" ]
79
118
119
+ # list of buffers, will hold one buffer for each mouse
80
120
mouse_bufs = []
81
-
82
- for mouse_tg in mouse_tgs :
121
+ for i in range (2 ):
83
122
# Buffer to hold data read from the mouse
84
- # Boot mice have 4 byte reports
85
123
mouse_bufs .append (array .array ("b" , [0 ] * 8 ))
86
124
87
125
88
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
+ """
89
133
if read_count == 4 :
90
134
delta_x = buffer [1 ]
91
135
delta_y = buffer [2 ]
@@ -97,25 +141,45 @@ def get_mouse_deltas(buffer, read_count):
97
141
return delta_x , delta_y
98
142
99
143
144
+ # main loop
100
145
while True :
146
+ # for each mouse instance
101
147
for mouse_index , mouse in enumerate (mice ):
148
+ # try to read data from the mouse
102
149
try :
103
150
count = mouse .read (
104
151
mouse_endpoint_addresses [mouse_index ], mouse_bufs [mouse_index ], timeout = 10
105
152
)
153
+
154
+ # if there is no data it will raise USBTimeoutError
106
155
except usb .core .USBTimeoutError :
156
+ # Nothing to do if there is no data for this mouse
107
157
continue
158
+
159
+ # there was mouse data, so get the delta x and y values from it
108
160
mouse_deltas = get_mouse_deltas (mouse_bufs [mouse_index ], count )
161
+
162
+ # update the x position of this mouse cursor using the delta value
163
+ # clamped to the display size
109
164
mouse_tgs [mouse_index ].x = max (
110
165
0 , min (display .width - 1 , mouse_tgs [mouse_index ].x + mouse_deltas [0 ])
111
166
)
167
+ # update the y position of this mouse cursor using the delta value
168
+ # clamped to the display size
112
169
mouse_tgs [mouse_index ].y = max (
113
170
0 , min (display .height - 1 , mouse_tgs [mouse_index ].y + mouse_deltas [1 ])
114
171
)
115
172
173
+ # output string with the new cursor position
116
174
out_str = f"{ mouse_tgs [mouse_index ].x } ,{ mouse_tgs [mouse_index ].y } "
175
+
176
+ # loop over possible button bit indexes
117
177
for i , button in enumerate (BUTTONS ):
178
+ # check each bit index to determin if the button was pressed
118
179
if mouse_bufs [mouse_index ][0 ] & (1 << i ) != 0 :
180
+ # if it was pressed, add the button to the output string
119
181
out_str += f" { button } "
120
182
183
+ # set the output string into text of the label
184
+ # to show it on the display
121
185
output_lbls [mouse_index ].text = out_str
0 commit comments