-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Improve headlessness detection for backend selection. #17396
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,68 @@ | ||
#define PY_SSIZE_T_CLEAN | ||
#include <Python.h> | ||
#ifdef __linux__ | ||
#include <dlfcn.h> | ||
#endif | ||
#ifdef _WIN32 | ||
#include <Objbase.h> | ||
#include <Shobjidl.h> | ||
#include <Windows.h> | ||
#endif | ||
|
||
static PyObject* mpl_GetCurrentProcessExplicitAppUserModelID(PyObject* module) | ||
static PyObject* | ||
mpl_display_is_valid(PyObject* module) | ||
{ | ||
#ifdef __linux__ | ||
void* libX11; | ||
// The getenv check is redundant but helps performance as it is much faster | ||
// than dlopen(). | ||
if (getenv("DISPLAY") | ||
&& (libX11 = dlopen("libX11.so.6", RTLD_LAZY))) { | ||
struct Display* display = NULL; | ||
struct Display* (* XOpenDisplay)(char const*) = | ||
dlsym(libX11, "XOpenDisplay"); | ||
int (* XCloseDisplay)(struct Display*) = | ||
dlsym(libX11, "XCloseDisplay"); | ||
if (XOpenDisplay && XCloseDisplay | ||
&& (display = XOpenDisplay(NULL))) { | ||
XCloseDisplay(display); | ||
} | ||
if (dlclose(libX11)) { | ||
PyErr_SetString(PyExc_RuntimeError, dlerror()); | ||
return NULL; | ||
} | ||
if (display) { | ||
Py_RETURN_TRUE; | ||
} | ||
} | ||
void* libwayland_client; | ||
if (getenv("WAYLAND_DISPLAY") | ||
&& (libwayland_client = dlopen("libwayland-client.so.0", RTLD_LAZY))) { | ||
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"); | ||
if (wl_display_connect && wl_display_disconnect | ||
&& (display = wl_display_connect(NULL))) { | ||
wl_display_disconnect(display); | ||
} | ||
if (dlclose(libwayland_client)) { | ||
PyErr_SetString(PyExc_RuntimeError, dlerror()); | ||
return NULL; | ||
} | ||
if (display) { | ||
Py_RETURN_TRUE; | ||
} | ||
} | ||
Py_RETURN_FALSE; | ||
#else | ||
Py_RETURN_TRUE; | ||
#endif | ||
} | ||
|
||
static PyObject* | ||
mpl_GetCurrentProcessExplicitAppUserModelID(PyObject* module) | ||
{ | ||
#ifdef _WIN32 | ||
wchar_t* appid = NULL; | ||
|
@@ -22,7 +78,8 @@ static PyObject* mpl_GetCurrentProcessExplicitAppUserModelID(PyObject* module) | |
#endif | ||
} | ||
|
||
static PyObject* mpl_SetCurrentProcessExplicitAppUserModelID(PyObject* module, PyObject* arg) | ||
static PyObject* | ||
mpl_SetCurrentProcessExplicitAppUserModelID(PyObject* module, PyObject* arg) | ||
{ | ||
#ifdef _WIN32 | ||
wchar_t* appid = PyUnicode_AsWideCharString(arg, NULL); | ||
|
@@ -40,7 +97,8 @@ static PyObject* mpl_SetCurrentProcessExplicitAppUserModelID(PyObject* module, P | |
#endif | ||
} | ||
|
||
static PyObject* mpl_GetForegroundWindow(PyObject* module) | ||
static PyObject* | ||
mpl_GetForegroundWindow(PyObject* module) | ||
{ | ||
#ifdef _WIN32 | ||
return PyLong_FromVoidPtr(GetForegroundWindow()); | ||
|
@@ -49,7 +107,8 @@ static PyObject* mpl_GetForegroundWindow(PyObject* module) | |
#endif | ||
} | ||
|
||
static PyObject* mpl_SetForegroundWindow(PyObject* module, PyObject *arg) | ||
static PyObject* | ||
mpl_SetForegroundWindow(PyObject* module, PyObject *arg) | ||
{ | ||
#ifdef _WIN32 | ||
HWND handle = PyLong_AsVoidPtr(arg); | ||
|
@@ -66,6 +125,12 @@ static PyObject* mpl_SetForegroundWindow(PyObject* module, PyObject *arg) | |
} | ||
|
||
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. On other platforms, always returns True."}, | ||
{"Win32_GetCurrentProcessExplicitAppUserModelID", | ||
(PyCFunction)mpl_GetCurrentProcessExplicitAppUserModelID, METH_NOARGS, | ||
"Win32_GetCurrentProcessExplicitAppUserModelID()\n--\n\n" | ||
|
@@ -83,7 +148,7 @@ static PyMethodDef functions[] = { | |
"always returns None."}, | ||
{"Win32_SetForegroundWindow", | ||
(PyCFunction)mpl_SetForegroundWindow, METH_O, | ||
"Win32_SetForegroundWindow(hwnd)\n--\n\n" | ||
"Win32_SetForegroundWindow(hwnd, /)\n--\n\n" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the slash doing here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This marks |
||
"Wrapper for Windows' SetForegroundWindow. On non-Windows platforms, \n" | ||
"a no-op."}, | ||
{NULL, NULL}}; // sentinel. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would still make sense to check that environment variables DISPLAY, or WAYLAND_DISPLAY exist before hand. First, to avoid libwayland's behavior of defaulting to connect to 'wayland-0', even if no environment variables are set; second, because dlopen is very slow -- one one computer, calling
dlopen
followed bydlclose
onlibX11.so
andlibwayland-client.so
costs between 1.4 msec (marginal cost), 2 msec (initial cost, hot cache), and 6 msec (uncached, initial cost). Importing matplotlib already costs me 240msec every script load. (Aside: It's not necessary to check env variable WAYLAND_SOCKET, because the socket named by it cannot be reused over two successive wl_display_connect calls.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you check the speed of getenv()? (I genuinely do not know whether it is much faster than dlopen().)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getenv is very fast, since the environment variables are provided to a program at the start, just like the command line arguments. My benchmark of getenv uses around 0.0002 msec to call both getenv("WAYLAND_DISPLAY") and getenv("DISPLAY").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, then.