/* -*- mode: c++; c-basic-offset: 4 -*- */
/*
* This code is derived from The Python Imaging Library and is covered
* by the PIL license.
*
* See LICENSE/LICENSE.PIL for details.
*
*/
#define PY_SSIZE_T_CLEAN
#include
#include
#include
#include
#include // agg:int8u
#ifdef _WIN32
#include
#endif
// Include our own excerpts from the Tcl / Tk headers
#include "_tkmini.h"
#include "py_converters.h"
#if defined(_MSC_VER)
# define IMG_FORMAT "%d %d %Iu"
#else
# define IMG_FORMAT "%d %d %zu"
#endif
#define BBOX_FORMAT "%f %f %f %f"
typedef struct
{
PyObject_HEAD
Tcl_Interp *interp;
} TkappObject;
// Global vars for Tcl / Tk functions. We load these symbols from the tkinter
// extension module or loaded Tcl / Tk libraries at run-time.
static Tcl_CreateCommand_t TCL_CREATE_COMMAND;
static Tcl_AppendResult_t TCL_APPEND_RESULT;
static Tk_MainWindow_t TK_MAIN_WINDOW;
static Tk_FindPhoto_t TK_FIND_PHOTO;
static Tk_PhotoPutBlock_NoComposite_t TK_PHOTO_PUT_BLOCK_NO_COMPOSITE;
static Tk_PhotoBlank_t TK_PHOTO_BLANK;
static PyObject *mpl_tk_blit(PyObject *self, PyObject *args)
{
Tcl_Interp *interp;
char const *photo_name;
int height, width;
unsigned char *data_ptr;
int o0, o1, o2, o3;
int x1, x2, y1, y2;
Tk_PhotoHandle photo;
Tk_PhotoImageBlock block;
if (!PyArg_ParseTuple(args, "O&s(iiO&)(iiii)(iiii):blit",
convert_voidptr, &interp, &photo_name,
&height, &width, convert_voidptr, &data_ptr,
&o0, &o1, &o2, &o3,
&x1, &x2, &y1, &y2)) {
goto exit;
}
if (!(photo = TK_FIND_PHOTO(interp, photo_name))) {
PyErr_SetString(PyExc_ValueError, "Failed to extract Tk_PhotoHandle");
goto exit;
}
if (0 > y1 || y1 > y2 || y2 > height || 0 > x1 || x1 > x2 || x2 > width) {
PyErr_SetString(PyExc_ValueError, "Attempting to draw out of bounds");
goto exit;
}
block.pixelPtr = data_ptr + 4 * ((height - y2) * width + x1);
block.width = x2 - x1;
block.height = y2 - y1;
block.pitch = 4 * width;
block.pixelSize = 4;
block.offset[0] = o0;
block.offset[1] = o1;
block.offset[2] = o2;
block.offset[3] = o3;
TK_PHOTO_PUT_BLOCK_NO_COMPOSITE(
photo, &block, x1, height - y2, x2 - x1, y2 - y1);
exit:
if (PyErr_Occurred()) {
return NULL;
} else {
Py_RETURN_NONE;
}
}
#ifdef _WIN32
static PyObject *
Win32_GetForegroundWindow(PyObject *module, PyObject *args)
{
HWND handle = GetForegroundWindow();
if (!PyArg_ParseTuple(args, ":GetForegroundWindow")) {
return NULL;
}
return PyLong_FromSize_t((size_t)handle);
}
static PyObject *
Win32_SetForegroundWindow(PyObject *module, PyObject *args)
{
HWND handle;
if (!PyArg_ParseTuple(args, "n:SetForegroundWindow", &handle)) {
return NULL;
}
if (!SetForegroundWindow(handle)) {
return PyErr_Format(PyExc_RuntimeError, "Error setting window");
}
Py_INCREF(Py_None);
return Py_None;
}
#endif
static PyMethodDef functions[] = {
/* Tkinter interface stuff */
{ "blit", (PyCFunction)mpl_tk_blit, METH_VARARGS },
#ifdef _WIN32
{ "Win32_GetForegroundWindow", (PyCFunction)Win32_GetForegroundWindow, METH_VARARGS },
{ "Win32_SetForegroundWindow", (PyCFunction)Win32_SetForegroundWindow, METH_VARARGS },
#endif
{ NULL, NULL } /* sentinel */
};
// Functions to fill global TCL / Tk function pointers by dynamic loading
#ifdef _WIN32
/*
* On Windows, we can't load the tkinter module to get the TCL or Tk symbols,
* because Windows does not load symbols into the library name-space of
* importing modules. So, knowing that tkinter has already been imported by
* Python, we scan all modules in the running process for the TCL and Tk
* function names.
*/
#define PSAPI_VERSION 1
#include
// Must be linked with 'psapi' library
FARPROC _dfunc(HMODULE lib_handle, const char *func_name)
{
// Load function `func_name` from `lib_handle`.
// Set Python exception if we can't find `func_name` in `lib_handle`.
// Returns function pointer or NULL if not present.
char message[100];
FARPROC func = GetProcAddress(lib_handle, func_name);
if (func == NULL) {
sprintf(message, "Cannot load function %s", func_name);
PyErr_SetString(PyExc_RuntimeError, message);
}
return func;
}
int get_tcl(HMODULE hMod)
{
// Try to fill TCL global vars with function pointers. Return 0 for no
// functions found, 1 for all functions found, -1 for some but not all
// functions found.
TCL_CREATE_COMMAND = (Tcl_CreateCommand_t)
GetProcAddress(hMod, "Tcl_CreateCommand");
if (TCL_CREATE_COMMAND == NULL) { // Maybe not TCL module
return 0;
}
TCL_APPEND_RESULT = (Tcl_AppendResult_t) _dfunc(hMod, "Tcl_AppendResult");
return (TCL_APPEND_RESULT == NULL) ? -1 : 1;
}
int get_tk(HMODULE hMod)
{
// Try to fill Tk global vars with function pointers. Return 0 for no
// functions found, 1 for all functions found, -1 for some but not all
// functions found.
TK_MAIN_WINDOW = (Tk_MainWindow_t)
GetProcAddress(hMod, "Tk_MainWindow");
if (TK_MAIN_WINDOW == NULL) { // Maybe not Tk module
return 0;
}
return // -1 if any remaining symbols are NULL
((TK_FIND_PHOTO = (Tk_FindPhoto_t)
_dfunc(hMod, "Tk_FindPhoto")) == NULL) ||
((TK_PHOTO_PUT_BLOCK_NO_COMPOSITE = (Tk_PhotoPutBlock_NoComposite_t)
_dfunc(hMod, "Tk_PhotoPutBlock_NoComposite")) == NULL) ||
((TK_PHOTO_BLANK = (Tk_PhotoBlank_t)
_dfunc(hMod, "Tk_PhotoBlank")) == NULL)
? -1 : 1;
}
void load_tkinter_funcs(void)
{
// Load TCL and Tk functions by searching all modules in current process.
// Sets an error on failure.
HMODULE hMods[1024];
HANDLE hProcess;
DWORD cbNeeded;
unsigned int i;
int found_tcl = 0;
int found_tk = 0;
// Returns pseudo-handle that does not need to be closed
hProcess = GetCurrentProcess();
// Iterate through modules in this process looking for TCL / Tk names
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
if (!found_tcl) {
found_tcl = get_tcl(hMods[i]);
if (found_tcl == -1) {
return;
}
}
if (!found_tk) {
found_tk = get_tk(hMods[i]);
if (found_tk == -1) {
return;
}
}
if (found_tcl && found_tk) {
return;
}
}
}
if (found_tcl == 0) {
PyErr_SetString(PyExc_RuntimeError, "Could not find TCL routines");
} else {
PyErr_SetString(PyExc_RuntimeError, "Could not find Tk routines");
}
return;
}
#else // not Windows
/*
* On Unix, we can get the TCL and Tk symbols from the tkinter module, because
* tkinter uses these symbols, and the symbols are therefore visible in the
* tkinter dynamic library (module).
*/
#include
void *_dfunc(void *lib_handle, const char *func_name)
{
// Load function `func_name` from `lib_handle`.
// Set Python exception if we can't find `func_name` in `lib_handle`.
// Returns function pointer or NULL if not present.
void* func;
// Reset errors.
dlerror();
func = dlsym(lib_handle, func_name);
if (func == NULL) {
PyErr_SetString(PyExc_RuntimeError, dlerror());
}
return func;
}
int _func_loader(void *lib)
{
// Fill global function pointers from dynamic lib.
// Return 1 if any pointer is NULL, 0 otherwise.
return
((TCL_CREATE_COMMAND = (Tcl_CreateCommand_t)
_dfunc(lib, "Tcl_CreateCommand")) == NULL) ||
((TCL_APPEND_RESULT = (Tcl_AppendResult_t)
_dfunc(lib, "Tcl_AppendResult")) == NULL) ||
((TK_MAIN_WINDOW = (Tk_MainWindow_t)
_dfunc(lib, "Tk_MainWindow")) == NULL) ||
((TK_FIND_PHOTO = (Tk_FindPhoto_t)
_dfunc(lib, "Tk_FindPhoto")) == NULL) ||
((TK_PHOTO_PUT_BLOCK_NO_COMPOSITE = (Tk_PhotoPutBlock_NoComposite_t)
_dfunc(lib, "Tk_PhotoPutBlock_NoComposite")) == NULL) ||
((TK_PHOTO_BLANK = (Tk_PhotoBlank_t)
_dfunc(lib, "Tk_PhotoBlank")) == NULL);
}
void load_tkinter_funcs(void)
{
// Load tkinter global funcs from tkinter compiled module.
// Sets an error on failure.
void *main_program, *tkinter_lib;
PyObject *module = NULL, *py_path = NULL, *py_path_b = NULL;
char *path;
// Try loading from the main program namespace first.
main_program = dlopen(NULL, RTLD_LAZY);
if (_func_loader(main_program) == 0) {
goto exit;
}
// Clear exception triggered when we didn't find symbols above.
PyErr_Clear();
// Handle PyPy first, as that import will correctly fail on CPython.
module = PyImport_ImportModule("_tkinter.tklib_cffi"); // PyPy
if (!module) {
PyErr_Clear();
module = PyImport_ImportModule("_tkinter"); // CPython
}
if (!(module &&
(py_path = PyObject_GetAttrString(module, "__file__")) &&
(py_path_b = PyUnicode_EncodeFSDefault(py_path)) &&
(path = PyBytes_AsString(py_path_b)))) {
goto exit;
}
tkinter_lib = dlopen(path, RTLD_LAZY);
if (!tkinter_lib) {
PyErr_SetString(PyExc_RuntimeError, dlerror());
goto exit;
}
_func_loader(tkinter_lib);
// dlclose is safe because tkinter has been imported.
dlclose(tkinter_lib);
goto exit;
exit:
Py_XDECREF(module);
Py_XDECREF(py_path);
Py_XDECREF(py_path_b);
}
#endif // end not Windows
static PyModuleDef _tkagg_module = {
PyModuleDef_HEAD_INIT, "_tkagg", "", -1, functions, NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC PyInit__tkagg(void)
{
load_tkinter_funcs();
return PyErr_Occurred() ? NULL : PyModule_Create(&_tkagg_module);
}