Skip to content
13 changes: 13 additions & 0 deletions buildconfig/stubs/pygame/window.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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.

.. versionadded:: 2.5.7
"""

@property
def utility(self) -> bool:
"""Get if the window is an utility window (**read-only**).
Expand Down
1 change: 1 addition & 0 deletions src_c/doc/window_doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
89 changes: 89 additions & 0 deletions src_c/window.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
#include "doc/sdl2_video_doc.h"
#include "doc/window_doc.h"

#if !SDL_VERSION_ATLEAST(3, 0, 0)
#include <SDL_syswm.h>
#endif

static int is_window_mod_init = 0;

#if !defined(__APPLE__)
Expand Down Expand Up @@ -916,6 +920,90 @@ 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, 0, 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_ANDROID_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_FromSize_t(0);
}

#if defined(SDL_VIDEO_DRIVER_WINDOWS)
if (info.subsystem == SDL_SYSWM_WINDOWS) {
handle = (size_t)info.info.win.window;
}
#endif
#if defined(SDL_VIDEO_DRIVER_X11)
if (info.subsystem == SDL_SYSWM_X11) {
handle = (size_t)info.info.x11.window;
}
#endif
#if defined(SDL_VIDEO_DRIVER_COCOA)
if (info.subsystem == SDL_SYSWM_COCOA) {
handle = (size_t)info.info.cocoa.window;
}
#endif
#if defined(SDL_VIDEO_DRIVER_UIKIT)
if (info.subsystem == SDL_SYSWM_UIKIT) {
handle = (size_t)info.info.uikit.window;
}
#endif
#if defined(SDL_VIDEO_DRIVER_ANDROID)
if (info.subsystem == SDL_SYSWM_ANDROID) {
handle = (size_t)info.info.android.window;
}
#endif
#if defined(SDL_VIDEO_DRIVER_VIVANTE)
if (info.subsystem == SDL_SYSWM_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)
{
Expand Down Expand Up @@ -1433,6 +1521,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 */
};
Expand Down
4 changes: 4 additions & 0 deletions test/window_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
Loading