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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ docs/output/
.mypy_cache/
__pycache__/
ruff_cache/
.venv/
venv/
Comment thread
halldorfannar marked this conversation as resolved.
16 changes: 6 additions & 10 deletions docs/source/release-history/v10.2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Actual performance improvements will vary depending on factors such as:

### Windows Improvements

Windows has received both capture-reliability improvements and a package restructure that prepares the codebase for additional backends.
Windows has received capture-reliability improvements and we have named the backend, for operational consistency with Linux.

#### Capture with `CreateDIBSection`

Expand All @@ -170,17 +170,13 @@ Additional improvements include:
- improved Win32 error handling and diagnostics
- fixes for capture failures during extended recordings

#### Package Restructure
### Restructure

`mss.windows` is now a **package** rather than a single module, with a `choose_impl()` dispatcher that mirrors the `mss.linux` architecture. This prepares the project for future Windows backends (such as a DXGI-based implementation) that will sit alongside the existing GDI code.
The existing GDI implementation has been converted to a named backend, `"gdi"`.
It is currently the only backend for Windows and therefore the default. We plan
to add a DXGI backend in the near future.

The existing GDI implementation has moved to `mss.windows.gdi`, but the previously-public names remain importable from `mss.windows` unchanged:

- `mss.windows.MSS`
- `mss.windows.MSSImplWindows`
- `mss.windows.BACKENDS`

Existing code does not need to be updated.
These changes were made while keeping backwards compatibility with the existing API.

### macOS Stability Fix

Expand Down
5 changes: 3 additions & 2 deletions docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ If you want to choose a particular backend, you can pass the ``backend`` keyword
with MSS(backend="xgetimage") as sct:
...

Currently, only the GNU/Linux implementation has multiple backends. These are described in their own section below.
GNU/Linux has multiple backend implementations. Windows also exposes the named ``gdi`` backend, which is currently the
same as ``default``. The GNU/Linux backends are described in their own section below.


GNU/Linux
Expand Down Expand Up @@ -152,7 +153,7 @@ Or via direct call from Python::
the output file name
-b, --backend BACKEND
platform-specific backend to use
(Linux: default/xlib/xgetimage/xshmgetimage; macOS/Windows: default)
(Linux: default/xlib/xgetimage/xshmgetimage; macOS: default; Windows: default/gdi)
--with-cursor include the cursor
-q, --quiet do not print created files
-v, --version show program's version number and exit
Expand Down
10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ dev = [
"twine==6.2.0",
]
docs = [
"myst-parser==5.0.0",
"shibuya==2026.1.9",
"sphinx==9.1.0",
"sphinx-copybutton==0.5.2",
"sphinx-new-tab-link==0.8.1",
"myst-parser==5.0.0 ; python_version >= '3.12'",
"shibuya==2026.1.9 ; python_version >= '3.12'",
"sphinx==9.1.0 ; python_version >= '3.12'",
"sphinx-copybutton==0.5.2 ; python_version >= '3.12'",
"sphinx-new-tab-link==0.8.1 ; python_version >= '3.12'",
]
tests = [
"numpy==2.4.3 ; sys_platform == 'linux' and python_version == '3.13'",
Expand Down
22 changes: 9 additions & 13 deletions src/mss/windows/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@
from mss.base import MSSImplementation
from mss.exception import ScreenShotError

# Re-export the GDI implementation so existing code that references
# ``mss.windows.MSSImplWindows`` keeps working.
from mss.windows.gdi import MSSImplWindows

__all__ = ["MSS"]

BACKENDS = ["default"]
BACKENDS = ["default", "gdi"]


class MSS(_MSS):
Expand All @@ -42,22 +38,22 @@ def choose_impl(backend: str = "default", **kwargs: Any) -> MSSImplementation:

:param backend: Backend selector. Valid values:

- ``"default"`` (default): GDI-based backend using ``BitBlt`` and
``CreateDIBSection`` for direct memory access to pixel data.

.. versionadded:: 10.3.0 Prior to this version, Windows had a single
implementation selected through :class:`mss.windows.MSS`.
- ``"default"`` or ``"gdi"`` (default): GDI-based backend using
``BitBlt`` and ``CreateDIBSection`` for direct memory access to pixel
data.

:param kwargs: Additional keyword arguments passed to the backend class.
:returns: An MSS backend implementation.

.. versionadded:: 10.3.0 Prior to this version, this didn't exist:
.. versionadded:: 10.2.0 Prior to this version, this didn't exist:
Windows had a single implementation selected through
:class:`mss.windows.MSS`.
"""
backend = backend.lower()
if backend == "default":
return MSSImplWindows(**kwargs)
if backend in {"default", "gdi"}:
from mss.windows.gdi import MSSImplGdi # noqa: PLC0415

return MSSImplGdi(**kwargs)
assert backend not in BACKENDS # noqa: S101
msg = f"Backend {backend!r} not (yet?) implemented."
raise ScreenShotError(msg)
4 changes: 2 additions & 2 deletions src/mss/windows/gdi.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

from mss.models import CFunctionsErrChecked, Monitor, Monitors

__all__ = ("MSSImplWindows",)
__all__ = ()


LPCRECT = POINTER(RECT) # Actually a const pointer, but ctypes has no const.
Expand Down Expand Up @@ -162,7 +162,7 @@ def _errcheck(result: int | _Pointer, func: Callable, arguments: tuple) -> tuple
}


class MSSImplWindows(MSSImplementation):
class MSSImplGdi(MSSImplementation):
"""Multiple ScreenShots implementation for Microsoft Windows (GDI backend).

This implementation uses CreateDIBSection for direct memory access to pixel data,
Expand Down
2 changes: 1 addition & 1 deletion src/tests/bench_grab_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,14 @@ def benchmark_raw_bitblt() -> None:
captureblt = 0x40000000

with mss.MSS() as sct:
assert isinstance(sct._impl, mss.windows.MSSImplWindows)
Comment thread
halldorfannar marked this conversation as resolved.
monitor = sct.monitors[1]
width, height = monitor["width"], monitor["height"]
left, top = monitor["left"], monitor["top"]

# Force region setup
sct.grab(monitor)

assert isinstance(sct._impl, mss.windows.gdi.MSSImplGdi)
srcdc = sct._impl._srcdc
memdc = sct._impl._memdc

Expand Down
2 changes: 1 addition & 1 deletion src/tests/test_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ def test_sdist() -> None:
f"mss-{__version__}/PKG-INFO",
f"mss-{__version__}/README.md",
f"mss-{__version__}/docs/source/api.rst",
f"mss-{__version__}/docs/source/changelog.rst",
f"mss-{__version__}/docs/source/conf.py",
f"mss-{__version__}/docs/source/developers.rst",
f"mss-{__version__}/docs/source/examples.rst",
Expand All @@ -59,6 +58,7 @@ def test_sdist() -> None:
f"mss-{__version__}/docs/source/examples/part_of_screen_monitor_2.py",
f"mss-{__version__}/docs/source/examples/pil.py",
f"mss-{__version__}/docs/source/examples/pil_pixels.py",
f"mss-{__version__}/docs/source/history.rst",
f"mss-{__version__}/docs/source/index.rst",
f"mss-{__version__}/docs/source/installation.rst",
*[f"mss-{__version__}/docs/source/release-history/{changelog.name}" for changelog in changelogs],
Expand Down
14 changes: 11 additions & 3 deletions src/tests/test_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

try:
import mss.windows
from mss.windows.gdi import MSSImplGdi
except ImportError:
pytestmark = pytest.mark.skip

Expand All @@ -25,11 +26,18 @@ def test_choose_impl_unknown_backend_raises() -> None:
mss.windows.choose_impl(backend=bogus)


def test_factory_gdi_backend() -> None:
"""``mss.MSS()`` and ``mss.MSS(backend="gdi")`` must select the GDI backend on Windows."""
with mss.MSS() as default_sct, mss.MSS(backend="gdi") as gdi_sct:
assert type(default_sct._impl) is MSSImplGdi
assert type(gdi_sct._impl) is MSSImplGdi


def test_region_caching() -> None:
"""The region to grab is cached, ensure this is well-done."""
with mss.MSS() as sct:
assert isinstance(sct, mss.MSS)
assert isinstance(sct._impl, mss.windows.MSSImplWindows)
assert isinstance(sct._impl, MSSImplGdi)

# Grab the area 1
region1 = {"top": 0, "left": 0, "width": 200, "height": 200}
Expand All @@ -55,8 +63,8 @@ def test_region_not_caching() -> None:

assert isinstance(grab1, mss.MSS) # For Mypy
assert isinstance(grab2, mss.MSS) # For Mypy
assert isinstance(grab1._impl, mss.windows.MSSImplWindows)
assert isinstance(grab2._impl, mss.windows.MSSImplWindows)
assert isinstance(grab1._impl, MSSImplGdi)
assert isinstance(grab2._impl, MSSImplGdi)

region1 = {"top": 0, "left": 0, "width": 100, "height": 100}
region2 = {"top": 0, "left": 0, "width": 50, "height": 1}
Expand Down
Loading