diff options
author | chai <chaifix@163.com> | 2019-05-11 22:54:56 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2019-05-11 22:54:56 +0800 |
commit | 9645be0af1b1d5cb0ad5892d5464e1b23c51b550 (patch) | |
tree | 129c716bed8e93312421c3adb2f8e7c4f811602d /source/3rd-party/SDL2/src/video/cocoa |
Diffstat (limited to 'source/3rd-party/SDL2/src/video/cocoa')
28 files changed, 6738 insertions, 0 deletions
diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaclipboard.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaclipboard.h new file mode 100644 index 0000000..54e4c88 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaclipboard.h @@ -0,0 +1,36 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_cocoaclipboard_h_ +#define SDL_cocoaclipboard_h_ + +/* Forward declaration */ +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(struct SDL_VideoData * data); + +#endif /* SDL_cocoaclipboard_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaclipboard.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaclipboard.m new file mode 100644 index 0000000..9c96634 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaclipboard.m @@ -0,0 +1,103 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_COCOA + +#include "SDL_cocoavideo.h" +#include "../../events/SDL_clipboardevents_c.h" + +int +Cocoa_SetClipboardText(_THIS, const char *text) +{ @autoreleasepool +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + NSPasteboard *pasteboard; + NSString *format = NSPasteboardTypeString; + + pasteboard = [NSPasteboard generalPasteboard]; + data->clipboard_count = [pasteboard declareTypes:[NSArray arrayWithObject:format] owner:nil]; + [pasteboard setString:[NSString stringWithUTF8String:text] forType:format]; + + return 0; +}} + +char * +Cocoa_GetClipboardText(_THIS) +{ @autoreleasepool +{ + NSPasteboard *pasteboard; + NSString *format = NSPasteboardTypeString; + NSString *available; + char *text; + + pasteboard = [NSPasteboard generalPasteboard]; + available = [pasteboard availableTypeFromArray:[NSArray arrayWithObject:format]]; + if ([available isEqualToString:format]) { + NSString* string; + const char *utf8; + + string = [pasteboard stringForType:format]; + if (string == nil) { + utf8 = ""; + } else { + utf8 = [string UTF8String]; + } + text = SDL_strdup(utf8); + } else { + text = SDL_strdup(""); + } + + return text; +}} + +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); + } + return result; +} + +void +Cocoa_CheckClipboardUpdate(struct SDL_VideoData * data) +{ @autoreleasepool +{ + NSPasteboard *pasteboard; + NSInteger count; + + pasteboard = [NSPasteboard generalPasteboard]; + count = [pasteboard changeCount]; + if (count != data->clipboard_count) { + if (data->clipboard_count) { + SDL_SendClipboardUpdate(); + } + data->clipboard_count = count; + } +}} + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaevents.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaevents.h new file mode 100644 index 0000000..986168e --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaevents.h @@ -0,0 +1,32 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_cocoaevents_h_ +#define SDL_cocoaevents_h_ + +extern void Cocoa_RegisterApp(void); +extern void Cocoa_PumpEvents(_THIS); +extern void Cocoa_SuspendScreenSaver(_THIS); + +#endif /* SDL_cocoaevents_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaevents.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaevents.m new file mode 100644 index 0000000..76d235e --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaevents.m @@ -0,0 +1,483 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_COCOA +#include "SDL_timer.h" + +#include "SDL_cocoavideo.h" +#include "../../events/SDL_events_c.h" +#include "SDL_assert.h" +#include "SDL_hints.h" + +/* This define was added in the 10.9 SDK. */ +#ifndef kIOPMAssertPreventUserIdleDisplaySleep +#define kIOPMAssertPreventUserIdleDisplaySleep kIOPMAssertionTypePreventUserIdleDisplaySleep +#endif + +@interface SDLApplication : NSApplication + +- (void)terminate:(id)sender; +- (void)sendEvent:(NSEvent *)theEvent; + ++ (void)registerUserDefaults; + +@end + +@implementation SDLApplication + +// Override terminate to handle Quit and System Shutdown smoothly. +- (void)terminate:(id)sender +{ + 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]; + [appDefaults release]; +} + +@end // SDLApplication + +/* setAppleMenu disappeared from the headers in 10.4 */ +@interface NSApplication(NSAppleMenu) +- (void)setAppleMenu:(NSMenu *)menu; +@end + +@interface SDLAppDelegate : NSObject <NSApplicationDelegate> { +@public + BOOL seenFirstActivate; +} + +- (id)init; +@end + +@implementation SDLAppDelegate : NSObject +- (id)init +{ + self = [super init]; + if (self) { + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + + seenFirstActivate = NO; + + [center addObserver:self + selector:@selector(windowWillClose:) + name:NSWindowWillCloseNotification + object:nil]; + + [center addObserver:self + selector:@selector(focusSomeWindow:) + name:NSApplicationDidBecomeActiveNotification + object:nil]; + } + + return self; +} + +- (void)dealloc +{ + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + + [center removeObserver:self name:NSWindowWillCloseNotification object:nil]; + [center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil]; + + [super dealloc]; +} + +- (void)windowWillClose:(NSNotification *)notification; +{ + NSWindow *win = (NSWindow*)[notification object]; + + if (![win isKeyWindow]) { + 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; + } + } +} + +- (void)focusSomeWindow:(NSNotification *)aNotification +{ + /* 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. + */ + if (!seenFirstActivate) { + seenFirstActivate = YES; + return; + } + + SDL_VideoDevice *device = SDL_GetVideoDevice(); + if (device && device->windows) { + SDL_Window *window = device->windows; + int i; + for (i = 0; i < device->num_displays; ++i) { + SDL_Window *fullscreen_window = device->displays[i].fullscreen_window; + if (fullscreen_window) { + if (fullscreen_window->flags & SDL_WINDOW_MINIMIZED) { + SDL_RestoreWindow(fullscreen_window); + } + return; + } + } + + if (window->flags & SDL_WINDOW_MINIMIZED) { + SDL_RestoreWindow(window); + } else { + SDL_RaiseWindow(window); + } + } +} + +- (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)) { + [NSApp activateIgnoringOtherApps:YES]; + } + + /* If we call this before NSApp activation, macOS might print a complaint + * about ApplePersistenceIgnoreState. */ + [SDLApplication registerUserDefaults]; +} +@end + +static SDLAppDelegate *appDelegate = nil; + +static NSString * +GetApplicationName(void) +{ + NSString *appName; + + /* Determine the application name */ + appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]; + if (!appName) { + appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; + } + + if (![appName length]) { + appName = [[NSProcessInfo processInfo] processName]; + } + + return appName; +} + +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]; + + [mainMenu release]; /* we're done with it, let NSApp own it. */ + mainMenu = nil; + + /* Create the application menu */ + appName = GetApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + [appleMenu addItemWithTitle:@"Preferences…" action:nil keyEquivalent:@","]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + serviceMenu = [[NSMenu alloc] initWithTitle:@""]; + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:serviceMenu]; + + [NSApp setServicesMenu:serviceMenu]; + [serviceMenu release]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + /* Put menu into the menubar */ + 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:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + + [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; + + /* 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 (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) { + /* 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:NSEventModifierFlagControl | NSEventModifierFlagCommand]; + + /* 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 +{ + /* This can get called more than once! Be careful what you initialize! */ + + 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 ([NSApp mainMenu] == nil) { + 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]; + } + } + if (NSApp && !appDelegate) { + appDelegate = [[SDLAppDelegate alloc] init]; + + /* If someone else has an app delegate, it means we can't turn a + * termination into SDL_Quit, and we can't handle application:openFile: + */ + if (![NSApp delegate]) { + [(NSApplication *)NSApp setDelegate:appDelegate]; + } else { + appDelegate->seenFirstActivate = YES; + } + } +}} + +void +Cocoa_PumpEvents(_THIS) +{ @autoreleasepool +{ +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 + /* Update activity every 30 seconds to prevent screensaver */ + SDL_VideoData *data = (SDL_VideoData *)_this->driverdata; + if (_this->suspend_screensaver && !data->screensaver_use_iopm) { + Uint32 now = SDL_GetTicks(); + if (!data->screensaver_activity || + SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) { + UpdateSystemActivity(UsrActivity); + data->screensaver_activity = now; + } + } +#endif + + for ( ; ; ) { + NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES ]; + if ( event == nil ) { + break; + } + + if (!s_bShouldHandleEventsInSDLApplication) { + Cocoa_DispatchEvent(event); + } + + // Pass events down to SDLApplication to be handled in sendEvent: + [NSApp sendEvent:event]; + } +}} + +void +Cocoa_SuspendScreenSaver(_THIS) +{ @autoreleasepool +{ + SDL_VideoData *data = (SDL_VideoData *)_this->driverdata; + + if (!data->screensaver_use_iopm) { + return; + } + + if (data->screensaver_assertion) { + IOPMAssertionRelease(data->screensaver_assertion); + data->screensaver_assertion = 0; + } + + 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. + */ + NSString *name = [GetApplicationName() stringByAppendingString:@" using SDL_DisableScreenSaver"]; + IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep, + (CFStringRef) name, + NULL, NULL, NULL, 0, NULL, + &data->screensaver_assertion); + } +}} + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoakeyboard.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoakeyboard.h new file mode 100644 index 0000000..7d89523 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoakeyboard.h @@ -0,0 +1,36 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_cocoakeyboard_h_ +#define SDL_cocoakeyboard_h_ + +extern void Cocoa_InitKeyboard(_THIS); +extern void Cocoa_HandleKeyEvent(_THIS, NSEvent * event); +extern void Cocoa_QuitKeyboard(_THIS); + +extern void Cocoa_StartTextInput(_THIS); +extern void Cocoa_StopTextInput(_THIS); +extern void Cocoa_SetTextInputRect(_THIS, SDL_Rect *rect); + +#endif /* SDL_cocoakeyboard_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoakeyboard.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoakeyboard.m new file mode 100644 index 0000000..8436047 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoakeyboard.m @@ -0,0 +1,720 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#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" + +#include <Carbon/Carbon.h> +#include <IOKit/hid/IOHIDLib.h> + +/*#define DEBUG_IME NSLog */ +#define DEBUG_IME(...) + +@interface SDLTranslatorResponder : NSView <NSTextInputClient> { + NSString *_markedText; + NSRange _markedRange; + NSRange _selectedRange; + SDL_Rect _inputRect; +} +- (void)doCommandBySelector:(SEL)myselector; +- (void)setInputRect:(SDL_Rect *)rect; +@end + +@implementation SDLTranslatorResponder + +- (void)setInputRect:(SDL_Rect *)rect +{ + _inputRect = *rect; +} + +- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange +{ + /* 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]]) { + str = [[aString string] UTF8String]; + } else { + str = [aString UTF8String]; + } + + SDL_SendKeyboardText(str); +} + +- (void)doCommandBySelector:(SEL)myselector +{ + /* No need to do anything since we are not using Cocoa + selectors to handle special keys, instead we use SDL + key events to do the same job. + */ +} + +- (BOOL)hasMarkedText +{ + return _markedText != nil; +} + +- (NSRange)markedRange +{ + return _markedRange; +} + +- (NSRange)selectedRange +{ + return _selectedRange; +} + +- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange +{ + if ([aString isKindOfClass:[NSAttributedString class]]) { + aString = [aString string]; + } + + if ([aString length] == 0) { + [self unmarkText]; + return; + } + + if (_markedText != aString) { + [_markedText release]; + _markedText = [aString retain]; + } + + _selectedRange = selectedRange; + _markedRange = NSMakeRange(0, [aString length]); + + SDL_SendEditingText([aString UTF8String], + (int) selectedRange.location, (int) selectedRange.length); + + DEBUG_IME(@"setMarkedText: %@, (%d, %d)", _markedText, + selRange.location, selRange.length); +} + +- (void)unmarkText +{ + [_markedText release]; + _markedText = nil; + + SDL_SendEditingText("", 0, 0); +} + +- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange +{ + NSWindow *window = [self window]; + 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, + NSStringFromRect(rect)); + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 + if (![window respondsToSelector:@selector(convertRectToScreen:)]) { + rect.origin = [window convertBaseToScreen:rect.origin]; + } else +#endif + { + rect = [window convertRectToScreen:rect]; + } + + return rect; +} + +- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange +{ + DEBUG_IME(@"attributedSubstringFromRange: (%d, %d)", aRange.location, aRange.length); + return nil; +} + +- (NSInteger)conversationIdentifier +{ + return (NSInteger) self; +} + +/* This method returns the index for character that is + * nearest to thePoint. thPoint is in screen coordinate system. + */ +- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint +{ + DEBUG_IME(@"characterIndexForPoint: (%g, %g)", thePoint.x, thePoint.y); + return 0; +} + +/* This method is the key to attribute extension. + * We could add new attributes through this method. + * NSInputServer examines the return value of this + * method & constructs appropriate attributed string. + */ +- (NSArray *)validAttributesForMarkedText +{ + return [NSArray array]; +} + +@end + +/*------------------------------------------------------------------------------ +Set up a HID callback to properly detect Caps Lock up/down events. +Derived from: +http://stackoverflow.com/questions/7190852/using-iohidmanager-to-get-modifier-key-events +*/ + +static IOHIDManagerRef s_hidManager = NULL; + +static void +HIDCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) +{ + if (context != s_hidManager) { + /* An old callback, ignore it (related to bug 2157 below) */ + return; + } + + IOHIDElementRef elem = IOHIDValueGetElement(value); + if (IOHIDElementGetUsagePage(elem) != kHIDPage_KeyboardOrKeypad + || IOHIDElementGetUsage(elem) != kHIDUsage_KeyboardCapsLock) { + return; + } + CFIndex pressed = IOHIDValueGetIntegerValue(value); + SDL_SendKeyboardKey(pressed ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_CAPSLOCK); +} + +static CFDictionaryRef +CreateHIDDeviceMatchingDictionary(UInt32 usagePage, UInt32 usage) +{ + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (dict) { + CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage); + if (number) { + CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), number); + CFRelease(number); + number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); + if (number) { + CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), number); + CFRelease(number); + return dict; + } + } + CFRelease(dict); + } + return NULL; +} + +static void +QuitHIDCallback() +{ + if (!s_hidManager) { + return; + } + +#if 0 /* Releasing here causes a crash on Mac OS X 10.10 and earlier, + * so just leak it for now. See bug 2157 for details. + */ + IOHIDManagerUnscheduleFromRunLoop(s_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDManagerRegisterInputValueCallback(s_hidManager, NULL, NULL); + IOHIDManagerClose(s_hidManager, 0); + + CFRelease(s_hidManager); +#endif + s_hidManager = NULL; +} + +static void +InitHIDCallback() +{ + s_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (!s_hidManager) { + return; + } + CFDictionaryRef keyboard = NULL, keypad = NULL; + CFArrayRef matches = NULL; + keyboard = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard); + if (!keyboard) { + goto fail; + } + keypad = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keypad); + if (!keypad) { + goto fail; + } + CFDictionaryRef matchesList[] = { keyboard, keypad }; + matches = CFArrayCreate(kCFAllocatorDefault, (const void **)matchesList, 2, NULL); + if (!matches) { + goto fail; + } + IOHIDManagerSetDeviceMatchingMultiple(s_hidManager, matches); + IOHIDManagerRegisterInputValueCallback(s_hidManager, HIDCallback, s_hidManager); + IOHIDManagerScheduleWithRunLoop(s_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); + if (IOHIDManagerOpen(s_hidManager, kIOHIDOptionsTypeNone) == kIOReturnSuccess) { + goto cleanup; + } + +fail: + QuitHIDCallback(); + +cleanup: + if (matches) { + CFRelease(matches); + } + if (keypad) { + CFRelease(keypad); + } + if (keyboard) { + CFRelease(keyboard); + } +} + +/* 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) +{ + 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); + } +} + +/* 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); + } +} + +/* 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); + } + } +} + +/* 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) +{ + 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; + } + + /* + * 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 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; + + /* Iterate through the bits, testing each against the old modifiers */ + for (i = 0, bit = NSEventModifierFlagShift; bit <= NSEventModifierFlagCommand; 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 +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, SDL_bool send_event) +{ + TISInputSourceRef key_layout; + const void *chr_data; + int i; + SDL_Scancode scancode; + SDL_Keycode keymap[SDL_NUM_SCANCODES]; + + /* See if the keymap needs to be updated */ + key_layout = TISCopyCurrentKeyboardLayoutInputSource(); + if (key_layout == data->key_layout) { + return; + } + data->key_layout = key_layout; + + SDL_GetDefaultKeymap(keymap); + + /* Try Unicode data first */ + CFDataRef uchrDataRef = TISGetInputSourceProperty(key_layout, kTISPropertyUnicodeKeyLayoutData); + if (uchrDataRef) { + chr_data = CFDataGetBytePtr(uchrDataRef); + } else { + goto cleanup; + } + + if (chr_data) { + UInt32 keyboard_type = LMGetKbdType(); + OSStatus err; + + for (i = 0; i < SDL_arraysize(darwin_scancode_table); i++) { + UniChar s[8]; + UniCharCount len; + UInt32 dead_key_state; + + /* Make sure this scancode is a valid character scancode */ + scancode = darwin_scancode_table[i]; + if (scancode == SDL_SCANCODE_UNKNOWN || + (keymap[scancode] & SDLK_SCANCODE_MASK)) { + continue; + } + + dead_key_state = 0; + err = UCKeyTranslate ((UCKeyboardLayout *) chr_data, + i, kUCKeyActionDown, + 0, keyboard_type, + kUCKeyTranslateNoDeadKeysMask, + &dead_key_state, 8, &len, s); + if (err != noErr) { + continue; + } + + if (len > 0 && s[0] != 0x10) { + keymap[scancode] = s[0]; + } + } + SDL_SetKeymap(0, keymap, SDL_NUM_SCANCODES); + if (send_event) { + SDL_SendKeymapChangedEvent(); + } + return; + } + +cleanup: + CFRelease(key_layout); +} + +void +Cocoa_InitKeyboard(_THIS) +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + + UpdateKeymap(data, SDL_FALSE); + + /* Set our own names for the platform-dependent but layout-independent keys */ + /* This key is NumLock on the MacBook keyboard. :) */ + /*SDL_SetScancodeName(SDL_SCANCODE_NUMLOCKCLEAR, "Clear");*/ + SDL_SetScancodeName(SDL_SCANCODE_LALT, "Left Option"); + SDL_SetScancodeName(SDL_SCANCODE_LGUI, "Left Command"); + SDL_SetScancodeName(SDL_SCANCODE_RALT, "Right Option"); + SDL_SetScancodeName(SDL_SCANCODE_RGUI, "Right Command"); + + data->modifierFlags = [NSEvent modifierFlags]; + SDL_ToggleModState(KMOD_CAPS, (data->modifierFlags & NSEventModifierFlagCapsLock) != 0); + + InitHIDCallback(); +} + +void +Cocoa_StartTextInput(_THIS) +{ @autoreleasepool +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + SDL_Window *window = SDL_GetKeyboardFocus(); + NSWindow *nswindow = nil; + if (window) { + nswindow = ((SDL_WindowData*)window->driverdata)->nswindow; + } + + 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 = + [[SDLTranslatorResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)]; + } + + if (![[data->fieldEdit superview] isEqual:parentView]) { + /* DEBUG_IME(@"add fieldEdit to window contentView"); */ + [data->fieldEdit removeFromSuperview]; + [parentView addSubview: data->fieldEdit]; + [nswindow makeFirstResponder: data->fieldEdit]; + } +}} + +void +Cocoa_StopTextInput(_THIS) +{ @autoreleasepool +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + + if (data && data->fieldEdit) { + [data->fieldEdit removeFromSuperview]; + [data->fieldEdit release]; + data->fieldEdit = nil; + } +}} + +void +Cocoa_SetTextInputRect(_THIS, SDL_Rect *rect) +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + + if (!rect) { + SDL_InvalidParamError("rect"); + return; + } + + [data->fieldEdit setInputRect:rect]; +} + +void +Cocoa_HandleKeyEvent(_THIS, NSEvent *event) +{ + SDL_VideoData *data = _this ? ((SDL_VideoData *) _this->driverdata) : NULL; + if (!data) { + return; /* can happen when returning from fullscreen Space on shutdown */ + } + + unsigned short scancode = [event keyCode]; + SDL_Scancode code; +#if 0 + const char *text; +#endif + + if ((scancode == 10 || scancode == 50) && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) { + /* see comments in SDL_cocoakeys.h */ + scancode = 60 - scancode; + } + + if (scancode < SDL_arraysize(darwin_scancode_table)) { + code = darwin_scancode_table[scancode]; + } else { + /* Hmm, does this ever happen? If so, need to extend the keymap... */ + code = SDL_SCANCODE_UNKNOWN; + } + + switch ([event type]) { + case NSEventTypeKeyDown: + if (![event isARepeat]) { + /* See if we need to rebuild the keyboard layout */ + UpdateKeymap(data, SDL_TRUE); + } + + SDL_SendKeyboardKey(SDL_PRESSED, code); +#if 1 + if (code == SDL_SCANCODE_UNKNOWN) { + fprintf(stderr, "The key you just pressed is not recognized by SDL. To help get this fixed, report this to the SDL forums/mailing list <https://discourse.libsdl.org/> or to Christian Walther <cwalther@gmx.ch>. 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]]; +#if 0 + text = [[event characters] UTF8String]; + if(text && *text) { + SDL_SendKeyboardText(text); + [data->fieldEdit setString:@""]; + } +#endif + } + break; + case NSEventTypeKeyUp: + SDL_SendKeyboardKey(SDL_RELEASED, code); + break; + case NSEventTypeFlagsChanged: + /* 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) +{ + QuitHIDCallback(); +} + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamessagebox.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamessagebox.h new file mode 100644 index 0000000..74a815a --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamessagebox.h @@ -0,0 +1,29 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_COCOA + +extern int Cocoa_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid); + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamessagebox.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamessagebox.m new file mode 100644 index 0000000..a98237f --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamessagebox.m @@ -0,0 +1,145 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_COCOA + +#include "SDL_events.h" +#include "SDL_timer.h" +#include "SDL_messagebox.h" +#include "SDL_cocoavideo.h" + +@interface SDLMessageBoxPresenter : NSObject { +@public + NSInteger clicked; + NSWindow *nswindow; +} +- (id) initWithParentWindow:(SDL_Window *)window; +- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo; +@end + +@implementation SDLMessageBoxPresenter +- (id) initWithParentWindow:(SDL_Window *)window +{ + self = [super init]; + if (self) { + clicked = -1; + + /* Retain the NSWindow because we'll show the alert later on the main thread */ + if (window) { + nswindow = [((SDL_WindowData *) window->driverdata)->nswindow retain]; + } else { + nswindow = NULL; + } + } + + return self; +} + +- (void)showAlert:(NSAlert*)alert +{ + if (nswindow) { +#ifdef MAC_OS_X_VERSION_10_9 + if ([alert respondsToSelector:@selector(beginSheetModalForWindow:completionHandler:)]) { + [alert beginSheetModalForWindow:nswindow completionHandler:^(NSModalResponse returnCode) { + clicked = returnCode; + }]; + } else +#endif + { +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 + [alert beginSheetModalForWindow:nswindow modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:nil]; +#endif + } + + while (clicked < 0) { + SDL_PumpEvents(); + SDL_Delay(100); + } + + [nswindow release]; + } else { + clicked = [alert runModal]; + } +} + +- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +{ + clicked = returnCode; +} + +@end + + +/* Display a Cocoa message box */ +int +Cocoa_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) +{ @autoreleasepool +{ + Cocoa_RegisterApp(); + + NSAlert* alert = [[[NSAlert alloc] init] autorelease]; + + if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) { + [alert setAlertStyle:NSAlertStyleCritical]; + } else if (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) { + [alert setAlertStyle:NSAlertStyleWarning]; + } else { + [alert setAlertStyle:NSAlertStyleInformational]; + } + + [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) { + NSButton *button = [alert addButtonWithTitle:[NSString stringWithUTF8String:buttons[i].text]]; + if (buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { + [button setKeyEquivalent:@"\r"]; + } else if (buttons[i].flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) { + [button setKeyEquivalent:@"\033"]; + } else { + [button setKeyEquivalent:@""]; + } + } + + SDLMessageBoxPresenter* presenter = [[[SDLMessageBoxPresenter alloc] initWithParentWindow:messageboxdata->window] autorelease]; + + [presenter performSelectorOnMainThread:@selector(showAlert:) + withObject:alert + waitUntilDone:YES]; + + int returnValue = 0; + NSInteger clicked = presenter->clicked; + if (clicked >= NSAlertFirstButtonReturn) { + clicked -= NSAlertFirstButtonReturn; + *buttonid = buttons[clicked].buttonid; + } else { + returnValue = SDL_SetError("Did not get a valid `clicked button' id: %ld", (long)clicked); + } + + return returnValue; +}} + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoametalview.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoametalview.h new file mode 100644 index 0000000..185d45d --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoametalview.h @@ -0,0 +1,63 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +/* + * @author Mark Callow, www.edgewise-consulting.com. + * + * Thanks to Alex Szpakowski, @slime73 on GitHub, for his gist showing + * how to add a CAMetalLayer backed view. + */ + +#ifndef SDL_cocoametalview_h_ +#define SDL_cocoametalview_h_ + +#import "../SDL_sysvideo.h" +#import "SDL_cocoawindow.h" + +#if SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_RENDER_METAL) + +#import <Cocoa/Cocoa.h> +#import <Metal/Metal.h> +#import <QuartzCore/CAMetalLayer.h> + +#define METALVIEW_TAG 255 + +@interface SDL_cocoametalview : NSView + +- (instancetype)initWithFrame:(NSRect)frame + highDPI:(BOOL)highDPI; + +/* Override superclass tag so this class can set it. */ +@property (assign, readonly) NSInteger tag; + +@property (nonatomic) BOOL highDPI; + +@end + +SDL_cocoametalview* Cocoa_Mtl_AddMetalView(SDL_Window* window); + +void Cocoa_Mtl_GetDrawableSize(SDL_Window * window, int * w, int * h); + +#endif /* SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_RENDER_METAL) */ + +#endif /* SDL_cocoametalview_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoametalview.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoametalview.m new file mode 100644 index 0000000..9447fb8 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoametalview.m @@ -0,0 +1,138 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +/* + * @author Mark Callow, www.edgewise-consulting.com. + * + * Thanks to Alex Szpakowski, @slime73 on GitHub, for his gist showing + * how to add a CAMetalLayer backed view. + */ + +#import "SDL_cocoametalview.h" + +#if SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_RENDER_METAL) + +#include "SDL_assert.h" + +@implementation SDL_cocoametalview + +/* Return a Metal-compatible layer. */ ++ (Class)layerClass +{ + return NSClassFromString(@"CAMetalLayer"); +} + +/* Indicate the view wants to draw using a backing layer instead of drawRect. */ +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +/* When the wantsLayer property is set to YES, this method will be invoked to + * return a layer instance. + */ +- (CALayer*)makeBackingLayer +{ + return [self.class.layerClass layer]; +} + +- (instancetype)initWithFrame:(NSRect)frame + highDPI:(BOOL)highDPI +{ + if ((self = [super initWithFrame:frame])) { + self.highDPI = highDPI; + self.wantsLayer = YES; + + /* Allow resize. */ + self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + + [self updateDrawableSize]; + } + + return self; +} + +- (NSInteger)tag +{ + return METALVIEW_TAG; +} + +- (void)updateDrawableSize +{ + CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer; + CGSize size = self.bounds.size; + CGSize backingSize = size; + + if (self.highDPI) { + /* Note: NSHighResolutionCapable must be set to true in the app's + * Info.plist in order for the backing size to be high res. + */ + backingSize = [self convertSizeToBacking:size]; + } + + metalLayer.contentsScale = backingSize.height / size.height; + metalLayer.drawableSize = backingSize; +} + +/* Set the size of the metal drawables when the view is resized. */ +- (void)resizeWithOldSuperviewSize:(NSSize)oldSize +{ + [super resizeWithOldSuperviewSize:oldSize]; + [self updateDrawableSize]; +} + +@end + +SDL_cocoametalview* +Cocoa_Mtl_AddMetalView(SDL_Window* window) +{ + SDL_WindowData* data = (__bridge SDL_WindowData *)window->driverdata; + NSView *view = data->nswindow.contentView; + BOOL highDPI = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0; + SDL_cocoametalview *metalview; + + metalview = [[SDL_cocoametalview alloc] initWithFrame:view.frame highDPI:highDPI]; + [view addSubview:metalview]; + return metalview; +} + +void +Cocoa_Mtl_GetDrawableSize(SDL_Window * window, int * w, int * h) +{ + SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; + NSView *view = data->nswindow.contentView; + SDL_cocoametalview* metalview = [view viewWithTag:METALVIEW_TAG]; + if (metalview) { + CAMetalLayer *layer = (CAMetalLayer*)metalview.layer; + assert(layer != NULL); + if (w) { + *w = layer.drawableSize.width; + } + if (h) { + *h = layer.drawableSize.height; + } + } else { + SDL_GetWindowSize(window, w, h); + } +} + +#endif /* SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_RENDER_METAL) */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.h new file mode 100644 index 0000000..05482e8 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.h @@ -0,0 +1,46 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_cocoamodes_h_ +#define SDL_cocoamodes_h_ + +typedef struct +{ + CGDirectDisplayID display; +} SDL_DisplayData; + +typedef struct +{ + CGDisplayModeRef 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_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.m new file mode 100644 index 0000000..97ccd94 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamodes.m @@ -0,0 +1,490 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" +#include "SDL_assert.h" + +#if SDL_VIDEO_DRIVER_COCOA + +#include "SDL_cocoavideo.h" + +/* We need this for IODisplayCreateInfoDictionary and kIODisplayOnlyPreferredName */ +#include <IOKit/graphics/IOGraphicsLib.h> + +/* We need this for CVDisplayLinkGetNominalOutputVideoRefreshPeriod */ +#include <CoreVideo/CVBase.h> +#include <CoreVideo/CVDisplayLink.h> + +/* we need this for ShowMenuBar() and HideMenuBar(). */ +#include <Carbon/Carbon.h> + +/* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */ +#include <AvailabilityMacros.h> + + +static void +Cocoa_ToggleMenuBar(const BOOL show) +{ + /* !!! FIXME: keep an eye on this. + * ShowMenuBar/HideMenuBar is officially unavailable for 64-bit binaries. + * It happens to work, as of 10.7, but we're going to see if + * we can just simply do without it on newer OSes... + */ +#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1070) && !defined(__LP64__) + if (show) { + ShowMenuBar(); + } else { + HideMenuBar(); + } +#endif +} + +static int +CG_SetError(const char *prefix, CGDisplayErr result) +{ + const char *error; + + switch (result) { + case kCGErrorFailure: + error = "kCGErrorFailure"; + break; + case kCGErrorIllegalArgument: + error = "kCGErrorIllegalArgument"; + break; + case kCGErrorInvalidConnection: + error = "kCGErrorInvalidConnection"; + break; + case kCGErrorInvalidContext: + error = "kCGErrorInvalidContext"; + break; + case kCGErrorCannotComplete: + error = "kCGErrorCannotComplete"; + break; + case kCGErrorNotImplemented: + error = "kCGErrorNotImplemented"; + break; + case kCGErrorRangeCheck: + error = "kCGErrorRangeCheck"; + break; + case kCGErrorTypeCheck: + error = "kCGErrorTypeCheck"; + break; + case kCGErrorInvalidOperation: + error = "kCGErrorInvalidOperation"; + break; + case kCGErrorNoneAvailable: + error = "kCGErrorNoneAvailable"; + break; + default: + error = "Unknown Error"; + break; + } + return SDL_SetError("%s: %s", prefix, error); +} + +static SDL_bool +GetDisplayMode(_THIS, CGDisplayModeRef vidmode, CVDisplayLinkRef link, SDL_DisplayMode *mode) +{ + SDL_DisplayModeData *data; + int width = 0; + int height = 0; + int bpp = 0; + int refreshRate = 0; + CFStringRef fmt; + + data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data)); + if (!data) { + return SDL_FALSE; + } + data->moderef = vidmode; + + fmt = CGDisplayModeCopyPixelEncoding(vidmode); + width = (int) CGDisplayModeGetWidth(vidmode); + height = (int) CGDisplayModeGetHeight(vidmode); + refreshRate = (int) (CGDisplayModeGetRefreshRate(vidmode) + 0.5); + + if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + bpp = 32; + } else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + bpp = 16; + } else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels), + kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + bpp = 30; + } else { + bpp = 0; /* ignore 8-bit and such for now. */ + } + + CFRelease(fmt); + + /* CGDisplayModeGetRefreshRate returns 0 for many non-CRT displays. */ + if (refreshRate == 0 && link != NULL) { + CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); + if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) { + refreshRate = (int) ((time.timeScale / (double) time.timeValue) + 0.5); + } + } + + mode->format = SDL_PIXELFORMAT_UNKNOWN; + switch (bpp) { + case 16: + mode->format = SDL_PIXELFORMAT_ARGB1555; + break; + case 30: + mode->format = SDL_PIXELFORMAT_ARGB2101010; + break; + case 32: + mode->format = SDL_PIXELFORMAT_ARGB8888; + break; + case 8: /* We don't support palettized modes now */ + default: /* Totally unrecognizable bit depth. */ + SDL_free(data); + return SDL_FALSE; + } + mode->w = width; + mode->h = height; + mode->refresh_rate = refreshRate; + mode->driverdata = data; + return SDL_TRUE; +} + +static const char * +Cocoa_GetDisplayName(CGDirectDisplayID displayID) +{ + CFDictionaryRef deviceInfo = IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName); + NSDictionary *localizedNames = [(NSDictionary *)deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]]; + const char* displayName = NULL; + + if ([localizedNames count] > 0) { + displayName = SDL_strdup([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]); + } + CFRelease(deviceInfo); + return displayName; +} + +void +Cocoa_InitModes(_THIS) +{ @autoreleasepool +{ + CGDisplayErr result; + CGDirectDisplayID *displays; + CGDisplayCount numDisplays; + int pass, i; + + result = CGGetOnlineDisplayList(0, NULL, &numDisplays); + if (result != kCGErrorSuccess) { + CG_SetError("CGGetOnlineDisplayList()", result); + return; + } + displays = SDL_stack_alloc(CGDirectDisplayID, numDisplays); + result = CGGetOnlineDisplayList(numDisplays, displays, &numDisplays); + if (result != kCGErrorSuccess) { + CG_SetError("CGGetOnlineDisplayList()", result); + SDL_stack_free(displays); + return; + } + + /* Pick up the primary display in the first pass, then get the rest */ + for (pass = 0; pass < 2; ++pass) { + for (i = 0; i < numDisplays; ++i) { + SDL_VideoDisplay display; + SDL_DisplayData *displaydata; + SDL_DisplayMode mode; + CGDisplayModeRef moderef = NULL; + CVDisplayLinkRef link = NULL; + + if (pass == 0) { + if (!CGDisplayIsMain(displays[i])) { + continue; + } + } else { + if (CGDisplayIsMain(displays[i])) { + continue; + } + } + + if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay) { + continue; + } + + moderef = CGDisplayCopyDisplayMode(displays[i]); + + if (!moderef) { + continue; + } + + displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata)); + if (!displaydata) { + CGDisplayModeRelease(moderef); + continue; + } + displaydata->display = displays[i]; + + CVDisplayLinkCreateWithCGDisplay(displays[i], &link); + + SDL_zero(display); + /* this returns a stddup'ed string */ + display.name = (char *)Cocoa_GetDisplayName(displays[i]); + if (!GetDisplayMode(_this, moderef, link, &mode)) { + CVDisplayLinkRelease(link); + CGDisplayModeRelease(moderef); + SDL_free(display.name); + SDL_free(displaydata); + continue; + } + + CVDisplayLinkRelease(link); + + display.desktop_mode = mode; + display.current_mode = mode; + display.driverdata = displaydata; + SDL_AddVideoDisplay(&display); + SDL_free(display.name); + } + } + SDL_stack_free(displays); +}} + +int +Cocoa_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) +{ + SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; + CGRect cgrect; + + cgrect = CGDisplayBounds(displaydata->display); + rect->x = (int)cgrect.origin.x; + rect->y = (int)cgrect.origin.y; + rect->w = (int)cgrect.size.width; + rect->h = (int)cgrect.size.height; + return 0; +} + +int +Cocoa_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) +{ + SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; + const CGDirectDisplayID cgdisplay = displaydata->display; + NSArray *screens = [NSScreen screens]; + NSScreen *screen = nil; + + /* !!! FIXME: maybe track the NSScreen in SDL_DisplayData? */ + for (NSScreen *i in screens) { + const CGDirectDisplayID thisDisplay = (CGDirectDisplayID) [[[i deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; + if (thisDisplay == cgdisplay) { + screen = i; + break; + } + } + + SDL_assert(screen != nil); /* didn't find it?! */ + if (screen == nil) { + return -1; + } + + const CGRect cgrect = CGDisplayBounds(cgdisplay); + const NSRect frame = [screen visibleFrame]; + + // !!! FIXME: I assume -[NSScreen visibleFrame] is relative to the origin of the screen in question and not the whole desktop. + // !!! FIXME: The math vs CGDisplayBounds might be incorrect if that's not the case, though. Check this. + rect->x = (int)(cgrect.origin.x + frame.origin.x); + rect->y = (int)(cgrect.origin.y + frame.origin.y); + rect->w = (int)frame.size.width; + rect->h = (int)frame.size.height; + + return 0; +} + +int +Cocoa_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi) +{ + const float MM_IN_INCH = 25.4f; + + SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; + + CGSize displaySize = CGDisplayScreenSize(data->display); + int pixelWidth = (int) CGDisplayPixelsWide(data->display); + int pixelHeight = (int) CGDisplayPixelsHigh(data->display); + + if (ddpi) { + *ddpi = SDL_ComputeDiagonalDPI(pixelWidth, pixelHeight, displaySize.width / MM_IN_INCH, displaySize.height / MM_IN_INCH); + } + if (hdpi) { + *hdpi = pixelWidth * MM_IN_INCH / displaySize.width; + } + if (vdpi) { + *vdpi = pixelHeight * MM_IN_INCH / displaySize.height; + } + + return 0; +} + +void +Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display) +{ + SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; + CVDisplayLinkRef link = NULL; + CGDisplayModeRef desktopmoderef; + SDL_DisplayMode desktopmode; + CFArrayRef modes; + + CVDisplayLinkCreateWithCGDisplay(data->display, &link); + + desktopmoderef = CGDisplayCopyDisplayMode(data->display); + + /* CopyAllDisplayModes won't always contain the desktop display mode (if + * NULL is passed in) - for example on a retina 15" MBP, System Preferences + * allows choosing 1920x1200 but it's not in the list. AddDisplayMode makes + * sure there are no duplicates so it's safe to always add the desktop mode + * even in cases where it is in the CopyAllDisplayModes list. + */ + if (desktopmoderef && GetDisplayMode(_this, desktopmoderef, link, &desktopmode)) { + if (!SDL_AddDisplayMode(display, &desktopmode)) { + CGDisplayModeRelease(desktopmoderef); + SDL_free(desktopmode.driverdata); + } + } else { + CGDisplayModeRelease(desktopmoderef); + } + + modes = CGDisplayCopyAllDisplayModes(data->display, NULL); + + if (modes) { + CFIndex i; + const CFIndex count = CFArrayGetCount(modes); + + for (i = 0; i < count; i++) { + CGDisplayModeRef moderef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + SDL_DisplayMode mode; + + if (GetDisplayMode(_this, moderef, link, &mode)) { + if (SDL_AddDisplayMode(display, &mode)) { + CGDisplayModeRetain(moderef); + } else { + SDL_free(mode.driverdata); + } + } + } + + CFRelease(modes); + } + + CVDisplayLinkRelease(link); +} + +int +Cocoa_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) +{ + SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; + SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata; + CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken; + CGError result; + + /* Fade to black to hide resolution-switching flicker */ + if (CGAcquireDisplayFadeReservation(5, &fade_token) == kCGErrorSuccess) { + CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); + } + + if (data == display->desktop_mode.driverdata) { + /* Restoring desktop mode */ + CGDisplaySetDisplayMode(displaydata->display, data->moderef, NULL); + + if (CGDisplayIsMain(displaydata->display)) { + CGReleaseAllDisplays(); + } else { + CGDisplayRelease(displaydata->display); + } + + if (CGDisplayIsMain(displaydata->display)) { + Cocoa_ToggleMenuBar(YES); + } + } else { + /* Put up the blanking window (a window above all other windows) */ + if (CGDisplayIsMain(displaydata->display)) { + /* If we don't capture all displays, Cocoa tries to rearrange windows... *sigh* */ + result = CGCaptureAllDisplays(); + } else { + result = CGDisplayCapture(displaydata->display); + } + if (result != kCGErrorSuccess) { + CG_SetError("CGDisplayCapture()", result); + goto ERR_NO_CAPTURE; + } + + /* Do the physical switch */ + result = CGDisplaySetDisplayMode(displaydata->display, data->moderef, NULL); + if (result != kCGErrorSuccess) { + CG_SetError("CGDisplaySwitchToMode()", result); + goto ERR_NO_SWITCH; + } + + /* Hide the menu bar so it doesn't intercept events */ + if (CGDisplayIsMain(displaydata->display)) { + Cocoa_ToggleMenuBar(NO); + } + } + + /* Fade in again (asynchronously) */ + if (fade_token != kCGDisplayFadeReservationInvalidToken) { + CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGReleaseDisplayFadeReservation(fade_token); + } + + return 0; + + /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ +ERR_NO_SWITCH: + CGDisplayRelease(displaydata->display); +ERR_NO_CAPTURE: + if (fade_token != kCGDisplayFadeReservationInvalidToken) { + CGDisplayFade (fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGReleaseDisplayFadeReservation(fade_token); + } + return -1; +} + +void +Cocoa_QuitModes(_THIS) +{ + int i, j; + + for (i = 0; i < _this->num_displays; ++i) { + SDL_VideoDisplay *display = &_this->displays[i]; + SDL_DisplayModeData *mode; + + if (display->current_mode.driverdata != display->desktop_mode.driverdata) { + Cocoa_SetDisplayMode(_this, display, &display->desktop_mode); + } + + mode = (SDL_DisplayModeData *) display->desktop_mode.driverdata; + CGDisplayModeRelease(mode->moderef); + + for (j = 0; j < display->num_display_modes; j++) { + mode = (SDL_DisplayModeData*) display->display_modes[j].driverdata; + CGDisplayModeRelease(mode->moderef); + } + + } + Cocoa_ToggleMenuBar(YES); +} + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.h new file mode 100644 index 0000000..b79a3cf --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.h @@ -0,0 +1,52 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_cocoamouse_h_ +#define SDL_cocoamouse_h_ + +#include "SDL_cocoavideo.h" + +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 { + /* 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; + CGFloat lastWarpY; + /* What location we last saw the cursor move to. */ + CGFloat lastMoveX; + CGFloat lastMoveY; + void *tapdata; +} SDL_MouseData; + +@interface NSCursor (InvisibleCursor) ++ (NSCursor *)invisibleCursor; +@end + +#endif /* SDL_cocoamouse_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.m new file mode 100644 index 0000000..c9db253 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.m @@ -0,0 +1,477 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_COCOA + +#include "SDL_assert.h" +#include "SDL_events.h" +#include "SDL_cocoamouse.h" +#include "SDL_cocoamousetap.h" +#include "SDL_cocoavideo.h" + +#include "../../events/SDL_mouse_c.h" + +/* #define DEBUG_COCOAMOUSE */ + +#ifdef DEBUG_COCOAMOUSE +#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__) +#else +#define DLog(...) do { } while (0) +#endif + +@implementation NSCursor (InvisibleCursor) ++ (NSCursor *)invisibleCursor +{ + static NSCursor *invisibleCursor = NULL; + if (!invisibleCursor) { + /* RAW 16x16 transparent GIF */ + static unsigned char cursorBytes[] = { + 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED, + 0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B + }; + + NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0] + length:sizeof(cursorBytes) + freeWhenDone:NO]; + NSImage *cursorImage = [[[NSImage alloc] initWithData:cursorData] autorelease]; + invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage + hotSpot:NSZeroPoint]; + } + + return invisibleCursor; +} +@end + + +static SDL_Cursor * +Cocoa_CreateDefaultCursor() +{ @autoreleasepool +{ + NSCursor *nscursor; + SDL_Cursor *cursor = NULL; + + nscursor = [NSCursor arrowCursor]; + + if (nscursor) { + cursor = SDL_calloc(1, sizeof(*cursor)); + if (cursor) { + cursor->driverdata = nscursor; + [nscursor retain]; + } + } + + return cursor; +}} + +static SDL_Cursor * +Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) +{ @autoreleasepool +{ + NSImage *nsimage; + NSCursor *nscursor = NULL; + SDL_Cursor *cursor = NULL; + + nsimage = Cocoa_CreateImage(surface); + if (nsimage) { + nscursor = [[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)]; + } + + if (nscursor) { + cursor = SDL_calloc(1, sizeof(*cursor)); + if (cursor) { + cursor->driverdata = nscursor; + } else { + [nscursor release]; + } + } + + return cursor; +}} + +static SDL_Cursor * +Cocoa_CreateSystemCursor(SDL_SystemCursor id) +{ @autoreleasepool +{ + NSCursor *nscursor = NULL; + SDL_Cursor *cursor = NULL; + + 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_WAITARROW: + nscursor = [NSCursor arrowCursor]; + break; + case SDL_SYSTEM_CURSOR_SIZENWSE: + case SDL_SYSTEM_CURSOR_SIZENESW: + nscursor = [NSCursor closedHandCursor]; + break; + case SDL_SYSTEM_CURSOR_SIZEWE: + nscursor = [NSCursor resizeLeftRightCursor]; + break; + case SDL_SYSTEM_CURSOR_SIZENS: + nscursor = [NSCursor resizeUpDownCursor]; + break; + case SDL_SYSTEM_CURSOR_SIZEALL: + nscursor = [NSCursor closedHandCursor]; + break; + case SDL_SYSTEM_CURSOR_NO: + nscursor = [NSCursor operationNotAllowedCursor]; + break; + case SDL_SYSTEM_CURSOR_HAND: + nscursor = [NSCursor pointingHandCursor]; + break; + default: + SDL_assert(!"Unknown system cursor"); + return NULL; + } + + if (nscursor) { + cursor = SDL_calloc(1, sizeof(*cursor)); + if (cursor) { + /* We'll free it later, so retain it here */ + [nscursor retain]; + cursor->driverdata = nscursor; + } + } + + return cursor; +}} + +static void +Cocoa_FreeCursor(SDL_Cursor * cursor) +{ @autoreleasepool +{ + NSCursor *nscursor = (NSCursor *)cursor->driverdata; + + [nscursor release]; + SDL_free(cursor); +}} + +static int +Cocoa_ShowCursor(SDL_Cursor * cursor) +{ @autoreleasepool +{ + SDL_VideoDevice *device = SDL_GetVideoDevice(); + SDL_Window *window = (device ? device->windows : NULL); + for (; window != NULL; window = window->next) { + SDL_WindowData *driverdata = (SDL_WindowData *)window->driverdata; + if (driverdata) { + [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; + } + } + + return NULL; +} + +static int +Cocoa_WarpMouseGlobal(int x, int y) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + if (mouse->focus) { + SDL_WindowData *data = (SDL_WindowData *) mouse->focus->driverdata; + if ([data->listener isMoving]) { + DLog("Postponing warp, window being moved."); + [data->listener setPendingMoveX:x Y:y]; + return 0; + } + } + const CGPoint point = CGPointMake((float)x, (float)y); + + Cocoa_HandleMouseWarp(point.x, point.y); + + CGWarpMouseCursorPosition(point); + + /* 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. + */ + if (!mouse->relative_mode) { + CGAssociateMouseAndMouseCursorPosition(YES); + } + + /* 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); + } + } + + return 0; +} + +static void +Cocoa_WarpMouse(SDL_Window * window, int x, int y) +{ + Cocoa_WarpMouseGlobal(x + window->x, y + window->y); +} + +static int +Cocoa_SetRelativeMouseMode(SDL_bool enabled) +{ + /* 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; + } + + /* We will re-apply the relative mode when the window finishes being moved, + * if it is being moved right now. + */ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if ([data->listener isMoving]) { + return 0; + } + + CGError result; + if (enabled) { + DLog("Turning on."); + result = CGAssociateMouseAndMouseCursorPosition(NO); + } else { + DLog("Turning off."); + result = CGAssociateMouseAndMouseCursorPosition(YES); + } + if (result != kCGErrorSuccess) { + return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed"); + } + + /* The hide/unhide calls are redundant most of the time, but they fix + * https://bugzilla.libsdl.org/show_bug.cgi?id=2550 + */ + if (enabled) { + [NSCursor hide]; + } else { + [NSCursor unhide]; + } + 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) +{ + const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons]; + const NSPoint cocoaLocation = [NSEvent mouseLocation]; + Uint32 retval = 0; + + for (NSScreen *screen in [NSScreen screens]) { + NSRect frame = [screen frame]; + if (NSMouseInRect(cocoaLocation, frame, NO)) { + *x = (int) cocoaLocation.x; + *y = (int) ((frame.origin.y + frame.size.height) - cocoaLocation.y); + break; + } + } + + 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; +} + +void +Cocoa_InitMouse(_THIS) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + 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()); + + Cocoa_InitMouseEventTap(mouse->driverdata); + + SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata; + const NSPoint location = [NSEvent mouseLocation]; + driverdata->lastMoveX = location.x; + driverdata->lastMoveY = location.y; +} + +void +Cocoa_HandleMouseEvent(_THIS, NSEvent *event) +{ + switch ([event type]) { + case NSEventTypeMouseMoved: + case NSEventTypeLeftMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeOtherMouseDragged: + break; + + default: + /* Ignore any other events. */ + return; + } + + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata; + if (!driverdata) { + return; /* can happen when returning from fullscreen Space on shutdown */ + } + + const SDL_bool seenWarp = driverdata->seenWarp; + driverdata->seenWarp = NO; + + 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); + + /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */ + if (!mouse->relative_mode) { + return; + } + + /* 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)) { + return; + } + } + + float deltaX = [event deltaX]; + float deltaY = [event deltaY]; + + 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, mouse->mouseID, 1, (int)deltaX, (int)deltaY); +} + +void +Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + + CGFloat x = -[event deltaX]; + CGFloat y = [event deltaY]; + SDL_MouseWheelDirection direction = SDL_MOUSEWHEEL_NORMAL; + + if ([event respondsToSelector:@selector(isDirectionInvertedFromDevice)]) { + if ([event isDirectionInvertedFromDevice] == YES) { + direction = SDL_MOUSEWHEEL_FLIPPED; + } + } + + 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); + } + SDL_SendMouseWheel(window, mouse->mouseID, x, y, direction); +} + +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. + */ + SDL_MouseData *driverdata = (SDL_MouseData*)SDL_GetMouse()->driverdata; + driverdata->lastWarpX = x; + driverdata->lastWarpY = y; + driverdata->seenWarp = SDL_TRUE; + + DLog("(%g, %g)", x, y); +} + +void +Cocoa_QuitMouse(_THIS) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + if (mouse) { + if (mouse->driverdata) { + Cocoa_QuitMouseEventTap(((SDL_MouseData*)mouse->driverdata)); + } + + SDL_free(mouse->driverdata); + } +} + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamousetap.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamousetap.h new file mode 100644 index 0000000..40ce386 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamousetap.h @@ -0,0 +1,34 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_cocoamousetap_h_ +#define SDL_cocoamousetap_h_ + +#include "SDL_cocoamouse.h" + +extern void Cocoa_InitMouseEventTap(SDL_MouseData *driverdata); +extern void Cocoa_EnableMouseEventTap(SDL_MouseData *driverdata, SDL_bool enabled); +extern void Cocoa_QuitMouseEventTap(SDL_MouseData *driverdata); + +#endif /* SDL_cocoamousetap_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamousetap.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamousetap.m new file mode 100644 index 0000000..aa4f152 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamousetap.m @@ -0,0 +1,286 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#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_cocoavideo.h" +#include "../../thread/SDL_systhread.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: + { + CGEventTapEnable(tapdata->tap, true); + return NULL; + } + case kCGEventTapDisabledByUserInput: + { + 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 (!NSMouseInRect(NSPointFromCGPoint(eventLocation), windowRect, NO)) { + + /* 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)); + } + + CGWarpMouseCursorPosition(newLocation); + CGAssociateMouseAndMouseCursorPosition(YES); + + 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; + + /* Tap was created on main thread but we own it now. */ + CFMachPortRef eventTap = tapdata->tap; + if (eventTap) { + /* Try to create a runloop source we can schedule. */ + CFRunLoopSourceRef runloopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0); + if (runloopSource) { + 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->tap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, + kCGEventTapOptionDefault, allGrabbedEventsMask, + &Cocoa_MouseTapCallback, tapdata); + if (tapdata->tap) { + /* Tap starts disabled, until app requests mouse grab */ + CGEventTapEnable(tapdata->tap, false); + tapdata->thread = SDL_CreateThreadInternal(&Cocoa_MouseTapThread, "Event Tap Loop", 512 * 1024, tapdata); + if (tapdata->thread) { + /* Success - early out. Ownership transferred to thread. */ + return; + } + CFRelease(tapdata->tap); + } + SDL_DestroySemaphore(tapdata->runloopStartedSemaphore); + } + SDL_free(driverdata->tapdata); + driverdata->tapdata = NULL; +} + +void +Cocoa_EnableMouseEventTap(SDL_MouseData *driverdata, SDL_bool enabled) +{ + SDL_MouseEventTapData *tapdata = (SDL_MouseEventTapData*)driverdata->tapdata; + if (tapdata && tapdata->tap) + { + CGEventTapEnable(tapdata->tap, !!enabled); + } +} + +void +Cocoa_QuitMouseEventTap(SDL_MouseData *driverdata) +{ + SDL_MouseEventTapData *tapdata = (SDL_MouseEventTapData*)driverdata->tapdata; + int status; + + if (tapdata == NULL) { + /* event tap was already cleaned up (possibly due to CGEventTapCreate + * returning null.) + */ + return; + } + + /* 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_EnableMouseEventTap(SDL_MouseData *driverdata, SDL_bool enabled) +{ +} + +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 a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengl.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengl.h new file mode 100644 index 0000000..81ca5ed --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengl.h @@ -0,0 +1,68 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_cocoaopengl_h_ +#define SDL_cocoaopengl_h_ + +#if SDL_VIDEO_OPENGL_CGL + +#include "SDL_atomic.h" +#import <Cocoa/Cocoa.h> + +struct SDL_GLDriverData +{ + int initialized; +}; + +@interface SDLOpenGLContext : NSOpenGLContext { + SDL_atomic_t dirty; + SDL_Window *window; +} + +- (id)initWithFormat:(NSOpenGLPixelFormat *)format + shareContext:(NSOpenGLContext *)share; +- (void)scheduleUpdate; +- (void)updateIfNeeded; +- (void)setWindow:(SDL_Window *)window; + +@end + + +/* OpenGL functions */ +extern int Cocoa_GL_LoadLibrary(_THIS, const char *path); +extern void *Cocoa_GL_GetProcAddress(_THIS, const char *proc); +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_DeleteContext(_THIS, SDL_GLContext context); + +#endif /* SDL_VIDEO_OPENGL_CGL */ + +#endif /* SDL_cocoaopengl_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengl.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengl.m new file mode 100644 index 0000000..9539c17 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengl.m @@ -0,0 +1,436 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +/* NSOpenGL implementation of SDL OpenGL support */ + +#if SDL_VIDEO_OPENGL_CGL +#include "SDL_cocoavideo.h" +#include "SDL_cocoaopengl.h" +#include "SDL_cocoaopengles.h" + +#include <OpenGL/CGLTypes.h> +#include <OpenGL/OpenGL.h> +#include <OpenGL/CGLRenderers.h> + +#include "SDL_loadso.h" +#include "SDL_opengl.h" + +#define DEFAULT_OPENGL "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib" + +@implementation SDLOpenGLContext : NSOpenGLContext + +- (id)initWithFormat:(NSOpenGLPixelFormat *)format + shareContext:(NSOpenGLContext *)share +{ + self = [super initWithFormat:format shareContext:share]; + if (self) { + SDL_AtomicSet(&self->dirty, 0); + self->window = NULL; + } + return self; +} + +- (void)scheduleUpdate +{ + SDL_AtomicAdd(&self->dirty, 1); +} + +/* This should only be called on the thread on which a user is using the context. */ +- (void)updateIfNeeded +{ + int value = SDL_AtomicSet(&self->dirty, 0); + if (value > 0) { + /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */ + [super update]; + } +} + +/* This should only be called on the thread on which a user is using the context. */ +- (void)update +{ + /* This ensures that regular 'update' calls clear the atomic dirty flag. */ + [self scheduleUpdate]; + [self updateIfNeeded]; +} + +/* Updates the drawable for the contexts and manages related state. */ +- (void)setWindow:(SDL_Window *)newWindow +{ + if (self->window) { + 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; + @synchronized (contexts) { + [contexts removeObject:self]; + } + } + + self->window = newWindow; + + if (newWindow) { + SDL_WindowData *windowdata = (SDL_WindowData *)newWindow->driverdata; + + /* Now sign up for scheduled updates for the new window. */ + NSMutableArray *contexts = windowdata->nscontexts; + @synchronized (contexts) { + [contexts addObject:self]; + } + + if ([self view] != [windowdata->nswindow contentView]) { + [self setView:[windowdata->nswindow contentView]]; + if (self == [NSOpenGLContext currentContext]) { + [self update]; + } else { + [self scheduleUpdate]; + } + } + } else { + [self clearDrawable]; + if (self == [NSOpenGLContext currentContext]) { + [self update]; + } else { + [self scheduleUpdate]; + } + } +} + +@end + + +int +Cocoa_GL_LoadLibrary(_THIS, const char *path) +{ + /* Load the OpenGL library */ + if (path == NULL) { + path = SDL_getenv("SDL_OPENGL_LIBRARY"); + } + if (path == NULL) { + path = DEFAULT_OPENGL; + } + _this->gl_config.dll_handle = SDL_LoadObject(path); + if (!_this->gl_config.dll_handle) { + return -1; + } + SDL_strlcpy(_this->gl_config.driver_path, path, + SDL_arraysize(_this->gl_config.driver_path)); + return 0; +} + +void * +Cocoa_GL_GetProcAddress(_THIS, const char *proc) +{ + return SDL_LoadFunction(_this->gl_config.dll_handle, proc); +} + +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_VideoDisplay *display = SDL_GetDisplayForWindow(window); + SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata; + SDL_bool lion_or_later = floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6; + NSOpenGLPixelFormatAttribute attr[32]; + NSOpenGLPixelFormat *fmt; + SDLOpenGLContext *context; + NSOpenGLContext *share_context = nil; + int i = 0; + const char *glversion; + int glversion_major; + int glversion_minor; + + if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { +#if 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"); + return NULL; +#endif + } + if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) && !lion_or_later) { + SDL_SetError ("OpenGL Core Profile is not supported on this platform version"); + return NULL; + } + + attr[i++] = NSOpenGLPFAAllowOfflineRenderers; + + /* specify a profile if we're on Lion (10.7) or later. */ + if (lion_or_later) { + 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++] = NSOpenGLPFAColorSize; + attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8; + + attr[i++] = NSOpenGLPFADepthSize; + attr[i++] = _this->gl_config.depth_size; + + if (_this->gl_config.double_buffer) { + attr[i++] = NSOpenGLPFADoubleBuffer; + } + + if (_this->gl_config.stereo) { + attr[i++] = NSOpenGLPFAStereo; + } + + if (_this->gl_config.stencil_size) { + attr[i++] = NSOpenGLPFAStencilSize; + attr[i++] = _this->gl_config.stencil_size; + } + + if ((_this->gl_config.accum_red_size + + _this->gl_config.accum_green_size + + _this->gl_config.accum_blue_size + + _this->gl_config.accum_alpha_size) > 0) { + attr[i++] = NSOpenGLPFAAccumSize; + attr[i++] = _this->gl_config.accum_red_size + _this->gl_config.accum_green_size + _this->gl_config.accum_blue_size + _this->gl_config.accum_alpha_size; + } + + if (_this->gl_config.multisamplebuffers) { + attr[i++] = NSOpenGLPFASampleBuffers; + attr[i++] = _this->gl_config.multisamplebuffers; + } + + if (_this->gl_config.multisamplesamples) { + attr[i++] = NSOpenGLPFASamples; + attr[i++] = _this->gl_config.multisamplesamples; + attr[i++] = NSOpenGLPFANoRecovery; + } + + if (_this->gl_config.accelerated >= 0) { + if (_this->gl_config.accelerated) { + attr[i++] = NSOpenGLPFAAccelerated; + } else { + attr[i++] = NSOpenGLPFARendererID; + attr[i++] = kCGLRendererGenericFloatID; + } + } + + attr[i++] = NSOpenGLPFAScreenMask; + attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display); + attr[i] = 0; + + fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; + if (fmt == nil) { + SDL_SetError("Failed creating OpenGL pixel format"); + return NULL; + } + + if (_this->gl_config.share_with_current_context) { + 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"); + return NULL; + } + + if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) { + Cocoa_GL_DeleteContext(_this, context); + SDL_SetError("Failed making OpenGL context current"); + return NULL; + } + + if (_this->gl_config.major_version < 3 && + _this->gl_config.profile_mask == 0 && + _this->gl_config.flags == 0) { + /* This is a legacy profile, so to match other backends, we're done. */ + } else { + const GLubyte *(APIENTRY * glGetStringFunc)(GLenum); + + glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString"); + if (!glGetStringFunc) { + Cocoa_GL_DeleteContext(_this, context); + SDL_SetError ("Failed getting OpenGL glGetString entry point"); + return NULL; + } + + glversion = (const char *)glGetStringFunc(GL_VERSION); + if (glversion == NULL) { + 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) { + 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))) { + Cocoa_GL_DeleteContext(_this, context); + SDL_SetError ("Failed creating OpenGL context at version requested"); + return NULL; + } + + /* In the future we'll want to do this, but to match other platforms + we'll leave the OpenGL version the way it is for now + */ + /*_this->gl_config.major_version = glversion_major;*/ + /*_this->gl_config.minor_version = glversion_minor;*/ + } + return context; +}} + +int +Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) +{ @autoreleasepool +{ + if (context) { + SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context; + [nscontext setWindow:window]; + [nscontext updateIfNeeded]; + [nscontext makeCurrentContext]; + } else { + [NSOpenGLContext clearCurrentContext]; + } + + return 0; +}} + +void +Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h) +{ + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; + NSView *contentView = [windata->nswindow contentView]; + NSRect viewport = [contentView bounds]; + + if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { + /* This gives us the correct viewport for a Retina-enabled view, only + * supported on 10.7+. */ + if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) { + viewport = [contentView convertRectToBacking:viewport]; + } + } + + if (w) { + *w = viewport.size.width; + } + + if (h) { + *h = viewport.size.height; + } +} + +int +Cocoa_GL_SetSwapInterval(_THIS, int interval) +{ @autoreleasepool +{ + NSOpenGLContext *nscontext; + GLint value; + int status; + + if (interval < 0) { /* no extension for this on Mac OS X at the moment. */ + return SDL_SetError("Late swap tearing currently unsupported"); + } + + 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"); + } + + return status; +}} + +int +Cocoa_GL_GetSwapInterval(_THIS) +{ @autoreleasepool +{ + NSOpenGLContext *nscontext; + GLint value; + int status = 0; + + nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); + if (nscontext != nil) { + [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval]; + status = (int)value; + } + + return status; +}} + +int +Cocoa_GL_SwapWindow(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext(); + SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; + + /* 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); + [nscontext flushBuffer]; + [nscontext updateIfNeeded]; + SDL_UnlockMutex(videodata->swaplock); + return 0; +}} + +void +Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context) +{ @autoreleasepool +{ + SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context; + + [nscontext setWindow:NULL]; + [nscontext release]; +}} + +#endif /* SDL_VIDEO_OPENGL_CGL */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengles.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengles.h new file mode 100644 index 0000000..fc7f5c0 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengles.h @@ -0,0 +1,49 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_cocoaopengles_h_ +#define SDL_cocoaopengles_h_ + +#if SDL_VIDEO_OPENGL_EGL + +#include "../SDL_sysvideo.h" +#include "../SDL_egl_c.h" + +/* OpenGLES functions */ +#define Cocoa_GLES_GetAttribute SDL_EGL_GetAttribute +#define Cocoa_GLES_GetProcAddress SDL_EGL_GetProcAddress +#define Cocoa_GLES_UnloadLibrary SDL_EGL_UnloadLibrary +#define Cocoa_GLES_GetSwapInterval SDL_EGL_GetSwapInterval +#define Cocoa_GLES_SetSwapInterval SDL_EGL_SetSwapInterval + +extern int Cocoa_GLES_LoadLibrary(_THIS, const char *path); +extern SDL_GLContext Cocoa_GLES_CreateContext(_THIS, SDL_Window * window); +extern int Cocoa_GLES_SwapWindow(_THIS, SDL_Window * window); +extern int Cocoa_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context); +extern void Cocoa_GLES_DeleteContext(_THIS, SDL_GLContext context); +extern int Cocoa_GLES_SetupWindow(_THIS, SDL_Window * window); + +#endif /* SDL_VIDEO_OPENGL_EGL */ + +#endif /* SDL_cocoaopengles_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengles.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengles.m new file mode 100644 index 0000000..e0a05a1 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoaopengles.m @@ -0,0 +1,132 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_COCOA && SDL_VIDEO_OPENGL_EGL + +#include "SDL_cocoavideo.h" +#include "SDL_cocoaopengles.h" +#include "SDL_cocoaopengl.h" +#include "SDL_log.h" + +/* EGL implementation of SDL OpenGL support */ + +int +Cocoa_GLES_LoadLibrary(_THIS, const char *path) { + + /* If the profile requested is not GL ES, switch over to WIN_GL functions */ + if (_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES) { +#if SDL_VIDEO_OPENGL_CGL + Cocoa_GLES_UnloadLibrary(_this); + _this->GL_LoadLibrary = Cocoa_GL_LoadLibrary; + _this->GL_GetProcAddress = Cocoa_GL_GetProcAddress; + _this->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary; + _this->GL_CreateContext = Cocoa_GL_CreateContext; + _this->GL_MakeCurrent = Cocoa_GL_MakeCurrent; + _this->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval; + _this->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval; + _this->GL_SwapWindow = Cocoa_GL_SwapWindow; + _this->GL_DeleteContext = Cocoa_GL_DeleteContext; + return Cocoa_GL_LoadLibrary(_this, path); +#else + return SDL_SetError("SDL not configured with OpenGL/CGL support"); +#endif + } + + if (_this->egl_data == NULL) { + return SDL_EGL_LoadLibrary(_this, NULL, EGL_DEFAULT_DISPLAY, 0); + } + + return 0; +} + +SDL_GLContext +Cocoa_GLES_CreateContext(_THIS, SDL_Window * window) +{ + SDL_GLContext context; + SDL_WindowData *data = (SDL_WindowData *)window->driverdata; + +#if SDL_VIDEO_OPENGL_CGL + if (_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES) { + /* Switch to CGL based functions */ + Cocoa_GLES_UnloadLibrary(_this); + _this->GL_LoadLibrary = Cocoa_GL_LoadLibrary; + _this->GL_GetProcAddress = Cocoa_GL_GetProcAddress; + _this->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary; + _this->GL_CreateContext = Cocoa_GL_CreateContext; + _this->GL_MakeCurrent = Cocoa_GL_MakeCurrent; + _this->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval; + _this->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval; + _this->GL_SwapWindow = Cocoa_GL_SwapWindow; + _this->GL_DeleteContext = Cocoa_GL_DeleteContext; + + if (Cocoa_GL_LoadLibrary(_this, NULL) != 0) { + return NULL; + } + + return Cocoa_GL_CreateContext(_this, window); + } +#endif + + context = SDL_EGL_CreateContext(_this, data->egl_surface); + return context; +} + +void +Cocoa_GLES_DeleteContext(_THIS, SDL_GLContext context) +{ + SDL_EGL_DeleteContext(_this, context); + Cocoa_GLES_UnloadLibrary(_this); +} + +SDL_EGL_SwapWindow_impl(Cocoa) +SDL_EGL_MakeCurrent_impl(Cocoa) + +int +Cocoa_GLES_SetupWindow(_THIS, SDL_Window * window) +{ + /* The current context is lost in here; save it and reset it. */ + SDL_WindowData *windowdata = (SDL_WindowData *) window->driverdata; + SDL_Window *current_win = SDL_GL_GetCurrentWindow(); + SDL_GLContext current_ctx = SDL_GL_GetCurrentContext(); + + + if (_this->egl_data == NULL) { + if (SDL_EGL_LoadLibrary(_this, NULL, EGL_DEFAULT_DISPLAY, 0) < 0) { + SDL_EGL_UnloadLibrary(_this); + return -1; + } + } + + /* Create the GLES window surface */ + NSView* v = windowdata->nswindow.contentView; + windowdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)[v layer]); + + if (windowdata->egl_surface == EGL_NO_SURFACE) { + return SDL_SetError("Could not create GLES window surface"); + } + + return Cocoa_GLES_MakeCurrent(_this, current_win, current_ctx); +} + +#endif /* SDL_VIDEO_DRIVER_COCOA && SDL_VIDEO_OPENGL_EGL */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoashape.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoashape.h new file mode 100644 index 0000000..da1b5eb --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoashape.h @@ -0,0 +1,45 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "../../SDL_internal.h" + +#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" + +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: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoashape.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoashape.m new file mode 100644 index 0000000..7a2f04f --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoashape.m @@ -0,0 +1,113 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "../../SDL_internal.h" + +#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" + +SDL_WindowShaper* +Cocoa_CreateShaper(SDL_Window* window) +{ + SDL_WindowData* windata = (SDL_WindowData*)window->driverdata; + [windata->nswindow setOpaque:NO]; + + [windata->nswindow setStyleMask:NSWindowStyleMaskBorderless]; + + 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; + + SDL_ShapeData* data = malloc(sizeof(SDL_ShapeData)); + result->driverdata = data; + data->context = [windata->nswindow graphicsContext]; + data->saved = SDL_FALSE; + data->shape = NULL; + + int resized_properly = Cocoa_ResizeWindowShape(window); + SDL_assert(resized_properly == 0); + return result; +} + +typedef struct { + NSView* view; + NSBezierPath* path; + SDL_Window* window; +} SDL_CocoaClosure; + +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]]; + } +} + +int +Cocoa_SetWindowShape(SDL_WindowShaper *shaper, SDL_Surface *shape, SDL_WindowShapeMode *shape_mode) +{ @autoreleasepool +{ + SDL_ShapeData* data = (SDL_ShapeData*)shaper->driverdata; + SDL_WindowData* windata = (SDL_WindowData*)shaper->window->driverdata; + SDL_CocoaClosure closure; + if(data->saved == SDL_TRUE) { + [data->context restoreGraphicsState]; + data->saved = SDL_FALSE; + } + + /*[data->context saveGraphicsState];*/ + /*data->saved = SDL_TRUE;*/ + [NSGraphicsContext setCurrentContext:data->context]; + + [[NSColor clearColor] set]; + NSRectFill([[windata->nswindow contentView] frame]); + data->shape = SDL_CalculateShapeTree(*shape_mode,shape); + + closure.view = [windata->nswindow contentView]; + closure.path = [NSBezierPath bezierPath]; + closure.window = shaper->window; + SDL_TraverseShapeTree(data->shape,&ConvertRects,&closure); + [closure.path addClip]; + + return 0; +}} + +int +Cocoa_ResizeWindowShape(SDL_Window *window) +{ + SDL_ShapeData* data = window->shaper->driverdata; + SDL_assert(data != NULL); + return 0; +} + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavideo.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavideo.h new file mode 100644 index 0000000..b1c26fa --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavideo.h @@ -0,0 +1,118 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_cocoavideo_h_ +#define SDL_cocoavideo_h_ + +#include "SDL_opengl.h" + +#include <ApplicationServices/ApplicationServices.h> +#include <IOKit/pwr_mgt/IOPMLib.h> +#include <Cocoa/Cocoa.h> + +#include "SDL_keycode.h" +#include "../SDL_sysvideo.h" + +#include "SDL_cocoaclipboard.h" +#include "SDL_cocoaevents.h" +#include "SDL_cocoakeyboard.h" +#include "SDL_cocoamodes.h" +#include "SDL_cocoamouse.h" +#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 +#endif + +/* Private display data */ + +@class SDLTranslatorResponder; + +typedef struct SDL_VideoData +{ + int allow_spaces; + unsigned int modifierFlags; + void *key_layout; + SDLTranslatorResponder *fieldEdit; + NSInteger clipboard_count; + Uint32 screensaver_activity; + BOOL screensaver_use_iopm; + IOPMAssertionID screensaver_assertion; + SDL_mutex *swaplock; +} SDL_VideoData; + +/* Utility functions */ +extern NSImage * Cocoa_CreateImage(SDL_Surface * surface); + +#endif /* SDL_cocoavideo_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavideo.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavideo.m new file mode 100644 index 0000000..20bdfa7 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavideo.m @@ -0,0 +1,262 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_COCOA + +#include "SDL.h" +#include "SDL_endian.h" +#include "SDL_cocoavideo.h" +#include "SDL_cocoashape.h" +#include "SDL_cocoavulkan.h" +#include "SDL_assert.h" + +/* Initialization/Query functions */ +static int Cocoa_VideoInit(_THIS); +static void Cocoa_VideoQuit(_THIS); + +/* Cocoa driver bootstrap functions */ + +static int +Cocoa_Available(void) +{ + return (1); +} + +static void +Cocoa_DeleteDevice(SDL_VideoDevice * device) +{ + SDL_free(device->driverdata); + SDL_free(device); +} + +static SDL_VideoDevice * +Cocoa_CreateDevice(int devindex) +{ + SDL_VideoDevice *device; + SDL_VideoData *data; + + Cocoa_RegisterApp(); + + /* Initialize all variables that we clean on shutdown */ + device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice)); + if (device) { + data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData)); + } else { + data = NULL; + } + if (!data) { + SDL_OutOfMemory(); + SDL_free(device); + return NULL; + } + device->driverdata = data; + + /* 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->SuspendScreenSaver = Cocoa_SuspendScreenSaver; + + device->CreateSDLWindow = Cocoa_CreateWindow; + device->CreateSDLWindowFrom = Cocoa_CreateWindowFrom; + device->SetWindowTitle = Cocoa_SetWindowTitle; + device->SetWindowIcon = Cocoa_SetWindowIcon; + device->SetWindowPosition = Cocoa_SetWindowPosition; + device->SetWindowSize = Cocoa_SetWindowSize; + device->SetWindowMinimumSize = Cocoa_SetWindowMinimumSize; + device->SetWindowMaximumSize = Cocoa_SetWindowMaximumSize; + device->SetWindowOpacity = Cocoa_SetWindowOpacity; + device->ShowWindow = Cocoa_ShowWindow; + device->HideWindow = Cocoa_HideWindow; + device->RaiseWindow = Cocoa_RaiseWindow; + device->MaximizeWindow = Cocoa_MaximizeWindow; + device->MinimizeWindow = Cocoa_MinimizeWindow; + device->RestoreWindow = Cocoa_RestoreWindow; + device->SetWindowBordered = Cocoa_SetWindowBordered; + device->SetWindowResizable = Cocoa_SetWindowResizable; + device->SetWindowFullscreen = Cocoa_SetWindowFullscreen; + device->SetWindowGammaRamp = Cocoa_SetWindowGammaRamp; + device->GetWindowGammaRamp = Cocoa_GetWindowGammaRamp; + device->SetWindowGrab = Cocoa_SetWindowGrab; + device->DestroyWindow = Cocoa_DestroyWindow; + device->GetWindowWMInfo = Cocoa_GetWindowWMInfo; + device->SetWindowHitTest = Cocoa_SetWindowHitTest; + device->AcceptDragAndDrop = Cocoa_AcceptDragAndDrop; + + device->shape_driver.CreateShaper = Cocoa_CreateShaper; + device->shape_driver.SetWindowShape = Cocoa_SetWindowShape; + device->shape_driver.ResizeWindowShape = Cocoa_ResizeWindowShape; + +#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 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 + +#if 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 + + device->StartTextInput = Cocoa_StartTextInput; + device->StopTextInput = Cocoa_StopTextInput; + device->SetTextInputRect = Cocoa_SetTextInputRect; + + device->SetClipboardText = Cocoa_SetClipboardText; + device->GetClipboardText = Cocoa_GetClipboardText; + device->HasClipboardText = Cocoa_HasClipboardText; + + device->free = Cocoa_DeleteDevice; + + return device; +} + +VideoBootStrap COCOA_bootstrap = { + "cocoa", "SDL Cocoa video driver", + Cocoa_Available, Cocoa_CreateDevice +}; + + +int +Cocoa_VideoInit(_THIS) +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + + Cocoa_InitModes(_this); + Cocoa_InitKeyboard(_this); + Cocoa_InitMouse(_this); + + data->allow_spaces = ((floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) && SDL_GetHintBoolean(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, SDL_TRUE)); + + /* The IOPM assertion API can disable the screensaver as of 10.7. */ + data->screensaver_use_iopm = floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6; + + data->swaplock = SDL_CreateMutex(); + if (!data->swaplock) { + return -1; + } + + return 0; +} + +void +Cocoa_VideoQuit(_THIS) +{ + SDL_VideoData *data = (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) +{ + SDL_Surface *converted; + NSBitmapImageRep *imgrep; + Uint8 *pixels; + int i; + NSImage *img; + + converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA32, 0); + if (!converted) { + return nil; + } + + imgrep = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL + pixelsWide: converted->w + pixelsHigh: converted->h + bitsPerSample: 8 + samplesPerPixel: 4 + hasAlpha: YES + isPlanar: NO + colorSpaceName: NSDeviceRGBColorSpace + bytesPerRow: converted->pitch + bitsPerPixel: converted->format->BitsPerPixel] autorelease]; + if (imgrep == nil) { + SDL_FreeSurface(converted); + return nil; + } + + /* Copy the pixels */ + pixels = [imgrep bitmapData]; + SDL_memcpy(pixels, converted->pixels, converted->h * converted->pitch); + SDL_FreeSurface(converted); + + /* Premultiply the alpha channel */ + for (i = (surface->h * surface->w); i--; ) { + Uint8 alpha = pixels[3]; + pixels[0] = (Uint8)(((Uint16)pixels[0] * alpha) / 255); + pixels[1] = (Uint8)(((Uint16)pixels[1] * alpha) / 255); + pixels[2] = (Uint8)(((Uint16)pixels[2] * alpha) / 255); + pixels += 4; + } + + img = [[[NSImage alloc] initWithSize: NSMakeSize(surface->w, surface->h)] autorelease]; + if (img != nil) { + [img addRepresentation: imgrep]; + } + return img; +} + +/* + * Mac OS X log support. + * + * 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. + */ + +void SDL_NSLog(const char *text) +{ + NSLog(@"%s", text); +} + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vim: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavulkan.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavulkan.h new file mode 100644 index 0000000..a49c148 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavulkan.h @@ -0,0 +1,55 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + * @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's + * SDL_x11vulkan.h. + */ + + +#include "../../SDL_internal.h" + +#ifndef SDL_cocoavulkan_h_ +#define SDL_cocoavulkan_h_ + +#include "../SDL_vulkan_internal.h" +#include "../SDL_sysvideo.h" + +#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_COCOA + +int Cocoa_Vulkan_LoadLibrary(_THIS, const char *path); +void Cocoa_Vulkan_UnloadLibrary(_THIS); +SDL_bool Cocoa_Vulkan_GetInstanceExtensions(_THIS, + SDL_Window *window, + unsigned *count, + const char **names); +SDL_bool Cocoa_Vulkan_CreateSurface(_THIS, + SDL_Window *window, + VkInstance instance, + VkSurfaceKHR *surface); + +void Cocoa_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h); + +#endif + +#endif /* SDL_cocoavulkan_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavulkan.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavulkan.m new file mode 100644 index 0000000..0e53d21 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoavulkan.m @@ -0,0 +1,230 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* + * @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's + * SDL_x11vulkan.c. + */ + +#include "../../SDL_internal.h" + +#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_COCOA + +#include "SDL_cocoavideo.h" +#include "SDL_cocoawindow.h" +#include "SDL_assert.h" + +#include "SDL_loadso.h" +#include "SDL_cocoametalview.h" +#include "SDL_cocoavulkan.h" +#include "SDL_syswm.h" + +#include <dlfcn.h> + +const char* defaultPaths[] = { + "vulkan.framework/vulkan", + "libvulkan.1.dylib", + "MoltenVK.framework/MoltenVK", + "libMoltenVK.dylib" +}; + +/* Since libSDL is most likely a .dylib, need RTLD_DEFAULT not RTLD_SELF. */ +#define DEFAULT_HANDLE RTLD_DEFAULT + +int Cocoa_Vulkan_LoadLibrary(_THIS, const char *path) +{ + VkExtensionProperties *extensions = NULL; + Uint32 extensionCount = 0; + SDL_bool hasSurfaceExtension = SDL_FALSE; + SDL_bool hasMacOSSurfaceExtension = SDL_FALSE; + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; + + if (_this->vulkan_config.loader_handle) { + return SDL_SetError("Vulkan/MoltenVK already loaded"); + } + + /* Load the Vulkan loader library */ + if (!path) { + path = SDL_getenv("SDL_VULKAN_LIBRARY"); + } + + if (!path) { + /* MoltenVK framework, currently, v0.17.0, has a static library and is + * the recommended way to use the package. There is likely no object to + * load. */ + vkGetInstanceProcAddr = + (PFN_vkGetInstanceProcAddr)dlsym(DEFAULT_HANDLE, + "vkGetInstanceProcAddr"); + } + + if (vkGetInstanceProcAddr) { + _this->vulkan_config.loader_handle = DEFAULT_HANDLE; + } else { + const char** paths; + const char *foundPath = NULL; + int numPaths; + int i; + + if (path) { + paths = &path; + numPaths = 1; + } else { + /* Look for framework or .dylib packaged with the application + * instead. */ + paths = defaultPaths; + numPaths = SDL_arraysize(defaultPaths); + } + + for (i = 0; i < numPaths && _this->vulkan_config.loader_handle == NULL; i++) { + foundPath = paths[i]; + _this->vulkan_config.loader_handle = SDL_LoadObject(foundPath); + } + + if (_this->vulkan_config.loader_handle == NULL) { + return SDL_SetError("Failed to load Vulkan/MoltenVK library"); + } + + SDL_strlcpy(_this->vulkan_config.loader_path, foundPath, + SDL_arraysize(_this->vulkan_config.loader_path)); + vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction( + _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr"); + } + + if (!vkGetInstanceProcAddr) { + SDL_SetError("Failed to find %s in either executable or %s: %s", + "vkGetInstanceProcAddr", + _this->vulkan_config.loader_path, + (const char *) dlerror()); + goto fail; + } + + _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr; + _this->vulkan_config.vkEnumerateInstanceExtensionProperties = + (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)( + VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"); + if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) { + goto fail; + } + extensions = SDL_Vulkan_CreateInstanceExtensionsList( + (PFN_vkEnumerateInstanceExtensionProperties) + _this->vulkan_config.vkEnumerateInstanceExtensionProperties, + &extensionCount); + if (!extensions) { + goto fail; + } + for (Uint32 i = 0; i < extensionCount; i++) { + if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { + hasSurfaceExtension = SDL_TRUE; + } else if (SDL_strcmp(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { + hasMacOSSurfaceExtension = SDL_TRUE; + } + } + SDL_free(extensions); + if (!hasSurfaceExtension) { + SDL_SetError("Installed MoltenVK/Vulkan doesn't implement the " + VK_KHR_SURFACE_EXTENSION_NAME " extension"); + goto fail; + } else if (!hasMacOSSurfaceExtension) { + SDL_SetError("Installed MoltenVK/Vulkan doesn't implement the " + VK_MVK_MACOS_SURFACE_EXTENSION_NAME "extension"); + goto fail; + } + return 0; + +fail: + SDL_UnloadObject(_this->vulkan_config.loader_handle); + _this->vulkan_config.loader_handle = NULL; + return -1; +} + +void Cocoa_Vulkan_UnloadLibrary(_THIS) +{ + if (_this->vulkan_config.loader_handle) { + if (_this->vulkan_config.loader_handle != DEFAULT_HANDLE) { + SDL_UnloadObject(_this->vulkan_config.loader_handle); + } + _this->vulkan_config.loader_handle = NULL; + } +} + +SDL_bool Cocoa_Vulkan_GetInstanceExtensions(_THIS, + SDL_Window *window, + unsigned *count, + const char **names) +{ + static const char *const extensionsForCocoa[] = { + VK_KHR_SURFACE_EXTENSION_NAME, VK_MVK_MACOS_SURFACE_EXTENSION_NAME + }; + if (!_this->vulkan_config.loader_handle) { + SDL_SetError("Vulkan is not loaded"); + return SDL_FALSE; + } + return SDL_Vulkan_GetInstanceExtensions_Helper( + count, names, SDL_arraysize(extensionsForCocoa), + extensionsForCocoa); +} + +SDL_bool Cocoa_Vulkan_CreateSurface(_THIS, + SDL_Window *window, + VkInstance instance, + VkSurfaceKHR *surface) +{ + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = + (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; + PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK = + (PFN_vkCreateMacOSSurfaceMVK)vkGetInstanceProcAddr( + (VkInstance)instance, + "vkCreateMacOSSurfaceMVK"); + VkMacOSSurfaceCreateInfoMVK createInfo = {}; + VkResult result; + + if (!_this->vulkan_config.loader_handle) { + SDL_SetError("Vulkan is not loaded"); + return SDL_FALSE; + } + + if (!vkCreateMacOSSurfaceMVK) { + SDL_SetError(VK_MVK_MACOS_SURFACE_EXTENSION_NAME + " extension is not enabled in the Vulkan instance."); + return SDL_FALSE; + } + createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + createInfo.pNext = NULL; + createInfo.flags = 0; + createInfo.pView = Cocoa_Mtl_AddMetalView(window); + result = vkCreateMacOSSurfaceMVK(instance, &createInfo, + NULL, surface); + if (result != VK_SUCCESS) { + SDL_SetError("vkCreateMacOSSurfaceMVK failed: %s", + SDL_Vulkan_GetResultString(result)); + return SDL_FALSE; + } + return SDL_TRUE; +} + +void Cocoa_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h) +{ + Cocoa_Mtl_GetDrawableSize(window, w, h); +} + +#endif + +/* vim: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoawindow.h b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoawindow.h new file mode 100644 index 0000000..2311e3d --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoawindow.h @@ -0,0 +1,155 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_cocoawindow_h_ +#define SDL_cocoawindow_h_ + +#import <Cocoa/Cocoa.h> + +#if SDL_VIDEO_OPENGL_EGL +#include "../SDL_egl_c.h" +#endif + +typedef struct SDL_WindowData SDL_WindowData; + +typedef enum +{ + PENDING_OPERATION_NONE, + PENDING_OPERATION_ENTER_FULLSCREEN, + PENDING_OPERATION_LEAVE_FULLSCREEN, + PENDING_OPERATION_MINIMIZE +} PendingWindowOperation; + +@interface Cocoa_WindowListener : NSResponder <NSWindowDelegate> { + SDL_WindowData *_data; + BOOL observingVisible; + BOOL wasCtrlLeft; + BOOL wasVisible; + BOOL isFullscreenSpace; + BOOL inFullscreenTransition; + PendingWindowOperation pendingWindowOperation; + BOOL isMoving; + int pendingWindowWarpX, pendingWindowWarpY; + BOOL isDragAreaRunning; +} + +-(void) listen:(SDL_WindowData *) data; +-(void) pauseVisibleObservation; +-(void) resumeVisibleObservation; +-(BOOL) setFullscreenSpace:(BOOL) state; +-(BOOL) isInFullscreenSpace; +-(BOOL) isInFullscreenSpaceTransition; +-(void) addPendingWindowOperation:(PendingWindowOperation) operation; +-(void) close; + +-(BOOL) isMoving; +-(void) setPendingMoveX:(int)x Y:(int)y; +-(void) windowDidFinishMoving; + +/* Window delegate functionality */ +-(BOOL) windowShouldClose:(id) sender; +-(void) windowDidExpose:(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) windowWillEnterFullScreen:(NSNotification *) aNotification; +-(void) windowDidEnterFullScreen:(NSNotification *) aNotification; +-(void) windowWillExitFullScreen:(NSNotification *) aNotification; +-(void) windowDidExitFullScreen:(NSNotification *) aNotification; +-(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions; + +/* See if event is in a drag area, toggle on window dragging. */ +-(BOOL) processHitTest:(NSEvent *)theEvent; + +/* Window event handling */ +-(void) mouseDown:(NSEvent *) theEvent; +-(void) rightMouseDown:(NSEvent *) theEvent; +-(void) otherMouseDown:(NSEvent *) theEvent; +-(void) mouseUp:(NSEvent *) theEvent; +-(void) rightMouseUp:(NSEvent *) theEvent; +-(void) otherMouseUp:(NSEvent *) theEvent; +-(void) mouseMoved:(NSEvent *) theEvent; +-(void) mouseDragged:(NSEvent *) theEvent; +-(void) rightMouseDragged:(NSEvent *) theEvent; +-(void) otherMouseDragged:(NSEvent *) theEvent; +-(void) scrollWheel:(NSEvent *) theEvent; +-(void) touchesBeganWithEvent:(NSEvent *) theEvent; +-(void) touchesMovedWithEvent:(NSEvent *) theEvent; +-(void) touchesEndedWithEvent:(NSEvent *) theEvent; +-(void) touchesCancelledWithEvent:(NSEvent *) theEvent; + +/* Touch event handling */ +-(void) handleTouches:(NSTouchPhase) phase withEvent:(NSEvent*) theEvent; + +@end +/* *INDENT-ON* */ + +@class SDLOpenGLContext; + +struct SDL_WindowData +{ + SDL_Window *window; + NSWindow *nswindow; + NSMutableArray *nscontexts; + SDL_bool created; + SDL_bool inWindowMove; + Cocoa_WindowListener *listener; + struct SDL_VideoData *videodata; +#if SDL_VIDEO_OPENGL_EGL + EGLSurface egl_surface; +#endif +}; + +extern int Cocoa_CreateWindow(_THIS, SDL_Window * window); +extern int Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, + const void *data); +extern void Cocoa_SetWindowTitle(_THIS, SDL_Window * window); +extern void Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon); +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 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); +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_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen); +extern int Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp); +extern int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp); +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); + +#endif /* SDL_cocoawindow_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoawindow.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoawindow.m new file mode 100644 index 0000000..a8e95cc --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoawindow.m @@ -0,0 +1,1905 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_COCOA + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 +# error SDL for Mac OS X must be built with a 10.7 SDK or above. +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1070 */ + +#include "SDL_syswm.h" +#include "SDL_timer.h" /* For SDL_GetTicks() */ +#include "SDL_hints.h" +#include "../SDL_sysvideo.h" +#include "../../events/SDL_keyboard_c.h" +#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_cocoamousetap.h" +#include "SDL_cocoaopengl.h" +#include "SDL_cocoaopengles.h" +#include "SDL_assert.h" + +/* #define DEBUG_COCOAWINDOW */ + +#ifdef DEBUG_COCOAWINDOW +#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__) +#else +#define DLog(...) do { } while (0) +#endif + + +#define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN) + + +@interface SDLWindow : NSWindow <NSDraggingDestination> +/* 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 <NSDraggingInfo>)sender; +- (BOOL)performDragOperation:(id <NSDraggingInfo>)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)) != 0) { + return NO; + } else if ((window->flags & SDL_WINDOW_RESIZABLE) == 0) { + return NO; + } + } + return [super validateMenuItem:menuItem]; +} + +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +- (BOOL)canBecomeMainWindow +{ + return YES; +} + +- (void)sendEvent:(NSEvent *)event +{ + [super sendEvent:event]; + + if ([event type] != NSEventTypeLeftMouseUp) { + return; + } + + id delegate = [self delegate]; + if (![delegate isKindOfClass:[Cocoa_WindowListener class]]) { + return; + } + + if ([delegate isMoving]) { + [delegate windowDidFinishMoving]; + } +} + +/* 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));*/ +} + +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +{ + if (([sender draggingSourceOperationMask] & NSDragOperationGeneric) == NSDragOperationGeneric) { + return NSDragOperationGeneric; + } + + return NSDragOperationNone; /* no idea what to do with this, reject it. */ +} + +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender +{ @autoreleasepool +{ + NSPasteboard *pasteboard = [sender draggingPasteboard]; + NSArray *types = [NSArray arrayWithObject:NSFilenamesPboardType]; + NSString *desiredType = [pasteboard availableTypeFromArray:types]; + SDL_Window *sdlwindow = [self findSDLWindow]; + + if (desiredType == nil) { + return NO; /* can't accept anything that's being dropped here. */ + } + + NSData *data = [pasteboard dataForType:desiredType]; + if (data == nil) { + return NO; + } + + SDL_assert([desiredType isEqualToString:NSFilenamesPboardType]); + NSArray *array = [pasteboard propertyListForType:@"NSFilenamesPboardType"]; + + 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 = ((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) +{ + r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height; +} + +static void +ScheduleContextUpdates(SDL_WindowData *data) +{ + NSOpenGLContext *currentContext = [NSOpenGLContext currentContext]; + NSMutableArray *contexts = data->nscontexts; + @synchronized (contexts) { + for (SDLOpenGLContext *context in contexts) { + if (context == currentContext) { + [context update]; + } else { + [context scheduleUpdate]; + } + } + } +} + +/* !!! FIXME: this should use a hint callback. */ +static int +GetHintCtrlClickEmulateRightClick() +{ + return SDL_GetHintBoolean(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, SDL_FALSE); +} + +static NSUInteger +GetWindowStyle(SDL_Window * window) +{ + NSUInteger style = 0; + + if (window->flags & SDL_WINDOW_FULLSCREEN) { + style = NSWindowStyleMaskBorderless; + } else { + if (window->flags & SDL_WINDOW_BORDERLESS) { + style = NSWindowStyleMaskBorderless; + } else { + style = (NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable); + } + if (window->flags & SDL_WINDOW_RESIZABLE) { + style |= NSWindowStyleMaskResizable; + } + } + return style; +} + +static SDL_bool +SetWindowStyle(SDL_Window * window, NSUInteger style) +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = data->nswindow; + + /* The view responder chain gets messed with during setStyleMask */ + if ([[nswindow contentView] nextResponder] == data->listener) { + [[nswindow contentView] setNextResponder:nil]; + } + + [nswindow setStyleMask:style]; + + /* The view responder chain gets messed with during setStyleMask */ + if ([[nswindow contentView] nextResponder] != data->listener) { + [[nswindow contentView] setNextResponder:data->listener]; + } + + return SDL_TRUE; +} + + +@implementation Cocoa_WindowListener + +- (void)listen:(SDL_WindowData *)data +{ + NSNotificationCenter *center; + NSWindow *window = data->nswindow; + NSView *view = [window contentView]; + + _data = data; + observingVisible = YES; + wasCtrlLeft = NO; + wasVisible = [window isVisible]; + isFullscreenSpace = NO; + inFullscreenTransition = NO; + pendingWindowOperation = PENDING_OPERATION_NONE; + isMoving = NO; + isDragAreaRunning = NO; + + center = [NSNotificationCenter defaultCenter]; + + if ([window delegate] != nil) { + [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification 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(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]; + } else { + [window setDelegate:self]; + } + + /* Haven't found a delegate / notification that triggers when the window is + * ordered out (is not visible any more). You can be ordered out without + * minimizing, so DidMiniaturize doesn't work. (e.g. -[NSWindow orderOut:]) + */ + [window addObserver:self + forKeyPath:@"visible" + options:NSKeyValueObservingOptionNew + context:NULL]; + + [window setNextResponder:self]; + [window setAcceptsMouseMovedEvents:YES]; + + [view setNextResponder:self]; + + [view setAcceptsTouchEvents:YES]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (!observingVisible) { + return; + } + + if (object == _data->nswindow && [keyPath isEqualToString:@"visible"]) { + int newVisibility = [[change objectForKey:@"new"] intValue]; + if (newVisibility) { + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0); + } else { + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0); + } + } +} + +-(void) pauseVisibleObservation +{ + observingVisible = NO; + wasVisible = [_data->nswindow isVisible]; +} + +-(void) resumeVisibleObservation +{ + BOOL isVisible = [_data->nswindow isVisible]; + observingVisible = YES; + if (wasVisible != isVisible) { + if (isVisible) { + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0); + } else { + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0); + } + + wasVisible = isVisible; + } +} + +-(BOOL) setFullscreenSpace:(BOOL) state +{ + SDL_Window *window = _data->window; + NSWindow *nswindow = _data->nswindow; + SDL_VideoData *videodata = ((SDL_WindowData *) window->driverdata)->videodata; + + 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. */ + } + + if (inFullscreenTransition) { + if (state) { + [self addPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN]; + } else { + [self addPendingWindowOperation:PENDING_OPERATION_LEAVE_FULLSCREEN]; + } + return YES; + } + inFullscreenTransition = YES; + + /* you need to be FullScreenPrimary, or toggleFullScreen doesn't work. Unset it again in windowDidExitFullScreen. */ + [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + [nswindow performSelectorOnMainThread: @selector(toggleFullScreen:) withObject:nswindow waitUntilDone:NO]; + return YES; +} + +-(BOOL) isInFullscreenSpace +{ + return isFullscreenSpace; +} + +-(BOOL) isInFullscreenSpaceTransition +{ + return inFullscreenTransition; +} + +-(void) addPendingWindowOperation:(PendingWindowOperation) operation +{ + pendingWindowOperation = operation; +} + +- (void)close +{ + NSNotificationCenter *center; + NSWindow *window = _data->nswindow; + NSView *view = [window contentView]; + + center = [NSNotificationCenter defaultCenter]; + + if ([window delegate] != self) { + [center removeObserver:self name:NSWindowDidExposeNotification 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: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]; + } else { + [window setDelegate:nil]; + } + + [window removeObserver:self forKeyPath:@"visible"]; + + if ([window nextResponder] == self) { + [window setNextResponder:nil]; + } + if ([view nextResponder] == self) { + [view setNextResponder:nil]; + } +} + +- (BOOL)isMoving +{ + return isMoving; +} + +-(void) setPendingMoveX:(int)x Y:(int)y +{ + pendingWindowWarpX = x; + pendingWindowWarpY = y; +} + +- (void)windowDidFinishMoving +{ + if ([self isMoving]) { + isMoving = NO; + + SDL_Mouse *mouse = SDL_GetMouse(); + if (pendingWindowWarpX != INT_MAX && pendingWindowWarpY != INT_MAX) { + mouse->WarpMouseGlobal(pendingWindowWarpX, pendingWindowWarpY); + pendingWindowWarpX = pendingWindowWarpY = INT_MAX; + } + if (mouse->relative_mode && !mouse->relative_mode_warp && mouse->focus == _data->window) { + mouse->SetRelativeMouseMode(SDL_TRUE); + } + } +} + +- (BOOL)windowShouldClose:(id)sender +{ + 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)windowWillMove:(NSNotification *)aNotification +{ + if ([_data->nswindow isKindOfClass:[SDLWindow class]]) { + pendingWindowWarpX = pendingWindowWarpY = INT_MAX; + isMoving = YES; + } +} + +- (void)windowDidMove:(NSNotification *)aNotification +{ + int x, y; + SDL_Window *window = _data->window; + NSWindow *nswindow = _data->nswindow; + BOOL fullscreen = window->flags & FULLSCREEN_MASK; + 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; + } + + if (s_moveHack) { + SDL_bool blockMove = ((SDL_GetTicks() - s_moveHack) < 500); + + s_moveHack = 0; + + if (blockMove) { + /* 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); + [nswindow setFrameOrigin:rect.origin]; + return; + } + } + + x = (int)rect.origin.x; + y = (int)rect.origin.y; + + ScheduleContextUpdates(_data); + + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y); +} + +- (void)windowDidResize:(NSNotification *)aNotification +{ + if (inFullscreenTransition) { + /* We'll take care of this at the end of the transition */ + return; + } + + SDL_Window *window = _data->window; + NSWindow *nswindow = _data->nswindow; + int x, y, w, h; + NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]]; + ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect); + x = (int)rect.origin.x; + y = (int)rect.origin.y; + w = (int)rect.size.width; + h = (int)rect.size.height; + + if (SDL_IsShapedWindow(window)) { + Cocoa_ResizeWindowShape(window); + } + + ScheduleContextUpdates(_data); + + /* The window can move during a resize event, such as when maximizing + or resizing from a corner */ + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y); + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h); + + const BOOL zoomed = [nswindow isZoomed]; + if (!zoomed) { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0); + } else if (zoomed) { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0); + } +} + +- (void)windowDidMiniaturize:(NSNotification *)aNotification +{ + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); +} + +- (void)windowDidDeminiaturize:(NSNotification *)aNotification +{ + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0); +} + +- (void)windowDidBecomeKey:(NSNotification *)aNotification +{ + SDL_Window *window = _data->window; + SDL_Mouse *mouse = SDL_GetMouse(); + + /* 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 isMoving]) { + 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]; + x = (int)point.x; + y = (int)(window->h - point.y); + + if (x >= 0 && x < window->w && y >= 0 && y < window->h) { + SDL_SendMouseMotion(window, 0, 0, x, y); + } + } + + /* Check to see if someone updated the clipboard */ + 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 != 0); +} + +- (void)windowDidResignKey:(NSNotification *)aNotification +{ + SDL_Mouse *mouse = SDL_GetMouse(); + if (mouse->relative_mode && !mouse->relative_mode_warp) { + mouse->SetRelativeMouseMode(SDL_FALSE); + } + + /* Some other window will get mouse events, since we're not key. */ + 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) { + SDL_SetKeyboardFocus(NULL); + } + + if (isFullscreenSpace) { + [NSMenu setMenuBarVisible:YES]; + } +} + +- (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)windowWillEnterFullScreen:(NSNotification *)aNotification +{ + SDL_Window *window = _data->window; + + SetWindowStyle(window, (NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskResizable)); + + 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_WindowData *data = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = data->nswindow; + + inFullscreenTransition = NO; + + if (pendingWindowOperation == PENDING_OPERATION_LEAVE_FULLSCREEN) { + pendingWindowOperation = PENDING_OPERATION_NONE; + [self setFullscreenSpace:NO]; + } else { + /* Unset the resizable flag. + This is a workaround for https://bugzilla.libsdl.org/show_bug.cgi?id=3697 + */ + SetWindowStyle(window, [nswindow styleMask] & (~NSWindowStyleMaskResizable)); + + if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) { + [NSMenu setMenuBarVisible:NO]; + } + + pendingWindowOperation = PENDING_OPERATION_NONE; + /* 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]; + } +} + +- (void)windowWillExitFullScreen:(NSNotification *)aNotification +{ + SDL_Window *window = _data->window; + + isFullscreenSpace = NO; + inFullscreenTransition = YES; + + /* As of OS X 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. + */ + SetWindowStyle(window, GetWindowStyle(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; + + inFullscreenTransition = NO; + + SetWindowStyle(window, GetWindowStyle(window)); + + [nswindow setLevel:kCGNormalWindowLevel]; + + if (pendingWindowOperation == PENDING_OPERATION_ENTER_FULLSCREEN) { + pendingWindowOperation = PENDING_OPERATION_NONE; + [self setFullscreenSpace:YES]; + } else if (pendingWindowOperation == PENDING_OPERATION_MINIMIZE) { + pendingWindowOperation = PENDING_OPERATION_NONE; + [nswindow miniaturize:nil]; + } else { + /* Adjust the fullscreen toggle button and readd menu now that we're here. */ + 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]; + } + [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? */ + if (window->flags & SDL_WINDOW_SHOWN) { + Cocoa_ShowWindow(SDL_GetVideoDevice(), window); + } + } +} + +-(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions +{ + if ((_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) { + return NSApplicationPresentationFullScreen | NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; + } else { + return proposedOptions; + } +} + + +/* 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);*/ +} +- (void)keyDown:(NSEvent *)theEvent +{ + /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/ +} +- (void)keyUp:(NSEvent *)theEvent +{ + /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/ +} + +/* 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));*/ +} + +- (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. */ +} + +- (void)mouseDown:(NSEvent *)theEvent +{ + int button; + int clicks; + + /* 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()) { + wasCtrlLeft = YES; + button = SDL_BUTTON_RIGHT; + } else { + wasCtrlLeft = NO; + button = SDL_BUTTON_LEFT; + } + break; + case 1: + button = SDL_BUTTON_RIGHT; + break; + case 2: + button = SDL_BUTTON_MIDDLE; + break; + default: + button = (int) [theEvent buttonNumber] + 1; + break; + } + + clicks = (int) [theEvent clickCount]; + SDL_SendMouseButtonClicks(_data->window, 0, SDL_PRESSED, button, clicks); +} + +- (void)rightMouseDown:(NSEvent *)theEvent +{ + [self mouseDown:theEvent]; +} + +- (void)otherMouseDown:(NSEvent *)theEvent +{ + [self mouseDown:theEvent]; +} + +- (void)mouseUp:(NSEvent *)theEvent +{ + int button; + int clicks; + + if ([self processHitTest:theEvent]) { + SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); + return; /* stopped dragging, drop event. */ + } + + switch ([theEvent buttonNumber]) { + case 0: + if (wasCtrlLeft) { + button = SDL_BUTTON_RIGHT; + wasCtrlLeft = NO; + } else { + button = SDL_BUTTON_LEFT; + } + break; + case 1: + button = SDL_BUTTON_RIGHT; + break; + case 2: + button = SDL_BUTTON_MIDDLE; + break; + default: + button = (int) [theEvent buttonNumber] + 1; + break; + } + + clicks = (int) [theEvent clickCount]; + SDL_SendMouseButtonClicks(_data->window, 0, SDL_RELEASED, button, clicks); +} + +- (void)rightMouseUp:(NSEvent *)theEvent +{ + [self mouseUp:theEvent]; +} + +- (void)otherMouseUp:(NSEvent *)theEvent +{ + [self mouseUp:theEvent]; +} + +- (void)mouseMoved:(NSEvent *)theEvent +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_Window *window = _data->window; + NSPoint point; + int x, y; + + if ([self processHitTest:theEvent]) { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); + return; /* dragging, drop event. */ + } + + if (mouse->relative_mode) { + return; + } + + point = [theEvent locationInWindow]; + x = (int)point.x; + y = (int)(window->h - point.y); + + if (window->flags & SDL_WINDOW_INPUT_GRABBED) { + if (x < 0 || x >= window->w || y < 0 || y >= window->h) { + 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 !SDL_MAC_NO_SANDBOX + CGPoint cgpoint; + + /* When SDL_MAC_NO_SANDBOX is set, this is handled by + * SDL_cocoamousetap.m. + */ + + cgpoint.x = window->x + x; + cgpoint.y = window->y + y; + + CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint); + CGAssociateMouseAndMouseCursorPosition(YES); + + Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y); +#endif + } + } + SDL_SendMouseMotion(window, 0, 0, x, y); +} + +- (void)mouseDragged:(NSEvent *)theEvent +{ + [self mouseMoved:theEvent]; +} + +- (void)rightMouseDragged:(NSEvent *)theEvent +{ + [self mouseMoved:theEvent]; +} + +- (void)otherMouseDragged:(NSEvent *)theEvent +{ + [self mouseMoved:theEvent]; +} + +- (void)scrollWheel:(NSEvent *)theEvent +{ + Cocoa_HandleMouseWheel(_data->window, theEvent); +} + +- (void)touchesBeganWithEvent:(NSEvent *) theEvent +{ + NSSet *touches = [theEvent touchesMatchingPhase:NSTouchPhaseAny inView:nil]; + int existingTouchCount = 0; + + for (NSTouch* touch in touches) { + if ([touch phase] != NSTouchPhaseBegan) { + existingTouchCount++; + } + } + if (existingTouchCount == 0) { + SDL_TouchID touchID = (SDL_TouchID)(intptr_t)[[touches anyObject] device]; + int numFingers = SDL_GetNumTouchFingers(touchID); + DLog("Reset Lost Fingers: %d", numFingers); + for (--numFingers; numFingers >= 0; --numFingers) { + SDL_Finger* finger = SDL_GetTouchFinger(touchID, numFingers); + SDL_SendTouch(touchID, finger->id, SDL_FALSE, 0, 0, 0); + } + } + + DLog("Began Fingers: %lu .. existing: %d", (unsigned long)[touches count], existingTouchCount); + [self handleTouches:NSTouchPhaseBegan withEvent:theEvent]; +} + +- (void)touchesMovedWithEvent:(NSEvent *) theEvent +{ + [self handleTouches:NSTouchPhaseMoved withEvent:theEvent]; +} + +- (void)touchesEndedWithEvent:(NSEvent *) theEvent +{ + [self handleTouches:NSTouchPhaseEnded withEvent:theEvent]; +} + +- (void)touchesCancelledWithEvent:(NSEvent *) theEvent +{ + [self handleTouches:NSTouchPhaseCancelled withEvent:theEvent]; +} + +- (void)handleTouches:(NSTouchPhase) phase withEvent:(NSEvent *) theEvent +{ + NSSet *touches = [theEvent touchesMatchingPhase:phase inView:nil]; + + for (NSTouch *touch in touches) { + const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device]; + if (SDL_AddTouch(touchId, "") < 0) { + return; + } + + 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, SDL_TRUE, x, y, 1.0f); + break; + case NSTouchPhaseEnded: + case NSTouchPhaseCancelled: + SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f); + break; + case NSTouchPhaseMoved: + SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f); + break; + default: + break; + } + } +} + +@end + +@interface SDLView : NSView { + SDL_Window *_sdlWindow; +} + +- (void)setSDLWindow:(SDL_Window*)window; + +/* 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. 10.8 and later use updateLayer. */ +- (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. */ + [[NSColor blackColor] setFill]; + NSRectFill(dirtyRect); + SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0); +} + +-(BOOL) wantsUpdateLayer +{ + return YES; +} + +-(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 = NSColor.blackColor.CGColor; + ScheduleContextUpdates((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]; + SDL_Mouse *mouse = SDL_GetMouse(); + + if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) { + [self addCursorRect:[self bounds] + cursor:mouse->cur_cursor->driverdata]; + } else { + [self addCursorRect:[self bounds] + cursor:[NSCursor invisibleCursor]]; + } +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent +{ + if (SDL_GetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH)) { + return SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE); + } else { + return SDL_GetHintBoolean("SDL_MAC_MOUSE_FOCUS_CLICKTHROUGH", SDL_FALSE); + } +} +@end + +static int +SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created) +{ @autoreleasepool +{ + SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; + SDL_WindowData *data; + + /* Allocate the window data */ + window->driverdata = 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->nscontexts = [[NSMutableArray alloc] init]; + + /* Create an event listener for the window */ + 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); + window->x = (int)rect.origin.x; + window->y = (int)rect.origin.y; + window->w = (int)rect.size.width; + window->h = (int)rect.size.height; + } + + /* Set up the listener after we create the view */ + [data->listener listen:data]; + + if ([nswindow isVisible]) { + window->flags |= SDL_WINDOW_SHOWN; + } else { + window->flags &= ~SDL_WINDOW_SHOWN; + } + + { + unsigned long style = [nswindow styleMask]; + + if (style == NSWindowStyleMaskBorderless) { + window->flags |= SDL_WINDOW_BORDERLESS; + } else { + window->flags &= ~SDL_WINDOW_BORDERLESS; + } + if (style & NSWindowStyleMaskResizable) { + window->flags |= SDL_WINDOW_RESIZABLE; + } else { + window->flags &= ~SDL_WINDOW_RESIZABLE; + } + } + + /* isZoomed always returns true if the window is not resizable */ + if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) { + window->flags |= SDL_WINDOW_MAXIMIZED; + } else { + window->flags &= ~SDL_WINDOW_MAXIMIZED; + } + + if ([nswindow isMiniaturized]) { + window->flags |= SDL_WINDOW_MINIMIZED; + } else { + window->flags &= ~SDL_WINDOW_MINIMIZED; + } + + if ([nswindow isKeyWindow]) { + window->flags |= SDL_WINDOW_INPUT_FOCUS; + SDL_SetKeyboardFocus(data->window); + } + + /* 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 = data; + return 0; +}} + +int +Cocoa_CreateWindow(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; + NSWindow *nswindow; + SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); + NSRect rect; + SDL_Rect bounds; + NSUInteger style; + NSArray *screens = [NSScreen screens]; + + 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); + + style = GetWindowStyle(window); + + /* Figure out which screen to place this window */ + NSScreen *screen = nil; + for (NSScreen *candidate in screens) { + NSRect screenRect = [candidate frame]; + if (rect.origin.x >= screenRect.origin.x && + rect.origin.x < screenRect.origin.x + screenRect.size.width && + rect.origin.y >= screenRect.origin.y && + rect.origin.y < screenRect.origin.y + screenRect.size.height) { + screen = candidate; + rect.origin.x -= screenRect.origin.x; + rect.origin.y -= screenRect.origin.y; + } + } + + @try { + nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen]; + } + @catch (NSException *e) { + return SDL_SetError("%s", [[e reason] UTF8String]); + } + + if (videodata->allow_spaces) { + SDL_assert(floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6); + 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]; + } + } + + /* Create a default view for this window */ + rect = [nswindow contentRectForFrameRect:[nswindow frame]]; + SDLView *contentView = [[SDLView alloc] initWithFrame:rect]; + [contentView setSDLWindow:window]; + + if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { + if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) { + [contentView setWantsBestResolutionOpenGLSurface:YES]; + } + } + +#if SDL_VIDEO_OPENGL_ES2 +#if 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]; + [contentView release]; + + if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) { + [nswindow release]; + return -1; + } + + if (!(window->flags & SDL_WINDOW_OPENGL)) { + return 0; + } + + /* The rest of this macro mess is for OpenGL or OpenGL ES windows */ +#if SDL_VIDEO_OPENGL_ES2 + if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { +#if 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 */ + } +#endif /* SDL_VIDEO_OPENGL_ES2 */ + return 0; +}} + +int +Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) +{ @autoreleasepool +{ + NSWindow *nswindow = (NSWindow *) data; + NSString *title; + + /* Query the title from the existing window */ + title = [nswindow title]; + if (title) { + window->title = SDL_strdup([title UTF8String]); + } + + return SetupWindowData(_this, window, nswindow, SDL_FALSE); +}} + +void +Cocoa_SetWindowTitle(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + const char *title = window->title ? window->title : ""; + NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow; + NSString *string = [[NSString alloc] initWithUTF8String:title]; + [nswindow setTitle:string]; + [string release]; +}} + +void +Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) +{ @autoreleasepool +{ + NSImage *nsimage = Cocoa_CreateImage(icon); + + if (nsimage) { + [NSApp setApplicationIconImage:nsimage]; + } +}} + +void +Cocoa_SetWindowPosition(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = windata->nswindow; + NSRect rect; + Uint32 moveHack; + + 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); + + moveHack = s_moveHack; + s_moveHack = 0; + [nswindow setFrameOrigin:rect.origin]; + s_moveHack = moveHack; + + ScheduleContextUpdates(windata); +}} + +void +Cocoa_SetWindowSize(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = windata->nswindow; + NSRect rect; + Uint32 moveHack; + + /* 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); + + moveHack = s_moveHack; + s_moveHack = 0; + [nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES]; + s_moveHack = moveHack; + + ScheduleContextUpdates(windata); +}} + +void +Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; + + NSSize minSize; + minSize.width = window->min_w; + minSize.height = window->min_h; + + [windata->nswindow setContentMinSize:minSize]; +}} + +void +Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + 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_ShowWindow(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata); + NSWindow *nswindow = windowData->nswindow; + + if (![nswindow isMiniaturized]) { + [windowData->listener pauseVisibleObservation]; + [nswindow makeKeyAndOrderFront:nil]; + [windowData->listener resumeVisibleObservation]; + } +}} + +void +Cocoa_HideWindow(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow; + + [nswindow orderOut:nil]; +}} + +void +Cocoa_RaiseWindow(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + 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]; + if (![nswindow isMiniaturized] && [nswindow isVisible]) { + [NSApp activateIgnoringOtherApps:YES]; + [nswindow makeKeyAndOrderFront:nil]; + } + [windowData->listener resumeVisibleObservation]; +}} + +void +Cocoa_MaximizeWindow(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = windata->nswindow; + + [nswindow zoom:nil]; + + ScheduleContextUpdates(windata); +}} + +void +Cocoa_MinimizeWindow(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + 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]; + } +}} + +void +Cocoa_RestoreWindow(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + 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]; + } +}} + +void +Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) +{ @autoreleasepool +{ + if (SetWindowStyle(window, GetWindowStyle(window))) { + if (bordered) { + Cocoa_SetWindowTitle(_this, window); /* this got blanked out. */ + } + } +}} + +void +Cocoa_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable) +{ @autoreleasepool +{ + /* Don't set this if we're in a space! + * The window will get permanently stuck if resizable is false. + * -flibit + */ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + Cocoa_WindowListener *listener = data->listener; + if (![listener isInFullscreenSpace]) { + SetWindowStyle(window, GetWindowStyle(window)); + } +}} + +void +Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) +{ @autoreleasepool +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + NSWindow *nswindow = data->nswindow; + NSRect rect; + + /* The view responder chain gets messed with during setStyleMask */ + if ([[nswindow contentView] nextResponder] == data->listener) { + [[nswindow contentView] setNextResponder:nil]; + } + + if (fullscreen) { + SDL_Rect bounds; + + Cocoa_GetDisplayBounds(_this, display, &bounds); + rect.origin.x = bounds.x; + rect.origin.y = bounds.y; + rect.size.width = bounds.w; + rect.size.height = bounds.h; + ConvertNSRect([nswindow screen], fullscreen, &rect); + + /* 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]; + } else { + 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); + + [nswindow setStyleMask:GetWindowStyle(window)]; + + /* Hack to restore window decorations on Mac OS X 10.10 */ + NSRect 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]; + } + + /* The view responder chain gets messed with during setStyleMask */ + if ([[nswindow contentView] nextResponder] != data->listener) { + [[nswindow contentView] setNextResponder:data->listener]; + } + + s_moveHack = 0; + [nswindow setContentSize:rect.size]; + [nswindow setFrameOrigin:rect.origin]; + s_moveHack = SDL_GetTicks(); + + /* When the window style changes the title is cleared */ + if (!fullscreen) { + Cocoa_SetWindowTitle(_this, window); + } + + if (SDL_ShouldAllowTopmost() && fullscreen) { + /* OpenGL is rendering to the window, so make it visible! */ + [nswindow setLevel:CGShieldingWindowLevel()]; + } else { + [nswindow setLevel:kCGNormalWindowLevel]; + } + + if ([nswindow isVisible] || fullscreen) { + [data->listener pauseVisibleObservation]; + [nswindow makeKeyAndOrderFront:nil]; + [data->listener resumeVisibleObservation]; + } + + ScheduleContextUpdates(data); +}} + +int +Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) +{ + SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); + CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display; + const uint32_t tableSize = 256; + CGGammaValue redTable[tableSize]; + CGGammaValue greenTable[tableSize]; + CGGammaValue blueTable[tableSize]; + uint32_t i; + float inv65535 = 1.0f / 65535.0f; + + /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */ + for (i = 0; i < 256; i++) { + redTable[i] = ramp[0*256+i] * inv65535; + greenTable[i] = ramp[1*256+i] * inv65535; + blueTable[i] = ramp[2*256+i] * inv65535; + } + + if (CGSetDisplayTransferByTable(display_id, tableSize, + redTable, greenTable, blueTable) != CGDisplayNoErr) { + return SDL_SetError("CGSetDisplayTransferByTable()"); + } + return 0; +} + +int +Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp) +{ + SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); + CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display; + const uint32_t tableSize = 256; + CGGammaValue redTable[tableSize]; + CGGammaValue greenTable[tableSize]; + CGGammaValue blueTable[tableSize]; + uint32_t i, tableCopied; + + if (CGGetDisplayTransferByTable(display_id, tableSize, + redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) { + return SDL_SetError("CGGetDisplayTransferByTable()"); + } + + for (i = 0; i < tableCopied; i++) { + ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f); + ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f); + ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f); + } + return 0; +} + +void +Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + + /* Enable or disable the event tap as necessary */ + Cocoa_EnableMouseEventTap(mouse->driverdata, grabbed); + + /* Move the cursor to the nearest point in the window */ + if (grabbed && data && ![data->listener isMoving]) { + int x, y; + CGPoint cgpoint; + + SDL_GetMouseState(&x, &y); + cgpoint.x = window->x + x; + cgpoint.y = window->y + y; + + Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y); + + DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y); + CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint); + } + + if ( data && (window->flags & SDL_WINDOW_FULLSCREEN) ) { + if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS) + && ![data->listener isInFullscreenSpace]) { + /* 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 { + [data->nswindow setLevel:kCGNormalWindowLevel]; + } + } +} + +void +Cocoa_DestroyWindow(_THIS, SDL_Window * window) +{ @autoreleasepool +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + + if (data) { + if ([data->listener isInFullscreenSpace]) { + [NSMenu setMenuBarVisible:YES]; + } + [data->listener close]; + [data->listener release]; + if (data->created) { + [data->nswindow close]; + } + + NSArray *contexts = [[data->nscontexts copy] autorelease]; + for (SDLOpenGLContext *context in contexts) { + /* Calling setWindow:NULL causes the context to remove itself from the context list. */ + [context setWindow:NULL]; + } + [data->nscontexts release]; + + SDL_free(data); + } + window->driverdata = NULL; +}} + +SDL_bool +Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) +{ + 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.%d", + SDL_MAJOR_VERSION, SDL_MINOR_VERSION); + return SDL_FALSE; + } +} + +SDL_bool +Cocoa_IsWindowInFullscreenSpace(SDL_Window * window) +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + + if ([data->listener isInFullscreenSpace]) { + return SDL_TRUE; + } else { + return SDL_FALSE; + } +} + +SDL_bool +Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state) +{ @autoreleasepool +{ + SDL_bool succeeded = SDL_FALSE; + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + + 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 */ + succeeded = SDL_TRUE; + } + + return succeeded; +}} + +int +Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled) +{ + return 0; /* just succeed, the real work is done elsewhere. */ +} + +void +Cocoa_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept) +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + if (accept) { + [data->nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]]; + } else { + [data->nswindow unregisterDraggedTypes]; + } +} + +int +Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity) +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + [data->nswindow setAlphaValue:opacity]; + return 0; +} + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ |