33# SPDX-License-Identifier: MIT
44import array
55
6- import displayio
76import supervisor
87import terminalio
98import usb .core
109from 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
1311
1412import adafruit_usb_host_descriptors
1513
14+ # use the default built-in display,
1615display = supervisor .runtime .display
1716
17+ # a group to hold all other visual elements
1818main_group = Group ()
19+
20+ # set the main group to show on the display
1921display .root_group = main_group
2022
23+ # load the cursor bitmap file
2124mouse_bmp = OnDiskBitmap ("mouse_cursor.bmp" )
2225
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.
2329output_lbls = []
2430mouse_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 = []
3032
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 ]
3436
35- # add the two extra colors to the palette
3637for 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 )
3844
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
4457 mouse_tg .x = display .width // 2 - (i * 12 )
4558 mouse_tg .y = display .height // 2
59+
60+ # add this mouse tilegrid to the list of mouse tilegrids
4661 mouse_tgs .append (mouse_tg )
62+
63+ # add this mouse tilegrid to the main group so it will show
64+ # on the display
4765 main_group .append (mouse_tg )
4866
67+ # create a label for this mouse
4968 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
5070 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
5175 output_lbl .anchored_position = (1 , 1 + i * 13 )
76+
77+ # add the label to the list of labels
5278 output_lbls .append (output_lbl )
79+
80+ # add the label to the main group so it will show
81+ # on the display
5382 main_group .append (output_lbl )
5483
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
5586mouse_interface_indexes = []
5687mouse_endpoint_addresses = []
5788mice = []
5889
5990# scan for connected USB devices
6091for device in usb .core .find (find_all = True ):
92+ # check for boot mouse endpoints on this device
6193 mouse_interface_index , mouse_endpoint_address = (
6294 adafruit_usb_host_descriptors .find_boot_mouse_endpoint (device )
6395 )
96+ # if a boot mouse interface index and endpoint address were found
6497 if mouse_interface_index is not None and mouse_endpoint_address is not None :
98+ # add the interface index to the list of indexes
6599 mouse_interface_indexes .append (mouse_interface_index )
100+ # add the endpoint address to the list of addresses
66101 mouse_endpoint_addresses .append (mouse_endpoint_address )
67-
102+ # add the device instance to the list of mice
68103 mice .append (device )
104+
105+ # print details to the console
69106 print (f"mouse interface: { mouse_interface_index } " , end = "" )
70107 print (f"endpoint_address: { hex (mouse_endpoint_address )} " )
108+
109+ # detach device from kernel if needed
71110 if device .is_kernel_driver_active (0 ):
72111 device .detach_kernel_driver (0 )
73112
77116# This is ordered by bit position.
78117BUTTONS = ["left" , "right" , "middle" ]
79118
119+ # list of buffers, will hold one buffer for each mouse
80120mouse_bufs = []
81-
82- for mouse_tg in mouse_tgs :
121+ for i in range (2 ):
83122 # Buffer to hold data read from the mouse
84- # Boot mice have 4 byte reports
85123 mouse_bufs .append (array .array ("b" , [0 ] * 8 ))
86124
87125
88126def 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+ """
89133 if read_count == 4 :
90134 delta_x = buffer [1 ]
91135 delta_y = buffer [2 ]
@@ -97,25 +141,45 @@ def get_mouse_deltas(buffer, read_count):
97141 return delta_x , delta_y
98142
99143
144+ # main loop
100145while True :
146+ # for each mouse instance
101147 for mouse_index , mouse in enumerate (mice ):
148+ # try to read data from the mouse
102149 try :
103150 count = mouse .read (
104151 mouse_endpoint_addresses [mouse_index ], mouse_bufs [mouse_index ], timeout = 10
105152 )
153+
154+ # if there is no data it will raise USBTimeoutError
106155 except usb .core .USBTimeoutError :
156+ # Nothing to do if there is no data for this mouse
107157 continue
158+
159+ # there was mouse data, so get the delta x and y values from it
108160 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
109164 mouse_tgs [mouse_index ].x = max (
110165 0 , min (display .width - 1 , mouse_tgs [mouse_index ].x + mouse_deltas [0 ])
111166 )
167+ # update the y position of this mouse cursor using the delta value
168+ # clamped to the display size
112169 mouse_tgs [mouse_index ].y = max (
113170 0 , min (display .height - 1 , mouse_tgs [mouse_index ].y + mouse_deltas [1 ])
114171 )
115172
173+ # output string with the new cursor position
116174 out_str = f"{ mouse_tgs [mouse_index ].x } ,{ mouse_tgs [mouse_index ].y } "
175+
176+ # loop over possible button bit indexes
117177 for i , button in enumerate (BUTTONS ):
178+ # check each bit index to determin if the button was pressed
118179 if mouse_bufs [mouse_index ][0 ] & (1 << i ) != 0 :
180+ # if it was pressed, add the button to the output string
119181 out_str += f" { button } "
120182
183+ # set the output string into text of the label
184+ # to show it on the display
121185 output_lbls [mouse_index ].text = out_str
0 commit comments