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 <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"
+
+#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 <NSApplicationDelegate> {
+#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 <IOKit/pwr_mgt/IOPMLib.h>
 #include <Cocoa/Cocoa.h>
 
+#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 <Cocoa/Cocoa.h>
 
+#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 <NSWindowDelegate> {
+#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 <NSDraggingDestination>
+#else
+@interface SDLWindow : NSWindow
+#endif
 /* These are needed for borderless/fullscreen windows */
 - (BOOL)canBecomeKeyWindow;
 - (BOOL)canBecomeMainWindow;
@@ -158,7 +207,7 @@
 }
 
 - (BOOL)performDragOperation:(id <NSDraggingInfo>)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];
