From 5b6d657dd08abf39cf0109e73e6a9f72c4c27571 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 1 Dec 2025 18:15:01 -0500 Subject: [PATCH 1/7] Trackpad support --- Metro/Metro_RP2350_Match3/match3_game/code.py | 79 ++++++++++++++++--- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/Metro/Metro_RP2350_Match3/match3_game/code.py b/Metro/Metro_RP2350_Match3/match3_game/code.py index 7f71ebf14..13c31ee63 100644 --- a/Metro/Metro_RP2350_Match3/match3_game/code.py +++ b/Metro/Metro_RP2350_Match3/match3_game/code.py @@ -185,17 +185,15 @@ # transparent pixels in the corners for the rounded corner effect exit_btn_bmp.pixel_shader.make_transparent(0) -# centered within the display, offset to the right -exit_btn.x = display.width // scale_factor // 2 - (exit_btn_bmp.width) // 2 + 30 - -# inside the bounds of the game over label, so it looks like a dialog visually -exit_btn.y = 100 - # add the play again and exit buttons to the game over group game_over_group.append(play_again_btn) -game_over_group.append(exit_btn) main_group.append(game_over_group) +# Along right border +exit_btn.x = display.width // scale_factor - (exit_btn_bmp.width) +exit_btn.y = 100 +main_group.append(exit_btn) + # wait a second for USB devices to be ready time.sleep(1) @@ -289,6 +287,51 @@ # The mouse might have glitched and may not be detected but at least we don't crash print(e) + if len(mice) >= 2: + break + +if len(mice) < 2: + for device in usb.core.find(find_all=True): + if device in mice: + print('found device twice') + continue + # check if current device is has a boot mouse endpoint + try: + mouse_interface_index, mouse_endpoint_address = ( + adafruit_usb_host_descriptors.find_report_mouse_endpoint(device) + ) + if mouse_interface_index is not None and mouse_endpoint_address is not None: + if mouse_interface_index in mouse_interface_indexes and mouse_endpoint_address in mouse_endpoint_addresses: + print('found index/address twice') + continue + # if it does have a boot mouse endpoint then add information to the + # usb info lists + mouse_interface_indexes.append(mouse_interface_index) + mouse_endpoint_addresses.append(mouse_endpoint_address) + + # add the mouse device instance to list + mice.append(device) + print( + f"mouse interface: {mouse_interface_index} " + + f"endpoint_address: {hex(mouse_endpoint_address)}" + ) + mouse_sync.append(-1) + + # detach kernel driver if needed + kernel_driver_active_flags.append(device.is_kernel_driver_active(0)) + if device.is_kernel_driver_active(0): + device.detach_kernel_driver(0) + + # set the mouse configuration so it can be used + device.set_configuration() + + except usb.core.USBError as e: + # The mouse might have glitched and may not be detected but at least we don't crash + print(e) + + if len(mice) >= 2: + break + def is_mouse1_left_clicked(): """ Check if mouse 1 left click is pressed @@ -356,7 +399,11 @@ def get_mouse_deltas(buffer, read_count, sync): :param read_count: the number of bytes read from the mouse :return: tuple containing x and y delta values """ - if read_count == 4 or (read_count == 8 and sync > 50): + + if read_count == 6 and sync == -1: + delta_x = buffer[2] + delta_y = buffer[3] + elif read_count == 4 or (read_count == 8 and sync > 50): delta_x = buffer[1] delta_y = buffer[2] elif read_count == 8: @@ -407,6 +454,8 @@ def atexit_callback(): ) mouse_deltas = get_mouse_deltas(mouse_bufs[i], data_len, mouse_sync[i]) mouse_sync[i] = mouse_deltas[2] + if mouse_sync[i] == -1: + mouse_bufs[i][0] = mouse_bufs[i][1] # if we got 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( @@ -445,6 +494,11 @@ def atexit_callback(): # get the current mouse coordinates coords = (mouse_tg.x, mouse_tg.y, 0) + # if the mouse point is within the exit + # button bounding box + if exit_btn.contains(coords): + supervisor.reload() + # if the current state is GAMEOVER if match3_game.cur_state != STATE_GAMEOVER: # let the game object handle the click event @@ -458,11 +512,6 @@ def atexit_callback(): # reload supervisor.reload() - # if the mouse point is within the exit - # button bounding box - if exit_btn.contains(coords): - supervisor.reload() - # if the game is over except GameOverException: # check for a winner @@ -483,6 +532,10 @@ def atexit_callback(): # show a tie game message message = "\nGame Over\nTie Game Everyone Wins!" + # centered within the display, offset to the right + # inside the bounds of the game over label, so it looks like a dialog visually + exit_btn.x = display.width // scale_factor // 2 - (exit_btn_bmp.width) // 2 + 30 + exit_btn.y = 100 # make the gameover group visible game_over_group.hidden = False From 800d71cfeacb995794ad30e074c3f72a57480242 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 1 Dec 2025 22:12:37 -0500 Subject: [PATCH 2/7] kernel driver should be indexied by mouse_interface --- Metro/Metro_RP2350_Match3/match3_game/code.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Metro/Metro_RP2350_Match3/match3_game/code.py b/Metro/Metro_RP2350_Match3/match3_game/code.py index 13c31ee63..fbf55b7b0 100644 --- a/Metro/Metro_RP2350_Match3/match3_game/code.py +++ b/Metro/Metro_RP2350_Match3/match3_game/code.py @@ -276,9 +276,11 @@ mouse_sync.append(0) # detach kernel driver if needed - kernel_driver_active_flags.append(device.is_kernel_driver_active(0)) - if device.is_kernel_driver_active(0): - device.detach_kernel_driver(0) + kernel_driver_active_flags.append( + device.is_kernel_driver_active(mouse_interface_index) + ) + if device.is_kernel_driver_active(mouse_interface_index): + device.detach_kernel_driver(mouse_interface_index) # set the mouse configuration so it can be used device.set_configuration() @@ -318,9 +320,11 @@ mouse_sync.append(-1) # detach kernel driver if needed - kernel_driver_active_flags.append(device.is_kernel_driver_active(0)) - if device.is_kernel_driver_active(0): - device.detach_kernel_driver(0) + kernel_driver_active_flags.append( + device.is_kernel_driver_active(mouse_interface_index) + ) + if device.is_kernel_driver_active(mouse_interface_index): + device.detach_kernel_driver(mouse_interface_index) # set the mouse configuration so it can be used device.set_configuration() @@ -426,8 +430,8 @@ def atexit_callback(): """ for _i, _mouse in enumerate(mice): if kernel_driver_active_flags[_i]: - if not _mouse.is_kernel_driver_active(0): - _mouse.attach_kernel_driver(0) + if not _mouse.is_kernel_driver_active(mouse_interface_indexes[_i]): + _mouse.attach_kernel_driver(mouse_interface_indexes[_i]) supervisor.runtime.autoreload = original_autoreload_val From 5273772200b01dc19ca40b0e95f4f891fe2b4b95 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 2 Dec 2025 01:40:28 -0500 Subject: [PATCH 3/7] additional informational prints --- Metro/Metro_RP2350_Match3/match3_game/code.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Metro/Metro_RP2350_Match3/match3_game/code.py b/Metro/Metro_RP2350_Match3/match3_game/code.py index fbf55b7b0..606c3ba39 100644 --- a/Metro/Metro_RP2350_Match3/match3_game/code.py +++ b/Metro/Metro_RP2350_Match3/match3_game/code.py @@ -432,6 +432,11 @@ def atexit_callback(): if kernel_driver_active_flags[_i]: if not _mouse.is_kernel_driver_active(mouse_interface_indexes[_i]): _mouse.attach_kernel_driver(mouse_interface_indexes[_i]) + print(f'#{_i} Index: {mouse_interface_indexes[_i]} (reattaching)') + else: + print(f'#{_i} Index: {mouse_interface_indexes[_i]} (Not Attaching)') + else: + print(f'#{_i} Index: {mouse_interface_indexes[_i]} kernel not active (was not attached)') supervisor.runtime.autoreload = original_autoreload_val From 95181ebcecaa6d75228dbd1afb5ffe5dfc2a525b Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 2 Dec 2025 04:22:12 -0500 Subject: [PATCH 4/7] update for multi interface detach --- Metro/Metro_RP2350_Match3/match3_game/code.py | 93 +++++++------------ 1 file changed, 36 insertions(+), 57 deletions(-) diff --git a/Metro/Metro_RP2350_Match3/match3_game/code.py b/Metro/Metro_RP2350_Match3/match3_game/code.py index 606c3ba39..58185f682 100644 --- a/Metro/Metro_RP2350_Match3/match3_game/code.py +++ b/Metro/Metro_RP2350_Match3/match3_game/code.py @@ -245,7 +245,7 @@ # USB info lists mouse_interface_indexes = [] mouse_endpoint_addresses = [] -kernel_driver_active_flags = [] +detached_interfaces = [] # USB device object instance list mice = [] # buffers list for mouse packet data @@ -255,58 +255,23 @@ mouse_sync = [] # scan for connected USB devices -for device in usb.core.find(find_all=True): - # check if current device is has a boot mouse endpoint - try: - mouse_interface_index, mouse_endpoint_address = ( - adafruit_usb_host_descriptors.find_boot_mouse_endpoint(device) - ) - if mouse_interface_index is not None and mouse_endpoint_address is not None: - # if it does have a boot mouse endpoint then add information to the - # usb info lists - mouse_interface_indexes.append(mouse_interface_index) - mouse_endpoint_addresses.append(mouse_endpoint_address) - - # add the mouse device instance to list - mice.append(device) - print( - f"mouse interface: {mouse_interface_index} " - + f"endpoint_address: {hex(mouse_endpoint_address)}" - ) - mouse_sync.append(0) - - # detach kernel driver if needed - kernel_driver_active_flags.append( - device.is_kernel_driver_active(mouse_interface_index) - ) - if device.is_kernel_driver_active(mouse_interface_index): - device.detach_kernel_driver(mouse_interface_index) - - # set the mouse configuration so it can be used - device.set_configuration() +for find_endpoint, default_sync in [ + (adafruit_usb_host_descriptors.find_boot_mouse_endpoint, 0), + (adafruit_usb_host_descriptors.find_report_mouse_endpoint, -1) +]: - except usb.core.USBError as e: - # The mouse might have glitched and may not be detected but at least we don't crash - print(e) - - if len(mice) >= 2: - break - -if len(mice) < 2: for device in usb.core.find(find_all=True): if device in mice: print('found device twice') continue # check if current device is has a boot mouse endpoint try: - mouse_interface_index, mouse_endpoint_address = ( - adafruit_usb_host_descriptors.find_report_mouse_endpoint(device) - ) + mouse_interface_index, mouse_endpoint_address = (find_endpoint(device)) if mouse_interface_index is not None and mouse_endpoint_address is not None: if mouse_interface_index in mouse_interface_indexes and mouse_endpoint_address in mouse_endpoint_addresses: print('found index/address twice') continue - # if it does have a boot mouse endpoint then add information to the + # if it does have a mouse endpoint then add information to the # usb info lists mouse_interface_indexes.append(mouse_interface_index) mouse_endpoint_addresses.append(mouse_endpoint_address) @@ -314,17 +279,26 @@ # add the mouse device instance to list mice.append(device) print( - f"mouse interface: {mouse_interface_index} " + f"{default_sync} mouse interface: {mouse_interface_index} " + f"endpoint_address: {hex(mouse_endpoint_address)}" ) - mouse_sync.append(-1) + mouse_sync.append(default_sync) # detach kernel driver if needed - kernel_driver_active_flags.append( - device.is_kernel_driver_active(mouse_interface_index) - ) - if device.is_kernel_driver_active(mouse_interface_index): - device.detach_kernel_driver(mouse_interface_index) + detached = [] + + # Typically HID devices have interfaces 0,1,2 + # Trying 0..mouse_iface is safe and sufficient + for intf in range(mouse_interface_index + 1): + try: + if device.is_kernel_driver_active(intf): + device.detach_kernel_driver(intf) + detached.append(intf) + print(f"Detached kernel driver from interface {intf}") + except usb.core.USBError as e: + print(e) + + detached_interfaces.append(detached) # set the mouse configuration so it can be used device.set_configuration() @@ -336,6 +310,9 @@ if len(mice) >= 2: break + if len(mice) >= 2: + break + def is_mouse1_left_clicked(): """ Check if mouse 1 left click is pressed @@ -429,14 +406,16 @@ def atexit_callback(): :return: """ for _i, _mouse in enumerate(mice): - if kernel_driver_active_flags[_i]: - if not _mouse.is_kernel_driver_active(mouse_interface_indexes[_i]): - _mouse.attach_kernel_driver(mouse_interface_indexes[_i]) - print(f'#{_i} Index: {mouse_interface_indexes[_i]} (reattaching)') - else: - print(f'#{_i} Index: {mouse_interface_indexes[_i]} (Not Attaching)') - else: - print(f'#{_i} Index: {mouse_interface_indexes[_i]} kernel not active (was not attached)') + detached_from_device = detached_interfaces[_i] + + if detached_from_device: + for intf in detached_from_device: + if not _mouse.is_kernel_driver_active(intf): + _mouse.attach_kernel_driver(intf) + print(f'#{_i} Index: {intf} (reattaching)') + else: + print(f'#{_i} Index: {intf} (Not Attaching)') + supervisor.runtime.autoreload = original_autoreload_val From 67fadb55e5dea00e7e7f277bfe158d4621b8ed74 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 2 Dec 2025 04:42:19 -0500 Subject: [PATCH 5/7] pylint fixes --- Metro/Metro_RP2350_Match3/match3_game/code.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Metro/Metro_RP2350_Match3/match3_game/code.py b/Metro/Metro_RP2350_Match3/match3_game/code.py index 58185f682..916edaef9 100644 --- a/Metro/Metro_RP2350_Match3/match3_game/code.py +++ b/Metro/Metro_RP2350_Match3/match3_game/code.py @@ -268,7 +268,10 @@ try: mouse_interface_index, mouse_endpoint_address = (find_endpoint(device)) if mouse_interface_index is not None and mouse_endpoint_address is not None: - if mouse_interface_index in mouse_interface_indexes and mouse_endpoint_address in mouse_endpoint_addresses: + if ( + mouse_interface_index in mouse_interface_indexes and + mouse_endpoint_address in mouse_endpoint_addresses + ): print('found index/address twice') continue # if it does have a mouse endpoint then add information to the @@ -409,12 +412,12 @@ def atexit_callback(): detached_from_device = detached_interfaces[_i] if detached_from_device: - for intf in detached_from_device: - if not _mouse.is_kernel_driver_active(intf): - _mouse.attach_kernel_driver(intf) - print(f'#{_i} Index: {intf} (reattaching)') + for _intf in detached_from_device: + if not _mouse.is_kernel_driver_active(_intf): + _mouse.attach_kernel_driver(_intf) + print(f'#{_i} Index: {_intf} (reattaching)') else: - print(f'#{_i} Index: {intf} (Not Attaching)') + print(f'#{_i} Index: {_intf} (Not Attaching)') supervisor.runtime.autoreload = original_autoreload_val From 443647c9bb1ff104055f26698d0eec037c357c42 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 2 Dec 2025 17:36:01 -0500 Subject: [PATCH 6/7] Check first 3 interface for report mice, fix crash on missing resume file --- Metro/Metro_RP2350_Match3/match3_game/code.py | 11 ++++++++--- .../match3_game/match3_game_helpers.py | 13 +++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Metro/Metro_RP2350_Match3/match3_game/code.py b/Metro/Metro_RP2350_Match3/match3_game/code.py index 916edaef9..fe01d39b3 100644 --- a/Metro/Metro_RP2350_Match3/match3_game/code.py +++ b/Metro/Metro_RP2350_Match3/match3_game/code.py @@ -291,20 +291,22 @@ detached = [] # Typically HID devices have interfaces 0,1,2 - # Trying 0..mouse_iface is safe and sufficient - for intf in range(mouse_interface_index + 1): + for intf in range(3): + print(f'interface: {intf} ',end="") try: if device.is_kernel_driver_active(intf): device.detach_kernel_driver(intf) detached.append(intf) print(f"Detached kernel driver from interface {intf}") + else: + print("not active") except usb.core.USBError as e: print(e) detached_interfaces.append(detached) # set the mouse configuration so it can be used - device.set_configuration() + #device.set_configuration() except usb.core.USBError as e: # The mouse might have glitched and may not be detected but at least we don't crash @@ -316,6 +318,9 @@ if len(mice) >= 2: break +for device in mice: + device.set_configuration() + def is_mouse1_left_clicked(): """ Check if mouse 1 left click is pressed diff --git a/Metro/Metro_RP2350_Match3/match3_game/match3_game_helpers.py b/Metro/Metro_RP2350_Match3/match3_game/match3_game_helpers.py index bbd5b9dbb..689155588 100644 --- a/Metro/Metro_RP2350_Match3/match3_game/match3_game_helpers.py +++ b/Metro/Metro_RP2350_Match3/match3_game/match3_game_helpers.py @@ -663,12 +663,13 @@ def handle_left_click(self, player_index, coords): and self.title_screen.resume_btn.contains(coords) ): - # load the game from the given game state - self.load_from_game_state(self.game_state) - # hide the title screen - self.title_screen.hidden = True - # set the current state to open play - self.cur_state = STATE_PLAYING_OPEN + if self.game_state is not None: + # load the game from the given game state + self.load_from_game_state(self.game_state) + # hide the title screen + self.title_screen.hidden = True + # set the current state to open play + self.cur_state = STATE_PLAYING_OPEN # if the new game button was clicked elif self.title_screen.new_game_btn.contains(coords): From cee3e990d958e0350378a2061e84266e783ba372 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Fri, 5 Dec 2025 14:55:14 -0500 Subject: [PATCH 7/7] remove unused set_configuration command --- Metro/Metro_RP2350_Match3/match3_game/code.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Metro/Metro_RP2350_Match3/match3_game/code.py b/Metro/Metro_RP2350_Match3/match3_game/code.py index fe01d39b3..5220f18a5 100644 --- a/Metro/Metro_RP2350_Match3/match3_game/code.py +++ b/Metro/Metro_RP2350_Match3/match3_game/code.py @@ -305,9 +305,6 @@ detached_interfaces.append(detached) - # set the mouse configuration so it can be used - #device.set_configuration() - except usb.core.USBError as e: # The mouse might have glitched and may not be detected but at least we don't crash print(e) @@ -318,6 +315,7 @@ if len(mice) >= 2: break +# set the mouse configuration on any detected mice so they can be used for device in mice: device.set_configuration()