diff --git a/buildconfig/stubs/pygame/window.pyi b/buildconfig/stubs/pygame/window.pyi index 3dfe2411a6..a3073b85fa 100644 --- a/buildconfig/stubs/pygame/window.pyi +++ b/buildconfig/stubs/pygame/window.pyi @@ -312,6 +312,19 @@ class Window: .. versionadded:: 2.5.0 """ + @property + def handle(self) -> int: + """Get the window handle provided by the window manager if supported otherwise 0 + + Returns the window handle provided by the window manager as an integer. If the operating + system is not supported or the window manager hides the handle the sentinel ``0`` is returned. + + The handle is generally available with Windows, X11 (Linux), Cocoa (MacOS), UIKit (iOS), + Android and Vivante while unavailable under Wayland and everything else. + + .. versionaddedold:: 2.5.4 + """ + @property def utility(self) -> bool: """Get if the window is an utility window (**read-only**). diff --git a/src_c/doc/window_doc.h b/src_c/doc/window_doc.h index 6495da670e..32790cf8f9 100644 --- a/src_c/doc/window_doc.h +++ b/src_c/doc/window_doc.h @@ -17,6 +17,7 @@ #define DOC_WINDOW_POSITION "position -> tuple[int, int]\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_HANDLE "handle -> int\nGet the window handle provided by the window manager if supported otherwise 0" #define DOC_WINDOW_UTILITY "utility -> bool\nGet if the window is an utility window (**read-only**)." #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." diff --git a/src_c/window.c b/src_c/window.c index 8e0b1bde50..371d08850b 100644 --- a/src_c/window.c +++ b/src_c/window.c @@ -8,6 +8,10 @@ #include "doc/sdl2_video_doc.h" #include "doc/window_doc.h" +#if !SDL_VERSION_ATLEAST(3, 0, 0) +#include +#endif + static int is_window_mod_init = 0; #if !defined(__APPLE__) @@ -916,6 +920,78 @@ window_get_opengl(pgWindowObject *self, void *v) return PyBool_FromLong(hasGL); } +static PyObject * +window_get_handle(pgWindowObject *self, void *v) +{ + SDL_Window *win = self->_win; + size_t handle = 0; + +#if SDL_VERSION_ATLEAST(3, 2, 0) + const char *driver = SDL_GetCurrentVideoDriver(); + if (driver == NULL) { + handle = 0; + return PyLong_FromSize_t(handle); + } + + SDL_PropertiesID props = SDL_GetWindowProperties(win); + + if (!strcmp(driver, "windows")) { + handle = (size_t)SDL_GetPointerProperty( + props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); + } + else if (!strcmp(driver, "x11")) { + handle = (size_t)SDL_GetNumberProperty( + props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0); + } + else if (!strcmp(driver, "cocoa")) { + handle = (size_t)SDL_GetPointerProperty( + props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, NULL); + } + else if (!strcmp(driver, "uikit")) { + handle = (size_t)SDL_GetPointerProperty( + props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, NULL); + } + else if (!strcmp(driver, "android")) { + handle = (size_t)SDL_GetPointerProperty( + props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, NULL); + } + else if (!strcmp(driver, "vivante")) { + handle = (size_t)SDL_GetPointerProperty( + props, SDL_PROP_WINDOW_VIVANTE_WINDOW_POINTER, NULL); + } + +#else // sdl 2 + SDL_SysWMinfo info; + + SDL_VERSION(&(info.version)) + + if (!SDL_GetWindowWMInfo(win, &info)) { + return PyLong_FromLong(0); + } + +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + handle = (size_t)info.info.win.window; +#endif +#if defined(SDL_VIDEO_DRIVER_X11) + handle = (size_t)info.info.x11.window; +#endif +#if defined(SDL_VIDEO_DRIVER_COCOA) + handle = (size_t)info.info.cocoa.window; +#endif +#if defined(SDL_VIDEO_DRIVER_UIKIT) + handle = (size_t)info.info.uikit.window; +#endif +#if defined(SDL_VIDEO_DRIVER_ANDROID) + handle = (size_t)info.info.android.window; +#endif +#if defined(SDL_VIDEO_DRIVER_VIVANTE) + handle = (size_t)info.info.vivante.window; +#endif +#endif // sdl 3 + + return PyLong_FromSize_t(handle); +} + static PyObject * window_get_utility(pgWindowObject *self, void *v) { @@ -1433,6 +1509,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}, + {"handle", (getter)window_get_handle, NULL, DOC_WINDOW_HANDLE, NULL}, {"utility", (getter)window_get_utility, NULL, DOC_WINDOW_UTILITY, NULL}, {NULL, 0, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/test/window_test.py b/test/window_test.py index f30ab08bc5..4d044a867c 100644 --- a/test/window_test.py +++ b/test/window_test.py @@ -458,6 +458,10 @@ def test_window_focused(self): window = pygame.Window() self.assertIsInstance(window.focused, bool) + def test_handle(self): + window = pygame.Window() + self.assertIsInstance(window.handle, int) + def tearDown(self): self.win.destroy()