diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index 32dc1b53bf..470dd608d1 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -127,8 +127,9 @@ typedef enum SDL_EventType SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED, /**< Display has changed desktop mode */ SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED, /**< Display has changed current mode */ SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED, /**< Display has changed content scale */ + SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED, /**< Display has changed usable bounds */ SDL_EVENT_DISPLAY_FIRST = SDL_EVENT_DISPLAY_ORIENTATION, - SDL_EVENT_DISPLAY_LAST = SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED, + SDL_EVENT_DISPLAY_LAST = SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED, /* Window events */ /* 0x200 was SDL_WINDOWEVENT, reserve the number for sdl2-compat */ diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index e98af73990..fa1122140a 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -525,6 +525,7 @@ int SDL_GetEventDescription(const SDL_Event *event, char *buf, int buflen) SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED); SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED); SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED); + SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED); #undef SDL_DISPLAYEVENT_CASE #define SDL_WINDOWEVENT_CASE(x) \ diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index bffa61527c..b5ffc9a46f 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -1626,6 +1626,14 @@ void SDLTest_PrintEvent(const SDL_Event *event) event->display.displayID, (int)(scale * 100.0f)); } break; + case SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED: + { + SDL_Rect bounds; + SDL_GetDisplayUsableBounds(event->display.displayID, &bounds); + SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " changed usable bounds to %dx%d at %d,%d", + event->display.displayID, bounds.w, bounds.h, bounds.x, bounds.y); + } + break; case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED: SDL_Log("SDL EVENT: Display %" SDL_PRIu32 " desktop mode changed to %" SDL_PRIs32 "x%" SDL_PRIs32, event->display.displayID, event->display.data1, event->display.data2); diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index d961db5789..a016f0800c 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1064,6 +1064,7 @@ bool SDL_GetDisplayBounds(SDL_DisplayID displayID, SDL_Rect *rect) } if (_this->GetDisplayBounds) { + SDL_zerop(rect); if (_this->GetDisplayBounds(_this, display, rect)) { return true; } @@ -1103,6 +1104,7 @@ bool SDL_GetDisplayUsableBounds(SDL_DisplayID displayID, SDL_Rect *rect) } if (_this->GetDisplayUsableBounds) { + SDL_zerop(rect); if (_this->GetDisplayUsableBounds(_this, display, rect)) { return true; } diff --git a/src/video/cocoa/SDL_cocoamodes.h b/src/video/cocoa/SDL_cocoamodes.h index 37f3aa5840..1dc000c304 100644 --- a/src/video/cocoa/SDL_cocoamodes.h +++ b/src/video/cocoa/SDL_cocoamodes.h @@ -26,6 +26,7 @@ struct SDL_DisplayData { CGDirectDisplayID display; + SDL_Rect usable_bounds; }; struct SDL_DisplayModeData diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m index 4c168de8d3..974ef570b8 100644 --- a/src/video/cocoa/SDL_cocoamodes.m +++ b/src/video/cocoa/SDL_cocoamodes.m @@ -323,6 +323,21 @@ static void Cocoa_GetHDRProperties(CGDirectDisplayID displayID, SDL_HDROutputPro } } +static bool Cocoa_GetUsableBounds(CGDirectDisplayID displayID, SDL_Rect *rect) +{ + NSScreen *screen = GetNSScreenForDisplayID(displayID); + + if (screen == nil) { + return false; + } + + const NSRect frame = [screen visibleFrame]; + rect->x = (int)frame.origin.x; + rect->y = (int)(CGDisplayPixelsHigh(kCGDirectMainDisplay) - frame.origin.y - frame.size.height); + rect->w = (int)frame.size.width; + rect->h = (int)frame.size.height; + return true; +} bool Cocoa_AddDisplay(CGDirectDisplayID display, bool send_event) { @@ -331,7 +346,7 @@ bool Cocoa_AddDisplay(CGDirectDisplayID display, bool send_event) return false; } - SDL_DisplayData *displaydata = (SDL_DisplayData *)SDL_malloc(sizeof(*displaydata)); + SDL_DisplayData *displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata)); if (!displaydata) { CGDisplayModeRelease(moderef); return false; @@ -359,6 +374,8 @@ bool Cocoa_AddDisplay(CGDirectDisplayID display, bool send_event) Cocoa_GetHDRProperties(displaydata->display, &viddisplay.HDR); + Cocoa_GetUsableBounds(displaydata->display, &displaydata->usable_bounds); + viddisplay.desktop_mode = mode; viddisplay.internal = displaydata; const bool retval = SDL_AddVideoDisplay(&viddisplay, send_event); @@ -538,6 +555,13 @@ void Cocoa_UpdateDisplays(SDL_VideoDevice *_this) Cocoa_GetHDRProperties(displaydata->display, &HDR); SDL_SetDisplayHDRProperties(display, &HDR); + + SDL_Rect rect; + if (Cocoa_GetUsableBounds(displaydata->display, &rect) && + SDL_memcmp(&displaydata->usable_bounds, &rect, sizeof(rect)) != 0) { + SDL_memcpy(&displaydata->usable_bounds, &rect, sizeof(rect)); + SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED, 0, 0); + } } } @@ -556,24 +580,10 @@ bool Cocoa_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, S bool Cocoa_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect) { - @autoreleasepool { - SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal; - NSScreen *screen = GetNSScreenForDisplayID(displaydata->display); + SDL_DisplayData *displaydata = (SDL_DisplayData *)display->internal; - if (screen == nil) { - return SDL_SetError("Couldn't get NSScreen for display"); - } - - { - const NSRect frame = [screen visibleFrame]; - rect->x = (int)frame.origin.x; - rect->y = (int)(CGDisplayPixelsHigh(kCGDirectMainDisplay) - frame.origin.y - frame.size.height); - rect->w = (int)frame.size.width; - rect->h = (int)frame.size.height; - } - - return true; - } + SDL_memcpy(rect, &displaydata->usable_bounds, sizeof(*rect)); + return true; } bool Cocoa_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 667223c27c..9854767c30 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -2422,6 +2422,9 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara if (wParam == SPI_SETMOUSE || wParam == SPI_SETMOUSESPEED) { WIN_UpdateMouseSystemScale(); } + if (wParam == SPI_SETWORKAREA) { + WIN_UpdateDisplayUsableBounds(SDL_GetVideoDevice()); + } break; #endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 833f78c937..6123d8232c 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -933,6 +933,14 @@ void WIN_RefreshDisplays(SDL_VideoDevice *_this) } } +void WIN_UpdateDisplayUsableBounds(SDL_VideoDevice *_this) +{ + // This almost never happens, so just go ahead and send update events for all displays + for (int i = 0; i < _this->num_displays; ++i) { + SDL_SendDisplayEvent(_this->displays[i], SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED, 0, 0); + } +} + void WIN_QuitModes(SDL_VideoDevice *_this) { // All fullscreen windows should have restored modes by now diff --git a/src/video/windows/SDL_windowsmodes.h b/src/video/windows/SDL_windowsmodes.h index e49817ca95..7942a670a5 100644 --- a/src/video/windows/SDL_windowsmodes.h +++ b/src/video/windows/SDL_windowsmodes.h @@ -50,6 +50,7 @@ extern bool WIN_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay extern bool WIN_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display); extern bool WIN_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode); extern void WIN_RefreshDisplays(SDL_VideoDevice *_this); +extern void WIN_UpdateDisplayUsableBounds(SDL_VideoDevice *_this); extern void WIN_QuitModes(SDL_VideoDevice *_this); #endif // SDL_windowsmodes_h_ diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 1299be7368..15066f95a2 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -1418,31 +1418,33 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) X11_UpdateKeymap(_this, true); } - } else if (xevent->type == PropertyNotify && videodata && videodata->windowlist) { + } else if (xevent->type == PropertyNotify && videodata) { char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom); - - if (SDL_strncmp(name_of_atom, "_ICC_PROFILE", sizeof("_ICC_PROFILE") - 1) == 0) { - XWindowAttributes attrib; - int screennum; - for (i = 0; i < videodata->numwindows; ++i) { - if (videodata->windowlist[i] != NULL) { - data = videodata->windowlist[i]; - X11_XGetWindowAttributes(display, data->xwindow, &attrib); - screennum = X11_XScreenNumberOfScreen(attrib.screen); - if (screennum == 0 && SDL_strcmp(name_of_atom, "_ICC_PROFILE") == 0) { - SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0); - } else if (SDL_strncmp(name_of_atom, "_ICC_PROFILE_", sizeof("_ICC_PROFILE_") - 1) == 0 && SDL_strlen(name_of_atom) > sizeof("_ICC_PROFILE_") - 1) { - int iccscreennum = SDL_atoi(&name_of_atom[sizeof("_ICC_PROFILE_") - 1]); - - if (screennum == iccscreennum) { + if (name_of_atom) { + if (SDL_startswith(name_of_atom, "_ICC_PROFILE")) { + XWindowAttributes attrib; + int screennum; + for (i = 0; i < videodata->numwindows; ++i) { + if (videodata->windowlist[i] != NULL) { + data = videodata->windowlist[i]; + X11_XGetWindowAttributes(display, data->xwindow, &attrib); + screennum = X11_XScreenNumberOfScreen(attrib.screen); + if (screennum == 0 && SDL_strcmp(name_of_atom, "_ICC_PROFILE") == 0) { SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0); + } else if (SDL_strncmp(name_of_atom, "_ICC_PROFILE_", sizeof("_ICC_PROFILE_") - 1) == 0 && SDL_strlen(name_of_atom) > sizeof("_ICC_PROFILE_") - 1) { + int iccscreennum = SDL_atoi(&name_of_atom[sizeof("_ICC_PROFILE_") - 1]); + + if (screennum == iccscreennum) { + SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0); + } } } } + } else if (SDL_strcmp(name_of_atom, "_NET_WORKAREA") == 0) { + for (i = 0; i < _this->num_displays; ++i) { + SDL_SendDisplayEvent(_this->displays[i], SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED, 0, 0); + } } - } - - if (name_of_atom) { X11_XFree(name_of_atom); } }