diff --git src/video/cocoa/SDL_cocoaclipboard.m src/video/cocoa/SDL_cocoaclipboard.m index 5b32bbca0..d5d4b505f 100644 --- src/video/cocoa/SDL_cocoaclipboard.m +++ src/video/cocoa/SDL_cocoaclipboard.m @@ -26,7 +26,7 @@ #include "../../events/SDL_clipboardevents_c.h" int Cocoa_SetClipboardText(_THIS, const char *text) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; NSPasteboard *pasteboard; @@ -44,7 +44,7 @@ int Cocoa_SetClipboardText(_THIS, const char *text) }} char *Cocoa_GetClipboardText(_THIS) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSPasteboard *pasteboard; NSString *format = NSPasteboardTypeString; @@ -83,7 +83,7 @@ SDL_bool Cocoa_HasClipboardText(_THIS) } void Cocoa_CheckClipboardUpdate(SDL_VideoData * data) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSPasteboard *pasteboard; NSInteger count; diff --git src/video/cocoa/SDL_cocoacompat.h src/video/cocoa/SDL_cocoacompat.h new file mode 100644 index 000000000..d9fc0a863 --- /dev/null +++ src/video/cocoa/SDL_cocoacompat.h @@ -0,0 +1,138 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 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, 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" + +#ifndef SDL_cocoacompat_h_ +#define SDL_cocoacompat_h_ + +/* Compatibility layer that lets the Cocoa backend build in two modes: + + - clang with Objective-C ARC (the upstream default; modern macOS), and + - GCC with manual retain/release, for legacy macOS (10.5/10.6, PowerPC) + where clang is not available. + + GCC's Objective-C has no ARC, no blocks, no @autoreleasepool and no + instancetype; on top of that, pre-10.7 SDKs lack a number of constants + and functions. Everything here compiles away to the stock spelling + under clang/ARC. */ + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#if __has_feature(objc_arc) +#define SDL_COCOA_ARC 1 +#else +#define SDL_COCOA_ARC 0 +#endif + +#if !defined(__clang__) +/* Make ARC bridge casts vanish: (__bridge NSWindow *)ptr -> (NSWindow *)ptr */ +#ifndef __bridge +#define __bridge +#endif +#ifndef instancetype +#define instancetype id +#endif +/* The code paths that actually use blocks are all guarded to clang, so + __block locals become plain locals. Apple GCC 4.2 predefines __block as a + built-in macro, hence the #undef. */ +#undef __block +#define __block +#endif /* !__clang__ */ + +/* ARC ivar/local qualifiers */ +#if SDL_COCOA_ARC +#define SDL_COCOA_WEAK __weak +#else +#define SDL_COCOA_WEAK /* unretained plain pointer under MRR */ +#endif + +/* Ownership transfer between ObjC objects and void * driverdata. + SDL_CFBridgingRetain: +1 retain, returns a void pointer. + SDL_CFBridgingRelease: consumes that +1, returns the object (still + valid: ARC-managed / autoreleased under MRR). */ +#if SDL_COCOA_ARC +#define SDL_CFBridgingRetain(obj) ((void *)CFBridgingRetain(obj)) +#define SDL_CFBridgingRelease(ptr) CFBridgingRelease(ptr) +#else +/* the typedef keeps the casts working in scopes where a local variable + shadows 'id' (e.g. Cocoa_CreateSystemCursor's parameter) */ +typedef id SDL_cocoa_id; +#define SDL_CFBridgingRetain(obj) ((void *)[(SDL_cocoa_id)(obj) retain]) +#define SDL_CFBridgingRelease(ptr) ((SDL_cocoa_id)[(SDL_cocoa_id)(ptr) autorelease]) +#endif + +/* Explicit retain/release/autorelease that compile away under ARC. + Used where MRR needs an ownership operation that ARC does implicitly. + Variadic because the argument is often a [bracketed message send] whose + commas square brackets do not hide from the preprocessor. */ +#if SDL_COCOA_ARC +#define SDL_COCOA_RETAIN(...) (__VA_ARGS__) +#define SDL_COCOA_RELEASE(...) do { } while (0) +#define SDL_COCOA_AUTORELEASE(...) (__VA_ARGS__) +#else +#define SDL_COCOA_RETAIN(...) [(__VA_ARGS__) retain] +#define SDL_COCOA_RELEASE(...) [(__VA_ARGS__) release] +#define SDL_COCOA_AUTORELEASE(...) [(__VA_ARGS__) autorelease] +#endif + +/* @autoreleasepool replacement. GCC cannot parse @autoreleasepool, so + spell pools as SDL_COCOA_POOL { ... }. The GCC version is a plain + declaration whose cleanup attribute drains the pool when the enclosing + scope exits (including early return/goto) - slightly wider than the + braces, but every use is the first statement of a function body, where + the two are equivalent. For the same reason it can only be used once + per scope, and (because of -Wdeclaration-after-statement) only at the + top of a block. */ +#if defined(__clang__) +#define SDL_COCOA_POOL @autoreleasepool +#else +static inline void SDL_CocoaDrainPool(NSAutoreleasePool **pool) +{ + [*pool drain]; +} +#define SDL_COCOA_POOL \ + NSAutoreleasePool *sdl_pool_ \ + __attribute__((__cleanup__(SDL_CocoaDrainPool), __unused__)) \ + = [[NSAutoreleasePool alloc] init]; +#endif + +/* Pre-10.7 SDK fallbacks */ + +#ifndef NSAppKitVersionNumber10_6 +#define NSAppKitVersionNumber10_6 1038 +#endif +#ifndef NSAppKitVersionNumber10_7 +#define NSAppKitVersionNumber10_7 1138 +#endif +#ifndef NSAppKitVersionNumber10_8 +#define NSAppKitVersionNumber10_8 1187 +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 +/* The pasteboard type constants were introduced with the 10.6 SDK. */ +#define NSPasteboardTypeString NSStringPboardType +#endif + +#endif /* SDL_cocoacompat_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoaevents.m src/video/cocoa/SDL_cocoaevents.m index 39c5c4f38..de686770d 100644 --- src/video/cocoa/SDL_cocoaevents.m +++ src/video/cocoa/SDL_cocoaevents.m @@ -116,7 +116,7 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) + (void)registerUserDefaults { - NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys: + NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:NO], @"AppleMomentumScrollSupported", [NSNumber numberWithBool:NO], @"ApplePressAndHoldEnabled", [NSNumber numberWithBool:YES], @"ApplePersistenceIgnoreState", @@ -131,7 +131,11 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) - (void)setAppleMenu:(NSMenu *)menu; @end +#ifdef MAC_OS_X_VERSION_10_6 /* delegate protocols were introduced with the 10.6 SDK */ @interface SDLAppDelegate : NSObject { +#else +@interface SDLAppDelegate : NSObject { +#endif @public BOOL seenFirstActivate; } @@ -183,6 +187,10 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL]; } + +#if !SDL_COCOA_ARC + [super dealloc]; +#endif } - (void)windowWillClose:(NSNotification *)notification; @@ -209,14 +217,17 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) */ for (NSWindow *window in [NSApp orderedWindows]) { if (window != win && [window canBecomeKeyWindow]) { +#ifdef MAC_OS_X_VERSION_10_6 /* -isOnActiveSpace arrived with the 10.6 SDK */ if (![window isOnActiveSpace]) { continue; } +#endif [window makeKeyAndOrderFront:self]; return; } } +#ifdef MAC_OS_X_VERSION_10_6 /* +windowNumbersWithOptions: arrived with the 10.6 SDK */ /* If a window wasn't found above, iterate through all visible windows in * the active Space in z-order (including the 'About' window, if it's shown) * and make the first one key. @@ -228,6 +239,7 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) return; } } +#endif } - (void)focusSomeWindow:(NSNotification *)aNotification @@ -295,17 +307,19 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) behaviour there. https://github.com/libsdl-org/SDL/issues/10340 (13.6 still needs it, presumably 13.7 does, too.) */ SDL_bool background_app_default = SDL_FALSE; - if (@available(macOS 14.0, *)) { + if (floor(NSAppKitVersionNumber) >= 2487 /* NSAppKitVersionNumber14_0 */) { background_app_default = SDL_TRUE; /* by default, don't explicitly activate the dock and then us again to force to foreground */ } if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, background_app_default)) { +#ifdef MAC_OS_X_VERSION_10_6 /* NSRunningApplication arrived with the 10.6 SDK */ /* Get more aggressive for Catalina: activate the Dock first so we definitely reset all activation state. */ for (NSRunningApplication *i in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) { [i activateWithOptions:NSApplicationActivateIgnoringOtherApps]; break; } SDL_Delay(300); /* !!! FIXME: this isn't right. */ +#endif [NSApp activateIgnoringOtherApps:YES]; } @@ -401,6 +415,7 @@ static void CreateApplicationMenus(void) /* Create the main menu bar */ [NSApp setMainMenu:mainMenu]; + SDL_COCOA_RELEASE(mainMenu); /* NSApp owns it now */ /* Create the application menu */ appName = GetApplicationName(); @@ -421,6 +436,7 @@ static void CreateApplicationMenus(void) [menuItem setSubmenu:serviceMenu]; [NSApp setServicesMenu:serviceMenu]; + SDL_COCOA_RELEASE(serviceMenu); [appleMenu addItem:[NSMenuItem separatorItem]]; @@ -441,9 +457,11 @@ static void CreateApplicationMenus(void) menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; [menuItem setSubmenu:appleMenu]; [[NSApp mainMenu] addItem:menuItem]; + SDL_COCOA_RELEASE(menuItem); /* Tell the application object that this is now the application menu */ [NSApp setAppleMenu:appleMenu]; + SDL_COCOA_RELEASE(appleMenu); /* Create the window menu */ windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; @@ -462,18 +480,21 @@ static void CreateApplicationMenus(void) menuItem = [[NSMenuItem alloc] initWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; [menuItem setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; [windowMenu addItem:menuItem]; + SDL_COCOA_RELEASE(menuItem); /* Put menu into the menubar */ menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; [menuItem setSubmenu:windowMenu]; [[NSApp mainMenu] addItem:menuItem]; + SDL_COCOA_RELEASE(menuItem); /* Tell the application object that this is now the window menu */ [NSApp setWindowsMenu:windowMenu]; + SDL_COCOA_RELEASE(windowMenu); } void Cocoa_RegisterApp(void) -{ @autoreleasepool +{ SDL_COCOA_POOL { /* This can get called more than once! Be careful what you initialize! */ @@ -484,7 +505,12 @@ void Cocoa_RegisterApp(void) s_bShouldHandleEventsInSDLApplication = SDL_TRUE; if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, SDL_FALSE)) { +#ifdef MAC_OS_X_VERSION_10_6 /* -setActivationPolicy: arrived with the 10.6 SDK */ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; +#else + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); +#endif } /* If there aren't already menus in place, look to see if there's @@ -552,7 +578,7 @@ int Cocoa_PumpEventsUntilDate(_THIS, NSDate *expiration, bool accumulate) } int Cocoa_WaitEventTimeout(_THIS, int timeout) -{ @autoreleasepool +{ SDL_COCOA_POOL { if (timeout > 0) { NSDate *limitDate = [NSDate dateWithTimeIntervalSinceNow: (double) timeout / 1000.0]; @@ -567,13 +593,13 @@ int Cocoa_WaitEventTimeout(_THIS, int timeout) }} void Cocoa_PumpEvents(_THIS) -{ @autoreleasepool +{ SDL_COCOA_POOL { Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], true); }} void Cocoa_SendWakeupEvent(_THIS, SDL_Window *window) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSEvent* event = [NSEvent otherEventWithType: NSEventTypeApplicationDefined location: NSMakePoint(0,0) @@ -589,7 +615,7 @@ void Cocoa_SendWakeupEvent(_THIS, SDL_Window *window) }} void Cocoa_SuspendScreenSaver(_THIS) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoData *data = (__bridge SDL_VideoData *)_this->driverdata; @@ -606,10 +632,22 @@ void Cocoa_SuspendScreenSaver(_THIS) */ IOPMAssertionID assertion = kIOPMNullAssertionID; NSString *name = [GetApplicationName() stringByAppendingString:@" using SDL_DisableScreenSaver"]; +#if defined(MAC_OS_X_VERSION_10_7) IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep, (__bridge CFStringRef) name, NULL, NULL, NULL, 0, NULL, &assertion); +#elif defined(MAC_OS_X_VERSION_10_6) + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, + kIOPMAssertionLevelOn, + (__bridge CFStringRef) name, + &assertion); +#else + /* 10.5 only has the nameless variant */ + (void) name; + IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, + kIOPMAssertionLevelOn, &assertion); +#endif data.screensaver_assertion = assertion; } }} diff --git src/video/cocoa/SDL_cocoakeyboard.m src/video/cocoa/SDL_cocoakeyboard.m index 2a6763228..544b1266a 100644 --- src/video/cocoa/SDL_cocoakeyboard.m +++ src/video/cocoa/SDL_cocoakeyboard.m @@ -109,7 +109,8 @@ } if (_markedText != aString) { - _markedText = aString; + SDL_COCOA_RELEASE(_markedText); + _markedText = SDL_COCOA_RETAIN(aString); } _selectedRange = selectedRange; @@ -124,11 +125,20 @@ - (void)unmarkText { + SDL_COCOA_RELEASE(_markedText); _markedText = nil; SDL_SendEditingText("", 0, 0); } +#if !SDL_COCOA_ARC +- (void)dealloc +{ + [_markedText release]; + [super dealloc]; +} +#endif + - (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { NSWindow *window = [self window]; @@ -145,7 +155,11 @@ aRange.location, aRange.length, windowHeight, NSStringFromRect(rect)); +#ifdef MAC_OS_X_VERSION_10_7 rect = [window convertRectToScreen:rect]; +#else + rect.origin = [window convertBaseToScreen:rect.origin]; +#endif return rect; } @@ -325,12 +339,17 @@ void Cocoa_InitKeyboard(_THIS) SDL_SetScancodeName(SDL_SCANCODE_RALT, "Right Option"); SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Command"); +#ifdef MAC_OS_X_VERSION_10_6 /* +[NSEvent modifierFlags] arrived with the 10.6 SDK */ data.modifierFlags = (unsigned int)[NSEvent modifierFlags]; +#else + /* same NX_* bit values as the AppKit modifier masks */ + data.modifierFlags = (unsigned int)CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState); +#endif SDL_ToggleModState(KMOD_CAPS, (data.modifierFlags & NSEventModifierFlagCapsLock) ? SDL_TRUE : SDL_FALSE); } void Cocoa_StartTextInput(_THIS) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSView *parentView; SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; @@ -348,8 +367,10 @@ void Cocoa_StartTextInput(_THIS) * text input, simply remove the field editor from its superview then add * it to the front most window's content view */ if (!data.fieldEdit) { - data.fieldEdit = + SDLTranslatorResponder *fieldEdit = [[SDLTranslatorResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)]; + data.fieldEdit = fieldEdit; + SDL_COCOA_RELEASE(fieldEdit); /* the property holds a reference */ } if (![[data.fieldEdit superview] isEqual:parentView]) { @@ -361,7 +382,7 @@ void Cocoa_StartTextInput(_THIS) }} void Cocoa_StopTextInput(_THIS) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; diff --git src/video/cocoa/SDL_cocoamessagebox.m src/video/cocoa/SDL_cocoamessagebox.m index 20eabe3ee..7ac760c97 100644 --- src/video/cocoa/SDL_cocoamessagebox.m +++ src/video/cocoa/SDL_cocoamessagebox.m @@ -47,7 +47,7 @@ /* Retain the NSWindow because we'll show the alert later on the main thread */ if (window) { - nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; + nswindow = SDL_COCOA_RETAIN(((__bridge SDL_WindowData *) window->driverdata).nswindow); } else { nswindow = nil; } @@ -56,10 +56,18 @@ return self; } +#if !SDL_COCOA_ARC +- (void)dealloc +{ + [nswindow release]; + [super dealloc]; +} +#endif + - (void)showAlert:(NSAlert*)alert { if (nswindow) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) && defined(__clang__) if ([alert respondsToSelector:@selector(beginSheetModalForWindow:completionHandler:)]) { [alert beginSheetModalForWindow:nswindow completionHandler:^(NSModalResponse returnCode) { @@ -73,6 +81,7 @@ #endif } clicked = [NSApp runModalForWindow:nswindow]; + SDL_COCOA_RELEASE(nswindow); nswindow = nil; } else { clicked = [alert runModal]; @@ -97,7 +106,7 @@ static void Cocoa_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, i int i; Cocoa_RegisterApp(); - alert = [[NSAlert alloc] init]; + alert = SDL_COCOA_AUTORELEASE([[NSAlert alloc] init]); if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) { [alert setAlertStyle:NSAlertStyleCritical]; @@ -130,7 +139,7 @@ static void Cocoa_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, i } } - presenter = [[SDLMessageBoxPresenter alloc] initWithParentWindow:messageboxdata->window]; + presenter = SDL_COCOA_AUTORELEASE([[SDLMessageBoxPresenter alloc] initWithParentWindow:messageboxdata->window]); [presenter showAlert:alert]; @@ -149,14 +158,22 @@ static void Cocoa_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, i /* Display a Cocoa message box */ int Cocoa_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) -{ @autoreleasepool +{ SDL_COCOA_POOL { __block int returnValue = 0; if ([NSThread isMainThread]) { Cocoa_ShowMessageBoxImpl(messageboxdata, buttonid, &returnValue); } else { +#if defined(__clang__) dispatch_sync(dispatch_get_main_queue(), ^{ Cocoa_ShowMessageBoxImpl(messageboxdata, buttonid, &returnValue); }); +#else + /* Call directly, like SDL <= 2.0.9 always did: the era of AppKit + that GCC builds target tolerates this off the main thread, and + the main thread may not be running a runloop to service a + marshalled call. */ + Cocoa_ShowMessageBoxImpl(messageboxdata, buttonid, &returnValue); +#endif } return returnValue; }} diff --git src/video/cocoa/SDL_cocoamodes.m src/video/cocoa/SDL_cocoamodes.m index fa5b18458..3a71725f9 100644 --- src/video/cocoa/SDL_cocoamodes.m +++ src/video/cocoa/SDL_cocoamodes.m @@ -41,6 +41,65 @@ #define kDisplayModeNativeFlag 0x02000000 #endif +/* !!! FIXME: clean out the pre-10.6 code when it makes sense to do so. */ +#ifndef FORCE_OLD_API +#define FORCE_OLD_API 0 +#endif + +/* The old pre-10.6 CGDisplay API (CFDictionary-based display modes) is used + * when targeting the 10.5 SDK or older, or when explicitly requested with + * -DFORCE_OLD_API=1. The latter is needed on OS builds - e.g. some of the + * PowerPC Mac OS X 10.6.8 images - whose CoreGraphics/OpenGL stack predates + * the CGDisplayMode API even though the 10.6 SDK declares it. */ +#if FORCE_OLD_API || !defined(MAC_OS_X_VERSION_10_6) +#define SDL_COCOA_USE_OLD_CGDISPLAY_API 1 +typedef CFDictionaryRef SDL_CocoaDisplayModeRef; +#else +#define SDL_COCOA_USE_OLD_CGDISPLAY_API 0 +typedef CGDisplayModeRef SDL_CocoaDisplayModeRef; +#endif + +/* Small wrappers hiding the difference between the old (CFDictionary-based, + * 10.0+) and the new (CGDisplayMode-based, 10.6+) display mode APIs. + * Both return +1 retained references (or NULL). */ +static SDL_CocoaDisplayModeRef CopyCurrentDisplayMode(CGDirectDisplayID display) +{ +#if SDL_COCOA_USE_OLD_CGDISPLAY_API + CFDictionaryRef moderef = CGDisplayCurrentMode(display); + if (moderef) { + CFRetain(moderef); + } + return moderef; +#else + return CGDisplayCopyDisplayMode(display); +#endif +} + +static void ReleaseDisplayModeRef(SDL_CocoaDisplayModeRef moderef) +{ +#if SDL_COCOA_USE_OLD_CGDISPLAY_API + if (moderef) { + CFRelease(moderef); + } +#else + CGDisplayModeRelease(moderef); /* NULL is ok */ +#endif +} + +static CFArrayRef CopyAllDisplayModes(CGDirectDisplayID display, CFDictionaryRef options) +{ +#if SDL_COCOA_USE_OLD_CGDISPLAY_API + CFArrayRef modes = CGDisplayAvailableModes(display); + (void) options; + if (modes) { + CFRetain(modes); + } + return modes; +#else + return CGDisplayCopyAllDisplayModes(display, options); +#endif +} + static int CG_SetError(const char *prefix, CGDisplayErr result) { @@ -84,9 +143,20 @@ static int CG_SetError(const char *prefix, CGDisplayErr result) return SDL_SetError("%s: %s", prefix, error); } -static int GetDisplayModeRefreshRate(CGDisplayModeRef vidmode, CVDisplayLinkRef link) +static int GetDisplayModeRefreshRate(SDL_CocoaDisplayModeRef vidmode, CVDisplayLinkRef link) { +#if SDL_COCOA_USE_OLD_CGDISPLAY_API + int refreshRate = 0; + CFNumberRef number = CFDictionaryGetValue(vidmode, kCGDisplayRefreshRate); + if (number) { + double rate = 0.0; + if (CFNumberGetValue(number, kCFNumberDoubleType, &rate)) { + refreshRate = (int) (rate + 0.5); + } + } +#else int refreshRate = (int) (CGDisplayModeGetRefreshRate(vidmode) + 0.5); +#endif /* CGDisplayModeGetRefreshRate can return 0 (eg for built-in displays). */ if (refreshRate == 0 && link != NULL) { @@ -99,8 +169,13 @@ static int GetDisplayModeRefreshRate(CGDisplayModeRef vidmode, CVDisplayLinkRef return refreshRate; } -static SDL_bool HasValidDisplayModeFlags(CGDisplayModeRef vidmode) +static SDL_bool HasValidDisplayModeFlags(SDL_CocoaDisplayModeRef vidmode) { +#if SDL_COCOA_USE_OLD_CGDISPLAY_API + /* The old API modes were not flag-filtered (matching SDL up to 2.0.9); + * unusable modes are rejected by their pixel format instead. */ + (void) vidmode; +#else uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode); /* Filter out modes which have flags that we don't want. */ @@ -112,12 +187,33 @@ static SDL_bool HasValidDisplayModeFlags(CGDisplayModeRef vidmode) if (!(ioflags & kDisplayModeValidFlag) || !(ioflags & kDisplayModeSafeFlag)) { return SDL_FALSE; } +#endif return SDL_TRUE; } -static Uint32 GetDisplayModePixelFormat(CGDisplayModeRef vidmode) +static Uint32 GetDisplayModePixelFormat(SDL_CocoaDisplayModeRef vidmode) { +#if SDL_COCOA_USE_OLD_CGDISPLAY_API + long bpp = 0; + CFNumberRef number = CFDictionaryGetValue(vidmode, kCGDisplayBitsPerPixel); + Uint32 pixelformat = SDL_PIXELFORMAT_UNKNOWN; + + if (number) { + CFNumberGetValue(number, kCFNumberLongType, &bpp); + } + switch (bpp) { + case 32: + pixelformat = SDL_PIXELFORMAT_ARGB8888; + break; + case 16: + pixelformat = SDL_PIXELFORMAT_ARGB1555; + break; + default: + /* ignore 8-bit and such for now. */ + break; + } +#else /* This API is deprecated in 10.11 with no good replacement (as of 10.15). */ CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode); Uint32 pixelformat = SDL_PIXELFORMAT_UNKNOWN; @@ -128,21 +224,44 @@ static Uint32 GetDisplayModePixelFormat(CGDisplayModeRef vidmode) } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { pixelformat = SDL_PIXELFORMAT_ARGB1555; +#ifdef kIO30BitDirectPixels /* Added in the 10.11 SDK */ } else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { pixelformat = SDL_PIXELFORMAT_ARGB2101010; +#endif } else { /* ignore 8-bit and such for now. */ } CFRelease(fmt); +#endif return pixelformat; } -static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmodeCurrent, CFArrayRef modelist, CVDisplayLinkRef link, SDL_DisplayMode *mode) +static SDL_bool GetDisplayMode(_THIS, SDL_CocoaDisplayModeRef vidmode, SDL_bool vidmodeCurrent, CFArrayRef modelist, CVDisplayLinkRef link, SDL_DisplayMode *mode) { SDL_DisplayModeData *data; +#if SDL_COCOA_USE_OLD_CGDISPLAY_API + int width = 0; + int height = 0; + int refreshrate = GetDisplayModeRefreshRate(vidmode, link); + Uint32 format = GetDisplayModePixelFormat(vidmode); + CFMutableArrayRef modes; + { + long w = 0, h = 0; + CFNumberRef number = CFDictionaryGetValue(vidmode, kCGDisplayWidth); + if (number) { + CFNumberGetValue(number, kCFNumberLongType, &w); + } + number = CFDictionaryGetValue(vidmode, kCGDisplayHeight); + if (number) { + CFNumberGetValue(number, kCFNumberLongType, &h); + } + width = (int) w; + height = (int) h; + } +#else bool usableForGUI = CGDisplayModeIsUsableForDesktopGUI(vidmode); int width = (int) CGDisplayModeGetWidth(vidmode); int height = (int) CGDisplayModeGetHeight(vidmode); @@ -151,6 +270,7 @@ static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmode Uint32 format = GetDisplayModePixelFormat(vidmode); bool interlaced = (ioflags & kDisplayModeInterlacedFlag) != 0; CFMutableArrayRef modes; +#endif if (format == SDL_PIXELFORMAT_UNKNOWN) { return SDL_FALSE; @@ -171,7 +291,7 @@ static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmode * prefered, and it can add CGDisplayModes to the DisplayModeData's list of * modes to try (see comment below for why that's necessary). * CGDisplayModeGetPixelWidth and friends are only available in 10.8+. */ -#ifdef MAC_OS_X_VERSION_10_8 +#if !SDL_COCOA_USE_OLD_CGDISPLAY_API && defined(MAC_OS_X_VERSION_10_8) if (modelist != NULL && floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_7) { int pixelW = (int) CGDisplayModeGetPixelWidth(vidmode); int pixelH = (int) CGDisplayModeGetPixelHeight(vidmode); @@ -294,7 +414,7 @@ static const char *Cocoa_GetDisplayName(CGDirectDisplayID displayID) } void Cocoa_InitModes(_THIS) -{ @autoreleasepool +{ SDL_COCOA_POOL { CGDisplayErr result; CGDirectDisplayID *displays; @@ -321,7 +441,7 @@ void Cocoa_InitModes(_THIS) SDL_VideoDisplay display; SDL_DisplayData *displaydata; SDL_DisplayMode mode; - CGDisplayModeRef moderef = NULL; + SDL_CocoaDisplayModeRef moderef = NULL; CVDisplayLinkRef link = NULL; if (pass == 0) { @@ -338,7 +458,7 @@ void Cocoa_InitModes(_THIS) continue; } - moderef = CGDisplayCopyDisplayMode(displays[i]); + moderef = CopyCurrentDisplayMode(displays[i]); if (!moderef) { continue; @@ -346,7 +466,7 @@ void Cocoa_InitModes(_THIS) displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata)); if (!displaydata) { - CGDisplayModeRelease(moderef); + ReleaseDisplayModeRef(moderef); continue; } displaydata->display = displays[i]; @@ -358,14 +478,14 @@ void Cocoa_InitModes(_THIS) display.name = (char *)Cocoa_GetDisplayName(displays[i]); if (!GetDisplayMode(_this, moderef, SDL_TRUE, NULL, link, &mode)) { CVDisplayLinkRelease(link); - CGDisplayModeRelease(moderef); + ReleaseDisplayModeRef(moderef); SDL_free(display.name); SDL_free(displaydata); continue; } CVDisplayLinkRelease(link); - CGDisplayModeRelease(moderef); + ReleaseDisplayModeRef(moderef); display.desktop_mode = mode; display.current_mode = mode; @@ -423,7 +543,7 @@ int Cocoa_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * r } int Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi) -{ @autoreleasepool +{ SDL_COCOA_POOL { const float MM_IN_INCH = 25.4f; @@ -439,7 +559,7 @@ int Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * for (NSScreen *screen in screens) { const CGDirectDisplayID dpyid = (const CGDirectDisplayID ) [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; if (dpyid == data->display) { -#ifdef MAC_OS_X_VERSION_10_8 +#if !SDL_COCOA_USE_OLD_CGDISPLAY_API && defined(MAC_OS_X_VERSION_10_8) /* Neither CGDisplayScreenSize(description's NSScreenNumber) nor [NSScreen backingScaleFactor] can calculate the correct dpi in macOS. E.g. backingScaleFactor is always 2 in all display modes for rMBP 16" */ if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_7) { CFStringRef dmKeys[1] = { kCGDisplayShowDuplicateLowResolutionModes }; @@ -473,10 +593,14 @@ int Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * } else #endif { +#ifdef MAC_OS_X_VERSION_10_7 // fallback for 10.7 scaleFactor = [screen backingScaleFactor]; displayNativeSize.width = displayNativeSize.width * scaleFactor; displayNativeSize.height = displayNativeSize.height * scaleFactor; +#else + (void) scaleFactor; /* no Retina displays before 10.7 */ +#endif break; } } @@ -504,14 +628,14 @@ void Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) { SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; CVDisplayLinkRef link = NULL; - CGDisplayModeRef desktopmoderef; + SDL_CocoaDisplayModeRef desktopmoderef; SDL_DisplayMode desktopmode; CFArrayRef modes; CFDictionaryRef dict = NULL; CVDisplayLinkCreateWithCGDisplay(data->display, &link); - desktopmoderef = CGDisplayCopyDisplayMode(data->display); + desktopmoderef = CopyCurrentDisplayMode(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 @@ -526,7 +650,7 @@ void Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) } } - CGDisplayModeRelease(desktopmoderef); + ReleaseDisplayModeRef(desktopmoderef); /* By default, CGDisplayCopyAllDisplayModes will only get a subset of the * system's available modes. For example on a 15" 2016 MBP, users can @@ -539,7 +663,7 @@ void Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) * the content of the screen to move up, which this setting avoids: * https://bugzilla.libsdl.org/show_bug.cgi?id=4822 */ -#ifdef MAC_OS_X_VERSION_10_8 +#if !SDL_COCOA_USE_OLD_CGDISPLAY_API && defined(MAC_OS_X_VERSION_10_8) if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_7) { const CFStringRef dictkeys[] = {kCGDisplayShowDuplicateLowResolutionModes}; const CFBooleanRef dictvalues[] = {kCFBooleanTrue}; @@ -552,7 +676,7 @@ void Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) } #endif - modes = CGDisplayCopyAllDisplayModes(data->display, dict); + modes = CopyAllDisplayModes(data->display, dict); if (dict) { CFRelease(dict); @@ -563,7 +687,7 @@ void Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) const CFIndex count = CFArrayGetCount(modes); for (i = 0; i < count; i++) { - CGDisplayModeRef moderef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + SDL_CocoaDisplayModeRef moderef = (SDL_CocoaDisplayModeRef) CFArrayGetValueAtIndex(modes, i); SDL_DisplayMode mode; if (GetDisplayMode(_this, moderef, SDL_FALSE, modes, link, &mode)) { @@ -587,8 +711,12 @@ static CGError SetDisplayModeForDisplay(CGDirectDisplayID display, SDL_DisplayMo */ CGError result = kCGErrorFailure; for (CFIndex i = 0; i < CFArrayGetCount(data->modes); i++) { - CGDisplayModeRef moderef = (CGDisplayModeRef)CFArrayGetValueAtIndex(data->modes, i); + SDL_CocoaDisplayModeRef moderef = (SDL_CocoaDisplayModeRef)CFArrayGetValueAtIndex(data->modes, i); +#if SDL_COCOA_USE_OLD_CGDISPLAY_API + result = CGDisplaySwitchToMode(display, moderef); +#else result = CGDisplaySetDisplayMode(display, moderef, NULL); +#endif if (result == kCGErrorSuccess) { /* If this mode works, try it first next time. */ CFArrayExchangeValuesAtIndices(data->modes, i, 0); diff --git src/video/cocoa/SDL_cocoamouse.m src/video/cocoa/SDL_cocoamouse.m index e70c40956..aff231621 100644 --- src/video/cocoa/SDL_cocoamouse.m +++ src/video/cocoa/SDL_cocoamouse.m @@ -53,7 +53,7 @@ NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0] length:sizeof(cursorBytes) freeWhenDone:NO]; - NSImage *cursorImage = [[NSImage alloc] initWithData:cursorData]; + NSImage *cursorImage = SDL_COCOA_AUTORELEASE([[NSImage alloc] initWithData:cursorData]); invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage hotSpot:NSZeroPoint]; } @@ -64,7 +64,7 @@ static SDL_Cursor *Cocoa_CreateDefaultCursor() -{ @autoreleasepool +{ SDL_COCOA_POOL { NSCursor *nscursor; SDL_Cursor *cursor = NULL; @@ -74,7 +74,7 @@ static SDL_Cursor *Cocoa_CreateDefaultCursor() if (nscursor) { cursor = SDL_calloc(1, sizeof(*cursor)); if (cursor) { - cursor->driverdata = (void *)CFBridgingRetain(nscursor); + cursor->driverdata = SDL_CFBridgingRetain(nscursor); } } @@ -82,7 +82,7 @@ static SDL_Cursor *Cocoa_CreateDefaultCursor() }} static SDL_Cursor *Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSImage *nsimage; NSCursor *nscursor = NULL; @@ -90,13 +90,13 @@ static SDL_Cursor *Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_ nsimage = Cocoa_CreateImage(surface); if (nsimage) { - nscursor = [[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)]; + nscursor = SDL_COCOA_AUTORELEASE([[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)]); } if (nscursor) { cursor = SDL_calloc(1, sizeof(*cursor)); if (cursor) { - cursor->driverdata = (void *)CFBridgingRetain(nscursor); + cursor->driverdata = SDL_CFBridgingRetain(nscursor); } } @@ -113,7 +113,7 @@ static NSCursor *LoadHiddenSystemCursor(NSString *cursorName, SEL fallback) /* we can't do animation atm. :/ */ const int frames = (int)[[info valueForKey:@"frames"] integerValue]; NSCursor *cursor; - NSImage *image = [[NSImage alloc] initWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"cursor.pdf"]]; + NSImage *image = SDL_COCOA_AUTORELEASE([[NSImage alloc] initWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"cursor.pdf"]]); if ((image == nil) || (image.isValid == NO)) { return [NSCursor performSelector:fallback]; } @@ -125,7 +125,7 @@ static NSCursor *LoadHiddenSystemCursor(NSString *cursorName, SEL fallback) const NSCompositingOperation operation = NSCompositeCopy; #endif const NSSize cropped_size = NSMakeSize(image.size.width, (int) (image.size.height / frames)); - NSImage *cropped = [[NSImage alloc] initWithSize:cropped_size]; + NSImage *cropped = SDL_COCOA_AUTORELEASE([[NSImage alloc] initWithSize:cropped_size]); if (cropped == nil) { return [NSCursor performSelector:fallback]; } @@ -139,12 +139,12 @@ static NSCursor *LoadHiddenSystemCursor(NSString *cursorName, SEL fallback) image = cropped; } - cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint([[info valueForKey:@"hotx"] doubleValue], [[info valueForKey:@"hoty"] doubleValue])]; + cursor = SDL_COCOA_AUTORELEASE([[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint([[info valueForKey:@"hotx"] doubleValue], [[info valueForKey:@"hoty"] doubleValue])]); return cursor; } static SDL_Cursor *Cocoa_CreateSystemCursor(SDL_SystemCursor id) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSCursor *nscursor = NULL; SDL_Cursor *cursor = NULL; @@ -195,7 +195,7 @@ static SDL_Cursor *Cocoa_CreateSystemCursor(SDL_SystemCursor id) cursor = SDL_calloc(1, sizeof(*cursor)); if (cursor) { /* We'll free it later, so retain it here */ - cursor->driverdata = (void *)CFBridgingRetain(nscursor); + cursor->driverdata = SDL_CFBridgingRetain(nscursor); } } @@ -203,14 +203,14 @@ static SDL_Cursor *Cocoa_CreateSystemCursor(SDL_SystemCursor id) }} static void Cocoa_FreeCursor(SDL_Cursor * cursor) -{ @autoreleasepool +{ SDL_COCOA_POOL { - CFBridgingRelease(cursor->driverdata); + (void) SDL_CFBridgingRelease(cursor->driverdata); SDL_free(cursor); }} static int Cocoa_ShowCursor(SDL_Cursor * cursor) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoDevice *device = SDL_GetVideoDevice(); SDL_Window *window = (device ? device->windows : NULL); @@ -343,9 +343,28 @@ static int Cocoa_CaptureMouse(SDL_Window *window) return 0; } +#ifndef MAC_OS_X_VERSION_10_6 +/* +[NSEvent pressedMouseButtons] arrived with the 10.6 SDK */ +static NSUInteger Cocoa_PressedMouseButtons(void) +{ + NSUInteger buttons = 0; + int btn; + for (btn = 0; btn < 5; ++btn) { + if (CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, (CGMouseButton) btn)) { + buttons |= (1 << btn); + } + } + return buttons; +} +#endif + static Uint32 Cocoa_GetGlobalMouseState(int *x, int *y) { +#ifdef MAC_OS_X_VERSION_10_6 const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons]; +#else + const NSUInteger cocoaButtons = Cocoa_PressedMouseButtons(); +#endif const NSPoint cocoaLocation = [NSEvent mouseLocation]; Uint32 retval = 0; @@ -521,13 +540,16 @@ void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event) y = [event deltaY]; direction = SDL_MOUSEWHEEL_NORMAL; +#ifdef MAC_OS_X_VERSION_10_7 /* these NSEvent methods arrived with the 10.7 SDK */ if ([event isDirectionInvertedFromDevice] == YES) { direction = SDL_MOUSEWHEEL_FLIPPED; } /* For discrete scroll events from conventional mice, always send a full tick. For continuous scroll events from trackpads, send fractional deltas for smoother scrolling. */ - if (![event hasPreciseScrollingDeltas]) { + if (![event hasPreciseScrollingDeltas]) +#endif + { if (x > 0) { x = SDL_ceil(x); } else if (x < 0) { diff --git src/video/cocoa/SDL_cocoaopengl.h src/video/cocoa/SDL_cocoaopengl.h index f152a726c..bddf20b01 100644 --- src/video/cocoa/SDL_cocoaopengl.h +++ src/video/cocoa/SDL_cocoaopengl.h @@ -44,6 +44,9 @@ struct SDL_GLDriverData SDL_atomic_t dirty; SDL_Window *window; CVDisplayLinkRef displayLink; + /* Explicit ivar: synthesized ivars are unavailable with the fragile + Objective-C ABI (32-bit Mac OS X) and with GCC. */ + NSOpenGLPixelFormat *_openglPixelFormat; @public SDL_mutex *swapIntervalMutex; @public SDL_cond *swapIntervalCond; @public SDL_atomic_t swapIntervalSetting; diff --git src/video/cocoa/SDL_cocoaopengl.m src/video/cocoa/SDL_cocoaopengl.m index ffc59e671..da161ac9a 100644 --- src/video/cocoa/SDL_cocoaopengl.m +++ src/video/cocoa/SDL_cocoaopengl.m @@ -62,6 +62,7 @@ SDL_OpenGLAsyncDispatchChanged(void *userdata, const char *name, const char *old SDL_opengl_async_dispatch = SDL_GetStringBoolean(hint, SDL_FALSE); } +#if defined(__clang__) /* legacy GCC builds vsync via NSOpenGLCPSwapInterval instead, see below */ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *) displayLinkContext; @@ -77,9 +78,12 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt return kCVReturnSuccess; } +#endif /* __clang__ */ @implementation SDLOpenGLContext : NSOpenGLContext +@synthesize openglPixelFormat = _openglPixelFormat; + - (id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share { @@ -93,14 +97,20 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt self->swapIntervalCond = SDL_CreateCond(); self->swapIntervalMutex = SDL_CreateMutex(); if (!self->swapIntervalCond || !self->swapIntervalMutex) { + SDL_COCOA_RELEASE(self); return nil; } +#if defined(__clang__) /* !!! FIXME: check return values. */ CVDisplayLinkCreateWithActiveCGDisplays(&self->displayLink); CVDisplayLinkSetOutputCallback(self->displayLink, &DisplayLinkCallback, (__bridge void * _Nullable) self); CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self->displayLink, [self CGLContextObj], [format CGLPixelFormatObj]); CVDisplayLinkStart(displayLink); +#endif + /* legacy GCC builds don't use the display link: NSOpenGLCPSwapInterval + still works before macOS 10.14, and CoreVideo may not on the OS + builds that need FORCE_OLD_API */ } SDL_AddHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL); @@ -122,7 +132,24 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt /* This should only be called on the thread on which a user is using the context. */ - (void)updateIfNeeded { - const int value = SDL_AtomicSet(&self->dirty, 0); + int value; + +#if !defined(__clang__) + /* Complete a view attach that setWindow: deferred (old OS X logs + "invalid drawable" if it happens while the window has no backing + surface). This runs on every MakeCurrent and buffer swap, so the + attach completes as soon as the window is on screen. */ + if (self->window) { + SDL_WindowData *windowdata = (__bridge SDL_WindowData *)self->window->driverdata; + NSView *contentview = windowdata.sdlContentView; + if (([self view] != contentview) && [windowdata.nswindow isVisible]) { + [self setView:contentview]; + [self scheduleUpdate]; + } + } +#endif + + value = SDL_AtomicSet(&self->dirty, 0); if (value > 0) { /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */ [self explicitUpdate]; @@ -163,10 +190,27 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt } if ([self view] != contentview) { +#if !defined(__clang__) + /* Attaching to a window with no backing surface yet makes old + OS X log "invalid drawable" to the terminal; defer the attach + to updateIfNeeded until the window is on screen. (The context + is already registered in nscontexts above.) */ + if (![windowdata.nswindow isVisible]) { + return; + } +#endif if ([NSThread isMainThread]) { [self setView:contentview]; } else { +#if defined(__clang__) dispatch_sync(dispatch_get_main_queue(), ^{ [self setView:contentview]; }); +#else + /* Call directly, like SDL <= 2.0.9 always did: the era of + AppKit that GCC builds target tolerates this off the main + thread, and the main thread may not be running a runloop + to service a marshalled call (e.g. mpv's vo thread). */ + [self setView:contentview]; +#endif } if (self == [NSOpenGLContext currentContext]) { [self explicitUpdate]; @@ -178,7 +222,11 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt if ([NSThread isMainThread]) { [self setView:nil]; } else { +#if defined(__clang__) dispatch_sync(dispatch_get_main_queue(), ^{ [self setView:nil]; }); +#else + [self setView:nil]; /* see the comment in the branch above */ +#endif } } } @@ -193,17 +241,22 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt if ([NSThread isMainThread]) { [super update]; } else { +#if defined(__clang__) if (SDL_opengl_async_dispatch) { dispatch_async(dispatch_get_main_queue(), ^{ [super update]; }); } else { dispatch_sync(dispatch_get_main_queue(), ^{ [super update]; }); } +#else + [super update]; /* see the comment in -setWindow: */ +#endif } } - (void)cleanup { [self setWindow:NULL]; + self.openglPixelFormat = nil; SDL_DelHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL); if (self->displayLink) { @@ -253,7 +306,7 @@ void Cocoa_GL_UnloadLibrary(_THIS) } SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata; @@ -267,7 +320,11 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) int glversion_major; int glversion_minor; NSOpenGLPixelFormatAttribute profile; +#if defined(__clang__) int interval; +#else + long interval; /* -[NSOpenGLContext setValues:forParameter:] takes const long * on old SDKs */ +#endif if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { #ifdef SDL_VIDEO_OPENGL_EGL @@ -295,12 +352,20 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) attr[i++] = NSOpenGLPFAAllowOfflineRenderers; +#ifdef MAC_OS_X_VERSION_10_7 /* NSOpenGLPFAOpenGLProfile arrived with the 10.7 SDK */ profile = NSOpenGLProfileVersionLegacy; if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) { profile = NSOpenGLProfileVersion3_2Core; } attr[i++] = NSOpenGLPFAOpenGLProfile; attr[i++] = profile; +#else + (void) profile; + if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) { + SDL_SetError("OpenGL core profiles are not supported before Mac OS X 10.7"); + return NULL; + } +#endif attr[i++] = NSOpenGLPFAColorSize; attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8; @@ -356,7 +421,7 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display); attr[i] = 0; - fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; + fmt = SDL_COCOA_AUTORELEASE([[NSOpenGLPixelFormat alloc] initWithAttributes:attr]); if (fmt == nil) { SDL_SetError("Failed creating OpenGL pixel format"); return NULL; @@ -373,9 +438,11 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) return NULL; } - sdlcontext = (SDL_GLContext)CFBridgingRetain(context); + sdlcontext = (SDL_GLContext)SDL_CFBridgingRetain(context); + SDL_COCOA_RELEASE(context); /* the sdlcontext reference keeps it alive */ - /* vsync is handled separately by synchronizing with a display link. */ + /* vsync is handled separately by synchronizing with a display link + (or via this parameter in SetSwapInterval on legacy GCC builds). */ interval = 0; [context setValues:&interval forParameter:NSOpenGLCPSwapInterval]; @@ -429,7 +496,7 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) }} int Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) -{ @autoreleasepool +{ SDL_COCOA_POOL { if (context) { SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *)context; @@ -446,7 +513,7 @@ int Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) }} int Cocoa_GL_SetSwapInterval(_THIS, int interval) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *) SDL_GL_GetCurrentContext(); int status; @@ -458,6 +525,15 @@ int Cocoa_GL_SetSwapInterval(_THIS, int interval) SDL_AtomicSet(&nscontext->swapIntervalsPassed, 0); SDL_AtomicSet(&nscontext->swapIntervalSetting, interval); SDL_UnlockMutex(nscontext->swapIntervalMutex); +#if !defined(__clang__) + { + /* Legacy GCC builds vsync via the context parameter, as SDL + <= 2.0.9 did (no adaptive vsync this way). The parameter + takes a const long * on old SDKs. */ + long value = (interval < 0) ? 1 : interval; + [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval]; + } +#endif status = 0; } @@ -465,17 +541,18 @@ int Cocoa_GL_SetSwapInterval(_THIS, int interval) }} int Cocoa_GL_GetSwapInterval(_THIS) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDLOpenGLContext* nscontext = (__bridge SDLOpenGLContext*)SDL_GL_GetCurrentContext(); return nscontext ? SDL_AtomicGet(&nscontext->swapIntervalSetting) : 0; }} int Cocoa_GL_SwapWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDLOpenGLContext* nscontext = (__bridge SDLOpenGLContext*)SDL_GL_GetCurrentContext(); SDL_VideoData *videodata = (__bridge SDL_VideoData *) _this->driverdata; +#if defined(__clang__) /* legacy GCC builds vsync via NSOpenGLCPSwapInterval instead */ const int setting = SDL_AtomicGet(&nscontext->swapIntervalSetting); if (setting == 0) { @@ -495,6 +572,7 @@ int Cocoa_GL_SwapWindow(_THIS, SDL_Window * window) SDL_AtomicSet(&nscontext->swapIntervalsPassed, 0); SDL_UnlockMutex(nscontext->swapIntervalMutex); } +#endif /* __clang__ */ /*{ static Uint64 prev = 0; const Uint64 now = SDL_GetTicks64(); const unsigned int diff = (unsigned int) (now - prev); prev = now; printf("GLSWAPBUFFERS TICKS %u\n", diff); }*/ @@ -509,7 +587,7 @@ int Cocoa_GL_SwapWindow(_THIS, SDL_Window * window) static void DispatchedDeleteContext(SDL_GLContext context) { - @autoreleasepool { + SDL_COCOA_POOL { SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *)context; [nscontext cleanup]; CFRelease(context); @@ -521,6 +599,7 @@ void Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context) if ([NSThread isMainThread]) { DispatchedDeleteContext(context); } else { +#if defined(__clang__) if (SDL_opengl_async_dispatch) { dispatch_async(dispatch_get_main_queue(), ^{ DispatchedDeleteContext(context); @@ -530,6 +609,9 @@ void Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context) DispatchedDeleteContext(context); }); } +#else + DispatchedDeleteContext(context); /* see the comment in -setWindow: */ +#endif } } diff --git src/video/cocoa/SDL_cocoaopengles.m src/video/cocoa/SDL_cocoaopengles.m index 074646037..94bd6027c 100644 --- src/video/cocoa/SDL_cocoaopengles.m +++ src/video/cocoa/SDL_cocoaopengles.m @@ -57,7 +57,7 @@ int Cocoa_GLES_LoadLibrary(_THIS, const char *path) } SDL_GLContext Cocoa_GLES_CreateContext(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_GLContext context; SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; @@ -89,19 +89,19 @@ SDL_GLContext Cocoa_GLES_CreateContext(_THIS, SDL_Window * window) }} void Cocoa_GLES_DeleteContext(_THIS, SDL_GLContext context) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_EGL_DeleteContext(_this, context); }} int Cocoa_GLES_SwapWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { return SDL_EGL_SwapBuffers(_this, ((__bridge SDL_WindowData *) window->driverdata).egl_surface); }} int Cocoa_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) -{ @autoreleasepool +{ SDL_COCOA_POOL { return SDL_EGL_MakeCurrent(_this, window ? ((__bridge SDL_WindowData *) window->driverdata).egl_surface : EGL_NO_SURFACE, context); }} diff --git src/video/cocoa/SDL_cocoashape.h src/video/cocoa/SDL_cocoashape.h index 82b8658b3..ed4c9cca4 100644 --- src/video/cocoa/SDL_cocoashape.h +++ src/video/cocoa/SDL_cocoashape.h @@ -30,7 +30,12 @@ #include "../SDL_shape_internals.h" @interface SDL_ShapeData : NSObject - @property (nonatomic) NSGraphicsContext* context; +{ + NSGraphicsContext *_context; + SDL_bool _saved; + SDL_ShapeTree *_shape; +} + @property (nonatomic, retain) NSGraphicsContext* context; @property (nonatomic) SDL_bool saved; @property (nonatomic) SDL_ShapeTree* shape; @end diff --git src/video/cocoa/SDL_cocoashape.m src/video/cocoa/SDL_cocoashape.m index e1421ee1b..1373a361b 100644 --- src/video/cocoa/SDL_cocoashape.m +++ src/video/cocoa/SDL_cocoashape.m @@ -28,19 +28,51 @@ #include "../SDL_sysvideo.h" @implementation SDL_ShapeData + +@synthesize context = _context; +@synthesize saved = _saved; +@synthesize shape = _shape; + +#if !SDL_COCOA_ARC +- (void)dealloc +{ + [_context release]; + [super dealloc]; +} +#endif + @end @interface SDL_CocoaClosure : NSObject - @property (nonatomic) NSView* view; - @property (nonatomic) NSBezierPath* path; +{ + NSView *_view; + NSBezierPath *_path; + SDL_Window *_window; +} + @property (nonatomic, retain) NSView* view; + @property (nonatomic, retain) NSBezierPath* path; @property (nonatomic) SDL_Window* window; @end @implementation SDL_CocoaClosure + +@synthesize view = _view; +@synthesize path = _path; +@synthesize window = _window; + +#if !SDL_COCOA_ARC +- (void)dealloc +{ + [_view release]; + [_path release]; + [super dealloc]; +} +#endif + @end SDL_WindowShaper *Cocoa_CreateShaper(SDL_Window* window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowShaper* result; SDL_ShapeData* data; @@ -55,7 +87,13 @@ SDL_WindowShaper *Cocoa_CreateShaper(SDL_Window* window) [windata.nswindow setOpaque:NO]; +#ifdef MAC_OS_X_VERSION_10_6 [windata.nswindow setStyleMask:NSWindowStyleMaskBorderless]; +#else + if ([windata.nswindow respondsToSelector:@selector(setStyleMask:)]) { + [windata.nswindow performSelector:@selector(setStyleMask:) withObject:(id)(uintptr_t)NSWindowStyleMaskBorderless]; + } +#endif result->window = window; result->mode.mode = ShapeModeDefault; @@ -69,7 +107,8 @@ SDL_WindowShaper *Cocoa_CreateShaper(SDL_Window* window) data.shape = NULL; /* TODO: There's no place to release this... */ - result->driverdata = (void*) CFBridgingRetain(data); + result->driverdata = SDL_CFBridgingRetain(data); + SDL_COCOA_RELEASE(data); /* the driverdata reference keeps it alive */ resized_properly = Cocoa_ResizeWindowShape(window); SDL_assert(resized_properly == 0); @@ -86,7 +125,7 @@ static void ConvertRects(SDL_ShapeTree* tree, void* closure) } int Cocoa_SetWindowShape(SDL_WindowShaper *shaper, SDL_Surface *shape, SDL_WindowShapeMode *shape_mode) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_ShapeData* data = (__bridge SDL_ShapeData*)shaper->driverdata; SDL_WindowData* windata = (__bridge SDL_WindowData*)shaper->window->driverdata; @@ -104,7 +143,7 @@ int Cocoa_SetWindowShape(SDL_WindowShaper *shaper, SDL_Surface *shape, SDL_Windo NSRectFill([windata.sdlContentView frame]); data.shape = SDL_CalculateShapeTree(*shape_mode, shape); - closure = [[SDL_CocoaClosure alloc] init]; + closure = SDL_COCOA_AUTORELEASE([[SDL_CocoaClosure alloc] init]); closure.view = windata.sdlContentView; closure.path = [NSBezierPath bezierPath]; @@ -116,7 +155,7 @@ int Cocoa_SetWindowShape(SDL_WindowShaper *shaper, SDL_Surface *shape, SDL_Windo }} int Cocoa_ResizeWindowShape(SDL_Window *window) -{ @autoreleasepool { +{ SDL_COCOA_POOL { SDL_ShapeData* data = (__bridge SDL_ShapeData*)window->shaper->driverdata; SDL_assert(data != NULL); return 0; diff --git src/video/cocoa/SDL_cocoavideo.h src/video/cocoa/SDL_cocoavideo.h index d7663f90c..d159a0c1d 100644 --- src/video/cocoa/SDL_cocoavideo.h +++ src/video/cocoa/SDL_cocoavideo.h @@ -29,6 +29,8 @@ #include #include +#include "SDL_cocoacompat.h" + #include "SDL_keycode.h" #include "../SDL_sysvideo.h" @@ -41,56 +43,57 @@ #include "SDL_cocoawindow.h" #ifndef MAC_OS_X_VERSION_10_12 -#define DECLARE_EVENT(name) static const NSEventType NSEventType##name = NS##name -DECLARE_EVENT(LeftMouseDown); -DECLARE_EVENT(LeftMouseUp); -DECLARE_EVENT(RightMouseDown); -DECLARE_EVENT(RightMouseUp); -DECLARE_EVENT(OtherMouseDown); -DECLARE_EVENT(OtherMouseUp); -DECLARE_EVENT(MouseMoved); -DECLARE_EVENT(LeftMouseDragged); -DECLARE_EVENT(RightMouseDragged); -DECLARE_EVENT(OtherMouseDragged); -DECLARE_EVENT(ScrollWheel); -DECLARE_EVENT(KeyDown); -DECLARE_EVENT(KeyUp); -DECLARE_EVENT(FlagsChanged); -#undef DECLARE_EVENT - -static const NSEventMask NSEventMaskAny = NSAnyEventMask; - -#define DECLARE_MODIFIER_FLAG(name) static const NSUInteger NSEventModifierFlag##name = NS##name##KeyMask -DECLARE_MODIFIER_FLAG(Shift); -DECLARE_MODIFIER_FLAG(Control); -DECLARE_MODIFIER_FLAG(Command); -DECLARE_MODIFIER_FLAG(NumericPad); -DECLARE_MODIFIER_FLAG(Help); -DECLARE_MODIFIER_FLAG(Function); -#undef DECLARE_MODIFIER_FLAG -static const NSUInteger NSEventModifierFlagCapsLock = NSAlphaShiftKeyMask; -static const NSUInteger NSEventModifierFlagOption = NSAlternateKeyMask; - -#define DECLARE_WINDOW_MASK(name) static const unsigned int NSWindowStyleMask##name = NS##name##WindowMask -DECLARE_WINDOW_MASK(Borderless); -DECLARE_WINDOW_MASK(Titled); -DECLARE_WINDOW_MASK(Closable); -DECLARE_WINDOW_MASK(Miniaturizable); -DECLARE_WINDOW_MASK(Resizable); -DECLARE_WINDOW_MASK(TexturedBackground); -DECLARE_WINDOW_MASK(UnifiedTitleAndToolbar); -DECLARE_WINDOW_MASK(FullScreen); -/*DECLARE_WINDOW_MASK(FullSizeContentView);*/ /* Not used, fails compile on older SDKs */ -static const unsigned int NSWindowStyleMaskUtilityWindow = NSUtilityWindowMask; -static const unsigned int NSWindowStyleMaskDocModalWindow = NSDocModalWindowMask; -static const unsigned int NSWindowStyleMaskHUDWindow = NSHUDWindowMask; -#undef DECLARE_WINDOW_MASK - -#define DECLARE_ALERT_STYLE(name) static const NSUInteger NSAlertStyle##name = NS##name##AlertStyle -DECLARE_ALERT_STYLE(Warning); -DECLARE_ALERT_STYLE(Informational); -DECLARE_ALERT_STYLE(Critical); -#undef DECLARE_ALERT_STYLE +/* #defines rather than static consts: GCC does not accept const variables + as switch case labels, and these are used in event-type switches. */ +#define NSEventTypeLeftMouseDown NSLeftMouseDown +#define NSEventTypeLeftMouseUp NSLeftMouseUp +#define NSEventTypeRightMouseDown NSRightMouseDown +#define NSEventTypeRightMouseUp NSRightMouseUp +#define NSEventTypeOtherMouseDown NSOtherMouseDown +#define NSEventTypeOtherMouseUp NSOtherMouseUp +#define NSEventTypeMouseMoved NSMouseMoved +#define NSEventTypeLeftMouseDragged NSLeftMouseDragged +#define NSEventTypeRightMouseDragged NSRightMouseDragged +#define NSEventTypeOtherMouseDragged NSOtherMouseDragged +#define NSEventTypeScrollWheel NSScrollWheel +#define NSEventTypeKeyDown NSKeyDown +#define NSEventTypeKeyUp NSKeyUp +#define NSEventTypeFlagsChanged NSFlagsChanged + +/* Pre-10.12 SDKs do not all have the NSEventMask typedef */ +#define NSEventMaskAny NSAnyEventMask + +#define NSEventModifierFlagShift NSShiftKeyMask +#define NSEventModifierFlagControl NSControlKeyMask +#define NSEventModifierFlagCommand NSCommandKeyMask +#define NSEventModifierFlagNumericPad NSNumericPadKeyMask +#define NSEventModifierFlagHelp NSHelpKeyMask +#define NSEventModifierFlagFunction NSFunctionKeyMask +#define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask +#define NSEventModifierFlagOption NSAlternateKeyMask + +#define NSWindowStyleMaskBorderless NSBorderlessWindowMask +#define NSWindowStyleMaskTitled NSTitledWindowMask +#define NSWindowStyleMaskClosable NSClosableWindowMask +#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask +#define NSWindowStyleMaskResizable NSResizableWindowMask +#define NSWindowStyleMaskTexturedBackground NSTexturedBackgroundWindowMask +#define NSWindowStyleMaskUnifiedTitleAndToolbar NSUnifiedTitleAndToolbarWindowMask +#ifdef MAC_OS_X_VERSION_10_7 /* NSFullScreenWindowMask first appeared in the 10.7 SDK */ +#define NSWindowStyleMaskFullScreen NSFullScreenWindowMask +#else +#define NSWindowStyleMaskFullScreen (1 << 14) +#endif +/* NSWindowStyleMaskFullSizeContentView not provided; unused, fails compile on older SDKs */ +#define NSWindowStyleMaskUtilityWindow NSUtilityWindowMask +#define NSWindowStyleMaskDocModalWindow NSDocModalWindowMask +#ifdef MAC_OS_X_VERSION_10_6 /* NSHUDWindowMask first appeared in the 10.6 SDK */ +#define NSWindowStyleMaskHUDWindow NSHUDWindowMask +#endif + +#define NSAlertStyleWarning NSWarningAlertStyle +#define NSAlertStyleInformational NSInformationalAlertStyle +#define NSAlertStyleCritical NSCriticalAlertStyle #endif /* Private display data */ @@ -98,11 +101,23 @@ DECLARE_ALERT_STYLE(Critical); @class SDLTranslatorResponder; @interface SDL_VideoData : NSObject +{ + /* Explicit ivars: synthesized ivars are unavailable with the fragile + Objective-C ABI (32-bit Mac OS X) and with GCC. */ + int _allow_spaces; + int _trackpad_is_touch_only; + unsigned int _modifierFlags; + void *_key_layout; + SDLTranslatorResponder *_fieldEdit; + NSInteger _clipboard_count; + IOPMAssertionID _screensaver_assertion; + SDL_mutex *_swaplock; +} @property (nonatomic) int allow_spaces; @property (nonatomic) int trackpad_is_touch_only; @property (nonatomic) unsigned int modifierFlags; @property (nonatomic) void *key_layout; - @property (nonatomic) SDLTranslatorResponder *fieldEdit; + @property (nonatomic, retain) SDLTranslatorResponder *fieldEdit; @property (nonatomic) NSInteger clipboard_count; @property (nonatomic) IOPMAssertionID screensaver_assertion; @property (nonatomic) SDL_mutex *swaplock; diff --git src/video/cocoa/SDL_cocoavideo.m src/video/cocoa/SDL_cocoavideo.m index 4ca68ba83..02d823515 100644 --- src/video/cocoa/SDL_cocoavideo.m +++ src/video/cocoa/SDL_cocoavideo.m @@ -22,10 +22,6 @@ #ifdef SDL_VIDEO_DRIVER_COCOA -#if !__has_feature(objc_arc) -#error SDL must be built with Objective-C ARC (automatic reference counting) enabled -#endif - #include "SDL.h" #include "SDL_endian.h" #include "SDL_cocoavideo.h" @@ -35,8 +31,31 @@ #include "SDL_cocoaopengles.h" #include "SDL_cocoamessagebox.h" +/* ARC is expected with clang on the modern (non-fragile) Objective-C runtime; + GCC and 32-bit fragile-runtime builds use manual retain/release instead. */ +#if defined(__clang__) && defined(__OBJC2__) && !SDL_COCOA_ARC +#error SDL must be built with Objective-C ARC (automatic reference counting) enabled +#endif + @implementation SDL_VideoData +@synthesize allow_spaces = _allow_spaces; +@synthesize trackpad_is_touch_only = _trackpad_is_touch_only; +@synthesize modifierFlags = _modifierFlags; +@synthesize key_layout = _key_layout; +@synthesize fieldEdit = _fieldEdit; +@synthesize clipboard_count = _clipboard_count; +@synthesize screensaver_assertion = _screensaver_assertion; +@synthesize swaplock = _swaplock; + +#if !SDL_COCOA_ARC +- (void)dealloc +{ + [_fieldEdit release]; + [super dealloc]; +} +#endif + @end /* Initialization/Query functions */ @@ -46,14 +65,14 @@ static void Cocoa_VideoQuit(_THIS); /* Cocoa driver bootstrap functions */ static void Cocoa_DeleteDevice(SDL_VideoDevice * device) -{ @autoreleasepool +{ SDL_COCOA_POOL { - CFBridgingRelease(device->driverdata); + (void) SDL_CFBridgingRelease(device->driverdata); SDL_free(device); }} static SDL_VideoDevice *Cocoa_CreateDevice(void) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoDevice *device; SDL_VideoData *data; @@ -72,7 +91,8 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void) SDL_free(device); return NULL; } - device->driverdata = (void *)CFBridgingRetain(data); + device->driverdata = SDL_CFBridgingRetain(data); + SDL_COCOA_RELEASE(data); /* the driverdata reference keeps it alive */ /* Set the function pointers */ device->VideoInit = Cocoa_VideoInit; @@ -182,7 +202,7 @@ VideoBootStrap COCOA_bootstrap = { int Cocoa_VideoInit(_THIS) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; @@ -192,7 +212,12 @@ int Cocoa_VideoInit(_THIS) return -1; } - data.allow_spaces = SDL_GetHintBoolean(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, SDL_TRUE); +#ifdef MAC_OS_X_VERSION_10_7 + data.allow_spaces = SDL_GetHintBoolean(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, SDL_TRUE) && + (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_7); +#else + data.allow_spaces = 0; /* fullscreen Spaces need 10.7+ */ +#endif data.trackpad_is_touch_only = SDL_GetHintBoolean(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, SDL_FALSE); data.swaplock = SDL_CreateMutex(); @@ -204,7 +229,7 @@ int Cocoa_VideoInit(_THIS) }} void Cocoa_VideoQuit(_THIS) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; Cocoa_QuitModes(_this); @@ -228,7 +253,7 @@ NSImage *Cocoa_CreateImage(SDL_Surface * surface) return nil; } - imgrep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL + imgrep = SDL_COCOA_AUTORELEASE([[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL pixelsWide: converted->w pixelsHigh: converted->h bitsPerSample: 8 @@ -237,7 +262,7 @@ NSImage *Cocoa_CreateImage(SDL_Surface * surface) isPlanar: NO colorSpaceName: NSDeviceRGBColorSpace bytesPerRow: converted->pitch - bitsPerPixel: converted->format->BitsPerPixel]; + bitsPerPixel: converted->format->BitsPerPixel]); if (imgrep == nil) { SDL_FreeSurface(converted); return nil; @@ -257,7 +282,7 @@ NSImage *Cocoa_CreateImage(SDL_Surface * surface) pixels += 4; } - img = [[NSImage alloc] initWithSize: NSMakeSize(surface->w, surface->h)]; + img = SDL_COCOA_AUTORELEASE([[NSImage alloc] initWithSize: NSMakeSize(surface->w, surface->h)]); if (img != nil) { [img addRepresentation: imgrep]; } @@ -276,7 +301,7 @@ NSImage *Cocoa_CreateImage(SDL_Surface * surface) void SDL_NSLog(const char *prefix, const char *text) { - @autoreleasepool { + SDL_COCOA_POOL { NSString *nsText = [NSString stringWithUTF8String:text]; if (prefix) { NSString *nsPrefix = [NSString stringWithUTF8String:prefix]; diff --git src/video/cocoa/SDL_cocoawindow.h src/video/cocoa/SDL_cocoawindow.h index 7ef0bc036..324c4b54e 100644 --- src/video/cocoa/SDL_cocoawindow.h +++ src/video/cocoa/SDL_cocoawindow.h @@ -25,6 +25,8 @@ #import +#include "SDL_cocoacompat.h" + #ifdef SDL_VIDEO_OPENGL_EGL #include "../SDL_egl_c.h" #endif @@ -39,11 +41,15 @@ typedef enum PENDING_OPERATION_MINIMIZE } PendingWindowOperation; +#ifdef MAC_OS_X_VERSION_10_6 /* delegate protocols were introduced with the 10.6 SDK */ @interface Cocoa_WindowListener : NSResponder { +#else +@interface Cocoa_WindowListener : NSResponder { +#endif /* SDL_WindowData owns this Listener and has a strong reference to it. * To avoid reference cycles, we could have either a weak or an * unretained ref to the WindowData. */ - __weak SDL_WindowData *_data; + SDL_COCOA_WEAK SDL_WindowData *_data; BOOL observingVisible; BOOL wasCtrlLeft; BOOL wasVisible; @@ -94,7 +100,9 @@ typedef enum -(void) windowDidEnterFullScreen:(NSNotification *) aNotification; -(void) windowWillExitFullScreen:(NSNotification *) aNotification; -(void) windowDidExitFullScreen:(NSNotification *) aNotification; +#ifdef MAC_OS_X_VERSION_10_7 /* fullscreen presentation options arrived with the 10.7 SDK */ -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions; +#endif /* See if event is in a drag area, toggle on window dragging. */ -(BOOL) processHitTest:(NSEvent *)theEvent; @@ -111,6 +119,7 @@ typedef enum -(void) rightMouseDragged:(NSEvent *) theEvent; -(void) otherMouseDragged:(NSEvent *) theEvent; -(void) scrollWheel:(NSEvent *) theEvent; +#ifdef MAC_OS_X_VERSION_10_6 /* NSTouch and friends arrived with the 10.6 SDK */ -(void) touchesBeganWithEvent:(NSEvent *) theEvent; -(void) touchesMovedWithEvent:(NSEvent *) theEvent; -(void) touchesEndedWithEvent:(NSEvent *) theEvent; @@ -118,6 +127,7 @@ typedef enum /* Touch event handling */ -(void) handleTouches:(NSTouchPhase) phase withEvent:(NSEvent*) theEvent; +#endif @end /* *INDENT-ON* */ @@ -126,16 +136,33 @@ typedef enum @class SDL_VideoData; @interface SDL_WindowData : NSObject +{ + /* Explicit ivars: synthesized ivars are unavailable with the fragile + Objective-C ABI (32-bit Mac OS X) and with GCC. */ + SDL_Window *_window; + NSWindow *_nswindow; + NSView *_sdlContentView; + NSMutableArray *_nscontexts; + SDL_bool _created; + SDL_bool _inWindowFullscreenTransition; + NSInteger _window_number; + NSInteger _flash_request; + Cocoa_WindowListener *_listener; + SDL_VideoData *_videodata; +#ifdef SDL_VIDEO_OPENGL_EGL + EGLSurface _egl_surface; +#endif +} @property (nonatomic) SDL_Window *window; - @property (nonatomic) NSWindow *nswindow; - @property (nonatomic) NSView *sdlContentView; - @property (nonatomic) NSMutableArray *nscontexts; + @property (nonatomic, retain) NSWindow *nswindow; + @property (nonatomic, retain) NSView *sdlContentView; + @property (nonatomic, retain) NSMutableArray *nscontexts; @property (nonatomic) SDL_bool created; @property (nonatomic) SDL_bool inWindowFullscreenTransition; @property (nonatomic) NSInteger window_number; @property (nonatomic) NSInteger flash_request; - @property (nonatomic) Cocoa_WindowListener *listener; - @property (nonatomic) SDL_VideoData *videodata; + @property (nonatomic, retain) Cocoa_WindowListener *listener; + @property (nonatomic, retain) SDL_VideoData *videodata; #ifdef SDL_VIDEO_OPENGL_EGL @property (nonatomic) EGLSurface egl_surface; #endif diff --git src/video/cocoa/SDL_cocoawindow.m src/video/cocoa/SDL_cocoawindow.m index 641df8129..40f559523 100644 --- src/video/cocoa/SDL_cocoawindow.m +++ src/video/cocoa/SDL_cocoawindow.m @@ -22,9 +22,9 @@ #ifdef SDL_VIDEO_DRIVER_COCOA -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 -# error SDL for Mac OS X must be built with a 10.7 SDK or above. -#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1070 */ +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 +# error SDL for Mac OS X must be built with a 10.5 SDK or above. +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1050 */ #include "SDL_syswm.h" #include "SDL_timer.h" /* For SDL_GetTicks() */ @@ -62,8 +62,53 @@ #define NSAppKitVersionNumber10_14 1671 #endif +/* The fullscreen-Spaces machinery is compiled in unconditionally, but is only + * ever exercised at runtime on 10.7+ (videodata.allow_spaces checks the OS + * version). Old SDKs lack these constants and notification-name symbols; + * providing the raw values / literal strings keeps the code building, and the + * observers simply never fire on OS releases that never post them. */ +#ifndef MAC_OS_X_VERSION_10_6 +#define NSWindowCollectionBehaviorManaged (1 << 2) +#define NSWindowWillStartLiveResizeNotification @"NSWindowWillStartLiveResizeNotification" +#define NSWindowDidEndLiveResizeNotification @"NSWindowDidEndLiveResizeNotification" +#endif +#ifndef MAC_OS_X_VERSION_10_7 +#define NSWindowCollectionBehaviorFullScreenPrimary (1 << 7) +#define NSWindowDidChangeBackingPropertiesNotification @"NSWindowDidChangeBackingPropertiesNotification" +#define NSWindowWillEnterFullScreenNotification @"NSWindowWillEnterFullScreenNotification" +#define NSWindowDidEnterFullScreenNotification @"NSWindowDidEnterFullScreenNotification" +#define NSWindowWillExitFullScreenNotification @"NSWindowWillExitFullScreenNotification" +#define NSWindowDidExitFullScreenNotification @"NSWindowDidExitFullScreenNotification" +#endif + @implementation SDL_WindowData +@synthesize window = _window; +@synthesize nswindow = _nswindow; +@synthesize sdlContentView = _sdlContentView; +@synthesize nscontexts = _nscontexts; +@synthesize created = _created; +@synthesize inWindowFullscreenTransition = _inWindowFullscreenTransition; +@synthesize window_number = _window_number; +@synthesize flash_request = _flash_request; +@synthesize listener = _listener; +@synthesize videodata = _videodata; +#ifdef SDL_VIDEO_OPENGL_EGL +@synthesize egl_surface = _egl_surface; +#endif + +#if !SDL_COCOA_ARC +- (void)dealloc +{ + [_nswindow release]; + [_sdlContentView release]; + [_nscontexts release]; + [_listener release]; + [_videodata release]; + [super dealloc]; +} +#endif + @end @interface NSWindow (SDL) @@ -75,7 +120,11 @@ @property (nonatomic) NSRect mouseConfinementRect; @end +#ifdef MAC_OS_X_VERSION_10_7 /* formal NSDraggingDestination protocol arrived with the 10.7 SDK */ @interface SDLWindow : NSWindow +#else +@interface SDLWindow : NSWindow +#endif /* These are needed for borderless/fullscreen windows */ - (BOOL)canBecomeKeyWindow; - (BOOL)canBecomeMainWindow; @@ -158,7 +207,7 @@ } - (BOOL)performDragOperation:(id )sender -{ @autoreleasepool +{ SDL_COCOA_POOL { NSPasteboard *pasteboard = [sender draggingPasteboard]; NSArray *types = [NSArray arrayWithObject:NSFilenamesPboardType]; @@ -194,6 +243,8 @@ for (NSString *path in array) { NSURL *fileURL = [NSURL fileURLWithPath:path]; + +#ifdef MAC_OS_X_VERSION_10_6 /* URL resource values and bookmarks arrived with the 10.6 SDK */ NSNumber *isAlias = nil; [fileURL getResourceValue:&isAlias forKey:NSURLIsAliasFileKey error:nil]; @@ -214,6 +265,7 @@ } } } +#endif if (!SDL_SendDropFile(sdlwindow, [[fileURL path] UTF8String])) { return NO; @@ -335,12 +387,25 @@ static SDL_bool SetWindowStyle(SDL_Window * window, NSUInteger style) SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; NSWindow *nswindow = data.nswindow; +#ifndef MAC_OS_X_VERSION_10_6 + /* -[NSWindow setStyleMask:] arrived in 10.6; a 10.5 window keeps the + style it was created with. */ + if (![nswindow respondsToSelector:@selector(setStyleMask:)]) { + return SDL_FALSE; + } +#endif + /* The view responder chain gets messed with during setStyleMask */ if ([data.sdlContentView nextResponder] == data.listener) { [data.sdlContentView setNextResponder:nil]; } +#ifdef MAC_OS_X_VERSION_10_6 [nswindow setStyleMask:style]; +#else + /* the NSUInteger argument rides in the pointer slot */ + [nswindow performSelector:@selector(setStyleMask:) withObject:(id)(uintptr_t)style]; +#endif /* The view responder chain gets messed with during setStyleMask */ if ([data.sdlContentView nextResponder] != data.listener) { @@ -531,7 +596,9 @@ static NSCursor *Cocoa_GetDesiredCursor(void) [view setNextResponder:self]; +#ifdef MAC_OS_X_VERSION_10_6 /* -setAcceptsTouchEvents: arrived with the 10.6 SDK */ [view setAcceptsTouchEvents:YES]; +#endif } - (void)observeValueForKeyPath:(NSString *)keyPath @@ -921,7 +988,12 @@ static NSCursor *Cocoa_GetDesiredCursor(void) [NSMenu setMenuBarVisible:NO]; } { +#ifdef MAC_OS_X_VERSION_10_6 /* +[NSEvent modifierFlags] arrived with the 10.6 SDK */ const unsigned int newflags = [NSEvent modifierFlags] & NSEventModifierFlagCapsLock; +#else + /* same NX_* bit values as the AppKit modifier masks */ + const unsigned int newflags = (unsigned int)CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState) & NSEventModifierFlagCapsLock; +#endif _data.videodata.modifierFlags = (_data.videodata.modifierFlags & ~NSEventModifierFlagCapsLock) | newflags; SDL_ToggleModState(KMOD_CAPS, newflags ? SDL_TRUE : SDL_FALSE); } @@ -951,6 +1023,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) - (void)windowDidChangeBackingProperties:(NSNotification *)aNotification { +#ifdef MAC_OS_X_VERSION_10_7 /* backing scale factors arrived with the 10.7 SDK, and this never fires before 10.7 */ NSNumber *oldscale = [[aNotification userInfo] objectForKey:NSBackingPropertyOldScaleFactorKey]; if (inFullscreenTransition) { @@ -963,6 +1036,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) _data.window->h = 0; [self windowDidResize:aNotification]; } +#endif } - (void)windowDidChangeScreenProfile:(NSNotification *)aNotification @@ -1161,6 +1235,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) } } +#ifdef MAC_OS_X_VERSION_10_7 /* fullscreen presentation options arrived with the 10.7 SDK */ -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions { if ((_data.window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) { @@ -1169,6 +1244,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) return proposedOptions; } } +#endif /* We'll respond to key events by mostly doing nothing so we don't beep. * We could handle key messages here, but we lose some in the NSApp dispatch, @@ -1455,6 +1531,7 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL with the SDL_HINT_TRACKPAD_IS_TOUCH_ONLY hint. */ BOOL istrackpad = NO; if (!videodata.trackpad_is_touch_only) { +#if defined(__clang__) @try { istrackpad = ([theEvent subtype] == NSEventSubtypeMouseEvent); } @@ -1466,10 +1543,16 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL * *** Assertion failure in -[NSEvent subtype] */ } +#else + /* GCC would need -fobjc-exceptions for the @try above. GCC builds + * only target Mac OS X 10.5-10.10, where -[NSEvent subtype] raises + * for gesture events, so the result would be NO there anyway. */ +#endif } return istrackpad; } +#ifdef MAC_OS_X_VERSION_10_6 /* NSTouch and friends arrived with the 10.6 SDK */ - (void)touchesBeganWithEvent:(NSEvent *) theEvent { NSSet *touches; @@ -1579,6 +1662,7 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL } } } +#endif /* MAC_OS_X_VERSION_10_6 */ @end @@ -1593,8 +1677,10 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL - (BOOL)mouseDownCanMoveWindow; - (void)drawRect:(NSRect)dirtyRect; - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent; +#ifdef MAC_OS_X_VERSION_10_7 /* the layer-backed path is 10.8+ only */ - (BOOL)wantsUpdateLayer; - (void)updateLayer; +#endif @end @implementation SDLView @@ -1616,13 +1702,17 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL if ([NSGraphicsContext currentContext]) { [[NSColor blackColor] setFill]; NSRectFill(dirtyRect); +#ifdef MAC_OS_X_VERSION_10_7 /* pre-10.7 SDKs don't expose the CALayer interface via Cocoa.h */ } else if (self.layer) { self.layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack); +#endif } SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0); } +#ifdef MAC_OS_X_VERSION_10_7 +/* The updateLayer path is only taken by layer-backed views (10.8+). */ - (BOOL)wantsUpdateLayer { return YES; @@ -1638,6 +1728,7 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL ScheduleContextUpdates((__bridge SDL_WindowData *) _sdlWindow->driverdata); SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0); } +#endif /* MAC_OS_X_VERSION_10_7 */ - (void)rightMouseDown:(NSEvent *)theEvent { @@ -1670,7 +1761,7 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL @end static int SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, NSView *nsview, SDL_bool created) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoData *videodata = (__bridge SDL_VideoData *) _this->driverdata; SDL_WindowData *data; @@ -1685,11 +1776,19 @@ static int SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, NSVie data.created = created; data.videodata = videodata; data.window_number = nswindow.windowNumber; - data.nscontexts = [[NSMutableArray alloc] init]; + { + NSMutableArray *contexts = [[NSMutableArray alloc] init]; + data.nscontexts = contexts; + SDL_COCOA_RELEASE(contexts); /* the property holds a reference */ + } data.sdlContentView = nsview; /* Create an event listener for the window */ - data.listener = [[Cocoa_WindowListener alloc] init]; + { + Cocoa_WindowListener *listener = [[Cocoa_WindowListener alloc] init]; + data.listener = listener; + SDL_COCOA_RELEASE(listener); /* the property holds a reference */ + } /* Fill in the SDL window with the window data */ { @@ -1750,7 +1849,9 @@ static int SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, NSVie * it will also call [NSWindow close] in DestroyWindow before releasing the * NSWindow, so the extra release provided by releasedWhenClosed isn't * necessary. */ - nswindow.releasedWhenClosed = NO; + /* bracket form: GCC's dot syntax can't resolve this without a declared + @property (the getter is named isReleasedWhenClosed) */ + [nswindow setReleasedWhenClosed:NO]; /* Prevents the window's "window device" from being destroyed when it is * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html @@ -1758,12 +1859,13 @@ static int SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, NSVie [nswindow setOneShot:NO]; /* All done! */ - window->driverdata = (void *)CFBridgingRetain(data); + window->driverdata = SDL_CFBridgingRetain(data); + SDL_COCOA_RELEASE(data); /* the driverdata reference keeps it alive */ return 0; }} int Cocoa_CreateWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoData *videodata = (__bridge SDL_VideoData *) _this->driverdata; NSWindow *nswindow; @@ -1798,14 +1900,21 @@ int Cocoa_CreateWindow(_THIS, SDL_Window * window) } } +#if defined(__clang__) @try { nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen]; } @catch (NSException *e) { return SDL_SetError("%s", [[e reason] UTF8String]); } +#else + /* GCC would need -fobjc-exceptions for the @try above. */ + nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen]; +#endif +#ifdef MAC_OS_X_VERSION_10_6 /* -setColorSpace: arrived with the 10.6 SDK */ [nswindow setColorSpace:[NSColorSpace sRGBColorSpace]]; +#endif #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 /* Added in the 10.12.0 SDK. */ /* By default, don't allow users to make our window tabbed in 10.12 or later */ @@ -1838,8 +1947,12 @@ int Cocoa_CreateWindow(_THIS, SDL_Window * window) #endif /* Note: as of the macOS 10.15 SDK, this defaults to YES instead of NO when * the NSHighResolutionCapable boolean is set in Info.plist. */ +#ifdef MAC_OS_X_VERSION_10_7 /* Retina support arrived with the 10.7 SDK */ highdpi = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0; [contentView setWantsBestResolutionOpenGLSurface:highdpi]; +#else + (void) highdpi; +#endif #ifdef __clang__ #pragma clang diagnostic pop #endif @@ -1853,10 +1966,13 @@ int Cocoa_CreateWindow(_THIS, SDL_Window * window) #endif /* SDL_VIDEO_OPENGL_EGL */ #endif /* SDL_VIDEO_OPENGL_ES2 */ [nswindow setContentView:contentView]; + SDL_COCOA_RELEASE(contentView); /* the window and window data hold references */ if (SetupWindowData(_this, window, nswindow, contentView, SDL_TRUE) < 0) { + SDL_COCOA_RELEASE(nswindow); return -1; } + SDL_COCOA_RELEASE(nswindow); /* the window data holds a reference */ if (!(window->flags & SDL_WINDOW_OPENGL)) { return 0; @@ -1880,7 +1996,7 @@ int Cocoa_CreateWindow(_THIS, SDL_Window * window) }} int Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSView* nsview = nil; NSWindow *nswindow = nil; @@ -1910,8 +2026,12 @@ int Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) #endif /* Note: as of the macOS 10.15 SDK, this defaults to YES instead of NO when * the NSHighResolutionCapable boolean is set in Info.plist. */ +#ifdef MAC_OS_X_VERSION_10_7 /* Retina support arrived with the 10.7 SDK */ highdpi = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0; [nsview setWantsBestResolutionOpenGLSurface:highdpi]; +#else + (void) highdpi; +#endif #ifdef __clang__ #pragma clang diagnostic pop #endif @@ -1920,16 +2040,16 @@ int Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) }} void Cocoa_SetWindowTitle(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { const char *title = window->title ? window->title : ""; NSWindow *nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; - NSString *string = [[NSString alloc] initWithUTF8String:title]; + NSString *string = SDL_COCOA_AUTORELEASE([[NSString alloc] initWithUTF8String:title]); [nswindow setTitle:string]; }} void Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSImage *nsimage = Cocoa_CreateImage(icon); @@ -1939,7 +2059,7 @@ void Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) }} void Cocoa_SetWindowPosition(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; NSWindow *nswindow = windata.nswindow; @@ -1961,7 +2081,7 @@ void Cocoa_SetWindowPosition(_THIS, SDL_Window * window) }} void Cocoa_SetWindowSize(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; NSWindow *nswindow = windata.nswindow; @@ -1987,7 +2107,7 @@ void Cocoa_SetWindowSize(_THIS, SDL_Window * window) }} void Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; @@ -1999,7 +2119,7 @@ void Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window) }} void Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; @@ -2011,15 +2131,17 @@ void Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window) }} void Cocoa_GetWindowSizeInPixels(_THIS, SDL_Window * window, int *w, int *h) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; NSView *contentView = windata.sdlContentView; NSRect viewport = [contentView bounds]; if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { +#ifdef MAC_OS_X_VERSION_10_7 /* Retina support arrived with the 10.7 SDK */ /* This gives us the correct viewport for a Retina-enabled view. */ viewport = [contentView convertRectToBacking:viewport]; +#endif } *w = viewport.size.width; @@ -2028,7 +2150,7 @@ void Cocoa_GetWindowSizeInPixels(_THIS, SDL_Window * window, int *w, int *h) void Cocoa_ShowWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *windowData = ((__bridge SDL_WindowData *) window->driverdata); NSWindow *nswindow = windowData.nswindow; @@ -2041,7 +2163,7 @@ void Cocoa_ShowWindow(_THIS, SDL_Window * window) }} void Cocoa_HideWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSWindow *nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; @@ -2049,7 +2171,7 @@ void Cocoa_HideWindow(_THIS, SDL_Window * window) }} void Cocoa_RaiseWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *windowData = ((__bridge SDL_WindowData *) window->driverdata); NSWindow *nswindow = windowData.nswindow; @@ -2066,7 +2188,7 @@ void Cocoa_RaiseWindow(_THIS, SDL_Window * window) }} void Cocoa_MaximizeWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; NSWindow *nswindow = windata.nswindow; @@ -2077,7 +2199,7 @@ void Cocoa_MaximizeWindow(_THIS, SDL_Window * window) }} void Cocoa_MinimizeWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; NSWindow *nswindow = data.nswindow; @@ -2089,7 +2211,7 @@ void Cocoa_MinimizeWindow(_THIS, SDL_Window * window) }} void Cocoa_RestoreWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSWindow *nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; @@ -2101,7 +2223,7 @@ void Cocoa_RestoreWindow(_THIS, SDL_Window * window) }} void Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) -{ @autoreleasepool +{ SDL_COCOA_POOL { if (SetWindowStyle(window, GetWindowStyle(window))) { if (bordered) { @@ -2111,7 +2233,7 @@ void Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) }} void Cocoa_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable) -{ @autoreleasepool +{ SDL_COCOA_POOL { /* Don't set this if we're in a space! * The window will get permanently stuck if resizable is false. @@ -2135,7 +2257,7 @@ void Cocoa_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable) }} void Cocoa_SetWindowAlwaysOnTop(_THIS, SDL_Window * window, SDL_bool on_top) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSWindow *nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; if (on_top) { @@ -2146,7 +2268,7 @@ void Cocoa_SetWindowAlwaysOnTop(_THIS, SDL_Window * window, SDL_bool on_top) }} void Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; NSWindow *nswindow = data.nswindow; @@ -2177,7 +2299,13 @@ void Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * di } } +#ifdef MAC_OS_X_VERSION_10_6 [nswindow setStyleMask:NSWindowStyleMaskBorderless]; +#else + if ([nswindow respondsToSelector:@selector(setStyleMask:)]) { + [nswindow performSelector:@selector(setStyleMask:) withObject:(id)(uintptr_t)NSWindowStyleMaskBorderless]; + } +#endif } else { NSRect frameRect; rect.origin.x = window->windowed.x; @@ -2192,7 +2320,13 @@ void Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * di * macOS 10.15 where the window doesn't properly restore the windowed * mode decorations after exiting fullscreen-desktop, when the window * was created as fullscreen-desktop. */ +#ifdef MAC_OS_X_VERSION_10_6 [nswindow setStyleMask:GetWindowWindowedStyle(window)]; +#else + if ([nswindow respondsToSelector:@selector(setStyleMask:)]) { + [nswindow performSelector:@selector(setStyleMask:) withObject:(id)(uintptr_t)GetWindowWindowedStyle(window)]; + } +#endif /* Hack to restore window decorations on Mac OS X 10.10 */ frameRect = [nswindow frame]; @@ -2234,7 +2368,7 @@ void Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * di }} int Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display; @@ -2260,8 +2394,12 @@ int Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) }} void *Cocoa_GetWindowICCProfile(_THIS, SDL_Window * window, size_t * size) -{ @autoreleasepool +{ SDL_COCOA_POOL { +#ifndef MAC_OS_X_VERSION_10_6 /* -[NSScreen colorSpace] arrived with the 10.6 SDK */ + SDL_Unsupported(); + return NULL; +#else SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; NSWindow *nswindow = data.nswindow; NSScreen *screen = [nswindow screen]; @@ -2293,10 +2431,11 @@ void *Cocoa_GetWindowICCProfile(_THIS, SDL_Window * window, size_t * size) [iccProfileData getBytes:retIccProfileData length:[iccProfileData length]]; *size = [iccProfileData length]; return retIccProfileData; +#endif /* MAC_OS_X_VERSION_10_6 */ }} int Cocoa_GetWindowDisplayIndex(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSScreen *screen; SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; @@ -2362,7 +2501,7 @@ void Cocoa_SetWindowMouseRect(_THIS, SDL_Window * window) } void Cocoa_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; @@ -2383,9 +2522,9 @@ void Cocoa_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed) }} void Cocoa_DestroyWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { - SDL_WindowData *data = (SDL_WindowData *) CFBridgingRelease(window->driverdata); + SDL_WindowData *data = (SDL_WindowData *) SDL_CFBridgingRelease(window->driverdata); if (data) { #ifdef SDL_VIDEO_OPENGL @@ -2397,22 +2536,26 @@ void Cocoa_DestroyWindow(_THIS, SDL_Window * window) } [data.listener close]; data.listener = nil; - if (data.created) { - /* Release the content view to avoid further updateLayer callbacks */ - [data.nswindow setContentView:nil]; - [data.nswindow close]; - } #ifdef SDL_VIDEO_OPENGL - contexts = [data.nscontexts copy]; + /* Detach any GL contexts before tearing down the window: closing it + (or clearing its content view) with a context still attached makes + old OS X log "invalid drawable". */ + contexts = SDL_COCOA_AUTORELEASE([data.nscontexts copy]); for (SDLOpenGLContext *context in contexts) { /* Calling setWindow:NULL causes the context to remove itself from the context list. */ [context setWindow:NULL]; } #endif /* SDL_VIDEO_OPENGL */ + if (data.created) { + /* Release the content view to avoid further updateLayer callbacks */ + [data.nswindow setContentView:nil]; + [data.nswindow close]; + } + if (window->shaper) { - CFBridgingRelease(window->shaper->driverdata); + (void) SDL_CFBridgingRelease(window->shaper->driverdata); SDL_free(window->shaper); window->shaper = NULL; } @@ -2421,7 +2564,7 @@ void Cocoa_DestroyWindow(_THIS, SDL_Window * window) }} SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) -{ @autoreleasepool +{ SDL_COCOA_POOL { NSWindow *nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; @@ -2437,7 +2580,7 @@ SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) }} SDL_bool Cocoa_IsWindowInFullscreenSpace(SDL_Window * window) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; @@ -2449,7 +2592,7 @@ SDL_bool Cocoa_IsWindowInFullscreenSpace(SDL_Window * window) }} SDL_bool Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_bool succeeded = SDL_FALSE; SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; @@ -2496,18 +2639,23 @@ int Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled) } void Cocoa_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; if (accept) { +#ifdef MAC_OS_X_VERSION_10_6 [data.nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]]; +#else + /* pre-UTI drag types; performDragOperation reads NSFilenamesPboardType either way */ + [data.nswindow registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; +#endif } else { [data.nswindow unregisterDraggedTypes]; } }} int Cocoa_FlashWindow(_THIS, SDL_Window *window, SDL_FlashOperation operation) -{ @autoreleasepool +{ SDL_COCOA_POOL { /* Note that this is app-wide and not window-specific! */ SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; @@ -2534,7 +2682,7 @@ int Cocoa_FlashWindow(_THIS, SDL_Window *window, SDL_FlashOperation operation) }} int Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity) -{ @autoreleasepool +{ SDL_COCOA_POOL { SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; [data.nswindow setAlphaValue:opacity];