From c3114975db76b4288122975e235fa2708908c3f4 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 4 Jan 2016 23:52:40 -0500 Subject: [PATCH] Added SDL_GetDisplayUsableBounds(). --- .hgignore | 1 + include/SDL_video.h | 19 ++++++++++++++ src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + src/video/SDL_sysvideo.h | 5 ++++ src/video/SDL_video.c | 21 ++++++++++++++- src/video/cocoa/SDL_cocoamodes.h | 1 + src/video/cocoa/SDL_cocoamodes.m | 36 +++++++++++++++++++++++++ src/video/cocoa/SDL_cocoavideo.m | 1 + src/video/uikit/SDL_uikitmodes.h | 1 + src/video/uikit/SDL_uikitmodes.m | 20 ++++++++++++++ src/video/uikit/SDL_uikitvideo.m | 1 + src/video/windows/SDL_windowsmodes.c | 37 ++++++++++++++++++++++++++ src/video/windows/SDL_windowsmodes.h | 1 + src/video/windows/SDL_windowsvideo.c | 1 + src/video/x11/SDL_x11modes.c | 37 ++++++++++++++++++++++++++ src/video/x11/SDL_x11modes.h | 1 + src/video/x11/SDL_x11video.c | 1 + test/Makefile.in | 4 +++ test/testbounds.c | 39 ++++++++++++++++++++++++++++ 20 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 test/testbounds.c diff --git a/.hgignore b/.hgignore index 55c0667932a60..5fa33f7820bbe 100644 --- a/.hgignore +++ b/.hgignore @@ -116,6 +116,7 @@ test/testtimer test/testver test/testviewport test/testwm2 +test/testbounds test/torturethread test/testdisplayinfo test/*.exe diff --git a/include/SDL_video.h b/include/SDL_video.h index dde0fd18342a5..c9f351a666fe6 100644 --- a/include/SDL_video.h +++ b/include/SDL_video.h @@ -311,6 +311,25 @@ extern DECLSPEC int SDLCALL SDL_GetDisplayBounds(int displayIndex, SDL_Rect * re */ extern DECLSPEC int SDLCALL SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi); +/** + * \brief Get the usable desktop area represented by a display, with the + * primary display located at 0,0 + * + * This is the same area as SDL_GetDisplayBounds() reports, but with portions + * reserved by the system removed. For example, on Mac OS X, this subtracts + * the area occupied by the menu bar and dock. + * + * Setting a window to be fullscreen generally bypasses these unusable areas, + * so these are good guidelines for the maximum space available to a + * non-fullscreen window. + * + * \return 0 on success, or -1 if the index is out of range. + * + * \sa SDL_GetDisplayBounds() + * \sa SDL_GetNumVideoDisplays() + */ +extern DECLSPEC int SDLCALL SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect * rect); + /** * \brief Returns the number of available display modes. * diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index c9ebfffe22f3c..9292582329e1f 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -597,3 +597,4 @@ #define SDL_JoystickCurrentPowerLevel SDL_JoystickCurrentPowerLevel_REAL #define SDL_GameControllerFromInstanceID SDL_GameControllerFromInstanceID_REAL #define SDL_JoystickFromInstanceID SDL_JoystickFromInstanceID_REAL +#define SDL_GetDisplayUsableBounds SDL_GetDisplayUsableBounds_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 3f11a25f972fd..7146492e38f67 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -631,3 +631,4 @@ SDL_DYNAPI_PROC(int,SDL_GetDisplayDPI,(int a, float *b, float *c, float *d),(a,b SDL_DYNAPI_PROC(SDL_JoystickPowerLevel,SDL_JoystickCurrentPowerLevel,(SDL_Joystick *a),(a),return) SDL_DYNAPI_PROC(SDL_GameController*,SDL_GameControllerFromInstanceID,(SDL_JoystickID a),(a),return) SDL_DYNAPI_PROC(SDL_Joystick*,SDL_JoystickFromInstanceID,(SDL_JoystickID a),(a),return) +SDL_DYNAPI_PROC(int,SDL_GetDisplayUsableBounds,(int a, SDL_Rect *b),(a,b),return) diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 77426c3eb4115..4ce883be6a95f 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -175,6 +175,11 @@ struct SDL_VideoDevice */ int (*GetDisplayDPI) (_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi); + /* + * Get the usable bounds of a display (bounds minus menubar or whatever) + */ + int (*GetDisplayUsableBounds) (_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); + /* * Get a list of the available display modes for a display. */ diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 511b4c087d54a..300abea57011b 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -684,7 +684,26 @@ SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect) rect->w = display->current_mode.w; rect->h = display->current_mode.h; } - return 0; + return 0; /* !!! FIXME: should this be an error if (rect==NULL) ? */ +} + +int SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect * rect) +{ + CHECK_DISPLAY_INDEX(displayIndex, -1); + + if (rect) { + SDL_VideoDisplay *display = &_this->displays[displayIndex]; + + if (_this->GetDisplayUsableBounds) { + if (_this->GetDisplayUsableBounds(_this, display, rect) == 0) { + return 0; + } + } + + /* Oh well, just give the entire display bounds. */ + return SDL_GetDisplayBounds(displayIndex, rect); + } + return 0; /* !!! FIXME: should this be an error if (rect==NULL) ? */ } int diff --git a/src/video/cocoa/SDL_cocoamodes.h b/src/video/cocoa/SDL_cocoamodes.h index a0a29f50108f3..38bf079512884 100644 --- a/src/video/cocoa/SDL_cocoamodes.h +++ b/src/video/cocoa/SDL_cocoamodes.h @@ -35,6 +35,7 @@ typedef struct extern void Cocoa_InitModes(_THIS); extern int Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); +extern int Cocoa_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); extern void Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display); extern int Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode); extern void Cocoa_QuitModes(_THIS); diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m index 7d98264a72c93..6df0fe9bdf299 100644 --- a/src/video/cocoa/SDL_cocoamodes.m +++ b/src/video/cocoa/SDL_cocoamodes.m @@ -19,6 +19,7 @@ 3. This notice may not be removed or altered from any source distribution. */ #include "../../SDL_internal.h" +#include "SDL_assert.h" #if SDL_VIDEO_DRIVER_COCOA @@ -338,6 +339,41 @@ return 0; } +int +Cocoa_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) +{ + SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; + const CGDirectDisplayID cgdisplay = displaydata->display; + NSArray *screens = [NSScreen screens]; + NSScreen *screen = nil; + + /* !!! FIXME: maybe track the NSScreen in SDL_DisplayData? */ + for (NSScreen *i in screens) { + const CGDirectDisplayID thisDisplay = (CGDirectDisplayID) [[[i deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; + if (thisDisplay == cgdisplay) { + screen = i; + break; + } + } + + SDL_assert(screen != nil); /* didn't find it?! */ + if (screen == nil) { + return -1; + } + + const CGRect cgrect = CGDisplayBounds(cgdisplay); + const NSRect frame = [screen visibleFrame]; + + // !!! FIXME: I assume -[NSScreen visibleFrame] is relative to the origin of the screen in question and not the whole desktop. + // !!! FIXME: The math vs CGDisplayBounds might be incorrect if that's not the case, though. Check this. + rect->x = (int)(cgrect.origin.x + frame.origin.x); + rect->y = (int)(cgrect.origin.y + frame.origin.y); + rect->w = (int)frame.size.width; + rect->h = (int)frame.size.height; + + return 0; +} + void Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) { diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m index b8f775ddb2bc6..dbee221b59836 100644 --- a/src/video/cocoa/SDL_cocoavideo.m +++ b/src/video/cocoa/SDL_cocoavideo.m @@ -73,6 +73,7 @@ device->VideoInit = Cocoa_VideoInit; device->VideoQuit = Cocoa_VideoQuit; device->GetDisplayBounds = Cocoa_GetDisplayBounds; + device->GetDisplayUsableBounds = Cocoa_GetDisplayUsableBounds; device->GetDisplayModes = Cocoa_GetDisplayModes; device->SetDisplayMode = Cocoa_SetDisplayMode; device->PumpEvents = Cocoa_PumpEvents; diff --git a/src/video/uikit/SDL_uikitmodes.h b/src/video/uikit/SDL_uikitmodes.h index 027cf7aa03c20..b936df616f186 100644 --- a/src/video/uikit/SDL_uikitmodes.h +++ b/src/video/uikit/SDL_uikitmodes.h @@ -43,6 +43,7 @@ extern int UIKit_InitModes(_THIS); extern void UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display); extern int UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode); extern void UIKit_QuitModes(_THIS); +extern int UIKit_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); #endif /* _SDL_uikitmodes_h */ diff --git a/src/video/uikit/SDL_uikitmodes.m b/src/video/uikit/SDL_uikitmodes.m index 98d0314848a50..c02e6e6e7fff4 100644 --- a/src/video/uikit/SDL_uikitmodes.m +++ b/src/video/uikit/SDL_uikitmodes.m @@ -242,6 +242,26 @@ @implementation SDL_DisplayModeData return 0; } +int +UIKit_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) +{ + /* the default function iterates displays to make a fake offset, + as if all the displays were side-by-side, which is fine for iOS. */ + const int displayIndex = (int) (display - _this->displays); + if (SDL_GetDisplayBounds(displayIndex, rect) < 0) { + return -1; + } + + SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; + const CGRect frame = [data->uiscreen applicationFrame]; + const float scale = (float) data->scale; + rect->x += (int) (frame.origin.x * scale); + rect->y += (int) (frame.origin.y * scale); + rect->w = (int) (frame.size.width * scale); + rect->h = (int) (frame.size.height * scale); + return 0; +} + void UIKit_QuitModes(_THIS) { diff --git a/src/video/uikit/SDL_uikitvideo.m b/src/video/uikit/SDL_uikitvideo.m index 4f3c7dc4326b4..15613435adce6 100644 --- a/src/video/uikit/SDL_uikitvideo.m +++ b/src/video/uikit/SDL_uikitvideo.m @@ -85,6 +85,7 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device) device->SetWindowFullscreen = UIKit_SetWindowFullscreen; device->DestroyWindow = UIKit_DestroyWindow; device->GetWindowWMInfo = UIKit_GetWindowWMInfo; + device->GetDisplayUsableBounds = UIKit_GetDisplayUsableBounds; #if SDL_IPHONE_KEYBOARD device->HasScreenKeyboardSupport = UIKit_HasScreenKeyboardSupport; diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 91ed67f8718b3..76eff0c4be75f 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -332,6 +332,43 @@ WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, return data->DiagDPI != 0.0f ? 0 : -1; } +int +WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) +{ + const SDL_DisplayModeData *data = (const SDL_DisplayModeData *) display->current_mode.driverdata; + const DEVMODE *pDevMode = &data->DeviceMode; + POINT pt = { + /* !!! FIXME: no scale, right? */ + (LONG) (pDevMode->dmPosition.x + (pDevMode->dmPelsWidth / 2)), + (LONG) (pDevMode->dmPosition.y + (pDevMode->dmPelsHeight / 2)) + }; + HMONITOR hmon = MonitorFromPoint(&pt, MONITOR_DEFAULTTONULL); + MONITORINFO minfo; + const RECT *work; + BOOL rc = FALSE; + + SDL_assert(hmon != NULL); + + if (hmon != NULL) { + SDL_zero(minfo); + minfo.cbSize = sizeof (MONITORINFO); + rc = GetMonitorInfo(hmon, &minfo); + SDL_assert(rc); + } + + if (!rc) { + return SDL_SetError("Couldn't find monitor data"); + } + + work = &minfo->rcWork; + rect->x = (int)SDL_ceil(work->left * data->ScaleX); + rect->y = (int)SDL_ceil(work->top * data->ScaleY); + rect->w = (int)SDL_ceil((work->right - work->left) * data->ScaleX); + rect->h = (int)SDL_ceil((work->bottom - work->top) * data->ScaleY); + + return 0; +} + void WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display) { diff --git a/src/video/windows/SDL_windowsmodes.h b/src/video/windows/SDL_windowsmodes.h index e725848b22384..6aa293d218dd2 100644 --- a/src/video/windows/SDL_windowsmodes.h +++ b/src/video/windows/SDL_windowsmodes.h @@ -40,6 +40,7 @@ typedef struct extern int WIN_InitModes(_THIS); extern int WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); +extern int WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); extern int WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi); extern void WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display); extern int WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode); diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 37199805b8e72..dc444bca4e50a 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -124,6 +124,7 @@ WIN_CreateDevice(int devindex) device->VideoInit = WIN_VideoInit; device->VideoQuit = WIN_VideoQuit; device->GetDisplayBounds = WIN_GetDisplayBounds; + device->GetDisplayUsableBounds = WIN_GetDisplayUsableBounds; device->GetDisplayDPI = WIN_GetDisplayDPI; device->GetDisplayModes = WIN_GetDisplayModes; device->SetDisplayMode = WIN_SetDisplayMode; diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c index 5a866589e6702..59225ab7a13a1 100644 --- a/src/video/x11/SDL_x11modes.c +++ b/src/video/x11/SDL_x11modes.c @@ -1073,6 +1073,43 @@ X11_GetDisplayDPI(_THIS, SDL_VideoDisplay * sdl_display, float * ddpi, float * h return data->ddpi != 0.0f ? 0 : -1; } +int +X11_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * sdl_display, SDL_Rect * rect) +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + Display *display = data->display; + Atom _NET_WORKAREA; + int status, real_format; + int retval = -1; + Atom real_type; + unsigned long items_read = 0, items_left = 0; + unsigned char *propdata = NULL; + + if (X11_GetDisplayBounds(_this, sdl_display, rect) < 0) { + return -1; + } + + _NET_WORKAREA = X11_XInternAtom(display, "_NET_WORKAREA", False); + status = X11_XGetWindowProperty(display, DefaultRootWindow(display), + _NET_WORKAREA, 0L, 4L, False, XA_CARDINAL, + &real_type, &real_format, &items_read, + &items_left, &propdata); + if ((status == Success) && (items_read >= 4)) { + retval = 0; + const long *p = (long*) propdata; + const SDL_Rect usable = { (int)p[0], (int)p[1], (int)p[2], (int)p[3] }; + if (!SDL_IntersectRect(rect, &usable, rect)) { + SDL_zerop(rect); + } + } + + if (propdata) { + X11_XFree(propdata); + } + + return retval; +} + #endif /* SDL_VIDEO_DRIVER_X11 */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/x11/SDL_x11modes.h b/src/video/x11/SDL_x11modes.h index a68286ab19b13..4f47f3b5c9121 100644 --- a/src/video/x11/SDL_x11modes.h +++ b/src/video/x11/SDL_x11modes.h @@ -77,6 +77,7 @@ extern int X11_GetVisualInfoFromVisual(Display * display, Visual * visual, extern Uint32 X11_GetPixelFormatFromVisualInfo(Display * display, XVisualInfo * vinfo); extern int X11_GetDisplayBounds(_THIS, SDL_VideoDisplay * sdl_display, SDL_Rect * rect); +extern int X11_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * sdl_display, SDL_Rect * rect); extern int X11_GetDisplayDPI(_THIS, SDL_VideoDisplay * sdl_display, float * ddpi, float * hdpi, float * vdpi); #endif /* _SDL_x11modes_h */ diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c index 59384e487c6fa..015b7f3a63de0 100644 --- a/src/video/x11/SDL_x11video.c +++ b/src/video/x11/SDL_x11video.c @@ -218,6 +218,7 @@ X11_CreateDevice(int devindex) device->VideoQuit = X11_VideoQuit; device->GetDisplayModes = X11_GetDisplayModes; device->GetDisplayBounds = X11_GetDisplayBounds; + device->GetDisplayUsableBounds = X11_GetDisplayUsableBounds; device->GetDisplayDPI = X11_GetDisplayDPI; device->SetDisplayMode = X11_SetDisplayMode; device->SuspendScreenSaver = X11_SuspendScreenSaver; diff --git a/test/Makefile.in b/test/Makefile.in index 9a1df774e03d6..10ab2fd8f2a75 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -14,6 +14,7 @@ TARGETS = \ testatomic$(EXE) \ testaudioinfo$(EXE) \ testautomation$(EXE) \ + testbounds$(EXE) \ testdraw2$(EXE) \ testdrawchessboard$(EXE) \ testdropfile$(EXE) \ @@ -270,6 +271,9 @@ testmessage$(EXE): $(srcdir)/testmessage.c testdisplayinfo$(EXE): $(srcdir)/testdisplayinfo.c $(CC) -o $@ $^ $(CFLAGS) $(LIBS) +testbounds$(EXE): $(srcdir)/testbounds.c + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + controllermap$(EXE): $(srcdir)/controllermap.c $(CC) -o $@ $^ $(CFLAGS) $(LIBS) diff --git a/test/testbounds.c b/test/testbounds.c new file mode 100644 index 0000000000000..a447c7a7e0da8 --- /dev/null +++ b/test/testbounds.c @@ -0,0 +1,39 @@ +/* + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely. +*/ + +#include "SDL.h" + +int main(int argc, char **argv) +{ + int total, i; + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + SDL_Log("SDL_Init(SDL_INIT_VIDEO) failed: %s", SDL_GetError()); + } + + total = SDL_GetNumVideoDisplays(); + for (i = 0; i < total; i++) { + SDL_Rect bounds = { -1,-1,-1,-1 }, usable = { -1,-1,-1,-1 }; + SDL_GetDisplayBounds(i, &bounds); + SDL_GetDisplayUsableBounds(i, &usable); + SDL_Log("Display #%d ('%s'): bounds={(%d,%d),%dx%d}, usable={(%d,%d),%dx%d}", + i, SDL_GetDisplayName(i), + bounds.x, bounds.y, bounds.w, bounds.h, + usable.x, usable.y, usable.w, usable.h); + } + + SDL_Quit(); + return 0; +} + +/* vi: set ts=4 sw=4 expandtab: */ +