From 2f8e751e222158efd725736be8f268888b9816ec Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 10 Sep 2025 22:18:08 -0400 Subject: [PATCH 1/4] Refactor for USB mouse library and new core USB errors --- Metro/Metro_RP2350_Minesweeper/code.py | 140 ++++++++++--------------- 1 file changed, 54 insertions(+), 86 deletions(-) diff --git a/Metro/Metro_RP2350_Minesweeper/code.py b/Metro/Metro_RP2350_Minesweeper/code.py index 4816e7e83..fd9eeac34 100644 --- a/Metro/Metro_RP2350_Minesweeper/code.py +++ b/Metro/Metro_RP2350_Minesweeper/code.py @@ -17,7 +17,7 @@ from eventbutton import EventButton import supervisor import terminalio -import usb.core +from adafruit_usb_host_mouse import find_and_init_boot_mouse from gamelogic import GameLogic, BLANK, INFO_BAR_HEIGHT, DIFFICULTIES from menu import Menu, SubMenu @@ -117,62 +117,14 @@ def update_ui(): elapsed_time_label.text = f"Time: {game_logic.elapsed_time}" # variable for the mouse USB device instance -mouse = None - -# wait a second for USB devices to be ready -time.sleep(1) - -good_devices = False -wait_time = time.monotonic() + 10 # wait up to 20 seconds for a good device to be found -while not good_devices and time.monotonic() < wait_time: - for device in usb.core.find(find_all=True): - if device.manufacturer is not None: - good_devices = True - break -# scan for connected USB devices -for device in usb.core.find(find_all=True): - # print information about the found devices - print(f"{device.idVendor:04x}:{device.idProduct:04x}") - print(device.manufacturer, device.product) - print(device.serial_number) - - mouse_intfc,mouse_endpt = adafruit_usb_host_descriptors.find_boot_mouse_endpoint(device) - if (mouse_intfc is None or mouse_endpt is None): - continue # Not a mouse device - - # assume this device is the mouse - mouse = device - - # detach from kernel driver if active - if mouse.is_kernel_driver_active(mouse_intfc): - mouse.detach_kernel_driver(mouse_intfc) - - # set the mouse configuration so it can be used - mouse.set_configuration() - - # Verify mouse works by reading from it - buf = array.array("b", [0] * 4) - try: - # Try to read some data with a short timeout - data = mouse.read(mouse_endpt, buf, timeout=100) - print(f"Mouse test read successful: {data} bytes - {buf}") - break - except usb.core.USBTimeoutError: - # Timeout is normal if mouse isn't moving - print("Mouse connected but not sending data (normal)") - break - except Exception as e: # pylint: disable=broad-except - print(f"Mouse test read failed: {e}") - # Continue to try next device or retry - mouse = None - +mouse = find_and_init_boot_mouse() if mouse is None: raise RuntimeError("No mouse found. Please connect a USB mouse.") buf = array.array("b", [0] * 4) waiting_for_release = False left_button = right_button = False -mouse_coords = (0, 0) +mouse_coords = (display.width // 2, display.height // 2) # Create the UI Elements (Ideally fit into 320x16 area) # Label for the Mines Left (Left of Center) @@ -279,45 +231,61 @@ def hide_group(group): menus = (reset_menu, difficulty_menu) +# Mouse state +last_left_button_state = 0 +last_right_button_state = 0 +left_button_pressed = False +right_button_pressed = False + +# Mouse resolution/sensitivity +sensitivity = 4 +mouse.x = mouse_coords[0] * sensitivity +mouse.y = mouse_coords[1] * sensitivity + +# Change the mouse resolution so it's not too sensitive +mouse.display_size = (display.width*sensitivity, display.height*sensitivity) + # main loop while True: update_ui() # attempt mouse read - try: - # try to read data from the mouse, small timeout so the code will move on - # quickly if there is no data - while True: - try: - # read data from the mouse endpoint - data_len = mouse.read(mouse_endpt, buf, timeout=10) - if data_len > 0: - break - except usb.core.USBTimeoutError: - # if we get a timeout error, it means there is no data available - pass - except usb.core.USBError as exc: - # if we get a USBError, We may be getting no endpoint msgs which can be waited out - pass - - left_button = buf[0] & 0x01 - right_button = buf[0] & 0x02 - - # if there was data, then update the mouse cursor on the display - # using min and max to keep it within the bounds of the display - mouse_tg.x = max(0, min(display.width - 1, mouse_tg.x + buf[1] // 2)) - mouse_tg.y = max(0, min(display.height - 1, mouse_tg.y + buf[2] // 2)) - mouse_coords = (mouse_tg.x, mouse_tg.y) - - if waiting_for_release and not left_button and not right_button: - # If both buttons are released, we can process the next click - waiting_for_release = False - - # timeout error is raised if no data was read within the allotted timeout - except usb.core.USBTimeoutError: - # no problem, just go on - pass - except AttributeError as exc: - raise RuntimeError("Mouse not found") from exc + buttons = mouse.update() + + # Extract button states + if buttons is None: + left_button = 0 + right_button = 0 + else: + print(buttons) + left_button = 1 if 'left' in buttons else 0 + right_button = 1 if 'right' in buttons else 0 + + # Detect button presses + if left_button == 1 and last_left_button_state == 0: + left_button_pressed = True + elif left_button == 0 and last_left_button_state == 1: + left_button_pressed = False + + if right_button == 1 and last_right_button_state == 0: + right_button_pressed = True + elif right_button == 0 and last_right_button_state == 1: + right_button_pressed = False + + # Update button states + last_left_button_state = left_button + last_right_button_state = right_button + + # Update position + # Ensure position stays within bounds + mouse_tg.x = max(0, min(display.width - 1, mouse.x // sensitivity)) + mouse_tg.y = max(0, min(display.height - 1, mouse.y // sensitivity)) + + mouse_coords = (mouse_tg.x, mouse_tg.y) + + if waiting_for_release and not left_button and not right_button: + # If both buttons are released, we can process the next click + waiting_for_release = False + if not message_dialog.hidden: if message_button.handle_mouse(mouse_coords, left_button, waiting_for_release): waiting_for_release = True From 2b5764a6687738d53fb76aa7d30edeb05e093787 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 10 Sep 2025 22:32:38 -0400 Subject: [PATCH 2/4] drop unneded imports --- Metro/Metro_RP2350_Minesweeper/code.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Metro/Metro_RP2350_Minesweeper/code.py b/Metro/Metro_RP2350_Minesweeper/code.py index fd9eeac34..983994b86 100644 --- a/Metro/Metro_RP2350_Minesweeper/code.py +++ b/Metro/Metro_RP2350_Minesweeper/code.py @@ -9,11 +9,9 @@ the player successfully reveals all squares without mines or clicks on a mine. """ import array -import time from displayio import Group, OnDiskBitmap, TileGrid, Bitmap, Palette from adafruit_display_text.bitmap_label import Label from adafruit_display_text.text_box import TextBox -import adafruit_usb_host_descriptors from eventbutton import EventButton import supervisor import terminalio From 9aee497aa3259a187478ff7fe889724c09e4eaf9 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Thu, 18 Sep 2025 17:58:15 -0400 Subject: [PATCH 3/4] better use of host_mouse library --- Metro/Metro_RP2350_Minesweeper/code.py | 39 ++++++++++---------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/Metro/Metro_RP2350_Minesweeper/code.py b/Metro/Metro_RP2350_Minesweeper/code.py index 983994b86..ab403fd91 100644 --- a/Metro/Metro_RP2350_Minesweeper/code.py +++ b/Metro/Metro_RP2350_Minesweeper/code.py @@ -47,6 +47,7 @@ color_depth=16, ) display = framebufferio.FramebufferDisplay(fb) + supervisor.runtime.display = display game_logic = GameLogic(display) # pylint: disable=no-value-for-parameter @@ -74,14 +75,6 @@ ui_group = Group() main_group.append(ui_group) -# Create the mouse graphics and add to the main group -mouse_bmp = OnDiskBitmap("bitmaps/mouse_cursor.bmp") -mouse_bmp.pixel_shader.make_transparent(0) -mouse_tg = TileGrid(mouse_bmp, pixel_shader=mouse_bmp.pixel_shader) -mouse_tg.x = display.width // 2 -mouse_tg.y = display.height // 2 -main_group.append(mouse_tg) - MENU_ITEM_HEIGHT = INFO_BAR_HEIGHT def create_game_board(): @@ -114,11 +107,23 @@ def update_ui(): mines_left_label.text = f"Mines: {game_logic.mines_left}" elapsed_time_label.text = f"Time: {game_logic.elapsed_time}" + +# Mouse resolution/sensitivity +sensitivity = 4 + # variable for the mouse USB device instance -mouse = find_and_init_boot_mouse() +mouse = find_and_init_boot_mouse(cursor_image="bitmaps/mouse_cursor.bmp") if mouse is None: raise RuntimeError("No mouse found. Please connect a USB mouse.") +mouse.sensitivity = sensitivity +mouse_tg = mouse.tilegrid +mouse_tg.x = display.width // 2 +mouse_tg.y = display.height // 2 + +# add mouse graphic to main_group +main_group.append(mouse_tg) + buf = array.array("b", [0] * 4) waiting_for_release = False left_button = right_button = False @@ -235,18 +240,10 @@ def hide_group(group): left_button_pressed = False right_button_pressed = False -# Mouse resolution/sensitivity -sensitivity = 4 -mouse.x = mouse_coords[0] * sensitivity -mouse.y = mouse_coords[1] * sensitivity - -# Change the mouse resolution so it's not too sensitive -mouse.display_size = (display.width*sensitivity, display.height*sensitivity) - # main loop while True: update_ui() - # attempt mouse read + # update cursor position, and check for clicks buttons = mouse.update() # Extract button states @@ -254,7 +251,6 @@ def hide_group(group): left_button = 0 right_button = 0 else: - print(buttons) left_button = 1 if 'left' in buttons else 0 right_button = 1 if 'right' in buttons else 0 @@ -273,11 +269,6 @@ def hide_group(group): last_left_button_state = left_button last_right_button_state = right_button - # Update position - # Ensure position stays within bounds - mouse_tg.x = max(0, min(display.width - 1, mouse.x // sensitivity)) - mouse_tg.y = max(0, min(display.height - 1, mouse.y // sensitivity)) - mouse_coords = (mouse_tg.x, mouse_tg.y) if waiting_for_release and not left_button and not right_button: From cf5b48b6e2ada8b49f827d996143035dd983e269 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Thu, 18 Sep 2025 21:24:47 -0400 Subject: [PATCH 4/4] sensitivity variable not really needed --- Metro/Metro_RP2350_Minesweeper/code.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Metro/Metro_RP2350_Minesweeper/code.py b/Metro/Metro_RP2350_Minesweeper/code.py index ab403fd91..5a45b2f6f 100644 --- a/Metro/Metro_RP2350_Minesweeper/code.py +++ b/Metro/Metro_RP2350_Minesweeper/code.py @@ -108,15 +108,12 @@ def update_ui(): elapsed_time_label.text = f"Time: {game_logic.elapsed_time}" -# Mouse resolution/sensitivity -sensitivity = 4 - # variable for the mouse USB device instance mouse = find_and_init_boot_mouse(cursor_image="bitmaps/mouse_cursor.bmp") if mouse is None: raise RuntimeError("No mouse found. Please connect a USB mouse.") -mouse.sensitivity = sensitivity +mouse.sensitivity = 4 # Slow the mouse down a bit mouse_tg = mouse.tilegrid mouse_tg.x = display.width // 2 mouse_tg.y = display.height // 2