# coding=utf-8
# pynput
# Copyright (C) 2015-2022 Moses Palmér
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see .
"""
This module contains the base implementation.
The actual interface to mouse classes is defined here, but the implementation
is located in a platform dependent module.
"""
# pylint: disable=R0903
# We implement stubs
import enum
from pynput._util import AbstractListener, prefix
from pynput import _logger
class Button(enum.Enum):
"""The various buttons.
The actual values for these items differ between platforms. Some
platforms may have additional buttons, but these are guaranteed to be
present everywhere.
"""
#: An unknown button was pressed
unknown = 0
#: The left button
left = 1
#: The middle button
middle = 2
#: The right button
right = 3
class Controller(object):
"""A controller for sending virtual mouse events to the system.
"""
def __init__(self):
self._log = _logger(self.__class__)
@property
def position(self):
"""The current position of the mouse pointer.
This is the tuple ``(x, y)``, and setting it will move the pointer.
"""
return self._position_get()
@position.setter
def position(self, pos):
self._position_set(pos)
def scroll(self, dx, dy):
"""Sends scroll events.
:param int dx: The horizontal scroll. The units of scrolling is
undefined.
:param int dy: The vertical scroll. The units of scrolling is
undefined.
:raises ValueError: if the values are invalid, for example out of
bounds
"""
self._scroll(dx, dy)
def press(self, button):
"""Emits a button press event at the current position.
:param Button button: The button to press.
"""
self._press(button)
def release(self, button):
"""Emits a button release event at the current position.
:param Button button: The button to release.
"""
self._release(button)
def move(self, dx, dy):
"""Moves the mouse pointer a number of pixels from its current
position.
:param int dx: The horizontal offset.
:param int dy: The vertical offset.
:raises ValueError: if the values are invalid, for example out of
bounds
"""
self.position = tuple(sum(i) for i in zip(self.position, (dx, dy)))
def click(self, button, count=1):
"""Emits a button click event at the current position.
The default implementation sends a series of press and release events.
:param Button button: The button to click.
:param int count: The number of clicks to send.
"""
with self as controller:
for _ in range(count):
controller.press(button)
controller.release(button)
def __enter__(self):
"""Begins a series of clicks.
In the default :meth:`click` implementation, the return value of this
method is used for the calls to :meth:`press` and :meth:`release`
instead of ``self``.
The default implementation is a no-op.
"""
return self
def __exit__(self, exc_type, value, traceback):
"""Ends a series of clicks.
"""
pass
def _position_get(self):
"""The implementation of the getter for :attr:`position`.
This is a platform dependent implementation.
"""
raise NotImplementedError()
def _position_set(self, pos):
"""The implementation of the setter for :attr:`position`.
This is a platform dependent implementation.
"""
raise NotImplementedError()
def _scroll(self, dx, dy):
"""The implementation of the :meth:`scroll` method.
This is a platform dependent implementation.
"""
raise NotImplementedError()
def _press(self, button):
"""The implementation of the :meth:`press` method.
This is a platform dependent implementation.
"""
raise NotImplementedError()
def _release(self, button):
"""The implementation of the :meth:`release` method.
This is a platform dependent implementation.
"""
raise NotImplementedError()
# pylint: disable=W0223; This is also an abstract class
class Listener(AbstractListener):
"""A listener for mouse events.
Instances of this class can be used as context managers. This is equivalent
to the following code::
listener.start()
try:
listener.wait()
with_statements()
finally:
listener.stop()
This class inherits from :class:`threading.Thread` and supports all its
methods. It will set :attr:`daemon` to ``True`` when created.
:param callable on_move: The callback to call when mouse move events occur.
It will be called with the arguments ``(x, y)``, which is the new
pointer position. If this callback raises :class:`StopException` or
returns ``False``, the listener is stopped.
:param callable on_click: The callback to call when a mouse button is
clicked.
It will be called with the arguments ``(x, y, button, pressed)``,
where ``(x, y)`` is the new pointer position, ``button`` is one of the
:class:`Button` values and ``pressed`` is whether the button was
pressed.
If this callback raises :class:`StopException` or returns ``False``,
the listener is stopped.
:param callable on_scroll: The callback to call when mouse scroll
events occur.
It will be called with the arguments ``(x, y, dx, dy)``, where
``(x, y)`` is the new pointer position, and ``(dx, dy)`` is the scroll
vector.
If this callback raises :class:`StopException` or returns ``False``,
the listener is stopped.
:param bool suppress: Whether to suppress events. Setting this to ``True``
will prevent the input events from being passed to the rest of the
system.
:param kwargs: Any non-standard platform dependent options. These should be
prefixed with the platform name thus: ``darwin_``, ``xorg_`` or
``win32_``.
Supported values are:
``darwin_intercept``
A callable taking the arguments ``(event_type, event)``, where
``event_type`` is any mouse related event type constant, and
``event`` is a ``CGEventRef``.
This callable can freely modify the event using functions like
``Quartz.CGEventSetIntegerValueField``. If this callable does not
return the event, the event is suppressed system wide.
``win32_event_filter``
A callable taking the arguments ``(msg, data)``, where ``msg`` is
the current message, and ``data`` associated data as a
`MSLLHOOKSTRUCT `_.
If this callback returns ``False``, the event will not
be propagated to the listener callback.
If ``self.suppress_event()`` is called, the event is suppressed
system wide.
"""
def __init__(self, on_move=None, on_click=None, on_scroll=None,
suppress=False, **kwargs):
self._log = _logger(self.__class__)
option_prefix = prefix(Listener, self.__class__)
self._options = {
key[len(option_prefix):]: value
for key, value in kwargs.items()
if key.startswith(option_prefix)}
super(Listener, self).__init__(
on_move=on_move, on_click=on_click, on_scroll=on_scroll,
suppress=suppress)
# pylint: enable=W0223