Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
ImageGrab.py232 linesDownload Raw Back to PIL
1#
2# The Python Imaging Library
3# $Id$
4#
5# screen grabber
6#
7# History:
8# 2001-04-26 fl  created
9# 2001-09-17 fl  use builtin driver, if present
10# 2002-11-19 fl  added grabclipboard support
11#
12# Copyright (c) 2001-2002 by Secret Labs AB
13# Copyright (c) 2001-2002 by Fredrik Lundh
14#
15# See the README file for information on usage and redistribution.
16#
17from __future__ import annotations
18
19import io
20import os
21import shutil
22import subprocess
23import sys
24import tempfile
25
26from . import Image
27
28TYPE_CHECKING = False
29if TYPE_CHECKING:
30    from . import ImageWin
31
32
33def grab(
34    bbox: tuple[int, int, int, int] | None = None,
35    include_layered_windows: bool = False,
36    all_screens: bool = False,
37    xdisplay: str | None = None,
38    window: int | ImageWin.HWND | None = None,
39) -> Image.Image:
40    im: Image.Image
41    if xdisplay is None:
42        if sys.platform == "darwin":
43            fh, filepath = tempfile.mkstemp(".png")
44            os.close(fh)
45            args = ["screencapture"]
46            if window is not None:
47                args += ["-l", str(window)]
48            elif bbox:
49                left, top, right, bottom = bbox
50                args += ["-R", f"{left},{top},{right-left},{bottom-top}"]
51            args += ["-x", filepath]
52            retcode = subprocess.call(args)
53            if retcode:
54                raise subprocess.CalledProcessError(retcode, args)
55            im = Image.open(filepath)
56            im.load()
57            os.unlink(filepath)
58            if bbox:
59                if window is not None:
60                    # Determine if the window was in Retina mode or not
61                    # by capturing it without the shadow,
62                    # and checking how different the width is
63                    fh, filepath = tempfile.mkstemp(".png")
64                    os.close(fh)
65                    args = ["screencapture", "-l", str(window), "-o", "-x", filepath]
66                    retcode = subprocess.call(args)
67                    if retcode:
68                        raise subprocess.CalledProcessError(retcode, args)
69                    with Image.open(filepath) as im_no_shadow:
70                        retina = im.width - im_no_shadow.width > 100
71                    os.unlink(filepath)
72
73                    # Since screencapture's -R does not work with -l,
74                    # crop the image manually
75                    if retina:
76                        left, top, right, bottom = bbox
77                        im_cropped = im.resize(
78                            (right - left, bottom - top),
79                            box=tuple(coord * 2 for coord in bbox),
80                        )
81                    else:
82                        im_cropped = im.crop(bbox)
83                    im.close()
84                    return im_cropped
85                else:
86                    im_resized = im.resize((right - left, bottom - top))
87                    im.close()
88                    return im_resized
89            return im
90        elif sys.platform == "win32":
91            if window is not None:
92                all_screens = -1
93            offset, size, data = Image.core.grabscreen_win32(
94                include_layered_windows,
95                all_screens,
96                int(window) if window is not None else 0,
97            )
98            im = Image.frombytes(
99                "RGB",
100                size,
101                data,
102                # RGB, 32-bit line padding, origin lower left corner
103                "raw",
104                "BGR",
105                (size[0] * 3 + 3) & -4,
106                -1,
107            )
108            if bbox:
109                x0, y0 = offset
110                left, top, right, bottom = bbox
111                im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
112            return im
113    # Cast to Optional[str] needed for Windows and macOS.
114    display_name: str | None = xdisplay
115    try:
116        if not Image.core.HAVE_XCB:
117            msg = "Pillow was built without XCB support"
118            raise OSError(msg)
119        size, data = Image.core.grabscreen_x11(display_name)
120    except OSError:
121        if display_name is None and sys.platform not in ("darwin", "win32"):
122            if shutil.which("gnome-screenshot"):
123                args = ["gnome-screenshot", "-f"]
124            elif shutil.which("grim"):
125                args = ["grim"]
126            elif shutil.which("spectacle"):
127                args = ["spectacle", "-n", "-b", "-f", "-o"]
128            else:
129                raise
130            fh, filepath = tempfile.mkstemp(".png")
131            os.close(fh)
132            args.append(filepath)
133            retcode = subprocess.call(args)
134            if retcode:
135                raise subprocess.CalledProcessError(retcode, args)
136            im = Image.open(filepath)
137            im.load()
138            os.unlink(filepath)
139            if bbox:
140                im_cropped = im.crop(bbox)
141                im.close()
142                return im_cropped
143            return im
144        else:
145            raise
146    else:
147        im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1)
148        if bbox:
149            im = im.crop(bbox)
150        return im
151
152
153def grabclipboard() -> Image.Image | list[str] | None:
154    if sys.platform == "darwin":
155        p = subprocess.run(
156            ["osascript", "-e", "get the clipboard as «class PNGf»"],
157            capture_output=True,
158        )
159        if p.returncode != 0:
160            return None
161
162        import binascii
163
164        data = io.BytesIO(binascii.unhexlify(p.stdout[11:-3]))
165        return Image.open(data)
166    elif sys.platform == "win32":
167        fmt, data = Image.core.grabclipboard_win32()
168        if fmt == "file":  # CF_HDROP
169            import struct
170
171            o = struct.unpack_from("I", data)[0]
172            if data[16] == 0:
173                files = data[o:].decode("mbcs").split("\0")
174            else:
175                files = data[o:].decode("utf-16le").split("\0")
176            return files[: files.index("")]
177        if isinstance(data, bytes):
178            data = io.BytesIO(data)
179            if fmt == "png":
180                from . import PngImagePlugin
181
182                return PngImagePlugin.PngImageFile(data)
183            elif fmt == "DIB":
184                from . import BmpImagePlugin
185
186                return BmpImagePlugin.DibImageFile(data)
187        return None
188    else:
189        if os.getenv("WAYLAND_DISPLAY"):
190            session_type = "wayland"
191        elif os.getenv("DISPLAY"):
192            session_type = "x11"
193        else:  # Session type check failed
194            session_type = None
195
196        if shutil.which("wl-paste") and session_type in ("wayland", None):
197            args = ["wl-paste", "-t", "image"]
198        elif shutil.which("xclip") and session_type in ("x11", None):
199            args = ["xclip", "-selection", "clipboard", "-t", "image/png", "-o"]
200        else:
201            msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux"
202            raise NotImplementedError(msg)
203
204        p = subprocess.run(args, capture_output=True)
205        if p.returncode != 0:
206            err = p.stderr
207            for silent_error in [
208                # wl-paste, when the clipboard is empty
209                b"Nothing is copied",
210                # Ubuntu/Debian wl-paste, when the clipboard is empty
211                b"No selection",
212                # Ubuntu/Debian wl-paste, when an image isn't available
213                b"No suitable type of content copied",
214                # wl-paste or Ubuntu/Debian xclip, when an image isn't available
215                b" not available",
216                # xclip, when an image isn't available
217                b"cannot convert ",
218                # xclip, when the clipboard isn't initialized
219                b"xclip: Error: There is no owner for the ",
220            ]:
221                if silent_error in err:
222                    return None
223            msg = f"{args[0]} error"
224            if err:
225                msg += f": {err.strip().decode()}"
226            raise ChildProcessError(msg)
227
228        data = io.BytesIO(p.stdout)
229        im = Image.open(data)
230        im.load()
231        return im
232 
codekingpro/portable-devtools · Team Ai