use adafruit_usb_host_mouse
This commit is contained in:
parent
5ac9e6cd2e
commit
5ad0efac9f
1 changed files with 33 additions and 164 deletions
|
|
@ -24,7 +24,7 @@ import board
|
||||||
import displayio
|
import displayio
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import usb.core
|
from adafruit_usb_host_mouse import find_and_init_boot_mouse
|
||||||
usb_available = True
|
usb_available = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
usb_available = False
|
usb_available = False
|
||||||
|
|
@ -206,111 +206,12 @@ class MousePoller():
|
||||||
|
|
||||||
|
|
||||||
def find_mouse(self): # pylint: disable=too-many-statements, too-many-locals
|
def find_mouse(self): # pylint: disable=too-many-statements, too-many-locals
|
||||||
"""Find the mouse device with multiple retry attempts"""
|
"""Find and initialize the USB mouse."""
|
||||||
MAX_ATTEMPTS = 5
|
self.mouse = find_and_init_boot_mouse()
|
||||||
RETRY_DELAY = 1 # seconds
|
if self.mouse is None:
|
||||||
|
print("No mouse found.")
|
||||||
if not usb_available:
|
|
||||||
print("USB library not available; cannot find mouse.")
|
|
||||||
return False
|
return False
|
||||||
|
return True
|
||||||
for attempt in range(MAX_ATTEMPTS):
|
|
||||||
print(f"Mouse detection attempt {attempt+1}/{MAX_ATTEMPTS}")
|
|
||||||
|
|
||||||
# Constants for USB control transfers
|
|
||||||
DIR_OUT = 0
|
|
||||||
# DIR_IN = 0x80 # Unused variable
|
|
||||||
REQTYPE_CLASS = 1 << 5
|
|
||||||
REQREC_INTERFACE = 1 << 0
|
|
||||||
HID_REQ_SET_PROTOCOL = 0x0B
|
|
||||||
|
|
||||||
# Find all USB devices
|
|
||||||
devices_found = False
|
|
||||||
|
|
||||||
for device in usb.core.find(find_all=True):
|
|
||||||
devices_found = True
|
|
||||||
print(f"Found device: {device.idVendor:04x}:{device.idProduct:04x}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Try to get device info
|
|
||||||
try:
|
|
||||||
manufacturer = device.manufacturer
|
|
||||||
product = device.product
|
|
||||||
except Exception: # pylint: disable=broad-except
|
|
||||||
manufacturer = "Unknown"
|
|
||||||
product = "Unknown"
|
|
||||||
|
|
||||||
# Just use whatever device we find
|
|
||||||
self.mouse = device
|
|
||||||
|
|
||||||
# Try to detach kernel driver
|
|
||||||
try:
|
|
||||||
has_kernel_driver = hasattr(device, 'is_kernel_driver_active')
|
|
||||||
if has_kernel_driver and device.is_kernel_driver_active(0):
|
|
||||||
device.detach_kernel_driver(0)
|
|
||||||
except Exception as e: # pylint: disable=broad-except
|
|
||||||
print(f"Error detaching kernel driver: {e}")
|
|
||||||
|
|
||||||
# Set configuration
|
|
||||||
try:
|
|
||||||
device.set_configuration()
|
|
||||||
except Exception as e: # pylint: disable=broad-except
|
|
||||||
print(f"Error setting configuration: {e}")
|
|
||||||
continue # Try next device
|
|
||||||
|
|
||||||
# Just assume endpoint 0x81 (common for mice)
|
|
||||||
self.in_endpoint = 0x81
|
|
||||||
print(f"Using mouse: {manufacturer}, {product}")
|
|
||||||
|
|
||||||
# Set to report protocol mode
|
|
||||||
try:
|
|
||||||
bmRequestType = DIR_OUT | REQTYPE_CLASS | REQREC_INTERFACE
|
|
||||||
bRequest = HID_REQ_SET_PROTOCOL
|
|
||||||
wValue = 1 # 1 = report protocol
|
|
||||||
wIndex = 0 # First interface
|
|
||||||
|
|
||||||
buf = bytearray(1)
|
|
||||||
device.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, buf)
|
|
||||||
print("Set to report protocol mode")
|
|
||||||
except Exception as e: # pylint: disable=broad-except
|
|
||||||
print(f"Could not set protocol: {e}")
|
|
||||||
|
|
||||||
# Buffer for reading data
|
|
||||||
self.buf = array.array("B", [0] * 4)
|
|
||||||
print("Created 4-byte buffer for mouse data")
|
|
||||||
|
|
||||||
# Verify mouse works by reading from it
|
|
||||||
try:
|
|
||||||
# Try to read some data with a short timeout
|
|
||||||
data = device.read(self.in_endpoint, self.buf, timeout=100)
|
|
||||||
print(f"Mouse test read successful: {data} bytes")
|
|
||||||
return True
|
|
||||||
except usb.core.USBTimeoutError:
|
|
||||||
# Timeout is normal if mouse isn't moving
|
|
||||||
print("Mouse connected but not sending data (normal)")
|
|
||||||
return True
|
|
||||||
except Exception as e: # pylint: disable=broad-except
|
|
||||||
print(f"Mouse test read failed: {e}")
|
|
||||||
# Continue to try next device or retry
|
|
||||||
self.mouse = None
|
|
||||||
self.in_endpoint = None
|
|
||||||
continue
|
|
||||||
|
|
||||||
except Exception as e: # pylint: disable=broad-except
|
|
||||||
print(f"Error initializing device: {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not devices_found:
|
|
||||||
print("No USB devices found")
|
|
||||||
|
|
||||||
# If we get here without returning, no suitable mouse was found
|
|
||||||
print(f"No working mouse found on attempt {attempt+1}, retrying...")
|
|
||||||
gc.collect()
|
|
||||||
time.sleep(RETRY_DELAY)
|
|
||||||
|
|
||||||
print("Failed to find a working mouse after multiple attempts")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def poll(self):
|
def poll(self):
|
||||||
"""Check for input. Returns contact (a bool), False (no button B),
|
"""Check for input. Returns contact (a bool), False (no button B),
|
||||||
|
|
@ -345,72 +246,40 @@ class MousePoller():
|
||||||
def _process_mouse_input(self):
|
def _process_mouse_input(self):
|
||||||
"""Process mouse input - simplified version without wheel support"""
|
"""Process mouse input - simplified version without wheel support"""
|
||||||
|
|
||||||
try:
|
buttons = self.mouse.update()
|
||||||
# Attempt to read data from the mouse (10ms timeout)
|
|
||||||
count = self.mouse.read(self.in_endpoint, self.buf, timeout=10)
|
|
||||||
|
|
||||||
except usb.core.USBTimeoutError:
|
# Extract button states
|
||||||
# Timeout is normal if mouse isn't moving
|
if buttons is None:
|
||||||
return False
|
current_left_button_state = 0
|
||||||
except usb.core.USBError as e:
|
current_right_button_state = 0
|
||||||
# Handle timeouts silently
|
else:
|
||||||
if e.errno == 110: # Operation timed out
|
current_left_button_state = 1 if 'left' in buttons else 0
|
||||||
return False
|
current_right_button_state = 1 if 'right' in buttons else 0
|
||||||
|
|
||||||
# Handle disconnections
|
# Detect button presses
|
||||||
if e.errno == 19: # No such device
|
if current_left_button_state == 1 and self.last_left_button_state == 0:
|
||||||
print("Mouse disconnected")
|
self.left_button_pressed = True
|
||||||
self.mouse = None
|
elif current_left_button_state == 0 and self.last_left_button_state == 1:
|
||||||
self.in_endpoint = None
|
self.left_button_pressed = False
|
||||||
gc.collect()
|
|
||||||
|
|
||||||
return False
|
if current_right_button_state == 1 and self.last_right_button_state == 0:
|
||||||
except Exception as e: # pylint: disable=broad-except
|
self.right_button_pressed = True
|
||||||
print(f"Error reading mouse: {type(e).__name__}")
|
elif current_right_button_state == 0 and self.last_right_button_state == 1:
|
||||||
return False
|
self.right_button_pressed = False
|
||||||
|
|
||||||
if count >= 3: # We need at least buttons, X and Y
|
# Update button states
|
||||||
# Extract mouse button states
|
self.last_left_button_state = current_left_button_state
|
||||||
buttons = self.buf[0]
|
self.last_right_button_state = current_right_button_state
|
||||||
x = self.buf[1]
|
|
||||||
y = self.buf[2]
|
|
||||||
|
|
||||||
# Convert to signed values if needed
|
# Update position
|
||||||
if x > 127:
|
self.mouse_x = self.mouse.x
|
||||||
x = x - 256
|
self.mouse_y = self.mouse.y
|
||||||
if y > 127:
|
|
||||||
y = y - 256
|
|
||||||
|
|
||||||
# Extract button states
|
# Ensure position stays within bounds
|
||||||
current_left_button_state = buttons & 0x01
|
self.mouse_x = max(0, min(self.SCREEN_WIDTH - 1, self.mouse_x))
|
||||||
current_right_button_state = (buttons & 0x02) >> 1
|
self.mouse_y = max(0, min(self.SCREEN_HEIGHT - 1, self.mouse_y))
|
||||||
|
|
||||||
# Detect button presses
|
return True
|
||||||
if current_left_button_state == 1 and self.last_left_button_state == 0:
|
|
||||||
self.left_button_pressed = True
|
|
||||||
elif current_left_button_state == 0 and self.last_left_button_state == 1:
|
|
||||||
self.left_button_pressed = False
|
|
||||||
|
|
||||||
if current_right_button_state == 1 and self.last_right_button_state == 0:
|
|
||||||
self.right_button_pressed = True
|
|
||||||
elif current_right_button_state == 0 and self.last_right_button_state == 1:
|
|
||||||
self.right_button_pressed = False
|
|
||||||
|
|
||||||
# Update button states
|
|
||||||
self.last_left_button_state = current_left_button_state
|
|
||||||
self.last_right_button_state = current_right_button_state
|
|
||||||
|
|
||||||
# Update position
|
|
||||||
self.mouse_x += x
|
|
||||||
self.mouse_y += y
|
|
||||||
|
|
||||||
# Ensure position stays within bounds
|
|
||||||
self.mouse_x = max(0, min(self.SCREEN_WIDTH - 1, self.mouse_x))
|
|
||||||
self.mouse_y = max(0, min(self.SCREEN_HEIGHT - 1, self.mouse_y))
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue