From 722283e8199ab071906982d5efde51f2122ed5ae Mon Sep 17 00:00:00 2001 From: Adian Kozlica Date: Fri, 28 Mar 2025 16:43:10 +0100 Subject: [PATCH 1/4] Add KDE Wayland support for ImageGrab --- Tests/test_imagegrab.py | 4 +--- src/PIL/ImageGrab.py | 9 +++++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 5cd510751e2..e8fd9524ce4 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -40,9 +40,7 @@ def test_grab_x11(self) -> None: @pytest.mark.skipif(Image.core.HAVE_XCB, reason="tests missing XCB") def test_grab_no_xcb(self) -> None: - if sys.platform not in ("win32", "darwin") and not shutil.which( - "gnome-screenshot" - ): + if sys.platform not in ("win32", "darwin") and not shutil.which("gnome-screenshot") and not shutil.which('spectacle'): with pytest.raises(OSError) as e: ImageGrab.grab() assert str(e.value).startswith("Pillow was built without XCB support") diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index fe27bfaeb6f..5ac0b6a2199 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -80,11 +80,16 @@ def grab( 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]) + if shutil.which("gnome-screenshot"): + subprocess.call(["gnome-screenshot", "-f", filepath]) + elif shutil.which("spectacle"): + subprocess.call(["spectacle", "-n", "-b", "-f", "-o", filepath]) + else: + os.unlink(filepath) + raise im = Image.open(filepath) im.load() os.unlink(filepath) From eeb494abf714579c0744381eaaaab1fc1f5eac85 Mon Sep 17 00:00:00 2001 From: Adian Kozlica Date: Fri, 28 Mar 2025 17:18:09 +0100 Subject: [PATCH 2/4] Fix formatting --- Tests/test_imagegrab.py | 6 +++++- src/PIL/ImageGrab.py | 7 ++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index e8fd9524ce4..ab06f04e25d 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -40,7 +40,11 @@ def test_grab_x11(self) -> None: @pytest.mark.skipif(Image.core.HAVE_XCB, reason="tests missing XCB") def test_grab_no_xcb(self) -> None: - if sys.platform not in ("win32", "darwin") and not shutil.which("gnome-screenshot") and not shutil.which('spectacle'): + if ( + sys.platform not in ("win32", "darwin") + and not shutil.which("gnome-screenshot") + and not shutil.which("spectacle") + ): with pytest.raises(OSError) as e: ImageGrab.grab() assert str(e.value).startswith("Pillow was built without XCB support") diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 5ac0b6a2199..e79b4d651a4 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -77,10 +77,7 @@ def grab( 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 display_name is None and sys.platform not in ("darwin", "win32"): fh, filepath = tempfile.mkstemp(".png") os.close(fh) if shutil.which("gnome-screenshot"): @@ -89,7 +86,7 @@ def grab( subprocess.call(["spectacle", "-n", "-b", "-f", "-o", filepath]) else: os.unlink(filepath) - raise + raise im = Image.open(filepath) im.load() os.unlink(filepath) From e685e2833e9e6de8f03cdc71ac9b58107c909768 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 29 Mar 2025 18:27:02 +1100 Subject: [PATCH 3/4] Do not create temporary file if no utility is available --- src/PIL/ImageGrab.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index e79b4d651a4..db2a8db0692 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -78,15 +78,15 @@ def grab( size, data = Image.core.grabscreen_x11(display_name) except OSError: if display_name is None and sys.platform not in ("darwin", "win32"): - fh, filepath = tempfile.mkstemp(".png") - os.close(fh) if shutil.which("gnome-screenshot"): - subprocess.call(["gnome-screenshot", "-f", filepath]) + args = ["gnome-screenshot", "-f"] elif shutil.which("spectacle"): - subprocess.call(["spectacle", "-n", "-b", "-f", "-o", filepath]) + args = ["spectacle", "-n", "-b", "-f", "-o"] else: - os.unlink(filepath) raise + fh, filepath = tempfile.mkstemp(".png") + os.close(fh) + subprocess.call(args + [filepath]) im = Image.open(filepath) im.load() os.unlink(filepath) From 382c3ab10d5583072a70250218d1bd5bef65ef8a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 30 Mar 2025 11:16:05 +1100 Subject: [PATCH 4/4] spectacle may also be used on Linux --- docs/reference/ImageGrab.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index db2987eb081..1b81bc42c11 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -16,8 +16,9 @@ or the clipboard to a PIL image memory. the entire screen is copied, and on macOS, it will be at 2x if on a Retina screen. On Linux, if ``xdisplay`` is ``None`` and the default X11 display does not return - a snapshot of the screen, ``gnome-screenshot`` will be used as fallback if it is - installed. To disable this behaviour, pass ``xdisplay=""`` instead. + a snapshot of the screen, ``gnome-screenshot`` or ``spectacle`` will be used as a + fallback if they are installed. To disable this behaviour, pass ``xdisplay=""`` + instead. .. versionadded:: 1.1.3 (Windows), 3.0.0 (macOS), 7.1.0 (Linux)