77
88Players trade off using the USB mouse to play their turns.
99"""
10- import array
10+ import sys
1111import random
1212import time
1313import atexit
14- from displayio import Group , OnDiskBitmap , TileGrid
14+ from displayio import Group , OnDiskBitmap , TileGrid , CIRCUITPYTHON_TERMINAL
1515from adafruit_display_text .bitmap_label import Label
1616from adafruit_display_text .text_box import TextBox
1717from adafruit_displayio_layout .layouts .grid_layout import GridLayout
1818from adafruit_ticks import ticks_ms
1919import supervisor
2020import terminalio
21- import usb .core
2221from adafruit_fruitjam .peripherals import request_display_config
23- import adafruit_usb_host_descriptors
22+ from adafruit_usb_host_mouse import find_and_init_boot_mouse , find_and_init_report_mouse
2423from adafruit_pathlib import Path
2524
2625
@@ -223,6 +222,20 @@ def update_score_text():
223222# add the game over group to the main group
224223main_group .append (game_over_group )
225224
225+ # add the Exit Game button to game screen
226+ exit_game = TextBox (
227+ terminalio .FONT ,
228+ text = "Exit" ,
229+ color = 0xFFFFFF ,
230+ background_color = 0xFF0000 ,
231+ width = 30 ,
232+ height = 15 ,
233+ align = TextBox .ALIGN_CENTER ,
234+ )
235+ exit_game .x = display .width - 30
236+ exit_game .y = display .height - 15
237+ main_group .append (exit_game )
238+
226239# create score label for each player
227240for i in range (2 ):
228241 # create a new label to hold score
@@ -268,78 +281,52 @@ def update_score_text():
268281# add it to the main group
269282main_group .append (title_screen_tg )
270283
271- # load the mouse bitmap
272- mouse_bmp = OnDiskBitmap ("mouse_cursor.bmp" )
273-
274- # make the background pink pixels transparent
275- mouse_bmp .pixel_shader .make_transparent (0 )
276-
277- # create a TileGrid for the mouse
278- mouse_tg = TileGrid (mouse_bmp , pixel_shader = mouse_bmp .pixel_shader )
279-
280- # place it in the center of the display
281- mouse_tg .x = display .width // 2
282- mouse_tg .y = display .height // 2
283-
284- # add the mouse to the main group
285- main_group .append (mouse_tg )
286-
287284# variable for the mouse USB device instance
288285mouse = None
289286
290287# wait a second for USB devices to be ready
291288time .sleep (1 )
292289
293- mouse_interface_index , mouse_endpoint_address = None , None
294- mouse = None
295-
296290# scan for connected USB devices
297- for device in usb .core .find (find_all = True ):
298- # print information about the found devices
299- print (f"{ device .idVendor :04x} :{ device .idProduct :04x} " )
300- print (device .manufacturer , device .product )
301- print (device .serial_number )
302- mouse_interface_index , mouse_endpoint_address = (
303- adafruit_usb_host_descriptors .find_boot_mouse_endpoint (device ))
304-
305- if mouse_interface_index is not None and mouse_endpoint_address is not None :
306- mouse = device
307- print (
308- f"mouse interface: { mouse_interface_index } "
309- + f"endpoint_address: { hex (mouse_endpoint_address )} "
310- )
311-
312- break
291+ mouse_ptr = find_and_init_boot_mouse ("mouse_cursor.bmp" )
292+ if mouse_ptr is None :
293+ mouse_ptr = find_and_init_report_mouse ("mouse_cursor.bmp" )
294+ if mouse_ptr is None :
295+ display .root_group = CIRCUITPYTHON_TERMINAL
296+ print ("\n No mouse found" )
297+ print ("Memory requires a mouse to run" )
298+ print ("please attach a mouse and try again." )
299+ time .sleep (7 )
300+ # restart back to code.py
301+ supervisor .reload ()
302+
303+ mouse = mouse_ptr .device
304+
305+ mouse_tg = mouse_ptr .tilegrid
313306
307+ # place it in the center of the display
308+ mouse_tg .x = display .width // 2
309+ mouse_tg .y = display .height // 2
314310
315- mouse_was_attached = None
316- if mouse is not None :
317- # detach the kernel driver if needed
318- if mouse .is_kernel_driver_active (0 ):
319- mouse_was_attached = True
320- mouse .detach_kernel_driver (0 )
321- else :
322- mouse_was_attached = False
323-
324- # set configuration on the mouse so we can use it
325- mouse .set_configuration ()
311+ # add the mouse to the main group
312+ main_group .append (mouse_tg )
326313
327314def atexit_callback ():
328315 """
329316 re-attach USB devices to kernel if needed.
330317 :return:
331318 """
332319 print ("inside atexit callback" )
333- if mouse_was_attached and not mouse .is_kernel_driver_active (0 ):
334- mouse .attach_kernel_driver (0 )
335-
320+ if mouse_ptr .device is not None :
321+ mouse_ptr .release ()
322+ if mouse_ptr .was_attached :
323+ # The keyboard buffer seems to have data left over from when it was detached
324+ # This clears it before the next process starts
325+ while supervisor .runtime .serial_bytes_available :
326+ sys .stdin .read (1 )
336327
337328atexit .register (atexit_callback )
338329
339- # Buffer to hold data read from the mouse
340- # Boot mice have 4 byte reports
341- buf = array .array ("b" , [0 ] * 4 )
342-
343330# timestamp in the future to wait until before
344331# awarding points for a pair, or flipping cards
345332# back over and changing turns
@@ -352,36 +339,40 @@ def atexit_callback():
352339waiting_to_reset = False
353340
354341# main loop
342+ last_left_button_state = None
343+ left_button_pressed = False
355344while True :
356345 # timestamp of the current time
357346 now = ticks_ms ()
358347
359348 # attempt mouse read
360- try :
361- # try to read data from the mouse, small timeout so the code will move on
362- # quickly if there is no data
363- data_len = mouse .read (mouse_endpoint_address , buf , timeout = 20 )
349+ buttons = mouse_ptr .update ()
350+
351+ # Extract button states
352+ if buttons is None or last_left_button_state is None :
353+ current_left_button_state = 0
354+ else :
355+ current_left_button_state = 1 if 'left' in buttons else 0
364356
365- # if there was data, then update the mouse cursor on the display
366- # using min and max to keep it within the bounds of the display
367- mouse_tg .x = max (0 , min (display .width - 1 , mouse_tg .x + buf [1 ] // 2 ))
368- mouse_tg .y = max (0 , min (display .height - 1 , mouse_tg .y + buf [2 ] // 2 ))
357+ # Detect button presses
358+ if current_left_button_state == 1 and last_left_button_state == 0 :
359+ left_button_pressed = True
360+ elif current_left_button_state == 0 and last_left_button_state == 1 :
361+ left_button_pressed = False
369362
370- # timeout error is raised if no data was read within the allotted timeout
371- except usb .core .USBTimeoutError :
372- # no problem, just go on
373- pass
363+ # Update button states
364+ last_left_button_state = current_left_button_state
374365
375366 # if the current state is title screen
376367 if CUR_STATE == STATE_TITLE :
377368 # if the left mouse button was clicked
378- if buf [ 0 ] & ( 1 << 0 ) != 0 :
369+ if left_button_pressed :
379370 # change the current state to playing
380371 CUR_STATE = STATE_PLAYING
381372 # hide the title screen
382373 title_screen_tg .hidden = True
383374 # change the mouse cursor color to match the current player
384- mouse_bmp .pixel_shader [2 ] = colors [current_turn_index ]
375+ mouse_tg .pixel_shader [2 ] = colors [current_turn_index ]
385376
386377 # if the current state is playing
387378 elif CUR_STATE == STATE_PLAYING :
@@ -455,15 +446,20 @@ def atexit_callback():
455446 current_player_lbl .color = colors [current_turn_index ]
456447
457448 # update the color of the mouse cursor
458- mouse_bmp .pixel_shader [2 ] = colors [current_turn_index ]
449+ mouse_tg .pixel_shader [2 ] = colors [current_turn_index ]
459450
460451 # empty out the cards flipped this turn list
461452 cards_flipped_this_turn = []
462453
463454 # ignore any clicks while the code is waiting to take reset cards
464455 if now >= WAIT_UNTIL :
465456 # left btn pressed
466- if buf [0 ] & (1 << 0 ) != 0 :
457+ if left_button_pressed :
458+ # if the mouse point is within the exit button
459+ if (mouse_tg .x >= display .width - 30 and
460+ mouse_tg .y >= display .height - 20 ):
461+ # restart back to code.py
462+ supervisor .reload ()
467463
468464 # loop over all cards
469465 for card_index , card in enumerate (card_tgs ):
@@ -491,7 +487,7 @@ def atexit_callback():
491487 # if the current state is gameover
492488 elif CUR_STATE == STATE_GAMEOVER :
493489 # left btn pressed
494- if buf [ 0 ] & ( 1 << 0 ) != 0 :
490+ if left_button_pressed :
495491 # get the coordinates of the mouse cursor point
496492 coords = (mouse_tg .x , mouse_tg .y , 0 )
497493
@@ -500,7 +496,7 @@ def atexit_callback():
500496 if play_again_btn .contains (coords ):
501497 # set next code file to this one
502498 supervisor .set_next_code_file (__file__ ,
503- working_directory = Path (__file__ ).parent .absolute ())
499+ working_directory = Path (__file__ ).parent .absolute ())
504500 # reload
505501 supervisor .reload ()
506502
0 commit comments