Spaces:
Running
Running
# | |
# 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 | |