Skip to content

Commit 75f9866

Browse files
committed
Larsio_Paint_Music: refactor for use with adafruit_usb_host_mouse library
1 parent 216b271 commit 75f9866

3 files changed

Lines changed: 53 additions & 186 deletions

File tree

Fruit_Jam/Larsio_Paint_Music/code.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,9 @@ def run(self):
7373
MAX_ATTEMPTS = 5
7474
RETRY_DELAY = 1 # seconds
7575

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:
76+
if self.ui_manager.find_mouse():
77+
print("Mouse found successfully!")
78+
else:
8879
print("WARNING: Mouse not found after multiple attempts.")
8980
print("The application will run, but mouse control may be limited.")
9081

Fruit_Jam/Larsio_Paint_Music/display_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
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
1011
import picodvi
1112
import framebufferio
@@ -38,6 +39,7 @@ def initialize_display(self):
3839

3940
# Create the display
4041
self.display = framebufferio.FramebufferDisplay(fb)
42+
supervisor.runtime.display = self.display # Keep display on in runtime info
4143

4244
# Create main group
4345
self.main_group = displayio.Group()

Fruit_Jam/Larsio_Paint_Music/input_handler.py

Lines changed: 48 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,8 @@
66
"""
77

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

1612
# pylint: disable=invalid-name,no-member,too-many-instance-attributes,too-many-arguments
1713
# pylint: disable=too-many-branches,too-many-statements,broad-except
@@ -34,182 +30,60 @@ def __init__(self, screen_width, screen_height, staff_y_start, staff_height):
3430
self.buf = None
3531
self.in_endpoint = None
3632

33+
self.sensitivity = 4 # Sensitivity factor for mouse movement
34+
3735
# Mouse position
38-
self.mouse_x = screen_width // 2
39-
self.mouse_y = screen_height // 2
36+
self.mouse_x = (screen_width // 2) * self.sensitivity
37+
self.mouse_y = (screen_height // 2) * self.sensitivity
4038

4139
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-
40+
self.mouse = find_and_init_boot_mouse()
41+
if self.mouse is None:
42+
print("Failed to find a working mouse after multiple attempts")
19543
return False
19644

197-
except usb.core.USBError as e:
198-
# Handle timeouts silently
199-
if e.errno == 110: # Operation timed out
200-
return False
45+
# Change the mouse resolution so it's not too sensitive
46+
self.mouse.display_size = \
47+
(self.SCREEN_WIDTH*self.sensitivity, self.SCREEN_HEIGHT*self.sensitivity)
20148

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()
49+
return True
20850

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

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

0 commit comments

Comments
 (0)