Monitor and control user input devices across multiple operating systems
Comprehensive mouse input simulation and monitoring functionality for controlling mouse cursor position, generating button clicks, scroll events, and listening to real-time mouse interactions across multiple operating systems.
The Controller class provides programmatic control over mouse cursor position and button interactions, enabling automation of mouse-based operations.
class Controller:
"""A controller for sending virtual mouse events to the system."""
def __init__(self):
"""Initialize the mouse controller."""
...
@property
def position(self) -> tuple[int, int]:
"""
The current position of the mouse pointer.
Returns:
tuple[int, int]: The (x, y) coordinates of the mouse pointer
"""
...
@position.setter
def position(self, pos: tuple[int, int]):
"""
Set the mouse pointer position.
Args:
pos (tuple[int, int]): The (x, y) coordinates to move the pointer to
"""
...
def move(self, dx: int, dy: int):
"""
Move the mouse pointer relative to its current position.
Args:
dx (int): Horizontal offset in pixels
dy (int): Vertical offset in pixels
Raises:
ValueError: If the values are invalid (e.g., out of bounds)
"""
...
def click(self, button: Button, count: int = 1):
"""
Emit button click event(s) at the current position.
Args:
button (Button): The button to click
count (int): Number of clicks to perform (default: 1)
"""
...
def press(self, button: Button):
"""
Emit a button press event at the current position.
Args:
button (Button): The button to press
"""
...
def release(self, button: Button):
"""
Emit a button release event at the current position.
Args:
button (Button): The button to release
"""
...
def scroll(self, dx: int, dy: int):
"""
Send scroll events.
Args:
dx (int): Horizontal scroll amount (units undefined, platform-dependent)
dy (int): Vertical scroll amount (units undefined, platform-dependent)
Raises:
ValueError: If the values are invalid (e.g., out of bounds)
"""
...from pynput.mouse import Button, Controller
# Create controller
mouse = Controller()
# Get current position
current_pos = mouse.position
print(f"Mouse is at {current_pos}")
# Move to absolute position
mouse.position = (500, 300)
# Move relatively
mouse.move(50, -25)
# Single click
mouse.click(Button.left)
# Double click
mouse.click(Button.left, 2)
# Right click
mouse.click(Button.right)
# Press and hold, then release
mouse.press(Button.left)
# ... do something while button is pressed
mouse.release(Button.left)
# Scroll down 3 units
mouse.scroll(0, 3)
# Scroll horizontally
mouse.scroll(2, 0)The Listener class monitors mouse events in real-time, providing callbacks for mouse movements, clicks, and scroll events.
class Listener:
"""A listener for mouse events."""
def __init__(
self,
on_move: callable = None,
on_click: callable = None,
on_scroll: callable = None,
suppress: bool = False,
**kwargs
):
"""
Initialize the mouse event listener.
Args:
on_move (callable): Callback for mouse move events (x, y, injected)
on_click (callable): Callback for mouse click events (x, y, button, pressed, injected)
on_scroll (callable): Callback for scroll events (x, y, dx, dy, injected)
suppress (bool): Whether to suppress events system-wide
**kwargs: Platform-specific options (darwin_*, win32_*, xorg_*)
"""
...
def start(self):
"""Start the listener thread."""
...
def stop(self):
"""Stop the listener. Cannot be restarted once stopped."""
...
def wait(self):
"""Wait for the listener to become ready."""
...
def join(self, timeout: float = None):
"""
Wait for the listener thread to complete.
Args:
timeout (float): Maximum time to wait in seconds
"""
...
@property
def running(self) -> bool:
"""Whether the listener is currently running."""
...
@property
def suppress(self) -> bool:
"""Whether events are being suppressed system-wide."""
...from pynput import mouse
def on_move(x, y, injected):
"""Handle mouse move events."""
if not injected: # Ignore synthetic events
print(f'Mouse moved to ({x}, {y})')
def on_click(x, y, button, pressed, injected):
"""Handle mouse click events."""
action = 'Pressed' if pressed else 'Released'
print(f'{action} {button} at ({x}, {y})')
# Stop listener on right button release
if button == mouse.Button.right and not pressed:
return False
def on_scroll(x, y, dx, dy, injected):
"""Handle mouse scroll events."""
direction = 'up' if dy > 0 else 'down'
print(f'Scrolled {direction} at ({x}, {y})')
# Context manager usage (recommended)
with mouse.Listener(
on_move=on_move,
on_click=on_click,
on_scroll=on_scroll
) as listener:
listener.join()
# Manual control
listener = mouse.Listener(
on_move=on_move,
on_click=on_click,
on_scroll=on_scroll
)
listener.start()
listener.join() # Wait for listener to finish
# Non-blocking usage
listener = mouse.Listener(on_click=on_click)
listener.start()
# ... do other work
listener.stop()The Events class provides synchronous iteration over mouse events, useful for processing events in sequence.
class Events:
"""A mouse event listener supporting synchronous iteration over events."""
def __init__(self):
"""Initialize the events iterator."""
...
def __enter__(self):
"""Start the event listener."""
...
def __exit__(self, *args):
"""Stop the event listener."""
...
def __iter__(self):
"""Return iterator interface."""
...
def __next__(self):
"""Get the next event."""
...
def get(self, timeout: float = None):
"""
Get the next event with optional timeout.
Args:
timeout (float): Maximum time to wait for an event
Returns:
Event or None: The next event, or None if timeout or stopped
"""
...
class Move:
"""A mouse move event."""
def __init__(self, x: int, y: int, injected: bool):
self.x = x
self.y = y
self.injected = injected
class Click:
"""A mouse click event."""
def __init__(self, x: int, y: int, button: Button, pressed: bool, injected: bool):
self.x = x
self.y = y
self.button = button
self.pressed = pressed
self.injected = injected
class Scroll:
"""A mouse scroll event."""
def __init__(self, x: int, y: int, dx: int, dy: int, injected: bool):
self.x = x
self.y = y
self.dx = dx
self.dy = dy
self.injected = injectedfrom pynput.mouse import Events
# Process events synchronously
with Events() as events:
for event in events:
if isinstance(event, Events.Move):
print(f'Mouse moved to ({event.x}, {event.y})')
elif isinstance(event, Events.Click):
if event.pressed:
print(f'Button {event.button} pressed at ({event.x}, {event.y})')
else:
print(f'Button {event.button} released')
break # Exit on button release
elif isinstance(event, Events.Scroll):
print(f'Scrolled ({event.dx}, {event.dy}) at ({event.x}, {event.y})')
# Get events with timeout
with Events() as events:
while True:
event = events.get(timeout=1.0)
if event is None:
print("No event received within timeout")
break
print(f"Received event: {event}")class Button(enum.Enum):
"""Mouse button identifiers."""
left = 1 # Left mouse button
middle = 2 # Middle mouse button (scroll wheel)
right = 3 # Right mouse button
unknown = 0 # Unknown button# Event filtering
def win32_filter(msg, data):
"""Filter Windows mouse events."""
# Return False to suppress event from reaching listener
return True
listener = mouse.Listener(
on_click=on_click,
win32_event_filter=win32_filter
)# Event intercepting and modification
def darwin_intercept(event_type, event):
"""Intercept and modify macOS mouse events."""
# Modify event using Quartz functions
# Return None to suppress event system-wide
return event
listener = mouse.Listener(
on_click=on_click,
darwin_intercept=darwin_intercept
)Platform-specific options are available for X11/Xorg systems, though specific parameters may vary based on system configuration.
ImportError: Missing platform dependencies
Permission Issues: Some operations may require elevated privileges
Display Issues: Linux requires DISPLAY environment variable to be set
from pynput import mouse
try:
controller = mouse.Controller()
controller.position = (100, 100)
except Exception as e:
print(f"Mouse control error: {e}")
try:
with mouse.Listener(on_click=lambda *args: None) as listener:
listener.join()
except Exception as e:
print(f"Mouse listener error: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-pynput