From c694c5a5bc69eeddf10faad5a8f848aa8dcdb1a7 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 28 May 2025 03:52:52 +0800 Subject: [PATCH] Cocoa fixes --- src/video/cocoa/SDL_cocoaclipboard.h | 12 +- src/video/cocoa/SDL_cocoaclipboard.m | 82 +- src/video/cocoa/SDL_cocoaevents.h | 11 +- src/video/cocoa/SDL_cocoaevents.m | 509 ++---- src/video/cocoa/SDL_cocoakeyboard.h | 12 +- src/video/cocoa/SDL_cocoakeyboard.m | 532 ++++--- src/video/cocoa/SDL_cocoamessagebox.h | 4 +- src/video/cocoa/SDL_cocoamessagebox.m | 117 +- src/video/cocoa/SDL_cocoamodes.h | 12 +- src/video/cocoa/SDL_cocoamodes.m | 605 +++----- src/video/cocoa/SDL_cocoamouse.h | 19 +- src/video/cocoa/SDL_cocoamouse.m | 455 ++---- src/video/cocoa/SDL_cocoamousetap.h | 33 + src/video/cocoa/SDL_cocoamousetap.m | 259 ++++ src/video/cocoa/SDL_cocoaopengl.h | 37 +- src/video/cocoa/SDL_cocoaopengl.m | 397 ++--- src/video/cocoa/SDL_cocoashape.h | 21 +- src/video/cocoa/SDL_cocoashape.m | 122 +- src/video/cocoa/SDL_cocoavideo.h | 94 +- src/video/cocoa/SDL_cocoavideo.m | 163 +- src/video/cocoa/SDL_cocoawindow.h | 93 +- src/video/cocoa/SDL_cocoawindow.m | 2047 +++++++------------------ 22 files changed, 2116 insertions(+), 3520 deletions(-) create mode 100644 src/video/cocoa/SDL_cocoamousetap.h create mode 100644 src/video/cocoa/SDL_cocoamousetap.m diff --git src/video/cocoa/SDL_cocoaclipboard.h src/video/cocoa/SDL_cocoaclipboard.h index b3b3d8162..0a5e0dc0d 100644 --- src/video/cocoa/SDL_cocoaclipboard.h +++ src/video/cocoa/SDL_cocoaclipboard.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,17 +20,17 @@ */ #include "../../SDL_internal.h" -#ifndef SDL_cocoaclipboard_h_ -#define SDL_cocoaclipboard_h_ +#ifndef _SDL_cocoaclipboard_h +#define _SDL_cocoaclipboard_h /* Forward declaration */ -@class SDL_VideoData; +struct SDL_VideoData; extern int Cocoa_SetClipboardText(_THIS, const char *text); extern char *Cocoa_GetClipboardText(_THIS); extern SDL_bool Cocoa_HasClipboardText(_THIS); -extern void Cocoa_CheckClipboardUpdate(SDL_VideoData * data); +extern void Cocoa_CheckClipboardUpdate(struct SDL_VideoData * data); -#endif /* SDL_cocoaclipboard_h_ */ +#endif /* _SDL_cocoaclipboard_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoaclipboard.m src/video/cocoa/SDL_cocoaclipboard.m index 5b32bbca0..0e8b97776 100644 --- src/video/cocoa/SDL_cocoaclipboard.m +++ src/video/cocoa/SDL_cocoaclipboard.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,39 +20,59 @@ */ #include "../../SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_COCOA +#if SDL_VIDEO_DRIVER_COCOA #include "SDL_cocoavideo.h" #include "../../events/SDL_clipboardevents_c.h" -int Cocoa_SetClipboardText(_THIS, const char *text) -{ @autoreleasepool +static NSString * +GetTextFormat(_THIS) { - SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; - NSPasteboard *pasteboard; - NSString *format = NSPasteboardTypeString; - NSString *nsstr = [NSString stringWithUTF8String:text]; - if (nsstr == nil) { - return SDL_SetError("Couldn't create NSString; is your string data in UTF-8 format?"); + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + if (data->osversion >= 0x1060) { + return NSPasteboardTypeString; + } else { +#endif + return NSStringPboardType; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 } +#endif +} + +int +Cocoa_SetClipboardText(_THIS, const char *text) +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + NSAutoreleasePool *pool; + NSPasteboard *pasteboard; + NSString *format = GetTextFormat(_this); + + pool = [[NSAutoreleasePool alloc] init]; pasteboard = [NSPasteboard generalPasteboard]; - data.clipboard_count = [pasteboard declareTypes:[NSArray arrayWithObject:format] owner:nil]; - [pasteboard setString:nsstr forType:format]; + data->clipboard_count = [pasteboard declareTypes:[NSArray arrayWithObject:format] owner:nil]; + [pasteboard setString:[NSString stringWithUTF8String:text] forType:format]; + + [pool release]; return 0; -}} +} -char *Cocoa_GetClipboardText(_THIS) -{ @autoreleasepool +char * +Cocoa_GetClipboardText(_THIS) { + NSAutoreleasePool *pool; NSPasteboard *pasteboard; - NSString *format = NSPasteboardTypeString; + NSString *format = GetTextFormat(_this); NSString *available; char *text; + pool = [[NSAutoreleasePool alloc] init]; + pasteboard = [NSPasteboard generalPasteboard]; - available = [pasteboard availableTypeFromArray:[NSArray arrayWithObject:format]]; + available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:format]]; if ([available isEqualToString:format]) { NSString* string; const char *utf8; @@ -63,40 +83,48 @@ char *Cocoa_GetClipboardText(_THIS) } else { utf8 = [string UTF8String]; } - text = SDL_strdup(utf8 ? utf8 : ""); + text = SDL_strdup(utf8); } else { text = SDL_strdup(""); } + [pool release]; + return text; -}} +} -SDL_bool Cocoa_HasClipboardText(_THIS) +SDL_bool +Cocoa_HasClipboardText(_THIS) { SDL_bool result = SDL_FALSE; char *text = Cocoa_GetClipboardText(_this); if (text) { result = text[0] != '\0' ? SDL_TRUE : SDL_FALSE; - SDL_free(text); + SDL_free(text); } return result; } -void Cocoa_CheckClipboardUpdate(SDL_VideoData * data) -{ @autoreleasepool +void +Cocoa_CheckClipboardUpdate(struct SDL_VideoData * data) { + NSAutoreleasePool *pool; NSPasteboard *pasteboard; NSInteger count; + pool = [[NSAutoreleasePool alloc] init]; + pasteboard = [NSPasteboard generalPasteboard]; count = [pasteboard changeCount]; - if (count != data.clipboard_count) { - if (data.clipboard_count) { + if (count != data->clipboard_count) { + if (data->clipboard_count) { SDL_SendClipboardUpdate(); } - data.clipboard_count = count; + data->clipboard_count = count; } -}} + + [pool release]; +} #endif /* SDL_VIDEO_DRIVER_COCOA */ diff --git src/video/cocoa/SDL_cocoaevents.h src/video/cocoa/SDL_cocoaevents.h index e99089ab1..687cf647e 100644 --- src/video/cocoa/SDL_cocoaevents.h +++ src/video/cocoa/SDL_cocoaevents.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,15 +20,12 @@ */ #include "../../SDL_internal.h" -#ifndef SDL_cocoaevents_h_ -#define SDL_cocoaevents_h_ +#ifndef _SDL_cocoaevents_h +#define _SDL_cocoaevents_h extern void Cocoa_RegisterApp(void); extern void Cocoa_PumpEvents(_THIS); -extern int Cocoa_WaitEventTimeout(_THIS, int timeout); -extern void Cocoa_SendWakeupEvent(_THIS, SDL_Window *window); -extern void Cocoa_SuspendScreenSaver(_THIS); -#endif /* SDL_cocoaevents_h_ */ +#endif /* _SDL_cocoaevents_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoaevents.m src/video/cocoa/SDL_cocoaevents.m index 25f8aa91b..cc1f11224 100644 --- src/video/cocoa/SDL_cocoaevents.m +++ src/video/cocoa/SDL_cocoaevents.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,47 +20,25 @@ */ #include "../../SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_COCOA - +#if SDL_VIDEO_DRIVER_COCOA #include "SDL_timer.h" #include "SDL_cocoavideo.h" #include "../../events/SDL_events_c.h" -#include "SDL_hints.h" -/* This define was added in the 10.9 SDK. */ -#ifndef kIOPMAssertPreventUserIdleDisplaySleep -#define kIOPMAssertPreventUserIdleDisplaySleep kIOPMAssertionTypePreventUserIdleDisplaySleep -#endif -#ifndef NSAppKitVersionNumber10_8 -#define NSAppKitVersionNumber10_8 1187 -#endif -#ifndef MAC_OS_X_VERSION_10_12 -#define NSEventTypeApplicationDefined NSApplicationDefined +#if !defined(UsrActivity) && defined(__LP64__) && !defined(__POWER__) +/* + * Workaround for a bug in the 10.5 SDK: By accident, OSService.h does + * not include Power.h at all when compiling in 64bit mode. This has + * been fixed in 10.6, but for 10.5, we manually define UsrActivity + * to ensure compilation works. + */ +#define UsrActivity 1 #endif -static SDL_Window *FindSDLWindowForNSWindow(NSWindow *win) -{ - SDL_Window *sdlwindow = NULL; - SDL_VideoDevice *device = SDL_GetVideoDevice(); - if (device && device->windows) { - for (sdlwindow = device->windows; sdlwindow; sdlwindow = sdlwindow->next) { - NSWindow *nswindow = ((__bridge SDL_WindowData *) sdlwindow->driverdata).nswindow; - if (win == nswindow) { - return sdlwindow; - } - } - } - - return sdlwindow; -} - @interface SDLApplication : NSApplication - (void)terminate:(id)sender; -- (void)sendEvent:(NSEvent *)theEvent; - -+ (void)registerUserDefaults; @end @@ -72,58 +50,6 @@ static SDL_Window *FindSDLWindowForNSWindow(NSWindow *win) SDL_SendQuit(); } -static SDL_bool s_bShouldHandleEventsInSDLApplication = SDL_FALSE; - -static void Cocoa_DispatchEvent(NSEvent *theEvent) -{ - SDL_VideoDevice *_this = SDL_GetVideoDevice(); - - switch ([theEvent type]) { - case NSEventTypeLeftMouseDown: - case NSEventTypeOtherMouseDown: - case NSEventTypeRightMouseDown: - case NSEventTypeLeftMouseUp: - case NSEventTypeOtherMouseUp: - case NSEventTypeRightMouseUp: - case NSEventTypeLeftMouseDragged: - case NSEventTypeRightMouseDragged: - case NSEventTypeOtherMouseDragged: /* usually middle mouse dragged */ - case NSEventTypeMouseMoved: - case NSEventTypeScrollWheel: - Cocoa_HandleMouseEvent(_this, theEvent); - break; - case NSEventTypeKeyDown: - case NSEventTypeKeyUp: - case NSEventTypeFlagsChanged: - Cocoa_HandleKeyEvent(_this, theEvent); - break; - default: - break; - } -} - -// Dispatch events here so that we can handle events caught by -// nextEventMatchingMask in SDL, as well as events caught by other -// processes (such as CEF) that are passed down to NSApp. -- (void)sendEvent:(NSEvent *)theEvent -{ - if (s_bShouldHandleEventsInSDLApplication) { - Cocoa_DispatchEvent(theEvent); - } - - [super sendEvent:theEvent]; -} - -+ (void)registerUserDefaults -{ - NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys: - [NSNumber numberWithBool:NO], @"AppleMomentumScrollSupported", - [NSNumber numberWithBool:NO], @"ApplePressAndHoldEnabled", - [NSNumber numberWithBool:YES], @"ApplePersistenceIgnoreState", - nil]; - [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; -} - @end // SDLApplication /* setAppleMenu disappeared from the headers in 10.4 */ @@ -131,39 +57,25 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) - (void)setAppleMenu:(NSMenu *)menu; @end -@interface SDLAppDelegate : NSObject { +@interface SDLAppDelegate : NSObject { @public BOOL seenFirstActivate; } - (id)init; -- (void)localeDidChange:(NSNotification *)notification; -- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app; @end @implementation SDLAppDelegate : NSObject - (id)init { self = [super init]; - if (self) { - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + if (self) { seenFirstActivate = NO; - - [center addObserver:self - selector:@selector(windowWillClose:) - name:NSWindowWillCloseNotification - object:nil]; - - [center addObserver:self - selector:@selector(focusSomeWindow:) - name:NSApplicationDidBecomeActiveNotification - object:nil]; - - [center addObserver:self - selector:@selector(localeDidChange:) - name:NSCurrentLocaleDidChangeNotification - object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(focusSomeWindow:) + name:NSApplicationDidBecomeActiveNotification + object:nil]; } return self; @@ -171,92 +83,32 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) - (void)dealloc { - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - - [center removeObserver:self name:NSWindowWillCloseNotification object:nil]; - [center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil]; - [center removeObserver:self name:NSCurrentLocaleDidChangeNotification object:nil]; - - /* Remove our URL event handler only if we set it */ - if ([NSApp delegate] == self) { - [[NSAppleEventManager sharedAppleEventManager] - removeEventHandlerForEventClass:kInternetEventClass - andEventID:kAEGetURL]; - } -} - -- (void)windowWillClose:(NSNotification *)notification; -{ - NSWindow *win = (NSWindow*)[notification object]; - - if (![win isKeyWindow]) { - return; - } - - /* Don't do anything if this was not an SDL window that was closed */ - if (FindSDLWindowForNSWindow(win) == NULL) { - return; - } - - /* HACK: Make the next window in the z-order key when the key window is - * closed. The custom event loop and/or windowing code we have seems to - * prevent the normal behavior: https://bugzilla.libsdl.org/show_bug.cgi?id=1825 - */ - - /* +[NSApp orderedWindows] never includes the 'About' window, but we still - * want to try its list first since the behavior in other apps is to only - * make the 'About' window key if no other windows are on-screen. - */ - for (NSWindow *window in [NSApp orderedWindows]) { - if (window != win && [window canBecomeKeyWindow]) { - if (![window isOnActiveSpace]) { - continue; - } - [window makeKeyAndOrderFront:self]; - return; - } - } - - /* 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. - */ - for (NSNumber *num in [NSWindow windowNumbersWithOptions:0]) { - NSWindow *window = [NSApp windowWithWindowNumber:[num integerValue]]; - if (window && window != win && [window canBecomeKeyWindow]) { - [window makeKeyAndOrderFront:self]; - return; - } - } + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; } - (void)focusSomeWindow:(NSNotification *)aNotification { - SDL_VideoDevice *device; /* HACK: Ignore the first call. The application gets a * applicationDidBecomeActive: a little bit after the first window is * created, and if we don't ignore it, a window that has been created with - * SDL_WINDOW_MINIMIZED will ~immediately be restored. + * SDL_WINDOW_MINIZED will ~immediately be restored. */ if (!seenFirstActivate) { seenFirstActivate = YES; return; } - /* Don't do anything if the application already has a key window - * that is not an SDL window. - */ - if ([NSApp keyWindow] && FindSDLWindowForNSWindow([NSApp keyWindow]) == NULL) { - return; - } - - device = SDL_GetVideoDevice(); - if (device && device->windows) { + SDL_VideoDevice *device = SDL_GetVideoDevice(); + if (device && device->windows) + { SDL_Window *window = device->windows; int i; - for (i = 0; i < device->num_displays; ++i) { + for (i = 0; i < device->num_displays; ++i) + { SDL_Window *fullscreen_window = device->displays[i].fullscreen_window; - if (fullscreen_window) { + if (fullscreen_window) + { if (fullscreen_window->flags & SDL_WINDOW_MINIMIZED) { SDL_RestoreWindow(fullscreen_window); } @@ -272,125 +124,48 @@ static void Cocoa_DispatchEvent(NSEvent *theEvent) } } -- (void)localeDidChange:(NSNotification *)notification; -{ - SDL_SendLocaleChangedEvent(); -} - - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { - return (BOOL)SDL_SendDropFile(NULL, [filename UTF8String]) && SDL_SendDropComplete(NULL); -} - -- (void)applicationDidFinishLaunching:(NSNotification *)notification -{ - /* The menu bar of SDL apps which don't have the typical .app bundle - * structure fails to work the first time a window is created (until it's - * de-focused and re-focused), if this call is in Cocoa_RegisterApp instead - * of here. https://bugzilla.libsdl.org/show_bug.cgi?id=3051 - */ - if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, SDL_FALSE)) { - /* 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. */ - [NSApp activateIgnoringOtherApps:YES]; - } - - /* If we call this before NSApp activation, macOS might print a complaint - * about ApplePersistenceIgnoreState. */ - [SDLApplication registerUserDefaults]; -} - -- (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent -{ - NSString* path = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; - SDL_SendDropFile(NULL, [path UTF8String]); - SDL_SendDropComplete(NULL); + //return (BOOL)SDL_SendDropFile([filename UTF8String]); + return false; // LAULAND } - -- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app -{ - // This just tells Cocoa that we didn't do any custom save state magic for the app, - // so the system is safe to use NSSecureCoding internally, instead of using unencrypted - // save states for backwards compatibility. If we don't return YES here, we'll get a - // warning on the console at startup: - // - // ``` - // WARNING: Secure coding is not enabled for restorable state! Enable secure coding by implementing NSApplicationDelegate.applicationSupportsSecureRestorableState: and returning YES. - // ``` - // - // More-detailed explanation: - // https://stackoverflow.com/questions/77283578/sonoma-and-nsapplicationdelegate-applicationsupportssecurerestorablestate/77320845#77320845 - return YES; -} - @end static SDLAppDelegate *appDelegate = nil; -static NSString *GetApplicationName(void) +static NSString * +GetApplicationName(void) { NSString *appName; /* Determine the application name */ appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]; - if (!appName) { + if (!appName) appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; - } - if (![appName length]) { + if (![appName length]) appName = [[NSProcessInfo processInfo] processName]; - } return appName; } -static bool LoadMainMenuNibIfAvailable(void) -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 - NSDictionary *infoDict; - NSString *mainNibFileName; - bool success = false; - - if (floor(NSAppKitVersionNumber) < NSAppKitVersionNumber10_8) { - return false; - } - infoDict = [[NSBundle mainBundle] infoDictionary]; - if (infoDict) { - mainNibFileName = [infoDict valueForKey:@"NSMainNibFile"]; - - if (mainNibFileName) { - success = [[NSBundle mainBundle] loadNibNamed:mainNibFileName owner:[NSApplication sharedApplication] topLevelObjects:nil]; - } - } - - return success; -#else - return false; -#endif -} - -static void CreateApplicationMenus(void) +static void +CreateApplicationMenus(void) { NSString *appName; NSString *title; NSMenu *appleMenu; NSMenu *serviceMenu; NSMenu *windowMenu; + NSMenu *viewMenu; NSMenuItem *menuItem; - NSMenu *mainMenu; if (NSApp == nil) { return; } - - mainMenu = [[NSMenu alloc] init]; - + /* Create the main menu bar */ - [NSApp setMainMenu:mainMenu]; + [NSApp setMainMenu:[[NSMenu alloc] init]]; /* Create the application menu */ appName = GetApplicationName(); @@ -407,10 +182,11 @@ static void CreateApplicationMenus(void) [appleMenu addItem:[NSMenuItem separatorItem]]; serviceMenu = [[NSMenu alloc] initWithTitle:@""]; - menuItem = [appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""]; + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""]; [menuItem setSubmenu:serviceMenu]; [NSApp setServicesMenu:serviceMenu]; + [serviceMenu release]; [appleMenu addItem:[NSMenuItem separatorItem]]; @@ -418,7 +194,7 @@ static void CreateApplicationMenus(void) [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; - [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; @@ -431,71 +207,77 @@ static void CreateApplicationMenus(void) menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; [menuItem setSubmenu:appleMenu]; [[NSApp mainMenu] addItem:menuItem]; + [menuItem release]; /* Tell the application object that this is now the application menu */ [NSApp setAppleMenu:appleMenu]; + [appleMenu release]; + /* Create the window menu */ windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; /* Add menu items */ - [windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"]; - [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; - /* Add the fullscreen toggle menu option. */ - /* Cocoa should update the title to Enter or Exit Full Screen automatically. - * But if not, then just fallback to Toggle Full Screen. - */ - menuItem = [[NSMenuItem alloc] initWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; - [menuItem setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; - [windowMenu addItem:menuItem]; - /* Put menu into the menubar */ menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; [menuItem setSubmenu:windowMenu]; [[NSApp mainMenu] addItem:menuItem]; + [menuItem release]; /* Tell the application object that this is now the window menu */ [NSApp setWindowsMenu:windowMenu]; + [windowMenu release]; + + + /* Add the fullscreen view toggle menu option, if supported */ + if ([NSApp respondsToSelector:@selector(setPresentationOptions:)]) { + /* Create the view menu */ + viewMenu = [[NSMenu alloc] initWithTitle:@"View"]; + + /* Add menu items */ + menuItem = [viewMenu addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; + [menuItem setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask]; + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:viewMenu]; + [[NSApp mainMenu] addItem:menuItem]; + [menuItem release]; + + [viewMenu release]; + } } -void Cocoa_RegisterApp(void) -{ @autoreleasepool +void +Cocoa_RegisterApp(void) { /* This can get called more than once! Be careful what you initialize! */ + ProcessSerialNumber psn; + NSAutoreleasePool *pool; + + if (!GetCurrentProcess(&psn)) { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + } + pool = [[NSAutoreleasePool alloc] init]; if (NSApp == nil) { [SDLApplication sharedApplication]; - SDL_assert(NSApp != nil); - - s_bShouldHandleEventsInSDLApplication = SDL_TRUE; - if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, SDL_FALSE)) { - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - } - - /* If there aren't already menus in place, look to see if there's - * a nib we should use. If not, then manually create the basic - * menus we meed. - */ if ([NSApp mainMenu] == nil) { - bool nibLoaded; - - nibLoaded = LoadMainMenuNibIfAvailable(); - if (!nibLoaded) { - CreateApplicationMenus(); - } + CreateApplicationMenus(); } [NSApp finishLaunching]; - if ([NSApp delegate]) { - /* The SDL app delegate calls this in didFinishLaunching if it's - * attached to the NSApp, otherwise we need to call it manually. - */ - [SDLApplication registerUserDefaults]; - } + NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithBool:NO], @"AppleMomentumScrollSupported", + [NSNumber numberWithBool:NO], @"ApplePressAndHoldEnabled", + nil]; + [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; + } if (NSApp && !appDelegate) { appDelegate = [[SDLAppDelegate alloc] init]; @@ -504,105 +286,64 @@ void Cocoa_RegisterApp(void) * termination into SDL_Quit, and we can't handle application:openFile: */ if (![NSApp delegate]) { - /* Only register the URL event handler if we are being set as the - * app delegate to avoid replacing any existing event handler. - */ - [[NSAppleEventManager sharedAppleEventManager] - setEventHandler:appDelegate - andSelector:@selector(handleURLEvent:withReplyEvent:) - forEventClass:kInternetEventClass - andEventID:kAEGetURL]; - - [(NSApplication *)NSApp setDelegate:appDelegate]; + [NSApp setDelegate:appDelegate]; } else { appDelegate->seenFirstActivate = YES; } } -}} + [pool release]; +} -int Cocoa_PumpEventsUntilDate(_THIS, NSDate *expiration, bool accumulate) +void +Cocoa_PumpEvents(_THIS) { - for ( ; ; ) { - NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:expiration inMode:NSDefaultRunLoopMode dequeue:YES ]; - if ( event == nil ) { - return 0; - } + NSAutoreleasePool *pool; - if (!s_bShouldHandleEventsInSDLApplication) { - Cocoa_DispatchEvent(event); + /* Update activity every 30 seconds to prevent screensaver */ + if (_this->suspend_screensaver) { + SDL_VideoData *data = (SDL_VideoData *)_this->driverdata; + Uint32 now = SDL_GetTicks(); + if (!data->screensaver_activity || + SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) { + UpdateSystemActivity(UsrActivity); + data->screensaver_activity = now; } + } - // Pass events down to SDLApplication to be handled in sendEvent: - [NSApp sendEvent:event]; - if ( !accumulate) { + pool = [[NSAutoreleasePool alloc] init]; + for ( ; ; ) { + NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES ]; + if ( event == nil ) { break; } - } - return 1; -} -int Cocoa_WaitEventTimeout(_THIS, int timeout) -{ @autoreleasepool -{ - if (timeout > 0) { - NSDate *limitDate = [NSDate dateWithTimeIntervalSinceNow: (double) timeout / 1000.0]; - return Cocoa_PumpEventsUntilDate(_this, limitDate, false); - } else if (timeout == 0) { - return Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], false); - } else { - while (Cocoa_PumpEventsUntilDate(_this, [NSDate distantFuture], false) == 0) { + switch ([event type]) { + case NSLeftMouseDown: + case NSOtherMouseDown: + case NSRightMouseDown: + case NSLeftMouseUp: + case NSOtherMouseUp: + case NSRightMouseUp: + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: /* usually middle mouse dragged */ + case NSMouseMoved: + case NSScrollWheel: + Cocoa_HandleMouseEvent(_this, event); + break; + case NSKeyDown: + case NSKeyUp: + case NSFlagsChanged: + Cocoa_HandleKeyEvent(_this, event); + break; + default: + break; } + /* Pass through to NSApp to make sure everything stays in sync */ + [NSApp sendEvent:event]; } - return 1; -}} - -void Cocoa_PumpEvents(_THIS) -{ @autoreleasepool -{ - Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], true); -}} - -void Cocoa_SendWakeupEvent(_THIS, SDL_Window *window) -{ @autoreleasepool -{ - NSEvent* event = [NSEvent otherEventWithType: NSEventTypeApplicationDefined - location: NSMakePoint(0,0) - modifierFlags: 0 - timestamp: 0.0 - windowNumber: ((__bridge SDL_WindowData *) window->driverdata).window_number - context: nil - subtype: 0 - data1: 0 - data2: 0]; - - [NSApp postEvent: event atStart: YES]; -}} - -void Cocoa_SuspendScreenSaver(_THIS) -{ @autoreleasepool -{ - SDL_VideoData *data = (__bridge SDL_VideoData *)_this->driverdata; - - if (data.screensaver_assertion) { - IOPMAssertionRelease(data.screensaver_assertion); - data.screensaver_assertion = kIOPMNullAssertionID; - } - - if (_this->suspend_screensaver) { - /* FIXME: this should ideally describe the real reason why the game - * called SDL_DisableScreenSaver. Note that the name is only meant to be - * seen by OS X power users. there's an additional optional human-readable - * (localized) reason parameter which we don't set. - */ - IOPMAssertionID assertion = kIOPMNullAssertionID; - NSString *name = [GetApplicationName() stringByAppendingString:@" using SDL_DisableScreenSaver"]; - IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep, - (__bridge CFStringRef) name, - NULL, NULL, NULL, 0, NULL, - &assertion); - data.screensaver_assertion = assertion; - } -}} + [pool release]; +} #endif /* SDL_VIDEO_DRIVER_COCOA */ diff --git src/video/cocoa/SDL_cocoakeyboard.h src/video/cocoa/SDL_cocoakeyboard.h index 62d6f0383..a85f9b7b0 100644 --- src/video/cocoa/SDL_cocoakeyboard.h +++ src/video/cocoa/SDL_cocoakeyboard.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,8 +20,8 @@ */ #include "../../SDL_internal.h" -#ifndef SDL_cocoakeyboard_h_ -#define SDL_cocoakeyboard_h_ +#ifndef _SDL_cocoakeyboard_h +#define _SDL_cocoakeyboard_h extern void Cocoa_InitKeyboard(_THIS); extern void Cocoa_HandleKeyEvent(_THIS, NSEvent * event); @@ -29,10 +29,8 @@ extern void Cocoa_QuitKeyboard(_THIS); extern void Cocoa_StartTextInput(_THIS); extern void Cocoa_StopTextInput(_THIS); -extern void Cocoa_SetTextInputRect(_THIS, const SDL_Rect *rect); +extern void Cocoa_SetTextInputRect(_THIS, SDL_Rect *rect); -extern void Cocoa_SetWindowKeyboardGrab(_THIS, SDL_Window * window, SDL_bool grabbed); - -#endif /* SDL_cocoakeyboard_h_ */ +#endif /* _SDL_cocoakeyboard_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoakeyboard.m src/video/cocoa/SDL_cocoakeyboard.m index 2a6763228..e973ef4cb 100644 --- src/video/cocoa/SDL_cocoakeyboard.m +++ src/video/cocoa/SDL_cocoakeyboard.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,11 +20,10 @@ */ #include "../../SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_COCOA +#if SDL_VIDEO_DRIVER_COCOA #include "SDL_cocoavideo.h" -#include "../../events/SDL_events_c.h" #include "../../events/SDL_keyboard_c.h" #include "../../events/scancodes_darwin.h" @@ -33,48 +32,66 @@ /*#define DEBUG_IME NSLog */ #define DEBUG_IME(...) -@interface SDLTranslatorResponder : NSView { +#ifndef NX_DEVICERCTLKEYMASK + #define NX_DEVICELCTLKEYMASK 0x00000001 +#endif +#ifndef NX_DEVICELSHIFTKEYMASK + #define NX_DEVICELSHIFTKEYMASK 0x00000002 +#endif +#ifndef NX_DEVICERSHIFTKEYMASK + #define NX_DEVICERSHIFTKEYMASK 0x00000004 +#endif +#ifndef NX_DEVICELCMDKEYMASK + #define NX_DEVICELCMDKEYMASK 0x00000008 +#endif +#ifndef NX_DEVICERCMDKEYMASK + #define NX_DEVICERCMDKEYMASK 0x00000010 +#endif +#ifndef NX_DEVICELALTKEYMASK + #define NX_DEVICELALTKEYMASK 0x00000020 +#endif +#ifndef NX_DEVICERALTKEYMASK + #define NX_DEVICERALTKEYMASK 0x00000040 +#endif +#ifndef NX_DEVICERCTLKEYMASK + #define NX_DEVICERCTLKEYMASK 0x00002000 +#endif + +@interface SDLTranslatorResponder : NSView +{ NSString *_markedText; NSRange _markedRange; NSRange _selectedRange; SDL_Rect _inputRect; } -- (void)doCommandBySelector:(SEL)myselector; -- (void)setInputRect:(const SDL_Rect *)rect; +- (void) doCommandBySelector:(SEL)myselector; +- (void) setInputRect:(SDL_Rect *) rect; @end @implementation SDLTranslatorResponder -- (void)setInputRect:(const SDL_Rect *)rect +- (void) setInputRect:(SDL_Rect *) rect { _inputRect = *rect; } -- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange +- (void) insertText:(id) aString { - /* TODO: Make use of replacementRange? */ - const char *str; DEBUG_IME(@"insertText: %@", aString); /* Could be NSString or NSAttributedString, so we have * to test and convert it before return as SDL event */ - if ([aString isKindOfClass: [NSAttributedString class]]) { + if ([aString isKindOfClass: [NSAttributedString class]]) str = [[aString string] UTF8String]; - } else { + else str = [aString UTF8String]; - } - - /* We're likely sending the composed text, so we reset the IME status. */ - if ([self hasMarkedText]) { - [self unmarkText]; - } SDL_SendKeyboardText(str); } -- (void)doCommandBySelector:(SEL)myselector +- (void) doCommandBySelector:(SEL) myselector { /* No need to do anything since we are not using Cocoa selectors to handle special keys, instead we use SDL @@ -82,81 +99,80 @@ */ } -- (BOOL)hasMarkedText +- (BOOL) hasMarkedText { return _markedText != nil; } -- (NSRange)markedRange +- (NSRange) markedRange { return _markedRange; } -- (NSRange)selectedRange +- (NSRange) selectedRange { return _selectedRange; } -- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange +- (void) setMarkedText:(id) aString + selectedRange:(NSRange) selRange { - if ([aString isKindOfClass:[NSAttributedString class]]) { + if ([aString isKindOfClass: [NSAttributedString class]]) aString = [aString string]; - } - if ([aString length] == 0) { + if ([aString length] == 0) + { [self unmarkText]; return; } - if (_markedText != aString) { - _markedText = aString; + if (_markedText != aString) + { + [_markedText release]; + _markedText = [aString retain]; } - _selectedRange = selectedRange; + _selectedRange = selRange; _markedRange = NSMakeRange(0, [aString length]); SDL_SendEditingText([aString UTF8String], - (int) selectedRange.location, (int) selectedRange.length); + selRange.location, selRange.length); DEBUG_IME(@"setMarkedText: %@, (%d, %d)", _markedText, - selectedRange.location, selectedRange.length); + selRange.location, selRange.length); } -- (void)unmarkText +- (void) unmarkText { + [_markedText release]; _markedText = nil; SDL_SendEditingText("", 0, 0); } -- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange +- (NSRect) firstRectForCharacterRange: (NSRange) theRange { NSWindow *window = [self window]; - NSRect contentRect = [window contentRectForFrameRect:[window frame]]; + NSRect contentRect = [window contentRectForFrameRect: [window frame]]; float windowHeight = contentRect.size.height; NSRect rect = NSMakeRect(_inputRect.x, windowHeight - _inputRect.y - _inputRect.h, _inputRect.w, _inputRect.h); - if (actualRange) { - *actualRange = aRange; - } - DEBUG_IME(@"firstRectForCharacterRange: (%d, %d): windowHeight = %g, rect = %@", - aRange.location, aRange.length, windowHeight, + theRange.location, theRange.length, windowHeight, NSStringFromRect(rect)); - - rect = [window convertRectToScreen:rect]; + rect.origin = [[self window] convertBaseToScreen: rect.origin]; return rect; } -- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange +- (NSAttributedString *) attributedSubstringFromRange: (NSRange) theRange { - DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", aRange.location, aRange.length); + DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", theRange.location, theRange.length); return nil; } -- (NSInteger)conversationIdentifier +- (NSInteger) conversationIdentifier { return (NSInteger) self; } @@ -164,7 +180,7 @@ /* This method returns the index for character that is * nearest to thePoint. thPoint is in screen coordinate system. */ -- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint +- (NSUInteger) characterIndexForPoint:(NSPoint) thePoint { DEBUG_IME(@"characterIndexForPoint: (%g, %g)", thePoint.x, thePoint.y); return 0; @@ -175,92 +191,263 @@ * NSInputServer examines the return value of this * method & constructs appropriate attributed string. */ -- (NSArray *)validAttributesForMarkedText +- (NSArray *) validAttributesForMarkedText { return [NSArray array]; } @end -static bool IsModifierKeyPressed(unsigned int flags, - unsigned int target_mask, - unsigned int other_mask, - unsigned int either_mask) +/* This is a helper function for HandleModifierSide. This + * function reverts back to behavior before the distinction between + * sides was made. + */ +static void +HandleNonDeviceModifier(unsigned int device_independent_mask, + unsigned int oldMods, + unsigned int newMods, + SDL_Scancode scancode) { - bool target_pressed = (flags & target_mask) != 0; - bool other_pressed = (flags & other_mask) != 0; - bool either_pressed = (flags & either_mask) != 0; + unsigned int oldMask, newMask; + + /* Isolate just the bits we care about in the depedent bits so we can + * figure out what changed + */ + oldMask = oldMods & device_independent_mask; + newMask = newMods & device_independent_mask; + + if (oldMask && oldMask != newMask) { + SDL_SendKeyboardKey(SDL_RELEASED, scancode); + } else if (newMask && oldMask != newMask) { + SDL_SendKeyboardKey(SDL_PRESSED, scancode); + } +} - if (either_pressed != (target_pressed || other_pressed)) - return either_pressed; +/* This is a helper function for HandleModifierSide. + * This function sets the actual SDL_PrivateKeyboard event. + */ +static void +HandleModifierOneSide(unsigned int oldMods, unsigned int newMods, + SDL_Scancode scancode, + unsigned int sided_device_dependent_mask) +{ + unsigned int old_dep_mask, new_dep_mask; + + /* Isolate just the bits we care about in the depedent bits so we can + * figure out what changed + */ + old_dep_mask = oldMods & sided_device_dependent_mask; + new_dep_mask = newMods & sided_device_dependent_mask; + + /* We now know that this side bit flipped. But we don't know if + * it went pressed to released or released to pressed, so we must + * find out which it is. + */ + if (new_dep_mask && old_dep_mask != new_dep_mask) { + SDL_SendKeyboardKey(SDL_PRESSED, scancode); + } else { + SDL_SendKeyboardKey(SDL_RELEASED, scancode); + } +} - return target_pressed; +/* This is a helper function for DoSidedModifiers. + * This function will figure out if the modifier key is the left or right side, + * e.g. left-shift vs right-shift. + */ +static void +HandleModifierSide(int device_independent_mask, + unsigned int oldMods, unsigned int newMods, + SDL_Scancode left_scancode, + SDL_Scancode right_scancode, + unsigned int left_device_dependent_mask, + unsigned int right_device_dependent_mask) +{ + unsigned int device_dependent_mask = (left_device_dependent_mask | + right_device_dependent_mask); + unsigned int diff_mod; + + /* On the basis that the device independent mask is set, but there are + * no device dependent flags set, we'll assume that we can't detect this + * keyboard and revert to the unsided behavior. + */ + if ((device_dependent_mask & newMods) == 0) { + /* Revert to the old behavior */ + HandleNonDeviceModifier(device_independent_mask, oldMods, newMods, left_scancode); + return; + } + + /* XOR the previous state against the new state to see if there's a change */ + diff_mod = (device_dependent_mask & oldMods) ^ + (device_dependent_mask & newMods); + if (diff_mod) { + /* A change in state was found. Isolate the left and right bits + * to handle them separately just in case the values can simulataneously + * change or if the bits don't both exist. + */ + if (left_device_dependent_mask & diff_mod) { + HandleModifierOneSide(oldMods, newMods, left_scancode, left_device_dependent_mask); + } + if (right_device_dependent_mask & diff_mod) { + HandleModifierOneSide(oldMods, newMods, right_scancode, right_device_dependent_mask); + } + } } -static void HandleModifiers(_THIS, SDL_Scancode code, unsigned int modifierFlags) +/* This is a helper function for DoSidedModifiers. + * This function will release a key press in the case that + * it is clear that the modifier has been released (i.e. one side + * can't still be down). + */ +static void +ReleaseModifierSide(unsigned int device_independent_mask, + unsigned int oldMods, unsigned int newMods, + SDL_Scancode left_scancode, + SDL_Scancode right_scancode, + unsigned int left_device_dependent_mask, + unsigned int right_device_dependent_mask) { - bool pressed = false; - - if (code == SDL_SCANCODE_LSHIFT) { - pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELSHIFTKEYMASK, - NX_DEVICERSHIFTKEYMASK, NX_SHIFTMASK); - } else if (code == SDL_SCANCODE_LCTRL) { - pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELCTLKEYMASK, - NX_DEVICERCTLKEYMASK, NX_CONTROLMASK); - } else if (code == SDL_SCANCODE_LALT) { - pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELALTKEYMASK, - NX_DEVICERALTKEYMASK, NX_ALTERNATEMASK); - } else if (code == SDL_SCANCODE_LGUI) { - pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICELCMDKEYMASK, - NX_DEVICERCMDKEYMASK, NX_COMMANDMASK); - } else if (code == SDL_SCANCODE_RSHIFT) { - pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERSHIFTKEYMASK, - NX_DEVICELSHIFTKEYMASK, NX_SHIFTMASK); - } else if (code == SDL_SCANCODE_RCTRL) { - pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERCTLKEYMASK, - NX_DEVICELCTLKEYMASK, NX_CONTROLMASK); - } else if (code == SDL_SCANCODE_RALT) { - pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERALTKEYMASK, - NX_DEVICELALTKEYMASK, NX_ALTERNATEMASK); - } else if (code == SDL_SCANCODE_RGUI) { - pressed = IsModifierKeyPressed(modifierFlags, NX_DEVICERCMDKEYMASK, - NX_DEVICELCMDKEYMASK, NX_COMMANDMASK); - } else { + unsigned int device_dependent_mask = (left_device_dependent_mask | + right_device_dependent_mask); + + /* On the basis that the device independent mask is set, but there are + * no device dependent flags set, we'll assume that we can't detect this + * keyboard and revert to the unsided behavior. + */ + if ((device_dependent_mask & oldMods) == 0) { + /* In this case, we can't detect the keyboard, so use the left side + * to represent both, and release it. + */ + SDL_SendKeyboardKey(SDL_RELEASED, left_scancode); return; } - if (pressed) { - SDL_SendKeyboardKey(SDL_PRESSED, code); - } else { - SDL_SendKeyboardKey(SDL_RELEASED, code); + /* + * This could have been done in an if-else case because at this point, + * we know that all keys have been released when calling this function. + * But I'm being paranoid so I want to handle each separately, + * so I hope this doesn't cause other problems. + */ + if ( left_device_dependent_mask & oldMods ) { + SDL_SendKeyboardKey(SDL_RELEASED, left_scancode); + } + if ( right_device_dependent_mask & oldMods ) { + SDL_SendKeyboardKey(SDL_RELEASED, right_scancode); + } +} + +/* This is a helper function for DoSidedModifiers. + * This function handles the CapsLock case. + */ +static void +HandleCapsLock(unsigned short scancode, + unsigned int oldMods, unsigned int newMods) +{ + unsigned int oldMask, newMask; + + oldMask = oldMods & NSAlphaShiftKeyMask; + newMask = newMods & NSAlphaShiftKeyMask; + + if (oldMask != newMask) { + SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_CAPSLOCK); + SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_CAPSLOCK); + } +} + +/* This function will handle the modifier keys and also determine the + * correct side of the key. + */ +static void +DoSidedModifiers(unsigned short scancode, + unsigned int oldMods, unsigned int newMods) +{ + /* Set up arrays for the key syms for the left and right side. */ + const SDL_Scancode left_mapping[] = { + SDL_SCANCODE_LSHIFT, + SDL_SCANCODE_LCTRL, + SDL_SCANCODE_LALT, + SDL_SCANCODE_LGUI + }; + const SDL_Scancode right_mapping[] = { + SDL_SCANCODE_RSHIFT, + SDL_SCANCODE_RCTRL, + SDL_SCANCODE_RALT, + SDL_SCANCODE_RGUI + }; + /* Set up arrays for the device dependent masks with indices that + * correspond to the _mapping arrays + */ + const unsigned int left_device_mapping[] = { NX_DEVICELSHIFTKEYMASK, NX_DEVICELCTLKEYMASK, NX_DEVICELALTKEYMASK, NX_DEVICELCMDKEYMASK }; + const unsigned int right_device_mapping[] = { NX_DEVICERSHIFTKEYMASK, NX_DEVICERCTLKEYMASK, NX_DEVICERALTKEYMASK, NX_DEVICERCMDKEYMASK }; + + unsigned int i, bit; + + /* Handle CAPSLOCK separately because it doesn't have a left/right side */ + HandleCapsLock(scancode, oldMods, newMods); + + /* Iterate through the bits, testing each against the old modifiers */ + for (i = 0, bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) { + unsigned int oldMask, newMask; + + oldMask = oldMods & bit; + newMask = newMods & bit; + + /* If the bit is set, we must always examine it because the left + * and right side keys may alternate or both may be pressed. + */ + if (newMask) { + HandleModifierSide(bit, oldMods, newMods, + left_mapping[i], right_mapping[i], + left_device_mapping[i], right_device_mapping[i]); + } + /* If the state changed from pressed to unpressed, we must examine + * the device dependent bits to release the correct keys. + */ + else if (oldMask && oldMask != newMask) { + ReleaseModifierSide(bit, oldMods, newMods, + left_mapping[i], right_mapping[i], + left_device_mapping[i], right_device_mapping[i]); + } } } -static void UpdateKeymap(SDL_VideoData *data, SDL_bool send_event) +static void +HandleModifiers(_THIS, unsigned short scancode, unsigned int modifierFlags) { + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + + if (modifierFlags == data->modifierFlags) { + return; + } + + DoSidedModifiers(scancode, data->modifierFlags, modifierFlags); + data->modifierFlags = modifierFlags; +} + +static void +UpdateKeymap(SDL_VideoData *data) +{ +#if defined(MAC_OS_X_VERSION_10_5) TISInputSourceRef key_layout; const void *chr_data; int i; SDL_Scancode scancode; SDL_Keycode keymap[SDL_NUM_SCANCODES]; - CFDataRef uchrDataRef; /* See if the keymap needs to be updated */ key_layout = TISCopyCurrentKeyboardLayoutInputSource(); - if (key_layout == data.key_layout) { + if (key_layout == data->key_layout) { return; } - data.key_layout = key_layout; + data->key_layout = key_layout; SDL_GetDefaultKeymap(keymap); /* Try Unicode data first */ - uchrDataRef = TISGetInputSourceProperty(key_layout, kTISPropertyUnicodeKeyLayoutData); - if (uchrDataRef) { + CFDataRef uchrDataRef = TISGetInputSourceProperty(key_layout, kTISPropertyUnicodeKeyLayoutData); + if (uchrDataRef) chr_data = CFDataGetBytePtr(uchrDataRef); - } else { + else goto cleanup; - } if (chr_data) { UInt32 keyboard_type = LMGetKbdType(); @@ -278,44 +465,34 @@ static void UpdateKeymap(SDL_VideoData *data, SDL_bool send_event) continue; } - /* - * Swap the scancode for these two wrongly translated keys - * UCKeyTranslate() function does not do its job properly for ISO layout keyboards, where the key '@', - * which is located in the top left corner of the keyboard right under the Escape key, and the additional - * key '<', which is on the right of the Shift key, are inverted - */ - if ((scancode == SDL_SCANCODE_NONUSBACKSLASH || scancode == SDL_SCANCODE_GRAVE) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) { - /* see comments in scancodes_darwin.h */ - scancode = (SDL_Scancode)((SDL_SCANCODE_NONUSBACKSLASH + SDL_SCANCODE_GRAVE) - scancode); - } - dead_key_state = 0; err = UCKeyTranslate ((UCKeyboardLayout *) chr_data, i, kUCKeyActionDown, 0, keyboard_type, kUCKeyTranslateNoDeadKeysMask, &dead_key_state, 8, &len, s); - if (err != noErr) { + if (err != noErr) continue; - } if (len > 0 && s[0] != 0x10) { keymap[scancode] = s[0]; } } - SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES, send_event); + SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES); return; } cleanup: CFRelease(key_layout); +#endif } -void Cocoa_InitKeyboard(_THIS) +void +Cocoa_InitKeyboard(_THIS) { - SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; - UpdateKeymap(data, SDL_FALSE); + UpdateKeymap(data); /* Set our own names for the platform-dependent but layout-independent keys */ /* This key is NumLock on the MacBook keyboard. :) */ @@ -324,107 +501,106 @@ void Cocoa_InitKeyboard(_THIS) SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Command"); SDL_SetScancodeName(SDL_SCANCODE_RALT, "Right Option"); SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Command"); - - data.modifierFlags = (unsigned int)[NSEvent modifierFlags]; - SDL_ToggleModState(KMOD_CAPS, (data.modifierFlags & NSEventModifierFlagCapsLock) ? SDL_TRUE : SDL_FALSE); } -void Cocoa_StartTextInput(_THIS) -{ @autoreleasepool +void +Cocoa_StartTextInput(_THIS) { - NSView *parentView; - SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; SDL_Window *window = SDL_GetKeyboardFocus(); NSWindow *nswindow = nil; - if (window) { - nswindow = ((__bridge SDL_WindowData*)window->driverdata).nswindow; - } + if (window) + nswindow = ((SDL_WindowData*)window->driverdata)->nswindow; - parentView = [nswindow contentView]; + NSView *parentView = [nswindow contentView]; /* We only keep one field editor per process, since only the front most * window can receive text input events, so it make no sense to keep more * than one copy. When we switched to another window and requesting for * 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 = + if (!data->fieldEdit) { + data->fieldEdit = [[SDLTranslatorResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)]; } - if (![[data.fieldEdit superview] isEqual:parentView]) { + if (![[data->fieldEdit superview] isEqual: parentView]) + { /* DEBUG_IME(@"add fieldEdit to window contentView"); */ - [data.fieldEdit removeFromSuperview]; - [parentView addSubview: data.fieldEdit]; - [nswindow makeFirstResponder: data.fieldEdit]; + [data->fieldEdit removeFromSuperview]; + [parentView addSubview: data->fieldEdit]; + [nswindow makeFirstResponder: data->fieldEdit]; } -}} -void Cocoa_StopTextInput(_THIS) -{ @autoreleasepool -{ - SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; + [pool release]; +} - if (data && data.fieldEdit) { - [data.fieldEdit removeFromSuperview]; - data.fieldEdit = nil; +void +Cocoa_StopTextInput(_THIS) +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + + if (data && data->fieldEdit) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [data->fieldEdit removeFromSuperview]; + [data->fieldEdit release]; + data->fieldEdit = nil; + [pool release]; } -}} +} -void Cocoa_SetTextInputRect(_THIS, const SDL_Rect *rect) +void +Cocoa_SetTextInputRect(_THIS, SDL_Rect *rect) { - SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; if (!rect) { - SDL_InvalidParamError("rect"); - return; + SDL_InvalidParamError("rect"); + return; } - [data.fieldEdit setInputRect:rect]; + [data->fieldEdit setInputRect: rect]; } -void Cocoa_HandleKeyEvent(_THIS, NSEvent *event) +void +Cocoa_HandleKeyEvent(_THIS, NSEvent *event) { - unsigned short scancode; + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + unsigned short scancode = [event keyCode]; SDL_Scancode code; - SDL_VideoData *data = _this ? ((__bridge SDL_VideoData *) _this->driverdata) : nil; - if (!data) { - return; /* can happen when returning from fullscreen Space on shutdown */ - } - - scancode = [event keyCode]; #if 0 const char *text; #endif if ((scancode == 10 || scancode == 50) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) { - /* see comments in scancodes_darwin.h */ + /* see comments in SDL_cocoakeys.h */ scancode = 60 - scancode; } - if (scancode < SDL_arraysize(darwin_scancode_table)) { code = darwin_scancode_table[scancode]; - } else { + } + else { /* Hmm, does this ever happen? If so, need to extend the keymap... */ code = SDL_SCANCODE_UNKNOWN; } switch ([event type]) { - case NSEventTypeKeyDown: + case NSKeyDown: if (![event isARepeat]) { /* See if we need to rebuild the keyboard layout */ - UpdateKeymap(data, SDL_TRUE); + UpdateKeymap(data); } SDL_SendKeyboardKey(SDL_PRESSED, code); -#ifdef DEBUG_SCANCODES +#if 1 if (code == SDL_SCANCODE_UNKNOWN) { - SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, report this to the SDL forums/mailing list or to Christian Walther . Mac virtual key code is %d.\n", scancode); + fprintf(stderr, "The key you just pressed is not recognized by SDL. To help get this fixed, report this to the SDL mailing list or to Christian Walther . Mac virtual key code is %d.\n", scancode); } #endif if (SDL_EventState(SDL_TEXTINPUT, SDL_QUERY)) { /* FIXME CW 2007-08-16: only send those events to the field editor for which we actually want text events, not e.g. esc or function keys. Arrow keys in particular seem to produce crashes sometimes. */ - [data.fieldEdit interpretKeyEvents:[NSArray arrayWithObject:event]]; + [data->fieldEdit interpretKeyEvents:[NSArray arrayWithObject:event]]; #if 0 text = [[event characters] UTF8String]; if(text && *text) { @@ -434,47 +610,23 @@ void Cocoa_HandleKeyEvent(_THIS, NSEvent *event) #endif } break; - case NSEventTypeKeyUp: + case NSKeyUp: SDL_SendKeyboardKey(SDL_RELEASED, code); break; - case NSEventTypeFlagsChanged: { - // see if the new modifierFlags mean any existing keys should be pressed/released... - const unsigned int modflags = (unsigned int)[event modifierFlags]; - HandleModifiers(_this, SDL_SCANCODE_LSHIFT, modflags); - HandleModifiers(_this, SDL_SCANCODE_LCTRL, modflags); - HandleModifiers(_this, SDL_SCANCODE_LALT, modflags); - HandleModifiers(_this, SDL_SCANCODE_LGUI, modflags); - HandleModifiers(_this, SDL_SCANCODE_RSHIFT, modflags); - HandleModifiers(_this, SDL_SCANCODE_RCTRL, modflags); - HandleModifiers(_this, SDL_SCANCODE_RALT, modflags); - HandleModifiers(_this, SDL_SCANCODE_RGUI, modflags); + case NSFlagsChanged: + /* FIXME CW 2007-08-14: check if this whole mess that takes up half of this file is really necessary */ + HandleModifiers(_this, scancode, [event modifierFlags]); break; - } default: /* just to avoid compiler warnings */ break; } } -void Cocoa_QuitKeyboard(_THIS) +void +Cocoa_QuitKeyboard(_THIS) { } -typedef int CGSConnection; -typedef enum { - CGSGlobalHotKeyEnable = 0, - CGSGlobalHotKeyDisable = 1, -} CGSGlobalHotKeyOperatingMode; - -extern CGSConnection _CGSDefaultConnection(void); -extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode); - -void Cocoa_SetWindowKeyboardGrab(_THIS, SDL_Window * window, SDL_bool grabbed) -{ -#ifdef SDL_MAC_NO_SANDBOX - CGSSetGlobalHotKeyOperatingMode(_CGSDefaultConnection(), grabbed ? CGSGlobalHotKeyDisable : CGSGlobalHotKeyEnable); -#endif -} - #endif /* SDL_VIDEO_DRIVER_COCOA */ /* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoamessagebox.h src/video/cocoa/SDL_cocoamessagebox.h index 501ffa502..ff468a881 100644 --- src/video/cocoa/SDL_cocoamessagebox.h +++ src/video/cocoa/SDL_cocoamessagebox.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,7 +20,7 @@ */ #include "../../SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_COCOA +#if SDL_VIDEO_DRIVER_COCOA extern int Cocoa_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid); diff --git src/video/cocoa/SDL_cocoamessagebox.m src/video/cocoa/SDL_cocoamessagebox.m index 20eabe3ee..730d952ee 100644 --- src/video/cocoa/SDL_cocoamessagebox.m +++ src/video/cocoa/SDL_cocoamessagebox.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,7 +20,14 @@ */ #include "../../SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_COCOA +#if SDL_VIDEO_DRIVER_COCOA + +#if defined(__APPLE__) && defined(__POWERPC__) && !defined(__APPLE_ALTIVEC__) +#include +#undef bool +#undef vector +#undef pixel +#endif #include "SDL_events.h" #include "SDL_timer.h" @@ -32,10 +39,8 @@ NSInteger clicked; NSWindow *nswindow; } -- (id)initWithParentWindow:(SDL_Window *)window; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 -- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo; -#endif +- (id) initWithParentWindow:(SDL_Window *)window; +- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo; @end @implementation SDLMessageBoxPresenter @@ -47,9 +52,9 @@ /* 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_WindowData *) window->driverdata)->nswindow retain]; } else { - nswindow = nil; + nswindow = NULL; } } @@ -59,107 +64,81 @@ - (void)showAlert:(NSAlert*)alert { if (nswindow) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 - if ([alert respondsToSelector:@selector(beginSheetModalForWindow:completionHandler:)]) { - [alert beginSheetModalForWindow:nswindow - completionHandler:^(NSModalResponse returnCode) { - [NSApp stopModalWithCode:returnCode]; - }]; - } else -#endif - { -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 - [alert beginSheetModalForWindow:nswindow modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:nil]; -#endif + [alert beginSheetModalForWindow:nswindow modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:nil]; + while (clicked < 0) { + SDL_PumpEvents(); + SDL_Delay(100); } - clicked = [NSApp runModalForWindow:nswindow]; - nswindow = nil; + [nswindow release]; } else { clicked = [alert runModal]; } } -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 - (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { - [NSApp stopModalWithCode:returnCode]; + clicked = returnCode; } -#endif + @end -static void Cocoa_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid, int *returnValue) +/* Display a Cocoa message box */ +int +Cocoa_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) { - NSAlert* alert; - const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons; - SDLMessageBoxPresenter* presenter; - NSInteger clicked; - int i; Cocoa_RegisterApp(); - alert = [[NSAlert alloc] init]; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSAlert* alert = [[[NSAlert alloc] init] autorelease]; if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) { - [alert setAlertStyle:NSAlertStyleCritical]; + [alert setAlertStyle:NSCriticalAlertStyle]; } else if (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) { - [alert setAlertStyle:NSAlertStyleWarning]; + [alert setAlertStyle:NSWarningAlertStyle]; } else { - [alert setAlertStyle:NSAlertStyleInformational]; + [alert setAlertStyle:NSInformationalAlertStyle]; } [alert setMessageText:[NSString stringWithUTF8String:messageboxdata->title]]; [alert setInformativeText:[NSString stringWithUTF8String:messageboxdata->message]]; + const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons; + int i; for (i = 0; i < messageboxdata->numbuttons; ++i) { - const SDL_MessageBoxButtonData *sdlButton; - NSButton *button; - - if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { - sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i]; - } else { - sdlButton = &messageboxdata->buttons[i]; - } - - button = [alert addButtonWithTitle:[NSString stringWithUTF8String:sdlButton->text]]; - if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { + NSButton *button = [alert addButtonWithTitle:[NSString stringWithUTF8String:buttons[i].text]]; + if (buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { [button setKeyEquivalent:@"\r"]; - } else if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) { + } else if (buttons[i].flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) { [button setKeyEquivalent:@"\033"]; } else { [button setKeyEquivalent:@""]; } } - presenter = [[SDLMessageBoxPresenter alloc] initWithParentWindow:messageboxdata->window]; + SDLMessageBoxPresenter* presenter = [[[SDLMessageBoxPresenter alloc] initWithParentWindow:messageboxdata->window] autorelease]; - [presenter showAlert:alert]; + [presenter performSelectorOnMainThread:@selector(showAlert:) + withObject:alert + waitUntilDone:YES]; - clicked = presenter->clicked; - if (clicked >= NSAlertFirstButtonReturn) { + int returnValue = 0; + NSInteger clicked = presenter->clicked; + if (clicked >= NSAlertFirstButtonReturn) + { clicked -= NSAlertFirstButtonReturn; - if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { - clicked = messageboxdata->numbuttons - 1 - clicked; - } *buttonid = buttons[clicked].buttonid; - *returnValue = 0; - } else { - *returnValue = SDL_SetError("Did not get a valid `clicked button' id: %ld", (long)clicked); } -} + else + { + returnValue = SDL_SetError("Did not get a valid `clicked button' id: %d", clicked); + } -/* Display a Cocoa message box */ -int Cocoa_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) -{ @autoreleasepool -{ - __block int returnValue = 0; + [pool release]; - if ([NSThread isMainThread]) { - Cocoa_ShowMessageBoxImpl(messageboxdata, buttonid, &returnValue); - } else { - dispatch_sync(dispatch_get_main_queue(), ^{ Cocoa_ShowMessageBoxImpl(messageboxdata, buttonid, &returnValue); }); - } return returnValue; -}} +} #endif /* SDL_VIDEO_DRIVER_COCOA */ diff --git src/video/cocoa/SDL_cocoamodes.h src/video/cocoa/SDL_cocoamodes.h index dc6273d5e..4f8e86324 100644 --- src/video/cocoa/SDL_cocoamodes.h +++ src/video/cocoa/SDL_cocoamodes.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,8 +20,8 @@ */ #include "../../SDL_internal.h" -#ifndef SDL_cocoamodes_h_ -#define SDL_cocoamodes_h_ +#ifndef _SDL_cocoamodes_h +#define _SDL_cocoamodes_h typedef struct { @@ -30,17 +30,15 @@ typedef struct typedef struct { - CFMutableArrayRef modes; + const void *moderef; } SDL_DisplayModeData; extern void Cocoa_InitModes(_THIS); extern int Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); -extern int Cocoa_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); extern void Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display); -extern int Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hpdi, float * vdpi); extern int Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode); extern void Cocoa_QuitModes(_THIS); -#endif /* SDL_cocoamodes_h_ */ +#endif /* _SDL_cocoamodes_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoamodes.m src/video/cocoa/SDL_cocoamodes.m index fa5b18458..fd39bfd6a 100644 --- src/video/cocoa/SDL_cocoamodes.m +++ src/video/cocoa/SDL_cocoamodes.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,29 +20,57 @@ */ #include "../../SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_COCOA +#if SDL_VIDEO_DRIVER_COCOA #include "SDL_cocoavideo.h" /* We need this for IODisplayCreateInfoDictionary and kIODisplayOnlyPreferredName */ #include -/* We need this for CVDisplayLinkGetNominalOutputVideoRefreshPeriod */ -#include -#include +/* we need this for ShowMenuBar() and HideMenuBar(). */ +#include /* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */ #include -#ifndef MAC_OS_X_VERSION_10_13 -#define NSAppKitVersionNumber10_12 1504 + +static void +Cocoa_ToggleMenuBar(const BOOL show) +{ + /* !!! FIXME: keep an eye on this. + * ShowMenuBar/HideMenuBar is officially unavailable for 64-bit binaries. + * It happens to work, as of 10.7, but we're going to see if + * we can just simply do without it on newer OSes... + */ +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__) + if (show) + ShowMenuBar(); + else + HideMenuBar(); #endif -#if (IOGRAPHICSTYPES_REV < 40) -#define kDisplayModeNativeFlag 0x02000000 +} + + +/* !!! FIXME: clean out the pre-10.6 code when it makes sense to do so. */ +#define FORCE_OLD_API 0 + +#if FORCE_OLD_API +#undef MAC_OS_X_VERSION_MIN_REQUIRED +#define MAC_OS_X_VERSION_MIN_REQUIRED 1050 #endif +static BOOL +IS_SNOW_LEOPARD_OR_LATER(_THIS) +{ +#if FORCE_OLD_API + return NO; +#else + return ((((SDL_VideoData *) _this->driverdata))->osversion >= 0x1060); +#endif +} -static int CG_SetError(const char *prefix, CGDisplayErr result) +static int +CG_SetError(const char *prefix, CGDisplayErr result) { const char *error; @@ -84,234 +112,132 @@ static int CG_SetError(const char *prefix, CGDisplayErr result) return SDL_SetError("%s: %s", prefix, error); } -static int GetDisplayModeRefreshRate(CGDisplayModeRef vidmode, CVDisplayLinkRef link) +static SDL_bool +GetDisplayMode(_THIS, const void *moderef, SDL_DisplayMode *mode) { - int refreshRate = (int) (CGDisplayModeGetRefreshRate(vidmode) + 0.5); + SDL_DisplayModeData *data; + long width = 0; + long height = 0; + long bpp = 0; + long refreshRate = 0; - /* CGDisplayModeGetRefreshRate can return 0 (eg for built-in displays). */ - if (refreshRate == 0 && link != NULL) { - CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); - if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) { - refreshRate = (int) ((time.timeScale / (double) time.timeValue) + 0.5); - } + data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data)); + if (!data) { + return SDL_FALSE; } + data->moderef = moderef; + + #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + if (IS_SNOW_LEOPARD_OR_LATER(_this)) { + CGDisplayModeRef vidmode = (CGDisplayModeRef) moderef; + CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode); + width = (long) CGDisplayModeGetWidth(vidmode); + height = (long) CGDisplayModeGetHeight(vidmode); + refreshRate = (long) CGDisplayModeGetRefreshRate(vidmode); + + if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + bpp = 32; + } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + bpp = 16; + } else { + bpp = 0; /* ignore 8-bit and such for now. */ + } - return refreshRate; -} - -static SDL_bool HasValidDisplayModeFlags(CGDisplayModeRef vidmode) -{ - uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode); - - /* Filter out modes which have flags that we don't want. */ - if (ioflags & (kDisplayModeNeverShowFlag | kDisplayModeNotGraphicsQualityFlag)) { - return SDL_FALSE; + CFRelease(fmt); } + #endif + + #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + if (!IS_SNOW_LEOPARD_OR_LATER(_this)) { + CFNumberRef number; + CFDictionaryRef vidmode = (CFDictionaryRef) moderef; + number = CFDictionaryGetValue(vidmode, kCGDisplayWidth); + CFNumberGetValue(number, kCFNumberLongType, &width); + number = CFDictionaryGetValue(vidmode, kCGDisplayHeight); + CFNumberGetValue(number, kCFNumberLongType, &height); + number = CFDictionaryGetValue(vidmode, kCGDisplayBitsPerPixel); + CFNumberGetValue(number, kCFNumberLongType, &bpp); + number = CFDictionaryGetValue(vidmode, kCGDisplayRefreshRate); + CFNumberGetValue(number, kCFNumberLongType, &refreshRate); + } + #endif - /* Filter out modes which don't have flags that we want. */ - if (!(ioflags & kDisplayModeValidFlag) || !(ioflags & kDisplayModeSafeFlag)) { + mode->format = SDL_PIXELFORMAT_UNKNOWN; + switch (bpp) { + case 16: + mode->format = SDL_PIXELFORMAT_ARGB1555; + break; + case 32: + mode->format = SDL_PIXELFORMAT_ARGB8888; + break; + case 8: /* We don't support palettized modes now */ + default: /* Totally unrecognizable bit depth. */ return SDL_FALSE; } - + mode->w = width; + mode->h = height; + mode->refresh_rate = refreshRate; + mode->driverdata = data; return SDL_TRUE; } -static Uint32 GetDisplayModePixelFormat(CGDisplayModeRef vidmode) +static void +Cocoa_ReleaseDisplayMode(_THIS, const void *moderef) { - /* This API is deprecated in 10.11 with no good replacement (as of 10.15). */ - CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode); - Uint32 pixelformat = SDL_PIXELFORMAT_UNKNOWN; - - if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels), - kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - pixelformat = SDL_PIXELFORMAT_ARGB8888; - } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels), - kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - pixelformat = SDL_PIXELFORMAT_ARGB1555; - } else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels), - kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - pixelformat = SDL_PIXELFORMAT_ARGB2101010; - } else { - /* ignore 8-bit and such for now. */ + #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + if (IS_SNOW_LEOPARD_OR_LATER(_this)) { + CGDisplayModeRelease((CGDisplayModeRef) moderef); /* NULL is ok */ } - - CFRelease(fmt); - - return pixelformat; + #endif } -static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmodeCurrent, CFArrayRef modelist, CVDisplayLinkRef link, SDL_DisplayMode *mode) +static void +Cocoa_ReleaseDisplayModeList(_THIS, CFArrayRef modelist) { - SDL_DisplayModeData *data; - bool usableForGUI = CGDisplayModeIsUsableForDesktopGUI(vidmode); - int width = (int) CGDisplayModeGetWidth(vidmode); - int height = (int) CGDisplayModeGetHeight(vidmode); - uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode); - int refreshrate = GetDisplayModeRefreshRate(vidmode, link); - Uint32 format = GetDisplayModePixelFormat(vidmode); - bool interlaced = (ioflags & kDisplayModeInterlacedFlag) != 0; - CFMutableArrayRef modes; - - if (format == SDL_PIXELFORMAT_UNKNOWN) { - return SDL_FALSE; - } - - /* Don't fail the current mode based on flags because this could prevent Cocoa_InitModes from - * succeeding if the current mode lacks certain flags (esp kDisplayModeSafeFlag). */ - if (!vidmodeCurrent && !HasValidDisplayModeFlags(vidmode)) { - return SDL_FALSE; + #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + if (IS_SNOW_LEOPARD_OR_LATER(_this)) { + CFRelease(modelist); /* NULL is ok */ } - - modes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - CFArrayAppendValue(modes, vidmode); - - /* If a list of possible diplay modes is passed in, use it to filter out - * modes that have duplicate sizes. We don't just rely on SDL's higher level - * duplicate filtering because this code can choose what properties are - * 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 (modelist != NULL && floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_7) { - int pixelW = (int) CGDisplayModeGetPixelWidth(vidmode); - int pixelH = (int) CGDisplayModeGetPixelHeight(vidmode); - - CFIndex modescount = CFArrayGetCount(modelist); - int i; - - for (i = 0; i < modescount; i++) { - int otherW, otherH, otherpixelW, otherpixelH, otherrefresh; - Uint32 otherformat; - bool otherGUI; - CGDisplayModeRef othermode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modelist, i); - uint32_t otherioflags = CGDisplayModeGetIOFlags(othermode); - - if (CFEqual(vidmode, othermode)) { - continue; - } - - if (!HasValidDisplayModeFlags(othermode)) { - continue; - } - - otherW = (int) CGDisplayModeGetWidth(othermode); - otherH = (int) CGDisplayModeGetHeight(othermode); - otherpixelW = (int) CGDisplayModeGetPixelWidth(othermode); - otherpixelH = (int) CGDisplayModeGetPixelHeight(othermode); - otherrefresh = GetDisplayModeRefreshRate(othermode, link); - otherformat = GetDisplayModePixelFormat(othermode); - otherGUI = CGDisplayModeIsUsableForDesktopGUI(othermode); - - /* Ignore this mode if it's low-dpi (@1x) and we have a high-dpi - * mode in the list with the same size in points. - */ - if (width == pixelW && height == pixelH - && width == otherW && height == otherH - && refreshrate == otherrefresh && format == otherformat - && (otherpixelW != otherW || otherpixelH != otherH)) { - CFRelease(modes); - return SDL_FALSE; - } - - /* Ignore this mode if it's interlaced and there's a non-interlaced - * mode in the list with the same properties. - */ - if (interlaced && ((otherioflags & kDisplayModeInterlacedFlag) == 0) - && width == otherW && height == otherH && pixelW == otherpixelW - && pixelH == otherpixelH && refreshrate == otherrefresh - && format == otherformat && usableForGUI == otherGUI) { - CFRelease(modes); - return SDL_FALSE; - } - - /* Ignore this mode if it's not usable for desktop UI and its - * properties are equal to another GUI-capable mode in the list. - */ - if (width == otherW && height == otherH && pixelW == otherpixelW - && pixelH == otherpixelH && !usableForGUI && otherGUI - && refreshrate == otherrefresh && format == otherformat) { - CFRelease(modes); - return SDL_FALSE; - } - - /* If multiple modes have the exact same properties, they'll all - * go in the list of modes to try when SetDisplayMode is called. - * This is needed because kCGDisplayShowDuplicateLowResolutionModes - * (which is used to expose highdpi display modes) can make the - * list of modes contain duplicates (according to their properties - * obtained via public APIs) which don't work with SetDisplayMode. - * Those duplicate non-functional modes *do* have different pixel - * formats according to their internal data structure viewed with - * NSLog, but currently no public API can detect that. - * https://bugzilla.libsdl.org/show_bug.cgi?id=4822 - * - * As of macOS 10.15.0, those duplicates have the exact same - * properties via public APIs in every way (even their IO flags and - * CGDisplayModeGetIODisplayModeID is the same), so we could test - * those for equality here too, but I'm intentionally not doing that - * in case there are duplicate modes with different IO flags or IO - * display mode IDs in the future. In that case I think it's better - * to try them all in SetDisplayMode than to risk one of them being - * correct but it being filtered out by SDL_AddDisplayMode as being - * a duplicate. - */ - if (width == otherW && height == otherH && pixelW == otherpixelW - && pixelH == otherpixelH && usableForGUI == otherGUI - && refreshrate == otherrefresh && format == otherformat) { - CFArrayAppendValue(modes, othermode); - } - } - } -#endif - - data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data)); - if (!data) { - CFRelease(modes); - return SDL_FALSE; - } - data->modes = modes; - mode->format = format; - mode->w = width; - mode->h = height; - mode->refresh_rate = refreshrate; - mode->driverdata = data; - return SDL_TRUE; + #endif } -static const char *Cocoa_GetDisplayName(CGDirectDisplayID displayID) +static const char * +Cocoa_GetDisplayName(CGDirectDisplayID displayID) { - /* This API is deprecated in 10.9 with no good replacement (as of 10.15). */ - io_service_t servicePort = CGDisplayIOServicePort(displayID); - CFDictionaryRef deviceInfo = IODisplayCreateInfoDictionary(servicePort, kIODisplayOnlyPreferredName); - NSDictionary *localizedNames = [(__bridge NSDictionary *)deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]]; + NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName); + NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]]; const char* displayName = NULL; if ([localizedNames count] > 0) { displayName = SDL_strdup([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]); } - CFRelease(deviceInfo); + [deviceInfo release]; return displayName; } -void Cocoa_InitModes(_THIS) -{ @autoreleasepool +void +Cocoa_InitModes(_THIS) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; CGDisplayErr result; CGDirectDisplayID *displays; CGDisplayCount numDisplays; - SDL_bool isstack; int pass, i; result = CGGetOnlineDisplayList(0, NULL, &numDisplays); if (result != kCGErrorSuccess) { CG_SetError("CGGetOnlineDisplayList()", result); + [pool release]; return; } - displays = SDL_small_alloc(CGDirectDisplayID, numDisplays, &isstack); + displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays); result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays); if (result != kCGErrorSuccess) { CG_SetError("CGGetOnlineDisplayList()", result); - SDL_small_free(displays, isstack); + SDL_stack_free(displays); + [pool release]; return; } @@ -321,8 +247,7 @@ void Cocoa_InitModes(_THIS) SDL_VideoDisplay display; SDL_DisplayData *displaydata; SDL_DisplayMode mode; - CGDisplayModeRef moderef = NULL; - CVDisplayLinkRef link = NULL; + const void *moderef = NULL; if (pass == 0) { if (!CGDisplayIsMain(displays[i])) { @@ -338,7 +263,17 @@ void Cocoa_InitModes(_THIS) continue; } - moderef = CGDisplayCopyDisplayMode(displays[i]); + #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + if (IS_SNOW_LEOPARD_OR_LATER(_this)) { + moderef = CGDisplayCopyDisplayMode(displays[i]); + } + #endif + + #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + if (!IS_SNOW_LEOPARD_OR_LATER(_this)) { + moderef = CGDisplayCurrentMode(displays[i]); + } + #endif if (!moderef) { continue; @@ -346,38 +281,34 @@ void Cocoa_InitModes(_THIS) displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata)); if (!displaydata) { - CGDisplayModeRelease(moderef); + Cocoa_ReleaseDisplayMode(_this, moderef); continue; } displaydata->display = displays[i]; - CVDisplayLinkCreateWithCGDisplay(displays[i], &link); - SDL_zero(display); /* this returns a stddup'ed string */ display.name = (char *)Cocoa_GetDisplayName(displays[i]); - if (!GetDisplayMode(_this, moderef, SDL_TRUE, NULL, link, &mode)) { - CVDisplayLinkRelease(link); - CGDisplayModeRelease(moderef); + if (!GetDisplayMode (_this, moderef, &mode)) { + Cocoa_ReleaseDisplayMode(_this, moderef); SDL_free(display.name); SDL_free(displaydata); continue; } - CVDisplayLinkRelease(link); - CGDisplayModeRelease(moderef); - display.desktop_mode = mode; display.current_mode = mode; display.driverdata = displaydata; - SDL_AddVideoDisplay(&display, SDL_FALSE); + SDL_AddVideoDisplay(&display,FALSE); SDL_free(display.name); } } - SDL_small_free(displays, isstack); -}} + SDL_stack_free(displays); + [pool release]; +} -int Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) +int +Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) { SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; CGRect cgrect; @@ -390,215 +321,65 @@ int Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) return 0; } -int Cocoa_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) -{ - SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; - const CGDirectDisplayID cgdisplay = displaydata->display; - NSArray *screens = [NSScreen screens]; - NSScreen *screen = nil; - - /* !!! FIXME: maybe track the NSScreen in SDL_DisplayData? */ - for (NSScreen *i in screens) { - const CGDirectDisplayID thisDisplay = (CGDirectDisplayID) [[[i deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; - if (thisDisplay == cgdisplay) { - screen = i; - break; - } - } - - SDL_assert(screen != nil); /* didn't find it?! */ - if (screen == nil) { - return -1; - } - - { - const NSRect frame = [screen visibleFrame]; - rect->x = (int)frame.origin.x; - rect->y = (int)(CGDisplayPixelsHigh(kCGDirectMainDisplay) - frame.origin.y - frame.size.height); - rect->w = (int)frame.size.width; - rect->h = (int)frame.size.height; - } - - return 0; -} - -int Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi) -{ @autoreleasepool -{ - const float MM_IN_INCH = 25.4f; - - SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; - - /* we need the backingScaleFactor for Retina displays, which is only exposed through NSScreen, not CGDisplay, afaik, so find our screen... */ - CGFloat scaleFactor = 1.0f; - NSArray *screens = [NSScreen screens]; - NSSize displayNativeSize; - displayNativeSize.width = (int) CGDisplayPixelsWide(data->display); - displayNativeSize.height = (int) CGDisplayPixelsHigh(data->display); - - 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 - /* 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 }; - CFBooleanRef dmValues[1] = { kCFBooleanTrue }; - CFDictionaryRef dmOptions = CFDictionaryCreate(kCFAllocatorDefault, (const void**) dmKeys, (const void**) dmValues, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); - CFArrayRef allDisplayModes = CGDisplayCopyAllDisplayModes(dpyid, dmOptions); - CFIndex n = CFArrayGetCount(allDisplayModes); - for(CFIndex i = 0; i < n; ++i) { - CGDisplayModeRef m = (CGDisplayModeRef)CFArrayGetValueAtIndex(allDisplayModes, i); - CGFloat width = CGDisplayModeGetPixelWidth(m); - CGFloat height = CGDisplayModeGetPixelHeight(m); - CGFloat HiDPIWidth = CGDisplayModeGetWidth(m); - - //Only check 1x mode - if(width == HiDPIWidth) { - if (CGDisplayModeGetIOFlags(m) & kDisplayModeNativeFlag) { - displayNativeSize.width = width; - displayNativeSize.height = height; - break; - } - - //Get the largest size even if kDisplayModeNativeFlag is not present e.g. iMac 27-Inch with 5K Retina - if(width > displayNativeSize.width) { - displayNativeSize.width = width; - displayNativeSize.height = height; - } - } - } - CFRelease(allDisplayModes); - CFRelease(dmOptions); - } else -#endif - { - // fallback for 10.7 - scaleFactor = [screen backingScaleFactor]; - displayNativeSize.width = displayNativeSize.width * scaleFactor; - displayNativeSize.height = displayNativeSize.height * scaleFactor; - break; - } - } - } - - { - const CGSize displaySize = CGDisplayScreenSize(data->display); - const int pixelWidth = displayNativeSize.width; - const int pixelHeight = displayNativeSize.height; - - if (ddpi) { - *ddpi = (SDL_ComputeDiagonalDPI(pixelWidth, pixelHeight, displaySize.width / MM_IN_INCH, displaySize.height / MM_IN_INCH)); - } - if (hdpi) { - *hdpi = (pixelWidth * MM_IN_INCH / displaySize.width); - } - if (vdpi) { - *vdpi = (pixelHeight * MM_IN_INCH / displaySize.height); - } - } - return 0; -}} - -void Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) +void +Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) { SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; - CVDisplayLinkRef link = NULL; - CGDisplayModeRef desktopmoderef; - SDL_DisplayMode desktopmode; - CFArrayRef modes; - CFDictionaryRef dict = NULL; - - CVDisplayLinkCreateWithCGDisplay(data->display, &link); - - desktopmoderef = CGDisplayCopyDisplayMode(data->display); - - /* CopyAllDisplayModes won't always contain the desktop display mode (if - * NULL is passed in) - for example on a retina 15" MBP, System Preferences - * allows choosing 1920x1200 but it's not in the list. AddDisplayMode makes - * sure there are no duplicates so it's safe to always add the desktop mode - * even in cases where it is in the CopyAllDisplayModes list. - */ - if (desktopmoderef && GetDisplayMode(_this, desktopmoderef, SDL_TRUE, NULL, link, &desktopmode)) { - if (!SDL_AddDisplayMode(display, &desktopmode)) { - CFRelease(((SDL_DisplayModeData*)desktopmode.driverdata)->modes); - SDL_free(desktopmode.driverdata); - } - } + CFArrayRef modes = NULL; - CGDisplayModeRelease(desktopmoderef); - - /* By default, CGDisplayCopyAllDisplayModes will only get a subset of the - * system's available modes. For example on a 15" 2016 MBP, users can - * choose 1920x1080@2x in System Preferences but it won't show up here, - * unless we specify the option below. - * The display modes returned by CGDisplayCopyAllDisplayModes are also not - * high dpi-capable unless this option is set. - * macOS 10.15 also seems to have a bug where entering, exiting, and - * re-entering exclusive fullscreen with a low dpi display mode can cause - * 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 (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_7) { - const CFStringRef dictkeys[] = {kCGDisplayShowDuplicateLowResolutionModes}; - const CFBooleanRef dictvalues[] = {kCFBooleanTrue}; - dict = CFDictionaryCreate(NULL, - (const void **)dictkeys, - (const void **)dictvalues, - 1, - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); + #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + if (IS_SNOW_LEOPARD_OR_LATER(_this)) { + modes = CGDisplayCopyAllDisplayModes(data->display, NULL); } -#endif - - modes = CGDisplayCopyAllDisplayModes(data->display, dict); + #endif - if (dict) { - CFRelease(dict); + #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + if (!IS_SNOW_LEOPARD_OR_LATER(_this)) { + modes = CGDisplayAvailableModes(data->display); } + #endif if (modes) { - CFIndex i; const CFIndex count = CFArrayGetCount(modes); + CFIndex i; for (i = 0; i < count; i++) { - CGDisplayModeRef moderef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + const void *moderef = CFArrayGetValueAtIndex(modes, i); SDL_DisplayMode mode; - - if (GetDisplayMode(_this, moderef, SDL_FALSE, modes, link, &mode)) { - if (!SDL_AddDisplayMode(display, &mode)) { - CFRelease(((SDL_DisplayModeData*)mode.driverdata)->modes); - SDL_free(mode.driverdata); + if (GetDisplayMode(_this, moderef, &mode)) { + #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + if (IS_SNOW_LEOPARD_OR_LATER(_this)) { + CGDisplayModeRetain((CGDisplayModeRef) moderef); } + #endif + SDL_AddDisplayMode(display, &mode); } } - CFRelease(modes); + Cocoa_ReleaseDisplayModeList(_this, modes); } - - CVDisplayLinkRelease(link); } -static CGError SetDisplayModeForDisplay(CGDirectDisplayID display, SDL_DisplayModeData *data) +static CGError +Cocoa_SwitchMode(_THIS, CGDirectDisplayID display, const void *mode) { - /* SDL_DisplayModeData can contain multiple CGDisplayModes to try (with - * identical properties), some of which might not work. See GetDisplayMode. - */ - CGError result = kCGErrorFailure; - for (CFIndex i = 0; i < CFArrayGetCount(data->modes); i++) { - CGDisplayModeRef moderef = (CGDisplayModeRef)CFArrayGetValueAtIndex(data->modes, i); - result = CGDisplaySetDisplayMode(display, moderef, NULL); - if (result == kCGErrorSuccess) { - /* If this mode works, try it first next time. */ - CFArrayExchangeValuesAtIndices(data->modes, i, 0); - break; - } + #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + if (IS_SNOW_LEOPARD_OR_LATER(_this)) { + return CGDisplaySetDisplayMode(display, (CGDisplayModeRef) mode, NULL); } - return result; + #endif + + #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + if (!IS_SNOW_LEOPARD_OR_LATER(_this)) { + return CGDisplaySwitchToMode(display, (CFDictionaryRef) mode); + } + #endif + + return kCGErrorFailure; } -int Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) +int +Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) { SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata; @@ -612,13 +393,17 @@ int Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mo if (data == display->desktop_mode.driverdata) { /* Restoring desktop mode */ - SetDisplayModeForDisplay(displaydata->display, data); + Cocoa_SwitchMode(_this, displaydata->display, data->moderef); if (CGDisplayIsMain(displaydata->display)) { CGReleaseAllDisplays(); } else { CGDisplayRelease(displaydata->display); } + + if (CGDisplayIsMain(displaydata->display)) { + Cocoa_ToggleMenuBar(YES); + } } else { /* Put up the blanking window (a window above all other windows) */ if (CGDisplayIsMain(displaydata->display)) { @@ -633,11 +418,16 @@ int Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mo } /* Do the physical switch */ - result = SetDisplayModeForDisplay(displaydata->display, data); + result = Cocoa_SwitchMode(_this, displaydata->display, data->moderef); if (result != kCGErrorSuccess) { CG_SetError("CGDisplaySwitchToMode()", result); goto ERR_NO_SWITCH; } + + /* Hide the menu bar so it doesn't intercept events */ + if (CGDisplayIsMain(displaydata->display)) { + Cocoa_ToggleMenuBar(NO); + } } /* Fade in again (asynchronously) */ @@ -650,11 +440,7 @@ int Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mo /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ ERR_NO_SWITCH: - if (CGDisplayIsMain(displaydata->display)) { - CGReleaseAllDisplays(); - } else { - CGDisplayRelease(displaydata->display); - } + CGDisplayRelease(displaydata->display); ERR_NO_CAPTURE: if (fade_token != kCGDisplayFadeReservationInvalidToken) { CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); @@ -663,7 +449,8 @@ ERR_NO_CAPTURE: return -1; } -void Cocoa_QuitModes(_THIS) +void +Cocoa_QuitModes(_THIS) { int i, j; @@ -676,13 +463,15 @@ void Cocoa_QuitModes(_THIS) } mode = (SDL_DisplayModeData *) display->desktop_mode.driverdata; - CFRelease(mode->modes); + Cocoa_ReleaseDisplayMode(_this, mode->moderef); for (j = 0; j < display->num_display_modes; j++) { mode = (SDL_DisplayModeData*) display->display_modes[j].driverdata; - CFRelease(mode->modes); + Cocoa_ReleaseDisplayMode(_this, mode->moderef); } + } + Cocoa_ToggleMenuBar(YES); } #endif /* SDL_VIDEO_DRIVER_COCOA */ diff --git src/video/cocoa/SDL_cocoamouse.h src/video/cocoa/SDL_cocoamouse.h index 4fee8655e..03f1d50a7 100644 --- src/video/cocoa/SDL_cocoamouse.h +++ src/video/cocoa/SDL_cocoamouse.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,19 +20,23 @@ */ #include "../../SDL_internal.h" -#ifndef SDL_cocoamouse_h_ -#define SDL_cocoamouse_h_ +#ifndef _SDL_cocoamouse_h +#define _SDL_cocoamouse_h #include "SDL_cocoavideo.h" -extern int Cocoa_InitMouse(_THIS); +#if !defined(MAC_OS_X_VERSION_10_5) +typedef float CGFloat; +#endif + +extern void Cocoa_InitMouse(_THIS); extern void Cocoa_HandleMouseEvent(_THIS, NSEvent * event); extern void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent * event); extern void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y); extern void Cocoa_QuitMouse(_THIS); typedef struct { - /* Whether we've seen a cursor warp since the last move event. */ + /* Wether we've seen a cursor warp since the last move event. */ SDL_bool seenWarp; /* What location our last cursor warp was to. */ CGFloat lastWarpX; @@ -40,14 +44,13 @@ typedef struct { /* What location we last saw the cursor move to. */ CGFloat lastMoveX; CGFloat lastMoveY; - /* If we just turned on relative mode, and should skip a single mouse motion event. */ - SDL_bool justEnabledRelative; + void *tapdata; } SDL_MouseData; @interface NSCursor (InvisibleCursor) + (NSCursor *)invisibleCursor; @end -#endif /* SDL_cocoamouse_h_ */ +#endif /* _SDL_cocoamouse_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoamouse.m src/video/cocoa/SDL_cocoamouse.m index e70c40956..107c7d2bd 100644 --- src/video/cocoa/SDL_cocoamouse.m +++ src/video/cocoa/SDL_cocoamouse.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,11 +20,12 @@ */ #include "../../SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_COCOA +#if SDL_VIDEO_DRIVER_COCOA +#include "SDL_assert.h" #include "SDL_events.h" #include "SDL_cocoamouse.h" -#include "SDL_cocoavideo.h" +#include "SDL_cocoamousetap.h" #include "../../events/SDL_mouse_c.h" @@ -53,7 +54,7 @@ NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0] length:sizeof(cursorBytes) freeWhenDone:NO]; - NSImage *cursorImage = [[NSImage alloc] initWithData:cursorData]; + NSImage *cursorImage = [[[NSImage alloc] initWithData:cursorData] autorelease]; invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage hotSpot:NSZeroPoint]; } @@ -63,9 +64,10 @@ @end -static SDL_Cursor *Cocoa_CreateDefaultCursor() -{ @autoreleasepool +static SDL_Cursor * +Cocoa_CreateDefaultCursor() { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSCursor *nscursor; SDL_Cursor *cursor = NULL; @@ -74,16 +76,20 @@ static SDL_Cursor *Cocoa_CreateDefaultCursor() if (nscursor) { cursor = SDL_calloc(1, sizeof(*cursor)); if (cursor) { - cursor->driverdata = (void *)CFBridgingRetain(nscursor); + cursor->driverdata = nscursor; + [nscursor retain]; } } + [pool release]; + return cursor; -}} +} -static SDL_Cursor *Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) -{ @autoreleasepool +static SDL_Cursor * +Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSImage *nsimage; NSCursor *nscursor = NULL; SDL_Cursor *cursor = NULL; @@ -96,92 +102,56 @@ static SDL_Cursor *Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_ if (nscursor) { cursor = SDL_calloc(1, sizeof(*cursor)); if (cursor) { - cursor->driverdata = (void *)CFBridgingRetain(nscursor); + cursor->driverdata = nscursor; + } else { + [nscursor release]; } } - return cursor; -}} + [pool release]; -/* there are .pdf files of some of the cursors we need, installed by default on macOS, but not available through NSCursor. - If we can load them ourselves, use them, otherwise fallback to something standard but not super-great. - Since these are under /System, they should be available even to sandboxed apps. */ -static NSCursor *LoadHiddenSystemCursor(NSString *cursorName, SEL fallback) -{ - NSString *cursorPath = [@"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors" stringByAppendingPathComponent:cursorName]; - NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"info.plist"]]; - /* 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"]]; - if ((image == nil) || (image.isValid == NO)) { - return [NSCursor performSelector:fallback]; - } - - if (frames > 1) { - #ifdef MAC_OS_VERSION_12_0 /* same value as deprecated symbol. */ - const NSCompositingOperation operation = NSCompositingOperationCopy; - #else - 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]; - if (cropped == nil) { - return [NSCursor performSelector:fallback]; - } - - [cropped lockFocus]; - { - const NSRect cropped_rect = NSMakeRect(0, 0, cropped_size.width, cropped_size.height); - [image drawInRect:cropped_rect fromRect:cropped_rect operation:operation fraction:1]; - } - [cropped unlockFocus]; - image = cropped; - } - - cursor = [[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 +static SDL_Cursor * +Cocoa_CreateSystemCursor(SDL_SystemCursor id) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSCursor *nscursor = NULL; SDL_Cursor *cursor = NULL; - switch(id) { + switch(id) + { case SDL_SYSTEM_CURSOR_ARROW: nscursor = [NSCursor arrowCursor]; break; case SDL_SYSTEM_CURSOR_IBEAM: nscursor = [NSCursor IBeamCursor]; break; + case SDL_SYSTEM_CURSOR_WAIT: + nscursor = [NSCursor arrowCursor]; + break; case SDL_SYSTEM_CURSOR_CROSSHAIR: nscursor = [NSCursor crosshairCursor]; break; - case SDL_SYSTEM_CURSOR_WAIT: /* !!! FIXME: this is more like WAITARROW */ - nscursor = LoadHiddenSystemCursor(@"busybutclickable", @selector(arrowCursor)); - break; - case SDL_SYSTEM_CURSOR_WAITARROW: /* !!! FIXME: this is meant to be animated */ - nscursor = LoadHiddenSystemCursor(@"busybutclickable", @selector(arrowCursor)); + case SDL_SYSTEM_CURSOR_WAITARROW: + nscursor = [NSCursor arrowCursor]; break; case SDL_SYSTEM_CURSOR_SIZENWSE: - nscursor = LoadHiddenSystemCursor(@"resizenorthwestsoutheast", @selector(closedHandCursor)); - break; case SDL_SYSTEM_CURSOR_SIZENESW: - nscursor = LoadHiddenSystemCursor(@"resizenortheastsouthwest", @selector(closedHandCursor)); + nscursor = [NSCursor closedHandCursor]; break; case SDL_SYSTEM_CURSOR_SIZEWE: - nscursor = LoadHiddenSystemCursor(@"resizeeastwest", @selector(resizeLeftRightCursor)); + nscursor = [NSCursor resizeLeftRightCursor]; break; case SDL_SYSTEM_CURSOR_SIZENS: - nscursor = LoadHiddenSystemCursor(@"resizenorthsouth", @selector(resizeUpDownCursor)); + nscursor = [NSCursor resizeUpDownCursor]; break; case SDL_SYSTEM_CURSOR_SIZEALL: - nscursor = LoadHiddenSystemCursor(@"move", @selector(closedHandCursor)); + nscursor = [NSCursor closedHandCursor]; break; case SDL_SYSTEM_CURSOR_NO: - nscursor = [NSCursor operationNotAllowedCursor]; + //nscursor = [NSCursor operationNotAllowedCursor]; break; case SDL_SYSTEM_CURSOR_HAND: nscursor = [NSCursor pointingHandCursor]; @@ -195,287 +165,164 @@ 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); + [nscursor retain]; + cursor->driverdata = nscursor; } } + [pool release]; + return cursor; -}} +} -static void Cocoa_FreeCursor(SDL_Cursor * cursor) -{ @autoreleasepool +static void +Cocoa_FreeCursor(SDL_Cursor * cursor) { - CFBridgingRelease(cursor->driverdata); + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSCursor *nscursor = (NSCursor *)cursor->driverdata; + + [nscursor release]; SDL_free(cursor); -}} -static int Cocoa_ShowCursor(SDL_Cursor * cursor) -{ @autoreleasepool + [pool release]; +} + +static int +Cocoa_ShowCursor(SDL_Cursor * cursor) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_VideoDevice *device = SDL_GetVideoDevice(); SDL_Window *window = (device ? device->windows : NULL); for (; window != NULL; window = window->next) { - SDL_WindowData *driverdata = (__bridge SDL_WindowData *)window->driverdata; + SDL_WindowData *driverdata = (SDL_WindowData *)window->driverdata; if (driverdata) { - [driverdata.nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:) - withObject:[driverdata.nswindow contentView] - waitUntilDone:NO]; + [driverdata->nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:) + withObject:[driverdata->nswindow contentView] + waitUntilDone:NO]; } } - return 0; -}} -static SDL_Window *SDL_FindWindowAtPoint(const int x, const int y) -{ - const SDL_Point pt = { x, y }; - SDL_Window *i; - for (i = SDL_GetVideoDevice()->windows; i; i = i->next) { - const SDL_Rect r = { i->x, i->y, i->w, i->h }; - if (SDL_PointInRect(&pt, &r)) { - return i; - } - } + [pool release]; - return NULL; + return 0; } -static int Cocoa_WarpMouseGlobal(int x, int y) +static void +Cocoa_WarpMouse(SDL_Window * window, int x, int y) { - CGPoint point; - SDL_Mouse *mouse = SDL_GetMouse(); - if (mouse->focus) { - SDL_WindowData *data = (__bridge SDL_WindowData *) mouse->focus->driverdata; - if ([data.listener isMovingOrFocusClickPending]) { - DLog("Postponing warp, window being moved or focused."); - [data.listener setPendingMoveX:x Y:y]; - return 0; - } + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if ([data->listener isMoving]) + { + DLog("Postponing warp, window being moved."); + [data->listener setPendingMoveX:x + Y:y]; + return; } - point = CGPointMake((float)x, (float)y); - Cocoa_HandleMouseWarp(point.x, point.y); + SDL_Mouse *mouse = SDL_GetMouse(); + CGPoint point = CGPointMake(x + (float)window->x, y + (float)window->y); - CGWarpMouseCursorPosition(point); + Cocoa_HandleMouseWarp(point.x, point.y); - /* CGWarpMouse causes a short delay by default, which is preventable by - * Calling this directly after. CGSetLocalEventsSuppressionInterval can also - * prevent it, but it's deprecated as of OS X 10.6. + /* According to the docs, this was deprecated in 10.6, but it's still + * around. The substitute requires a CGEventSource, but I'm not entirely + * sure how we'd procure the right one for this event. */ - if (!mouse->relative_mode) { - CGAssociateMouseAndMouseCursorPosition(YES); - } + CGSetLocalEventsSuppressionInterval(0.0); + CGWarpMouseCursorPosition(point); + CGSetLocalEventsSuppressionInterval(0.25); - /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our - * other implementations' APIs. Send what's appropriate. - */ if (!mouse->relative_mode) { - SDL_Window *win = SDL_FindWindowAtPoint(x, y); - SDL_SetMouseFocus(win); - if (win) { - SDL_assert(win == mouse->focus); - SDL_SendMouseMotion(win, mouse->mouseID, 0, x - win->x, y - win->y); - } + /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our + * other implementations' APIs. + */ + SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y); } - - return 0; } -static void Cocoa_WarpMouse(SDL_Window * window, int x, int y) +static int +Cocoa_SetRelativeMouseMode(SDL_bool enabled) { - Cocoa_WarpMouseGlobal(window->x + x, window->y + y); -} - -static int Cocoa_SetRelativeMouseMode(SDL_bool enabled) -{ - SDL_Window *window = SDL_GetKeyboardFocus(); - CGError result; - SDL_WindowData *data; - if (enabled) { - if (window) { - /* make sure the mouse isn't at the corner of the window, as this can confuse things if macOS thinks a window resize is happening on the first click. */ - SDL_MouseData *mousedriverdata = (SDL_MouseData*)SDL_GetMouse()->driverdata; - const CGPoint point = CGPointMake((float)(window->x + (window->w / 2)), (float)(window->y + (window->h / 2))); - if (mousedriverdata) { - mousedriverdata->justEnabledRelative = SDL_TRUE; - } - CGWarpMouseCursorPosition(point); - } - DLog("Turning on."); - result = CGAssociateMouseAndMouseCursorPosition(NO); - } else { - DLog("Turning off."); - result = CGAssociateMouseAndMouseCursorPosition(YES); - } - if (result != kCGErrorSuccess) { - return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed"); - } - - /* We will re-apply the non-relative mode when the window gets focus, if it + /* We will re-apply the relative mode when the window gets focus, if it * doesn't have focus right now. */ + SDL_Window *window = SDL_GetMouseFocus(); if (!window) { - return 0; + return 0; } - /* We will re-apply the non-relative mode when the window finishes being moved, + /* We will re-apply the relative mode when the window finishes being moved, * if it is being moved right now. */ - data = (__bridge SDL_WindowData *) window->driverdata; - if ([data.listener isMovingOrFocusClickPending]) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if ([data->listener isMoving]) { return 0; } - /* The hide/unhide calls are redundant most of the time, but they fix - * https://bugzilla.libsdl.org/show_bug.cgi?id=2550 - */ + CGError result; if (enabled) { - [NSCursor hide]; + DLog("Turning on."); + result = CGAssociateMouseAndMouseCursorPosition(NO); } else { - [NSCursor unhide]; + DLog("Turning off."); + result = CGAssociateMouseAndMouseCursorPosition(YES); + } + if (result != kCGErrorSuccess) { + return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed"); } return 0; } -static int Cocoa_CaptureMouse(SDL_Window *window) -{ - /* our Cocoa event code already tracks the mouse outside the window, - so all we have to do here is say "okay" and do what we always do. */ - return 0; -} - -static Uint32 Cocoa_GetGlobalMouseState(int *x, int *y) +void +Cocoa_InitMouse(_THIS) { - const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons]; - const NSPoint cocoaLocation = [NSEvent mouseLocation]; - Uint32 retval = 0; - - *x = (int) cocoaLocation.x; - *y = (int) (CGDisplayPixelsHigh(kCGDirectMainDisplay) - cocoaLocation.y); - - retval |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0; - retval |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0; - retval |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0; - retval |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0; - retval |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0; - - return retval; -} - -int Cocoa_InitMouse(_THIS) -{ - NSPoint location; SDL_Mouse *mouse = SDL_GetMouse(); - SDL_MouseData *driverdata = (SDL_MouseData*) SDL_calloc(1, sizeof(SDL_MouseData)); - if (driverdata == NULL) { - return SDL_OutOfMemory(); - } - mouse->driverdata = driverdata; + mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData)); + mouse->CreateCursor = Cocoa_CreateCursor; mouse->CreateSystemCursor = Cocoa_CreateSystemCursor; mouse->ShowCursor = Cocoa_ShowCursor; mouse->FreeCursor = Cocoa_FreeCursor; mouse->WarpMouse = Cocoa_WarpMouse; - mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal; mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode; - mouse->CaptureMouse = Cocoa_CaptureMouse; - mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState; SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor()); - location = [NSEvent mouseLocation]; + Cocoa_InitMouseEventTap(mouse->driverdata); + + SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata; + const NSPoint location = [NSEvent mouseLocation]; driverdata->lastMoveX = location.x; driverdata->lastMoveY = location.y; - return 0; -} - -static void Cocoa_HandleTitleButtonEvent(_THIS, NSEvent *event) -{ - SDL_Window *window; - NSWindow *nswindow = [event window]; - - /* You might land in this function before SDL_Init if showing a message box. - Don't derefence a NULL pointer if that happens. */ - if (_this == NULL) { - return; - } - - for (window = _this->windows; window; window = window->next) { - SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; - if (data && data.nswindow == nswindow) { - switch ([event type]) { - case NSEventTypeLeftMouseDown: - case NSEventTypeRightMouseDown: - case NSEventTypeOtherMouseDown: - [data.listener setFocusClickPending:[event buttonNumber]]; - break; - case NSEventTypeLeftMouseUp: - case NSEventTypeRightMouseUp: - case NSEventTypeOtherMouseUp: - [data.listener clearFocusClickPending:[event buttonNumber]]; - break; - default: - break; - } - break; - } - } } -void Cocoa_HandleMouseEvent(_THIS, NSEvent *event) +void +Cocoa_HandleMouseEvent(_THIS, NSEvent *event) { - SDL_Mouse *mouse; - SDL_MouseData *driverdata; - SDL_MouseID mouseID; - NSPoint location; - CGFloat lastMoveX, lastMoveY; - float deltaX, deltaY; - SDL_bool seenWarp; - switch ([event type]) { - case NSEventTypeMouseMoved: - case NSEventTypeLeftMouseDragged: - case NSEventTypeRightMouseDragged: - case NSEventTypeOtherMouseDragged: + switch ([event type]) + { + case NSMouseMoved: + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: break; - case NSEventTypeLeftMouseDown: - case NSEventTypeLeftMouseUp: - case NSEventTypeRightMouseDown: - case NSEventTypeRightMouseUp: - case NSEventTypeOtherMouseDown: - case NSEventTypeOtherMouseUp: - if ([event window]) { - NSRect windowRect = [[[event window] contentView] frame]; - if (!NSMouseInRect([event locationInWindow], windowRect, NO)) { - Cocoa_HandleTitleButtonEvent(_this, event); - return; - } - } - return; - default: /* Ignore any other events. */ return; } - mouse = SDL_GetMouse(); - driverdata = (SDL_MouseData*)mouse->driverdata; - if (!driverdata) { - return; /* can happen when returning from fullscreen Space on shutdown */ - } + SDL_Mouse *mouse = SDL_GetMouse(); - mouseID = mouse ? mouse->mouseID : 0; - seenWarp = driverdata->seenWarp; + SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata; + const SDL_bool seenWarp = driverdata->seenWarp; driverdata->seenWarp = NO; - if (driverdata->justEnabledRelative) { - driverdata->justEnabledRelative = SDL_FALSE; - return; // dump the first event back. - } - - location = [NSEvent mouseLocation]; - lastMoveX = driverdata->lastMoveX; - lastMoveY = driverdata->lastMoveY; + const NSPoint location = [NSEvent mouseLocation]; + const CGFloat lastMoveX = driverdata->lastMoveX; + const CGFloat lastMoveY = driverdata->lastMoveY; driverdata->lastMoveX = location.x; driverdata->lastMoveY = location.y; DLog("Last seen mouse: (%g, %g)", location.x, location.y); @@ -488,62 +335,48 @@ void Cocoa_HandleMouseEvent(_THIS, NSEvent *event) /* Ignore events that aren't inside the client area (i.e. title bar.) */ if ([event window]) { NSRect windowRect = [[[event window] contentView] frame]; - if (!NSMouseInRect([event locationInWindow], windowRect, NO)) { + if (!NSPointInRect([event locationInWindow], windowRect)) { return; } } - deltaX = [event deltaX]; - deltaY = [event deltaY]; + float deltaX = [event deltaX]; + float deltaY = [event deltaY]; - if (seenWarp) { + if (seenWarp) + { deltaX += (lastMoveX - driverdata->lastWarpX); deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - driverdata->lastWarpY); DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY); } - SDL_SendMouseMotion(mouse->focus, mouseID, 1, (int)deltaX, (int)deltaY); + SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)deltaX, (int)deltaY); } -void Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event) +void +Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event) { - SDL_MouseID mouseID; - SDL_MouseWheelDirection direction; - CGFloat x, y; SDL_Mouse *mouse = SDL_GetMouse(); - if (!mouse) { - return; - } - mouseID = mouse->mouseID; - x = -[event deltaX]; - y = [event deltaY]; - direction = SDL_MOUSEWHEEL_NORMAL; + float x = -[event deltaX]; + float y = [event deltaY]; - if ([event isDirectionInvertedFromDevice] == YES) { - direction = SDL_MOUSEWHEEL_FLIPPED; + if (x > 0) { + x += 0.9f; + } else if (x < 0) { + x -= 0.9f; } - - /* 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 (x > 0) { - x = SDL_ceil(x); - } else if (x < 0) { - x = SDL_floor(x); - } - if (y > 0) { - y = SDL_ceil(y); - } else if (y < 0) { - y = SDL_floor(y); - } + if (y > 0) { + y += 0.9f; + } else if (y < 0) { + y -= 0.9f; } - - SDL_SendMouseWheel(window, mouseID, x, y, direction); + SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y,SDL_MOUSEWHEEL_NORMAL); } -void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y) +void +Cocoa_HandleMouseWarp(CGFloat x, CGFloat y) { /* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp, * since it gets included in the next movement event. @@ -556,14 +389,16 @@ void Cocoa_HandleMouseWarp(CGFloat x, CGFloat y) DLog("(%g, %g)", x, y); } -void Cocoa_QuitMouse(_THIS) +void +Cocoa_QuitMouse(_THIS) { SDL_Mouse *mouse = SDL_GetMouse(); if (mouse) { if (mouse->driverdata) { - SDL_free(mouse->driverdata); - mouse->driverdata = NULL; + Cocoa_QuitMouseEventTap(((SDL_MouseData*)mouse->driverdata)); } + + SDL_free(mouse->driverdata); } } diff --git src/video/cocoa/SDL_cocoamousetap.h src/video/cocoa/SDL_cocoamousetap.h new file mode 100644 index 000000000..bf3e73838 --- /dev/null +++ src/video/cocoa/SDL_cocoamousetap.h @@ -0,0 +1,33 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, 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_cocoamousetap_h +#define _SDL_cocoamousetap_h + +#include "SDL_cocoamouse.h" + +extern void Cocoa_InitMouseEventTap(SDL_MouseData *driverdata); +extern void Cocoa_QuitMouseEventTap(SDL_MouseData *driverdata); + +#endif /* _SDL_cocoamousetap_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoamousetap.m src/video/cocoa/SDL_cocoamousetap.m new file mode 100644 index 000000000..9cf531cb5 --- /dev/null +++ src/video/cocoa/SDL_cocoamousetap.m @@ -0,0 +1,259 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, 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" + +#if SDL_VIDEO_DRIVER_COCOA + +#include "SDL_cocoamousetap.h" + +/* Event taps are forbidden in the Mac App Store, so we can only enable this + * code if your app doesn't need to ship through the app store. + * This code makes it so that a grabbed cursor cannot "leak" a mouse click + * past the edge of the window if moving the cursor too fast. + */ +#if SDL_MAC_NO_SANDBOX + +#include "SDL_keyboard.h" +#include "SDL_thread.h" +#include "SDL_cocoavideo.h" + +#include "../../events/SDL_mouse_c.h" + +typedef struct { + CFMachPortRef tap; + CFRunLoopRef runloop; + CFRunLoopSourceRef runloopSource; + SDL_Thread *thread; + SDL_sem *runloopStartedSemaphore; +} SDL_MouseEventTapData; + +static const CGEventMask movementEventsMask = + CGEventMaskBit(kCGEventLeftMouseDragged) + | CGEventMaskBit(kCGEventRightMouseDragged) + | CGEventMaskBit(kCGEventMouseMoved); + +static const CGEventMask allGrabbedEventsMask = + CGEventMaskBit(kCGEventLeftMouseDown) | CGEventMaskBit(kCGEventLeftMouseUp) + | CGEventMaskBit(kCGEventRightMouseDown) | CGEventMaskBit(kCGEventRightMouseUp) + | CGEventMaskBit(kCGEventOtherMouseDown) | CGEventMaskBit(kCGEventOtherMouseUp) + | CGEventMaskBit(kCGEventLeftMouseDragged) | CGEventMaskBit(kCGEventRightMouseDragged) + | CGEventMaskBit(kCGEventMouseMoved); + +static CGEventRef +Cocoa_MouseTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) +{ + SDL_MouseEventTapData *tapdata = (SDL_MouseEventTapData*)refcon; + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_Window *window = SDL_GetKeyboardFocus(); + NSWindow *nswindow; + NSRect windowRect; + CGPoint eventLocation; + + switch (type) + { + case kCGEventTapDisabledByTimeout: + case kCGEventTapDisabledByUserInput: + { + CGEventTapEnable(tapdata->tap, true); + return NULL; + } + default: + break; + } + + + if (!window || !mouse) { + return event; + } + + if (mouse->relative_mode) { + return event; + } + + if (!(window->flags & SDL_WINDOW_INPUT_GRABBED)) { + return event; + } + + /* This is the same coordinate system as Cocoa uses. */ + nswindow = ((SDL_WindowData *) window->driverdata)->nswindow; + eventLocation = CGEventGetUnflippedLocation(event); + windowRect = [nswindow contentRectForFrameRect:[nswindow frame]]; + + if (!NSPointInRect(NSPointFromCGPoint(eventLocation), windowRect)) { + + /* This is in CGs global screenspace coordinate system, which has a + * flipped Y. + */ + CGPoint newLocation = CGEventGetLocation(event); + + if (eventLocation.x < NSMinX(windowRect)) { + newLocation.x = NSMinX(windowRect); + } else if (eventLocation.x >= NSMaxX(windowRect)) { + newLocation.x = NSMaxX(windowRect) - 1.0; + } + + if (eventLocation.y < NSMinY(windowRect)) { + newLocation.y -= (NSMinY(windowRect) - eventLocation.y + 1); + } else if (eventLocation.y >= NSMaxY(windowRect)) { + newLocation.y += (eventLocation.y - NSMaxY(windowRect) + 1); + } + + CGSetLocalEventsSuppressionInterval(0); + CGWarpMouseCursorPosition(newLocation); + CGSetLocalEventsSuppressionInterval(0.25); + + if ((CGEventMaskBit(type) & movementEventsMask) == 0) { + /* For click events, we just constrain the event to the window, so + * no other app receives the click event. We can't due the same to + * movement events, since they mean that our warp cursor above + * behaves strangely. + */ + CGEventSetLocation(event, newLocation); + } + } + + return event; +} + +static void +SemaphorePostCallback(CFRunLoopTimerRef timer, void *info) +{ + SDL_SemPost((SDL_sem*)info); +} + +static int +Cocoa_MouseTapThread(void *data) +{ + SDL_MouseEventTapData *tapdata = (SDL_MouseEventTapData*)data; + + /* Create a tap. */ + CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, + kCGEventTapOptionDefault, allGrabbedEventsMask, + &Cocoa_MouseTapCallback, tapdata); + if (eventTap) { + /* Try to create a runloop source we can schedule. */ + CFRunLoopSourceRef runloopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0); + if (runloopSource) { + tapdata->tap = eventTap; + tapdata->runloopSource = runloopSource; + } else { + CFRelease(eventTap); + SDL_SemPost(tapdata->runloopStartedSemaphore); + /* TODO: Both here and in the return below, set some state in + * tapdata to indicate that initialization failed, which we should + * check in InitMouseEventTap, after we move the semaphore check + * from Quit to Init. + */ + return 1; + } + } else { + SDL_SemPost(tapdata->runloopStartedSemaphore); + return 1; + } + + tapdata->runloop = CFRunLoopGetCurrent(); + CFRunLoopAddSource(tapdata->runloop, tapdata->runloopSource, kCFRunLoopCommonModes); + CFRunLoopTimerContext context = {.info = tapdata->runloopStartedSemaphore}; + /* We signal the runloop started semaphore *after* the run loop has started, indicating it's safe to CFRunLoopStop it. */ + CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 0, 0, 0, &SemaphorePostCallback, &context); + CFRunLoopAddTimer(tapdata->runloop, timer, kCFRunLoopCommonModes); + CFRelease(timer); + + /* Run the event loop to handle events in the event tap. */ + CFRunLoopRun(); + /* Make sure this is signaled so that SDL_QuitMouseEventTap knows it can safely SDL_WaitThread for us. */ + if (SDL_SemValue(tapdata->runloopStartedSemaphore) < 1) { + SDL_SemPost(tapdata->runloopStartedSemaphore); + } + CFRunLoopRemoveSource(tapdata->runloop, tapdata->runloopSource, kCFRunLoopCommonModes); + + /* Clean up. */ + CGEventTapEnable(tapdata->tap, false); + CFRelease(tapdata->runloopSource); + CFRelease(tapdata->tap); + tapdata->runloopSource = NULL; + tapdata->tap = NULL; + + return 0; +} + +void +Cocoa_InitMouseEventTap(SDL_MouseData* driverdata) +{ + SDL_MouseEventTapData *tapdata; + driverdata->tapdata = SDL_calloc(1, sizeof(SDL_MouseEventTapData)); + tapdata = (SDL_MouseEventTapData*)driverdata->tapdata; + + tapdata->runloopStartedSemaphore = SDL_CreateSemaphore(0); + if (tapdata->runloopStartedSemaphore) { + tapdata->thread = SDL_CreateThread(&Cocoa_MouseTapThread, "Event Tap Loop", tapdata); + if (!tapdata->thread) { + SDL_DestroySemaphore(tapdata->runloopStartedSemaphore); + } + } + + if (!tapdata->thread) { + SDL_free(driverdata->tapdata); + driverdata->tapdata = NULL; + } +} + +void +Cocoa_QuitMouseEventTap(SDL_MouseData *driverdata) +{ + SDL_MouseEventTapData *tapdata = (SDL_MouseEventTapData*)driverdata->tapdata; + int status; + + /* Ensure that the runloop has been started first. + * TODO: Move this to InitMouseEventTap, check for error conditions that can + * happen in Cocoa_MouseTapThread, and fall back to the non-EventTap way of + * grabbing the mouse if it fails to Init. + */ + status = SDL_SemWaitTimeout(tapdata->runloopStartedSemaphore, 5000); + if (status > -1) { + /* Then stop it, which will cause Cocoa_MouseTapThread to return. */ + CFRunLoopStop(tapdata->runloop); + /* And then wait for Cocoa_MouseTapThread to finish cleaning up. It + * releases some of the pointers in tapdata. */ + SDL_WaitThread(tapdata->thread, &status); + } + + SDL_free(driverdata->tapdata); + driverdata->tapdata = NULL; +} + +#else /* SDL_MAC_NO_SANDBOX */ + +void +Cocoa_InitMouseEventTap(SDL_MouseData *unused) +{ +} + +void +Cocoa_QuitMouseEventTap(SDL_MouseData *driverdata) +{ +} + +#endif /* !SDL_MAC_NO_SANDBOX */ + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoaopengl.h src/video/cocoa/SDL_cocoaopengl.h index f152a726c..14fd3ab4a 100644 --- src/video/cocoa/SDL_cocoaopengl.h +++ src/video/cocoa/SDL_cocoaopengl.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,20 +20,13 @@ */ #include "../../SDL_internal.h" -#ifndef SDL_cocoaopengl_h_ -#define SDL_cocoaopengl_h_ +#ifndef _SDL_cocoaopengl_h +#define _SDL_cocoaopengl_h -#ifdef SDL_VIDEO_OPENGL_CGL +#if SDL_VIDEO_OPENGL_CGL #include "SDL_atomic.h" #import -#import - -/* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */ -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif struct SDL_GLDriverData { @@ -43,27 +36,17 @@ struct SDL_GLDriverData @interface SDLOpenGLContext : NSOpenGLContext { SDL_atomic_t dirty; SDL_Window *window; - CVDisplayLinkRef displayLink; - @public SDL_mutex *swapIntervalMutex; - @public SDL_cond *swapIntervalCond; - @public SDL_atomic_t swapIntervalSetting; - @public SDL_atomic_t swapIntervalsPassed; } - (id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share; - (void)scheduleUpdate; - (void)updateIfNeeded; -- (void)movedToNewScreen; - (void)setWindow:(SDL_Window *)window; -- (SDL_Window*)window; -- (void)explicitUpdate; -- (void)cleanup; - -@property (retain, nonatomic) NSOpenGLPixelFormat* openglPixelFormat; // macOS 10.10 has -[NSOpenGLContext pixelFormat] but this handles older OS releases. @end + /* OpenGL functions */ extern int Cocoa_GL_LoadLibrary(_THIS, const char *path); extern void *Cocoa_GL_GetProcAddress(_THIS, const char *proc); @@ -71,17 +54,15 @@ extern void Cocoa_GL_UnloadLibrary(_THIS); extern SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window); extern int Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context); +extern void Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, + int * w, int * h); extern int Cocoa_GL_SetSwapInterval(_THIS, int interval); extern int Cocoa_GL_GetSwapInterval(_THIS); -extern int Cocoa_GL_SwapWindow(_THIS, SDL_Window * window); +extern void Cocoa_GL_SwapWindow(_THIS, SDL_Window * window); extern void Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - #endif /* SDL_VIDEO_OPENGL_CGL */ -#endif /* SDL_cocoaopengl_h_ */ +#endif /* _SDL_cocoaopengl_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoaopengl.m src/video/cocoa/SDL_cocoaopengl.m index ffc59e671..c848b91cb 100644 --- src/video/cocoa/SDL_cocoaopengl.m +++ src/video/cocoa/SDL_cocoaopengl.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -22,62 +22,29 @@ /* NSOpenGL implementation of SDL OpenGL support */ -#ifdef SDL_VIDEO_OPENGL_CGL +#if SDL_VIDEO_OPENGL_CGL #include "SDL_cocoavideo.h" #include "SDL_cocoaopengl.h" -#include "SDL_cocoaopengles.h" #include #include #include -#include "SDL_hints.h" #include "SDL_loadso.h" #include "SDL_opengl.h" -#include "../../SDL_hints_c.h" #define DEFAULT_OPENGL "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib" -/* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */ -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - -/* _Nullable is available starting Xcode 7 */ -#ifdef __has_feature -#if __has_feature(nullability) -#define HAS_FEATURE_NULLABLE +#ifndef NSOpenGLPFAOpenGLProfile +#define NSOpenGLPFAOpenGLProfile 99 #endif +#ifndef NSOpenGLProfileVersionLegacy +#define NSOpenGLProfileVersionLegacy 0x1000 #endif -#ifndef HAS_FEATURE_NULLABLE -#define _Nullable +#ifndef NSOpenGLProfileVersion3_2Core +#define NSOpenGLProfileVersion3_2Core 0x3200 #endif -static SDL_bool SDL_opengl_async_dispatch = SDL_FALSE; - -static void SDLCALL -SDL_OpenGLAsyncDispatchChanged(void *userdata, const char *name, const char *oldValue, const char *hint) -{ - SDL_opengl_async_dispatch = SDL_GetStringBoolean(hint, SDL_FALSE); -} - -static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) -{ - SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *) displayLinkContext; - - /*printf("DISPLAY LINK! %u\n", (unsigned int) SDL_GetTicks()); */ - const int setting = SDL_AtomicGet(&nscontext->swapIntervalSetting); - if (setting != 0) { /* nothing to do if vsync is disabled, don't even lock */ - SDL_LockMutex(nscontext->swapIntervalMutex); - SDL_AtomicAdd(&nscontext->swapIntervalsPassed, 1); - SDL_CondSignal(nscontext->swapIntervalCond); - SDL_UnlockMutex(nscontext->swapIntervalMutex); - } - - return kCVReturnSuccess; -} - @implementation SDLOpenGLContext : NSOpenGLContext - (id)initWithFormat:(NSOpenGLPixelFormat *)format @@ -85,35 +52,12 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt { self = [super initWithFormat:format shareContext:share]; if (self) { - self.openglPixelFormat = format; SDL_AtomicSet(&self->dirty, 0); self->window = NULL; - SDL_AtomicSet(&self->swapIntervalSetting, 0); - SDL_AtomicSet(&self->swapIntervalsPassed, 0); - self->swapIntervalCond = SDL_CreateCond(); - self->swapIntervalMutex = SDL_CreateMutex(); - if (!self->swapIntervalCond || !self->swapIntervalMutex) { - return nil; - } - - /* !!! FIXME: check return values. */ - CVDisplayLinkCreateWithActiveCGDisplays(&self->displayLink); - CVDisplayLinkSetOutputCallback(self->displayLink, &DisplayLinkCallback, (__bridge void * _Nullable) self); - CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self->displayLink, [self CGLContextObj], [format CGLPixelFormatObj]); - CVDisplayLinkStart(displayLink); } - - SDL_AddHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL); return self; } -- (void)movedToNewScreen -{ - if (self->displayLink) { - CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(self->displayLink, [self CGLContextObj], [[self openglPixelFormat] CGLPixelFormatObj]); - } -} - - (void)scheduleUpdate { SDL_AtomicAdd(&self->dirty, 1); @@ -122,10 +66,10 @@ 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 = SDL_AtomicSet(&self->dirty, 0); if (value > 0) { /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */ - [self explicitUpdate]; + [super update]; } } @@ -141,10 +85,10 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (void)setWindow:(SDL_Window *)newWindow { if (self->window) { - SDL_WindowData *oldwindowdata = (__bridge SDL_WindowData *)self->window->driverdata; + SDL_WindowData *oldwindowdata = (SDL_WindowData *)self->window->driverdata; /* Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too. */ - NSMutableArray *contexts = oldwindowdata.nscontexts; + NSMutableArray *contexts = oldwindowdata->nscontexts; @synchronized (contexts) { [contexts removeObject:self]; } @@ -153,77 +97,37 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt self->window = newWindow; if (newWindow) { - SDL_WindowData *windowdata = (__bridge SDL_WindowData *)newWindow->driverdata; - NSView *contentview = windowdata.sdlContentView; + SDL_WindowData *windowdata = (SDL_WindowData *)newWindow->driverdata; /* Now sign up for scheduled updates for the new window. */ - NSMutableArray *contexts = windowdata.nscontexts; + NSMutableArray *contexts = windowdata->nscontexts; @synchronized (contexts) { [contexts addObject:self]; } - if ([self view] != contentview) { - if ([NSThread isMainThread]) { - [self setView:contentview]; - } else { - dispatch_sync(dispatch_get_main_queue(), ^{ [self setView:contentview]; }); - } + if ([self view] != [windowdata->nswindow contentView]) { + [self setView:[windowdata->nswindow contentView]]; if (self == [NSOpenGLContext currentContext]) { - [self explicitUpdate]; + [self update]; } else { [self scheduleUpdate]; } } } else { - if ([NSThread isMainThread]) { - [self setView:nil]; - } else { - dispatch_sync(dispatch_get_main_queue(), ^{ [self setView:nil]; }); - } - } -} - -- (SDL_Window*)window -{ - return self->window; -} - -- (void)explicitUpdate -{ - if ([NSThread isMainThread]) { - [super update]; - } else { - if (SDL_opengl_async_dispatch) { - dispatch_async(dispatch_get_main_queue(), ^{ [super update]; }); + [self clearDrawable]; + if (self == [NSOpenGLContext currentContext]) { + [self update]; } else { - dispatch_sync(dispatch_get_main_queue(), ^{ [super update]; }); + [self scheduleUpdate]; } } } -- (void)cleanup -{ - [self setWindow:NULL]; - - SDL_DelHintCallback(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, SDL_OpenGLAsyncDispatchChanged, NULL); - if (self->displayLink) { - CVDisplayLinkRelease(self->displayLink); - self->displayLink = nil; - } - if (self->swapIntervalCond) { - SDL_DestroyCond(self->swapIntervalCond); - self->swapIntervalCond = NULL; - } - if (self->swapIntervalMutex) { - SDL_DestroyMutex(self->swapIntervalMutex); - self->swapIntervalMutex = NULL; - } -} - @end -int Cocoa_GL_LoadLibrary(_THIS, const char *path) +int +Cocoa_GL_LoadLibrary(_THIS, const char *path) { /* Load the OpenGL library */ if (path == NULL) { @@ -241,66 +145,55 @@ int Cocoa_GL_LoadLibrary(_THIS, const char *path) return 0; } -void *Cocoa_GL_GetProcAddress(_THIS, const char *proc) +void * +Cocoa_GL_GetProcAddress(_THIS, const char *proc) { return SDL_LoadFunction(_this->gl_config.dll_handle, proc); } -void Cocoa_GL_UnloadLibrary(_THIS) +void +Cocoa_GL_UnloadLibrary(_THIS) { SDL_UnloadObject(_this->gl_config.dll_handle); _this->gl_config.dll_handle = NULL; } -SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) -{ @autoreleasepool +SDL_GLContext +Cocoa_GL_CreateContext(_THIS, SDL_Window * window) { + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + NSAutoreleasePool *pool; SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata; NSOpenGLPixelFormatAttribute attr[32]; NSOpenGLPixelFormat *fmt; SDLOpenGLContext *context; - SDL_GLContext sdlcontext; NSOpenGLContext *share_context = nil; int i = 0; const char *glversion; int glversion_major; int glversion_minor; - NSOpenGLPixelFormatAttribute profile; - int interval; if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { -#ifdef SDL_VIDEO_OPENGL_EGL - /* Switch to EGL based functions */ - Cocoa_GL_UnloadLibrary(_this); - _this->GL_LoadLibrary = Cocoa_GLES_LoadLibrary; - _this->GL_GetProcAddress = Cocoa_GLES_GetProcAddress; - _this->GL_UnloadLibrary = Cocoa_GLES_UnloadLibrary; - _this->GL_CreateContext = Cocoa_GLES_CreateContext; - _this->GL_MakeCurrent = Cocoa_GLES_MakeCurrent; - _this->GL_SetSwapInterval = Cocoa_GLES_SetSwapInterval; - _this->GL_GetSwapInterval = Cocoa_GLES_GetSwapInterval; - _this->GL_SwapWindow = Cocoa_GLES_SwapWindow; - _this->GL_DeleteContext = Cocoa_GLES_DeleteContext; - - if (Cocoa_GLES_LoadLibrary(_this, NULL) != 0) { - return NULL; - } - return Cocoa_GLES_CreateContext(_this, window); -#else - SDL_SetError("SDL not configured with EGL support"); + SDL_SetError ("OpenGL ES is not supported on this platform"); + return NULL; + } + if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) && (data->osversion < 0x1070)) { + SDL_SetError ("OpenGL Core Profile is not supported on this platform version"); return NULL; -#endif } - attr[i++] = NSOpenGLPFAAllowOfflineRenderers; + pool = [[NSAutoreleasePool alloc] init]; - profile = NSOpenGLProfileVersionLegacy; - if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) { - profile = NSOpenGLProfileVersion3_2Core; + /* specify a profile if we're on Lion (10.7) or later. */ + if (data->osversion >= 0x1070) { + NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy; + if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) { + profile = NSOpenGLProfileVersion3_2Core; + } + attr[i++] = NSOpenGLPFAOpenGLProfile; + attr[i++] = profile; } - attr[i++] = NSOpenGLPFAOpenGLProfile; - attr[i++] = profile; attr[i++] = NSOpenGLPFAColorSize; attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8; @@ -339,9 +232,6 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) attr[i++] = _this->gl_config.multisamplesamples; attr[i++] = NSOpenGLPFANoRecovery; } - if (_this->gl_config.floatbuffers) { - attr[i++] = NSOpenGLPFAColorFloat; - } if (_this->gl_config.accelerated >= 0) { if (_this->gl_config.accelerated) { @@ -359,28 +249,28 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; if (fmt == nil) { SDL_SetError("Failed creating OpenGL pixel format"); + [pool release]; return NULL; } if (_this->gl_config.share_with_current_context) { - share_context = (__bridge NSOpenGLContext*)SDL_GL_GetCurrentContext(); + share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); } context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context]; + [fmt release]; + if (context == nil) { SDL_SetError("Failed creating OpenGL context"); + [pool release]; return NULL; } - sdlcontext = (SDL_GLContext)CFBridgingRetain(context); - - /* vsync is handled separately by synchronizing with a display link. */ - interval = 0; - [context setValues:&interval forParameter:NSOpenGLCPSwapInterval]; + [pool release]; - if (Cocoa_GL_MakeCurrent(_this, window, sdlcontext) < 0) { - SDL_GL_DeleteContext(sdlcontext); + if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) { + Cocoa_GL_DeleteContext(_this, context); SDL_SetError("Failed making OpenGL context current"); return NULL; } @@ -394,27 +284,27 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString"); if (!glGetStringFunc) { - SDL_GL_DeleteContext(sdlcontext); + Cocoa_GL_DeleteContext(_this, context); SDL_SetError ("Failed getting OpenGL glGetString entry point"); return NULL; } glversion = (const char *)glGetStringFunc(GL_VERSION); if (glversion == NULL) { - SDL_GL_DeleteContext(sdlcontext); + Cocoa_GL_DeleteContext(_this, context); SDL_SetError ("Failed getting OpenGL context version"); return NULL; } if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) { - SDL_GL_DeleteContext(sdlcontext); + Cocoa_GL_DeleteContext(_this, context); SDL_SetError ("Failed parsing OpenGL context version"); return NULL; } if ((glversion_major < _this->gl_config.major_version) || ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) { - SDL_GL_DeleteContext(sdlcontext); + Cocoa_GL_DeleteContext(_this, context); SDL_SetError ("Failed creating OpenGL context at version requested"); return NULL; } @@ -425,118 +315,127 @@ SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window) /*_this->gl_config.major_version = glversion_major;*/ /*_this->gl_config.minor_version = glversion_minor;*/ } - return sdlcontext; -}} + return context; +} -int Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) -{ @autoreleasepool +int +Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) { + NSAutoreleasePool *pool; + + pool = [[NSAutoreleasePool alloc] init]; + if (context) { - SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *)context; - if ([nscontext window] != window) { - [nscontext setWindow:window]; - [nscontext updateIfNeeded]; - } + SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context; + [nscontext setWindow:window]; + [nscontext updateIfNeeded]; [nscontext makeCurrentContext]; } else { [NSOpenGLContext clearCurrentContext]; } + [pool release]; return 0; -}} +} -int Cocoa_GL_SetSwapInterval(_THIS, int interval) -{ @autoreleasepool +void +Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h) { - SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *) SDL_GL_GetCurrentContext(); + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; + NSView *contentView = [windata->nswindow contentView]; + NSRect viewport = [contentView bounds]; + + /* This gives us the correct viewport for a Retina-enabled view, only + * supported on 10.7+. */ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) { + viewport = [contentView convertRectToBacking:viewport]; + } +#endif + + if (w) { + *w = viewport.size.width; + } + + if (h) { + *h = viewport.size.height; + } +} + +int +Cocoa_GL_SetSwapInterval(_THIS, int interval) +{ + NSAutoreleasePool *pool; + NSOpenGLContext *nscontext; + GLint value; int status; - if (nscontext == nil) { - status = SDL_SetError("No current OpenGL context"); - } else { - SDL_LockMutex(nscontext->swapIntervalMutex); - SDL_AtomicSet(&nscontext->swapIntervalsPassed, 0); - SDL_AtomicSet(&nscontext->swapIntervalSetting, interval); - SDL_UnlockMutex(nscontext->swapIntervalMutex); + if (interval < 0) { /* no extension for this on Mac OS X at the moment. */ + return SDL_SetError("Late swap tearing currently unsupported"); + } + + pool = [[NSAutoreleasePool alloc] init]; + + nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); + if (nscontext != nil) { + value = interval; + [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval]; status = 0; + } else { + status = SDL_SetError("No current OpenGL context"); } + [pool release]; return status; -}} +} -int Cocoa_GL_GetSwapInterval(_THIS) -{ @autoreleasepool +int +Cocoa_GL_GetSwapInterval(_THIS) { - SDLOpenGLContext* nscontext = (__bridge SDLOpenGLContext*)SDL_GL_GetCurrentContext(); - return nscontext ? SDL_AtomicGet(&nscontext->swapIntervalSetting) : 0; -}} + NSAutoreleasePool *pool; + NSOpenGLContext *nscontext; + GLint value; + int status = 0; -int Cocoa_GL_SwapWindow(_THIS, SDL_Window * window) -{ @autoreleasepool -{ - SDLOpenGLContext* nscontext = (__bridge SDLOpenGLContext*)SDL_GL_GetCurrentContext(); - SDL_VideoData *videodata = (__bridge SDL_VideoData *) _this->driverdata; - const int setting = SDL_AtomicGet(&nscontext->swapIntervalSetting); - - if (setting == 0) { - /* nothing to do if vsync is disabled, don't even lock */ - } else if (setting < 0) { /* late swap tearing */ - SDL_LockMutex(nscontext->swapIntervalMutex); - while (SDL_AtomicGet(&nscontext->swapIntervalsPassed) == 0) { - SDL_CondWait(nscontext->swapIntervalCond, nscontext->swapIntervalMutex); - } - SDL_AtomicSet(&nscontext->swapIntervalsPassed, 0); - SDL_UnlockMutex(nscontext->swapIntervalMutex); - } else { - SDL_LockMutex(nscontext->swapIntervalMutex); - do { /* always wait here so we know we just hit a swap interval. */ - SDL_CondWait(nscontext->swapIntervalCond, nscontext->swapIntervalMutex); - } while ((SDL_AtomicGet(&nscontext->swapIntervalsPassed) % setting) != 0); - SDL_AtomicSet(&nscontext->swapIntervalsPassed, 0); - SDL_UnlockMutex(nscontext->swapIntervalMutex); + pool = [[NSAutoreleasePool alloc] init]; + + nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); + if (nscontext != nil) { + [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval]; + status = (int)value; } - /*{ 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); }*/ + [pool release]; + return status; +} + +void +Cocoa_GL_SwapWindow(_THIS, SDL_Window * window) +{ + NSAutoreleasePool *pool; + + pool = [[NSAutoreleasePool alloc] init]; - /* on 10.14 ("Mojave") and later, this deadlocks if two contexts in two - threads try to swap at the same time, so put a mutex around it. */ - SDL_LockMutex(videodata.swaplock); + SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext(); [nscontext flushBuffer]; [nscontext updateIfNeeded]; - SDL_UnlockMutex(videodata.swaplock); - return 0; -}} -static void DispatchedDeleteContext(SDL_GLContext context) -{ - @autoreleasepool { - SDLOpenGLContext *nscontext = (__bridge SDLOpenGLContext *)context; - [nscontext cleanup]; - CFRelease(context); - } + [pool release]; } -void Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context) +void +Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context) { - if ([NSThread isMainThread]) { - DispatchedDeleteContext(context); - } else { - if (SDL_opengl_async_dispatch) { - dispatch_async(dispatch_get_main_queue(), ^{ - DispatchedDeleteContext(context); - }); - } else { - dispatch_sync(dispatch_get_main_queue(), ^{ - DispatchedDeleteContext(context); - }); - } - } -} + NSAutoreleasePool *pool; + SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context; -/* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */ -#ifdef __clang__ -#pragma clang diagnostic pop -#endif + pool = [[NSAutoreleasePool alloc] init]; + + [nscontext setWindow:NULL]; + [nscontext release]; + + [pool release]; +} #endif /* SDL_VIDEO_OPENGL_CGL */ diff --git src/video/cocoa/SDL_cocoashape.h src/video/cocoa/SDL_cocoashape.h index 82b8658b3..0ab9980e3 100644 --- src/video/cocoa/SDL_cocoashape.h +++ src/video/cocoa/SDL_cocoashape.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -21,24 +21,23 @@ #include "../../SDL_internal.h" -#ifndef SDL_cocoashape_h_ -#define SDL_cocoashape_h_ +#ifndef _SDL_cocoashape_h +#define _SDL_cocoashape_h #include "SDL_stdinc.h" #include "SDL_video.h" #include "SDL_shape.h" #include "../SDL_shape_internals.h" -@interface SDL_ShapeData : NSObject - @property (nonatomic) NSGraphicsContext* context; - @property (nonatomic) SDL_bool saved; - @property (nonatomic) SDL_ShapeTree* shape; -@end +typedef struct { + NSGraphicsContext* context; + SDL_bool saved; + + SDL_ShapeTree* shape; +} SDL_ShapeData; extern SDL_WindowShaper* Cocoa_CreateShaper(SDL_Window* window); extern int Cocoa_SetWindowShape(SDL_WindowShaper *shaper,SDL_Surface *shape,SDL_WindowShapeMode *shape_mode); extern int Cocoa_ResizeWindowShape(SDL_Window *window); -#endif /* SDL_cocoashape_h_ */ - -/* vi: set ts=4 sw=4 expandtab: */ +#endif diff --git src/video/cocoa/SDL_cocoashape.m src/video/cocoa/SDL_cocoashape.m index e1421ee1b..b490a69d0 100644 --- src/video/cocoa/SDL_cocoashape.m +++ src/video/cocoa/SDL_cocoashape.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -18,109 +18,95 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ + #include "../../SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_COCOA +#if SDL_VIDEO_DRIVER_COCOA #include "SDL_cocoavideo.h" #include "SDL_shape.h" #include "SDL_cocoashape.h" #include "../SDL_sysvideo.h" +#include "SDL_assert.h" -@implementation SDL_ShapeData -@end - -@interface SDL_CocoaClosure : NSObject - @property (nonatomic) NSView* view; - @property (nonatomic) NSBezierPath* path; - @property (nonatomic) SDL_Window* window; -@end - -@implementation SDL_CocoaClosure -@end - -SDL_WindowShaper *Cocoa_CreateShaper(SDL_Window* window) -{ @autoreleasepool -{ - SDL_WindowShaper* result; - SDL_ShapeData* data; - int resized_properly; - SDL_WindowData* windata = (__bridge SDL_WindowData*)window->driverdata; - - result = (SDL_WindowShaper *)SDL_malloc(sizeof(SDL_WindowShaper)); - if (!result) { - SDL_OutOfMemory(); - return NULL; - } - - [windata.nswindow setOpaque:NO]; +SDL_WindowShaper* +Cocoa_CreateShaper(SDL_Window* window) { + SDL_WindowData* windata = (SDL_WindowData*)window->driverdata; + [windata->nswindow setOpaque:NO]; - [windata.nswindow setStyleMask:NSWindowStyleMaskBorderless]; + if ([windata->nswindow respondsToSelector:@selector(setStyleMask:)]) { + //[windata->nswindow setStyleMask:NSBorderlessWindowMask]; + } + SDL_WindowShaper* result = result = malloc(sizeof(SDL_WindowShaper)); result->window = window; result->mode.mode = ShapeModeDefault; result->mode.parameters.binarizationCutoff = 1; result->userx = result->usery = 0; window->shaper = result; - data = [[SDL_ShapeData alloc] init]; - data.context = [windata.nswindow graphicsContext]; - data.saved = SDL_FALSE; - data.shape = NULL; - - /* TODO: There's no place to release this... */ - result->driverdata = (void*) CFBridgingRetain(data); + SDL_ShapeData* data = malloc(sizeof(SDL_ShapeData)); + result->driverdata = data; + data->context = [windata->nswindow graphicsContext]; + data->saved = SDL_FALSE; + data->shape = NULL; - resized_properly = Cocoa_ResizeWindowShape(window); + int resized_properly = Cocoa_ResizeWindowShape(window); SDL_assert(resized_properly == 0); return result; -}} +} + +typedef struct { + NSView* view; + NSBezierPath* path; + SDL_Window* window; +} SDL_CocoaClosure; -static void ConvertRects(SDL_ShapeTree* tree, void* closure) -{ - SDL_CocoaClosure* data = (__bridge SDL_CocoaClosure*)closure; +void +ConvertRects(SDL_ShapeTree* tree,void* closure) { + SDL_CocoaClosure* data = (SDL_CocoaClosure*)closure; if(tree->kind == OpaqueShape) { - NSRect rect = NSMakeRect(tree->data.shape.x, data.window->h - tree->data.shape.y, tree->data.shape.w, tree->data.shape.h); - [data.path appendBezierPathWithRect:[data.view convertRect:rect toView:nil]]; + NSRect rect = NSMakeRect(tree->data.shape.x,data->window->h - tree->data.shape.y,tree->data.shape.w,tree->data.shape.h); + [data->path appendBezierPathWithRect:[data->view convertRect:rect toView:nil]]; } } -int Cocoa_SetWindowShape(SDL_WindowShaper *shaper, SDL_Surface *shape, SDL_WindowShapeMode *shape_mode) -{ @autoreleasepool -{ - SDL_ShapeData* data = (__bridge SDL_ShapeData*)shaper->driverdata; - SDL_WindowData* windata = (__bridge SDL_WindowData*)shaper->window->driverdata; - SDL_CocoaClosure* closure; - if(data.saved == SDL_TRUE) { - [data.context restoreGraphicsState]; - data.saved = SDL_FALSE; +int +Cocoa_SetWindowShape(SDL_WindowShaper *shaper,SDL_Surface *shape,SDL_WindowShapeMode *shape_mode) { + SDL_ShapeData* data = (SDL_ShapeData*)shaper->driverdata; + SDL_WindowData* windata = (SDL_WindowData*)shaper->window->driverdata; + SDL_CocoaClosure closure; + NSAutoreleasePool *pool = NULL; + if(data->saved == SDL_TRUE) { + [data->context restoreGraphicsState]; + data->saved = SDL_FALSE; } - /*[data.context saveGraphicsState];*/ - /*data.saved = SDL_TRUE;*/ - [NSGraphicsContext setCurrentContext:data.context]; + /*[data->context saveGraphicsState];*/ + /*data->saved = SDL_TRUE;*/ + [NSGraphicsContext setCurrentContext:data->context]; [[NSColor clearColor] set]; - NSRectFill([windata.sdlContentView frame]); - data.shape = SDL_CalculateShapeTree(*shape_mode, shape); - - closure = [[SDL_CocoaClosure alloc] init]; + NSRectFill([[windata->nswindow contentView] frame]); + data->shape = SDL_CalculateShapeTree(*shape_mode,shape); - closure.view = windata.sdlContentView; - closure.path = [NSBezierPath bezierPath]; + pool = [[NSAutoreleasePool alloc] init]; + closure.view = [windata->nswindow contentView]; + closure.path = [[NSBezierPath bezierPath] init]; closure.window = shaper->window; - SDL_TraverseShapeTree(data.shape, &ConvertRects, (__bridge void*)closure); + SDL_TraverseShapeTree(data->shape,&ConvertRects,&closure); [closure.path addClip]; + [pool release]; return 0; -}} +} -int Cocoa_ResizeWindowShape(SDL_Window *window) -{ @autoreleasepool { - SDL_ShapeData* data = (__bridge SDL_ShapeData*)window->shaper->driverdata; +int +Cocoa_ResizeWindowShape(SDL_Window *window) { + SDL_ShapeData* data = window->shaper->driverdata; SDL_assert(data != NULL); return 0; -}} +} #endif /* SDL_VIDEO_DRIVER_COCOA */ diff --git src/video/cocoa/SDL_cocoavideo.h src/video/cocoa/SDL_cocoavideo.h index d7663f90c..757d8e55a 100644 --- src/video/cocoa/SDL_cocoavideo.h +++ src/video/cocoa/SDL_cocoavideo.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,13 +20,18 @@ */ #include "../../SDL_internal.h" -#ifndef SDL_cocoavideo_h_ -#define SDL_cocoavideo_h_ +#ifndef _SDL_cocoavideo_h +#define _SDL_cocoavideo_h #include "SDL_opengl.h" +#if defined(__ALTIVEC__) && !defined(MAC_OS_X_VERSION_10_5) +/* to cricumvent a bug in Mac OS X 10.4 SDK */ +#define vector __vector +#include +#undef vector +#endif #include -#include #include #include "SDL_keycode.h" @@ -40,82 +45,29 @@ #include "SDL_cocoaopengl.h" #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 +#if !defined(MAC_OS_X_VERSION_10_5) +typedef long int NSInteger; +typedef unsigned int NSUInteger; #endif /* Private display data */ @class SDLTranslatorResponder; -@interface SDL_VideoData : NSObject - @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) NSInteger clipboard_count; - @property (nonatomic) IOPMAssertionID screensaver_assertion; - @property (nonatomic) SDL_mutex *swaplock; -@end +typedef struct SDL_VideoData +{ + SInt32 osversion; + int allow_spaces; + unsigned int modifierFlags; + void *key_layout; + SDLTranslatorResponder *fieldEdit; + NSInteger clipboard_count; + Uint32 screensaver_activity; +} SDL_VideoData; /* Utility functions */ extern NSImage * Cocoa_CreateImage(SDL_Surface * surface); -/* Fix build with the 10.11 SDK */ -#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 -#define NSEventSubtypeMouseEvent NSMouseEventSubtype -#endif - -#endif /* SDL_cocoavideo_h_ */ +#endif /* _SDL_cocoavideo_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoavideo.m src/video/cocoa/SDL_cocoavideo.m index 811e85dbb..04de3ff7e 100644 --- src/video/cocoa/SDL_cocoavideo.m +++ src/video/cocoa/SDL_cocoavideo.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,24 +20,21 @@ */ #include "../../SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_COCOA +#if SDL_VIDEO_DRIVER_COCOA -#if !__has_feature(objc_arc) -#error SDL must be built with Objective-C ARC (automatic reference counting) enabled +#if defined(__APPLE__) && defined(__POWERPC__) && !defined(__APPLE_ALTIVEC__) +#include +#undef bool +#undef vector +#undef pixel #endif #include "SDL.h" #include "SDL_endian.h" #include "SDL_cocoavideo.h" #include "SDL_cocoashape.h" -#include "SDL_cocoavulkan.h" -#include "SDL_cocoametalview.h" -#include "SDL_cocoaopengles.h" #include "SDL_cocoamessagebox.h" - -@implementation SDL_VideoData - -@end +#include "SDL_assert.h" /* Initialization/Query functions */ static int Cocoa_VideoInit(_THIS); @@ -45,18 +42,21 @@ static void Cocoa_VideoQuit(_THIS); /* Cocoa driver bootstrap functions */ -static void Cocoa_DeleteDevice(SDL_VideoDevice * device) -{ @autoreleasepool +static int +Cocoa_Available(void) { - if (device->wakeup_lock) { - SDL_DestroyMutex(device->wakeup_lock); - } - CFBridgingRelease(device->driverdata); + return (1); +} + +static void +Cocoa_DeleteDevice(SDL_VideoDevice * device) +{ + SDL_free(device->driverdata); SDL_free(device); -}} +} -static SDL_VideoDevice *Cocoa_CreateDevice(void) -{ @autoreleasepool +static SDL_VideoDevice * +Cocoa_CreateDevice(int devindex) { SDL_VideoDevice *device; SDL_VideoData *data; @@ -66,30 +66,27 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void) /* Initialize all variables that we clean on shutdown */ device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice)); if (device) { - data = [[SDL_VideoData alloc] init]; + data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData)); } else { - data = nil; + data = NULL; } if (!data) { SDL_OutOfMemory(); SDL_free(device); return NULL; } - device->driverdata = (void *)CFBridgingRetain(data); - device->wakeup_lock = SDL_CreateMutex(); + device->driverdata = data; + + /* Find out what version of Mac OS X we're running */ + Gestalt(gestaltSystemVersion, &data->osversion); /* Set the function pointers */ device->VideoInit = Cocoa_VideoInit; device->VideoQuit = Cocoa_VideoQuit; device->GetDisplayBounds = Cocoa_GetDisplayBounds; - device->GetDisplayUsableBounds = Cocoa_GetDisplayUsableBounds; - device->GetDisplayDPI = Cocoa_GetDisplayDPI; device->GetDisplayModes = Cocoa_GetDisplayModes; device->SetDisplayMode = Cocoa_SetDisplayMode; device->PumpEvents = Cocoa_PumpEvents; - device->WaitEventTimeout = Cocoa_WaitEventTimeout; - device->SendWakeupEvent = Cocoa_SendWakeupEvent; - device->SuspendScreenSaver = Cocoa_SuspendScreenSaver; device->CreateSDLWindow = Cocoa_CreateWindow; device->CreateSDLWindowFrom = Cocoa_CreateWindowFrom; @@ -99,8 +96,6 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void) device->SetWindowSize = Cocoa_SetWindowSize; device->SetWindowMinimumSize = Cocoa_SetWindowMinimumSize; device->SetWindowMaximumSize = Cocoa_SetWindowMaximumSize; - device->SetWindowOpacity = Cocoa_SetWindowOpacity; - device->GetWindowSizeInPixels = Cocoa_GetWindowSizeInPixels; device->ShowWindow = Cocoa_ShowWindow; device->HideWindow = Cocoa_HideWindow; device->RaiseWindow = Cocoa_RaiseWindow; @@ -108,61 +103,28 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void) device->MinimizeWindow = Cocoa_MinimizeWindow; device->RestoreWindow = Cocoa_RestoreWindow; device->SetWindowBordered = Cocoa_SetWindowBordered; - device->SetWindowResizable = Cocoa_SetWindowResizable; - device->SetWindowAlwaysOnTop = Cocoa_SetWindowAlwaysOnTop; device->SetWindowFullscreen = Cocoa_SetWindowFullscreen; device->SetWindowGammaRamp = Cocoa_SetWindowGammaRamp; device->GetWindowGammaRamp = Cocoa_GetWindowGammaRamp; - device->GetWindowICCProfile = Cocoa_GetWindowICCProfile; - device->GetWindowDisplayIndex = Cocoa_GetWindowDisplayIndex; - device->SetWindowMouseRect = Cocoa_SetWindowMouseRect; - device->SetWindowMouseGrab = Cocoa_SetWindowMouseGrab; - device->SetWindowKeyboardGrab = Cocoa_SetWindowKeyboardGrab; + device->SetWindowMouseGrab = Cocoa_SetWindowGrab; device->DestroyWindow = Cocoa_DestroyWindow; device->GetWindowWMInfo = Cocoa_GetWindowWMInfo; - device->SetWindowHitTest = Cocoa_SetWindowHitTest; - device->AcceptDragAndDrop = Cocoa_AcceptDragAndDrop; - device->FlashWindow = Cocoa_FlashWindow; device->shape_driver.CreateShaper = Cocoa_CreateShaper; device->shape_driver.SetWindowShape = Cocoa_SetWindowShape; device->shape_driver.ResizeWindowShape = Cocoa_ResizeWindowShape; -#ifdef SDL_VIDEO_OPENGL_CGL +#if SDL_VIDEO_OPENGL_CGL device->GL_LoadLibrary = Cocoa_GL_LoadLibrary; device->GL_GetProcAddress = Cocoa_GL_GetProcAddress; device->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary; device->GL_CreateContext = Cocoa_GL_CreateContext; device->GL_MakeCurrent = Cocoa_GL_MakeCurrent; + device->GL_GetDrawableSize = Cocoa_GL_GetDrawableSize; device->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval; device->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval; device->GL_SwapWindow = Cocoa_GL_SwapWindow; device->GL_DeleteContext = Cocoa_GL_DeleteContext; -#elif defined(SDL_VIDEO_OPENGL_EGL) - device->GL_LoadLibrary = Cocoa_GLES_LoadLibrary; - device->GL_GetProcAddress = Cocoa_GLES_GetProcAddress; - device->GL_UnloadLibrary = Cocoa_GLES_UnloadLibrary; - device->GL_CreateContext = Cocoa_GLES_CreateContext; - device->GL_MakeCurrent = Cocoa_GLES_MakeCurrent; - device->GL_SetSwapInterval = Cocoa_GLES_SetSwapInterval; - device->GL_GetSwapInterval = Cocoa_GLES_GetSwapInterval; - device->GL_SwapWindow = Cocoa_GLES_SwapWindow; - device->GL_DeleteContext = Cocoa_GLES_DeleteContext; -#endif - -#ifdef SDL_VIDEO_VULKAN - device->Vulkan_LoadLibrary = Cocoa_Vulkan_LoadLibrary; - device->Vulkan_UnloadLibrary = Cocoa_Vulkan_UnloadLibrary; - device->Vulkan_GetInstanceExtensions = Cocoa_Vulkan_GetInstanceExtensions; - device->Vulkan_CreateSurface = Cocoa_Vulkan_CreateSurface; - device->Vulkan_GetDrawableSize = Cocoa_Vulkan_GetDrawableSize; -#endif - -#ifdef SDL_VIDEO_METAL - device->Metal_CreateView = Cocoa_Metal_CreateView; - device->Metal_DestroyView = Cocoa_Metal_DestroyView; - device->Metal_GetLayer = Cocoa_Metal_GetLayer; - device->Metal_GetDrawableSize = Cocoa_Metal_GetDrawableSize; #endif device->StartTextInput = Cocoa_StartTextInput; @@ -176,50 +138,40 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void) device->free = Cocoa_DeleteDevice; return device; -}} +} VideoBootStrap COCOA_bootstrap = { "cocoa", "SDL Cocoa video driver", - Cocoa_CreateDevice, - Cocoa_ShowMessageBox + Cocoa_CreateDevice,Cocoa_ShowMessageBox }; -int Cocoa_VideoInit(_THIS) -{ @autoreleasepool +int +Cocoa_VideoInit(_THIS) { - SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; Cocoa_InitModes(_this); Cocoa_InitKeyboard(_this); - if (Cocoa_InitMouse(_this) < 0) { - return -1; - } + Cocoa_InitMouse(_this); - data.allow_spaces = SDL_GetHintBoolean(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, SDL_TRUE); - data.trackpad_is_touch_only = SDL_GetHintBoolean(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, SDL_FALSE); - - data.swaplock = SDL_CreateMutex(); - if (!data.swaplock) { - return -1; - } + const char *hint = SDL_GetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES); + data->allow_spaces = ( (data->osversion >= 0x1070) && (!hint || (*hint != '0')) ); return 0; -}} +} -void Cocoa_VideoQuit(_THIS) -{ @autoreleasepool +void +Cocoa_VideoQuit(_THIS) { - SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata; Cocoa_QuitModes(_this); Cocoa_QuitKeyboard(_this); Cocoa_QuitMouse(_this); - SDL_DestroyMutex(data.swaplock); - data.swaplock = NULL; -}} +} /* This function assumes that it's called from within an autorelease pool */ -NSImage *Cocoa_CreateImage(SDL_Surface * surface) +NSImage * +Cocoa_CreateImage(SDL_Surface * surface) { SDL_Surface *converted; NSBitmapImageRep *imgrep; @@ -227,12 +179,18 @@ NSImage *Cocoa_CreateImage(SDL_Surface * surface) int i; NSImage *img; - converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA32, 0); + converted = SDL_ConvertSurfaceFormat(surface, +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + SDL_PIXELFORMAT_RGBA8888, +#else + SDL_PIXELFORMAT_ABGR8888, +#endif + 0); if (!converted) { return nil; } - imgrep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL + imgrep = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL pixelsWide: converted->w pixelsHigh: converted->h bitsPerSample: 8 @@ -241,7 +199,7 @@ NSImage *Cocoa_CreateImage(SDL_Surface * surface) isPlanar: NO colorSpaceName: NSDeviceRGBColorSpace bytesPerRow: converted->pitch - bitsPerPixel: converted->format->BitsPerPixel]; + bitsPerPixel: converted->format->BitsPerPixel] autorelease]; if (imgrep == nil) { SDL_FreeSurface(converted); return nil; @@ -261,7 +219,7 @@ NSImage *Cocoa_CreateImage(SDL_Surface * surface) pixels += 4; } - img = [[NSImage alloc] initWithSize: NSMakeSize(surface->w, surface->h)]; + img = [[[NSImage alloc] initWithSize: NSMakeSize(surface->w, surface->h)] autorelease]; if (img != nil) { [img addRepresentation: imgrep]; } @@ -273,22 +231,11 @@ NSImage *Cocoa_CreateImage(SDL_Surface * surface) * * This doesn't really have aything to do with the interfaces of the SDL video * subsystem, but we need to stuff this into an Objective-C source code file. - * - * NOTE: This is copypasted in src/video/uikit/SDL_uikitvideo.m! Be sure both - * versions remain identical! */ -void SDL_NSLog(const char *prefix, const char *text) +void SDL_NSLog(const char *text) { - @autoreleasepool { - NSString *nsText = [NSString stringWithUTF8String:text]; - if (prefix) { - NSString *nsPrefix = [NSString stringWithUTF8String:prefix]; - NSLog(@"%@: %@", nsPrefix, nsText); - } else { - NSLog(@"%@", nsText); - } - } + NSLog(@"%s", text); } #endif /* SDL_VIDEO_DRIVER_COCOA */ diff --git src/video/cocoa/SDL_cocoawindow.h src/video/cocoa/SDL_cocoawindow.h index 7ef0bc036..6c57c9563 100644 --- src/video/cocoa/SDL_cocoawindow.h +++ src/video/cocoa/SDL_cocoawindow.h @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,16 +20,12 @@ */ #include "../../SDL_internal.h" -#ifndef SDL_cocoawindow_h_ -#define SDL_cocoawindow_h_ +#ifndef _SDL_cocoawindow_h +#define _SDL_cocoawindow_h #import -#ifdef SDL_VIDEO_OPENGL_EGL -#include "../SDL_egl_c.h" -#endif - -@class SDL_WindowData; +typedef struct SDL_WindowData SDL_WindowData; typedef enum { @@ -39,11 +35,12 @@ typedef enum PENDING_OPERATION_MINIMIZE } PendingWindowOperation; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 @interface Cocoa_WindowListener : NSResponder { - /* 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; +#else +@interface Cocoa_WindowListener : NSResponder { +#endif + SDL_WindowData *_data; BOOL observingVisible; BOOL wasCtrlLeft; BOOL wasVisible; @@ -51,13 +48,9 @@ typedef enum BOOL inFullscreenTransition; PendingWindowOperation pendingWindowOperation; BOOL isMoving; - NSInteger focusClickPending; int pendingWindowWarpX, pendingWindowWarpY; - BOOL isDragAreaRunning; - NSTimer *liveResizeTimer; } --(BOOL) isTouchFromTrackpad:(NSEvent *)theEvent; -(void) listen:(SDL_WindowData *) data; -(void) pauseVisibleObservation; -(void) resumeVisibleObservation; @@ -68,36 +61,25 @@ typedef enum -(void) close; -(BOOL) isMoving; --(BOOL) isMovingOrFocusClickPending; --(void) setFocusClickPending:(NSInteger) button; --(void) clearFocusClickPending:(NSInteger) button; -(void) setPendingMoveX:(int)x Y:(int)y; -(void) windowDidFinishMoving; --(void) onMovingOrFocusClickPendingStateCleared; /* Window delegate functionality */ -(BOOL) windowShouldClose:(id) sender; -(void) windowDidExpose:(NSNotification *) aNotification; --(void) onLiveResizeTimerFire:(id) sender; --(void) windowWillStartLiveResize:(NSNotification *)aNotification; --(void) windowDidEndLiveResize:(NSNotification *)aNotification; -(void) windowDidMove:(NSNotification *) aNotification; -(void) windowDidResize:(NSNotification *) aNotification; -(void) windowDidMiniaturize:(NSNotification *) aNotification; -(void) windowDidDeminiaturize:(NSNotification *) aNotification; -(void) windowDidBecomeKey:(NSNotification *) aNotification; -(void) windowDidResignKey:(NSNotification *) aNotification; --(void) windowDidChangeBackingProperties:(NSNotification *) aNotification; --(void) windowDidChangeScreenProfile:(NSNotification *) aNotification; --(void) windowDidChangeScreen:(NSNotification *) aNotification; -(void) windowWillEnterFullScreen:(NSNotification *) aNotification; -(void) windowDidEnterFullScreen:(NSNotification *) aNotification; -(void) windowWillExitFullScreen:(NSNotification *) aNotification; -(void) windowDidExitFullScreen:(NSNotification *) aNotification; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions; - -/* See if event is in a drag area, toggle on window dragging. */ --(BOOL) processHitTest:(NSEvent *)theEvent; +#endif /* Window event handling */ -(void) mouseDown:(NSEvent *) theEvent; @@ -117,29 +99,29 @@ typedef enum -(void) touchesCancelledWithEvent:(NSEvent *) theEvent; /* Touch event handling */ --(void) handleTouches:(NSTouchPhase) phase withEvent:(NSEvent*) theEvent; +typedef enum { + COCOA_TOUCH_DOWN, + COCOA_TOUCH_UP, + COCOA_TOUCH_MOVE, + COCOA_TOUCH_CANCELLED +} cocoaTouchType; +-(void) handleTouches:(cocoaTouchType)type withEvent:(NSEvent*) event; @end /* *INDENT-ON* */ @class SDLOpenGLContext; -@class SDL_VideoData; - -@interface SDL_WindowData : NSObject - @property (nonatomic) SDL_Window *window; - @property (nonatomic) NSWindow *nswindow; - @property (nonatomic) NSView *sdlContentView; - @property (nonatomic) 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; -#ifdef SDL_VIDEO_OPENGL_EGL - @property (nonatomic) EGLSurface egl_surface; -#endif -@end + +struct SDL_WindowData +{ + SDL_Window *window; + NSWindow *nswindow; + NSMutableArray *nscontexts; + SDL_bool created; + SDL_bool inWindowMove; + Cocoa_WindowListener *listener; + struct SDL_VideoData *videodata; +}; extern int Cocoa_CreateWindow(_THIS, SDL_Window * window); extern int Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, @@ -150,8 +132,6 @@ extern void Cocoa_SetWindowPosition(_THIS, SDL_Window * window); extern void Cocoa_SetWindowSize(_THIS, SDL_Window * window); extern void Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window); extern void Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window); -extern void Cocoa_GetWindowSizeInPixels(_THIS, SDL_Window * window, int *w, int *h); -extern int Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity); extern void Cocoa_ShowWindow(_THIS, SDL_Window * window); extern void Cocoa_HideWindow(_THIS, SDL_Window * window); extern void Cocoa_RaiseWindow(_THIS, SDL_Window * window); @@ -159,21 +139,14 @@ extern void Cocoa_MaximizeWindow(_THIS, SDL_Window * window); extern void Cocoa_MinimizeWindow(_THIS, SDL_Window * window); extern void Cocoa_RestoreWindow(_THIS, SDL_Window * window); extern void Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered); -extern void Cocoa_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable); -extern void Cocoa_SetWindowAlwaysOnTop(_THIS, SDL_Window * window, SDL_bool on_top); extern void Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen); extern int Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp); -extern void* Cocoa_GetWindowICCProfile(_THIS, SDL_Window * window, size_t * size); -extern int Cocoa_GetWindowDisplayIndex(_THIS, SDL_Window * window); extern int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp); -extern void Cocoa_SetWindowMouseRect(_THIS, SDL_Window * window); -extern void Cocoa_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed); +extern void Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed); extern void Cocoa_DestroyWindow(_THIS, SDL_Window * window); -extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info); -extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled); -extern void Cocoa_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept); -extern int Cocoa_FlashWindow(_THIS, SDL_Window * window, SDL_FlashOperation operation); +extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, + struct SDL_SysWMinfo *info); -#endif /* SDL_cocoawindow_h_ */ +#endif /* _SDL_cocoawindow_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git src/video/cocoa/SDL_cocoawindow.m src/video/cocoa/SDL_cocoawindow.m index 19e5aae30..eb0c66046 100644 --- src/video/cocoa/SDL_cocoawindow.m +++ src/video/cocoa/SDL_cocoawindow.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 1997-2014 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,10 +20,12 @@ */ #include "../../SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_COCOA +#if SDL_VIDEO_DRIVER_COCOA #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 +#if 0 # error SDL for Mac OS X must be built with a 10.7 SDK or above. +#endif #endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1070 */ #include "SDL_syswm.h" @@ -34,12 +36,11 @@ #include "../../events/SDL_mouse_c.h" #include "../../events/SDL_touch_c.h" #include "../../events/SDL_windowevents_c.h" -#include "../../events/SDL_dropevents_c.h" #include "SDL_cocoavideo.h" #include "SDL_cocoashape.h" #include "SDL_cocoamouse.h" #include "SDL_cocoaopengl.h" -#include "SDL_cocoaopengles.h" +#include "SDL_assert.h" /* #define DEBUG_COCOAWINDOW */ @@ -50,67 +51,14 @@ #endif -#define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN) - -#ifndef MAC_OS_X_VERSION_10_12 -#define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask -#endif -#ifndef NSAppKitVersionNumber10_13_2 -#define NSAppKitVersionNumber10_13_2 1561.2 -#endif -#ifndef NSAppKitVersionNumber10_14 -#define NSAppKitVersionNumber10_14 1671 -#endif - -@implementation SDL_WindowData - -@end - -@interface NSWindow (SDL) -#if MAC_OS_X_VERSION_MAX_ALLOWED < 101000 /* Added in the 10.10 SDK */ -@property (readonly) NSRect contentLayoutRect; -#endif - -/* This is available as of 10.13.2, but isn't in public headers */ -@property (nonatomic) NSRect mouseConfinementRect; -@end - -@interface SDLWindow : NSWindow +@interface SDLWindow : NSWindow /* These are needed for borderless/fullscreen windows */ - (BOOL)canBecomeKeyWindow; - (BOOL)canBecomeMainWindow; - (void)sendEvent:(NSEvent *)event; -- (void)doCommandBySelector:(SEL)aSelector; - -/* Handle drag-and-drop of files onto the SDL window. */ -- (NSDragOperation)draggingEntered:(id )sender; -- (BOOL)performDragOperation:(id )sender; -- (BOOL)wantsPeriodicDraggingUpdates; -- (BOOL)validateMenuItem:(NSMenuItem *)menuItem; - -- (SDL_Window*)findSDLWindow; @end @implementation SDLWindow - -- (BOOL)validateMenuItem:(NSMenuItem *)menuItem -{ - /* Only allow using the macOS native fullscreen toggle menubar item if the - * window is resizable and not in a SDL fullscreen mode. - */ - if ([menuItem action] == @selector(toggleFullScreen:)) { - SDL_Window *window = [self findSDLWindow]; - if (window == NULL) { - return NO; - } else if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_FULLSCREEN_DESKTOP)) { - return NO; - } else if ((window->flags & SDL_WINDOW_RESIZABLE) == 0) { - return NO; - } - } - return [super validateMenuItem:menuItem]; -} - - (BOOL)canBecomeKeyWindow { return YES; @@ -123,160 +71,45 @@ - (void)sendEvent:(NSEvent *)event { - id delegate; - [super sendEvent:event]; - - if ([event type] != NSEventTypeLeftMouseUp) { - return; - } - - delegate = [self delegate]; - if (![delegate isKindOfClass:[Cocoa_WindowListener class]]) { - return; - } - - if ([delegate isMoving]) { - [delegate windowDidFinishMoving]; - } -} + [super sendEvent:event]; -/* We'll respond to selectors by doing nothing so we don't beep. - * The escape key gets converted to a "cancel" selector, etc. - */ -- (void)doCommandBySelector:(SEL)aSelector -{ - /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/ -} + if ([event type] != NSLeftMouseUp) { + return; + } -- (NSDragOperation)draggingEntered:(id )sender -{ - if (([sender draggingSourceOperationMask] & NSDragOperationGeneric) == NSDragOperationGeneric) { - return NSDragOperationGeneric; - } + id delegate = [self delegate]; + if (![delegate isKindOfClass:[Cocoa_WindowListener class]]) { + return; + } - return NSDragOperationNone; /* no idea what to do with this, reject it. */ + if ([delegate isMoving]) { + [delegate windowDidFinishMoving]; + } } - -- (BOOL)performDragOperation:(id )sender -{ @autoreleasepool -{ - NSPasteboard *pasteboard = [sender draggingPasteboard]; - NSArray *types = [NSArray arrayWithObject:NSFilenamesPboardType]; - NSString *desiredType = [pasteboard availableTypeFromArray:types]; - SDL_Window *sdlwindow = [self findSDLWindow]; - NSData *data; - NSArray *array; - NSPoint point; - SDL_Mouse *mouse; - int x, y; - - if (desiredType == nil) { - return NO; /* can't accept anything that's being dropped here. */ - } - - data = [pasteboard dataForType:desiredType]; - if (data == nil) { - return NO; - } - - SDL_assert([desiredType isEqualToString:NSFilenamesPboardType]); - array = [pasteboard propertyListForType:@"NSFilenamesPboardType"]; - - /* Code addon to update the mouse location */ - point = [sender draggingLocation]; - mouse = SDL_GetMouse(); - x = (int)point.x; - y = (int)(sdlwindow->h - point.y); - if (x >= 0 && x < sdlwindow->w && y >= 0 && y < sdlwindow->h) { - SDL_SendMouseMotion(sdlwindow, mouse->mouseID, 0, x, y); - } - /* Code addon to update the mouse location */ - - for (NSString *path in array) { - NSURL *fileURL = [NSURL fileURLWithPath:path]; - NSNumber *isAlias = nil; - - [fileURL getResourceValue:&isAlias forKey:NSURLIsAliasFileKey error:nil]; - - /* If the URL is an alias, resolve it. */ - if ([isAlias boolValue]) { - NSURLBookmarkResolutionOptions opts = NSURLBookmarkResolutionWithoutMounting | NSURLBookmarkResolutionWithoutUI; - NSData *bookmark = [NSURL bookmarkDataWithContentsOfURL:fileURL error:nil]; - if (bookmark != nil) { - NSURL *resolvedURL = [NSURL URLByResolvingBookmarkData:bookmark - options:opts - relativeToURL:nil - bookmarkDataIsStale:nil - error:nil]; - - if (resolvedURL != nil) { - fileURL = resolvedURL; - } - } - } - - if (!SDL_SendDropFile(sdlwindow, [[fileURL path] UTF8String])) { - return NO; - } - } - - SDL_SendDropComplete(sdlwindow); - return YES; -}} - -- (BOOL)wantsPeriodicDraggingUpdates -{ - return NO; -} - -- (SDL_Window*)findSDLWindow -{ - SDL_Window *sdlwindow = NULL; - SDL_VideoDevice *_this = SDL_GetVideoDevice(); - - /* !!! FIXME: is there a better way to do this? */ - if (_this) { - for (sdlwindow = _this->windows; sdlwindow; sdlwindow = sdlwindow->next) { - NSWindow *nswindow = ((__bridge SDL_WindowData *) sdlwindow->driverdata).nswindow; - if (nswindow == self) { - break; - } - } - } - - return sdlwindow; -} - @end static Uint32 s_moveHack; -static void ConvertNSRect(NSScreen *screen, BOOL fullscreen, NSRect *r) +static void ConvertNSRect(NSRect *r) { r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height; } -static void ScheduleContextUpdates(SDL_WindowData *data) +static void +ScheduleContextUpdates(SDL_WindowData *data) { - /* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */ - #ifdef SDL_VIDEO_OPENGL - - #ifdef __clang__ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wdeprecated-declarations" - #endif - - NSOpenGLContext *currentContext; - NSMutableArray *contexts; - if (!data || !data.nscontexts) { - return; - } - - currentContext = [NSOpenGLContext currentContext]; - contexts = data.nscontexts; + NSOpenGLContext *currentContext = [NSOpenGLContext currentContext]; + NSMutableArray *contexts = data->nscontexts; @synchronized (contexts) { +#if defined(MAC_OS_X_VERSION_10_5) for (SDLOpenGLContext *context in contexts) { +#else + /* old way to iterate */ + int i; + for (i = 0; i < [contexts count]; i++) { + SDLOpenGLContext *context = [contexts objectAtIndex:i]; +#endif if (context == currentContext) { [context update]; } else { @@ -284,201 +117,68 @@ static void ScheduleContextUpdates(SDL_WindowData *data) } } } - - #ifdef __clang__ - #pragma clang diagnostic pop - #endif - - #endif /* SDL_VIDEO_OPENGL */ } -/* !!! FIXME: this should use a hint callback. */ -static int GetHintCtrlClickEmulateRightClick() +static int +GetHintCtrlClickEmulateRightClick() { - return SDL_GetHintBoolean(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, SDL_FALSE); + const char *hint = SDL_GetHint( SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK ); + return hint != NULL && *hint != '0'; } -static NSUInteger GetWindowWindowedStyle(SDL_Window * window) +static unsigned int +GetWindowStyle(SDL_Window * window) { - /* IF YOU CHANGE ANY FLAGS IN HERE, PLEASE READ - the NSWindowStyleMaskBorderless comments in SetupWindowData()! */ - - /* always allow miniaturization, otherwise you can't programatically - minimize the window, whether there's a title bar or not */ - NSUInteger style = NSWindowStyleMaskMiniaturizable; + unsigned int style; - if (window->flags & SDL_WINDOW_BORDERLESS) { - style |= NSWindowStyleMaskBorderless; + if (window->flags & SDL_WINDOW_FULLSCREEN) { + style = NSBorderlessWindowMask; } else { - style |= (NSWindowStyleMaskTitled|NSWindowStyleMaskClosable); - } - if (window->flags & SDL_WINDOW_RESIZABLE) { - style |= NSWindowStyleMaskResizable; + if (window->flags & SDL_WINDOW_BORDERLESS) { + style = NSBorderlessWindowMask; + } else { + style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask); + } + if (window->flags & SDL_WINDOW_RESIZABLE) { + style |= NSResizableWindowMask; + } } return style; } -static NSUInteger GetWindowStyle(SDL_Window * window) +static SDL_bool +SetWindowStyle(SDL_Window * window, unsigned int style) { - NSUInteger style = 0; + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = data->nswindow; - if (window->flags & SDL_WINDOW_FULLSCREEN) { - style = NSWindowStyleMaskBorderless; - } else { - style = GetWindowWindowedStyle(window); + if (![nswindow respondsToSelector: @selector(setStyleMask:)]) { + return SDL_FALSE; } - return style; -} - -static SDL_bool SetWindowStyle(SDL_Window * window, NSUInteger style) -{ - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - NSWindow *nswindow = data.nswindow; /* The view responder chain gets messed with during setStyleMask */ - if ([data.sdlContentView nextResponder] == data.listener) { - [data.sdlContentView setNextResponder:nil]; + if ([[nswindow contentView] nextResponder] == data->listener) { + [[nswindow contentView] setNextResponder:nil]; } - [nswindow setStyleMask:style]; + [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)style]; /* The view responder chain gets messed with during setStyleMask */ - if ([data.sdlContentView nextResponder] != data.listener) { - [data.sdlContentView setNextResponder:data.listener]; + if ([[nswindow contentView] nextResponder] != data->listener) { + [[nswindow contentView] setNextResponder:data->listener]; } return SDL_TRUE; } -static SDL_bool ShouldAdjustCoordinatesForGrab(SDL_Window * window) -{ - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - - if (!data || [data.listener isMovingOrFocusClickPending]) { - return SDL_FALSE; - } - - if (!(window->flags & SDL_WINDOW_INPUT_FOCUS)) { - return SDL_FALSE; - } - - if ((window->flags & SDL_WINDOW_MOUSE_GRABBED) || (window->mouse_rect.w > 0 && window->mouse_rect.h > 0)) { - return SDL_TRUE; - } - return SDL_FALSE; -} - -static SDL_bool AdjustCoordinatesForGrab(SDL_Window * window, int x, int y, CGPoint *adjusted) -{ - if (window->mouse_rect.w > 0 && window->mouse_rect.h > 0) { - SDL_Rect window_rect; - SDL_Rect mouse_rect; - - window_rect.x = 0; - window_rect.y = 0; - window_rect.w = window->w; - window_rect.h = window->h; - - if (SDL_IntersectRect(&window->mouse_rect, &window_rect, &mouse_rect)) { - int left = window->x + mouse_rect.x; - int right = left + mouse_rect.w - 1; - int top = window->y + mouse_rect.y; - int bottom = top + mouse_rect.h - 1; - if (x < left || x > right || y < top || y > bottom) { - adjusted->x = SDL_clamp(x, left, right); - adjusted->y = SDL_clamp(y, top, bottom); - return SDL_TRUE; - } - return SDL_FALSE; - } - } - - if (window->flags & SDL_WINDOW_MOUSE_GRABBED) { - int left = window->x; - int right = left + window->w - 1; - int top = window->y; - int bottom = top + window->h - 1; - if (x < left || x > right || y < top || y > bottom) { - adjusted->x = SDL_clamp(x, left, right); - adjusted->y = SDL_clamp(y, top, bottom); - return SDL_TRUE; - } - } - return SDL_FALSE; -} - -static void Cocoa_UpdateClipCursor(SDL_Window * window) -{ - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - - if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_13_2) { - NSWindow *nswindow = data.nswindow; - SDL_Rect mouse_rect; - - SDL_zero(mouse_rect); - - if (ShouldAdjustCoordinatesForGrab(window)) { - SDL_Rect window_rect; - - window_rect.x = 0; - window_rect.y = 0; - window_rect.w = window->w; - window_rect.h = window->h; - - if (window->mouse_rect.w > 0 && window->mouse_rect.h > 0) { - SDL_IntersectRect(&window->mouse_rect, &window_rect, &mouse_rect); - } - - if ((window->flags & SDL_WINDOW_MOUSE_GRABBED) != 0 && - SDL_RectEmpty(&mouse_rect)) { - SDL_memcpy(&mouse_rect, &window_rect, sizeof(mouse_rect)); - } - } - - if (SDL_RectEmpty(&mouse_rect)) { - nswindow.mouseConfinementRect = NSZeroRect; - } else { - NSRect rect; - rect.origin.x = mouse_rect.x; - rect.origin.y = [nswindow contentLayoutRect].size.height - mouse_rect.y - mouse_rect.h; - rect.size.width = mouse_rect.w; - rect.size.height = mouse_rect.h; - nswindow.mouseConfinementRect = rect; - } - } else { - /* Move the cursor to the nearest point in the window */ - if (ShouldAdjustCoordinatesForGrab(window)) { - int x, y; - CGPoint cgpoint; - - SDL_GetGlobalMouseState(&x, &y); - if (AdjustCoordinatesForGrab(window, x, y, &cgpoint)) { - Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y); - CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint); - } - } - } -} - -static NSCursor *Cocoa_GetDesiredCursor(void) -{ - SDL_Mouse *mouse = SDL_GetMouse(); - - if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) { - return (__bridge NSCursor *)mouse->cur_cursor->driverdata; - } - - return [NSCursor invisibleCursor]; -} - @implementation Cocoa_WindowListener - (void)listen:(SDL_WindowData *)data { NSNotificationCenter *center; - NSWindow *window = data.nswindow; - NSView *view = data.sdlContentView; + NSWindow *window = data->nswindow; + NSView *view = [window contentView]; _data = data; observingVisible = YES; @@ -488,31 +188,23 @@ static NSCursor *Cocoa_GetDesiredCursor(void) inFullscreenTransition = NO; pendingWindowOperation = PENDING_OPERATION_NONE; isMoving = NO; - isDragAreaRunning = NO; - pendingWindowWarpX = pendingWindowWarpY = INT_MAX; - liveResizeTimer = nil; center = [NSNotificationCenter defaultCenter]; if ([window delegate] != nil) { [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window]; - [center addObserver:self selector:@selector(windowWillStartLiveResize:) name:NSWindowWillStartLiveResizeNotification object:window]; - [center addObserver:self selector:@selector(windowDidEndLiveResize:) name:NSWindowDidEndLiveResizeNotification object:window]; [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window]; [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window]; [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window]; [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window]; [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window]; [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window]; - [center addObserver:self selector:@selector(windowDidChangeBackingProperties:) name:NSWindowDidChangeBackingPropertiesNotification object:window]; - [center addObserver:self selector:@selector(windowDidChangeScreenProfile:) name:NSWindowDidChangeScreenProfileNotification object:window]; - [center addObserver:self selector:@selector(windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification object:window]; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 [center addObserver:self selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:window]; [center addObserver:self selector:@selector(windowDidEnterFullScreen:) name:NSWindowDidEnterFullScreenNotification object:window]; [center addObserver:self selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:window]; [center addObserver:self selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:window]; - [center addObserver:self selector:@selector(windowDidFailToEnterFullScreen:) name:@"NSWindowDidFailToEnterFullScreenNotification" object:window]; - [center addObserver:self selector:@selector(windowDidFailToExitFullScreen:) name:@"NSWindowDidFailToExitFullScreenNotification" object:window]; +#endif } else { [window setDelegate:self]; } @@ -531,7 +223,11 @@ static NSCursor *Cocoa_GetDesiredCursor(void) [view setNextResponder:self]; - [view setAcceptsTouchEvents:YES]; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + if ([view respondsToSelector:@selector(setAcceptsTouchEvents:)]) { + [view setAcceptsTouchEvents:YES]; + } +#endif } - (void)observeValueForKeyPath:(NSString *)keyPath @@ -543,12 +239,12 @@ static NSCursor *Cocoa_GetDesiredCursor(void) return; } - if (object == _data.nswindow && [keyPath isEqualToString:@"visible"]) { + if (object == _data->nswindow && [keyPath isEqualToString:@"visible"]) { int newVisibility = [[change objectForKey:@"new"] intValue]; if (newVisibility) { - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_SHOWN, 0, 0); + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0); } else { - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_HIDDEN, 0, 0); + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0); } } } @@ -556,18 +252,18 @@ static NSCursor *Cocoa_GetDesiredCursor(void) -(void) pauseVisibleObservation { observingVisible = NO; - wasVisible = [_data.nswindow isVisible]; + wasVisible = [_data->nswindow isVisible]; } -(void) resumeVisibleObservation { - BOOL isVisible = [_data.nswindow isVisible]; + BOOL isVisible = [_data->nswindow isVisible]; observingVisible = YES; if (wasVisible != isVisible) { if (isVisible) { - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_SHOWN, 0, 0); + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0); } else { - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_HIDDEN, 0, 0); + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0); } wasVisible = isVisible; @@ -576,16 +272,14 @@ static NSCursor *Cocoa_GetDesiredCursor(void) -(BOOL) setFullscreenSpace:(BOOL) state { - SDL_Window *window = _data.window; - NSWindow *nswindow = _data.nswindow; - SDL_VideoData *videodata = ((__bridge SDL_WindowData *) window->driverdata).videodata; + SDL_Window *window = _data->window; + NSWindow *nswindow = _data->nswindow; + SDL_VideoData *videodata = ((SDL_WindowData *) window->driverdata)->videodata; - if (!videodata.allow_spaces) { + if (!videodata->allow_spaces) { return NO; /* Spaces are forcibly disabled. */ } else if (state && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) { return NO; /* we only allow you to make a Space on FULLSCREEN_DESKTOP windows. */ - } else if (!state && ((window->last_fullscreen_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) { - return NO; /* we only handle leaving the Space on windows that were previously FULLSCREEN_DESKTOP. */ } else if (state == isFullscreenSpace) { return YES; /* already there. */ } @@ -601,7 +295,9 @@ static NSCursor *Cocoa_GetDesiredCursor(void) inFullscreenTransition = YES; /* you need to be FullScreenPrimary, or toggleFullScreen doesn't work. Unset it again in windowDidExitFullScreen. */ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; +#endif [nswindow performSelectorOnMainThread: @selector(toggleFullScreen:) withObject:nswindow waitUntilDone:NO]; return YES; } @@ -624,30 +320,26 @@ static NSCursor *Cocoa_GetDesiredCursor(void) - (void)close { NSNotificationCenter *center; - NSWindow *window = _data.nswindow; + NSWindow *window = _data->nswindow; NSView *view = [window contentView]; + NSArray *windows = nil; center = [NSNotificationCenter defaultCenter]; if ([window delegate] != self) { [center removeObserver:self name:NSWindowDidExposeNotification object:window]; - [center removeObserver:self name:NSWindowWillStartLiveResizeNotification object:window]; - [center removeObserver:self name:NSWindowDidEndLiveResizeNotification object:window]; [center removeObserver:self name:NSWindowDidMoveNotification object:window]; [center removeObserver:self name:NSWindowDidResizeNotification object:window]; [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:window]; [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window]; [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window]; [center removeObserver:self name:NSWindowDidResignKeyNotification object:window]; - [center removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:window]; - [center removeObserver:self name:NSWindowDidChangeScreenProfileNotification object:window]; - [center removeObserver:self name:NSWindowDidChangeScreenNotification object:window]; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 [center removeObserver:self name:NSWindowWillEnterFullScreenNotification object:window]; [center removeObserver:self name:NSWindowDidEnterFullScreenNotification object:window]; [center removeObserver:self name:NSWindowWillExitFullScreenNotification object:window]; [center removeObserver:self name:NSWindowDidExitFullScreenNotification object:window]; - [center removeObserver:self name:@"NSWindowDidFailToEnterFullScreenNotification" object:window]; - [center removeObserver:self name:@"NSWindowDidFailToExitFullScreenNotification" object:window]; +#endif } else { [window setDelegate:nil]; } @@ -660,31 +352,37 @@ static NSCursor *Cocoa_GetDesiredCursor(void) if ([view nextResponder] == self) { [view setNextResponder:nil]; } -} - -- (BOOL)isMoving -{ - return isMoving; -} -- (BOOL)isMovingOrFocusClickPending -{ - return isMoving || (focusClickPending != 0); -} + /* Make the next window in the z-order Key. If we weren't the foreground + when closed, this is a no-op. + !!! FIXME: Note that this is a hack, and there are corner cases where + !!! FIXME: this fails (such as the About box). The typical nib+RunLoop + !!! FIXME: handles this for Cocoa apps, but we bypass all that in SDL. + !!! FIXME: We should remove this code when we find a better way to + !!! FIXME: have the system do this for us. See discussion in + !!! FIXME: http://bugzilla.libsdl.org/show_bug.cgi?id=1825 + */ + windows = [NSApp orderedWindows]; +#if defined(MAC_OS_X_VERSION_10_5) + for (NSWindow *win in windows) { +#else + /* old way to iterate */ + int i; + for (i = 0; i < [windows count]; i++) { + NSWindow *win = [windows objectAtIndex:i]; +#endif + if (win == window) { + continue; + } --(void) setFocusClickPending:(NSInteger) button -{ - focusClickPending |= (1 << button); + [win makeKeyAndOrderFront:self]; + break; + } } --(void) clearFocusClickPending:(NSInteger) button +- (BOOL)isMoving { - if (focusClickPending & (1 << button)) { - focusClickPending &= ~(1 << button); - if (focusClickPending == 0) { - [self onMovingOrFocusClickPendingStateCleared]; - } - } + return isMoving; } -(void) setPendingMoveX:(int)x Y:(int)y @@ -695,88 +393,36 @@ static NSCursor *Cocoa_GetDesiredCursor(void) - (void)windowDidFinishMoving { - if (isMoving) { + if ([self isMoving]) + { isMoving = NO; - [self onMovingOrFocusClickPendingStateCleared]; - } -} -- (void)onMovingOrFocusClickPendingStateCleared -{ - if (![self isMovingOrFocusClickPending]) { SDL_Mouse *mouse = SDL_GetMouse(); - if (pendingWindowWarpX != INT_MAX && pendingWindowWarpY != INT_MAX) { - mouse->WarpMouseGlobal(pendingWindowWarpX, pendingWindowWarpY); - pendingWindowWarpX = pendingWindowWarpY = INT_MAX; + if (pendingWindowWarpX >= 0 && pendingWindowWarpY >= 0) { + mouse->WarpMouse(_data->window, pendingWindowWarpX, pendingWindowWarpY); + pendingWindowWarpX = pendingWindowWarpY = -1; } - if (mouse->relative_mode && !mouse->relative_mode_warp && mouse->focus == _data.window) { - /* Move the cursor to the nearest point in the window */ - { - int x, y; - CGPoint cgpoint; - - SDL_GetMouseState(&x, &y); - cgpoint.x = _data.window->x + x; - cgpoint.y = _data.window->y + y; - - Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y); - - DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y); - CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint); - } - + if (mouse->relative_mode && SDL_GetMouseFocus() == _data->window) { mouse->SetRelativeMouseMode(SDL_TRUE); - } else { - Cocoa_UpdateClipCursor(_data.window); } } } - (BOOL)windowShouldClose:(id)sender { - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_CLOSE, 0, 0); + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_CLOSE, 0, 0); return NO; } - (void)windowDidExpose:(NSNotification *)aNotification { - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_EXPOSED, 0, 0); -} - -- (void)onLiveResizeTimerFire:(id)sender -{ - SDL_OnWindowLiveResizeUpdate(_data.window); -} - -- (void)windowWillStartLiveResize:(NSNotification *)aNotification -{ - // We'll try to maintain 60 FPS during live resizing - const NSTimeInterval interval = 1.0 / 60.0; - - NSMethodSignature *invocationSig = [Cocoa_WindowListener - instanceMethodSignatureForSelector:@selector(onLiveResizeTimerFire:)]; - NSInvocation *invocation = [NSInvocation - invocationWithMethodSignature:invocationSig]; - [invocation setTarget:self]; - [invocation setSelector:@selector(onLiveResizeTimerFire:)]; - - liveResizeTimer = [NSTimer scheduledTimerWithTimeInterval:interval - invocation:invocation - repeats:TRUE]; - - [[NSRunLoop currentRunLoop] addTimer:liveResizeTimer forMode:NSRunLoopCommonModes]; -} - -- (void)windowDidEndLiveResize:(NSNotification *)aNotification -{ - [liveResizeTimer invalidate]; - liveResizeTimer = nil; + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0); } - (void)windowWillMove:(NSNotification *)aNotification { - if ([_data.nswindow isKindOfClass:[SDLWindow class]]) { - pendingWindowWarpX = pendingWindowWarpY = INT_MAX; + if ([_data->nswindow isKindOfClass:[SDLWindow class]]) { + pendingWindowWarpX = pendingWindowWarpY = -1; isMoving = YES; } } @@ -784,16 +430,10 @@ static NSCursor *Cocoa_GetDesiredCursor(void) - (void)windowDidMove:(NSNotification *)aNotification { int x, y; - SDL_Window *window = _data.window; - NSWindow *nswindow = _data.nswindow; - BOOL fullscreen = window->flags & FULLSCREEN_MASK; + SDL_Window *window = _data->window; + NSWindow *nswindow = _data->nswindow; NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]]; - ConvertNSRect([nswindow screen], fullscreen, &rect); - - if (inFullscreenTransition) { - /* We'll take care of this at the end of the transition */ - return; - } + ConvertNSRect(&rect); if (s_moveHack) { SDL_bool blockMove = ((SDL_GetTicks() - s_moveHack) < 500); @@ -804,7 +444,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) /* Cocoa is adjusting the window in response to a mode change */ rect.origin.x = window->x; rect.origin.y = window->y; - ConvertNSRect([nswindow screen], fullscreen, &rect); + ConvertNSRect(&rect); [nswindow setFrameOrigin:rect.origin]; return; } @@ -820,25 +460,16 @@ static NSCursor *Cocoa_GetDesiredCursor(void) - (void)windowDidResize:(NSNotification *)aNotification { - SDL_Window *window; - NSWindow *nswindow; - NSRect rect; - int x, y, w, h; - BOOL zoomed; if (inFullscreenTransition) { /* We'll take care of this at the end of the transition */ return; } - if (focusClickPending) { - focusClickPending = 0; - [self onMovingOrFocusClickPendingStateCleared]; - } - - window = _data.window; - nswindow = _data.nswindow; - rect = [nswindow contentRectForFrameRect:[nswindow frame]]; - ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect); + SDL_Window *window = _data->window; + NSWindow *nswindow = _data->nswindow; + int x, y, w, h; + NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]]; + ConvertNSRect(&rect); x = (int)rect.origin.x; y = (int)rect.origin.y; w = (int)rect.size.width; @@ -855,12 +486,7 @@ static NSCursor *Cocoa_GetDesiredCursor(void) SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y); SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h); - /* isZoomed always returns true if the window is not resizable */ - if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) { - zoomed = YES; - } else { - zoomed = NO; - } + const BOOL zoomed = [nswindow isZoomed]; if (!zoomed) { SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0); } else if (zoomed) { @@ -870,72 +496,61 @@ static NSCursor *Cocoa_GetDesiredCursor(void) - (void)windowDidMiniaturize:(NSNotification *)aNotification { - if (focusClickPending) { - focusClickPending = 0; - [self onMovingOrFocusClickPendingStateCleared]; - } - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); } - (void)windowDidDeminiaturize:(NSNotification *)aNotification { - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_RESTORED, 0, 0); + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0); } - (void)windowDidBecomeKey:(NSNotification *)aNotification { - SDL_Window *window = _data.window; + SDL_Window *window = _data->window; SDL_Mouse *mouse = SDL_GetMouse(); + if (mouse->relative_mode && ![self isMoving]) { + mouse->SetRelativeMouseMode(SDL_TRUE); + } /* We're going to get keyboard events, since we're key. */ - /* This needs to be done before restoring the relative mouse mode. */ SDL_SetKeyboardFocus(window); - if (mouse->relative_mode && !mouse->relative_mode_warp && ![self isMovingOrFocusClickPending]) { - mouse->SetRelativeMouseMode(SDL_TRUE); - } - /* If we just gained focus we need the updated mouse position */ if (!mouse->relative_mode) { NSPoint point; int x, y; - point = [_data.nswindow mouseLocationOutsideOfEventStream]; + point = [_data->nswindow mouseLocationOutsideOfEventStream]; x = (int)point.x; y = (int)(window->h - point.y); if (x >= 0 && x < window->w && y >= 0 && y < window->h) { - SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y); + SDL_SendMouseMotion(window, 0, 0, x, y); } } /* Check to see if someone updated the clipboard */ - Cocoa_CheckClipboardUpdate(_data.videodata); + Cocoa_CheckClipboardUpdate(_data->videodata); if ((isFullscreenSpace) && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)) { [NSMenu setMenuBarVisible:NO]; } - { - const unsigned int newflags = [NSEvent modifierFlags] & NSEventModifierFlagCapsLock; - _data.videodata.modifierFlags = (_data.videodata.modifierFlags & ~NSEventModifierFlagCapsLock) | newflags; - SDL_ToggleModState(KMOD_CAPS, newflags ? SDL_TRUE : SDL_FALSE); - } } - (void)windowDidResignKey:(NSNotification *)aNotification { SDL_Mouse *mouse = SDL_GetMouse(); - if (mouse->relative_mode && !mouse->relative_mode_warp) { + if (mouse->relative_mode) { mouse->SetRelativeMouseMode(SDL_FALSE); } /* Some other window will get mouse events, since we're not key. */ - if (SDL_GetMouseFocus() == _data.window) { + if (SDL_GetMouseFocus() == _data->window) { SDL_SetMouseFocus(NULL); } /* Some other window will get keyboard events, since we're not key. */ - if (SDL_GetKeyboardFocus() == _data.window) { + if (SDL_GetKeyboardFocus() == _data->window) { SDL_SetKeyboardFocus(NULL); } @@ -944,68 +559,19 @@ static NSCursor *Cocoa_GetDesiredCursor(void) } } -- (void)windowDidChangeBackingProperties:(NSNotification *)aNotification -{ - NSNumber *oldscale = [[aNotification userInfo] objectForKey:NSBackingPropertyOldScaleFactorKey]; - - if (inFullscreenTransition) { - return; - } - - if ([oldscale doubleValue] != [_data.nswindow backingScaleFactor]) { - /* Force a resize event when the backing scale factor changes. */ - _data.window->w = 0; - _data.window->h = 0; - [self windowDidResize:aNotification]; - } -} - -- (void)windowDidChangeScreenProfile:(NSNotification *)aNotification -{ - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_ICCPROF_CHANGED, 0, 0); -} - -- (void)windowDidChangeScreen:(NSNotification *)aNotification -{ - /*printf("WINDOWDIDCHANGESCREEN\n");*/ -#ifdef SDL_VIDEO_OPENGL - if (_data && _data.nscontexts) { - for (SDLOpenGLContext *context in _data.nscontexts) { - [context movedToNewScreen]; - } - } -#endif /* SDL_VIDEO_OPENGL */ -} - - (void)windowWillEnterFullScreen:(NSNotification *)aNotification { - SDL_Window *window = _data.window; + SDL_Window *window = _data->window; - SetWindowStyle(window, (NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskResizable)); + SetWindowStyle(window, (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask)); isFullscreenSpace = YES; inFullscreenTransition = YES; } -- (void)windowDidFailToEnterFullScreen:(NSNotification *)aNotification -{ - SDL_Window *window = _data.window; - - if (window->is_destroying) { - return; - } - - SetWindowStyle(window, GetWindowStyle(window)); - - isFullscreenSpace = NO; - inFullscreenTransition = NO; - - [self windowDidExitFullScreen:nil]; -} - - (void)windowDidEnterFullScreen:(NSNotification *)aNotification { - SDL_Window *window = _data.window; + SDL_Window *window = _data->window; inFullscreenTransition = NO; @@ -1023,66 +589,28 @@ static NSCursor *Cocoa_GetDesiredCursor(void) */ window->w = 0; window->h = 0; - [self windowDidMove:aNotification]; [self windowDidResize:aNotification]; } } - (void)windowWillExitFullScreen:(NSNotification *)aNotification { - SDL_Window *window = _data.window; + SDL_Window *window = _data->window; + + SetWindowStyle(window, GetWindowStyle(window)); isFullscreenSpace = NO; inFullscreenTransition = YES; - - /* As of macOS 10.11, the window seems to need to be resizable when exiting - a Space, in order for it to resize back to its windowed-mode size. - As of macOS 10.15, the window decorations can go missing sometimes after - certain fullscreen-desktop->exlusive-fullscreen->windowed mode flows - sometimes. Making sure the style mask always uses the windowed mode style - when returning to windowed mode from a space (instead of using a pending - fullscreen mode style mask) seems to work around that issue. - */ - SetWindowStyle(window, GetWindowWindowedStyle(window) | NSWindowStyleMaskResizable); -} - -- (void)windowDidFailToExitFullScreen:(NSNotification *)aNotification -{ - SDL_Window *window = _data.window; - - if (window->is_destroying) { - return; - } - - SetWindowStyle(window, (NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskResizable)); - - isFullscreenSpace = YES; - inFullscreenTransition = NO; - - [self windowDidEnterFullScreen:nil]; } - (void)windowDidExitFullScreen:(NSNotification *)aNotification { - SDL_Window *window = _data.window; - NSWindow *nswindow = _data.nswindow; - NSButton *button = nil; + SDL_Window *window = _data->window; + NSWindow *nswindow = _data->nswindow; inFullscreenTransition = NO; - /* As of macOS 10.15, the window decorations can go missing sometimes after - certain fullscreen-desktop->exlusive-fullscreen->windowed mode flows - sometimes. Making sure the style mask always uses the windowed mode style - when returning to windowed mode from a space (instead of using a pending - fullscreen mode style mask) seems to work around that issue. - */ - SetWindowStyle(window, GetWindowWindowedStyle(window)); - - if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) { - [nswindow setLevel:NSFloatingWindowLevel]; - } else { - [nswindow setLevel:kCGNormalWindowLevel]; - } + [nswindow setLevel:kCGNormalWindowLevel]; if (pendingWindowOperation == PENDING_OPERATION_ENTER_FULLSCREEN) { pendingWindowOperation = PENDING_OPERATION_NONE; @@ -1092,45 +620,22 @@ static NSCursor *Cocoa_GetDesiredCursor(void) [nswindow miniaturize:nil]; } else { /* Adjust the fullscreen toggle button and readd menu now that we're here. */ +#if MAC_OS_X_VERSION_MIN_REQUIRED > 1070 if (window->flags & SDL_WINDOW_RESIZABLE) { /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */ [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; } else { [nswindow setCollectionBehavior:NSWindowCollectionBehaviorManaged]; } +#endif [NSMenu setMenuBarVisible:YES]; pendingWindowOperation = PENDING_OPERATION_NONE; - -#if 0 -/* This fixed bug 3719, which is that changing window size while fullscreen - doesn't take effect when leaving fullscreen, but introduces bug 3809, - which is that a maximized window doesn't go back to normal size when - restored, so this code is disabled until we can properly handle the - beginning and end of maximize and restore. - */ - /* Restore windowed size and position in case it changed while fullscreen */ - { - NSRect rect; - rect.origin.x = window->windowed.x; - rect.origin.y = window->windowed.y; - rect.size.width = window->windowed.w; - rect.size.height = window->windowed.h; - ConvertNSRect([nswindow screen], NO, &rect); - - s_moveHack = 0; - [nswindow setContentSize:rect.size]; - [nswindow setFrameOrigin:rect.origin]; - s_moveHack = SDL_GetTicks(); - } -#endif /* 0 */ - /* Force the size change event in case it was delivered earlier while the window was still animating into place. */ window->w = 0; window->h = 0; - [self windowDidMove:aNotification]; [self windowDidResize:aNotification]; /* FIXME: Why does the window get hidden? */ @@ -1138,56 +643,27 @@ static NSCursor *Cocoa_GetDesiredCursor(void) Cocoa_ShowWindow(SDL_GetVideoDevice(), window); } } - - /* There's some state that isn't quite back to normal when - windowDidExitFullScreen triggers. For example, the minimize button on - the titlebar doesn't actually enable for another 200 milliseconds or - so on this MacBook. Camp here and wait for that to happen before - going on, in case we're exiting fullscreen to minimize, which need - that window state to be normal before it will work. */ - button = [nswindow standardWindowButton:NSWindowMiniaturizeButton]; - if (button) { - int iterations = 0; - while (![button isEnabled] && (iterations < 100)) { - SDL_Delay(10); - SDL_PumpEvents(); - iterations++; - } - } } +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions { - if ((_data.window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) { + if ((_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) { return NSApplicationPresentationFullScreen | NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; } else { return proposedOptions; } } +#endif -/* We'll respond to key events by mostly doing nothing so we don't beep. + +/* We'll respond to key events by doing nothing so we don't beep. * We could handle key messages here, but we lose some in the NSApp dispatch, * where they get converted to action messages, etc. */ - (void)flagsChanged:(NSEvent *)theEvent { /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/ - - /* Catch capslock in here as a special case: - https://developer.apple.com/library/archive/qa/qa1519/_index.html - Note that technote's check of keyCode doesn't work. At least on the - 10.15 beta, capslock comes through here as keycode 255, but it's safe - to send duplicate key events; SDL filters them out quickly in - SDL_SendKeyboardKey(). */ - - /* Also note that SDL_SendKeyboardKey expects all capslock events to be - keypresses; it won't toggle the mod state if you send a keyrelease. */ - const SDL_bool osenabled = ([theEvent modifierFlags] & NSEventModifierFlagCapsLock) ? SDL_TRUE : SDL_FALSE; - const SDL_bool sdlenabled = (SDL_GetModState() & KMOD_CAPS) ? SDL_TRUE : SDL_FALSE; - if (osenabled ^ sdlenabled) { - SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_CAPSLOCK); - SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_CAPSLOCK); - } } - (void)keyDown:(NSEvent *)theEvent { @@ -1206,86 +682,14 @@ static NSCursor *Cocoa_GetDesiredCursor(void) /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/ } -- (BOOL)processHitTest:(NSEvent *)theEvent -{ - SDL_assert(isDragAreaRunning == [_data.nswindow isMovableByWindowBackground]); - - if (_data.window->hit_test) { /* if no hit-test, skip this. */ - const NSPoint location = [theEvent locationInWindow]; - const SDL_Point point = { (int) location.x, _data.window->h - (((int) location.y)-1) }; - const SDL_HitTestResult rc = _data.window->hit_test(_data.window, &point, _data.window->hit_test_data); - if (rc == SDL_HITTEST_DRAGGABLE) { - if (!isDragAreaRunning) { - isDragAreaRunning = YES; - [_data.nswindow setMovableByWindowBackground:YES]; - } - return YES; /* dragging! */ - } - } - - if (isDragAreaRunning) { - isDragAreaRunning = NO; - [_data.nswindow setMovableByWindowBackground:NO]; - return YES; /* was dragging, drop event. */ - } - - return NO; /* not a special area, carry on. */ -} - -static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL_Window * window, const Uint8 state, const Uint8 button) -{ - const SDL_MouseID mouseID = mouse->mouseID; - const int clicks = (int) [theEvent clickCount]; - SDL_Window *focus = SDL_GetKeyboardFocus(); - int rc; - - // macOS will send non-left clicks to background windows without raising them, so we need to - // temporarily adjust the mouse position when this happens, as `mouse` will be tracking - // the position in the currently-focused window. We don't (currently) send a mousemove - // event for the background window, this just makes sure the button is reported at the - // correct position in its own event. - if ( focus && ([theEvent window] == ((__bridge SDL_WindowData *) focus->driverdata).nswindow) ) { - rc = SDL_SendMouseButtonClicks(window, mouseID, state, button, clicks); - } else { - const int orig_x = mouse->x; - const int orig_y = mouse->y; - const NSPoint point = [theEvent locationInWindow]; - mouse->x = (int) point.x; - mouse->y = (int) (window->h - point.y); - rc = SDL_SendMouseButtonClicks(window, mouseID, state, button, clicks); - mouse->x = orig_x; - mouse->y = orig_y; - } - - return rc; -} - - (void)mouseDown:(NSEvent *)theEvent { - SDL_Mouse *mouse = SDL_GetMouse(); int button; - if (!mouse) { - return; - } - - /* Ignore events that aren't inside the client area (i.e. title bar.) */ - if ([theEvent window]) { - NSRect windowRect = [[[theEvent window] contentView] frame]; - if (!NSMouseInRect([theEvent locationInWindow], windowRect, NO)) { - return; - } - } - - if ([self processHitTest:theEvent]) { - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); - return; /* dragging, drop event. */ - } - switch ([theEvent buttonNumber]) { case 0: - if (([theEvent modifierFlags] & NSEventModifierFlagControl) && - GetHintCtrlClickEmulateRightClick()) { + if (([theEvent modifierFlags] & NSControlKeyMask) && + GetHintCtrlClickEmulateRightClick()) { wasCtrlLeft = YES; button = SDL_BUTTON_RIGHT; } else { @@ -1300,11 +704,10 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL button = SDL_BUTTON_MIDDLE; break; default: - button = (int) [theEvent buttonNumber] + 1; + button = [theEvent buttonNumber] + 1; break; } - - Cocoa_SendMouseButtonClicks(mouse, theEvent, _data.window, SDL_PRESSED, button); + SDL_SendMouseButton(_data->window, 0, SDL_PRESSED, button); } - (void)rightMouseDown:(NSEvent *)theEvent @@ -1317,19 +720,9 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL [self mouseDown:theEvent]; } -- (void)mouseUp:(NSEvent *)theEvent -{ - SDL_Mouse *mouse = SDL_GetMouse(); - int button; - - if (!mouse) { - return; - } - - if ([self processHitTest:theEvent]) { - SDL_SendWindowEvent(_data.window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); - return; /* stopped dragging, drop event. */ - } +- (void)mouseUp:(NSEvent *)theEvent +{ + int button; switch ([theEvent buttonNumber]) { case 0: @@ -1347,11 +740,10 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL button = SDL_BUTTON_MIDDLE; break; default: - button = (int) [theEvent buttonNumber] + 1; + button = [theEvent buttonNumber] + 1; break; } - - Cocoa_SendMouseButtonClicks(mouse, theEvent, _data.window, SDL_RELEASED, button); + SDL_SendMouseButton(_data->window, 0, SDL_RELEASED, button); } - (void)rightMouseUp:(NSEvent *)theEvent @@ -1367,55 +759,54 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL - (void)mouseMoved:(NSEvent *)theEvent { SDL_Mouse *mouse = SDL_GetMouse(); - SDL_MouseID mouseID; + SDL_Window *window = _data->window; NSPoint point; int x, y; - SDL_Window *window; - NSView *contentView; - if (!mouse) { + if (mouse->relative_mode) { return; } - mouseID = mouse->mouseID; - window = _data.window; - contentView = _data.sdlContentView; point = [theEvent locationInWindow]; + x = (int)point.x; + y = (int)(window->h - point.y); - if ([contentView mouse:[contentView convertPoint:point fromView:nil] inRect:[contentView bounds]] && - [NSCursor currentCursor] != Cocoa_GetDesiredCursor()) { - // The wrong cursor is on screen, fix it. This fixes an macOS bug that is only known to - // occur in fullscreen windows on the built-in displays of newer MacBooks with camera - // notches. When the mouse is moved near the top of such a window (within about 44 units) - // and then moved back down, the cursor rects aren't respected. - [_data.nswindow invalidateCursorRectsForView:contentView]; - } + if (x < 0 || x >= window->w || y < 0 || y >= window->h) { + if (window->flags & SDL_WINDOW_INPUT_GRABBED) { + if (x < 0) { + x = 0; + } else if (x >= window->w) { + x = window->w - 1; + } + if (y < 0) { + y = 0; + } else if (y >= window->h) { + y = window->h - 1; + } - if ([self processHitTest:theEvent]) { - SDL_SendWindowEvent(window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); - return; /* dragging, drop event. */ - } +#if !SDL_MAC_NO_SANDBOX + CGPoint cgpoint; - if (mouse->relative_mode) { - return; - } + /* When SDL_MAC_NO_SANDBOX is set, this is handled by + * SDL_cocoamousetap.m. + */ - x = (int)point.x; - y = (int)(window->h - point.y); + cgpoint.x = window->x + x; + cgpoint.y = window->y + y; - if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_13_2) { - /* Mouse grab is taken care of by the confinement rect */ - } else { - CGPoint cgpoint; - if (ShouldAdjustCoordinatesForGrab(window) && - AdjustCoordinatesForGrab(window, window->x + x, window->y + y, &cgpoint)) { - Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y); + /* According to the docs, this was deprecated in 10.6, but it's still + * around. The substitute requires a CGEventSource, but I'm not entirely + * sure how we'd procure the right one for this event. + */ + CGSetLocalEventsSuppressionInterval(0.0); CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint); - CGAssociateMouseAndMouseCursorPosition(YES); + CGSetLocalEventsSuppressionInterval(0.25); + + Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y); +#endif } } - - SDL_SendMouseMotion(window, mouseID, 0, x, y); + SDL_SendMouseMotion(window, 0, 0, x, y); } - (void)mouseDragged:(NSEvent *)theEvent @@ -1435,261 +826,141 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse * mouse, NSEvent *theEvent, SDL - (void)scrollWheel:(NSEvent *)theEvent { - Cocoa_HandleMouseWheel(_data.window, theEvent); -} - - -- (BOOL)isTouchFromTrackpad:(NSEvent *)theEvent -{ - SDL_Window *window = _data.window; - SDL_VideoData *videodata = ((__bridge SDL_WindowData *) window->driverdata).videodata; - - /* if this a MacBook trackpad, we'll make input look like a synthesized - event. This is backwards from reality, but better matches user - expectations. You can make it look like a generic touch device instead - with the SDL_HINT_TRACKPAD_IS_TOUCH_ONLY hint. */ - BOOL istrackpad = NO; - if (!videodata.trackpad_is_touch_only) { - @try { - istrackpad = ([theEvent subtype] == NSEventSubtypeMouseEvent); - } - @catch (NSException *e) { - /* if NSEvent type doesn't have subtype, such as NSEventTypeBeginGesture on - * macOS 10.5 to 10.10, then NSInternalInconsistencyException is thrown. - * This still prints a message to terminal so catching it's not an ideal solution. - * - * *** Assertion failure in -[NSEvent subtype] - */ - } - } - return istrackpad; + Cocoa_HandleMouseWheel(_data->window, theEvent); } - (void)touchesBeganWithEvent:(NSEvent *) theEvent { - NSSet *touches; - SDL_TouchID touchID; - int existingTouchCount; - const BOOL istrackpad = [self isTouchFromTrackpad:theEvent]; - - touches = [theEvent touchesMatchingPhase:NSTouchPhaseAny inView:nil]; - touchID = istrackpad ? SDL_MOUSE_TOUCHID : (SDL_TouchID)(intptr_t)[[touches anyObject] device]; - existingTouchCount = 0; - - for (NSTouch* touch in touches) { - if ([touch phase] != NSTouchPhaseBegan) { - existingTouchCount++; - } - } - if (existingTouchCount == 0) { - int numFingers = SDL_GetNumTouchFingers(touchID); - DLog("Reset Lost Fingers: %d", numFingers); - for (--numFingers; numFingers >= 0; --numFingers) { - SDL_Finger* finger = SDL_GetTouchFinger(touchID, numFingers); - /* trackpad touches have no window. If we really wanted one we could - * use the window that has mouse or keyboard focus. - * Sending a null window currently also prevents synthetic mouse - * events from being generated from touch events. - */ - SDL_Window *window = NULL; - SDL_SendTouch(touchID, finger->id, window, SDL_FALSE, 0, 0, 0); - } - } - - DLog("Began Fingers: %lu .. existing: %d", (unsigned long)[touches count], existingTouchCount); - [self handleTouches:NSTouchPhaseBegan withEvent:theEvent]; + [self handleTouches:COCOA_TOUCH_DOWN withEvent:theEvent]; } - (void)touchesMovedWithEvent:(NSEvent *) theEvent { - [self handleTouches:NSTouchPhaseMoved withEvent:theEvent]; + [self handleTouches:COCOA_TOUCH_MOVE withEvent:theEvent]; } - (void)touchesEndedWithEvent:(NSEvent *) theEvent { - [self handleTouches:NSTouchPhaseEnded withEvent:theEvent]; + [self handleTouches:COCOA_TOUCH_UP withEvent:theEvent]; } - (void)touchesCancelledWithEvent:(NSEvent *) theEvent { - [self handleTouches:NSTouchPhaseCancelled withEvent:theEvent]; + [self handleTouches:COCOA_TOUCH_CANCELLED withEvent:theEvent]; } -- (void)handleTouches:(NSTouchPhase) phase withEvent:(NSEvent *) theEvent +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 +- (void)handleTouches:(cocoaTouchType)type withEvent:(NSEvent *)event { - NSSet *touches = [theEvent touchesMatchingPhase:phase inView:nil]; - const BOOL istrackpad = [self isTouchFromTrackpad:theEvent]; - SDL_FingerID fingerId; - float x, y; + NSSet *touches = 0; + NSEnumerator *enumerator; + NSTouch *touch; - for (NSTouch *touch in touches) { - const SDL_TouchID touchId = istrackpad ? SDL_MOUSE_TOUCHID : (SDL_TouchID)(intptr_t)[touch device]; - SDL_TouchDeviceType devtype = SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE; + switch (type) { + case COCOA_TOUCH_DOWN: + touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:nil]; + break; + case COCOA_TOUCH_UP: + touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:nil]; + break; + case COCOA_TOUCH_CANCELLED: + touches = [event touchesMatchingPhase:NSTouchPhaseCancelled inView:nil]; + break; + case COCOA_TOUCH_MOVE: + touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:nil]; + break; + } - /* trackpad touches have no window. If we really wanted one we could - * use the window that has mouse or keyboard focus. - * Sending a null window currently also prevents synthetic mouse events - * from being generated from touch events. - */ - SDL_Window *window = NULL; - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101202 /* Added in the 10.12.2 SDK. */ - if ([touch respondsToSelector:@selector(type)]) { - /* TODO: Before implementing direct touch support here, we need to - * figure out whether the OS generates mouse events from them on its - * own. If it does, we should prevent SendTouch from generating - * synthetic mouse events for these touches itself (while also - * sending a window.) It will also need to use normalized window- - * relative coordinates via [touch locationInView:]. - */ - if ([touch type] == NSTouchTypeDirect) { - continue; + enumerator = [touches objectEnumerator]; + touch = (NSTouch*)[enumerator nextObject]; + while (touch) { + const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device]; + if (!SDL_GetTouch(touchId)) { + if (SDL_AddTouch(touchId, "") < 0) { + return; } } -#endif - - if (SDL_AddTouch(touchId, devtype, "") < 0) { - return; - } - fingerId = (SDL_FingerID)(intptr_t)[touch identity]; - x = [touch normalizedPosition].x; - y = [touch normalizedPosition].y; + const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity]; + float x = [touch normalizedPosition].x; + float y = [touch normalizedPosition].y; /* Make the origin the upper left instead of the lower left */ y = 1.0f - y; - switch (phase) { - case NSTouchPhaseBegan: - SDL_SendTouch(touchId, fingerId, window, SDL_TRUE, x, y, 1.0f); - break; - case NSTouchPhaseEnded: - case NSTouchPhaseCancelled: - SDL_SendTouch(touchId, fingerId, window, SDL_FALSE, x, y, 1.0f); + switch (type) { + case COCOA_TOUCH_DOWN: + SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f); break; - case NSTouchPhaseMoved: - SDL_SendTouchMotion(touchId, fingerId, window, x, y, 1.0f); + case COCOA_TOUCH_UP: + case COCOA_TOUCH_CANCELLED: + SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f); break; - default: + case COCOA_TOUCH_MOVE: + SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f); break; } + + touch = (NSTouch*)[enumerator nextObject]; } } +#endif @end -@interface SDLView : NSView { - SDL_Window *_sdlWindow; -} - -- (void)setSDLWindow:(SDL_Window*)window; +@interface SDLView : NSView /* The default implementation doesn't pass rightMouseDown to responder chain */ - (void)rightMouseDown:(NSEvent *)theEvent; -- (BOOL)mouseDownCanMoveWindow; -- (void)drawRect:(NSRect)dirtyRect; -- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent; -- (BOOL)wantsUpdateLayer; -- (void)updateLayer; @end @implementation SDLView - -- (void)setSDLWindow:(SDL_Window*)window -{ - _sdlWindow = window; -} - -/* this is used on older macOS revisions, and newer ones which emulate old - NSOpenGLContext behaviour while still using a layer under the hood. 10.8 and - later use updateLayer, up until 10.14.2 or so, which uses drawRect without - a GraphicsContext and with a layer active instead (for OpenGL contexts). */ -- (void)drawRect:(NSRect)dirtyRect -{ - /* Force the graphics context to clear to black so we don't get a flash of - white until the app is ready to draw. In practice on modern macOS, this - only gets called for window creation and other extraordinary events. */ - if ([NSGraphicsContext currentContext]) { - [[NSColor blackColor] setFill]; - NSRectFill(dirtyRect); - } else if (self.layer) { - self.layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack); - } - - SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0); -} - -- (BOOL)wantsUpdateLayer -{ - return YES; -} - -/* This is also called when a Metal layer is active. */ -- (void)updateLayer -{ - /* Force the graphics context to clear to black so we don't get a flash of - white until the app is ready to draw. In practice on modern macOS, this - only gets called for window creation and other extraordinary events. */ - self.layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack); - ScheduleContextUpdates((__bridge SDL_WindowData *) _sdlWindow->driverdata); - SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0); -} - - (void)rightMouseDown:(NSEvent *)theEvent { [[self nextResponder] rightMouseDown:theEvent]; } -- (BOOL)mouseDownCanMoveWindow -{ - /* Always say YES, but this doesn't do anything until we call - -[NSWindow setMovableByWindowBackground:YES], which we ninja-toggle - during mouse events when we're using a drag area. */ - return YES; -} - - (void)resetCursorRects { [super resetCursorRects]; - [self addCursorRect:[self bounds] - cursor:Cocoa_GetDesiredCursor()]; -} + SDL_Mouse *mouse = SDL_GetMouse(); -- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent -{ - if (SDL_GetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH)) { - return SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE); + if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) { + [self addCursorRect:[self bounds] + cursor:mouse->cur_cursor->driverdata]; } else { - return SDL_GetHintBoolean("SDL_MAC_MOUSE_FOCUS_CLICKTHROUGH", SDL_FALSE); + [self addCursorRect:[self bounds] + cursor:[NSCursor invisibleCursor]]; } } @end -static int SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, NSView *nsview, SDL_bool created) -{ @autoreleasepool +static int +SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created) { - SDL_VideoData *videodata = (__bridge SDL_VideoData *) _this->driverdata; + NSAutoreleasePool *pool; + SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; SDL_WindowData *data; /* Allocate the window data */ - data = [[SDL_WindowData alloc] init]; + data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data)); if (!data) { return SDL_OutOfMemory(); } - data.window = window; - data.nswindow = nswindow; - data.created = created; - data.videodata = videodata; - data.window_number = nswindow.windowNumber; - data.nscontexts = [[NSMutableArray alloc] init]; - data.sdlContentView = nsview; + data->window = window; + data->nswindow = nswindow; + data->created = created; + data->videodata = videodata; + data->nscontexts = [[NSMutableArray alloc] init]; + + pool = [[NSAutoreleasePool alloc] init]; /* Create an event listener for the window */ - data.listener = [[Cocoa_WindowListener alloc] init]; + data->listener = [[Cocoa_WindowListener alloc] init]; /* Fill in the SDL window with the window data */ { NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]]; - ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect); + ConvertNSRect(&rect); window->x = (int)rect.origin.x; window->y = (int)rect.origin.y; window->w = (int)rect.size.width; @@ -1697,7 +968,7 @@ static int SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, NSVie } /* Set up the listener after we create the view */ - [data.listener listen:data]; + [data->listener listen:data]; if ([nswindow isVisible]) { window->flags |= SDL_WINDOW_SHOWN; @@ -1706,17 +977,14 @@ static int SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, NSVie } { - unsigned long style = [nswindow styleMask]; + unsigned int style = [nswindow styleMask]; - /* NSWindowStyleMaskBorderless is zero, and it's possible to be - Resizeable _and_ borderless, so we can't do a simple bitwise AND - of NSWindowStyleMaskBorderless here. */ - if ((style & ~(NSWindowStyleMaskResizable|NSWindowStyleMaskMiniaturizable)) == NSWindowStyleMaskBorderless) { + if (style == NSBorderlessWindowMask) { window->flags |= SDL_WINDOW_BORDERLESS; } else { window->flags &= ~SDL_WINDOW_BORDERLESS; } - if (style & NSWindowStyleMaskResizable) { + if (style & NSResizableWindowMask) { window->flags |= SDL_WINDOW_RESIZABLE; } else { window->flags &= ~SDL_WINDOW_RESIZABLE; @@ -1738,50 +1006,47 @@ static int SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, NSVie if ([nswindow isKeyWindow]) { window->flags |= SDL_WINDOW_INPUT_FOCUS; - SDL_SetKeyboardFocus(data.window); + SDL_SetKeyboardFocus(data->window); } - /* SDL_WindowData will be holding a strong reference to the NSWindow, and - * 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; - /* Prevents the window's "window device" from being destroyed when it is * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html */ [nswindow setOneShot:NO]; /* All done! */ - window->driverdata = (void *)CFBridgingRetain(data); + [pool release]; + window->driverdata = data; return 0; -}} +} -int Cocoa_CreateWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +int +Cocoa_CreateWindow(_THIS, SDL_Window * window) { - SDL_VideoData *videodata = (__bridge SDL_VideoData *) _this->driverdata; + SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSWindow *nswindow; SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); NSRect rect; SDL_Rect bounds; - NSUInteger style; - NSArray *screens = [NSScreen screens]; - NSScreen *screen = nil; - SDLView *contentView; - BOOL highdpi; + unsigned int style; Cocoa_GetDisplayBounds(_this, display, &bounds); rect.origin.x = window->x; rect.origin.y = window->y; rect.size.width = window->w; rect.size.height = window->h; - ConvertNSRect([screens objectAtIndex:0], (window->flags & FULLSCREEN_MASK), &rect); + ConvertNSRect(&rect); style = GetWindowStyle(window); /* Figure out which screen to place this window */ - for (NSScreen *candidate in screens) { + NSArray *screens = [NSScreen screens]; + NSScreen *screen = nil; + NSScreen *candidate; + int i, count = [screens count]; + for (i = 0; i < count; ++i) { + candidate = [screens objectAtIndex:i]; NSRect screenRect = [candidate frame]; if (rect.origin.x >= screenRect.origin.x && rect.origin.x < screenRect.origin.x + screenRect.size.width && @@ -1797,100 +1062,54 @@ int Cocoa_CreateWindow(_THIS, SDL_Window * window) nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen]; } @catch (NSException *e) { - return SDL_SetError("%s", [[e reason] UTF8String]); - } - - [nswindow setColorSpace:[NSColorSpace sRGBColorSpace]]; - -#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 */ - if ([nswindow respondsToSelector:@selector(setTabbingMode:)]) { - [nswindow setTabbingMode:NSWindowTabbingModeDisallowed]; + SDL_SetError("%s", [[e reason] UTF8String]); + [pool release]; + return -1; } -#endif + [nswindow setBackgroundColor:[NSColor blackColor]]; - if (videodata.allow_spaces) { + if (videodata->allow_spaces) { +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + SDL_assert(videodata->osversion >= 0x1070); + SDL_assert([nswindow respondsToSelector:@selector(toggleFullScreen:)]); /* we put FULLSCREEN_DESKTOP windows in their own Space, without a toggle button or menubar, later */ if (window->flags & SDL_WINDOW_RESIZABLE) { /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */ [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; } - } - - if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) { - [nswindow setLevel:NSFloatingWindowLevel]; +#endif } /* Create a default view for this window */ rect = [nswindow contentRectForFrameRect:[nswindow frame]]; - contentView = [[SDLView alloc] initWithFrame:rect]; - [contentView setSDLWindow:window]; - - /* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */ - #ifdef __clang__ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wdeprecated-declarations" - #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. */ - highdpi = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0; - [contentView setWantsBestResolutionOpenGLSurface:highdpi]; - #ifdef __clang__ - #pragma clang diagnostic pop - #endif - -#ifdef SDL_VIDEO_OPENGL_ES2 -#ifdef SDL_VIDEO_OPENGL_EGL - if ((window->flags & SDL_WINDOW_OPENGL) && - _this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { - [contentView setWantsLayer:TRUE]; - } -#endif /* SDL_VIDEO_OPENGL_EGL */ -#endif /* SDL_VIDEO_OPENGL_ES2 */ - [nswindow setContentView:contentView]; - - if (SetupWindowData(_this, window, nswindow, contentView, SDL_TRUE) < 0) { - return -1; - } + NSView *contentView = [[SDLView alloc] initWithFrame:rect]; - if (!(window->flags & SDL_WINDOW_OPENGL)) { - return 0; + if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { + if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) { + //[contentView setWantsBestResolutionOpenGLSurface:YES]; + } } - /* The rest of this macro mess is for OpenGL or OpenGL ES windows */ -#ifdef SDL_VIDEO_OPENGL_ES2 - if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { -#ifdef SDL_VIDEO_OPENGL_EGL - if (Cocoa_GLES_SetupWindow(_this, window) < 0) { - Cocoa_DestroyWindow(_this, window); - return -1; - } - return 0; -#else - return SDL_SetError("Could not create GLES window surface (EGL support not configured)"); -#endif /* SDL_VIDEO_OPENGL_EGL */ + [nswindow setContentView: contentView]; + [contentView release]; + + [pool release]; + + if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) { + [nswindow release]; + return -1; } -#endif /* SDL_VIDEO_OPENGL_ES2 */ return 0; -}} +} -int Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) -{ @autoreleasepool +int +Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) { - NSView* nsview = nil; - NSWindow *nswindow = nil; + NSAutoreleasePool *pool; + NSWindow *nswindow = (NSWindow *) data; NSString *title; - BOOL highdpi; - - if ([(__bridge id)data isKindOfClass:[NSWindow class]]) { - nswindow = (__bridge NSWindow*)data; - nsview = [nswindow contentView]; - } else if ([(__bridge id)data isKindOfClass:[NSView class]]) { - nsview = (__bridge NSView*)data; - nswindow = [nsview window]; - } else { - SDL_assert(false); - } + + pool = [[NSAutoreleasePool alloc] init]; /* Query the title from the existing window */ title = [nswindow title]; @@ -1898,46 +1117,48 @@ int Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) window->title = SDL_strdup([title UTF8String]); } - /* We still support OpenGL as long as Apple offers it, deprecated or not, so disable deprecation warnings about it. */ - #ifdef __clang__ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wdeprecated-declarations" - #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. */ - highdpi = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0; - [nsview setWantsBestResolutionOpenGLSurface:highdpi]; - #ifdef __clang__ - #pragma clang diagnostic pop - #endif - - return SetupWindowData(_this, window, nswindow, nsview, SDL_FALSE); -}} - -void Cocoa_SetWindowTitle(_THIS, SDL_Window * window) -{ @autoreleasepool -{ - const char *title = window->title ? window->title : ""; - NSWindow *nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; - NSString *string = [[NSString alloc] initWithUTF8String:title]; + [pool release]; + + return SetupWindowData(_this, window, nswindow, SDL_FALSE); +} + +void +Cocoa_SetWindowTitle(_THIS, SDL_Window * window) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow; + NSString *string; + + if(window->title) { + string = [[NSString alloc] initWithUTF8String:window->title]; + } else { + string = [[NSString alloc] init]; + } [nswindow setTitle:string]; -}} + [string release]; + + [pool release]; +} -void Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) -{ @autoreleasepool +void +Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSImage *nsimage = Cocoa_CreateImage(icon); if (nsimage) { [NSApp setApplicationIconImage:nsimage]; } -}} -void Cocoa_SetWindowPosition(_THIS, SDL_Window * window) -{ @autoreleasepool + [pool release]; +} + +void +Cocoa_SetWindowPosition(_THIS, SDL_Window * window) { - SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; - NSWindow *nswindow = windata.nswindow; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = windata->nswindow; NSRect rect; Uint32 moveHack; @@ -1945,7 +1166,7 @@ void Cocoa_SetWindowPosition(_THIS, SDL_Window * window) rect.origin.y = window->y; rect.size.width = window->w; rect.size.height = window->h; - ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect); + ConvertNSRect(&rect); moveHack = s_moveHack; s_moveHack = 0; @@ -1953,203 +1174,188 @@ void Cocoa_SetWindowPosition(_THIS, SDL_Window * window) s_moveHack = moveHack; ScheduleContextUpdates(windata); -}} -void Cocoa_SetWindowSize(_THIS, SDL_Window * window) -{ @autoreleasepool -{ - SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; - NSWindow *nswindow = windata.nswindow; - NSRect rect; - Uint32 moveHack; + [pool release]; +} - /* Cocoa will resize the window from the bottom-left rather than the - * top-left when -[nswindow setContentSize:] is used, so we must set the - * entire frame based on the new size, in order to preserve the position. - */ - rect.origin.x = window->x; - rect.origin.y = window->y; - rect.size.width = window->w; - rect.size.height = window->h; - ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect); +void +Cocoa_SetWindowSize(_THIS, SDL_Window * window) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = windata->nswindow; + NSSize size; - moveHack = s_moveHack; - s_moveHack = 0; - [nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES]; - s_moveHack = moveHack; + size.width = window->w; + size.height = window->h; + [nswindow setContentSize:size]; ScheduleContextUpdates(windata); -}} -void Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window) -{ @autoreleasepool + [pool release]; +} + +void +Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window) { - SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; NSSize minSize; minSize.width = window->min_w; minSize.height = window->min_h; - [windata.nswindow setContentMinSize:minSize]; -}} + [windata->nswindow setContentMinSize:minSize]; + + [pool release]; +} -void Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window) -{ @autoreleasepool +void +Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window) { - SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; NSSize maxSize; maxSize.width = window->max_w; maxSize.height = window->max_h; - [windata.nswindow setContentMaxSize:maxSize]; -}} - -void Cocoa_GetWindowSizeInPixels(_THIS, SDL_Window * window, int *w, int *h) -{ @autoreleasepool -{ - SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; - NSView *contentView = windata.sdlContentView; - NSRect viewport = [contentView bounds]; - - if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { - /* This gives us the correct viewport for a Retina-enabled view. */ - viewport = [contentView convertRectToBacking:viewport]; - } - - *w = viewport.size.width; - *h = viewport.size.height; -}} + [windata->nswindow setContentMaxSize:maxSize]; + [pool release]; +} -void Cocoa_ShowWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +void +Cocoa_ShowWindow(_THIS, SDL_Window * window) { - SDL_WindowData *windowData = ((__bridge SDL_WindowData *) window->driverdata); - NSWindow *nswindow = windowData.nswindow; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata); + NSWindow *nswindow = windowData->nswindow; if (![nswindow isMiniaturized]) { - [windowData.listener pauseVisibleObservation]; + [windowData->listener pauseVisibleObservation]; [nswindow makeKeyAndOrderFront:nil]; - [windowData.listener resumeVisibleObservation]; + [windowData->listener resumeVisibleObservation]; } -}} + [pool release]; +} -void Cocoa_HideWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +void +Cocoa_HideWindow(_THIS, SDL_Window * window) { - NSWindow *nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow; [nswindow orderOut:nil]; -}} + [pool release]; +} -void Cocoa_RaiseWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +void +Cocoa_RaiseWindow(_THIS, SDL_Window * window) { - SDL_WindowData *windowData = ((__bridge SDL_WindowData *) window->driverdata); - NSWindow *nswindow = windowData.nswindow; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata); + NSWindow *nswindow = windowData->nswindow; /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing a minimized or hidden window, so check for that before showing it. */ - [windowData.listener pauseVisibleObservation]; + [windowData->listener pauseVisibleObservation]; if (![nswindow isMiniaturized] && [nswindow isVisible]) { - [NSApp activateIgnoringOtherApps:YES]; [nswindow makeKeyAndOrderFront:nil]; } - [windowData.listener resumeVisibleObservation]; -}} + [windowData->listener resumeVisibleObservation]; + + [pool release]; +} -void Cocoa_MaximizeWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +void +Cocoa_MaximizeWindow(_THIS, SDL_Window * window) { - SDL_WindowData *windata = (__bridge SDL_WindowData *) window->driverdata; - NSWindow *nswindow = windata.nswindow; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = windata->nswindow; [nswindow zoom:nil]; ScheduleContextUpdates(windata); -}} -void Cocoa_MinimizeWindow(_THIS, SDL_Window * window) -{ @autoreleasepool + [pool release]; +} + +void +Cocoa_MinimizeWindow(_THIS, SDL_Window * window) { - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - NSWindow *nswindow = data.nswindow; - if ([data.listener isInFullscreenSpaceTransition]) { - [data.listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE]; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = data->nswindow; + + if ([data->listener isInFullscreenSpaceTransition]) { + [data->listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE]; } else { [nswindow miniaturize:nil]; } -}} + [pool release]; +} -void Cocoa_RestoreWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +void +Cocoa_RestoreWindow(_THIS, SDL_Window * window) { - NSWindow *nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow; if ([nswindow isMiniaturized]) { [nswindow deminiaturize:nil]; } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) { [nswindow zoom:nil]; } -}} + [pool release]; +} -void Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) -{ @autoreleasepool +static NSWindow * +Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style) { - if (SetWindowStyle(window, GetWindowStyle(window))) { - if (bordered) { - Cocoa_SetWindowTitle(_this, window); /* this got blanked out. */ - } + if (!data->created) { + /* Don't mess with other people's windows... */ + return nswindow; } -}} -void Cocoa_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable) -{ @autoreleasepool + [data->listener close]; + data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]]; + [data->nswindow setContentView:[nswindow contentView]]; + /* See comment in SetupWindowData. */ + [data->nswindow setOneShot:NO]; + [data->listener listen:data]; + + [nswindow close]; + + return data->nswindow; +} + +void +Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) { - /* Don't set this if we're in a space! - * The window will get permanently stuck if resizable is false. - * -flibit - */ - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - Cocoa_WindowListener *listener = data.listener; - NSWindow *nswindow = data.nswindow; - SDL_VideoData *videodata = data.videodata; - if (![listener isInFullscreenSpace]) { - SetWindowStyle(window, GetWindowStyle(window)); - } - if (videodata.allow_spaces) { - if (resizable) { - /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */ - [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - } else { - [nswindow setCollectionBehavior:NSWindowCollectionBehaviorManaged]; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + if (SetWindowStyle(window, GetWindowStyle(window))) { + if (bordered) { + Cocoa_SetWindowTitle(_this, window); /* this got blanked out. */ } } -}} + [pool release]; +} -void Cocoa_SetWindowAlwaysOnTop(_THIS, SDL_Window * window, SDL_bool on_top) -{ @autoreleasepool - { - NSWindow *nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; - if (on_top) { - [nswindow setLevel:NSFloatingWindowLevel]; - } else { - [nswindow setLevel:kCGNormalWindowLevel]; - } - }} -void Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) -{ @autoreleasepool +void +Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) { - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - NSWindow *nswindow = data.nswindow; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = data->nswindow; NSRect rect; /* The view responder chain gets messed with during setStyleMask */ - if ([data.sdlContentView nextResponder] == data.listener) { - [data.sdlContentView setNextResponder:nil]; + if ([[nswindow contentView] nextResponder] == data->listener) { + [[nswindow contentView] setNextResponder:nil]; } if (fullscreen) { @@ -2160,44 +1366,36 @@ void Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * di rect.origin.y = bounds.y; rect.size.width = bounds.w; rect.size.height = bounds.h; - ConvertNSRect([nswindow screen], fullscreen, &rect); + ConvertNSRect(&rect); - /* Hack to fix origin on Mac OS X 10.4 - This is no longer needed as of Mac OS X 10.15, according to bug 4822. - */ - if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_14) { - NSRect screenRect = [[nswindow screen] frame]; - if (screenRect.size.height >= 1.0f) { - rect.origin.y += (screenRect.size.height - rect.size.height); - } + /* Hack to fix origin on Mac OS X 10.4 */ + NSRect screenRect = [[nswindow screen] frame]; + if (screenRect.size.height >= 1.0f) { + rect.origin.y += (screenRect.size.height - rect.size.height); } - [nswindow setStyleMask:NSWindowStyleMaskBorderless]; + if ([nswindow respondsToSelector: @selector(setStyleMask:)]) { + [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask]; + } else { + nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask); + } } else { - NSRect frameRect; rect.origin.x = window->windowed.x; rect.origin.y = window->windowed.y; rect.size.width = window->windowed.w; rect.size.height = window->windowed.h; - ConvertNSRect([nswindow screen], fullscreen, &rect); + ConvertNSRect(&rect); - /* The window is not meant to be fullscreen, but its flags might have a - * fullscreen bit set if it's scheduled to go fullscreen immediately - * after. Always using the windowed mode style here works around bugs in - * 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. */ - [nswindow setStyleMask:GetWindowWindowedStyle(window)]; - - /* Hack to restore window decorations on Mac OS X 10.10 */ - frameRect = [nswindow frame]; - [nswindow setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO]; - [nswindow setFrame:frameRect display:NO]; + if ([nswindow respondsToSelector: @selector(setStyleMask:)]) { + [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)]; + } else { + nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window)); + } } /* The view responder chain gets messed with during setStyleMask */ - if ([data.sdlContentView nextResponder] != data.listener) { - [data.sdlContentView setNextResponder:data.listener]; + if ([[nswindow contentView] nextResponder] != data->listener) { + [[nswindow contentView] setNextResponder:data->listener]; } s_moveHack = 0; @@ -2213,23 +1411,23 @@ void Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * di if (SDL_ShouldAllowTopmost() && fullscreen) { /* OpenGL is rendering to the window, so make it visible! */ [nswindow setLevel:CGShieldingWindowLevel()]; - } else if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) { - [nswindow setLevel:NSFloatingWindowLevel]; } else { [nswindow setLevel:kCGNormalWindowLevel]; } if ([nswindow isVisible] || fullscreen) { - [data.listener pauseVisibleObservation]; + [data->listener pauseVisibleObservation]; [nswindow makeKeyAndOrderFront:nil]; - [data.listener resumeVisibleObservation]; + [data->listener resumeVisibleObservation]; } ScheduleContextUpdates(data); -}} -int Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) -{ @autoreleasepool + [pool release]; +} + +int +Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) { SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display; @@ -2252,83 +1450,10 @@ int Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) return SDL_SetError("CGSetDisplayTransferByTable()"); } return 0; -}} - -void *Cocoa_GetWindowICCProfile(_THIS, SDL_Window * window, size_t * size) -{ @autoreleasepool -{ - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - NSWindow *nswindow = data.nswindow; - NSScreen *screen = [nswindow screen]; - NSData* iccProfileData = nil; - void* retIccProfileData = NULL; - - if (screen == nil) { - SDL_SetError("Could not get screen of window."); - return NULL; - } - - if ([screen colorSpace] == nil) { - SDL_SetError("Could not get colorspace information of screen."); - return NULL; - } - - iccProfileData = [[screen colorSpace] ICCProfileData]; - if (iccProfileData == nil) { - SDL_SetError("Could not get ICC profile data."); - return NULL; - } - - retIccProfileData = SDL_malloc([iccProfileData length]); - if (!retIccProfileData) { - SDL_OutOfMemory(); - return NULL; - } - - [iccProfileData getBytes:retIccProfileData length:[iccProfileData length]]; - *size = [iccProfileData length]; - return retIccProfileData; -}} - -int Cocoa_GetWindowDisplayIndex(_THIS, SDL_Window * window) -{ @autoreleasepool -{ - NSScreen *screen; - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - - /* Not recognized via CHECK_WINDOW_MAGIC */ - if (data == nil) { - /* Don't set the error here, it hides other errors and is ignored anyway */ - /*return SDL_SetError("Window data not set");*/ - return -1; - } - - /* NSWindow.screen may be nil when the window is off-screen. */ - screen = data.nswindow.screen; - - if (screen != nil) { - CGDirectDisplayID displayid; - int i; - - /* https://developer.apple.com/documentation/appkit/nsscreen/1388360-devicedescription?language=objc */ - displayid = [[screen.deviceDescription objectForKey:@"NSScreenNumber"] unsignedIntValue]; - - for (i = 0; i < _this->num_displays; i++) { - SDL_DisplayData *displaydata = (SDL_DisplayData *)_this->displays[i].driverdata; - if (displaydata != NULL && displaydata->display == displayid) { - return i; - } - } - } - - /* Other code may expect SDL_GetWindowDisplayIndex to always return a valid - * index for a window. The higher level GetWindowDisplayIndex code will fall - * back to a generic position-based query if the backend implementation - * fails. */ - return SDL_SetError("Couldn't find the display where the window is located."); -}} +} -int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp) +int +Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp) { SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display; @@ -2351,191 +1476,113 @@ int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp) return 0; } -void Cocoa_SetWindowMouseRect(_THIS, SDL_Window * window) +void +Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed) { - Cocoa_UpdateClipCursor(window); -} + /* Move the cursor to the nearest point in the window */ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if (grabbed && data && ![data->listener isMoving]) { + int x, y; + CGPoint cgpoint; -void Cocoa_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed) -{ @autoreleasepool -{ - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; + SDL_GetMouseState(&x, &y); + cgpoint.x = window->x + x; + cgpoint.y = window->y + y; + + Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y); - Cocoa_UpdateClipCursor(window); + DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y); + CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint); + } + + if ( window->flags & SDL_WINDOW_FULLSCREEN ) { + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; - if (data && (window->flags & SDL_WINDOW_FULLSCREEN)) { - if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS) - && ![data.listener isInFullscreenSpace]) { + if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { /* OpenGL is rendering to the window, so make it visible! */ - /* Doing this in 10.11 while in a Space breaks things (bug #3152) */ - [data.nswindow setLevel:CGShieldingWindowLevel()]; - } else if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) { - [data.nswindow setLevel:NSFloatingWindowLevel]; + [data->nswindow setLevel:CGShieldingWindowLevel()]; } else { - [data.nswindow setLevel:kCGNormalWindowLevel]; + [data->nswindow setLevel:kCGNormalWindowLevel]; } } -}} +} -void Cocoa_DestroyWindow(_THIS, SDL_Window * window) -{ @autoreleasepool +void +Cocoa_DestroyWindow(_THIS, SDL_Window * window) { - SDL_WindowData *data = (SDL_WindowData *) CFBridgingRelease(window->driverdata); + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; if (data) { -#ifdef SDL_VIDEO_OPENGL - NSArray *contexts; -#endif - - if ([data.listener isInFullscreenSpace]) { - [NSMenu setMenuBarVisible:YES]; - } - [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]; + [data->listener close]; + [data->listener release]; + if (data->created) { + [data->nswindow close]; } -#ifdef SDL_VIDEO_OPENGL - contexts = [data.nscontexts copy]; + NSArray *contexts = [[data->nscontexts copy] autorelease]; +#if defined(MAC_OS_X_VERSION_10_5) for (SDLOpenGLContext *context in contexts) { - /* Calling setWindow:NULL causes the context to remove itself from the context list. */ +#else + /* old way to iterate */ + int i; + for (i = 0; i < [contexts count]; i++) { + SDLOpenGLContext *context = [contexts objectAtIndex:i]; +#endif + /* Calling setWindow:NULL causes the context to remove itself from the context list. */ [context setWindow:NULL]; } -#endif /* SDL_VIDEO_OPENGL */ + [data->nscontexts release]; - if (window->shaper) { - CFBridgingRelease(window->shaper->driverdata); - SDL_free(window->shaper); - window->shaper = NULL; - } + SDL_free(data); } - window->driverdata = NULL; -}} + [pool release]; +} -SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) -{ @autoreleasepool +SDL_bool +Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) { - NSWindow *nswindow = ((__bridge SDL_WindowData *) window->driverdata).nswindow; + NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow; if (info->version.major <= SDL_MAJOR_VERSION) { info->subsystem = SDL_SYSWM_COCOA; info->info.cocoa.window = nswindow; return SDL_TRUE; } else { - SDL_SetError("Application not compiled with SDL %d", - SDL_MAJOR_VERSION); + SDL_SetError("Application not compiled with SDL %d.%d\n", + SDL_MAJOR_VERSION, SDL_MINOR_VERSION); return SDL_FALSE; } -}} +} -SDL_bool Cocoa_IsWindowInFullscreenSpace(SDL_Window * window) -{ @autoreleasepool +SDL_bool +Cocoa_IsWindowInFullscreenSpace(SDL_Window * window) { - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; - if ([data.listener isInFullscreenSpace]) { + if ([data->listener isInFullscreenSpace]) { return SDL_TRUE; } else { return SDL_FALSE; } -}} +} -SDL_bool Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state) -{ @autoreleasepool +SDL_bool +Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state) { SDL_bool succeeded = SDL_FALSE; - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - - if (data.inWindowFullscreenTransition) { - return SDL_FALSE; - } + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; - data.inWindowFullscreenTransition = SDL_TRUE; - if ([data.listener setFullscreenSpace:(state ? YES : NO)]) { - const int maxattempts = 3; - int attempt = 0; - while (++attempt <= maxattempts) { - /* Wait for the transition to complete, so application changes - take effect properly (e.g. setting the window size, etc.) - */ - const int limit = 10000; - int count = 0; - while ([data.listener isInFullscreenSpaceTransition]) { - if ( ++count == limit ) { - /* Uh oh, transition isn't completing. Should we assert? */ - break; - } - SDL_Delay(1); - SDL_PumpEvents(); - } - if ([data.listener isInFullscreenSpace] == (state ? YES : NO)) - break; - /* Try again, the last attempt was interrupted by user gestures */ - if (![data.listener setFullscreenSpace:(state ? YES : NO)]) - break; /* ??? */ - } - /* Return TRUE to prevent non-space fullscreen logic from running */ + if ([data->listener setFullscreenSpace:(state ? YES : NO)]) { succeeded = SDL_TRUE; } - data.inWindowFullscreenTransition = SDL_FALSE; - return succeeded; -}} + [pool release]; -int Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled) -{ - return 0; /* just succeed, the real work is done elsewhere. */ + return succeeded; } -void Cocoa_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept) -{ @autoreleasepool -{ - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - if (accept) { - [data.nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]]; - } else { - [data.nswindow unregisterDraggedTypes]; - } -}} - -int Cocoa_FlashWindow(_THIS, SDL_Window *window, SDL_FlashOperation operation) -{ @autoreleasepool -{ - /* Note that this is app-wide and not window-specific! */ - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - - if (data.flash_request) { - [NSApp cancelUserAttentionRequest:data.flash_request]; - data.flash_request = 0; - } - - switch (operation) { - case SDL_FLASH_CANCEL: - /* Canceled above */ - break; - case SDL_FLASH_BRIEFLY: - data.flash_request = [NSApp requestUserAttention:NSInformationalRequest]; - break; - case SDL_FLASH_UNTIL_FOCUSED: - data.flash_request = [NSApp requestUserAttention:NSCriticalRequest]; - break; - default: - return SDL_Unsupported(); - } - return 0; -}} - -int Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity) -{ @autoreleasepool -{ - SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; - [data.nswindow setAlphaValue:opacity]; - return 0; -}} - #endif /* SDL_VIDEO_DRIVER_COCOA */ /* vi: set ts=4 sw=4 expandtab: */ -- 2.48.0