prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <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()`.
  
  
  </issue>
  <code>
  [start of src/PIL/ImageGrab.py]
  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 of src/PIL/ImageGrab.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: 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
  
verification_info: {"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"}]}