Skip to content

Commit fd09a3c

Browse files
authored
Merge pull request #3122 from RetiredWizard/fruitjamlarsiomouse
Larsio_Paint_Music: refactor for use with adafruit_usb_host_mouse lib…
2 parents 85d15f4 + a24f8cd commit fd09a3c

File tree

4 files changed

+55
-204
lines changed

4 files changed

+55
-204
lines changed

Fruit_Jam/Larsio_Paint_Music/code.py

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,11 @@ def run(self):
6969
time.sleep(0.5)
7070
gc.collect()
7171

72-
# Try to find the mouse with multiple attempts
73-
MAX_ATTEMPTS = 5
74-
RETRY_DELAY = 1 # seconds
75-
76-
mouse_found = False
77-
for attempt in range(MAX_ATTEMPTS):
78-
print(f"Mouse detection attempt {attempt+1}/{MAX_ATTEMPTS}")
79-
if self.ui_manager.find_mouse():
80-
mouse_found = True
81-
print("Mouse found successfully!")
82-
break
83-
84-
print(f"Mouse detection attempt {attempt+1} failed, retrying...")
85-
time.sleep(RETRY_DELAY)
86-
87-
if not mouse_found:
88-
print("WARNING: Mouse not found after multiple attempts.")
72+
# Try to find the mouse
73+
if self.ui_manager.find_mouse():
74+
print("Mouse found successfully!")
75+
else:
76+
print("WARNING: Mouse not found.")
8977
print("The application will run, but mouse control may be limited.")
9078

9179
# Enter the main loop

Fruit_Jam/Larsio_Paint_Music/display_manager.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
"""
77
# pylint: disable=import-error,invalid-name,no-member,too-many-instance-attributes,too-many-arguments,too-many-branches,too-many-statements
88

9+
import supervisor
910
import displayio
10-
import picodvi
11-
import framebufferio
12-
import board
11+
from adafruit_fruitjam.peripherals import request_display_config
1312

1413

1514

@@ -25,19 +24,13 @@ def __init__(self, width=320, height=240):
2524

2625
def initialize_display(self):
2726
"""Initialize the DVI display"""
28-
# Release any existing displays
29-
displayio.release_displays()
27+
# Use the Fruit Jam library to set up display, that way on DAC fallback
28+
# the display doesn't attempt to be re-initialized
3029

31-
# Initialize the DVI framebuffer
32-
fb = picodvi.Framebuffer(self.SCREEN_WIDTH, self.SCREEN_HEIGHT,
33-
clk_dp=board.CKP, clk_dn=board.CKN,
34-
red_dp=board.D0P, red_dn=board.D0N,
35-
green_dp=board.D1P, green_dn=board.D1N,
36-
blue_dp=board.D2P, blue_dn=board.D2N,
37-
color_depth=16)
30+
request_display_config(self.SCREEN_WIDTH, self.SCREEN_HEIGHT,color_depth=16)
3831

3932
# Create the display
40-
self.display = framebufferio.FramebufferDisplay(fb)
33+
self.display = supervisor.runtime.display
4134

4235
# Create main group
4336
self.main_group = displayio.Group()

Fruit_Jam/Larsio_Paint_Music/input_handler.py

Lines changed: 43 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,7 @@
55
# input_handler.py: CircuitPython Music Staff Application component
66
"""
77

8-
import array
9-
import time
10-
import gc
11-
12-
# pylint: disable=import-error
13-
import usb.core
14-
8+
from adafruit_usb_host_mouse import find_and_init_boot_mouse
159

1610
# pylint: disable=invalid-name,no-member,too-many-instance-attributes,too-many-arguments
1711
# pylint: disable=too-many-branches,too-many-statements,broad-except
@@ -39,177 +33,52 @@ def __init__(self, screen_width, screen_height, staff_y_start, staff_height):
3933
self.mouse_y = screen_height // 2
4034

4135
def find_mouse(self):
42-
"""Find the mouse device with multiple retry attempts"""
43-
MAX_ATTEMPTS = 5
44-
RETRY_DELAY = 1 # seconds
45-
46-
for attempt in range(MAX_ATTEMPTS):
47-
try:
48-
print(f"Mouse detection attempt {attempt+1}/{MAX_ATTEMPTS}")
49-
50-
# Constants for USB control transfers
51-
DIR_OUT = 0
52-
# DIR_IN = 0x80 # Unused variable
53-
REQTYPE_CLASS = 1 << 5
54-
REQREC_INTERFACE = 1 << 0
55-
HID_REQ_SET_PROTOCOL = 0x0B
56-
57-
# Find all USB devices
58-
devices_found = False
59-
for device in usb.core.find(find_all=True):
60-
devices_found = True
61-
print(f"Found device: {device.idVendor:04x}:{device.idProduct:04x}")
62-
63-
try:
64-
# Try to get device info
65-
try:
66-
manufacturer = device.manufacturer
67-
product = device.product
68-
except Exception: # pylint: disable=broad-except
69-
manufacturer = "Unknown"
70-
product = "Unknown"
71-
72-
# Just use whatever device we find
73-
self.mouse = device
74-
75-
# Try to detach kernel driver
76-
try:
77-
has_kernel_driver = hasattr(device, 'is_kernel_driver_active')
78-
if has_kernel_driver and device.is_kernel_driver_active(0):
79-
device.detach_kernel_driver(0)
80-
except Exception as e: # pylint: disable=broad-except
81-
print(f"Error detaching kernel driver: {e}")
82-
83-
# Set configuration
84-
try:
85-
device.set_configuration()
86-
except Exception as e: # pylint: disable=broad-except
87-
print(f"Error setting configuration: {e}")
88-
continue # Try next device
89-
90-
# Just assume endpoint 0x81 (common for mice)
91-
self.in_endpoint = 0x81
92-
print(f"Using mouse: {manufacturer}, {product}")
93-
94-
# Set to report protocol mode
95-
try:
96-
bmRequestType = DIR_OUT | REQTYPE_CLASS | REQREC_INTERFACE
97-
bRequest = HID_REQ_SET_PROTOCOL
98-
wValue = 1 # 1 = report protocol
99-
wIndex = 0 # First interface
100-
101-
buf = bytearray(1)
102-
device.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, buf)
103-
print("Set to report protocol mode")
104-
except Exception as e: # pylint: disable=broad-except
105-
print(f"Could not set protocol: {e}")
106-
107-
# Buffer for reading data
108-
self.buf = array.array("B", [0] * 4)
109-
print("Created 4-byte buffer for mouse data")
110-
111-
# Verify mouse works by reading from it
112-
try:
113-
# Try to read some data with a short timeout
114-
data = device.read(self.in_endpoint, self.buf, timeout=100)
115-
print(f"Mouse test read successful: {data} bytes")
116-
return True
117-
except usb.core.USBTimeoutError:
118-
# Timeout is normal if mouse isn't moving
119-
print("Mouse connected but not sending data (normal)")
120-
return True
121-
except Exception as e: # pylint: disable=broad-except
122-
print(f"Mouse test read failed: {e}")
123-
# Continue to try next device or retry
124-
self.mouse = None
125-
self.in_endpoint = None
126-
continue
127-
128-
except Exception as e: # pylint: disable=broad-except
129-
print(f"Error initializing device: {e}")
130-
continue
131-
132-
if not devices_found:
133-
print("No USB devices found")
134-
135-
# If we get here without returning, no suitable mouse was found
136-
print(f"No working mouse found on attempt {attempt+1}, retrying...")
137-
gc.collect()
138-
time.sleep(RETRY_DELAY)
139-
140-
except Exception as e: # pylint: disable=broad-except
141-
print(f"Error during mouse detection: {e}")
142-
gc.collect()
143-
time.sleep(RETRY_DELAY)
144-
145-
print("Failed to find a working mouse after multiple attempts")
146-
return False
147-
148-
def process_mouse_input(self):
149-
"""Process mouse input - simplified version without wheel support"""
150-
try:
151-
# Attempt to read data from the mouse (10ms timeout)
152-
count = self.mouse.read(self.in_endpoint, self.buf, timeout=10)
153-
154-
if count >= 3: # We need at least buttons, X and Y
155-
# Extract mouse button states
156-
buttons = self.buf[0]
157-
x = self.buf[1]
158-
y = self.buf[2]
159-
160-
# Convert to signed values if needed
161-
if x > 127:
162-
x = x - 256
163-
if y > 127:
164-
y = y - 256
165-
166-
# Extract button states
167-
current_left_button_state = buttons & 0x01
168-
current_right_button_state = (buttons & 0x02) >> 1
169-
170-
# Detect button presses
171-
if current_left_button_state == 1 and self.last_left_button_state == 0:
172-
self.left_button_pressed = True
173-
else:
174-
self.left_button_pressed = False
175-
176-
if current_right_button_state == 1 and self.last_right_button_state == 0:
177-
self.right_button_pressed = True
178-
else:
179-
self.right_button_pressed = False
180-
181-
# Update button states
182-
self.last_left_button_state = current_left_button_state
183-
self.last_right_button_state = current_right_button_state
184-
185-
# Update position
186-
self.mouse_x += x
187-
self.mouse_y += y
188-
189-
# Ensure position stays within bounds
190-
self.mouse_x = max(0, min(self.SCREEN_WIDTH - 1, self.mouse_x))
191-
self.mouse_y = max(0, min(self.SCREEN_HEIGHT - 1, self.mouse_y))
192-
193-
return True
194-
36+
self.mouse = find_and_init_boot_mouse(cursor_image=None)
37+
if self.mouse is None:
38+
print("Failed to find a working mouse after multiple attempts")
19539
return False
19640

197-
except usb.core.USBError as e:
198-
# Handle timeouts silently
199-
if e.errno == 110: # Operation timed out
200-
return False
41+
# Change the mouse resolution so it's not too sensitive
42+
self.mouse.sensitivity = 4
20143

202-
# Handle disconnections
203-
if e.errno == 19: # No such device
204-
print("Mouse disconnected")
205-
self.mouse = None
206-
self.in_endpoint = None
207-
gc.collect()
44+
return True
20845

209-
return False
210-
except Exception as e: # pylint: disable=broad-except
211-
print(f"Error reading mouse: {type(e).__name__}")
212-
return False
46+
def process_mouse_input(self):
47+
"""Process mouse input - simplified version without wheel support"""
48+
buttons = self.mouse.update()
49+
50+
# Extract button states
51+
if buttons is None:
52+
current_left_button_state = 0
53+
current_right_button_state = 0
54+
else:
55+
current_left_button_state = 1 if 'left' in buttons else 0
56+
current_right_button_state = 1 if 'right' in buttons else 0
57+
58+
# Detect button presses
59+
if current_left_button_state == 1 and self.last_left_button_state == 0:
60+
self.left_button_pressed = True
61+
else:
62+
self.left_button_pressed = False
63+
64+
if current_right_button_state == 1 and self.last_right_button_state == 0:
65+
self.right_button_pressed = True
66+
else:
67+
self.right_button_pressed = False
68+
69+
# Update button states
70+
self.last_left_button_state = current_left_button_state
71+
self.last_right_button_state = current_right_button_state
72+
73+
# Update position
74+
self.mouse_x = self.mouse.x
75+
self.mouse_y = self.mouse.y
76+
77+
# Ensure position stays within bounds
78+
self.mouse_x = max(0, min(self.SCREEN_WIDTH - 1, self.mouse_x))
79+
self.mouse_y = max(0, min(self.SCREEN_HEIGHT - 1, self.mouse_y))
80+
81+
return True
21382

21483
def point_in_rect(self, x, y, rect_x, rect_y, rect_width, rect_height):
21584
"""Check if a point is inside a rectangle"""

Fruit_Jam/Larsio_Paint_Music/sound_manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ def __init__(self, audio_output="pwm", seconds_per_eighth=0.25):
126126
except Exception as e:
127127
print(f"Error initializing TLV320 DAC: {e}")
128128
print("Falling back to PWM audio output")
129+
fjPeriphs.deinit()
129130
# Fallback to PWM if I2S initialization fails
130131
self.audio = audiopwmio.PWMAudioOut(board.D10)
131132

0 commit comments

Comments
 (0)