Skip to content

Commit e70a3c2

Browse files
authored
Merge pull request #10 from relic-se/cursor-bitmap
Cursor bitmap
2 parents d214ace + b585540 commit e70a3c2

File tree

4 files changed

+57
-34
lines changed

4 files changed

+57
-34
lines changed

adafruit_usb_host_mouse.py renamed to adafruit_usb_host_mouse/__init__.py

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,18 @@
4141
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_USB_Host_Mouse.git"
4242

4343
BUTTONS = ["left", "right", "middle"]
44+
DEFAULT_CURSOR = "/".join(__file__.split("/")[:-1]) + "/mouse_cursor.bmp"
4445

4546

46-
def find_and_init_boot_mouse(cursor_image="/launcher_assets/mouse_cursor.bmp"):
47+
def find_and_init_boot_mouse(cursor_image=DEFAULT_CURSOR): # noqa: PLR0912
4748
"""
4849
Scan for an attached boot mouse connected via USB host.
49-
If one is found initialize an instance of BootMouse class
50+
If one is found initialize an instance of :class:`BootMouse` class
5051
and return it.
51-
:return: The BootMouse instance or None if no mouse was found.
52+
53+
: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.
5256
"""
5357
mouse_interface_index, mouse_endpoint_address = None, None
5458
mouse_device = None
@@ -101,17 +105,19 @@ def find_and_init_boot_mouse(cursor_image="/launcher_assets/mouse_cursor.bmp"):
101105
mouse_device.set_configuration()
102106

103107
# load the mouse cursor bitmap
104-
if not isinstance(cursor_image, str):
105-
raise TypeError("cursor_image must be a string")
106-
mouse_bmp = OnDiskBitmap(cursor_image)
108+
if isinstance(cursor_image, str):
109+
mouse_bmp = OnDiskBitmap(cursor_image)
110+
111+
# make the background pink pixels transparent
112+
mouse_bmp.pixel_shader.make_transparent(0)
107113

108-
# make the background pink pixels transparent
109-
mouse_bmp.pixel_shader.make_transparent(0)
114+
# create a TileGrid for the mouse, using its bitmap and pixel_shader
115+
mouse_tg = TileGrid(mouse_bmp, pixel_shader=mouse_bmp.pixel_shader)
110116

111-
# create a TileGrid for the mouse, using its bitmap and pixel_shader
112-
mouse_tg = TileGrid(mouse_bmp, pixel_shader=mouse_bmp.pixel_shader)
117+
else:
118+
mouse_tg = None
113119

114-
return BootMouse(mouse_device, mouse_endpoint_address, mouse_tg, mouse_was_attached)
120+
return BootMouse(mouse_device, mouse_endpoint_address, mouse_was_attached, mouse_tg)
115121

116122
# if no mouse found
117123
return None
@@ -125,13 +131,13 @@ class BootMouse:
125131
126132
:param device: The usb device instance for the mouse
127133
:param endpoint_address: The address of the mouse endpoint
128-
:param tilegrid: The TileGrid that holds the visible mouse cursor
129134
:param was_attached: Whether the usb device was attached to the kernel
135+
:param tilegrid: The TileGrid that holds the visible mouse cursor
130136
:param scale: The scale of the group that the Mouse TileGrid will be put into.
131137
Needed in order to properly clamp the mouse to the display bounds
132138
"""
133139

134-
def __init__(self, device, endpoint_address, tilegrid, was_attached, scale=1): # noqa: PLR0913, too many args
140+
def __init__(self, device, endpoint_address, was_attached, tilegrid=None, scale=1): # noqa: PLR0913, too many args
135141
self.device = device
136142

137143
self.tilegrid = tilegrid
@@ -154,29 +160,44 @@ def __init__(self, device, endpoint_address, tilegrid, was_attached, scale=1):
154160
If there's no new mouse data (nothing changes) this property can be checked to see
155161
which buttons are currently pressed."""
156162

157-
self.display_size = (supervisor.runtime.display.width, supervisor.runtime.display.height)
163+
if tilegrid is not None:
164+
self.display_size = (
165+
supervisor.runtime.display.width,
166+
supervisor.runtime.display.height,
167+
)
168+
self.tilegrid.x, self.tilegrid.y = (
169+
x // 2 for x in self.display_size
170+
) # center cursor in display
171+
else:
172+
self._x, self._y = 0, 0
158173

159174
@property
160175
def x(self) -> int:
161176
"""
162177
The x coordinate of the mouse cursor
163178
"""
164-
return self.tilegrid.x
179+
return self.tilegrid.x if self.tilegrid else self._x
165180

166181
@x.setter
167182
def x(self, new_x: int) -> None:
168-
self.tilegrid.x = new_x
183+
if self.tilegrid:
184+
self.tilegrid.x = new_x
185+
else:
186+
self._x = new_x
169187

170188
@property
171189
def y(self) -> int:
172190
"""
173191
The y coordinate of the mouse cursor
174192
"""
175-
return self.tilegrid.y
193+
return self.tilegrid.y if self.tilegrid else self._y
176194

177195
@y.setter
178196
def y(self, new_y: int) -> None:
179-
self.tilegrid.y = new_y
197+
if self.tilegrid:
198+
self.tilegrid.y = new_y
199+
else:
200+
self._y = new_y
180201

181202
def release(self):
182203
"""
@@ -206,22 +227,21 @@ def update(self):
206227
except usb.core.USBError:
207228
return None
208229

209-
# update the mouse tilegrid x and y coordinates
230+
# update the mouse x and y coordinates
210231
# based on the delta values read from the mouse
211-
self.tilegrid.x = max(
212-
0,
213-
min(
214-
(self.display_size[0] // self.scale) - 1,
215-
self.tilegrid.x + int(round((self.buffer[1] / self.sensitivity), 0)),
216-
),
217-
)
218-
self.tilegrid.y = max(
219-
0,
220-
min(
221-
(self.display_size[1] // self.scale) - 1,
222-
self.tilegrid.y + int(round((self.buffer[2] / self.sensitivity), 0)),
223-
),
224-
)
232+
dx, dy = self.buffer[1:3]
233+
dx = int(round((dx / self.sensitivity), 0))
234+
dy = int(round((dy / self.sensitivity), 0))
235+
if self.tilegrid:
236+
self.tilegrid.x = max(
237+
0, min((self.display_size[0] // self.scale) - 1, self.tilegrid.x + dx)
238+
)
239+
self.tilegrid.y = max(
240+
0, min((self.display_size[1] // self.scale) - 1, self.tilegrid.y + dy)
241+
)
242+
else:
243+
self._x += dx
244+
self._y += dy
225245

226246
self.pressed_btns = []
227247
for i, button in enumerate(BUTTONS):
198 Bytes
Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
2+
3+
SPDX-License-Identifier: MIT

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ dynamic = ["dependencies", "optional-dependencies"]
4848
[tool.setuptools]
4949
# TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER,
5050
# CHANGE `py_modules = ['...']` TO `packages = ['...']`
51-
py-modules = ["adafruit_usb_host_mouse"]
51+
packages = ["adafruit_usb_host_mouse"]
5252

5353
[tool.setuptools.dynamic]
5454
dependencies = {file = ["requirements.txt"]}

0 commit comments

Comments
 (0)