Skip to content

Commit f86a9ed

Browse files
authored
Merge pull request #15 from RetiredWizard/interfaceindx
add interface index to BOOTMOUSE class
2 parents bd0f233 + f725500 commit f86a9ed

File tree

1 file changed

+94
-96
lines changed

1 file changed

+94
-96
lines changed

adafruit_usb_host_mouse/__init__.py

Lines changed: 94 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,36 @@
4343
BUTTONS = ["left", "right", "middle"]
4444
DEFAULT_CURSOR = "/".join(__file__.split("/")[:-1]) + "/mouse_cursor.bmp"
4545

46+
SUBCLASS_BOOT = 0x01
47+
SUBCLASS_RESERVED = 0x00
4648

47-
def find_and_init_boot_mouse(cursor_image=DEFAULT_CURSOR): # noqa: PLR0912
49+
50+
def find_and_init_mouse(cursor_image=DEFAULT_CURSOR, subclass=SUBCLASS_BOOT):
4851
"""
49-
Scan for an attached boot mouse connected via USB host.
50-
If one is found initialize an instance of :class:`BootMouse` class
51-
and return it.
52+
Scan for an attached mouse connected via USB host.
53+
If one is found return a tuple containing the parameters needed to initalize an
54+
instance of :class: `BootMouse` or :class: `ReportMouse` depending on the value of
55+
the subclass parameter.
5256
5357
:param cursor_image: Provide the absolute path to the desired cursor bitmap image. If set as
54-
`None`, the :class:`BootMouse` instance will not control a :class:`displayio.TileGrid` object.
55-
:return: The :class:`BootMouse` instance or None if no mouse was found.
58+
`None`, the object instance created using the returned tuple will not control
59+
a :class:`displayio.TileGrid` object.
60+
:param subclass: Defines whether to search for boot or non-boot mice.
61+
SUBCLASS_BOOT (0X01), a boot mouse will be searched for
62+
SUBCLASS_RESERVED (0x00), a non-boot (report) mouse will be searched for
63+
:return: A tupple cotaining the arguments needed by the calling find and init helper
64+
function. If no mouse is found None is returned.
5665
"""
5766
mouse_interface_index, mouse_endpoint_address = None, None
5867
mouse_device = None
68+
deviceType, find_endpoint = (
69+
("boot", adafruit_usb_host_descriptors.find_boot_mouse_endpoint)
70+
if subclass == SUBCLASS_BOOT
71+
else ("report", adafruit_usb_host_descriptors.find_report_mouse_endpoint)
72+
)
5973

6074
# scan for connected USB device and loop over any found
61-
print("scanning usb (boot)")
75+
print(f"scanning usb ({deviceType})")
6276
for device in usb.core.find(find_all=True):
6377
# print device info
6478
try:
@@ -76,9 +90,7 @@ def find_and_init_boot_mouse(cursor_image=DEFAULT_CURSOR): # noqa: PLR0912
7690
)
7791
print(config_descriptor)
7892

79-
_possible_interface_index, _possible_endpoint_address = (
80-
adafruit_usb_host_descriptors.find_boot_mouse_endpoint(device)
81-
)
93+
_possible_interface_index, _possible_endpoint_address = find_endpoint(device)
8294
if _possible_interface_index is not None and _possible_endpoint_address is not None:
8395
mouse_device = device
8496
mouse_interface_index = _possible_interface_index
@@ -88,23 +100,25 @@ def find_and_init_boot_mouse(cursor_image=DEFAULT_CURSOR): # noqa: PLR0912
88100
+ f"endpoint_address: {hex(mouse_endpoint_address)}"
89101
)
90102
break
91-
print("was not a boot mouse")
103+
print(f"was not a {deviceType} mouse")
92104
except usb.core.USBError as e:
93105
print_exception(e, e, None)
94106

95-
mouse_was_attached = None
107+
mouse_was_attached = []
96108
if mouse_device is not None:
97109
# detach the kernel driver if needed
98-
if mouse_device.is_kernel_driver_active(0):
99-
mouse_was_attached = True
100-
mouse_device.detach_kernel_driver(0)
101-
else:
102-
mouse_was_attached = False
110+
possible_interfaces = [mouse_interface_index] if subclass == SUBCLASS_BOOT else [0, 1, 2]
111+
112+
for intf in possible_interfaces:
113+
if mouse_device.is_kernel_driver_active(intf):
114+
mouse_was_attached.append(intf)
115+
mouse_device.detach_kernel_driver(intf)
103116

104117
# set configuration on the mouse so we can use it
105118
mouse_device.set_configuration()
106119

107120
# load the mouse cursor bitmap
121+
mouse_tg = None
108122
if isinstance(cursor_image, str):
109123
mouse_bmp = OnDiskBitmap(cursor_image)
110124

@@ -114,92 +128,51 @@ def find_and_init_boot_mouse(cursor_image=DEFAULT_CURSOR): # noqa: PLR0912
114128
# create a TileGrid for the mouse, using its bitmap and pixel_shader
115129
mouse_tg = TileGrid(mouse_bmp, pixel_shader=mouse_bmp.pixel_shader)
116130

117-
else:
118-
mouse_tg = None
119-
120-
return BootMouse(mouse_device, mouse_endpoint_address, mouse_was_attached, mouse_tg)
131+
return (
132+
(mouse_device, mouse_interface_index, mouse_endpoint_address, mouse_was_attached),
133+
mouse_tg,
134+
)
121135

122136
# if no mouse found
123137
return None
124138

125139

126-
def find_and_init_report_mouse(cursor_image=DEFAULT_CURSOR): # noqa: PLR0912
140+
def find_and_init_boot_mouse(cursor_image=DEFAULT_CURSOR, scale=1):
141+
"""
142+
Scan for an attached boot mouse connected via USB host.
143+
If one is found initialize an instance of :class:`BootMouse` class
144+
and return it.
145+
146+
:param cursor_image: Provide the absolute path to the desired cursor bitmap image. If set as
147+
`None`, the :class:`BootMouse` instance will not control a :class:`displayio.TileGrid` object
148+
:param scale: The scale of the group that the Mouse TileGrid will be put into
149+
Needed in order to properly clamp the mouse to the display bounds
150+
:return: The :class:`BootMouse` instance or None if no mouse was found.
151+
"""
152+
found_mouse = find_and_init_mouse(cursor_image, SUBCLASS_BOOT)
153+
if found_mouse is not None:
154+
return BootMouse(*found_mouse[0], tilegrid=found_mouse[1], scale=scale)
155+
else:
156+
return None
157+
158+
159+
def find_and_init_report_mouse(cursor_image=DEFAULT_CURSOR, scale=1):
127160
"""
128161
Scan for an attached report mouse connected via USB host.
129162
If one is found initialize an instance of :class:`ReportMouse` class
130163
and return it.
131164
132165
:param cursor_image: Provide the absolute path to the desired cursor bitmap image. If set as
133166
`None`, the :class:`ReportMouse` will not control a :class:`displayio.TileGrid` object.
167+
:param scale: The scale of the group that the Mouse TileGrid will be put into
168+
Needed in order to properly clamp the mouse to the display bounds
134169
:return: The :class:`ReportMouse` instance or None if no mouse was found.
135170
"""
136-
mouse_interface_index, mouse_endpoint_address = None, None
137-
mouse_device = None
138-
139-
# scan for connected USB device and loop over any found
140-
print("scanning usb (report)")
141-
for device in usb.core.find(find_all=True):
142-
# print device info
143-
try:
144-
try:
145-
print(f"{device.idVendor:04x}:{device.idProduct:04x}")
146-
except usb.core.USBError as e:
147-
print_exception(e, e, None)
148-
try:
149-
print(device.manufacturer, device.product)
150-
except usb.core.USBError as e:
151-
print_exception(e, e, None)
152-
print()
153-
config_descriptor = adafruit_usb_host_descriptors.get_configuration_descriptor(
154-
device, 0
155-
)
156-
print(config_descriptor)
157-
158-
_possible_interface_index, _possible_endpoint_address = (
159-
adafruit_usb_host_descriptors.find_report_mouse_endpoint(device)
160-
)
161-
if _possible_interface_index is not None and _possible_endpoint_address is not None:
162-
mouse_device = device
163-
mouse_interface_index = _possible_interface_index
164-
mouse_endpoint_address = _possible_endpoint_address
165-
print(
166-
f"mouse interface: {mouse_interface_index} "
167-
+ f"endpoint_address: {hex(mouse_endpoint_address)}"
168-
)
169-
break
170-
print("was not a report mouse")
171-
except usb.core.USBError as e:
172-
print_exception(e, e, None)
173-
174-
mouse_was_attached = None
175-
if mouse_device is not None:
176-
# detach the kernel driver if needed
177-
if mouse_device.is_kernel_driver_active(0):
178-
mouse_was_attached = True
179-
mouse_device.detach_kernel_driver(0)
180-
else:
181-
mouse_was_attached = False
182-
183-
# set configuration on the mouse so we can use it
184-
mouse_device.set_configuration()
185-
186-
# load the mouse cursor bitmap
187-
if isinstance(cursor_image, str):
188-
mouse_bmp = OnDiskBitmap(cursor_image)
189-
190-
# make the background pink pixels transparent
191-
mouse_bmp.pixel_shader.make_transparent(0)
192-
193-
# create a TileGrid for the mouse, using its bitmap and pixel_shader
194-
mouse_tg = TileGrid(mouse_bmp, pixel_shader=mouse_bmp.pixel_shader)
195-
196-
else:
197-
mouse_tg = None
198-
199-
return ReportMouse(mouse_device, mouse_endpoint_address, mouse_was_attached, mouse_tg)
200-
201-
# if no mouse found
202-
return None
171+
found_mouse = find_and_init_mouse(cursor_image, SUBCLASS_RESERVED)
172+
if found_mouse is not None:
173+
return ReportMouse(*found_mouse[0], tilegrid=found_mouse[1], scale=scale)
174+
else:
175+
return None
203176

204177

205178
class BootMouse:
@@ -209,19 +182,23 @@ class BootMouse:
209182
were pressed.
210183
211184
:param device: The usb device instance for the mouse
185+
:param interface_index: The USB interface index of the mouse
212186
:param endpoint_address: The address of the mouse endpoint
213-
:param was_attached: Whether the usb device was attached to the kernel
187+
:param was_attached: A list of the usb devices detached from the kernel
214188
:param tilegrid: The TileGrid that holds the visible mouse cursor
215-
:param scale: The scale of the group that the Mouse TileGrid will be put into.
189+
:param scale: The scale of the group that the Mouse TileGrid will be put into
216190
Needed in order to properly clamp the mouse to the display bounds
217191
"""
218192

219-
def __init__(self, device, endpoint_address, was_attached, tilegrid=None, scale=1): # noqa: PLR0913, too many args
193+
def __init__( # noqa: PLR0913, too many args
194+
self, device, interface_index, endpoint_address, was_attached, *, tilegrid=None, scale=1
195+
):
220196
self.device = device
221197

222198
self.tilegrid = tilegrid
223199
"""TileGrid containing the Mouse cursor graphic."""
224200

201+
self.interface = interface_index
225202
self.endpoint = endpoint_address
226203
self.buffer = array.array("b", [0] * 4)
227204
self.was_attached = was_attached
@@ -283,8 +260,11 @@ def release(self):
283260
Release the mouse cursor and re-attach it to the kernel
284261
if it was attached previously.
285262
"""
286-
if self.was_attached and not self.device.is_kernel_driver_active(0):
287-
self.device.attach_kernel_driver(0)
263+
# was_attached is a list of interfaces detached from the kernel or
264+
# an empty list if no interfaces were detached
265+
for intf in self.was_attached:
266+
if not self.device.is_kernel_driver_active(intf):
267+
self.device.attach_kernel_driver(intf)
288268

289269
def update(self):
290270
"""
@@ -337,8 +317,26 @@ def update(self):
337317

338318

339319
class ReportMouse(BootMouse):
340-
def __init__(self, device, endpoint_address, was_attached, tilegrid=None, scale=1): # noqa: PLR0913, too many args
341-
super().__init__(device, endpoint_address, was_attached, tilegrid, scale)
320+
"""
321+
Helpler class that encapsulates the objects needed to interact with a non-Boot
322+
mouse (Report), show a visible cursor on the display, and determine when buttons
323+
were pressed. The class is a subclass of BootMouse that overrides the update method.
324+
325+
:param device: The usb device instance for the mouse
326+
:param interface_index: The USB interface index of the mouse
327+
:param endpoint_address: The address of the mouse endpoint
328+
:param was_attached: A list of the usb devices detached from the kernel
329+
:param tilegrid: The TileGrid that holds the visible mouse cursor
330+
:param scale: The scale of the group that the Mouse TileGrid will be put into
331+
Needed in order to properly clamp the mouse to the display bounds
332+
"""
333+
334+
def __init__( # noqa: PLR0913, too many args
335+
self, device, interface_index, endpoint_address, was_attached, *, tilegrid=None, scale=1
336+
):
337+
super().__init__(
338+
device, interface_index, endpoint_address, was_attached, tilegrid=tilegrid, scale=scale
339+
)
342340

343341
def update(self):
344342
"""

0 commit comments

Comments
 (0)