1+ #include < stdexcept>
2+
13#ifdef _WIN32
24#define WIN32_LEAN_AND_MEAN
35// Windows 10, for latest HiDPI API support.
46#define WINVER 0x0A00
57#define _WIN32_WINNT 0x0A00
68#endif
7- #define PY_SSIZE_T_CLEAN
8- #include < Python.h>
9+ #include < pybind11/pybind11.h>
910#ifdef __linux__
1011#include < dlfcn.h>
1112#endif
1213#ifdef _WIN32
1314#include < Objbase.h>
1415#include < Shobjidl.h>
1516#include < Windows.h>
17+ #define UNUSED_ON_NON_WINDOWS (x ) x
18+ #else
19+ #define UNUSED_ON_NON_WINDOWS Py_UNUSED
1620#endif
1721
18- static PyObject*
19- mpl_display_is_valid (PyObject* module )
22+ namespace py = pybind11;
23+
24+ static bool
25+ mpl_display_is_valid (void )
2026{
2127#ifdef __linux__
2228 void * libX11;
@@ -34,11 +40,10 @@ mpl_display_is_valid(PyObject* module)
3440 XCloseDisplay (display);
3541 }
3642 if (dlclose (libX11)) {
37- PyErr_SetString (PyExc_RuntimeError, dlerror ());
38- return NULL ;
43+ throw std::runtime_error (dlerror ());
3944 }
4045 if (display) {
41- Py_RETURN_TRUE ;
46+ return true ;
4247 }
4348 }
4449 void * libwayland_client;
@@ -56,84 +61,74 @@ mpl_display_is_valid(PyObject* module)
5661 wl_display_disconnect (display);
5762 }
5863 if (dlclose (libwayland_client)) {
59- PyErr_SetString (PyExc_RuntimeError, dlerror ());
60- return NULL ;
64+ throw std::runtime_error (dlerror ());
6165 }
6266 if (display) {
63- Py_RETURN_TRUE ;
67+ return true ;
6468 }
6569 }
66- Py_RETURN_FALSE ;
70+ return false ;
6771#else
68- Py_RETURN_TRUE ;
72+ return true ;
6973#endif
7074}
7175
72- static PyObject*
73- mpl_GetCurrentProcessExplicitAppUserModelID (PyObject* module )
76+ static py::object
77+ mpl_GetCurrentProcessExplicitAppUserModelID (void )
7478{
7579#ifdef _WIN32
7680 wchar_t * appid = NULL ;
7781 HRESULT hr = GetCurrentProcessExplicitAppUserModelID (&appid);
7882 if (FAILED (hr)) {
79- return PyErr_SetFromWindowsErr (hr);
83+ PyErr_SetFromWindowsErr (hr);
84+ throw py::error_already_set ();
8085 }
81- PyObject* py_appid = PyUnicode_FromWideChar (appid, - 1 );
86+ auto py_appid = py::cast (appid);
8287 CoTaskMemFree (appid);
8388 return py_appid;
8489#else
85- Py_RETURN_NONE ;
90+ return py::none () ;
8691#endif
8792}
8893
89- static PyObject*
90- mpl_SetCurrentProcessExplicitAppUserModelID (PyObject* module , PyObject* arg )
94+ static void
95+ mpl_SetCurrentProcessExplicitAppUserModelID (const wchar_t * UNUSED_ON_NON_WINDOWS (appid) )
9196{
9297#ifdef _WIN32
93- wchar_t * appid = PyUnicode_AsWideCharString (arg, NULL );
94- if (!appid) {
95- return NULL ;
96- }
9798 HRESULT hr = SetCurrentProcessExplicitAppUserModelID (appid);
98- PyMem_Free (appid);
9999 if (FAILED (hr)) {
100- return PyErr_SetFromWindowsErr (hr);
100+ PyErr_SetFromWindowsErr (hr);
101+ throw py::error_already_set ();
101102 }
102- Py_RETURN_NONE;
103- #else
104- Py_RETURN_NONE;
105103#endif
106104}
107105
108- static PyObject*
109- mpl_GetForegroundWindow (PyObject* module )
106+ static py::object
107+ mpl_GetForegroundWindow (void )
110108{
111109#ifdef _WIN32
112- return PyLong_FromVoidPtr (GetForegroundWindow ());
110+ return py::capsule (GetForegroundWindow (), " HWND " );
113111#else
114- Py_RETURN_NONE ;
112+ return py::none () ;
115113#endif
116114}
117115
118- static PyObject*
119- mpl_SetForegroundWindow (PyObject* module , PyObject *arg )
116+ static void
117+ mpl_SetForegroundWindow (py::capsule UNUSED_ON_NON_WINDOWS (handle_p) )
120118{
121119#ifdef _WIN32
122- HWND handle = PyLong_AsVoidPtr (arg);
123- if (PyErr_Occurred ()) {
124- return NULL ;
125- }
126- if (!SetForegroundWindow (handle)) {
127- return PyErr_Format (PyExc_RuntimeError, " Error setting window" );
128- }
129- Py_RETURN_NONE;
130- #else
131- Py_RETURN_NONE;
120+ if (handle_p.name () != " HWND" ) {
121+ throw std::runtime_error (" Handle must be a value returned from Win32_GetForegroundWindow" );
122+ }
123+ HWND handle = static_cast <HWND>(handle_p.get_pointer ());
124+ if (!SetForegroundWindow (handle)) {
125+ throw std::runtime_error (" Error setting window" );
126+ }
132127#endif
133128}
134129
135- static PyObject*
136- mpl_SetProcessDpiAwareness_max (PyObject* module )
130+ static void
131+ mpl_SetProcessDpiAwareness_max (void )
137132{
138133#ifdef _WIN32
139134#ifdef _DPI_AWARENESS_CONTEXTS_
@@ -171,49 +166,52 @@ mpl_SetProcessDpiAwareness_max(PyObject* module)
171166 SetProcessDPIAware ();
172167#endif
173168#endif
174- Py_RETURN_NONE;
175169}
176170
177- static PyMethodDef functions[] = {
178- {" display_is_valid" , (PyCFunction)mpl_display_is_valid, METH_NOARGS,
179- " display_is_valid()\n --\n\n "
180- " Check whether the current X11 or Wayland display is valid.\n\n "
181- " On Linux, returns True if either $DISPLAY is set and XOpenDisplay(NULL)\n "
182- " succeeds, or $WAYLAND_DISPLAY is set and wl_display_connect(NULL)\n "
183- " succeeds.\n\n "
184- " On other platforms, always returns True." },
185- {" Win32_GetCurrentProcessExplicitAppUserModelID" ,
186- (PyCFunction)mpl_GetCurrentProcessExplicitAppUserModelID, METH_NOARGS,
187- " Win32_GetCurrentProcessExplicitAppUserModelID()\n --\n\n "
188- " Wrapper for Windows's GetCurrentProcessExplicitAppUserModelID.\n\n "
189- " On non-Windows platforms, always returns None." },
190- {" Win32_SetCurrentProcessExplicitAppUserModelID" ,
191- (PyCFunction)mpl_SetCurrentProcessExplicitAppUserModelID, METH_O,
192- " Win32_SetCurrentProcessExplicitAppUserModelID(appid, /)\n --\n\n "
193- " Wrapper for Windows's SetCurrentProcessExplicitAppUserModelID.\n\n "
194- " On non-Windows platforms, does nothing." },
195- {" Win32_GetForegroundWindow" ,
196- (PyCFunction)mpl_GetForegroundWindow, METH_NOARGS,
197- " Win32_GetForegroundWindow()\n --\n\n "
198- " Wrapper for Windows' GetForegroundWindow.\n\n "
199- " On non-Windows platforms, always returns None." },
200- {" Win32_SetForegroundWindow" ,
201- (PyCFunction)mpl_SetForegroundWindow, METH_O,
202- " Win32_SetForegroundWindow(hwnd, /)\n --\n\n "
203- " Wrapper for Windows' SetForegroundWindow.\n\n "
204- " On non-Windows platforms, does nothing." },
205- {" Win32_SetProcessDpiAwareness_max" ,
206- (PyCFunction)mpl_SetProcessDpiAwareness_max, METH_NOARGS,
207- " Win32_SetProcessDpiAwareness_max()\n --\n\n "
208- " Set Windows' process DPI awareness to best option available.\n\n "
209- " On non-Windows platforms, does nothing." },
210- {NULL , NULL }}; // sentinel.
211- static PyModuleDef util_module = {
212- PyModuleDef_HEAD_INIT, " _c_internal_utils" , NULL , 0 , functions
213- };
214-
215- #pragma GCC visibility push(default)
216- PyMODINIT_FUNC PyInit__c_internal_utils (void )
171+ PYBIND11_MODULE (_c_internal_utils, m)
217172{
218- return PyModule_Create (&util_module);
173+ m.def (
174+ " display_is_valid" , &mpl_display_is_valid,
175+ R"""( --
176+ Check whether the current X11 or Wayland display is valid.
177+
178+ On Linux, returns True if either $DISPLAY is set and XOpenDisplay(NULL)
179+ succeeds, or $WAYLAND_DISPLAY is set and wl_display_connect(NULL)
180+ succeeds.
181+
182+ On other platforms, always returns True.)""" );
183+ m.def (
184+ " Win32_GetCurrentProcessExplicitAppUserModelID" ,
185+ &mpl_GetCurrentProcessExplicitAppUserModelID,
186+ R"""( --
187+ Wrapper for Windows's GetCurrentProcessExplicitAppUserModelID.
188+
189+ On non-Windows platforms, always returns None.)""" );
190+ m.def (
191+ " Win32_SetCurrentProcessExplicitAppUserModelID" ,
192+ &mpl_SetCurrentProcessExplicitAppUserModelID,
193+ py::arg (" appid" ), py::pos_only (),
194+ R"""( --
195+ Wrapper for Windows's SetCurrentProcessExplicitAppUserModelID.
196+
197+ On non-Windows platforms, does nothing.)""" );
198+ m.def (
199+ " Win32_GetForegroundWindow" , &mpl_GetForegroundWindow,
200+ R"""( --
201+ Wrapper for Windows' GetForegroundWindow.
202+
203+ On non-Windows platforms, always returns None.)""" );
204+ m.def (
205+ " Win32_SetForegroundWindow" , &mpl_SetForegroundWindow,
206+ py::arg (" hwnd" ),
207+ R"""( --
208+ Wrapper for Windows' SetForegroundWindow.
209+
210+ On non-Windows platforms, does nothing.)""" );
211+ m.def (
212+ " Win32_SetProcessDpiAwareness_max" , &mpl_SetProcessDpiAwareness_max,
213+ R"""( --
214+ Set Windows' process DPI awareness to best option available.
215+
216+ On non-Windows platforms, does nothing.)""" );
219217}
0 commit comments