Repository: python-pillow/Pillow

Issue:
Support ImageGrab from clipboard on Linux via xclip and wl-clipboard
Currently, `grabclipboard` raises `NotImplementedError` on Linux. But I think we can use xclip or wl-clipboard if they are installed. A subprocess call saving the image to a temporary file should be enough.

```sh
xclip -selection clipboard -t image/jpeg -o > test.jpg  # Edit 1
wl-paste --type image/jpg > test.jpg
```

I'm willing to help implement this feature.

### What did you do?

Try to grab an image from the clipboard on Linux

### What did you expect to happen?

The image is grabbed

### What actually happened?

```
NotImplementedError: ImageGrab.grabclipboard() is macOS and Windows only
```

### What are your OS, Python and Pillow versions?

* OS:  Linux (Manjaro KDE)
* Python: 3.10.8
* Pillow: 9.3.0

<!--
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.

The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as Plone, Django, or Buildout, try to replicate the issue just using Pillow.
-->

```python
from PIL.ImageGrab import grabclipboard
grabclipboard()
```



Discussion:
Considering that we support [`gnome-screenshot` in `ImageGrab.grab`](https://github.com/python-pillow/Pillow/blob/0ec32a30120cb22b533fd8563749d44bd5d3f78f/src/PIL/ImageGrab.py#L64-L75), this seems reasonable.
I experimented with this, but found when I copied an image, `xclip` didn't work.


https://user-images.githubusercontent.com/3112309/206149271-daab3ab1-854c-4a1a-b2c5-a93094fa329d.mov

I have however created PR #6783 for `wl-paste`.

Resolving patch:
diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py
--- a/src/PIL/ImageGrab.py
+++ b/src/PIL/ImageGrab.py
@@ -132,4 +132,18 @@ def grabclipboard():
                 return BmpImagePlugin.DibImageFile(data)
         return None
     else:
-        raise NotImplementedError("ImageGrab.grabclipboard() is macOS and Windows only")
+        if shutil.which("wl-paste"):
+            args = ["wl-paste"]
+        elif shutil.which("xclip"):
+            args = ["xclip", "-selection", "clipboard", "-t", "image/png", "-o"]
+        else:
+            raise NotImplementedError(
+                "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux"
+            )
+        fh, filepath = tempfile.mkstemp()
+        subprocess.call(args, stdout=fh)
+        os.close(fh)
+        im = Image.open(filepath)
+        im.load()
+        os.unlink(filepath)
+        return im


Test patch:
diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py
--- a/Tests/test_imagegrab.py
+++ b/Tests/test_imagegrab.py
@@ -64,9 +64,13 @@ def test_grabclipboard(self):
             )
             p.communicate()
         else:
-            with pytest.raises(NotImplementedError) as e:
-                ImageGrab.grabclipboard()
-            assert str(e.value) == "ImageGrab.grabclipboard() is macOS and Windows only"
+            if not shutil.which("wl-paste"):
+                with pytest.raises(
+                    NotImplementedError,
+                    match="wl-paste or xclip is required for"
+                    r" ImageGrab.grabclipboard\(\) on Linux",
+                ):
+                    ImageGrab.grabclipboard()
             return
 
         ImageGrab.grabclipboard()