From 24518d61d1cb5f539a459bda7c640c4ed4c1214b Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 16 Sep 2023 00:14:03 -0400 Subject: [PATCH 1/2] Change internal utility extension to C++ This requires a minor bit of typecasting as in the `_tkagg.cpp` file. This is a separate commit from the pybind11 change to improve rename detection. --- ..._internal_utils.c => _c_internal_utils.cpp} | 18 ++++++++++-------- src/meson.build | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) rename src/{_c_internal_utils.c => _c_internal_utils.cpp} (90%) diff --git a/src/_c_internal_utils.c b/src/_c_internal_utils.cpp similarity index 90% rename from src/_c_internal_utils.c rename to src/_c_internal_utils.cpp index 49e5057f1349..a8fc31fa8a7b 100644 --- a/src/_c_internal_utils.c +++ b/src/_c_internal_utils.cpp @@ -24,11 +24,11 @@ mpl_display_is_valid(PyObject* module) // than dlopen(). if (getenv("DISPLAY") && (libX11 = dlopen("libX11.so.6", RTLD_LAZY))) { + typedef struct Display* (*XOpenDisplay_t)(char const*); + typedef int (*XCloseDisplay_t)(struct Display*); struct Display* display = NULL; - struct Display* (* XOpenDisplay)(char const*) = - dlsym(libX11, "XOpenDisplay"); - int (* XCloseDisplay)(struct Display*) = - dlsym(libX11, "XCloseDisplay"); + XOpenDisplay_t XOpenDisplay = (XOpenDisplay_t)dlsym(libX11, "XOpenDisplay"); + XCloseDisplay_t XCloseDisplay = (XCloseDisplay_t)dlsym(libX11, "XCloseDisplay"); if (XOpenDisplay && XCloseDisplay && (display = XOpenDisplay(NULL))) { XCloseDisplay(display); @@ -44,11 +44,13 @@ mpl_display_is_valid(PyObject* module) void* libwayland_client; if (getenv("WAYLAND_DISPLAY") && (libwayland_client = dlopen("libwayland-client.so.0", RTLD_LAZY))) { + typedef struct wl_display* (*wl_display_connect_t)(char const*); + typedef void (*wl_display_disconnect_t)(struct wl_display*); struct wl_display* display = NULL; - struct wl_display* (* wl_display_connect)(char const*) = - dlsym(libwayland_client, "wl_display_connect"); - void (* wl_display_disconnect)(struct wl_display*) = - dlsym(libwayland_client, "wl_display_disconnect"); + wl_display_connect_t wl_display_connect = + (wl_display_connect_t)dlsym(libwayland_client, "wl_display_connect"); + wl_display_disconnect_t wl_display_disconnect = + (wl_display_disconnect_t)dlsym(libwayland_client, "wl_display_disconnect"); if (wl_display_connect && wl_display_disconnect && (display = wl_display_connect(NULL))) { wl_display_disconnect(display); diff --git a/src/meson.build b/src/meson.build index 6a33d2dfb12a..ce7a8cb034aa 100644 --- a/src/meson.build +++ b/src/meson.build @@ -82,7 +82,7 @@ extension_data = { '_c_internal_utils': { 'subdir': 'matplotlib', 'sources': files( - '_c_internal_utils.c', + '_c_internal_utils.cpp', ), 'dependencies': [py3_dep, dl, ole32, shell32, user32], }, From f33f6cfcef3670bcee0ba1f3643ff3d72851b3f1 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 14 Sep 2023 07:25:26 -0400 Subject: [PATCH 2/2] Convert internal utilities to pybind11 --- src/_c_internal_utils.cpp | 175 +++++++++++++++++++------------------- src/meson.build | 2 +- 2 files changed, 88 insertions(+), 89 deletions(-) diff --git a/src/_c_internal_utils.cpp b/src/_c_internal_utils.cpp index a8fc31fa8a7b..813aeb6f7d5a 100644 --- a/src/_c_internal_utils.cpp +++ b/src/_c_internal_utils.cpp @@ -1,11 +1,12 @@ +#include + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN // Windows 10, for latest HiDPI API support. #define WINVER 0x0A00 #define _WIN32_WINNT 0x0A00 #endif -#define PY_SSIZE_T_CLEAN -#include +#include #ifdef __linux__ #include #endif @@ -13,10 +14,16 @@ #include #include #include +#define UNUSED_ON_NON_WINDOWS(x) x +#else +#define UNUSED_ON_NON_WINDOWS Py_UNUSED #endif -static PyObject* -mpl_display_is_valid(PyObject* module) +namespace py = pybind11; +using namespace pybind11::literals; + +static bool +mpl_display_is_valid(void) { #ifdef __linux__ void* libX11; @@ -34,11 +41,10 @@ mpl_display_is_valid(PyObject* module) XCloseDisplay(display); } if (dlclose(libX11)) { - PyErr_SetString(PyExc_RuntimeError, dlerror()); - return NULL; + throw std::runtime_error(dlerror()); } if (display) { - Py_RETURN_TRUE; + return true; } } void* libwayland_client; @@ -56,84 +62,74 @@ mpl_display_is_valid(PyObject* module) wl_display_disconnect(display); } if (dlclose(libwayland_client)) { - PyErr_SetString(PyExc_RuntimeError, dlerror()); - return NULL; + throw std::runtime_error(dlerror()); } if (display) { - Py_RETURN_TRUE; + return true; } } - Py_RETURN_FALSE; + return false; #else - Py_RETURN_TRUE; + return true; #endif } -static PyObject* -mpl_GetCurrentProcessExplicitAppUserModelID(PyObject* module) +static py::object +mpl_GetCurrentProcessExplicitAppUserModelID(void) { #ifdef _WIN32 wchar_t* appid = NULL; HRESULT hr = GetCurrentProcessExplicitAppUserModelID(&appid); if (FAILED(hr)) { - return PyErr_SetFromWindowsErr(hr); + PyErr_SetFromWindowsErr(hr); + throw py::error_already_set(); } - PyObject* py_appid = PyUnicode_FromWideChar(appid, -1); + auto py_appid = py::cast(appid); CoTaskMemFree(appid); return py_appid; #else - Py_RETURN_NONE; + return py::none(); #endif } -static PyObject* -mpl_SetCurrentProcessExplicitAppUserModelID(PyObject* module, PyObject* arg) +static void +mpl_SetCurrentProcessExplicitAppUserModelID(const wchar_t* UNUSED_ON_NON_WINDOWS(appid)) { #ifdef _WIN32 - wchar_t* appid = PyUnicode_AsWideCharString(arg, NULL); - if (!appid) { - return NULL; - } HRESULT hr = SetCurrentProcessExplicitAppUserModelID(appid); - PyMem_Free(appid); if (FAILED(hr)) { - return PyErr_SetFromWindowsErr(hr); + PyErr_SetFromWindowsErr(hr); + throw py::error_already_set(); } - Py_RETURN_NONE; -#else - Py_RETURN_NONE; #endif } -static PyObject* -mpl_GetForegroundWindow(PyObject* module) +static py::object +mpl_GetForegroundWindow(void) { #ifdef _WIN32 - return PyLong_FromVoidPtr(GetForegroundWindow()); + return py::capsule(GetForegroundWindow(), "HWND"); #else - Py_RETURN_NONE; + return py::none(); #endif } -static PyObject* -mpl_SetForegroundWindow(PyObject* module, PyObject *arg) +static void +mpl_SetForegroundWindow(py::capsule UNUSED_ON_NON_WINDOWS(handle_p)) { #ifdef _WIN32 - HWND handle = PyLong_AsVoidPtr(arg); - if (PyErr_Occurred()) { - return NULL; - } - if (!SetForegroundWindow(handle)) { - return PyErr_Format(PyExc_RuntimeError, "Error setting window"); - } - Py_RETURN_NONE; -#else - Py_RETURN_NONE; + if (handle_p.name() != "HWND") { + throw std::runtime_error("Handle must be a value returned from Win32_GetForegroundWindow"); + } + HWND handle = static_cast(handle_p.get_pointer()); + if (!SetForegroundWindow(handle)) { + throw std::runtime_error("Error setting window"); + } #endif } -static PyObject* -mpl_SetProcessDpiAwareness_max(PyObject* module) +static void +mpl_SetProcessDpiAwareness_max(void) { #ifdef _WIN32 #ifdef _DPI_AWARENESS_CONTEXTS_ @@ -171,49 +167,52 @@ mpl_SetProcessDpiAwareness_max(PyObject* module) SetProcessDPIAware(); #endif #endif - Py_RETURN_NONE; } -static PyMethodDef functions[] = { - {"display_is_valid", (PyCFunction)mpl_display_is_valid, METH_NOARGS, - "display_is_valid()\n--\n\n" - "Check whether the current X11 or Wayland display is valid.\n\n" - "On Linux, returns True if either $DISPLAY is set and XOpenDisplay(NULL)\n" - "succeeds, or $WAYLAND_DISPLAY is set and wl_display_connect(NULL)\n" - "succeeds.\n\n" - "On other platforms, always returns True."}, - {"Win32_GetCurrentProcessExplicitAppUserModelID", - (PyCFunction)mpl_GetCurrentProcessExplicitAppUserModelID, METH_NOARGS, - "Win32_GetCurrentProcessExplicitAppUserModelID()\n--\n\n" - "Wrapper for Windows's GetCurrentProcessExplicitAppUserModelID.\n\n" - "On non-Windows platforms, always returns None."}, - {"Win32_SetCurrentProcessExplicitAppUserModelID", - (PyCFunction)mpl_SetCurrentProcessExplicitAppUserModelID, METH_O, - "Win32_SetCurrentProcessExplicitAppUserModelID(appid, /)\n--\n\n" - "Wrapper for Windows's SetCurrentProcessExplicitAppUserModelID.\n\n" - "On non-Windows platforms, does nothing."}, - {"Win32_GetForegroundWindow", - (PyCFunction)mpl_GetForegroundWindow, METH_NOARGS, - "Win32_GetForegroundWindow()\n--\n\n" - "Wrapper for Windows' GetForegroundWindow.\n\n" - "On non-Windows platforms, always returns None."}, - {"Win32_SetForegroundWindow", - (PyCFunction)mpl_SetForegroundWindow, METH_O, - "Win32_SetForegroundWindow(hwnd, /)\n--\n\n" - "Wrapper for Windows' SetForegroundWindow.\n\n" - "On non-Windows platforms, does nothing."}, - {"Win32_SetProcessDpiAwareness_max", - (PyCFunction)mpl_SetProcessDpiAwareness_max, METH_NOARGS, - "Win32_SetProcessDpiAwareness_max()\n--\n\n" - "Set Windows' process DPI awareness to best option available.\n\n" - "On non-Windows platforms, does nothing."}, - {NULL, NULL}}; // sentinel. -static PyModuleDef util_module = { - PyModuleDef_HEAD_INIT, "_c_internal_utils", NULL, 0, functions -}; - -#pragma GCC visibility push(default) -PyMODINIT_FUNC PyInit__c_internal_utils(void) +PYBIND11_MODULE(_c_internal_utils, m) { - return PyModule_Create(&util_module); + m.def( + "display_is_valid", &mpl_display_is_valid, + R"""( -- + Check whether the current X11 or Wayland display is valid. + + On Linux, returns True if either $DISPLAY is set and XOpenDisplay(NULL) + succeeds, or $WAYLAND_DISPLAY is set and wl_display_connect(NULL) + succeeds. + + On other platforms, always returns True.)"""); + m.def( + "Win32_GetCurrentProcessExplicitAppUserModelID", + &mpl_GetCurrentProcessExplicitAppUserModelID, + R"""( -- + Wrapper for Windows's GetCurrentProcessExplicitAppUserModelID. + + On non-Windows platforms, always returns None.)"""); + m.def( + "Win32_SetCurrentProcessExplicitAppUserModelID", + &mpl_SetCurrentProcessExplicitAppUserModelID, + "appid"_a, py::pos_only(), + R"""( -- + Wrapper for Windows's SetCurrentProcessExplicitAppUserModelID. + + On non-Windows platforms, does nothing.)"""); + m.def( + "Win32_GetForegroundWindow", &mpl_GetForegroundWindow, + R"""( -- + Wrapper for Windows' GetForegroundWindow. + + On non-Windows platforms, always returns None.)"""); + m.def( + "Win32_SetForegroundWindow", &mpl_SetForegroundWindow, + "hwnd"_a, + R"""( -- + Wrapper for Windows' SetForegroundWindow. + + On non-Windows platforms, does nothing.)"""); + m.def( + "Win32_SetProcessDpiAwareness_max", &mpl_SetProcessDpiAwareness_max, + R"""( -- + Set Windows' process DPI awareness to best option available. + + On non-Windows platforms, does nothing.)"""); } diff --git a/src/meson.build b/src/meson.build index ce7a8cb034aa..54457d74aceb 100644 --- a/src/meson.build +++ b/src/meson.build @@ -84,7 +84,7 @@ extension_data = { 'sources': files( '_c_internal_utils.cpp', ), - 'dependencies': [py3_dep, dl, ole32, shell32, user32], + 'dependencies': [pybind11_dep, dl, ole32, shell32, user32], }, 'ft2font': { 'subdir': 'matplotlib',