2010-07-31 06:52:47 +04:00
|
|
|
/*
|
|
|
|
* The Python Imaging Library.
|
|
|
|
*
|
|
|
|
* display support (and other windows-related stuff)
|
|
|
|
*
|
|
|
|
* History:
|
|
|
|
* 1996-05-13 fl Windows DIB support
|
|
|
|
* 1996-05-21 fl Added palette stuff
|
|
|
|
* 1996-05-28 fl Added display_mode stuff
|
|
|
|
* 1997-09-21 fl Added draw primitive
|
|
|
|
* 2001-09-17 fl Added ImagingGrabScreen (from _grabscreen.c)
|
|
|
|
* 2002-05-12 fl Added ImagingListWindows
|
|
|
|
* 2002-11-19 fl Added clipboard support
|
|
|
|
* 2002-11-25 fl Added GetDC/ReleaseDC helpers
|
|
|
|
* 2003-05-21 fl Added create window support (including window callback)
|
|
|
|
* 2003-09-05 fl Added fromstring/tostring methods
|
|
|
|
* 2009-03-14 fl Added WMF support (from pilwmf)
|
|
|
|
*
|
|
|
|
* Copyright (c) 1997-2003 by Secret Labs AB.
|
|
|
|
* Copyright (c) 1996-1997 by Fredrik Lundh.
|
|
|
|
*
|
|
|
|
* See the README file for information on usage and redistribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "Python.h"
|
|
|
|
|
|
|
|
#include "Imaging.h"
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* Windows DIB support */
|
|
|
|
|
2014-05-09 23:05:30 +04:00
|
|
|
#ifdef _WIN32
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
#include "ImDib.h"
|
|
|
|
|
2015-09-19 09:02:40 +03:00
|
|
|
#if SIZEOF_VOID_P == 8
|
|
|
|
#define F_HANDLE "K"
|
|
|
|
#else
|
|
|
|
#define F_HANDLE "k"
|
|
|
|
#endif
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
typedef struct {
|
|
|
|
PyObject_HEAD
|
|
|
|
ImagingDIB dib;
|
|
|
|
} ImagingDisplayObject;
|
|
|
|
|
2012-10-13 20:53:07 +04:00
|
|
|
static PyTypeObject ImagingDisplayType;
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
static ImagingDisplayObject*
|
|
|
|
_new(const char* mode, int xsize, int ysize)
|
|
|
|
{
|
|
|
|
ImagingDisplayObject *display;
|
|
|
|
|
2012-10-13 20:53:07 +04:00
|
|
|
if (PyType_Ready(&ImagingDisplayType) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
display = PyObject_New(ImagingDisplayObject, &ImagingDisplayType);
|
|
|
|
if (display == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
display->dib = ImagingNewDIB(mode, xsize, ysize);
|
|
|
|
if (!display->dib) {
|
|
|
|
Py_DECREF(display);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return display;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_delete(ImagingDisplayObject* display)
|
|
|
|
{
|
|
|
|
if (display->dib)
|
|
|
|
ImagingDeleteDIB(display->dib);
|
|
|
|
PyObject_Del(display);
|
|
|
|
}
|
|
|
|
|
2013-07-01 02:42:19 +04:00
|
|
|
static PyObject*
|
2010-07-31 06:52:47 +04:00
|
|
|
_expose(ImagingDisplayObject* display, PyObject* args)
|
|
|
|
{
|
2015-09-19 09:02:40 +03:00
|
|
|
HDC hdc;
|
|
|
|
if (!PyArg_ParseTuple(args, F_HANDLE, &hdc))
|
2010-07-31 06:52:47 +04:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ImagingExposeDIB(display->dib, hdc);
|
|
|
|
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
2013-07-01 02:42:19 +04:00
|
|
|
static PyObject*
|
2010-07-31 06:52:47 +04:00
|
|
|
_draw(ImagingDisplayObject* display, PyObject* args)
|
|
|
|
{
|
2015-09-19 09:02:40 +03:00
|
|
|
HDC hdc;
|
2010-07-31 06:52:47 +04:00
|
|
|
int dst[4];
|
|
|
|
int src[4];
|
2015-09-19 09:02:40 +03:00
|
|
|
if (!PyArg_ParseTuple(args, F_HANDLE "(iiii)(iiii)", &hdc,
|
2010-07-31 06:52:47 +04:00
|
|
|
dst+0, dst+1, dst+2, dst+3,
|
|
|
|
src+0, src+1, src+2, src+3))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ImagingDrawDIB(display->dib, hdc, dst, src);
|
|
|
|
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern Imaging PyImaging_AsImaging(PyObject *op);
|
|
|
|
|
|
|
|
static PyObject*
|
|
|
|
_paste(ImagingDisplayObject* display, PyObject* args)
|
|
|
|
{
|
|
|
|
Imaging im;
|
|
|
|
|
|
|
|
PyObject* op;
|
|
|
|
int xy[4];
|
|
|
|
xy[0] = xy[1] = xy[2] = xy[3] = 0;
|
|
|
|
if (!PyArg_ParseTuple(args, "O|(iiii)", &op, xy+0, xy+1, xy+2, xy+3))
|
|
|
|
return NULL;
|
|
|
|
im = PyImaging_AsImaging(op);
|
|
|
|
if (!im)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (xy[2] <= xy[0])
|
|
|
|
xy[2] = xy[0] + im->xsize;
|
|
|
|
if (xy[3] <= xy[1])
|
|
|
|
xy[3] = xy[1] + im->ysize;
|
|
|
|
|
|
|
|
ImagingPasteDIB(display->dib, im, xy);
|
|
|
|
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
2013-07-01 02:42:19 +04:00
|
|
|
static PyObject*
|
2010-07-31 06:52:47 +04:00
|
|
|
_query_palette(ImagingDisplayObject* display, PyObject* args)
|
|
|
|
{
|
2015-09-19 09:02:40 +03:00
|
|
|
HDC hdc;
|
2010-07-31 06:52:47 +04:00
|
|
|
int status;
|
|
|
|
|
2015-09-19 09:02:40 +03:00
|
|
|
if (!PyArg_ParseTuple(args, F_HANDLE, &hdc))
|
2010-07-31 06:52:47 +04:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
status = ImagingQueryPaletteDIB(display->dib, hdc);
|
|
|
|
|
|
|
|
return Py_BuildValue("i", status);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject*
|
|
|
|
_getdc(ImagingDisplayObject* display, PyObject* args)
|
|
|
|
{
|
2015-09-19 09:02:40 +03:00
|
|
|
HWND window;
|
2010-07-31 06:52:47 +04:00
|
|
|
HDC dc;
|
|
|
|
|
2015-09-19 09:02:40 +03:00
|
|
|
if (!PyArg_ParseTuple(args, F_HANDLE, &window))
|
2010-07-31 06:52:47 +04:00
|
|
|
return NULL;
|
|
|
|
|
2015-09-19 09:02:40 +03:00
|
|
|
dc = GetDC(window);
|
2010-07-31 06:52:47 +04:00
|
|
|
if (!dc) {
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_SetString(PyExc_OSError, "cannot create dc");
|
2010-07-31 06:52:47 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-09-19 09:02:40 +03:00
|
|
|
return Py_BuildValue(F_HANDLE, dc);
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject*
|
|
|
|
_releasedc(ImagingDisplayObject* display, PyObject* args)
|
|
|
|
{
|
2015-09-19 09:02:40 +03:00
|
|
|
HWND window;
|
|
|
|
HDC dc;
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2015-09-19 09:02:40 +03:00
|
|
|
if (!PyArg_ParseTuple(args, F_HANDLE F_HANDLE, &window, &dc))
|
2010-07-31 06:52:47 +04:00
|
|
|
return NULL;
|
|
|
|
|
2015-09-19 09:02:40 +03:00
|
|
|
ReleaseDC(window, dc);
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject*
|
2012-10-21 01:27:45 +04:00
|
|
|
_frombytes(ImagingDisplayObject* display, PyObject* args)
|
2010-07-31 06:52:47 +04:00
|
|
|
{
|
|
|
|
char* ptr;
|
|
|
|
int bytes;
|
2012-10-14 20:38:06 +04:00
|
|
|
|
|
|
|
if (!PyArg_ParseTuple(args, "y#:frombytes", &ptr, &bytes))
|
|
|
|
return NULL;
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
if (display->dib->ysize * display->dib->linesize != bytes) {
|
|
|
|
PyErr_SetString(PyExc_ValueError, "wrong size");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(display->dib->bits, ptr, bytes);
|
|
|
|
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject*
|
2012-10-21 01:27:45 +04:00
|
|
|
_tobytes(ImagingDisplayObject* display, PyObject* args)
|
2010-07-31 06:52:47 +04:00
|
|
|
{
|
2012-10-14 20:38:06 +04:00
|
|
|
if (!PyArg_ParseTuple(args, ":tobytes"))
|
|
|
|
return NULL;
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2012-10-14 20:38:06 +04:00
|
|
|
return PyBytes_FromStringAndSize(
|
2010-07-31 06:52:47 +04:00
|
|
|
display->dib->bits, display->dib->ysize * display->dib->linesize
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct PyMethodDef methods[] = {
|
|
|
|
{"draw", (PyCFunction)_draw, 1},
|
|
|
|
{"expose", (PyCFunction)_expose, 1},
|
|
|
|
{"paste", (PyCFunction)_paste, 1},
|
|
|
|
{"query_palette", (PyCFunction)_query_palette, 1},
|
|
|
|
{"getdc", (PyCFunction)_getdc, 1},
|
|
|
|
{"releasedc", (PyCFunction)_releasedc, 1},
|
2012-10-21 01:27:45 +04:00
|
|
|
{"frombytes", (PyCFunction)_frombytes, 1},
|
|
|
|
{"tobytes", (PyCFunction)_tobytes, 1},
|
|
|
|
{"fromstring", (PyCFunction)_frombytes, 1},
|
|
|
|
{"tostring", (PyCFunction)_tobytes, 1},
|
2010-07-31 06:52:47 +04:00
|
|
|
{NULL, NULL} /* sentinel */
|
|
|
|
};
|
|
|
|
|
2012-10-13 20:53:07 +04:00
|
|
|
static PyObject*
|
|
|
|
_getattr_mode(ImagingDisplayObject* self, void* closure)
|
2010-07-31 06:52:47 +04:00
|
|
|
{
|
|
|
|
return Py_BuildValue("s", self->dib->mode);
|
2012-10-13 20:53:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject*
|
|
|
|
_getattr_size(ImagingDisplayObject* self, void* closure)
|
|
|
|
{
|
2010-07-31 06:52:47 +04:00
|
|
|
return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize);
|
|
|
|
}
|
|
|
|
|
2012-10-13 20:53:07 +04:00
|
|
|
static struct PyGetSetDef getsetters[] = {
|
|
|
|
{ "mode", (getter) _getattr_mode },
|
|
|
|
{ "size", (getter) _getattr_size },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static PyTypeObject ImagingDisplayType = {
|
|
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
2010-07-31 06:52:47 +04:00
|
|
|
"ImagingDisplay", /*tp_name*/
|
|
|
|
sizeof(ImagingDisplayObject), /*tp_size*/
|
|
|
|
0, /*tp_itemsize*/
|
|
|
|
/* methods */
|
|
|
|
(destructor)_delete, /*tp_dealloc*/
|
|
|
|
0, /*tp_print*/
|
2012-10-13 20:53:07 +04:00
|
|
|
0, /*tp_getattr*/
|
|
|
|
0, /*tp_setattr*/
|
|
|
|
0, /*tp_compare*/
|
|
|
|
0, /*tp_repr*/
|
|
|
|
0, /*tp_as_number */
|
|
|
|
0, /*tp_as_sequence */
|
|
|
|
0, /*tp_as_mapping */
|
|
|
|
0, /*tp_hash*/
|
|
|
|
0, /*tp_call*/
|
|
|
|
0, /*tp_str*/
|
|
|
|
0, /*tp_getattro*/
|
|
|
|
0, /*tp_setattro*/
|
|
|
|
0, /*tp_as_buffer*/
|
|
|
|
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
|
|
0, /*tp_doc*/
|
|
|
|
0, /*tp_traverse*/
|
|
|
|
0, /*tp_clear*/
|
|
|
|
0, /*tp_richcompare*/
|
|
|
|
0, /*tp_weaklistoffset*/
|
|
|
|
0, /*tp_iter*/
|
|
|
|
0, /*tp_iternext*/
|
|
|
|
methods, /*tp_methods*/
|
|
|
|
0, /*tp_members*/
|
|
|
|
getsetters, /*tp_getset*/
|
2010-07-31 06:52:47 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
PyObject*
|
|
|
|
PyImaging_DisplayWin32(PyObject* self, PyObject* args)
|
|
|
|
{
|
|
|
|
ImagingDisplayObject* display;
|
|
|
|
char *mode;
|
|
|
|
int xsize, ysize;
|
|
|
|
|
|
|
|
if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
display = _new(mode, xsize, ysize);
|
|
|
|
if (display == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return (PyObject*) display;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject*
|
|
|
|
PyImaging_DisplayModeWin32(PyObject* self, PyObject* args)
|
|
|
|
{
|
|
|
|
char *mode;
|
|
|
|
int size[2];
|
|
|
|
|
|
|
|
mode = ImagingGetModeDIB(size);
|
|
|
|
|
|
|
|
return Py_BuildValue("s(ii)", mode, size[0], size[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* Windows screen grabber */
|
|
|
|
|
2019-07-25 22:34:58 +03:00
|
|
|
typedef HANDLE(__stdcall* Func_SetThreadDpiAwarenessContext)(HANDLE);
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
PyObject*
|
|
|
|
PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
|
|
|
|
{
|
2019-07-05 00:52:35 +03:00
|
|
|
int x = 0, y = 0, width, height;
|
2019-09-20 18:35:08 +03:00
|
|
|
int includeLayeredWindows = 0, all_screens = 0;
|
2010-07-31 06:52:47 +04:00
|
|
|
HBITMAP bitmap;
|
|
|
|
BITMAPCOREHEADER core;
|
|
|
|
HDC screen, screen_copy;
|
2019-04-26 13:09:46 +03:00
|
|
|
DWORD rop;
|
2010-07-31 06:52:47 +04:00
|
|
|
PyObject* buffer;
|
2019-07-25 22:34:58 +03:00
|
|
|
HANDLE dpiAwareness;
|
2019-08-03 14:52:05 +03:00
|
|
|
HMODULE user32;
|
|
|
|
Func_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContext_function;
|
2013-07-01 02:42:19 +04:00
|
|
|
|
2019-09-20 18:35:08 +03:00
|
|
|
if (!PyArg_ParseTuple(args, "|ii", &includeLayeredWindows, &all_screens))
|
2019-04-26 13:09:46 +03:00
|
|
|
return NULL;
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
/* step 1: create a memory DC large enough to hold the
|
|
|
|
entire screen */
|
|
|
|
|
2013-07-01 02:42:19 +04:00
|
|
|
screen = CreateDC("DISPLAY", NULL, NULL, NULL);
|
|
|
|
screen_copy = CreateCompatibleDC(screen);
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2019-07-25 22:34:58 +03:00
|
|
|
// added in Windows 10 (1607)
|
|
|
|
// loaded dynamically to avoid link errors
|
2019-08-03 14:52:05 +03:00
|
|
|
user32 = LoadLibraryA("User32.dll");
|
2019-07-25 22:34:58 +03:00
|
|
|
SetThreadDpiAwarenessContext_function =
|
|
|
|
(Func_SetThreadDpiAwarenessContext)
|
|
|
|
GetProcAddress(user32, "SetThreadDpiAwarenessContext");
|
|
|
|
if (SetThreadDpiAwarenessContext_function != NULL) {
|
|
|
|
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = ((DPI_CONTEXT_HANDLE)-3)
|
|
|
|
dpiAwareness = SetThreadDpiAwarenessContext_function((HANDLE) -3);
|
|
|
|
}
|
|
|
|
|
2019-09-20 18:35:08 +03:00
|
|
|
if (all_screens) {
|
2019-07-05 00:52:35 +03:00
|
|
|
x = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
|
|
|
y = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
|
|
|
width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
|
|
|
height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
|
|
|
} else {
|
2019-08-17 13:38:54 +03:00
|
|
|
width = GetDeviceCaps(screen, HORZRES);
|
|
|
|
height = GetDeviceCaps(screen, VERTRES);
|
2019-07-05 00:52:35 +03:00
|
|
|
}
|
2013-07-01 02:42:19 +04:00
|
|
|
|
2019-07-25 22:34:58 +03:00
|
|
|
if (SetThreadDpiAwarenessContext_function != NULL) {
|
2019-08-03 14:52:05 +03:00
|
|
|
SetThreadDpiAwarenessContext_function(dpiAwareness);
|
2019-07-25 22:34:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
FreeLibrary(user32);
|
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
bitmap = CreateCompatibleBitmap(screen, width, height);
|
|
|
|
if (!bitmap)
|
|
|
|
goto error;
|
2013-07-01 02:42:19 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
if (!SelectObject(screen_copy, bitmap))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* step 2: copy bits into memory DC bitmap */
|
|
|
|
|
2019-04-26 13:09:46 +03:00
|
|
|
rop = SRCCOPY;
|
|
|
|
if (includeLayeredWindows)
|
|
|
|
rop |= CAPTUREBLT;
|
2019-07-05 00:52:35 +03:00
|
|
|
if (!BitBlt(screen_copy, 0, 0, width, height, screen, x, y, rop))
|
2010-07-31 06:52:47 +04:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* step 3: extract bits from bitmap */
|
|
|
|
|
2013-01-10 11:03:39 +04:00
|
|
|
buffer = PyBytes_FromStringAndSize(NULL, height * ((width*3 + 3) & -4));
|
2010-07-31 06:52:47 +04:00
|
|
|
if (!buffer)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
core.bcSize = sizeof(core);
|
|
|
|
core.bcWidth = width;
|
|
|
|
core.bcHeight = height;
|
|
|
|
core.bcPlanes = 1;
|
|
|
|
core.bcBitCount = 24;
|
2013-01-10 11:03:39 +04:00
|
|
|
if (!GetDIBits(screen_copy, bitmap, 0, height, PyBytes_AS_STRING(buffer),
|
2010-07-31 06:52:47 +04:00
|
|
|
(BITMAPINFO*) &core, DIB_RGB_COLORS))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
DeleteObject(bitmap);
|
|
|
|
DeleteDC(screen_copy);
|
|
|
|
DeleteDC(screen);
|
|
|
|
|
2019-07-05 00:52:35 +03:00
|
|
|
return Py_BuildValue("(ii)(ii)N", x, y, width, height, buffer);
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
error:
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_SetString(PyExc_OSError, "screen grab failed");
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
DeleteDC(screen_copy);
|
|
|
|
DeleteDC(screen);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam)
|
|
|
|
{
|
|
|
|
PyObject* window_list = (PyObject*) lParam;
|
|
|
|
PyObject* item;
|
|
|
|
PyObject* title;
|
|
|
|
RECT inner, outer;
|
|
|
|
int title_size;
|
|
|
|
int status;
|
2013-07-01 02:42:19 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
/* get window title */
|
|
|
|
title_size = GetWindowTextLength(hwnd);
|
|
|
|
if (title_size > 0) {
|
2013-01-10 11:03:39 +04:00
|
|
|
title = PyUnicode_FromStringAndSize(NULL, title_size);
|
2010-07-31 06:52:47 +04:00
|
|
|
if (title)
|
2015-09-19 09:02:40 +03:00
|
|
|
GetWindowTextW(hwnd, PyUnicode_AS_UNICODE(title), title_size+1);
|
2010-07-31 06:52:47 +04:00
|
|
|
} else
|
2013-01-10 11:03:39 +04:00
|
|
|
title = PyUnicode_FromString("");
|
2010-07-31 06:52:47 +04:00
|
|
|
if (!title)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* get bounding boxes */
|
|
|
|
GetClientRect(hwnd, &inner);
|
|
|
|
GetWindowRect(hwnd, &outer);
|
|
|
|
|
|
|
|
item = Py_BuildValue(
|
2015-09-19 09:02:40 +03:00
|
|
|
F_HANDLE "N(iiii)(iiii)", hwnd, title,
|
2010-07-31 06:52:47 +04:00
|
|
|
inner.left, inner.top, inner.right, inner.bottom,
|
|
|
|
outer.left, outer.top, outer.right, outer.bottom
|
|
|
|
);
|
|
|
|
if (!item)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
status = PyList_Append(window_list, item);
|
|
|
|
|
|
|
|
Py_DECREF(item);
|
|
|
|
|
|
|
|
if (status < 0)
|
|
|
|
return 0;
|
2013-07-01 02:42:19 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject*
|
|
|
|
PyImaging_ListWindowsWin32(PyObject* self, PyObject* args)
|
|
|
|
{
|
|
|
|
PyObject* window_list;
|
2013-07-01 02:42:19 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
window_list = PyList_New(0);
|
|
|
|
if (!window_list)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
EnumWindows(list_windows_callback, (LPARAM) window_list);
|
|
|
|
|
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
Py_DECREF(window_list);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return window_list;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* Windows clipboard grabber */
|
|
|
|
|
|
|
|
PyObject*
|
|
|
|
PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
|
|
|
|
{
|
|
|
|
int clip;
|
|
|
|
HANDLE handle;
|
|
|
|
int size;
|
|
|
|
void* data;
|
|
|
|
PyObject* result;
|
2013-07-01 02:42:19 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
clip = OpenClipboard(NULL);
|
|
|
|
/* FIXME: check error status */
|
2013-07-01 02:42:19 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
handle = GetClipboardData(CF_DIB);
|
|
|
|
if (!handle) {
|
|
|
|
/* FIXME: add CF_HDROP support to allow cut-and-paste from
|
|
|
|
the explorer */
|
|
|
|
CloseClipboard();
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = GlobalSize(handle);
|
|
|
|
data = GlobalLock(handle);
|
|
|
|
|
2013-01-10 11:03:39 +04:00
|
|
|
result = PyBytes_FromStringAndSize(data, size);
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
GlobalUnlock(handle);
|
|
|
|
|
|
|
|
CloseClipboard();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* Windows class */
|
|
|
|
|
|
|
|
#ifndef WM_MOUSEWHEEL
|
|
|
|
#define WM_MOUSEWHEEL 522
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int mainloop = 0;
|
|
|
|
|
|
|
|
static void
|
|
|
|
callback_error(const char* handler)
|
|
|
|
{
|
|
|
|
PyObject* sys_stderr;
|
2013-07-01 02:42:19 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
sys_stderr = PySys_GetObject("stderr");
|
|
|
|
|
|
|
|
if (sys_stderr) {
|
|
|
|
PyFile_WriteString("*** ImageWin: error in ", sys_stderr);
|
|
|
|
PyFile_WriteString((char*) handler, sys_stderr);
|
|
|
|
PyFile_WriteString(":\n", sys_stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
PyErr_Print();
|
|
|
|
PyErr_Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
static LRESULT CALLBACK
|
|
|
|
windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
PAINTSTRUCT ps;
|
|
|
|
PyObject* callback = NULL;
|
|
|
|
PyObject* result;
|
|
|
|
PyThreadState* threadstate;
|
|
|
|
PyThreadState* current_threadstate;
|
|
|
|
HDC dc;
|
|
|
|
RECT rect;
|
|
|
|
LRESULT status = 0;
|
|
|
|
|
|
|
|
/* set up threadstate for messages that calls back into python */
|
|
|
|
switch (message) {
|
|
|
|
case WM_CREATE:
|
|
|
|
mainloop++;
|
|
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
|
|
mainloop--;
|
|
|
|
/* fall through... */
|
|
|
|
case WM_PAINT:
|
|
|
|
case WM_SIZE:
|
2015-09-19 09:02:40 +03:00
|
|
|
callback = (PyObject*) GetWindowLongPtr(wnd, 0);
|
2010-07-31 06:52:47 +04:00
|
|
|
if (callback) {
|
|
|
|
threadstate = (PyThreadState*)
|
2015-09-19 09:02:40 +03:00
|
|
|
GetWindowLongPtr(wnd, sizeof(PyObject*));
|
2010-07-31 06:52:47 +04:00
|
|
|
current_threadstate = PyThreadState_Swap(NULL);
|
|
|
|
PyEval_RestoreThread(threadstate);
|
|
|
|
} else
|
|
|
|
return DefWindowProc(wnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* process message */
|
|
|
|
switch (message) {
|
|
|
|
|
|
|
|
case WM_PAINT:
|
|
|
|
/* redraw (part of) window. this generates a WCK-style
|
|
|
|
damage/clear/repair cascade */
|
|
|
|
BeginPaint(wnd, &ps);
|
|
|
|
dc = GetDC(wnd);
|
|
|
|
GetWindowRect(wnd, &rect); /* in screen coordinates */
|
|
|
|
|
|
|
|
result = PyObject_CallFunction(
|
|
|
|
callback, "siiii", "damage",
|
|
|
|
ps.rcPaint.left, ps.rcPaint.top,
|
|
|
|
ps.rcPaint.right, ps.rcPaint.bottom
|
|
|
|
);
|
|
|
|
if (result)
|
|
|
|
Py_DECREF(result);
|
|
|
|
else
|
|
|
|
callback_error("window damage callback");
|
|
|
|
|
|
|
|
result = PyObject_CallFunction(
|
2015-09-19 09:02:40 +03:00
|
|
|
callback, "s" F_HANDLE "iiii", "clear", dc,
|
2010-07-31 06:52:47 +04:00
|
|
|
0, 0, rect.right-rect.left, rect.bottom-rect.top
|
|
|
|
);
|
|
|
|
if (result)
|
|
|
|
Py_DECREF(result);
|
|
|
|
else
|
|
|
|
callback_error("window clear callback");
|
|
|
|
|
|
|
|
result = PyObject_CallFunction(
|
2015-09-19 09:02:40 +03:00
|
|
|
callback, "s" F_HANDLE "iiii", "repair", dc,
|
2010-07-31 06:52:47 +04:00
|
|
|
0, 0, rect.right-rect.left, rect.bottom-rect.top
|
|
|
|
);
|
|
|
|
if (result)
|
|
|
|
Py_DECREF(result);
|
|
|
|
else
|
|
|
|
callback_error("window repair callback");
|
|
|
|
|
|
|
|
ReleaseDC(wnd, dc);
|
|
|
|
EndPaint(wnd, &ps);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_SIZE:
|
|
|
|
/* resize window */
|
|
|
|
result = PyObject_CallFunction(
|
|
|
|
callback, "sii", "resize", LOWORD(lParam), HIWORD(lParam)
|
|
|
|
);
|
|
|
|
if (result) {
|
|
|
|
InvalidateRect(wnd, NULL, 1);
|
|
|
|
Py_DECREF(result);
|
|
|
|
} else
|
|
|
|
callback_error("window resize callback");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_DESTROY:
|
|
|
|
/* destroy window */
|
|
|
|
result = PyObject_CallFunction(callback, "s", "destroy");
|
|
|
|
if (result)
|
|
|
|
Py_DECREF(result);
|
|
|
|
else
|
|
|
|
callback_error("window destroy callback");
|
|
|
|
Py_DECREF(callback);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
status = DefWindowProc(wnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (callback) {
|
|
|
|
/* restore thread state */
|
|
|
|
PyEval_SaveThread();
|
|
|
|
PyThreadState_Swap(threadstate);
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject*
|
|
|
|
PyImaging_CreateWindowWin32(PyObject* self, PyObject* args)
|
|
|
|
{
|
|
|
|
HWND wnd;
|
|
|
|
WNDCLASS windowClass;
|
|
|
|
|
|
|
|
char* title;
|
|
|
|
PyObject* callback;
|
|
|
|
int width = 0, height = 0;
|
|
|
|
if (!PyArg_ParseTuple(args, "sO|ii", &title, &callback, &width, &height))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (width <= 0)
|
|
|
|
width = CW_USEDEFAULT;
|
|
|
|
if (height <= 0)
|
|
|
|
height = CW_USEDEFAULT;
|
|
|
|
|
|
|
|
/* register toplevel window class */
|
|
|
|
windowClass.style = CS_CLASSDC;
|
|
|
|
windowClass.cbClsExtra = 0;
|
|
|
|
windowClass.cbWndExtra = sizeof(PyObject*) + sizeof(PyThreadState*);
|
|
|
|
windowClass.hInstance = GetModuleHandle(NULL);
|
|
|
|
/* windowClass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); */
|
|
|
|
windowClass.hbrBackground = NULL;
|
|
|
|
windowClass.lpszMenuName = NULL;
|
|
|
|
windowClass.lpszClassName = "pilWindow";
|
|
|
|
windowClass.lpfnWndProc = windowCallback;
|
|
|
|
windowClass.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(1));
|
|
|
|
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); /* CROSS? */
|
|
|
|
|
|
|
|
RegisterClass(&windowClass); /* FIXME: check return status */
|
|
|
|
|
|
|
|
wnd = CreateWindowEx(
|
|
|
|
0, windowClass.lpszClassName, title,
|
|
|
|
WS_OVERLAPPEDWINDOW,
|
2013-07-01 02:42:19 +04:00
|
|
|
CW_USEDEFAULT, CW_USEDEFAULT, width, height,
|
2010-07-31 06:52:47 +04:00
|
|
|
HWND_DESKTOP, NULL, NULL, NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!wnd) {
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_SetString(PyExc_OSError, "failed to create window");
|
2010-07-31 06:52:47 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* register window callback */
|
|
|
|
Py_INCREF(callback);
|
2013-02-14 07:10:28 +04:00
|
|
|
SetWindowLongPtr(wnd, 0, (LONG_PTR) callback);
|
|
|
|
SetWindowLongPtr(wnd, sizeof(callback), (LONG_PTR) PyThreadState_Get());
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
Py_BEGIN_ALLOW_THREADS
|
|
|
|
ShowWindow(wnd, SW_SHOWNORMAL);
|
|
|
|
SetForegroundWindow(wnd); /* to make sure it's visible */
|
|
|
|
Py_END_ALLOW_THREADS
|
|
|
|
|
2015-09-19 09:02:40 +03:00
|
|
|
return Py_BuildValue(F_HANDLE, wnd);
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
PyObject*
|
|
|
|
PyImaging_EventLoopWin32(PyObject* self, PyObject* args)
|
|
|
|
{
|
|
|
|
MSG msg;
|
|
|
|
|
|
|
|
Py_BEGIN_ALLOW_THREADS
|
|
|
|
while (mainloop && GetMessage(&msg, NULL, 0, 0)) {
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
}
|
|
|
|
Py_END_ALLOW_THREADS
|
|
|
|
|
|
|
|
Py_INCREF(Py_None);
|
|
|
|
return Py_None;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* windows WMF renderer */
|
|
|
|
|
|
|
|
#define GET32(p,o) ((DWORD*)(p+o))[0]
|
|
|
|
|
|
|
|
PyObject *
|
|
|
|
PyImaging_DrawWmf(PyObject* self, PyObject* args)
|
|
|
|
{
|
|
|
|
HBITMAP bitmap;
|
|
|
|
HENHMETAFILE meta;
|
|
|
|
BITMAPCOREHEADER core;
|
|
|
|
HDC dc;
|
|
|
|
RECT rect;
|
|
|
|
PyObject* buffer = NULL;
|
|
|
|
char* ptr;
|
|
|
|
|
|
|
|
char* data;
|
|
|
|
int datasize;
|
|
|
|
int width, height;
|
|
|
|
int x0, y0, x1, y1;
|
2019-09-26 15:12:28 +03:00
|
|
|
if (!PyArg_ParseTuple(args, "y#(ii)(iiii):_load", &data, &datasize,
|
2010-07-31 06:52:47 +04:00
|
|
|
&width, &height, &x0, &x1, &y0, &y1))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* step 1: copy metafile contents into METAFILE object */
|
|
|
|
|
|
|
|
if (datasize > 22 && GET32(data, 0) == 0x9ac6cdd7) {
|
|
|
|
|
|
|
|
/* placeable windows metafile (22-byte aldus header) */
|
|
|
|
meta = SetWinMetaFileBits(datasize-22, data+22, NULL, NULL);
|
|
|
|
|
|
|
|
} else if (datasize > 80 && GET32(data, 0) == 1 &&
|
|
|
|
GET32(data, 40) == 0x464d4520) {
|
|
|
|
|
|
|
|
/* enhanced metafile */
|
|
|
|
meta = SetEnhMetaFileBits(datasize, data);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* unknown meta format */
|
|
|
|
meta = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!meta) {
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_SetString(PyExc_OSError, "cannot load metafile");
|
2010-07-31 06:52:47 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* step 2: create bitmap */
|
|
|
|
|
|
|
|
core.bcSize = sizeof(core);
|
|
|
|
core.bcWidth = width;
|
|
|
|
core.bcHeight = height;
|
|
|
|
core.bcPlanes = 1;
|
|
|
|
core.bcBitCount = 24;
|
|
|
|
|
|
|
|
dc = CreateCompatibleDC(NULL);
|
|
|
|
|
|
|
|
bitmap = CreateDIBSection(
|
|
|
|
dc, (BITMAPINFO*) &core, DIB_RGB_COLORS, &ptr, NULL, 0
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!bitmap) {
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_SetString(PyExc_OSError, "cannot create bitmap");
|
2010-07-31 06:52:47 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SelectObject(dc, bitmap)) {
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_SetString(PyExc_OSError, "cannot select bitmap");
|
2010-07-31 06:52:47 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* step 3: render metafile into bitmap */
|
|
|
|
|
|
|
|
rect.left = rect.top = 0;
|
|
|
|
rect.right = width;
|
|
|
|
rect.bottom = height;
|
|
|
|
|
|
|
|
/* FIXME: make background transparent? configurable? */
|
|
|
|
FillRect(dc, &rect, GetStockObject(WHITE_BRUSH));
|
|
|
|
|
|
|
|
if (!PlayEnhMetaFile(dc, meta, &rect)) {
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_SetString(PyExc_OSError, "cannot render metafile");
|
2010-07-31 06:52:47 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* step 4: extract bits from bitmap */
|
|
|
|
|
|
|
|
GdiFlush();
|
|
|
|
|
2013-01-10 11:03:39 +04:00
|
|
|
buffer = PyBytes_FromStringAndSize(ptr, height * ((width*3 + 3) & -4));
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
error:
|
|
|
|
DeleteEnhMetaFile(meta);
|
|
|
|
|
|
|
|
if (bitmap)
|
|
|
|
DeleteObject(bitmap);
|
|
|
|
|
|
|
|
DeleteDC(dc);
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2014-05-09 23:05:30 +04:00
|
|
|
#endif /* _WIN32 */
|
2019-09-19 19:57:59 +03:00
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* 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)) {
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_Format(PyExc_OSError, "X connection failed: error %i", xcb_connection_has_error(connection));
|
2019-09-19 19:57:59 +03:00
|
|
|
xcb_disconnect(connection);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2019-09-20 02:14:35 +03:00
|
|
|
if (screen == NULL || screen->root == 0) {
|
|
|
|
// this case is usually caught with "X connection failed: error 6" above
|
2019-09-19 19:57:59 +03:00
|
|
|
xcb_disconnect(connection);
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_SetString(PyExc_OSError, "X screen not found");
|
2019-09-19 19:57:59 +03:00
|
|
|
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) {
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_Format(PyExc_OSError, "X get_image failed: error %i (%i, %i, %i)",
|
2019-09-20 02:14:35 +03:00
|
|
|
error->error_code, error->major_code, error->minor_code, error->resource_id);
|
2019-09-19 19:57:59 +03:00
|
|
|
free(error);
|
|
|
|
xcb_disconnect(connection);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* store data in Python buffer */
|
|
|
|
|
2019-09-20 02:14:35 +03:00
|
|
|
if (reply->depth == 24) {
|
|
|
|
buffer = PyBytes_FromStringAndSize((char*)xcb_get_image_data(reply),
|
|
|
|
xcb_get_image_data_length(reply));
|
|
|
|
} else {
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_Format(PyExc_OSError, "unsupported bit depth: %i", reply->depth);
|
2019-09-20 02:14:35 +03:00
|
|
|
}
|
2019-09-19 19:57:59 +03:00
|
|
|
|
|
|
|
free(reply);
|
|
|
|
xcb_disconnect(connection);
|
|
|
|
|
|
|
|
if (!buffer)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return Py_BuildValue("(ii)N", width, height, buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_XCB */
|