Skip to content

Commit

Permalink
Add option to capture all monitors on Windows (#3950)
Browse files Browse the repository at this point in the history
Add option to capture all monitors on Windows
  • Loading branch information
hugovk committed Sep 27, 2019
2 parents d9dd617 + 3c311f5 commit 310d2c9
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 13 deletions.
6 changes: 5 additions & 1 deletion Tests/test_imagegrab.py
Expand Up @@ -8,7 +8,11 @@

class TestImageGrab(PillowTestCase):
def test_grab(self):
for im in [ImageGrab.grab(), ImageGrab.grab(include_layered_windows=True)]:
for im in [
ImageGrab.grab(),
ImageGrab.grab(include_layered_windows=True),
ImageGrab.grab(all_screens=True),
]:
self.assert_image(im, im.mode, im.size)

def test_grabclipboard(self):
Expand Down
8 changes: 7 additions & 1 deletion docs/reference/ImageGrab.rst
Expand Up @@ -11,7 +11,7 @@ or the clipboard to a PIL image memory.

.. versionadded:: 1.1.3

.. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False)
.. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False, all_screens=False)
Take a snapshot of the screen. The pixels inside the bounding box are
returned as an "RGB" image on Windows or "RGBA" on macOS.
Expand All @@ -20,7 +20,13 @@ or the clipboard to a PIL image memory.
.. versionadded:: 1.1.3 (Windows), 3.0.0 (macOS)

:param bbox: What region to copy. Default is the entire screen.
Note that on Windows OS, the top-left point may be negative if ``all_screens=True`` is used.
:param include_layered_windows: Includes layered windows. Windows OS only.

.. versionadded:: 6.1.0
:param all_screens: Capture all monitors. Windows OS only.

.. versionadded:: 6.2.0
:return: An image

.. py:function:: PIL.ImageGrab.grabclipboard()
Expand Down
12 changes: 8 additions & 4 deletions src/PIL/ImageGrab.py
Expand Up @@ -29,16 +29,18 @@
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)
subprocess.call(["screencapture", "-x", filepath])
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,
Expand All @@ -49,8 +51,10 @@ def grab(bbox=None, include_layered_windows=False):
(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


Expand Down
21 changes: 14 additions & 7 deletions src/display.c
Expand Up @@ -322,15 +322,15 @@ PyImaging_DisplayModeWin32(PyObject* self, PyObject* args)
PyObject*
PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
{
int width, height;
int includeLayeredWindows = 0;
int x = 0, y = 0, width, height;
int includeLayeredWindows = 0, all_screens = 0;
HBITMAP bitmap;
BITMAPCOREHEADER core;
HDC screen, screen_copy;
DWORD rop;
PyObject* buffer;

if (!PyArg_ParseTuple(args, "|i", &includeLayeredWindows))
if (!PyArg_ParseTuple(args, "|ii", &includeLayeredWindows, &all_screens))
return NULL;

/* step 1: create a memory DC large enough to hold the
Expand All @@ -339,8 +339,15 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
screen = CreateDC("DISPLAY", NULL, NULL, NULL);
screen_copy = CreateCompatibleDC(screen);

width = GetDeviceCaps(screen, HORZRES);
height = GetDeviceCaps(screen, VERTRES);
if (all_screens) {
x = GetSystemMetrics(SM_XVIRTUALSCREEN);
y = GetSystemMetrics(SM_YVIRTUALSCREEN);
width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
} else {
width = GetDeviceCaps(screen, HORZRES);
height = GetDeviceCaps(screen, VERTRES);
}

bitmap = CreateCompatibleBitmap(screen, width, height);
if (!bitmap)
Expand All @@ -354,7 +361,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
rop = SRCCOPY;
if (includeLayeredWindows)
rop |= CAPTUREBLT;
if (!BitBlt(screen_copy, 0, 0, width, height, screen, 0, 0, rop))
if (!BitBlt(screen_copy, 0, 0, width, height, screen, x, y, rop))
goto error;

/* step 3: extract bits from bitmap */
Expand All @@ -376,7 +383,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
DeleteDC(screen_copy);
DeleteDC(screen);

return Py_BuildValue("(ii)N", width, height, buffer);
return Py_BuildValue("(ii)(ii)N", x, y, width, height, buffer);

error:
PyErr_SetString(PyExc_IOError, "screen grab failed");
Expand Down

0 comments on commit 310d2c9

Please sign in to comment.