Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
474 changes: 40 additions & 434 deletions docs/source/api.rst

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@

sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))

import ctypes

import mss

# -- General configuration ------------------------------------------------

extensions = [
"sphinx.ext.autodoc",
"sphinx_copybutton",
"sphinx.ext.intersphinx",
"sphinx_new_tab_link",
Expand All @@ -29,6 +32,16 @@
release = "latest"
language = "en"
todo_include_todos = True
autodoc_member_order = "bysource"
autodoc_default_options = {
"members": True,
"undoc-members": True,
"show-inheritance": True,
}

# Monkey-patch WINFUNCTYPE into ctypes, so that we can import
# mss.windows while building the documentation.
ctypes.WINFUNCTYPE = ctypes.CFUNCTYPE # type:ignore[attr-defined]


# -- Options for HTML output ----------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/source/developers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ You will need `pytest <https://pypi.org/project/pytest/>`_::
How to Test?
------------

Launch the test suit::
Launch the test suite::

$ python -m pytest

Expand Down
11 changes: 5 additions & 6 deletions src/mss/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# This module is maintained by Mickaël Schoentgen <contact@tiger-222.fr>.
#
# You can always get the latest version of this module at:
# https://github.com/BoboTiG/python-mss
# If that URL should fail, try contacting the author.
"""An ultra fast cross-platform multiple screenshots module in pure python
using ctypes.

This module is maintained by Mickaël Schoentgen <contact@tiger-222.fr>.

You can always get the latest version of this module at:
https://github.com/BoboTiG/python-mss
If that URL should fail, try contacting the author.
"""

from mss.exception import ScreenShotError
Expand Down
109 changes: 70 additions & 39 deletions src/mss/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""This is part of the MSS Python's module.
Source: https://github.com/BoboTiG/python-mss.
"""
# This is part of the MSS Python's module.
# Source: https://github.com/BoboTiG/python-mss.

from __future__ import annotations

Expand Down Expand Up @@ -35,15 +34,28 @@

UTC = timezone.utc

#: Global lock protecting access to platform screenshot calls.
#:
#: .. versionadded:: 6.0.0
lock = Lock()

OPAQUE = 255


class MSSBase(metaclass=ABCMeta):
"""This class will be overloaded by a system specific one."""
"""Base class for all Multiple ScreenShots implementations.

__slots__ = {"_monitors", "cls_image", "compression_level", "with_cursor"}
:param backend: Backend selector, for platforms with multiple backends.
:param compression_level: PNG compression level.
:param with_cursor: Include the mouse cursor in screenshots.
:param display: X11 display name (GNU/Linux only).
:param max_displays: Maximum number of displays to enumerate (macOS only).

.. versionadded:: 8.0.0
``compression_level``, ``display``, ``max_displays``, and ``with_cursor`` keyword arguments.
"""

__slots__ = {"_closed", "_monitors", "cls_image", "compression_level", "with_cursor"}

def __init__(
self,
Expand All @@ -58,9 +70,17 @@ def __init__(
max_displays: int = 32, # noqa: ARG002
) -> None:
self.cls_image: type[ScreenShot] = ScreenShot
#: PNG compression level used when saving the screenshot data into a file
#: (see :py:func:`zlib.compress()` for details).
#:
#: .. versionadded:: 3.2.0
self.compression_level = compression_level
#: Include the mouse cursor in screenshots.
#:
#: .. versionadded:: 8.0.0
self.with_cursor = with_cursor
self._monitors: Monitors = []
self._closed = False
# If there isn't a factory that removed the "backend" argument, make sure that it was set to "default".
# Factories that do backend-specific dispatch should remove that argument.
if backend != "default":
Expand Down Expand Up @@ -91,17 +111,43 @@ def _monitors_impl(self) -> None:
It must populate self._monitors.
"""

def close(self) -> None: # noqa:B027
"""Clean-up."""
def _close_impl(self) -> None: # noqa:B027
"""Clean up.

This will be called at most once.
"""
# It's not necessary for subclasses to implement this if they have nothing to clean up.

def close(self) -> None:
"""Clean up.

This releases resources that MSS may be using. Once the MSS
object is closed, it may not be used again.

It is safe to call this multiple times; multiple calls have no
effect.

Rather than use :py:meth:`close` explicitly, we recommend you
use the MSS object as a context manager::

with mss.mss() as sct:
...
"""
with lock:
if self._closed:
return
self._close_impl()
self._closed = True

def grab(self, monitor: Monitor | tuple[int, int, int, int], /) -> ScreenShot:
"""Retrieve screen pixels for a given monitor.

Note: *monitor* can be a tuple like the one PIL.Image.grab() accepts.
Note: *monitor* can be a tuple like the one
py:meth:`PIL.ImageGrab.grab` accepts: `(left, top, right, bottom)`

:param monitor: The coordinates and size of the box to capture.
See :meth:`monitors <monitors>` for object details.
:return :class:`ScreenShot <ScreenShot>`.
:returns: Screenshot of the requested region.
"""
# Convert PIL bbox style
if isinstance(monitor, tuple):
Expand All @@ -126,16 +172,16 @@ def monitors(self) -> Monitors:

This method has to fill self._monitors with all information
and use it as a cache:
self._monitors[0] is a dict of all monitors together
self._monitors[N] is a dict of the monitor N (with N > 0)

- self._monitors[0] is a dict of all monitors together
- self._monitors[N] is a dict of the monitor N (with N > 0)

Each monitor is a dict with:
{
'left': the x-coordinate of the upper-left corner,
'top': the y-coordinate of the upper-left corner,
'width': the width,
'height': the height
}

- ``left``: the x-coordinate of the upper-left corner
- ``top``: the y-coordinate of the upper-left corner
- ``width``: the width
- ``height``: the height
"""
if not self._monitors:
with lock:
Expand All @@ -153,28 +199,13 @@ def save(
) -> Iterator[str]:
"""Grab a screenshot and save it to a file.

:param int mon: The monitor to screenshot (default=0).
-1: grab one screenshot of all monitors
0: grab one screenshot by monitor
N: grab the screenshot of the monitor N

:param str output: The output filename.

It can take several keywords to customize the filename:
- `{mon}`: the monitor number
- `{top}`: the screenshot y-coordinate of the upper-left corner
- `{left}`: the screenshot x-coordinate of the upper-left corner
- `{width}`: the screenshot's width
- `{height}`: the screenshot's height
- `{date}`: the current date using the default formatter

As it is using the `format()` function, you can specify
formatting options like `{date:%Y-%m-%s}`.

:param callable callback: Callback called before saving the
screenshot to a file. Take the `output` argument as parameter.

:return generator: Created file(s).
:param int mon: The monitor to screenshot (default=0). ``-1`` grabs all
monitors, ``0`` grabs each monitor, and ``N`` grabs monitor ``N``.
:param str output: The output filename. Keywords: ``{mon}``, ``{top}``,
``{left}``, ``{width}``, ``{height}``, ``{date}``.
:param callable callback: Called before saving the screenshot; receives
the ``output`` argument.
:return: Created file(s).
"""
monitors = self.monitors
if not monitors:
Expand Down
24 changes: 18 additions & 6 deletions src/mss/darwin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""This is part of the MSS Python's module.
Source: https://github.com/BoboTiG/python-mss.
"""macOS CoreGraphics backend for MSS.

Uses the CoreGraphics APIs to capture windows and enumerates up to
``max_displays`` active displays.
"""

from __future__ import annotations
Expand All @@ -18,7 +20,7 @@
if TYPE_CHECKING: # pragma: nocover
from mss.models import CFunctions, Monitor

__all__ = ("MSS",)
__all__ = ("IMAGE_OPTIONS", "MSS")

BACKENDS = ["default"]

Expand All @@ -27,8 +29,9 @@
kCGWindowImageBoundsIgnoreFraming = 1 << 0 # noqa: N816
kCGWindowImageNominalResolution = 1 << 4 # noqa: N816
kCGWindowImageShouldBeOpaque = 1 << 1 # noqa: N816
# Note: set `IMAGE_OPTIONS = 0` to turn on scaling (see issue #257 for more information)
IMAGE_OPTIONS = kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque | kCGWindowImageNominalResolution
#: For advanced users: as a note, you can set ``IMAGE_OPTIONS = 0`` to turn on scaling; see issue #257 for more
#: information.
IMAGE_OPTIONS: int = kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque | kCGWindowImageNominalResolution


def cgfloat() -> type[c_double | c_float]:
Expand Down Expand Up @@ -92,14 +95,22 @@ def __repr__(self) -> str:
class MSS(MSSBase):
"""Multiple ScreenShots implementation for macOS.
It uses intensively the CoreGraphics library.

:param max_displays: maximum number of displays to handle (default: 32).
:type max_displays: int

.. seealso::

:py:class:`mss.base.MSSBase`
Lists other parameters.
"""

__slots__ = {"core", "max_displays"}

def __init__(self, /, **kwargs: Any) -> None:
"""MacOS initialisations."""
super().__init__(**kwargs)

#: Maximum number of displays to handle.
self.max_displays = kwargs.get("max_displays", 32)

self._init_library()
Expand All @@ -117,6 +128,7 @@ def _init_library(self) -> None:
if not coregraphics:
msg = "No CoreGraphics library found."
raise ScreenShotError(msg)
# :meta:private:
self.core = ctypes.cdll.LoadLibrary(coregraphics)

def _set_cfunctions(self) -> None:
Expand Down
12 changes: 9 additions & 3 deletions src/mss/exception.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""This is part of the MSS Python's module.
Source: https://github.com/BoboTiG/python-mss.
"""
# This is part of the MSS Python's module.
# Source: https://github.com/BoboTiG/python-mss.

from __future__ import annotations

Expand All @@ -12,4 +11,11 @@ class ScreenShotError(Exception):

def __init__(self, message: str, /, *, details: dict[str, Any] | None = None) -> None:
super().__init__(message)
#: On GNU/Linux, and if the error comes from the XServer, it contains XError details.
#: This is an empty dict by default.
#:
#: For XErrors, you can find information on
#: `Using the Default Error Handlers <https://tronche.com/gui/x/xlib/event-handling/protocol-errors/default-handlers.html>`_.
#:
#: .. versionadded:: 3.3.0
self.details = details or {}
14 changes: 11 additions & 3 deletions src/mss/factory.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""This is part of the MSS Python's module.
Source: https://github.com/BoboTiG/python-mss.
"""
# This is part of the MSS Python's module.
# Source: https://github.com/BoboTiG/python-mss.

import platform
from typing import Any
Expand All @@ -18,6 +17,15 @@ def mss(**kwargs: Any) -> MSSBase:

It then proxies its arguments to the class for
instantiation.

.. seealso::
- :class:`mss.darwin.MSS`
- :class:`mss.linux.MSS`
- :class:`mss.windows.MSS`
- :func:`mss.linux.mss`
- :class:`mss.linux.xshmgetimage.MSS`
- :class:`mss.linux.xgetimage.MSS`
- :class:`mss.linux.xlib.MSS`
"""
os_ = platform.system().lower()

Expand Down
40 changes: 34 additions & 6 deletions src/mss/linux/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""GNU/Linux backend dispatcher for X11 screenshot implementations."""

from typing import Any

from mss.base import MSSBase
Expand All @@ -7,14 +9,35 @@


def mss(backend: str = "default", **kwargs: Any) -> MSSBase:
"""Factory returning a proper MSS class instance.
"""Return a backend-specific MSS implementation for GNU/Linux.

Selects and instantiates the appropriate X11 backend based on the
``backend`` parameter.

:param backend: Backend selector. Valid values:

- ``"default"`` or ``"xshmgetimage"`` (default): XCB-based backend
using XShmGetImage with automatic fallback to XGetImage when MIT-SHM
is unavailable; see :py:class:`mss.linux.xshmgetimage.MSS`.
- ``"xgetimage"``: XCB-based backend using XGetImage;
see :py:class:`mss.linux.xgetimage.MSS`.
- ``"xlib"``: Legacy Xlib-based backend retained for environments
without working XCB libraries; see :py:class:`mss.linux.xlib.MSS`.

It examines the options provided, and chooses the most adapted MSS
class to take screenshots. It then proxies its arguments to the
class for instantiation.
.. versionadded:: 10.2.0 Prior to this version, the
:class:`mss.linux.xlib.MSS` implementation was the only available
backend.

Currently, the only option used is the "backend" flag. Future
versions will look at other options as well.
:param display: Optional keyword argument. Specifies an X11 display
string to connect to. The default is taken from the environment
variable :envvar:`DISPLAY`.
:type display: str | bytes | None
:param kwargs: Additional keyword arguments passed to the backend class.
:returns: An MSS backend implementation.

.. versionadded:: 10.2.0 Prior to this version, this didn't exist:
the :func:`mss.linux.MSS` was a class equivalent to the current
:class:`mss.linux.xlib.MSS` implementation.
"""
backend = backend.lower()
if backend == "xlib":
Expand All @@ -39,4 +62,9 @@ class for instantiation.

# Alias in upper-case for backward compatibility. This is a supported name in the docs.
def MSS(*args, **kwargs) -> MSSBase: # type: ignore[no-untyped-def] # noqa: N802, ANN002, ANN003
"""Alias for :func:`mss.linux.mss.mss` for backward compatibility.

.. versionchanged:: 10.2.0 Prior to this version, this was a class.
.. deprecated:: 10.2.0 Use :func:`mss.linux.mss` instead.
"""
return mss(*args, **kwargs)
Loading