admin / Synapse-Cortex
publicSelf Hosted ITSM Tool with RBAC/Tenanting and MFA
Synapse-Cortex / Synapse-Cortexv2 / .venv / Lib / site-packages / PIL / ImageGrab.py
8475 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | # # 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 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, *, scale_down: bool = False, ) -> Image.Image: im: Image.Image if xdisplay is None: if sys.platform == "darwin": fh, filepath = tempfile.mkstemp(".png") os.close(fh) args = ["screencapture"] if window is not None: args += ["-l", str(window)] elif bbox: left, top, right, bottom = bbox args += ["-R", f"{left},{top},{right-left},{bottom-top}"] args += ["-x", filepath] retcode = subprocess.call(args) if retcode: raise subprocess.CalledProcessError(retcode, args) im = Image.open(filepath) im.load() os.unlink(filepath) if bbox: if window is not None: # Determine if the window was in Retina mode or not # by capturing it without the shadow, # and checking how different the width is fh, filepath = tempfile.mkstemp(".png") os.close(fh) args = ["screencapture", "-l", str(window), "-o", "-x", filepath] retcode = subprocess.call(args) if retcode: raise subprocess.CalledProcessError(retcode, args) with Image.open(filepath) as im_no_shadow: retina = im.width - im_no_shadow.width > 100 os.unlink(filepath) # Since screencapture's -R does not work with -l, # crop the image manually if retina: left, top, right, bottom = bbox scale = 1 if scale_down else 2 im_cropped = im.resize( ((right - left) * scale, (bottom - top) * scale), box=tuple(coord * 2 for coord in bbox), ) else: im_cropped = im.crop(bbox) im.close() return im_cropped elif scale_down: 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, # 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"): 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) args.append(filepath) retcode = subprocess.call(args) if retcode: raise subprocess.CalledProcessError(retcode, args) 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("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 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 |