ImageGrab.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. #
  17. from __future__ import annotations
  18. import io
  19. import os
  20. import shutil
  21. import subprocess
  22. import sys
  23. import tempfile
  24. from . import Image
  25. def grab(
  26. bbox: tuple[int, int, int, int] | None = None,
  27. include_layered_windows: bool = False,
  28. all_screens: bool = False,
  29. xdisplay: str | None = None,
  30. ) -> Image.Image:
  31. im: Image.Image
  32. if xdisplay is None:
  33. if sys.platform == "darwin":
  34. fh, filepath = tempfile.mkstemp(".png")
  35. os.close(fh)
  36. args = ["screencapture"]
  37. if bbox:
  38. left, top, right, bottom = bbox
  39. args += ["-R", f"{left},{top},{right-left},{bottom-top}"]
  40. subprocess.call(args + ["-x", filepath])
  41. im = Image.open(filepath)
  42. im.load()
  43. os.unlink(filepath)
  44. if bbox:
  45. im_resized = im.resize((right - left, bottom - top))
  46. im.close()
  47. return im_resized
  48. return im
  49. elif sys.platform == "win32":
  50. offset, size, data = Image.core.grabscreen_win32(
  51. include_layered_windows, all_screens
  52. )
  53. im = Image.frombytes(
  54. "RGB",
  55. size,
  56. data,
  57. # RGB, 32-bit line padding, origin lower left corner
  58. "raw",
  59. "BGR",
  60. (size[0] * 3 + 3) & -4,
  61. -1,
  62. )
  63. if bbox:
  64. x0, y0 = offset
  65. left, top, right, bottom = bbox
  66. im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
  67. return im
  68. # Cast to Optional[str] needed for Windows and macOS.
  69. display_name: str | None = xdisplay
  70. try:
  71. if not Image.core.HAVE_XCB:
  72. msg = "Pillow was built without XCB support"
  73. raise OSError(msg)
  74. size, data = Image.core.grabscreen_x11(display_name)
  75. except OSError:
  76. if (
  77. display_name is None
  78. and sys.platform not in ("darwin", "win32")
  79. and shutil.which("gnome-screenshot")
  80. ):
  81. fh, filepath = tempfile.mkstemp(".png")
  82. os.close(fh)
  83. subprocess.call(["gnome-screenshot", "-f", filepath])
  84. im = Image.open(filepath)
  85. im.load()
  86. os.unlink(filepath)
  87. if bbox:
  88. im_cropped = im.crop(bbox)
  89. im.close()
  90. return im_cropped
  91. return im
  92. else:
  93. raise
  94. else:
  95. im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1)
  96. if bbox:
  97. im = im.crop(bbox)
  98. return im
  99. def grabclipboard() -> Image.Image | list[str] | None:
  100. if sys.platform == "darwin":
  101. fh, filepath = tempfile.mkstemp(".png")
  102. os.close(fh)
  103. commands = [
  104. 'set theFile to (open for access POSIX file "'
  105. + filepath
  106. + '" with write permission)',
  107. "try",
  108. " write (the clipboard as «class PNGf») to theFile",
  109. "end try",
  110. "close access theFile",
  111. ]
  112. script = ["osascript"]
  113. for command in commands:
  114. script += ["-e", command]
  115. subprocess.call(script)
  116. im = None
  117. if os.stat(filepath).st_size != 0:
  118. im = Image.open(filepath)
  119. im.load()
  120. os.unlink(filepath)
  121. return im
  122. elif sys.platform == "win32":
  123. fmt, data = Image.core.grabclipboard_win32()
  124. if fmt == "file": # CF_HDROP
  125. import struct
  126. o = struct.unpack_from("I", data)[0]
  127. if data[16] != 0:
  128. files = data[o:].decode("utf-16le").split("\0")
  129. else:
  130. files = data[o:].decode("mbcs").split("\0")
  131. return files[: files.index("")]
  132. if isinstance(data, bytes):
  133. data = io.BytesIO(data)
  134. if fmt == "png":
  135. from . import PngImagePlugin
  136. return PngImagePlugin.PngImageFile(data)
  137. elif fmt == "DIB":
  138. from . import BmpImagePlugin
  139. return BmpImagePlugin.DibImageFile(data)
  140. return None
  141. else:
  142. if os.getenv("WAYLAND_DISPLAY"):
  143. session_type = "wayland"
  144. elif os.getenv("DISPLAY"):
  145. session_type = "x11"
  146. else: # Session type check failed
  147. session_type = None
  148. if shutil.which("wl-paste") and session_type in ("wayland", None):
  149. args = ["wl-paste", "-t", "image"]
  150. elif shutil.which("xclip") and session_type in ("x11", None):
  151. args = ["xclip", "-selection", "clipboard", "-t", "image/png", "-o"]
  152. else:
  153. msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux"
  154. raise NotImplementedError(msg)
  155. p = subprocess.run(args, capture_output=True)
  156. if p.returncode != 0:
  157. err = p.stderr
  158. for silent_error in [
  159. # wl-paste, when the clipboard is empty
  160. b"Nothing is copied",
  161. # Ubuntu/Debian wl-paste, when the clipboard is empty
  162. b"No selection",
  163. # Ubuntu/Debian wl-paste, when an image isn't available
  164. b"No suitable type of content copied",
  165. # wl-paste or Ubuntu/Debian xclip, when an image isn't available
  166. b" not available",
  167. # xclip, when an image isn't available
  168. b"cannot convert ",
  169. # xclip, when the clipboard isn't initialized
  170. b"xclip: Error: There is no owner for the ",
  171. ]:
  172. if silent_error in err:
  173. return None
  174. msg = f"{args[0]} error"
  175. if err:
  176. msg += f": {err.strip().decode()}"
  177. raise ChildProcessError(msg)
  178. data = io.BytesIO(p.stdout)
  179. im = Image.open(data)
  180. im.load()
  181. return im