summaryrefslogtreecommitdiff
path: root/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.m
diff options
context:
space:
mode:
Diffstat (limited to 'source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.m')
-rw-r--r--source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.m490
1 files changed, 490 insertions, 0 deletions
diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.m
new file mode 100644
index 0000000..97ccd94
--- /dev/null
+++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.m
@@ -0,0 +1,490 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+ 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, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 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
+
+#include "SDL_cocoavideo.h"
+
+/* We need this for IODisplayCreateInfoDictionary and kIODisplayOnlyPreferredName */
+#include <IOKit/graphics/IOGraphicsLib.h>
+
+/* We need this for CVDisplayLinkGetNominalOutputVideoRefreshPeriod */
+#include <CoreVideo/CVBase.h>
+#include <CoreVideo/CVDisplayLink.h>
+
+/* we need this for ShowMenuBar() and HideMenuBar(). */
+#include <Carbon/Carbon.h>
+
+/* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */
+#include <AvailabilityMacros.h>
+
+
+static void
+Cocoa_ToggleMenuBar(const BOOL show)
+{
+ /* !!! FIXME: keep an eye on this.
+ * ShowMenuBar/HideMenuBar is officially unavailable for 64-bit binaries.
+ * It happens to work, as of 10.7, but we're going to see if
+ * we can just simply do without it on newer OSes...
+ */
+#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__)
+ if (show) {
+ ShowMenuBar();
+ } else {
+ HideMenuBar();
+ }
+#endif
+}
+
+static int
+CG_SetError(const char *prefix, CGDisplayErr result)
+{
+ const char *error;
+
+ switch (result) {
+ case kCGErrorFailure:
+ error = "kCGErrorFailure";
+ break;
+ case kCGErrorIllegalArgument:
+ error = "kCGErrorIllegalArgument";
+ break;
+ case kCGErrorInvalidConnection:
+ error = "kCGErrorInvalidConnection";
+ break;
+ case kCGErrorInvalidContext:
+ error = "kCGErrorInvalidContext";
+ break;
+ case kCGErrorCannotComplete:
+ error = "kCGErrorCannotComplete";
+ break;
+ case kCGErrorNotImplemented:
+ error = "kCGErrorNotImplemented";
+ break;
+ case kCGErrorRangeCheck:
+ error = "kCGErrorRangeCheck";
+ break;
+ case kCGErrorTypeCheck:
+ error = "kCGErrorTypeCheck";
+ break;
+ case kCGErrorInvalidOperation:
+ error = "kCGErrorInvalidOperation";
+ break;
+ case kCGErrorNoneAvailable:
+ error = "kCGErrorNoneAvailable";
+ break;
+ default:
+ error = "Unknown Error";
+ break;
+ }
+ return SDL_SetError("%s: %s", prefix, error);
+}
+
+static SDL_bool
+GetDisplayMode(_THIS, CGDisplayModeRef vidmode, CVDisplayLinkRef link, SDL_DisplayMode *mode)
+{
+ SDL_DisplayModeData *data;
+ int width = 0;
+ int height = 0;
+ int bpp = 0;
+ int refreshRate = 0;
+ CFStringRef fmt;
+
+ data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
+ if (!data) {
+ return SDL_FALSE;
+ }
+ data->moderef = vidmode;
+
+ fmt = CGDisplayModeCopyPixelEncoding(vidmode);
+ width = (int) CGDisplayModeGetWidth(vidmode);
+ height = (int) CGDisplayModeGetHeight(vidmode);
+ refreshRate = (int) (CGDisplayModeGetRefreshRate(vidmode) + 0.5);
+
+ if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels),
+ kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
+ bpp = 32;
+ } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels),
+ kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
+ bpp = 16;
+ } else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels),
+ kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
+ bpp = 30;
+ } else {
+ bpp = 0; /* ignore 8-bit and such for now. */
+ }
+
+ CFRelease(fmt);
+
+ /* CGDisplayModeGetRefreshRate returns 0 for many non-CRT displays. */
+ if (refreshRate == 0 && link != NULL) {
+ CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
+ if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) {
+ refreshRate = (int) ((time.timeScale / (double) time.timeValue) + 0.5);
+ }
+ }
+
+ mode->format = SDL_PIXELFORMAT_UNKNOWN;
+ switch (bpp) {
+ case 16:
+ mode->format = SDL_PIXELFORMAT_ARGB1555;
+ break;
+ case 30:
+ mode->format = SDL_PIXELFORMAT_ARGB2101010;
+ break;
+ case 32:
+ mode->format = SDL_PIXELFORMAT_ARGB8888;
+ break;
+ case 8: /* We don't support palettized modes now */
+ default: /* Totally unrecognizable bit depth. */
+ SDL_free(data);
+ return SDL_FALSE;
+ }
+ mode->w = width;
+ mode->h = height;
+ mode->refresh_rate = refreshRate;
+ mode->driverdata = data;
+ return SDL_TRUE;
+}
+
+static const char *
+Cocoa_GetDisplayName(CGDirectDisplayID displayID)
+{
+ CFDictionaryRef deviceInfo = IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName);
+ NSDictionary *localizedNames = [(NSDictionary *)deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
+ const char* displayName = NULL;
+
+ if ([localizedNames count] > 0) {
+ displayName = SDL_strdup([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]);
+ }
+ CFRelease(deviceInfo);
+ return displayName;
+}
+
+void
+Cocoa_InitModes(_THIS)
+{ @autoreleasepool
+{
+ CGDisplayErr result;
+ CGDirectDisplayID *displays;
+ CGDisplayCount numDisplays;
+ int pass, i;
+
+ result = CGGetOnlineDisplayList(0, NULL, &numDisplays);
+ if (result != kCGErrorSuccess) {
+ CG_SetError("CGGetOnlineDisplayList()", result);
+ return;
+ }
+ displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays);
+ result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays);
+ if (result != kCGErrorSuccess) {
+ CG_SetError("CGGetOnlineDisplayList()", result);
+ SDL_stack_free(displays);
+ return;
+ }
+
+ /* Pick up the primary display in the first pass, then get the rest */
+ for (pass = 0; pass < 2; ++pass) {
+ for (i = 0; i < numDisplays; ++i) {
+ SDL_VideoDisplay display;
+ SDL_DisplayData *displaydata;
+ SDL_DisplayMode mode;
+ CGDisplayModeRef moderef = NULL;
+ CVDisplayLinkRef link = NULL;
+
+ if (pass == 0) {
+ if (!CGDisplayIsMain(displays[i])) {
+ continue;
+ }
+ } else {
+ if (CGDisplayIsMain(displays[i])) {
+ continue;
+ }
+ }
+
+ if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) {
+ continue;
+ }
+
+ moderef = CGDisplayCopyDisplayMode(displays[i]);
+
+ if (!moderef) {
+ continue;
+ }
+
+ displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
+ if (!displaydata) {
+ CGDisplayModeRelease(moderef);
+ continue;
+ }
+ displaydata->display = displays[i];
+
+ CVDisplayLinkCreateWithCGDisplay(displays[i], &link);
+
+ SDL_zero(display);
+ /* this returns a stddup'ed string */
+ display.name = (char *)Cocoa_GetDisplayName(displays[i]);
+ if (!GetDisplayMode(_this, moderef, link, &mode)) {
+ CVDisplayLinkRelease(link);
+ CGDisplayModeRelease(moderef);
+ SDL_free(display.name);
+ SDL_free(displaydata);
+ continue;
+ }
+
+ CVDisplayLinkRelease(link);
+
+ display.desktop_mode = mode;
+ display.current_mode = mode;
+ display.driverdata = displaydata;
+ SDL_AddVideoDisplay(&display);
+ SDL_free(display.name);
+ }
+ }
+ SDL_stack_free(displays);
+}}
+
+int
+Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
+{
+ SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
+ CGRect cgrect;
+
+ cgrect = CGDisplayBounds(displaydata->display);
+ rect->x = (int)cgrect.origin.x;
+ rect->y = (int)cgrect.origin.y;
+ rect->w = (int)cgrect.size.width;
+ rect->h = (int)cgrect.size.height;
+ 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;
+}
+
+int
+Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi)
+{
+ const float MM_IN_INCH = 25.4f;
+
+ SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
+
+ CGSize displaySize = CGDisplayScreenSize(data->display);
+ int pixelWidth = (int) CGDisplayPixelsWide(data->display);
+ int pixelHeight = (int) CGDisplayPixelsHigh(data->display);
+
+ if (ddpi) {
+ *ddpi = SDL_ComputeDiagonalDPI(pixelWidth, pixelHeight, displaySize.width / MM_IN_INCH, displaySize.height / MM_IN_INCH);
+ }
+ if (hdpi) {
+ *hdpi = pixelWidth * MM_IN_INCH / displaySize.width;
+ }
+ if (vdpi) {
+ *vdpi = pixelHeight * MM_IN_INCH / displaySize.height;
+ }
+
+ return 0;
+}
+
+void
+Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
+{
+ SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
+ CVDisplayLinkRef link = NULL;
+ CGDisplayModeRef desktopmoderef;
+ SDL_DisplayMode desktopmode;
+ CFArrayRef modes;
+
+ CVDisplayLinkCreateWithCGDisplay(data->display, &link);
+
+ desktopmoderef = CGDisplayCopyDisplayMode(data->display);
+
+ /* CopyAllDisplayModes won't always contain the desktop display mode (if
+ * NULL is passed in) - for example on a retina 15" MBP, System Preferences
+ * allows choosing 1920x1200 but it's not in the list. AddDisplayMode makes
+ * sure there are no duplicates so it's safe to always add the desktop mode
+ * even in cases where it is in the CopyAllDisplayModes list.
+ */
+ if (desktopmoderef && GetDisplayMode(_this, desktopmoderef, link, &desktopmode)) {
+ if (!SDL_AddDisplayMode(display, &desktopmode)) {
+ CGDisplayModeRelease(desktopmoderef);
+ SDL_free(desktopmode.driverdata);
+ }
+ } else {
+ CGDisplayModeRelease(desktopmoderef);
+ }
+
+ modes = CGDisplayCopyAllDisplayModes(data->display, NULL);
+
+ if (modes) {
+ CFIndex i;
+ const CFIndex count = CFArrayGetCount(modes);
+
+ for (i = 0; i < count; i++) {
+ CGDisplayModeRef moderef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
+ SDL_DisplayMode mode;
+
+ if (GetDisplayMode(_this, moderef, link, &mode)) {
+ if (SDL_AddDisplayMode(display, &mode)) {
+ CGDisplayModeRetain(moderef);
+ } else {
+ SDL_free(mode.driverdata);
+ }
+ }
+ }
+
+ CFRelease(modes);
+ }
+
+ CVDisplayLinkRelease(link);
+}
+
+int
+Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
+{
+ SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
+ SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
+ CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
+ CGError result;
+
+ /* Fade to black to hide resolution-switching flicker */
+ if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) {
+ CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
+ }
+
+ if (data == display->desktop_mode.driverdata) {
+ /* Restoring desktop mode */
+ CGDisplaySetDisplayMode(displaydata->display, data->moderef, NULL);
+
+ if (CGDisplayIsMain(displaydata->display)) {
+ CGReleaseAllDisplays();
+ } else {
+ CGDisplayRelease(displaydata->display);
+ }
+
+ if (CGDisplayIsMain(displaydata->display)) {
+ Cocoa_ToggleMenuBar(YES);
+ }
+ } else {
+ /* Put up the blanking window (a window above all other windows) */
+ if (CGDisplayIsMain(displaydata->display)) {
+ /* If we don't capture all displays, Cocoa tries to rearrange windows... *sigh* */
+ result = CGCaptureAllDisplays();
+ } else {
+ result = CGDisplayCapture(displaydata->display);
+ }
+ if (result != kCGErrorSuccess) {
+ CG_SetError("CGDisplayCapture()", result);
+ goto ERR_NO_CAPTURE;
+ }
+
+ /* Do the physical switch */
+ result = CGDisplaySetDisplayMode(displaydata->display, data->moderef, NULL);
+ if (result != kCGErrorSuccess) {
+ CG_SetError("CGDisplaySwitchToMode()", result);
+ goto ERR_NO_SWITCH;
+ }
+
+ /* Hide the menu bar so it doesn't intercept events */
+ if (CGDisplayIsMain(displaydata->display)) {
+ Cocoa_ToggleMenuBar(NO);
+ }
+ }
+
+ /* Fade in again (asynchronously) */
+ if (fade_token != kCGDisplayFadeReservationInvalidToken) {
+ CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
+ CGReleaseDisplayFadeReservation(fade_token);
+ }
+
+ return 0;
+
+ /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
+ERR_NO_SWITCH:
+ CGDisplayRelease(displaydata->display);
+ERR_NO_CAPTURE:
+ if (fade_token != kCGDisplayFadeReservationInvalidToken) {
+ CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
+ CGReleaseDisplayFadeReservation(fade_token);
+ }
+ return -1;
+}
+
+void
+Cocoa_QuitModes(_THIS)
+{
+ int i, j;
+
+ for (i = 0; i < _this->num_displays; ++i) {
+ SDL_VideoDisplay *display = &_this->displays[i];
+ SDL_DisplayModeData *mode;
+
+ if (display->current_mode.driverdata != display->desktop_mode.driverdata) {
+ Cocoa_SetDisplayMode(_this, display, &display->desktop_mode);
+ }
+
+ mode = (SDL_DisplayModeData *) display->desktop_mode.driverdata;
+ CGDisplayModeRelease(mode->moderef);
+
+ for (j = 0; j < display->num_display_modes; j++) {
+ mode = (SDL_DisplayModeData*) display->display_modes[j].driverdata;
+ CGDisplayModeRelease(mode->moderef);
+ }
+
+ }
+ Cocoa_ToggleMenuBar(YES);
+}
+
+#endif /* SDL_VIDEO_DRIVER_COCOA */
+
+/* vi: set ts=4 sw=4 expandtab: */