Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion buildconfig/stubs/pygame/display.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ required).
"""

from collections.abc import Iterable
from typing import Literal, Optional, Union, overload
from typing import Literal, Optional, overload

from pygame._sdl2 import Window
from pygame.constants import FULLSCREEN
from pygame.rect import Rect
from pygame.surface import Surface
from pygame.typing import (
ColorLike,
Expand Down Expand Up @@ -421,6 +422,24 @@ def get_desktop_sizes() -> list[tuple[int, int]]:
.. versionaddedold:: 2.0.0
"""

def get_desktop_usable_bounds() -> list[Rect]:
"""Get the bounding rects of the usable area of active desktops.

Returns a list of :class:`pygame.Rect` representing the bounding rectangles
of the usable area of the currently configured virtual desktops.

The usable area is the desktop area minus the areas that are used by
the operating system. This includes, for example, taskbars. The location
and size of the unusable areas depend on the operating system and its
configuration. The usable area is usually the area that maximized windows
fill.

The length of the list is not the same as the number of attached monitors,
as a desktop can be mirrored across multiple monitors.

.. versionadded:: 2.5.6
"""

def list_modes(
depth: int = 0,
flags: int = FULLSCREEN,
Expand Down
61 changes: 61 additions & 0 deletions src_c/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -2296,6 +2296,65 @@ pg_get_desktop_screen_sizes(PyObject *self, PyObject *_null)
return result;
}

static PyObject *
pg_get_desktop_usable_bounds(PyObject *self, PyObject *_null)
{
int display_count, i;
PyObject *result = NULL;

VIDEO_INIT_CHECK();

#if PG_SDL3
SDL_DisplayID *displays = SDL_GetDisplays(&display_count);
if (displays == NULL) {
return RAISE(pgExc_SDLError, SDL_GetError());
}
#else
display_count = SDL_GetNumVideoDisplays();
if (display_count < 0) {
return RAISE(pgExc_SDLError, SDL_GetError());
}
#endif

result = PyList_New(display_count);
if (!result) {
#if PG_SDL3
SDL_free(displays);
#endif
return NULL;
}

for (i = 0; i < display_count; i++) {
SDL_Rect bounds;
#if PG_SDL3
SDL_DisplayID display_id = displays[i];
if (SDL_GetDisplayUsableBounds(display_id, &bounds) == SDL_FALSE) {
Py_DECREF(result);
SDL_free(displays);
return RAISE(pgExc_SDLError, SDL_GetError());
}
#else
if (SDL_GetDisplayUsableBounds(i, &bounds) < 0) {
Py_DECREF(result);
return RAISE(pgExc_SDLError, SDL_GetError());
}
#endif
PyObject *pg_rect = pgRect_New(&bounds);
if (pg_rect == NULL) {
#if PG_SDL3
SDL_free(displays);
#endif
Py_DECREF(result);
return NULL; /* exception already set */
}
PyList_SET_ITEM(result, i, pg_rect);
}

#if PG_SDL3
SDL_free(displays);
#endif
return result;
}
static PyObject *
pg_is_fullscreen(PyObject *self, PyObject *_null)
{
Expand Down Expand Up @@ -3130,6 +3189,8 @@ static PyMethodDef _pg_display_methods[] = {
METH_NOARGS, "provisional API, subject to change"},
{"get_desktop_sizes", (PyCFunction)pg_get_desktop_screen_sizes,
METH_NOARGS, DOC_DISPLAY_GETDESKTOPSIZES},
{"get_desktop_usable_bounds", (PyCFunction)pg_get_desktop_usable_bounds,
METH_NOARGS, DOC_DISPLAY_GETDESKTOPUSABLEBOUNDS},
{"is_fullscreen", (PyCFunction)pg_is_fullscreen, METH_NOARGS,
DOC_DISPLAY_ISFULLSCREEN},
{"is_vsync", (PyCFunction)pg_is_vsync, METH_NOARGS, DOC_DISPLAY_ISVSYNC},
Expand Down
1 change: 1 addition & 0 deletions src_c/doc/display_doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define DOC_DISPLAY_INFO "Info() -> _VidInfo\nCreate a video display information object."
#define DOC_DISPLAY_GETWMINFO "get_wm_info() -> dict[str, int]\nGet information about the current windowing system."
#define DOC_DISPLAY_GETDESKTOPSIZES "get_desktop_sizes() -> list[tuple[int, int]]\nGet sizes of active desktops."
#define DOC_DISPLAY_GETDESKTOPUSABLEBOUNDS "get_desktop_usable_bounds() -> list[Rect]\nGet the bounding rects of the usable area of active desktops."
#define DOC_DISPLAY_LISTMODES "list_modes(depth=0, flags=FULLSCREEN, display=0) -> list[tuple[int, int]]\nGet list of available fullscreen modes."
#define DOC_DISPLAY_MODEOK "mode_ok(size, flags=0, depth=0, display=0) -> int\nPick the best color depth for a display mode."
#define DOC_DISPLAY_GLGETATTRIBUTE "gl_get_attribute(flag, /) -> int\nGet the value for an OpenGL flag for the current display."
Expand Down
10 changes: 10 additions & 0 deletions test/display_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,16 @@ def test_get_set_window_position(self):
self.assertEqual(position[0], 420)
self.assertEqual(position[1], 360)

def test_get_desktop_usable_bounds(self):
bounds = pygame.display.get_desktop_usable_bounds()
sizes = pygame.display.get_desktop_sizes()
self.assertIsInstance(bounds, list)
for i, bound in enumerate(bounds):
self.assertIsInstance(bound, pygame.Rect)
size = sizes[i]
self.assertLessEqual(bound.w, size[0])
self.assertLessEqual(bound.h, size[1])


class DisplayUpdateTest(unittest.TestCase):
def question(self, qstr):
Expand Down
Loading