From a96be452e15dbcc59c74b4166178321ece904668 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 4 Jul 2025 13:52:51 +0200 Subject: [PATCH] Dynamic cairo symbol lookup wip --- cairo/cairomodule.c | 5 ++++- cairo/dynamic.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ cairo/dynamic.h | 50 +++++++++++++++++++++++++++++++++++++++++++++ cairo/meson.build | 1 + 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 cairo/dynamic.c create mode 100644 cairo/dynamic.h diff --git a/cairo/cairomodule.c b/cairo/cairomodule.c index d7d1716c..36c963f8 100644 --- a/cairo/cairomodule.c +++ b/cairo/cairomodule.c @@ -33,6 +33,7 @@ #include #include "private.h" +#include "dynamic.h" #ifdef CAIRO_HAS_PDF_SURFACE #include @@ -124,7 +125,9 @@ static Pycairo_CAPI_t CAPI = { static PyObject * pycairo_cairo_version (PyObject *self, PyObject *ignored) { - return PyLong_FromLong (cairo_version()); + RETURN_NULL_IF_NOT_HAS_CAIRO_FUNC(cairo_version); + + return PyLong_FromLong (PYCAIRO_GET_CAIRO_FUNC(cairo_version)()); } static PyObject * diff --git a/cairo/dynamic.c b/cairo/dynamic.c new file mode 100644 index 00000000..e5ed0e2b --- /dev/null +++ b/cairo/dynamic.c @@ -0,0 +1,49 @@ +#include +#ifdef _WIN32 + #include +#else + #define _GNU_SOURCE + #include +#endif + +#include "dynamic.h" + +#define INIT_CAIRO_FUNC(field, symbol_name) \ + .field = {symbol_name, NULL, 0} + +_Pycairo_cairo_funcs_t _Pycairo_cairo_funcs = { + INIT_CAIRO_FUNC(cairo_version, "cairo_version"), +}; + +void* _Pycairo_get_cairo_symbol(const char* symbol_name) { + static void* cairo_handle = NULL; + static int handle_initialized = 0; + + if (!handle_initialized) { + handle_initialized = 1; +#ifdef _WIN32 + HMODULE handle; + if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR)cairo_create, &handle)) { + cairo_handle = handle; + } +#else + Dl_info info; + if (dladdr((void*)cairo_create, &info) != 0) { + cairo_handle = dlopen(info.dli_fname, RTLD_LAZY | RTLD_NOLOAD); + } +#endif + } + + if (!cairo_handle) + return NULL; + +#ifdef _WIN32 + return (void*)GetProcAddress((HMODULE)cairo_handle, symbol_name); +#else + dlerror(); + void* symbol = dlsym(cairo_handle, symbol_name); + return (dlerror() == NULL) ? symbol : NULL; +#endif +} \ No newline at end of file diff --git a/cairo/dynamic.h b/cairo/dynamic.h new file mode 100644 index 00000000..7d893e4e --- /dev/null +++ b/cairo/dynamic.h @@ -0,0 +1,50 @@ +#ifndef _PYCAIRO_DYNAMIC_H_ +#define _PYCAIRO_DYNAMIC_H_ + +#include + +void* _Pycairo_get_cairo_symbol(const char* symbol_name); + +#define _PYCAIRO_DECLARE_CAIRO_FUNC(name, ret_type, params) \ + struct { \ + const char* symbol_name; \ + ret_type (*ptr)params; \ + int checked; \ + } name + +typedef struct { + _PYCAIRO_DECLARE_CAIRO_FUNC(cairo_version, int, (void)); +} _Pycairo_cairo_funcs_t; + +extern _Pycairo_cairo_funcs_t _Pycairo_cairo_funcs; + +#ifdef CAIRO_WIN32_STATIC_BUILD + +#define PYCAIRO_GET_CAIRO_FUNC(field) (field) + +#define RETURN_NULL_IF_NOT_HAS_CAIRO_FUNC(field) + +#define PYCAIRO_HAS_CAIRO_FUNC(field) (1) + +#else + +#define PYCAIRO_GET_CAIRO_FUNC(field) \ + (_Pycairo_cairo_funcs.field.checked ? _Pycairo_cairo_funcs.field.ptr : \ + (_Pycairo_cairo_funcs.field.checked = 1, \ + _Pycairo_cairo_funcs.field.ptr = _Pycairo_get_cairo_symbol(_Pycairo_cairo_funcs.field.symbol_name))) + +#define PYCAIRO_HAS_CAIRO_FUNC(field) \ + (PYCAIRO_GET_CAIRO_FUNC(field) != NULL) + +#define RETURN_NULL_IF_NOT_HAS_CAIRO_FUNC(field) \ + do { \ + if (!PYCAIRO_GET_CAIRO_FUNC(field)) { \ + PyErr_Format(PyExc_RuntimeError, "%s not available in this cairo build", \ + _Pycairo_cairo_funcs.field.symbol_name); \ + return NULL; \ + } \ + } while(0) + +#endif + +#endif // _PYCAIRO_DYNAMIC_H_ diff --git a/cairo/meson.build b/cairo/meson.build index 31069ba1..caa54777 100644 --- a/cairo/meson.build +++ b/cairo/meson.build @@ -22,6 +22,7 @@ sources = [ 'surface.c', 'textcluster.c', 'textextents.c', + 'dynamic.c', ] foreach python_file : python_sources