Skip to content

Commit 85d15f4

Browse files
authored
Merge pull request #3118 from RetiredWizard/fruitjamminemouse
Metro_RP2350_Minesweeper: Refactor for USB mouse library and new core USB errors
2 parents 060f0c3 + cf5b48b commit 85d15f4

File tree

1 file changed

+51
-97
lines changed
  • Metro/Metro_RP2350_Minesweeper

1 file changed

+51
-97
lines changed

Metro/Metro_RP2350_Minesweeper/code.py

Lines changed: 51 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@
99
the player successfully reveals all squares without mines or clicks on a mine.
1010
"""
1111
import array
12-
import time
1312
from displayio import Group, OnDiskBitmap, TileGrid, Bitmap, Palette
1413
from adafruit_display_text.bitmap_label import Label
1514
from adafruit_display_text.text_box import TextBox
16-
import adafruit_usb_host_descriptors
1715
from eventbutton import EventButton
1816
import supervisor
1917
import terminalio
20-
import usb.core
18+
from adafruit_usb_host_mouse import find_and_init_boot_mouse
2119
from gamelogic import GameLogic, BLANK, INFO_BAR_HEIGHT, DIFFICULTIES
2220
from menu import Menu, SubMenu
2321

@@ -49,6 +47,7 @@
4947
color_depth=16,
5048
)
5149
display = framebufferio.FramebufferDisplay(fb)
50+
supervisor.runtime.display = display
5251

5352
game_logic = GameLogic(display) # pylint: disable=no-value-for-parameter
5453

@@ -76,14 +75,6 @@
7675
ui_group = Group()
7776
main_group.append(ui_group)
7877

79-
# Create the mouse graphics and add to the main group
80-
mouse_bmp = OnDiskBitmap("bitmaps/mouse_cursor.bmp")
81-
mouse_bmp.pixel_shader.make_transparent(0)
82-
mouse_tg = TileGrid(mouse_bmp, pixel_shader=mouse_bmp.pixel_shader)
83-
mouse_tg.x = display.width // 2
84-
mouse_tg.y = display.height // 2
85-
main_group.append(mouse_tg)
86-
8778
MENU_ITEM_HEIGHT = INFO_BAR_HEIGHT
8879

8980
def create_game_board():
@@ -116,63 +107,24 @@ def update_ui():
116107
mines_left_label.text = f"Mines: {game_logic.mines_left}"
117108
elapsed_time_label.text = f"Time: {game_logic.elapsed_time}"
118109

119-
# variable for the mouse USB device instance
120-
mouse = None
121-
122-
# wait a second for USB devices to be ready
123-
time.sleep(1)
124-
125-
good_devices = False
126-
wait_time = time.monotonic() + 10 # wait up to 20 seconds for a good device to be found
127-
while not good_devices and time.monotonic() < wait_time:
128-
for device in usb.core.find(find_all=True):
129-
if device.manufacturer is not None:
130-
good_devices = True
131-
break
132-
# scan for connected USB devices
133-
for device in usb.core.find(find_all=True):
134-
# print information about the found devices
135-
print(f"{device.idVendor:04x}:{device.idProduct:04x}")
136-
print(device.manufacturer, device.product)
137-
print(device.serial_number)
138-
139-
mouse_intfc,mouse_endpt = adafruit_usb_host_descriptors.find_boot_mouse_endpoint(device)
140-
if (mouse_intfc is None or mouse_endpt is None):
141-
continue # Not a mouse device
142-
143-
# assume this device is the mouse
144-
mouse = device
145-
146-
# detach from kernel driver if active
147-
if mouse.is_kernel_driver_active(mouse_intfc):
148-
mouse.detach_kernel_driver(mouse_intfc)
149-
150-
# set the mouse configuration so it can be used
151-
mouse.set_configuration()
152-
153-
# Verify mouse works by reading from it
154-
buf = array.array("b", [0] * 4)
155-
try:
156-
# Try to read some data with a short timeout
157-
data = mouse.read(mouse_endpt, buf, timeout=100)
158-
print(f"Mouse test read successful: {data} bytes - {buf}")
159-
break
160-
except usb.core.USBTimeoutError:
161-
# Timeout is normal if mouse isn't moving
162-
print("Mouse connected but not sending data (normal)")
163-
break
164-
except Exception as e: # pylint: disable=broad-except
165-
print(f"Mouse test read failed: {e}")
166-
# Continue to try next device or retry
167-
mouse = None
168110

111+
# variable for the mouse USB device instance
112+
mouse = find_and_init_boot_mouse(cursor_image="bitmaps/mouse_cursor.bmp")
169113
if mouse is None:
170114
raise RuntimeError("No mouse found. Please connect a USB mouse.")
171115

116+
mouse.sensitivity = 4 # Slow the mouse down a bit
117+
mouse_tg = mouse.tilegrid
118+
mouse_tg.x = display.width // 2
119+
mouse_tg.y = display.height // 2
120+
121+
# add mouse graphic to main_group
122+
main_group.append(mouse_tg)
123+
172124
buf = array.array("b", [0] * 4)
173125
waiting_for_release = False
174126
left_button = right_button = False
175-
mouse_coords = (0, 0)
127+
mouse_coords = (display.width // 2, display.height // 2)
176128

177129
# Create the UI Elements (Ideally fit into 320x16 area)
178130
# Label for the Mines Left (Left of Center)
@@ -279,45 +231,47 @@ def hide_group(group):
279231

280232
menus = (reset_menu, difficulty_menu)
281233

234+
# Mouse state
235+
last_left_button_state = 0
236+
last_right_button_state = 0
237+
left_button_pressed = False
238+
right_button_pressed = False
239+
282240
# main loop
283241
while True:
284242
update_ui()
285-
# attempt mouse read
286-
try:
287-
# try to read data from the mouse, small timeout so the code will move on
288-
# quickly if there is no data
289-
while True:
290-
try:
291-
# read data from the mouse endpoint
292-
data_len = mouse.read(mouse_endpt, buf, timeout=10)
293-
if data_len > 0:
294-
break
295-
except usb.core.USBTimeoutError:
296-
# if we get a timeout error, it means there is no data available
297-
pass
298-
except usb.core.USBError as exc:
299-
# if we get a USBError, We may be getting no endpoint msgs which can be waited out
300-
pass
301-
302-
left_button = buf[0] & 0x01
303-
right_button = buf[0] & 0x02
304-
305-
# if there was data, then update the mouse cursor on the display
306-
# using min and max to keep it within the bounds of the display
307-
mouse_tg.x = max(0, min(display.width - 1, mouse_tg.x + buf[1] // 2))
308-
mouse_tg.y = max(0, min(display.height - 1, mouse_tg.y + buf[2] // 2))
309-
mouse_coords = (mouse_tg.x, mouse_tg.y)
310-
311-
if waiting_for_release and not left_button and not right_button:
312-
# If both buttons are released, we can process the next click
313-
waiting_for_release = False
314-
315-
# timeout error is raised if no data was read within the allotted timeout
316-
except usb.core.USBTimeoutError:
317-
# no problem, just go on
318-
pass
319-
except AttributeError as exc:
320-
raise RuntimeError("Mouse not found") from exc
243+
# update cursor position, and check for clicks
244+
buttons = mouse.update()
245+
246+
# Extract button states
247+
if buttons is None:
248+
left_button = 0
249+
right_button = 0
250+
else:
251+
left_button = 1 if 'left' in buttons else 0
252+
right_button = 1 if 'right' in buttons else 0
253+
254+
# Detect button presses
255+
if left_button == 1 and last_left_button_state == 0:
256+
left_button_pressed = True
257+
elif left_button == 0 and last_left_button_state == 1:
258+
left_button_pressed = False
259+
260+
if right_button == 1 and last_right_button_state == 0:
261+
right_button_pressed = True
262+
elif right_button == 0 and last_right_button_state == 1:
263+
right_button_pressed = False
264+
265+
# Update button states
266+
last_left_button_state = left_button
267+
last_right_button_state = right_button
268+
269+
mouse_coords = (mouse_tg.x, mouse_tg.y)
270+
271+
if waiting_for_release and not left_button and not right_button:
272+
# If both buttons are released, we can process the next click
273+
waiting_for_release = False
274+
321275
if not message_dialog.hidden:
322276
if message_button.handle_mouse(mouse_coords, left_button, waiting_for_release):
323277
waiting_for_release = True

0 commit comments

Comments
 (0)