_c_internal_utils.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #define PY_SSIZE_T_CLEAN
  2. #include <Python.h>
  3. #ifdef __linux__
  4. #include <dlfcn.h>
  5. #endif
  6. #ifdef _WIN32
  7. #include <Objbase.h>
  8. #include <Shobjidl.h>
  9. #include <Windows.h>
  10. #endif
  11. static PyObject*
  12. mpl_display_is_valid(PyObject* module)
  13. {
  14. #ifdef __linux__
  15. void* libX11;
  16. // The getenv check is redundant but helps performance as it is much faster
  17. // than dlopen().
  18. if (getenv("DISPLAY")
  19. && (libX11 = dlopen("libX11.so.6", RTLD_LAZY))) {
  20. struct Display* display = NULL;
  21. struct Display* (* XOpenDisplay)(char const*) =
  22. dlsym(libX11, "XOpenDisplay");
  23. int (* XCloseDisplay)(struct Display*) =
  24. dlsym(libX11, "XCloseDisplay");
  25. if (XOpenDisplay && XCloseDisplay
  26. && (display = XOpenDisplay(NULL))) {
  27. XCloseDisplay(display);
  28. }
  29. if (dlclose(libX11)) {
  30. PyErr_SetString(PyExc_RuntimeError, dlerror());
  31. return NULL;
  32. }
  33. if (display) {
  34. Py_RETURN_TRUE;
  35. }
  36. }
  37. void* libwayland_client;
  38. if (getenv("WAYLAND_DISPLAY")
  39. && (libwayland_client = dlopen("libwayland-client.so.0", RTLD_LAZY))) {
  40. struct wl_display* display = NULL;
  41. struct wl_display* (* wl_display_connect)(char const*) =
  42. dlsym(libwayland_client, "wl_display_connect");
  43. void (* wl_display_disconnect)(struct wl_display*) =
  44. dlsym(libwayland_client, "wl_display_disconnect");
  45. if (wl_display_connect && wl_display_disconnect
  46. && (display = wl_display_connect(NULL))) {
  47. wl_display_disconnect(display);
  48. }
  49. if (dlclose(libwayland_client)) {
  50. PyErr_SetString(PyExc_RuntimeError, dlerror());
  51. return NULL;
  52. }
  53. if (display) {
  54. Py_RETURN_TRUE;
  55. }
  56. }
  57. Py_RETURN_FALSE;
  58. #else
  59. Py_RETURN_TRUE;
  60. #endif
  61. }
  62. static PyObject*
  63. mpl_GetCurrentProcessExplicitAppUserModelID(PyObject* module)
  64. {
  65. #ifdef _WIN32
  66. wchar_t* appid = NULL;
  67. HRESULT hr = GetCurrentProcessExplicitAppUserModelID(&appid);
  68. if (FAILED(hr)) {
  69. return PyErr_SetFromWindowsErr(hr);
  70. }
  71. PyObject* py_appid = PyUnicode_FromWideChar(appid, -1);
  72. CoTaskMemFree(appid);
  73. return py_appid;
  74. #else
  75. Py_RETURN_NONE;
  76. #endif
  77. }
  78. static PyObject*
  79. mpl_SetCurrentProcessExplicitAppUserModelID(PyObject* module, PyObject* arg)
  80. {
  81. #ifdef _WIN32
  82. wchar_t* appid = PyUnicode_AsWideCharString(arg, NULL);
  83. if (!appid) {
  84. return NULL;
  85. }
  86. HRESULT hr = SetCurrentProcessExplicitAppUserModelID(appid);
  87. PyMem_Free(appid);
  88. if (FAILED(hr)) {
  89. return PyErr_SetFromWindowsErr(hr);
  90. }
  91. Py_RETURN_NONE;
  92. #else
  93. Py_RETURN_NONE;
  94. #endif
  95. }
  96. static PyObject*
  97. mpl_GetForegroundWindow(PyObject* module)
  98. {
  99. #ifdef _WIN32
  100. return PyLong_FromVoidPtr(GetForegroundWindow());
  101. #else
  102. Py_RETURN_NONE;
  103. #endif
  104. }
  105. static PyObject*
  106. mpl_SetForegroundWindow(PyObject* module, PyObject *arg)
  107. {
  108. #ifdef _WIN32
  109. HWND handle = PyLong_AsVoidPtr(arg);
  110. if (PyErr_Occurred()) {
  111. return NULL;
  112. }
  113. if (!SetForegroundWindow(handle)) {
  114. return PyErr_Format(PyExc_RuntimeError, "Error setting window");
  115. }
  116. Py_RETURN_NONE;
  117. #else
  118. Py_RETURN_NONE;
  119. #endif
  120. }
  121. static PyObject*
  122. mpl_SetProcessDpiAwareness_max(PyObject* module)
  123. {
  124. #ifdef _WIN32
  125. #ifdef _DPI_AWARENESS_CONTEXTS_
  126. // These functions and options were added in later Windows 10 updates, so
  127. // must be loaded dynamically.
  128. typedef BOOL (WINAPI *IsValidDpiAwarenessContext_t)(DPI_AWARENESS_CONTEXT);
  129. typedef BOOL (WINAPI *SetProcessDpiAwarenessContext_t)(DPI_AWARENESS_CONTEXT);
  130. HMODULE user32 = LoadLibrary("user32.dll");
  131. IsValidDpiAwarenessContext_t IsValidDpiAwarenessContextPtr =
  132. (IsValidDpiAwarenessContext_t)GetProcAddress(
  133. user32, "IsValidDpiAwarenessContext");
  134. SetProcessDpiAwarenessContext_t SetProcessDpiAwarenessContextPtr =
  135. (SetProcessDpiAwarenessContext_t)GetProcAddress(
  136. user32, "SetProcessDpiAwarenessContext");
  137. DPI_AWARENESS_CONTEXT ctxs[3] = {
  138. DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, // Win10 Creators Update
  139. DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, // Win10
  140. DPI_AWARENESS_CONTEXT_SYSTEM_AWARE}; // Win10
  141. if (IsValidDpiAwarenessContextPtr != NULL
  142. && SetProcessDpiAwarenessContextPtr != NULL) {
  143. for (int i = 0; i < sizeof(ctxs) / sizeof(DPI_AWARENESS_CONTEXT); ++i) {
  144. if (IsValidDpiAwarenessContextPtr(ctxs[i])) {
  145. SetProcessDpiAwarenessContextPtr(ctxs[i]);
  146. break;
  147. }
  148. }
  149. } else {
  150. // Added in Windows Vista.
  151. SetProcessDPIAware();
  152. }
  153. FreeLibrary(user32);
  154. #else
  155. // Added in Windows Vista.
  156. SetProcessDPIAware();
  157. #endif
  158. #endif
  159. Py_RETURN_NONE;
  160. }
  161. static PyMethodDef functions[] = {
  162. {"display_is_valid", (PyCFunction)mpl_display_is_valid, METH_NOARGS,
  163. "display_is_valid()\n--\n\n"
  164. "Check whether the current X11 or Wayland display is valid.\n\n"
  165. "On Linux, returns True if either $DISPLAY is set and XOpenDisplay(NULL)\n"
  166. "succeeds, or $WAYLAND_DISPLAY is set and wl_display_connect(NULL)\n"
  167. "succeeds.\n\n"
  168. "On other platforms, always returns True."},
  169. {"Win32_GetCurrentProcessExplicitAppUserModelID",
  170. (PyCFunction)mpl_GetCurrentProcessExplicitAppUserModelID, METH_NOARGS,
  171. "Win32_GetCurrentProcessExplicitAppUserModelID()\n--\n\n"
  172. "Wrapper for Windows's GetCurrentProcessExplicitAppUserModelID.\n\n"
  173. "On non-Windows platforms, always returns None."},
  174. {"Win32_SetCurrentProcessExplicitAppUserModelID",
  175. (PyCFunction)mpl_SetCurrentProcessExplicitAppUserModelID, METH_O,
  176. "Win32_SetCurrentProcessExplicitAppUserModelID(appid, /)\n--\n\n"
  177. "Wrapper for Windows's SetCurrentProcessExplicitAppUserModelID.\n\n"
  178. "On non-Windows platforms, does nothing."},
  179. {"Win32_GetForegroundWindow",
  180. (PyCFunction)mpl_GetForegroundWindow, METH_NOARGS,
  181. "Win32_GetForegroundWindow()\n--\n\n"
  182. "Wrapper for Windows' GetForegroundWindow.\n\n"
  183. "On non-Windows platforms, always returns None."},
  184. {"Win32_SetForegroundWindow",
  185. (PyCFunction)mpl_SetForegroundWindow, METH_O,
  186. "Win32_SetForegroundWindow(hwnd, /)\n--\n\n"
  187. "Wrapper for Windows' SetForegroundWindow.\n\n"
  188. "On non-Windows platforms, does nothing."},
  189. {"Win32_SetProcessDpiAwareness_max",
  190. (PyCFunction)mpl_SetProcessDpiAwareness_max, METH_NOARGS,
  191. "Win32_SetProcessDpiAwareness_max()\n--\n\n"
  192. "Set Windows' process DPI awareness to best option available.\n\n"
  193. "On non-Windows platforms, does nothing."},
  194. {NULL, NULL}}; // sentinel.
  195. static PyModuleDef util_module = {
  196. PyModuleDef_HEAD_INIT, "_c_internal_utils", NULL, 0, functions
  197. };
  198. #pragma GCC visibility push(default)
  199. PyMODINIT_FUNC PyInit__c_internal_utils(void)
  200. {
  201. return PyModule_Create(&util_module);
  202. }