diff --git a/buildconfig/stubs/pygame/window.pyi b/buildconfig/stubs/pygame/window.pyi index 69d04b3b36..b8af961f44 100644 --- a/buildconfig/stubs/pygame/window.pyi +++ b/buildconfig/stubs/pygame/window.pyi @@ -1,4 +1,4 @@ -from typing import Optional, Tuple, Union +from typing import Optional, Tuple, Dict, Union from typing_extensions import deprecated # added in 3.13 from pygame.typing import Point, RectLike @@ -70,6 +70,8 @@ class Window: def position(self, value: Union[int, Point]) -> None: ... @property def opengl(self) -> bool: ... + @property + def wm_info(self) -> Dict[str, int]: ... @classmethod @deprecated("since 2.4.0. Use either the display module or the Window class with get_surface and flip. Try not to mix display and Window") def from_display_module(cls) -> Window: ... diff --git a/docs/reST/ref/window.rst b/docs/reST/ref/window.rst index 7c78de9dc0..614c285f22 100644 --- a/docs/reST/ref/window.rst +++ b/docs/reST/ref/window.rst @@ -289,6 +289,18 @@ .. versionadded:: 2.5.0 + .. attribute:: wm_info + + | :sl:`Get information about the current windowing system` + | :sg:`wm_info -> dict` + + Creates a dictionary filled with string keys. The strings and values are + arbitrarily created by the system. Some systems may have no information and + an empty dictionary will be returned. Most platforms will return a "window" + key with the value set to the system id for the window. + + .. versionaddedold:: 2.5.3 + .. classmethod:: from_display_module | :sl:`Create a Window object using window data from display module` diff --git a/src_c/doc/window_doc.h b/src_c/doc/window_doc.h index b7fd796810..16394c840e 100644 --- a/src_c/doc/window_doc.h +++ b/src_c/doc/window_doc.h @@ -17,6 +17,7 @@ #define DOC_WINDOW_POSITION "position -> (int, int) or WINDOWPOS_CENTERED or WINDOWPOS_UNDEFINED\nGet or set the window position in screen coordinates" #define DOC_WINDOW_OPACITY "opacity -> float\nGet or set the window opacity, between 0.0 (fully transparent) and 1.0 (fully opaque)" #define DOC_WINDOW_OPENGL "opengl -> bool\nGet if the window supports OpenGL" +#define DOC_WINDOW_WMINFO "wm_info -> dict\nGet information about the current windowing system" #define DOC_WINDOW_FROMDISPLAYMODULE "from_display_module() -> Window\nCreate a Window object using window data from display module" #define DOC_WINDOW_GETSURFACE "get_surface() -> Surface\nGet the window surface" #define DOC_WINDOW_FLIP "flip() -> None\nUpdate the display surface to the window." diff --git a/src_c/window.c b/src_c/window.c index 63b7270d06..43724efd1b 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -8,6 +8,8 @@ #include "doc/sdl2_video_doc.h" #include "doc/window_doc.h" +#include + static int is_window_mod_init = 0; #if !defined(__APPLE__) @@ -779,6 +781,119 @@ window_get_opengl(pgWindowObject *self, void *v) return PyBool_FromLong(hasGL); } +static PyObject * +window_get_wm_info(pgWindowObject *self, void *v) +{ + PyObject *dict; + PyObject *tmp; + SDL_SysWMinfo info; + + SDL_VERSION(&(info.version)) + dict = PyDict_New(); + if (!dict) + return NULL; + + SDL_Window *win = self->_win; + if (!SDL_GetWindowWMInfo(win, &info)) + return dict; + + (void)tmp; +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + tmp = PyLong_FromLongLong((long long)info.info.win.window); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLongLong((long long)info.info.win.hdc); + PyDict_SetItemString(dict, "hdc", tmp); + Py_DECREF(tmp); + tmp = PyLong_FromLongLong((long long)info.info.win.hinstance); + PyDict_SetItemString(dict, "hinstance", tmp); + Py_DECREF(tmp); +#endif +#if defined(SDL_VIDEO_DRIVER_WINRT) + tmp = PyCapsule_New(info.info.winrt.window, "window", NULL); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); +#endif +#if defined(SDL_VIDEO_DRIVER_X11) + tmp = PyLong_FromLong(info.info.x11.window); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); + + tmp = PyCapsule_New(info.info.x11.display, "display", NULL); + PyDict_SetItemString(dict, "display", tmp); + Py_DECREF(tmp); +#endif +#if defined(SDL_VIDEO_DRIVER_DIRECTFB) + tmp = PyCapsule_New(info.info.dfb.dfb, "dfb", NULL); + PyDict_SetItemString(dict, "dfb", tmp); + Py_DECREF(tmp); + + tmp = PyCapsule_New(info.info.dfb.window, "window", NULL); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); + + tmp = PyCapsule_New(info.info.dfb.surface, "surface", NULL); + PyDict_SetItemString(dict, "surface", tmp); + Py_DECREF(tmp); +#endif +#if defined(SDL_VIDEO_DRIVER_COCOA) + tmp = PyCapsule_New(info.info.cocoa.window, "window", NULL); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); +#endif +#if defined(SDL_VIDEO_DRIVER_UIKIT) + tmp = PyCapsule_New(info.info.uikit.window, "window", NULL); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLong(info.info.uikit.framebuffer); + PyDict_SetItemString(dict, "framebuffer", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLong(info.info.uikit.colorbuffer); + PyDict_SetItemString(dict, "colorbuffer", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLong(info.info.uikit.resolveFramebuffer); + PyDict_SetItemString(dict, "resolveFramebuffer", tmp); + Py_DECREF(tmp); +#endif +#if defined(SDL_VIDEO_DRIVER_WAYLAND) + tmp = PyCapsule_New(info.info.wl.display, "display", NULL); + PyDict_SetItemString(dict, "display", tmp); + Py_DECREF(tmp); + + tmp = PyCapsule_New(info.info.wl.surface, "surface", NULL); + PyDict_SetItemString(dict, "surface", tmp); + Py_DECREF(tmp); + + tmp = PyCapsule_New(info.info.wl.shell_surface, "shell_surface", NULL); + PyDict_SetItemString(dict, "shell_surface", tmp); + Py_DECREF(tmp); +#endif +#if defined(SDL_VIDEO_DRIVER_ANDROID) + tmp = PyCapsule_New(info.info.android.window, "window", NULL); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLong((long)info.info.android.surface); + PyDict_SetItemString(dict, "surface", tmp); + Py_DECREF(tmp); +#endif +#if defined(SDL_VIDEO_DRIVER_VIVANTE) + tmp = PyLong_FromLong((long)info.info.vivante.display); + PyDict_SetItemString(dict, "display", tmp); + Py_DECREF(tmp); + + tmp = PyLong_FromLong((long)info.info.vivante.window); + PyDict_SetItemString(dict, "window", tmp); + Py_DECREF(tmp); +#endif + + return dict; +} + static void window_dealloc(pgWindowObject *self, PyObject *_null) { @@ -1195,6 +1310,7 @@ static PyGetSetDef _window_getset[] = { DOC_WINDOW_OPACITY, NULL}, {"id", (getter)window_get_window_id, NULL, DOC_WINDOW_ID, NULL}, {"opengl", (getter)window_get_opengl, NULL, DOC_WINDOW_OPENGL, NULL}, + {"wm_info", (getter)window_get_wm_info, NULL, DOC_WINDOW_WMINFO, NULL}, {NULL, 0, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/test/window_test.py b/test/window_test.py index fd7828393f..18f680c2b3 100644 --- a/test/window_test.py +++ b/test/window_test.py @@ -453,6 +453,41 @@ def test_window_focused(self): window = pygame.Window() self.assertIsInstance(window.focused, bool) + def test_wm_info(self): + window = pygame.Window() + wm_info = window.wm_info + self.assertIsInstance(wm_info, dict) + + wm_info_potential_keys = { + "colorbuffer", + "connection", + "data", + "dfb", + "display", + "framebuffer", + "fswindow", + "hdc", + "hglrc", + "hinstance", + "lock_func", + "resolveFramebuffer", + "shell_surface", + "surface", + "taskHandle", + "unlock_func", + "wimpVersion", + "window", + "wmwindow", + } + + # If any unexpected dict keys are present, they + # will be stored in set wm_info_remaining_keys + wm_info_remaining_keys = set(wm_info.keys()).difference(wm_info_potential_keys) + + # Assert set is empty (& therefore does not + # contain unexpected dict keys) + self.assertFalse(wm_info_remaining_keys) + def tearDown(self): self.win.destroy()