Spaces:
Paused
Paused
| # | |
| # The Python Imaging Library | |
| # $Id$ | |
| # | |
| # screen grabber | |
| # | |
| # History: | |
| # 2001-04-26 fl created | |
| # 2001-09-17 fl use builtin driver, if present | |
| # 2002-11-19 fl added grabclipboard support | |
| # | |
| # Copyright (c) 2001-2002 by Secret Labs AB | |
| # Copyright (c) 2001-2002 by Fredrik Lundh | |
| # | |
| # See the README file for information on usage and redistribution. | |
| # | |
| from __future__ import annotations | |
| import io | |
| import os | |
| import shutil | |
| import subprocess | |
| import sys | |
| import tempfile | |
| from . import Image | |
| def grab( | |
| bbox: tuple[int, int, int, int] | None = None, | |
| include_layered_windows: bool = False, | |
| all_screens: bool = False, | |
| xdisplay: str | None = None, | |
| ) -> Image.Image: | |
| im: Image.Image | |
| if xdisplay is None: | |
| if sys.platform == "darwin": | |
| fh, filepath = tempfile.mkstemp(".png") | |
| os.close(fh) | |
| args = ["screencapture"] | |
| if bbox: | |
| left, top, right, bottom = bbox | |
| args += ["-R", f"{left},{top},{right-left},{bottom-top}"] | |
| subprocess.call(args + ["-x", filepath]) | |
| im = Image.open(filepath) | |
| im.load() | |
| os.unlink(filepath) | |
| if bbox: | |
| im_resized = im.resize((right - left, bottom - top)) | |
| im.close() | |
| return im_resized | |
| return im | |
| elif sys.platform == "win32": | |
| offset, size, data = Image.core.grabscreen_win32( | |
| include_layered_windows, all_screens | |
| ) | |
| im = Image.frombytes( | |
| "RGB", | |
| size, | |
| data, | |
| # RGB, 32-bit line padding, origin lower left corner | |
| "raw", | |
| "BGR", | |
| (size[0] * 3 + 3) & -4, | |
| -1, | |
| ) | |
| if bbox: | |
| x0, y0 = offset | |
| left, top, right, bottom = bbox | |
| im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) | |
| return im | |
| # Cast to Optional[str] needed for Windows and macOS. | |
| display_name: str | None = xdisplay | |
| try: | |
| if not Image.core.HAVE_XCB: | |
| msg = "Pillow was built without XCB support" | |
| raise OSError(msg) | |
| size, data = Image.core.grabscreen_x11(display_name) | |
| except OSError: | |
| if ( | |
| display_name is None | |
| and sys.platform not in ("darwin", "win32") | |
| and shutil.which("gnome-screenshot") | |
| ): | |
| fh, filepath = tempfile.mkstemp(".png") | |
| os.close(fh) | |
| subprocess.call(["gnome-screenshot", "-f", filepath]) | |
| im = Image.open(filepath) | |
| im.load() | |
| os.unlink(filepath) | |
| if bbox: | |
| im_cropped = im.crop(bbox) | |
| im.close() | |
| return im_cropped | |
| return im | |
| else: | |
| raise | |
| else: | |
| im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1) | |
| if bbox: | |
| im = im.crop(bbox) | |
| return im | |
| def grabclipboard() -> Image.Image | list[str] | None: | |
| if sys.platform == "darwin": | |
| fh, filepath = tempfile.mkstemp(".png") | |
| os.close(fh) | |
| commands = [ | |
| 'set theFile to (open for access POSIX file "' | |
| + filepath | |
| + '" with write permission)', | |
| "try", | |
| " write (the clipboard as «class PNGf») to theFile", | |
| "end try", | |
| "close access theFile", | |
| ] | |
| script = ["osascript"] | |
| for command in commands: | |
| script += ["-e", command] | |
| subprocess.call(script) | |
| im = None | |
| if os.stat(filepath).st_size != 0: | |
| im = Image.open(filepath) | |
| im.load() | |
| os.unlink(filepath) | |
| return im | |
| elif sys.platform == "win32": | |
| fmt, data = Image.core.grabclipboard_win32() | |
| if fmt == "file": # CF_HDROP | |
| import struct | |
| o = struct.unpack_from("I", data)[0] | |
| if data[16] != 0: | |
| files = data[o:].decode("utf-16le").split("\0") | |
| else: | |
| files = data[o:].decode("mbcs").split("\0") | |
| return files[: files.index("")] | |
| if isinstance(data, bytes): | |
| data = io.BytesIO(data) | |
| if fmt == "png": | |
| from . import PngImagePlugin | |
| return PngImagePlugin.PngImageFile(data) | |
| elif fmt == "DIB": | |
| from . import BmpImagePlugin | |
| return BmpImagePlugin.DibImageFile(data) | |
| return None | |
| else: | |
| if os.getenv("WAYLAND_DISPLAY"): | |
| session_type = "wayland" | |
| elif os.getenv("DISPLAY"): | |
| session_type = "x11" | |
| else: # Session type check failed | |
| session_type = None | |
| if shutil.which("wl-paste") and session_type in ("wayland", None): | |
| args = ["wl-paste", "-t", "image"] | |
| elif shutil.which("xclip") and session_type in ("x11", None): | |
| args = ["xclip", "-selection", "clipboard", "-t", "image/png", "-o"] | |
| else: | |
| msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux" | |
| raise NotImplementedError(msg) | |
| p = subprocess.run(args, capture_output=True) | |
| if p.returncode != 0: | |
| err = p.stderr | |
| for silent_error in [ | |
| # wl-paste, when the clipboard is empty | |
| b"Nothing is copied", | |
| # Ubuntu/Debian wl-paste, when the clipboard is empty | |
| b"No selection", | |
| # Ubuntu/Debian wl-paste, when an image isn't available | |
| b"No suitable type of content copied", | |
| # wl-paste or Ubuntu/Debian xclip, when an image isn't available | |
| b" not available", | |
| # xclip, when an image isn't available | |
| b"cannot convert ", | |
| # xclip, when the clipboard isn't initialized | |
| b"xclip: Error: There is no owner for the ", | |
| ]: | |
| if silent_error in err: | |
| return None | |
| msg = f"{args[0]} error" | |
| if err: | |
| msg += f": {err.strip().decode()}" | |
| raise ChildProcessError(msg) | |
| data = io.BytesIO(p.stdout) | |
| im = Image.open(data) | |
| im.load() | |
| return im | |