| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| from __future__ import annotations |
|
|
| import io |
| import os |
| import shutil |
| import subprocess |
| import sys |
| import tempfile |
|
|
| from . import Image |
|
|
| TYPE_CHECKING = False |
| if TYPE_CHECKING: |
| from . import ImageWin |
|
|
|
|
| def grab( |
| bbox: tuple[int, int, int, int] | None = None, |
| include_layered_windows: bool = False, |
| all_screens: bool = False, |
| xdisplay: str | None = None, |
| window: int | ImageWin.HWND | 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": |
| if window is not None: |
| all_screens = -1 |
| offset, size, data = Image.core.grabscreen_win32( |
| include_layered_windows, |
| all_screens, |
| int(window) if window is not None else 0, |
| ) |
| im = Image.frombytes( |
| "RGB", |
| size, |
| data, |
| |
| "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 |
| |
| 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"): |
| if shutil.which("gnome-screenshot"): |
| args = ["gnome-screenshot", "-f"] |
| elif shutil.which("grim"): |
| args = ["grim"] |
| elif shutil.which("spectacle"): |
| args = ["spectacle", "-n", "-b", "-f", "-o"] |
| else: |
| raise |
| fh, filepath = tempfile.mkstemp(".png") |
| os.close(fh) |
| subprocess.call(args + [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": |
| import struct |
|
|
| o = struct.unpack_from("I", data)[0] |
| if data[16] == 0: |
| files = data[o:].decode("mbcs").split("\0") |
| else: |
| files = data[o:].decode("utf-16le").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 = 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 [ |
| |
| b"Nothing is copied", |
| |
| b"No selection", |
| |
| b"No suitable type of content copied", |
| |
| b" not available", |
| |
| b"cannot convert ", |
| |
| 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 |
|
|