123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- #
- # 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":
- p = subprocess.run(
- ["osascript", "-e", "get the clipboard as «class PNGf»"],
- capture_output=True,
- )
- if p.returncode != 0:
- return None
- import binascii
- data = io.BytesIO(binascii.unhexlify(p.stdout[11:-3]))
- return Image.open(data)
- 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
|