diff --git a/Fruit_Jam/Fruit_Jam_PyPaint/code.py b/Fruit_Jam/Fruit_Jam_PyPaint/code.py index ffebdb230..684dec266 100644 --- a/Fruit_Jam/Fruit_Jam_PyPaint/code.py +++ b/Fruit_Jam/Fruit_Jam_PyPaint/code.py @@ -17,13 +17,17 @@ """ import gc +import sys import time +import atexit import supervisor import board import displayio +import terminalio +from adafruit_display_text import label try: - from adafruit_usb_host_mouse import find_and_init_boot_mouse + from adafruit_usb_host_mouse import find_and_init_boot_mouse, find_and_init_report_mouse usb_available = True except ImportError: usb_available = False @@ -208,13 +212,17 @@ def __init__(self, splash, cursor_bmp, screen_width, screen_height, sensitivity= def find_mouse(self): # pylint: disable=too-many-statements, too-many-locals """Find and initialize the USB mouse.""" self.mouse = find_and_init_boot_mouse() + if self.mouse is None: + self.mouse = find_and_init_report_mouse() + self.sensitivity = 1 if self.mouse is None: print("No mouse found.") return False # Change the mouse resolution so it's not too sensitive + fontHeight = terminalio.FONT.get_bounding_box()[1] self.mouse.display_size = (supervisor.runtime.display.width*self.sensitivity, - supervisor.runtime.display.height*self.sensitivity) + (supervisor.runtime.display.height - fontHeight)*self.sensitivity) return True def poll(self): @@ -324,12 +332,24 @@ def _cursor_bitmap_3(): self._display = display self._w = self._display.width - self._h = self._display.height + self._h = self._display.height - terminalio.FONT.get_bounding_box()[1] self._x = self._w // 2 self._y = self._h // 2 self._splash = displayio.Group() + self._info_label = label.Label( + terminalio.FONT, + text = "Right Click->Palette:Exit Right Click->Canvas:Fill"[:self._w], + color = 0xFFFFFF, + x = 0, + y = self._h + ) + self._info_label.anchor_point = (0.0, 1.0) + self._info_label.anchored_position = (2, display.height - 2) + + self._splash.append(self._info_label) + self._bg_bitmap = displayio.Bitmap(self._w, self._h, 1) self._bg_palette = displayio.Palette(1) self._bg_palette[0] = Color.BLACK @@ -377,6 +397,7 @@ def _cursor_bitmap_3(): self._brush = 0 self._cursor_bitmaps = [_cursor_bitmap_1(), _cursor_bitmap_3()] + self.mouse = None if hasattr(board, "TOUCH_XL"): self._poller = TouchscreenPoller(self._splash, self._cursor_bitmaps[0]) elif hasattr(board, "BUTTON_CLOCK"): @@ -385,8 +406,9 @@ def _cursor_bitmap_3(): self._poller = MousePoller(self._splash, self._cursor_bitmaps[0], self._w, self._h) if not self._poller.mouse: raise RuntimeError("No mouse found. Please connect a USB mouse.") + self.mouse = self._poller.mouse else: - raise AttributeError("PyPaint requires a touchscreen or cursor.") + raise AttributeError("PyPaint requires a mouse, touchscreen or cursor.") self._a_pressed = False self._last_a_pressed = False @@ -603,6 +625,8 @@ def _handle_b_release(self, location): if location[0] >= self._w // 10: # not in color picker self._fill(location[0], location[1], self._pencolor) self._poller.poke() + else: + supervisor.reload() @property def _was_a_just_pressed(self): @@ -646,6 +670,23 @@ def run(self): self._handle_motion(self._last_location, self._location) time.sleep(0.1) - painter = Paint() + +def atexit_callback(): + """ + re-attach USB devices to kernel if needed. + :return: + """ + print("inside atexit callback") + if painter.mouse is not None: + mouse = painter.mouse + if mouse.was_attached and not mouse.device.is_kernel_driver_active(0): + mouse.device.attach_kernel_driver(0) + # The keyboard buffer seems to have data left over from when it was detached + # This clears it before the next process starts + while supervisor.runtime.serial_bytes_available: + sys.stdin.read(1) + +atexit.register(atexit_callback) + painter.run() diff --git a/Metro/Metro_RP2350_Memory/memory_game/code.py b/Metro/Metro_RP2350_Memory/memory_game/code.py index b01b58bbf..175fb6982 100644 --- a/Metro/Metro_RP2350_Memory/memory_game/code.py +++ b/Metro/Metro_RP2350_Memory/memory_game/code.py @@ -7,7 +7,7 @@ Players trade off using the USB mouse to play their turns. """ -import array +import sys import random import time import atexit @@ -18,9 +18,8 @@ from adafruit_ticks import ticks_ms import supervisor import terminalio -import usb.core from adafruit_fruitjam.peripherals import request_display_config -import adafruit_usb_host_descriptors +from adafruit_usb_host_mouse import find_and_init_boot_mouse, find_and_init_report_mouse from adafruit_pathlib import Path @@ -223,6 +222,20 @@ def update_score_text(): # add the game over group to the main group main_group.append(game_over_group) +# add the Exit Game button to game screen +exit_game = TextBox( + terminalio.FONT, + text="Exit", + color=0xFFFFFF, + background_color=0xFF0000, + width=30, + height=15, + align=TextBox.ALIGN_CENTER, +) +exit_game.x = display.width - 30 +exit_game.y = display.height - 15 +main_group.append(exit_game) + # create score label for each player for i in range(2): # create a new label to hold score @@ -268,61 +281,28 @@ def update_score_text(): # add it to the main group main_group.append(title_screen_tg) -# load the mouse bitmap -mouse_bmp = OnDiskBitmap("mouse_cursor.bmp") - -# make the background pink pixels transparent -mouse_bmp.pixel_shader.make_transparent(0) - -# create a TileGrid for the mouse -mouse_tg = TileGrid(mouse_bmp, pixel_shader=mouse_bmp.pixel_shader) - -# place it in the center of the display -mouse_tg.x = display.width // 2 -mouse_tg.y = display.height // 2 - -# add the mouse to the main group -main_group.append(mouse_tg) - # variable for the mouse USB device instance mouse = None # wait a second for USB devices to be ready time.sleep(1) -mouse_interface_index, mouse_endpoint_address = None, None -mouse = None - # 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_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: - mouse = device - print( - f"mouse interface: {mouse_interface_index} " - + f"endpoint_address: {hex(mouse_endpoint_address)}" - ) - - break +mouse_ptr = find_and_init_boot_mouse("mouse_cursor.bmp") +if mouse_ptr is None: + mouse_ptr = find_and_init_report_mouse("mouse_cursor.bmp") +if mouse_ptr is None: + print("No mouse found.") +mouse = mouse_ptr.device +mouse_tg = mouse_ptr.tilegrid -mouse_was_attached = None -if mouse is not None: - # detach the kernel driver if needed - if mouse.is_kernel_driver_active(0): - mouse_was_attached = True - mouse.detach_kernel_driver(0) - else: - mouse_was_attached = False +# place it in the center of the display +mouse_tg.x = display.width // 2 +mouse_tg.y = display.height // 2 - # set configuration on the mouse so we can use it - mouse.set_configuration() +# add the mouse to the main group +main_group.append(mouse_tg) def atexit_callback(): """ @@ -330,16 +310,16 @@ def atexit_callback(): :return: """ print("inside atexit callback") - if mouse_was_attached and not mouse.is_kernel_driver_active(0): - mouse.attach_kernel_driver(0) - + if mouse is not None: + if mouse_ptr.was_attached and not mouse_ptr.device.is_kernel_driver_active(0): + mouse.attach_kernel_driver(0) + # The keyboard buffer seems to have data left over from when it was detached + # This clears it before the next process starts + while supervisor.runtime.serial_bytes_available: + sys.stdin.read(1) atexit.register(atexit_callback) -# Buffer to hold data read from the mouse -# Boot mice have 4 byte reports -buf = array.array("b", [0] * 4) - # timestamp in the future to wait until before # awarding points for a pair, or flipping cards # back over and changing turns @@ -352,36 +332,40 @@ def atexit_callback(): waiting_to_reset = False # main loop +last_left_button_state = None +left_button_pressed = False while True: # timestamp of the current time now = ticks_ms() # 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 - data_len = mouse.read(mouse_endpoint_address, buf, timeout=20) + buttons = mouse_ptr.update() + + # Extract button states + if buttons is None or last_left_button_state is None: + current_left_button_state = 0 + else: + current_left_button_state = 1 if 'left' in buttons else 0 - # 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)) + # Detect button presses + if current_left_button_state == 1 and last_left_button_state == 0: + left_button_pressed = True + elif current_left_button_state == 0 and last_left_button_state == 1: + left_button_pressed = False - # timeout error is raised if no data was read within the allotted timeout - except usb.core.USBTimeoutError: - # no problem, just go on - pass + # Update button states + last_left_button_state = current_left_button_state # if the current state is title screen if CUR_STATE == STATE_TITLE: # if the left mouse button was clicked - if buf[0] & (1 << 0) != 0: + if left_button_pressed: # change the current state to playing CUR_STATE = STATE_PLAYING # hide the title screen title_screen_tg.hidden = True # change the mouse cursor color to match the current player - mouse_bmp.pixel_shader[2] = colors[current_turn_index] + mouse_tg.pixel_shader[2] = colors[current_turn_index] # if the current state is playing elif CUR_STATE == STATE_PLAYING: @@ -455,7 +439,7 @@ def atexit_callback(): current_player_lbl.color = colors[current_turn_index] # update the color of the mouse cursor - mouse_bmp.pixel_shader[2] = colors[current_turn_index] + mouse_tg.pixel_shader[2] = colors[current_turn_index] # empty out the cards flipped this turn list cards_flipped_this_turn = [] @@ -463,7 +447,12 @@ def atexit_callback(): # ignore any clicks while the code is waiting to take reset cards if now >= WAIT_UNTIL: # left btn pressed - if buf[0] & (1 << 0) != 0: + if left_button_pressed: + # if the mouse point is within the exit button + if (mouse_tg.x >= display.width - 30 and + mouse_tg.y >= display.height - 15): + # restart back to code.py + supervisor.reload() # loop over all cards for card_index, card in enumerate(card_tgs): @@ -491,7 +480,7 @@ def atexit_callback(): # if the current state is gameover elif CUR_STATE == STATE_GAMEOVER: # left btn pressed - if buf[0] & (1 << 0) != 0: + if left_button_pressed: # get the coordinates of the mouse cursor point coords = (mouse_tg.x, mouse_tg.y, 0) @@ -500,7 +489,7 @@ def atexit_callback(): if play_again_btn.contains(coords): # set next code file to this one supervisor.set_next_code_file(__file__, - working_directory=Path(__file__).parent.absolute()) + working_directory=Path(__file__).parent.absolute()) # reload supervisor.reload()