We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ImageGrab fails with multiple monitors
When calling ImageGrab.grab() passing in a bounding box that is outside the area of my primary monitor, I just get black.

For example, my primary monitor is 1920x1200, flanked on either side by monitors running at 1600x1200, making my total desktop size 5120x1200. Also, because my primary monitor is in the middle, the horizontal coordinates for the full virtual desktop go from -1600 to 3519, where 0 is the left-most pixel of my primary monitor. If I try to capture my rightmost monitor using the following code, all I get is a black image:

``` Python
from PIL import ImageGrab
img = ImageGrab.grab([1920, 0, 3519, 1199])
img.save("test.jpg")
```

Poking around the code, it looks like `ImageGrab.grab()` calls into `Image.core.grabscreen` which is an alias for [`PyImaging_GrabScreenWin32()`](https://github.com/python-pillow/Pillow/blob/2be12dec2b231d31400f44bfa855966484997c16/display.c#L323) in `display.c`. That function does retrieve a DC handle to the entire desktop, but the subsequent calls to `GetDeviceCaps` with `HORZRES` and `VERTRES` only return the x/y size of the primary monitor, not the entire desktop.

``` C
screen = CreateDC("DISPLAY", NULL, NULL, NULL);
// ...
width = GetDeviceCaps(screen, HORZRES);
height = GetDeviceCaps(screen, VERTRES);
// ...
if (!BitBlt(screen_copy, 0, 0, width, height, screen, 0, 0, SRCCOPY))
    goto error;
```

Another problem with the above code is that monitors to the left of or above the primary display have negative coordinates in the `screen` DC. So, for example, capturing the monitor to the left of my primary display (which has a resolution of 1600x1200) would need to call BitBlt with the following coordinates:

``` C
left = -1600
top = 0
width = 1600
height = 1200
BitBlt(screen_copy, 0, 0, width, height, screen, left, top, SRCCOPY)
```

Similarly, if I was trying to capture a monitor above my primary display, then `top` would be negative. Because of the negative coordinates issue, I don't see any way of fixing this without passing in `left, top, width, height` from the calling python code, which could be calculated easily from the `bbox` parameter. Then it's simply up to the caller to know the coordinates of the monitor they want to capture. If no `bbox` is provided, then the coordinates would default to the primary display (0, 0, HORZRES, VERTRES), keeping the current functionality unchanged so as not to break existing code that uses `ImageGrab.grab()`.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/PIL/ImageGrab.py`
Content:
```
1 #
2 # The Python Imaging Library
3 # $Id$
4 #
5 # screen grabber (macOS and Windows only)
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 
18 import sys
19 
20 from . import Image
21 
22 if sys.platform == "win32":
23     grabber = Image.core.grabscreen
24 elif sys.platform == "darwin":
25     import os
26     import tempfile
27     import subprocess
28 else:
29     raise ImportError("ImageGrab is macOS and Windows only")
30 
31 
32 def grab(bbox=None, include_layered_windows=False):
33     if sys.platform == "darwin":
34         fh, filepath = tempfile.mkstemp(".png")
35         os.close(fh)
36         subprocess.call(["screencapture", "-x", filepath])
37         im = Image.open(filepath)
38         im.load()
39         os.unlink(filepath)
40     else:
41         size, data = grabber(include_layered_windows)
42         im = Image.frombytes(
43             "RGB",
44             size,
45             data,
46             # RGB, 32-bit line padding, origin lower left corner
47             "raw",
48             "BGR",
49             (size[0] * 3 + 3) & -4,
50             -1,
51         )
52     if bbox:
53         im = im.crop(bbox)
54     return im
55 
56 
57 def grabclipboard():
58     if sys.platform == "darwin":
59         fh, filepath = tempfile.mkstemp(".jpg")
60         os.close(fh)
61         commands = [
62             'set theFile to (open for access POSIX file "'
63             + filepath
64             + '" with write permission)',
65             "try",
66             "    write (the clipboard as JPEG picture) to theFile",
67             "end try",
68             "close access theFile",
69         ]
70         script = ["osascript"]
71         for command in commands:
72             script += ["-e", command]
73         subprocess.call(script)
74 
75         im = None
76         if os.stat(filepath).st_size != 0:
77             im = Image.open(filepath)
78             im.load()
79         os.unlink(filepath)
80         return im
81     else:
82         data = Image.core.grabclipboard()
83         if isinstance(data, bytes):
84             from . import BmpImagePlugin
85             import io
86 
87             return BmpImagePlugin.DibImageFile(io.BytesIO(data))
88         return data
89
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py
--- a/src/PIL/ImageGrab.py
+++ b/src/PIL/ImageGrab.py
@@ -29,7 +29,7 @@
     raise ImportError("ImageGrab is macOS and Windows only")
 
 
-def grab(bbox=None, include_layered_windows=False):
+def grab(bbox=None, include_layered_windows=False, all_screens=False):
     if sys.platform == "darwin":
         fh, filepath = tempfile.mkstemp(".png")
         os.close(fh)
@@ -37,8 +37,10 @@
         im = Image.open(filepath)
         im.load()
         os.unlink(filepath)
+        if bbox:
+            im = im.crop(bbox)
     else:
-        size, data = grabber(include_layered_windows)
+        offset, size, data = grabber(include_layered_windows, all_screens)
         im = Image.frombytes(
             "RGB",
             size,
@@ -49,8 +51,10 @@
             (size[0] * 3 + 3) & -4,
             -1,
         )
-    if bbox:
-        im = im.crop(bbox)
+        if bbox:
+            x0, y0 = offset
+            left, top, right, bottom = bbox
+            im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
     return im

{"golden_diff": "diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py\n--- a/src/PIL/ImageGrab.py\n+++ b/src/PIL/ImageGrab.py\n@@ -29,7 +29,7 @@\n     raise ImportError(\"ImageGrab is macOS and Windows only\")\n \n \n-def grab(bbox=None, include_layered_windows=False):\n+def grab(bbox=None, include_layered_windows=False, all_screens=False):\n     if sys.platform == \"darwin\":\n         fh, filepath = tempfile.mkstemp(\".png\")\n         os.close(fh)\n@@ -37,8 +37,10 @@\n         im = Image.open(filepath)\n         im.load()\n         os.unlink(filepath)\n+        if bbox:\n+            im = im.crop(bbox)\n     else:\n-        size, data = grabber(include_layered_windows)\n+        offset, size, data = grabber(include_layered_windows, all_screens)\n         im = Image.frombytes(\n             \"RGB\",\n             size,\n@@ -49,8 +51,10 @@\n             (size[0] * 3 + 3) & -4,\n             -1,\n         )\n-    if bbox:\n-        im = im.crop(bbox)\n+        if bbox:\n+            x0, y0 = offset\n+            left, top, right, bottom = bbox\n+            im = im.crop((left - x0, top - y0, right - x0, bottom - y0))\n     return im\n", "issue": "ImageGrab fails with multiple monitors\nWhen calling ImageGrab.grab() passing in a bounding box that is outside the area of my primary monitor, I just get black.\n\nFor example, my primary monitor is 1920x1200, flanked on either side by monitors running at 1600x1200, making my total desktop size 5120x1200. Also, because my primary monitor is in the middle, the horizontal coordinates for the full virtual desktop go from -1600 to 3519, where 0 is the left-most pixel of my primary monitor. If I try to capture my rightmost monitor using the following code, all I get is a black image:\n\n``` Python\nfrom PIL import ImageGrab\nimg = ImageGrab.grab([1920, 0, 3519, 1199])\nimg.save(\"test.jpg\")\n```\n\nPoking around the code, it looks like `ImageGrab.grab()` calls into `Image.core.grabscreen` which is an alias for [`PyImaging_GrabScreenWin32()`](https://github.com/python-pillow/Pillow/blob/2be12dec2b231d31400f44bfa855966484997c16/display.c#L323) in `display.c`. That function does retrieve a DC handle to the entire desktop, but the subsequent calls to `GetDeviceCaps` with `HORZRES` and `VERTRES` only return the x/y size of the primary monitor, not the entire desktop.\n\n``` C\nscreen = CreateDC(\"DISPLAY\", NULL, NULL, NULL);\n// ...\nwidth = GetDeviceCaps(screen, HORZRES);\nheight = GetDeviceCaps(screen, VERTRES);\n// ...\nif (!BitBlt(screen_copy, 0, 0, width, height, screen, 0, 0, SRCCOPY))\n    goto error;\n```\n\nAnother problem with the above code is that monitors to the left of or above the primary display have negative coordinates in the `screen` DC. So, for example, capturing the monitor to the left of my primary display (which has a resolution of 1600x1200) would need to call BitBlt with the following coordinates:\n\n``` C\nleft = -1600\ntop = 0\nwidth = 1600\nheight = 1200\nBitBlt(screen_copy, 0, 0, width, height, screen, left, top, SRCCOPY)\n```\n\nSimilarly, if I was trying to capture a monitor above my primary display, then `top` would be negative. Because of the negative coordinates issue, I don't see any way of fixing this without passing in `left, top, width, height` from the calling python code, which could be calculated easily from the `bbox` parameter. Then it's simply up to the caller to know the coordinates of the monitor they want to capture. If no `bbox` is provided, then the coordinates would default to the primary display (0, 0, HORZRES, VERTRES), keeping the current functionality unchanged so as not to break existing code that uses `ImageGrab.grab()`.\n\n", "before_files": [{"content": "#\n# The Python Imaging Library\n# $Id$\n#\n# screen grabber (macOS and Windows only)\n#\n# History:\n# 2001-04-26 fl  created\n# 2001-09-17 fl  use builtin driver, if present\n# 2002-11-19 fl  added grabclipboard support\n#\n# Copyright (c) 2001-2002 by Secret Labs AB\n# Copyright (c) 2001-2002 by Fredrik Lundh\n#\n# See the README file for information on usage and redistribution.\n#\n\nimport sys\n\nfrom . import Image\n\nif sys.platform == \"win32\":\n    grabber = Image.core.grabscreen\nelif sys.platform == \"darwin\":\n    import os\n    import tempfile\n    import subprocess\nelse:\n    raise ImportError(\"ImageGrab is macOS and Windows only\")\n\n\ndef grab(bbox=None, include_layered_windows=False):\n    if sys.platform == \"darwin\":\n        fh, filepath = tempfile.mkstemp(\".png\")\n        os.close(fh)\n        subprocess.call([\"screencapture\", \"-x\", filepath])\n        im = Image.open(filepath)\n        im.load()\n        os.unlink(filepath)\n    else:\n        size, data = grabber(include_layered_windows)\n        im = Image.frombytes(\n            \"RGB\",\n            size,\n            data,\n            # RGB, 32-bit line padding, origin lower left corner\n            \"raw\",\n            \"BGR\",\n            (size[0] * 3 + 3) & -4,\n            -1,\n        )\n    if bbox:\n        im = im.crop(bbox)\n    return im\n\n\ndef grabclipboard():\n    if sys.platform == \"darwin\":\n        fh, filepath = tempfile.mkstemp(\".jpg\")\n        os.close(fh)\n        commands = [\n            'set theFile to (open for access POSIX file \"'\n            + filepath\n            + '\" with write permission)',\n            \"try\",\n            \"    write (the clipboard as JPEG picture) to theFile\",\n            \"end try\",\n            \"close access theFile\",\n        ]\n        script = [\"osascript\"]\n        for command in commands:\n            script += [\"-e\", command]\n        subprocess.call(script)\n\n        im = None\n        if os.stat(filepath).st_size != 0:\n            im = Image.open(filepath)\n            im.load()\n        os.unlink(filepath)\n        return im\n    else:\n        data = Image.core.grabclipboard()\n        if isinstance(data, bytes):\n            from . import BmpImagePlugin\n            import io\n\n            return BmpImagePlugin.DibImageFile(io.BytesIO(data))\n        return data\n", "path": "src/PIL/ImageGrab.py"}], "after_files": [{"content": "#\n# The Python Imaging Library\n# $Id$\n#\n# screen grabber (macOS and Windows only)\n#\n# History:\n# 2001-04-26 fl  created\n# 2001-09-17 fl  use builtin driver, if present\n# 2002-11-19 fl  added grabclipboard support\n#\n# Copyright (c) 2001-2002 by Secret Labs AB\n# Copyright (c) 2001-2002 by Fredrik Lundh\n#\n# See the README file for information on usage and redistribution.\n#\n\nimport sys\n\nfrom . import Image\n\nif sys.platform == \"win32\":\n    grabber = Image.core.grabscreen\nelif sys.platform == \"darwin\":\n    import os\n    import tempfile\n    import subprocess\nelse:\n    raise ImportError(\"ImageGrab is macOS and Windows only\")\n\n\ndef grab(bbox=None, include_layered_windows=False, all_screens=False):\n    if sys.platform == \"darwin\":\n        fh, filepath = tempfile.mkstemp(\".png\")\n        os.close(fh)\n        subprocess.call([\"screencapture\", \"-x\", filepath])\n        im = Image.open(filepath)\n        im.load()\n        os.unlink(filepath)\n        if bbox:\n            im = im.crop(bbox)\n    else:\n        offset, size, data = grabber(include_layered_windows, all_screens)\n        im = Image.frombytes(\n            \"RGB\",\n            size,\n            data,\n            # RGB, 32-bit line padding, origin lower left corner\n            \"raw\",\n            \"BGR\",\n            (size[0] * 3 + 3) & -4,\n            -1,\n        )\n        if bbox:\n            x0, y0 = offset\n            left, top, right, bottom = bbox\n            im = im.crop((left - x0, top - y0, right - x0, bottom - y0))\n    return im\n\n\ndef grabclipboard():\n    if sys.platform == \"darwin\":\n        fh, filepath = tempfile.mkstemp(\".jpg\")\n        os.close(fh)\n        commands = [\n            'set theFile to (open for access POSIX file \"'\n            + filepath\n            + '\" with write permission)',\n            \"try\",\n            \"    write (the clipboard as JPEG picture) to theFile\",\n            \"end try\",\n            \"close access theFile\",\n        ]\n        script = [\"osascript\"]\n        for command in commands:\n            script += [\"-e\", command]\n        subprocess.call(script)\n\n        im = None\n        if os.stat(filepath).st_size != 0:\n            im = Image.open(filepath)\n            im.load()\n        os.unlink(filepath)\n        return im\n    else:\n        data = Image.core.grabclipboard()\n        if isinstance(data, bytes):\n            from . import BmpImagePlugin\n            import io\n\n            return BmpImagePlugin.DibImageFile(io.BytesIO(data))\n        return data\n", "path": "src/PIL/ImageGrab.py"}]}