mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-13 05:06:49 +03:00
xcb screengrab support
This commit is contained in:
parent
291f1eb1e2
commit
3c39e6fcf6
10
setup.py
10
setup.py
|
@ -286,6 +286,7 @@ class pil_build_ext(build_ext):
|
||||||
"webpmux",
|
"webpmux",
|
||||||
"jpeg2000",
|
"jpeg2000",
|
||||||
"imagequant",
|
"imagequant",
|
||||||
|
"xcb",
|
||||||
]
|
]
|
||||||
|
|
||||||
required = {"jpeg", "zlib"}
|
required = {"jpeg", "zlib"}
|
||||||
|
@ -681,6 +682,12 @@ class pil_build_ext(build_ext):
|
||||||
):
|
):
|
||||||
feature.webpmux = "libwebpmux"
|
feature.webpmux = "libwebpmux"
|
||||||
|
|
||||||
|
if feature.want("xcb"):
|
||||||
|
_dbg("Looking for xcb")
|
||||||
|
if _find_include_file(self, "xcb/xcb.h"):
|
||||||
|
if _find_library_file(self, "xcb"):
|
||||||
|
feature.xcb = "xcb"
|
||||||
|
|
||||||
for f in feature:
|
for f in feature:
|
||||||
if not getattr(feature, f) and feature.require(f):
|
if not getattr(feature, f) and feature.require(f):
|
||||||
if f in ("jpeg", "zlib"):
|
if f in ("jpeg", "zlib"):
|
||||||
|
@ -715,6 +722,9 @@ class pil_build_ext(build_ext):
|
||||||
if feature.tiff:
|
if feature.tiff:
|
||||||
libs.append(feature.tiff)
|
libs.append(feature.tiff)
|
||||||
defs.append(("HAVE_LIBTIFF", None))
|
defs.append(("HAVE_LIBTIFF", None))
|
||||||
|
if feature.xcb:
|
||||||
|
libs.append(feature.xcb)
|
||||||
|
defs.append(("HAVE_XCB", None))
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
libs.extend(["kernel32", "user32", "gdi32"])
|
libs.extend(["kernel32", "user32", "gdi32"])
|
||||||
if struct.unpack("h", b"\0\1")[0] == 1:
|
if struct.unpack("h", b"\0\1")[0] == 1:
|
||||||
|
|
|
@ -22,38 +22,62 @@ import tempfile
|
||||||
|
|
||||||
from . import Image
|
from . import Image
|
||||||
|
|
||||||
if sys.platform not in ["win32", "darwin"]:
|
if sys.platform == "win32":
|
||||||
raise ImportError("ImageGrab is macOS and Windows only")
|
pass
|
||||||
|
elif sys.platform == "darwin":
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import subprocess
|
||||||
|
elif not Image.core.HAVE_XCB:
|
||||||
|
raise ImportError("ImageGrab requires Windows, macOS, or the XCB library")
|
||||||
|
|
||||||
|
|
||||||
def grab(bbox=None, include_layered_windows=False, all_screens=False):
|
def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None):
|
||||||
if sys.platform == "darwin":
|
if xdisplay is None:
|
||||||
fh, filepath = tempfile.mkstemp(".png")
|
if sys.platform == "darwin":
|
||||||
os.close(fh)
|
fh, filepath = tempfile.mkstemp(".png")
|
||||||
subprocess.call(["screencapture", "-x", filepath])
|
os.close(fh)
|
||||||
im = Image.open(filepath)
|
subprocess.call(["screencapture", "-x", filepath])
|
||||||
im.load()
|
im = Image.open(filepath)
|
||||||
os.unlink(filepath)
|
im.load()
|
||||||
if bbox:
|
os.unlink(filepath)
|
||||||
im_cropped = im.crop(bbox)
|
if bbox:
|
||||||
im.close()
|
im_cropped = im.crop(bbox)
|
||||||
return im_cropped
|
im.close()
|
||||||
else:
|
return im_cropped
|
||||||
offset, size, data = Image.core.grabscreen(include_layered_windows, all_screens)
|
return im
|
||||||
im = Image.frombytes(
|
elif sys.platform == "win32":
|
||||||
"RGB",
|
offset, size, data = Image.core.grabscreen(include_layered_windows, all_screens)
|
||||||
size,
|
im = Image.frombytes(
|
||||||
data,
|
"RGB",
|
||||||
# RGB, 32-bit line padding, origin lower left corner
|
size,
|
||||||
"raw",
|
data,
|
||||||
"BGR",
|
# RGB, 32-bit line padding, origin lower left corner
|
||||||
(size[0] * 3 + 3) & -4,
|
"raw",
|
||||||
-1,
|
"BGR",
|
||||||
)
|
(size[0] * 3 + 3) & -4,
|
||||||
if bbox:
|
-1,
|
||||||
x0, y0 = offset
|
)
|
||||||
left, top, right, bottom = bbox
|
if bbox:
|
||||||
im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
|
x0, y0 = offset
|
||||||
|
left, top, right, bottom = bbox
|
||||||
|
im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
|
||||||
|
return im
|
||||||
|
# use xdisplay=None for default display on non-win32/macOS systems
|
||||||
|
if not Image.core.HAVE_XCB:
|
||||||
|
raise OSError("XCB support not included")
|
||||||
|
size, data = Image.core.grabscreen_x11(xdisplay)
|
||||||
|
im = Image.frombytes(
|
||||||
|
"RGB",
|
||||||
|
size,
|
||||||
|
data,
|
||||||
|
"raw",
|
||||||
|
"BGRX",
|
||||||
|
size[0] * 4,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
if bbox:
|
||||||
|
im = im.crop(bbox)
|
||||||
return im
|
return im
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,8 +105,8 @@ def grabclipboard():
|
||||||
im.load()
|
im.load()
|
||||||
os.unlink(filepath)
|
os.unlink(filepath)
|
||||||
return im
|
return im
|
||||||
else:
|
elif sys.platform == "win32":
|
||||||
data = Image.core.grabclipboard()
|
data = Image.core.grabclipboard_win32()
|
||||||
if isinstance(data, bytes):
|
if isinstance(data, bytes):
|
||||||
from . import BmpImagePlugin
|
from . import BmpImagePlugin
|
||||||
import io
|
import io
|
||||||
|
|
|
@ -56,6 +56,7 @@ features = {
|
||||||
"raqm": ("PIL._imagingft", "HAVE_RAQM"),
|
"raqm": ("PIL._imagingft", "HAVE_RAQM"),
|
||||||
"libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO"),
|
"libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO"),
|
||||||
"libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT"),
|
"libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT"),
|
||||||
|
"xcb": ("PIL._imaging", "HAVE_XCB"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -132,6 +133,7 @@ def pilinfo(out=None, supported_formats=True):
|
||||||
("libtiff", "LIBTIFF"),
|
("libtiff", "LIBTIFF"),
|
||||||
("raqm", "RAQM (Bidirectional Text)"),
|
("raqm", "RAQM (Bidirectional Text)"),
|
||||||
("libimagequant", "LIBIMAGEQUANT (Quantization method)"),
|
("libimagequant", "LIBIMAGEQUANT (Quantization method)"),
|
||||||
|
("xcb", "XCB (X protocol)"),
|
||||||
]:
|
]:
|
||||||
if check(name):
|
if check(name):
|
||||||
print("---", feature, "support ok", file=out)
|
print("---", feature, "support ok", file=out)
|
||||||
|
|
|
@ -3781,6 +3781,9 @@ extern PyObject* PyImaging_ListWindowsWin32(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_EventLoopWin32(PyObject* self, PyObject* args);
|
extern PyObject* PyImaging_EventLoopWin32(PyObject* self, PyObject* args);
|
||||||
extern PyObject* PyImaging_DrawWmf(PyObject* self, PyObject* args);
|
extern PyObject* PyImaging_DrawWmf(PyObject* self, PyObject* args);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_XCB
|
||||||
|
extern PyObject* PyImaging_GrabScreenX11(PyObject* self, PyObject* args);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Experimental path stuff (in path.c) */
|
/* Experimental path stuff (in path.c) */
|
||||||
extern PyObject* PyPath_Create(ImagingObject* self, PyObject* args);
|
extern PyObject* PyPath_Create(ImagingObject* self, PyObject* args);
|
||||||
|
@ -3853,13 +3856,16 @@ static PyMethodDef functions[] = {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
{"display", (PyCFunction)PyImaging_DisplayWin32, 1},
|
{"display", (PyCFunction)PyImaging_DisplayWin32, 1},
|
||||||
{"display_mode", (PyCFunction)PyImaging_DisplayModeWin32, 1},
|
{"display_mode", (PyCFunction)PyImaging_DisplayModeWin32, 1},
|
||||||
{"grabscreen", (PyCFunction)PyImaging_GrabScreenWin32, 1},
|
{"grabscreen_win32", (PyCFunction)PyImaging_GrabScreenWin32, 1},
|
||||||
{"grabclipboard", (PyCFunction)PyImaging_GrabClipboardWin32, 1},
|
{"grabclipboard_win32", (PyCFunction)PyImaging_GrabClipboardWin32, 1},
|
||||||
{"createwindow", (PyCFunction)PyImaging_CreateWindowWin32, 1},
|
{"createwindow", (PyCFunction)PyImaging_CreateWindowWin32, 1},
|
||||||
{"eventloop", (PyCFunction)PyImaging_EventLoopWin32, 1},
|
{"eventloop", (PyCFunction)PyImaging_EventLoopWin32, 1},
|
||||||
{"listwindows", (PyCFunction)PyImaging_ListWindowsWin32, 1},
|
{"listwindows", (PyCFunction)PyImaging_ListWindowsWin32, 1},
|
||||||
{"drawwmf", (PyCFunction)PyImaging_DrawWmf, 1},
|
{"drawwmf", (PyCFunction)PyImaging_DrawWmf, 1},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_XCB
|
||||||
|
{"grabscreen_x11", (PyCFunction)PyImaging_GrabScreenX11, 1},
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Utilities */
|
/* Utilities */
|
||||||
{"getcodecstatus", (PyCFunction)_getcodecstatus, 1},
|
{"getcodecstatus", (PyCFunction)_getcodecstatus, 1},
|
||||||
|
@ -3979,6 +3985,12 @@ setup_module(PyObject* m) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_XCB
|
||||||
|
PyModule_AddObject(m, "HAVE_XCB", Py_True);
|
||||||
|
#else
|
||||||
|
PyModule_AddObject(m, "HAVE_XCB", Py_False);
|
||||||
|
#endif
|
||||||
|
|
||||||
PyDict_SetItemString(d, "PILLOW_VERSION", PyUnicode_FromString(version));
|
PyDict_SetItemString(d, "PILLOW_VERSION", PyUnicode_FromString(version));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -815,3 +815,82 @@ error:
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
/* X11 support */
|
||||||
|
|
||||||
|
#ifdef HAVE_XCB
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
/* X11 screen grabber */
|
||||||
|
|
||||||
|
PyObject*
|
||||||
|
PyImaging_GrabScreenX11(PyObject* self, PyObject* args)
|
||||||
|
{
|
||||||
|
int width, height;
|
||||||
|
char* display_name;
|
||||||
|
xcb_connection_t* connection;
|
||||||
|
int screen_number;
|
||||||
|
xcb_screen_iterator_t iter;
|
||||||
|
xcb_screen_t* screen = NULL;
|
||||||
|
xcb_get_image_reply_t* reply;
|
||||||
|
xcb_generic_error_t* error;
|
||||||
|
PyObject* buffer = NULL;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "|z", &display_name))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* connect to X and get screen data */
|
||||||
|
|
||||||
|
connection = xcb_connect(display_name, &screen_number);
|
||||||
|
if (xcb_connection_has_error(connection)) {
|
||||||
|
xcb_disconnect(connection);
|
||||||
|
PyErr_SetString(PyExc_IOError, "X connection failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
|
||||||
|
for (; iter.rem; --screen_number, xcb_screen_next(&iter)) {
|
||||||
|
if (screen_number == 0) {
|
||||||
|
screen = iter.data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (screen == NULL) {
|
||||||
|
xcb_disconnect(connection);
|
||||||
|
PyErr_SetString(PyExc_IOError, "X screen not found");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
width = screen->width_in_pixels;
|
||||||
|
height = screen->height_in_pixels;
|
||||||
|
|
||||||
|
/* get image data */
|
||||||
|
|
||||||
|
reply = xcb_get_image_reply(connection,
|
||||||
|
xcb_get_image(connection, XCB_IMAGE_FORMAT_Z_PIXMAP, screen->root,
|
||||||
|
0, 0, width, height, 0x00ffffff),
|
||||||
|
&error);
|
||||||
|
if (reply == NULL) {
|
||||||
|
free(error);
|
||||||
|
xcb_disconnect(connection);
|
||||||
|
PyErr_SetString(PyExc_IOError, "X get_image failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store data in Python buffer */
|
||||||
|
|
||||||
|
buffer = PyBytes_FromStringAndSize((char*)xcb_get_image_data(reply),
|
||||||
|
xcb_get_image_data_length(reply));
|
||||||
|
|
||||||
|
free(reply);
|
||||||
|
xcb_disconnect(connection);
|
||||||
|
|
||||||
|
if (!buffer)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return Py_BuildValue("(ii)N", width, height, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_XCB */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user