src/video/cocoa/SDL_cocoawindow.m
author Alex Szpakowski <slime73@gmail.com>
Sat, 10 Nov 2018 20:56:23 -0400
changeset 12405 5a95fbfd3617
parent 12404 eb60e952b13f
child 12424 6b3a68e3dd06
permissions -rw-r--r--
cocoa: fix building with the macOS 10.7 SDK (thanks Riccardo!)

Fixes bug #4368
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_COCOA
    24 
    25 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
    26 # error SDL for Mac OS X must be built with a 10.7 SDK or above.
    27 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1070 */
    28 
    29 #include "SDL_syswm.h"
    30 #include "SDL_timer.h"  /* For SDL_GetTicks() */
    31 #include "SDL_hints.h"
    32 #include "../SDL_sysvideo.h"
    33 #include "../../events/SDL_keyboard_c.h"
    34 #include "../../events/SDL_mouse_c.h"
    35 #include "../../events/SDL_touch_c.h"
    36 #include "../../events/SDL_windowevents_c.h"
    37 #include "../../events/SDL_dropevents_c.h"
    38 #include "SDL_cocoavideo.h"
    39 #include "SDL_cocoashape.h"
    40 #include "SDL_cocoamouse.h"
    41 #include "SDL_cocoamousetap.h"
    42 #include "SDL_cocoaopengl.h"
    43 #include "SDL_cocoaopengles.h"
    44 #include "SDL_assert.h"
    45 
    46 /* #define DEBUG_COCOAWINDOW */
    47 
    48 #ifdef DEBUG_COCOAWINDOW
    49 #define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
    50 #else
    51 #define DLog(...) do { } while (0)
    52 #endif
    53 
    54 
    55 #define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN)
    56 
    57 
    58 @interface SDLWindow : NSWindow <NSDraggingDestination>
    59 /* These are needed for borderless/fullscreen windows */
    60 - (BOOL)canBecomeKeyWindow;
    61 - (BOOL)canBecomeMainWindow;
    62 - (void)sendEvent:(NSEvent *)event;
    63 - (void)doCommandBySelector:(SEL)aSelector;
    64 
    65 /* Handle drag-and-drop of files onto the SDL window. */
    66 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
    67 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
    68 - (BOOL)wantsPeriodicDraggingUpdates;
    69 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem;
    70 
    71 - (SDL_Window*)findSDLWindow;
    72 @end
    73 
    74 @implementation SDLWindow
    75 
    76 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
    77 {
    78     /* Only allow using the macOS native fullscreen toggle menubar item if the
    79      * window is resizable and not in a SDL fullscreen mode.
    80      */
    81     if ([menuItem action] == @selector(toggleFullScreen:)) {
    82         SDL_Window *window = [self findSDLWindow];
    83         if (window == NULL) {
    84             return NO;
    85         } else if ((window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0) {
    86             return NO;
    87         } else if ((window->flags & SDL_WINDOW_RESIZABLE) == 0) {
    88             return NO;
    89         }
    90     }
    91     return [super validateMenuItem:menuItem];
    92 }
    93 
    94 - (BOOL)canBecomeKeyWindow
    95 {
    96     return YES;
    97 }
    98 
    99 - (BOOL)canBecomeMainWindow
   100 {
   101     return YES;
   102 }
   103 
   104 - (void)sendEvent:(NSEvent *)event
   105 {
   106     [super sendEvent:event];
   107 
   108     if ([event type] != NSEventTypeLeftMouseUp) {
   109         return;
   110     }
   111 
   112     id delegate = [self delegate];
   113     if (![delegate isKindOfClass:[Cocoa_WindowListener class]]) {
   114         return;
   115     }
   116 
   117     if ([delegate isMoving]) {
   118         [delegate windowDidFinishMoving];
   119     }
   120 }
   121 
   122 /* We'll respond to selectors by doing nothing so we don't beep.
   123  * The escape key gets converted to a "cancel" selector, etc.
   124  */
   125 - (void)doCommandBySelector:(SEL)aSelector
   126 {
   127     /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
   128 }
   129 
   130 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
   131 {
   132     if (([sender draggingSourceOperationMask] & NSDragOperationGeneric) == NSDragOperationGeneric) {
   133         return NSDragOperationGeneric;
   134     }
   135 
   136     return NSDragOperationNone; /* no idea what to do with this, reject it. */
   137 }
   138 
   139 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
   140 { @autoreleasepool
   141 {
   142     NSPasteboard *pasteboard = [sender draggingPasteboard];
   143     NSArray *types = [NSArray arrayWithObject:NSFilenamesPboardType];
   144     NSString *desiredType = [pasteboard availableTypeFromArray:types];
   145     SDL_Window *sdlwindow = [self findSDLWindow];
   146 
   147     if (desiredType == nil) {
   148         return NO;  /* can't accept anything that's being dropped here. */
   149     }
   150 
   151     NSData *data = [pasteboard dataForType:desiredType];
   152     if (data == nil) {
   153         return NO;
   154     }
   155 
   156     SDL_assert([desiredType isEqualToString:NSFilenamesPboardType]);
   157     NSArray *array = [pasteboard propertyListForType:@"NSFilenamesPboardType"];
   158 
   159     for (NSString *path in array) {
   160         NSURL *fileURL = [NSURL fileURLWithPath:path];
   161         NSNumber *isAlias = nil;
   162 
   163         [fileURL getResourceValue:&isAlias forKey:NSURLIsAliasFileKey error:nil];
   164 
   165         /* If the URL is an alias, resolve it. */
   166         if ([isAlias boolValue]) {
   167             NSURLBookmarkResolutionOptions opts = NSURLBookmarkResolutionWithoutMounting | NSURLBookmarkResolutionWithoutUI;
   168             NSData *bookmark = [NSURL bookmarkDataWithContentsOfURL:fileURL error:nil];
   169             if (bookmark != nil) {
   170                 NSURL *resolvedURL = [NSURL URLByResolvingBookmarkData:bookmark
   171                                                                options:opts
   172                                                          relativeToURL:nil
   173                                                    bookmarkDataIsStale:nil
   174                                                                  error:nil];
   175 
   176                 if (resolvedURL != nil) {
   177                     fileURL = resolvedURL;
   178                 }
   179             }
   180         }
   181 
   182         if (!SDL_SendDropFile(sdlwindow, [[fileURL path] UTF8String])) {
   183             return NO;
   184         }
   185     }
   186 
   187     SDL_SendDropComplete(sdlwindow);
   188     return YES;
   189 }}
   190 
   191 - (BOOL)wantsPeriodicDraggingUpdates
   192 {
   193     return NO;
   194 }
   195 
   196 - (SDL_Window*)findSDLWindow
   197 {
   198     SDL_Window *sdlwindow = NULL;
   199     SDL_VideoDevice *_this = SDL_GetVideoDevice();
   200 
   201     /* !!! FIXME: is there a better way to do this? */
   202     if (_this) {
   203         for (sdlwindow = _this->windows; sdlwindow; sdlwindow = sdlwindow->next) {
   204             NSWindow *nswindow = ((SDL_WindowData *) sdlwindow->driverdata)->nswindow;
   205             if (nswindow == self) {
   206                 break;
   207             }
   208         }
   209     }
   210 
   211     return sdlwindow;
   212 }
   213 
   214 @end
   215 
   216 
   217 static Uint32 s_moveHack;
   218 
   219 static void ConvertNSRect(NSScreen *screen, BOOL fullscreen, NSRect *r)
   220 {
   221     r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
   222 }
   223 
   224 static void
   225 ScheduleContextUpdates(SDL_WindowData *data)
   226 {
   227     NSOpenGLContext *currentContext = [NSOpenGLContext currentContext];
   228     NSMutableArray *contexts = data->nscontexts;
   229     @synchronized (contexts) {
   230         for (SDLOpenGLContext *context in contexts) {
   231             if (context == currentContext) {
   232                 [context update];
   233             } else {
   234                 [context scheduleUpdate];
   235             }
   236         }
   237     }
   238 }
   239 
   240 /* !!! FIXME: this should use a hint callback. */
   241 static int
   242 GetHintCtrlClickEmulateRightClick()
   243 {
   244     return SDL_GetHintBoolean(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, SDL_FALSE);
   245 }
   246 
   247 static NSUInteger
   248 GetWindowStyle(SDL_Window * window)
   249 {
   250     NSUInteger style = 0;
   251 
   252     if (window->flags & SDL_WINDOW_FULLSCREEN) {
   253         style = NSWindowStyleMaskBorderless;
   254     } else {
   255         if (window->flags & SDL_WINDOW_BORDERLESS) {
   256             style = NSWindowStyleMaskBorderless;
   257         } else {
   258             style = (NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable);
   259         }
   260         if (window->flags & SDL_WINDOW_RESIZABLE) {
   261             style |= NSWindowStyleMaskResizable;
   262         }
   263     }
   264     return style;
   265 }
   266 
   267 static SDL_bool
   268 SetWindowStyle(SDL_Window * window, NSUInteger style)
   269 {
   270     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   271     NSWindow *nswindow = data->nswindow;
   272 
   273     /* The view responder chain gets messed with during setStyleMask */
   274     if ([[nswindow contentView] nextResponder] == data->listener) {
   275         [[nswindow contentView] setNextResponder:nil];
   276     }
   277 
   278     [nswindow setStyleMask:style];
   279 
   280     /* The view responder chain gets messed with during setStyleMask */
   281     if ([[nswindow contentView] nextResponder] != data->listener) {
   282         [[nswindow contentView] setNextResponder:data->listener];
   283     }
   284 
   285     return SDL_TRUE;
   286 }
   287 
   288 
   289 @implementation Cocoa_WindowListener
   290 
   291 - (void)listen:(SDL_WindowData *)data
   292 {
   293     NSNotificationCenter *center;
   294     NSWindow *window = data->nswindow;
   295     NSView *view = [window contentView];
   296 
   297     _data = data;
   298     observingVisible = YES;
   299     wasCtrlLeft = NO;
   300     wasVisible = [window isVisible];
   301     isFullscreenSpace = NO;
   302     inFullscreenTransition = NO;
   303     pendingWindowOperation = PENDING_OPERATION_NONE;
   304     isMoving = NO;
   305     isDragAreaRunning = NO;
   306 
   307     center = [NSNotificationCenter defaultCenter];
   308 
   309     if ([window delegate] != nil) {
   310         [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window];
   311         [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window];
   312         [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window];
   313         [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
   314         [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
   315         [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
   316         [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window];
   317         [center addObserver:self selector:@selector(windowDidChangeBackingProperties:) name:NSWindowDidChangeBackingPropertiesNotification object:window];
   318         [center addObserver:self selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:window];
   319         [center addObserver:self selector:@selector(windowDidEnterFullScreen:) name:NSWindowDidEnterFullScreenNotification object:window];
   320         [center addObserver:self selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:window];
   321         [center addObserver:self selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:window];
   322         [center addObserver:self selector:@selector(windowDidFailToEnterFullScreen:) name:@"NSWindowDidFailToEnterFullScreenNotification" object:window];
   323         [center addObserver:self selector:@selector(windowDidFailToExitFullScreen:) name:@"NSWindowDidFailToExitFullScreenNotification" object:window];
   324     } else {
   325         [window setDelegate:self];
   326     }
   327 
   328     /* Haven't found a delegate / notification that triggers when the window is
   329      * ordered out (is not visible any more). You can be ordered out without
   330      * minimizing, so DidMiniaturize doesn't work. (e.g. -[NSWindow orderOut:])
   331      */
   332     [window addObserver:self
   333              forKeyPath:@"visible"
   334                 options:NSKeyValueObservingOptionNew
   335                 context:NULL];
   336 
   337     [window setNextResponder:self];
   338     [window setAcceptsMouseMovedEvents:YES];
   339 
   340     [view setNextResponder:self];
   341 
   342     [view setAcceptsTouchEvents:YES];
   343 }
   344 
   345 - (void)observeValueForKeyPath:(NSString *)keyPath
   346                       ofObject:(id)object
   347                         change:(NSDictionary *)change
   348                        context:(void *)context
   349 {
   350     if (!observingVisible) {
   351         return;
   352     }
   353 
   354     if (object == _data->nswindow && [keyPath isEqualToString:@"visible"]) {
   355         int newVisibility = [[change objectForKey:@"new"] intValue];
   356         if (newVisibility) {
   357             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   358         } else {
   359             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   360         }
   361     }
   362 }
   363 
   364 -(void) pauseVisibleObservation
   365 {
   366     observingVisible = NO;
   367     wasVisible = [_data->nswindow isVisible];
   368 }
   369 
   370 -(void) resumeVisibleObservation
   371 {
   372     BOOL isVisible = [_data->nswindow isVisible];
   373     observingVisible = YES;
   374     if (wasVisible != isVisible) {
   375         if (isVisible) {
   376             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   377         } else {
   378             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   379         }
   380 
   381         wasVisible = isVisible;
   382     }
   383 }
   384 
   385 -(BOOL) setFullscreenSpace:(BOOL) state
   386 {
   387     SDL_Window *window = _data->window;
   388     NSWindow *nswindow = _data->nswindow;
   389     SDL_VideoData *videodata = ((SDL_WindowData *) window->driverdata)->videodata;
   390 
   391     if (!videodata->allow_spaces) {
   392         return NO;  /* Spaces are forcibly disabled. */
   393     } else if (state && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) {
   394         return NO;  /* we only allow you to make a Space on FULLSCREEN_DESKTOP windows. */
   395     } else if (!state && ((window->last_fullscreen_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) {
   396         return NO;  /* we only handle leaving the Space on windows that were previously FULLSCREEN_DESKTOP. */
   397     } else if (state == isFullscreenSpace) {
   398         return YES;  /* already there. */
   399     }
   400 
   401     if (inFullscreenTransition) {
   402         if (state) {
   403             [self addPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
   404         } else {
   405             [self addPendingWindowOperation:PENDING_OPERATION_LEAVE_FULLSCREEN];
   406         }
   407         return YES;
   408     }
   409     inFullscreenTransition = YES;
   410 
   411     /* you need to be FullScreenPrimary, or toggleFullScreen doesn't work. Unset it again in windowDidExitFullScreen. */
   412     [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
   413     [nswindow performSelectorOnMainThread: @selector(toggleFullScreen:) withObject:nswindow waitUntilDone:NO];
   414     return YES;
   415 }
   416 
   417 -(BOOL) isInFullscreenSpace
   418 {
   419     return isFullscreenSpace;
   420 }
   421 
   422 -(BOOL) isInFullscreenSpaceTransition
   423 {
   424     return inFullscreenTransition;
   425 }
   426 
   427 -(void) addPendingWindowOperation:(PendingWindowOperation) operation
   428 {
   429     pendingWindowOperation = operation;
   430 }
   431 
   432 - (void)close
   433 {
   434     NSNotificationCenter *center;
   435     NSWindow *window = _data->nswindow;
   436     NSView *view = [window contentView];
   437 
   438     center = [NSNotificationCenter defaultCenter];
   439 
   440     if ([window delegate] != self) {
   441         [center removeObserver:self name:NSWindowDidExposeNotification object:window];
   442         [center removeObserver:self name:NSWindowDidMoveNotification object:window];
   443         [center removeObserver:self name:NSWindowDidResizeNotification object:window];
   444         [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
   445         [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
   446         [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
   447         [center removeObserver:self name:NSWindowDidResignKeyNotification object:window];
   448         [center removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:window];
   449         [center removeObserver:self name:NSWindowWillEnterFullScreenNotification object:window];
   450         [center removeObserver:self name:NSWindowDidEnterFullScreenNotification object:window];
   451         [center removeObserver:self name:NSWindowWillExitFullScreenNotification object:window];
   452         [center removeObserver:self name:NSWindowDidExitFullScreenNotification object:window];
   453         [center removeObserver:self name:@"NSWindowDidFailToEnterFullScreenNotification" object:window];
   454         [center removeObserver:self name:@"NSWindowDidFailToExitFullScreenNotification" object:window];
   455     } else {
   456         [window setDelegate:nil];
   457     }
   458 
   459     [window removeObserver:self forKeyPath:@"visible"];
   460 
   461     if ([window nextResponder] == self) {
   462         [window setNextResponder:nil];
   463     }
   464     if ([view nextResponder] == self) {
   465         [view setNextResponder:nil];
   466     }
   467 }
   468 
   469 - (BOOL)isMoving
   470 {
   471     return isMoving;
   472 }
   473 
   474 -(void) setPendingMoveX:(int)x Y:(int)y
   475 {
   476     pendingWindowWarpX = x;
   477     pendingWindowWarpY = y;
   478 }
   479 
   480 - (void)windowDidFinishMoving
   481 {
   482     if ([self isMoving]) {
   483         isMoving = NO;
   484 
   485         SDL_Mouse *mouse = SDL_GetMouse();
   486         if (pendingWindowWarpX != INT_MAX && pendingWindowWarpY != INT_MAX) {
   487             mouse->WarpMouseGlobal(pendingWindowWarpX, pendingWindowWarpY);
   488             pendingWindowWarpX = pendingWindowWarpY = INT_MAX;
   489         }
   490         if (mouse->relative_mode && !mouse->relative_mode_warp && mouse->focus == _data->window) {
   491             mouse->SetRelativeMouseMode(SDL_TRUE);
   492         }
   493     }
   494 }
   495 
   496 - (BOOL)windowShouldClose:(id)sender
   497 {
   498     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   499     return NO;
   500 }
   501 
   502 - (void)windowDidExpose:(NSNotification *)aNotification
   503 {
   504     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
   505 }
   506 
   507 - (void)windowWillMove:(NSNotification *)aNotification
   508 {
   509     if ([_data->nswindow isKindOfClass:[SDLWindow class]]) {
   510         pendingWindowWarpX = pendingWindowWarpY = INT_MAX;
   511         isMoving = YES;
   512     }
   513 }
   514 
   515 - (void)windowDidMove:(NSNotification *)aNotification
   516 {
   517     int x, y;
   518     SDL_Window *window = _data->window;
   519     NSWindow *nswindow = _data->nswindow;
   520     BOOL fullscreen = window->flags & FULLSCREEN_MASK;
   521     NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   522     ConvertNSRect([nswindow screen], fullscreen, &rect);
   523 
   524     if (inFullscreenTransition) {
   525         /* We'll take care of this at the end of the transition */
   526         return;
   527     }
   528 
   529     if (s_moveHack) {
   530         SDL_bool blockMove = ((SDL_GetTicks() - s_moveHack) < 500);
   531 
   532         s_moveHack = 0;
   533 
   534         if (blockMove) {
   535             /* Cocoa is adjusting the window in response to a mode change */
   536             rect.origin.x = window->x;
   537             rect.origin.y = window->y;
   538             ConvertNSRect([nswindow screen], fullscreen, &rect);
   539             [nswindow setFrameOrigin:rect.origin];
   540             return;
   541         }
   542     }
   543 
   544     x = (int)rect.origin.x;
   545     y = (int)rect.origin.y;
   546 
   547     ScheduleContextUpdates(_data);
   548 
   549     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
   550 }
   551 
   552 - (void)windowDidResize:(NSNotification *)aNotification
   553 {
   554     if (inFullscreenTransition) {
   555         /* We'll take care of this at the end of the transition */
   556         return;
   557     }
   558 
   559     SDL_Window *window = _data->window;
   560     NSWindow *nswindow = _data->nswindow;
   561     int x, y, w, h;
   562     NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   563     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
   564     x = (int)rect.origin.x;
   565     y = (int)rect.origin.y;
   566     w = (int)rect.size.width;
   567     h = (int)rect.size.height;
   568 
   569     if (SDL_IsShapedWindow(window)) {
   570         Cocoa_ResizeWindowShape(window);
   571     }
   572 
   573     ScheduleContextUpdates(_data);
   574 
   575     /* The window can move during a resize event, such as when maximizing
   576        or resizing from a corner */
   577     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
   578     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
   579 
   580     const BOOL zoomed = [nswindow isZoomed];
   581     if (!zoomed) {
   582         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
   583     } else if (zoomed) {
   584         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
   585     }
   586 }
   587 
   588 - (void)windowDidMiniaturize:(NSNotification *)aNotification
   589 {
   590     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   591 }
   592 
   593 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
   594 {
   595     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
   596 }
   597 
   598 - (void)windowDidBecomeKey:(NSNotification *)aNotification
   599 {
   600     SDL_Window *window = _data->window;
   601     SDL_Mouse *mouse = SDL_GetMouse();
   602 
   603     /* We're going to get keyboard events, since we're key. */
   604     /* This needs to be done before restoring the relative mouse mode. */
   605     SDL_SetKeyboardFocus(window);
   606 
   607     if (mouse->relative_mode && !mouse->relative_mode_warp && ![self isMoving]) {
   608         mouse->SetRelativeMouseMode(SDL_TRUE);
   609     }
   610 
   611     /* If we just gained focus we need the updated mouse position */
   612     if (!mouse->relative_mode) {
   613         NSPoint point;
   614         int x, y;
   615 
   616         point = [_data->nswindow mouseLocationOutsideOfEventStream];
   617         x = (int)point.x;
   618         y = (int)(window->h - point.y);
   619 
   620         if (x >= 0 && x < window->w && y >= 0 && y < window->h) {
   621             SDL_SendMouseMotion(window, 0, 0, x, y);
   622         }
   623     }
   624 
   625     /* Check to see if someone updated the clipboard */
   626     Cocoa_CheckClipboardUpdate(_data->videodata);
   627 
   628     if ((isFullscreenSpace) && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)) {
   629         [NSMenu setMenuBarVisible:NO];
   630     }
   631 
   632     const unsigned int newflags = [NSEvent modifierFlags] & NSEventModifierFlagCapsLock;
   633     _data->videodata->modifierFlags = (_data->videodata->modifierFlags & ~NSEventModifierFlagCapsLock) | newflags;
   634     SDL_ToggleModState(KMOD_CAPS, newflags != 0);
   635 }
   636 
   637 - (void)windowDidResignKey:(NSNotification *)aNotification
   638 {
   639     SDL_Mouse *mouse = SDL_GetMouse();
   640     if (mouse->relative_mode && !mouse->relative_mode_warp) {
   641         mouse->SetRelativeMouseMode(SDL_FALSE);
   642     }
   643 
   644     /* Some other window will get mouse events, since we're not key. */
   645     if (SDL_GetMouseFocus() == _data->window) {
   646         SDL_SetMouseFocus(NULL);
   647     }
   648 
   649     /* Some other window will get keyboard events, since we're not key. */
   650     if (SDL_GetKeyboardFocus() == _data->window) {
   651         SDL_SetKeyboardFocus(NULL);
   652     }
   653 
   654     if (isFullscreenSpace) {
   655         [NSMenu setMenuBarVisible:YES];
   656     }
   657 }
   658 
   659 - (void)windowDidChangeBackingProperties:(NSNotification *)aNotification
   660 {
   661     NSNumber *oldscale = [[aNotification userInfo] objectForKey:NSBackingPropertyOldScaleFactorKey];
   662 
   663     if (inFullscreenTransition) {
   664         return;
   665     }
   666 
   667     if ([oldscale doubleValue] != [_data->nswindow backingScaleFactor]) {
   668         /* Force a resize event when the backing scale factor changes. */
   669         _data->window->w = 0;
   670         _data->window->h = 0;
   671         [self windowDidResize:aNotification];
   672     }
   673 }
   674 
   675 - (void)windowWillEnterFullScreen:(NSNotification *)aNotification
   676 {
   677     SDL_Window *window = _data->window;
   678 
   679     SetWindowStyle(window, (NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskResizable));
   680 
   681     isFullscreenSpace = YES;
   682     inFullscreenTransition = YES;
   683 }
   684 
   685 - (void)windowDidFailToEnterFullScreen:(NSNotification *)aNotification
   686 {
   687     SDL_Window *window = _data->window;
   688 
   689     if (window->is_destroying) {
   690         return;
   691     }
   692 
   693     SetWindowStyle(window, GetWindowStyle(window));
   694 
   695     isFullscreenSpace = NO;
   696     inFullscreenTransition = NO;
   697     
   698     [self windowDidExitFullScreen:nil];
   699 }
   700 
   701 - (void)windowDidEnterFullScreen:(NSNotification *)aNotification
   702 {
   703     SDL_Window *window = _data->window;
   704     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   705     NSWindow *nswindow = data->nswindow;
   706 
   707     inFullscreenTransition = NO;
   708 
   709     if (pendingWindowOperation == PENDING_OPERATION_LEAVE_FULLSCREEN) {
   710         pendingWindowOperation = PENDING_OPERATION_NONE;
   711         [self setFullscreenSpace:NO];
   712     } else {
   713         /* Unset the resizable flag. 
   714            This is a workaround for https://bugzilla.libsdl.org/show_bug.cgi?id=3697
   715          */
   716         SetWindowStyle(window, [nswindow styleMask] & (~NSWindowStyleMaskResizable));
   717 
   718         if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
   719             [NSMenu setMenuBarVisible:NO];
   720         }
   721 
   722         pendingWindowOperation = PENDING_OPERATION_NONE;
   723         /* Force the size change event in case it was delivered earlier
   724            while the window was still animating into place.
   725          */
   726         window->w = 0;
   727         window->h = 0;
   728         [self windowDidMove:aNotification];
   729         [self windowDidResize:aNotification];
   730     }
   731 }
   732 
   733 - (void)windowWillExitFullScreen:(NSNotification *)aNotification
   734 {
   735     SDL_Window *window = _data->window;
   736 
   737     isFullscreenSpace = NO;
   738     inFullscreenTransition = YES;
   739 
   740     /* As of OS X 10.11, the window seems to need to be resizable when exiting
   741        a Space, in order for it to resize back to its windowed-mode size.
   742      */
   743     SetWindowStyle(window, GetWindowStyle(window) | NSWindowStyleMaskResizable);
   744 }
   745 
   746 - (void)windowDidFailToExitFullScreen:(NSNotification *)aNotification
   747 {
   748     SDL_Window *window = _data->window;
   749     
   750     if (window->is_destroying) {
   751         return;
   752     }
   753 
   754     SetWindowStyle(window, (NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskResizable));
   755     
   756     isFullscreenSpace = YES;
   757     inFullscreenTransition = NO;
   758     
   759     [self windowDidEnterFullScreen:nil];
   760 }
   761 
   762 - (void)windowDidExitFullScreen:(NSNotification *)aNotification
   763 {
   764     SDL_Window *window = _data->window;
   765     NSWindow *nswindow = _data->nswindow;
   766 
   767     inFullscreenTransition = NO;
   768 
   769     SetWindowStyle(window, GetWindowStyle(window));
   770 
   771     [nswindow setLevel:kCGNormalWindowLevel];
   772 
   773     if (pendingWindowOperation == PENDING_OPERATION_ENTER_FULLSCREEN) {
   774         pendingWindowOperation = PENDING_OPERATION_NONE;
   775         [self setFullscreenSpace:YES];
   776     } else if (pendingWindowOperation == PENDING_OPERATION_MINIMIZE) {
   777         pendingWindowOperation = PENDING_OPERATION_NONE;
   778         [nswindow miniaturize:nil];
   779     } else {
   780         /* Adjust the fullscreen toggle button and readd menu now that we're here. */
   781         if (window->flags & SDL_WINDOW_RESIZABLE) {
   782             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
   783             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
   784         } else {
   785             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorManaged];
   786         }
   787         [NSMenu setMenuBarVisible:YES];
   788 
   789         pendingWindowOperation = PENDING_OPERATION_NONE;
   790 
   791 #if 0
   792 /* This fixed bug 3719, which is that changing window size while fullscreen
   793    doesn't take effect when leaving fullscreen, but introduces bug 3809,
   794    which is that a maximized window doesn't go back to normal size when
   795    restored, so this code is disabled until we can properly handle the
   796    beginning and end of maximize and restore.
   797  */
   798         /* Restore windowed size and position in case it changed while fullscreen */
   799         {
   800             NSRect rect;
   801             rect.origin.x = window->windowed.x;
   802             rect.origin.y = window->windowed.y;
   803             rect.size.width = window->windowed.w;
   804             rect.size.height = window->windowed.h;
   805             ConvertNSRect([nswindow screen], NO, &rect);
   806 
   807             s_moveHack = 0;
   808             [nswindow setContentSize:rect.size];
   809             [nswindow setFrameOrigin:rect.origin];
   810             s_moveHack = SDL_GetTicks();
   811         }
   812 #endif /* 0 */
   813 
   814         /* Force the size change event in case it was delivered earlier
   815            while the window was still animating into place.
   816          */
   817         window->w = 0;
   818         window->h = 0;
   819         [self windowDidMove:aNotification];
   820         [self windowDidResize:aNotification];
   821 
   822         /* FIXME: Why does the window get hidden? */
   823         if (window->flags & SDL_WINDOW_SHOWN) {
   824             Cocoa_ShowWindow(SDL_GetVideoDevice(), window);
   825         }
   826     }
   827 }
   828 
   829 -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
   830 {
   831     if ((_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
   832         return NSApplicationPresentationFullScreen | NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar;
   833     } else {
   834         return proposedOptions;
   835     }
   836 }
   837 
   838 
   839 /* We'll respond to key events by doing nothing so we don't beep.
   840  * We could handle key messages here, but we lose some in the NSApp dispatch,
   841  * where they get converted to action messages, etc.
   842  */
   843 - (void)flagsChanged:(NSEvent *)theEvent
   844 {
   845     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   846 }
   847 - (void)keyDown:(NSEvent *)theEvent
   848 {
   849     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   850 }
   851 - (void)keyUp:(NSEvent *)theEvent
   852 {
   853     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   854 }
   855 
   856 /* We'll respond to selectors by doing nothing so we don't beep.
   857  * The escape key gets converted to a "cancel" selector, etc.
   858  */
   859 - (void)doCommandBySelector:(SEL)aSelector
   860 {
   861     /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
   862 }
   863 
   864 - (BOOL)processHitTest:(NSEvent *)theEvent
   865 {
   866     SDL_assert(isDragAreaRunning == [_data->nswindow isMovableByWindowBackground]);
   867 
   868     if (_data->window->hit_test) {  /* if no hit-test, skip this. */
   869         const NSPoint location = [theEvent locationInWindow];
   870         const SDL_Point point = { (int) location.x, _data->window->h - (((int) location.y)-1) };
   871         const SDL_HitTestResult rc = _data->window->hit_test(_data->window, &point, _data->window->hit_test_data);
   872         if (rc == SDL_HITTEST_DRAGGABLE) {
   873             if (!isDragAreaRunning) {
   874                 isDragAreaRunning = YES;
   875                 [_data->nswindow setMovableByWindowBackground:YES];
   876             }
   877             return YES;  /* dragging! */
   878         }
   879     }
   880 
   881     if (isDragAreaRunning) {
   882         isDragAreaRunning = NO;
   883         [_data->nswindow setMovableByWindowBackground:NO];
   884         return YES;  /* was dragging, drop event. */
   885     }
   886 
   887     return NO;  /* not a special area, carry on. */
   888 }
   889 
   890 - (void)mouseDown:(NSEvent *)theEvent
   891 {
   892     int button;
   893     int clicks;
   894 
   895     /* Ignore events that aren't inside the client area (i.e. title bar.) */
   896     if ([theEvent window]) {
   897         NSRect windowRect = [[[theEvent window] contentView] frame];
   898         if (!NSMouseInRect([theEvent locationInWindow], windowRect, NO)) {
   899             return;
   900         }
   901     }
   902 
   903     if ([self processHitTest:theEvent]) {
   904         SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
   905         return;  /* dragging, drop event. */
   906     }
   907 
   908     switch ([theEvent buttonNumber]) {
   909     case 0:
   910         if (([theEvent modifierFlags] & NSEventModifierFlagControl) &&
   911             GetHintCtrlClickEmulateRightClick()) {
   912             wasCtrlLeft = YES;
   913             button = SDL_BUTTON_RIGHT;
   914         } else {
   915             wasCtrlLeft = NO;
   916             button = SDL_BUTTON_LEFT;
   917         }
   918         break;
   919     case 1:
   920         button = SDL_BUTTON_RIGHT;
   921         break;
   922     case 2:
   923         button = SDL_BUTTON_MIDDLE;
   924         break;
   925     default:
   926         button = (int) [theEvent buttonNumber] + 1;
   927         break;
   928     }
   929 
   930     clicks = (int) [theEvent clickCount];
   931     SDL_SendMouseButtonClicks(_data->window, 0, SDL_PRESSED, button, clicks);
   932 }
   933 
   934 - (void)rightMouseDown:(NSEvent *)theEvent
   935 {
   936     [self mouseDown:theEvent];
   937 }
   938 
   939 - (void)otherMouseDown:(NSEvent *)theEvent
   940 {
   941     [self mouseDown:theEvent];
   942 }
   943 
   944 - (void)mouseUp:(NSEvent *)theEvent
   945 {
   946     int button;
   947     int clicks;
   948 
   949     if ([self processHitTest:theEvent]) {
   950         SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
   951         return;  /* stopped dragging, drop event. */
   952     }
   953 
   954     switch ([theEvent buttonNumber]) {
   955     case 0:
   956         if (wasCtrlLeft) {
   957             button = SDL_BUTTON_RIGHT;
   958             wasCtrlLeft = NO;
   959         } else {
   960             button = SDL_BUTTON_LEFT;
   961         }
   962         break;
   963     case 1:
   964         button = SDL_BUTTON_RIGHT;
   965         break;
   966     case 2:
   967         button = SDL_BUTTON_MIDDLE;
   968         break;
   969     default:
   970         button = (int) [theEvent buttonNumber] + 1;
   971         break;
   972     }
   973 
   974     clicks = (int) [theEvent clickCount];
   975     SDL_SendMouseButtonClicks(_data->window, 0, SDL_RELEASED, button, clicks);
   976 }
   977 
   978 - (void)rightMouseUp:(NSEvent *)theEvent
   979 {
   980     [self mouseUp:theEvent];
   981 }
   982 
   983 - (void)otherMouseUp:(NSEvent *)theEvent
   984 {
   985     [self mouseUp:theEvent];
   986 }
   987 
   988 - (void)mouseMoved:(NSEvent *)theEvent
   989 {
   990     SDL_Mouse *mouse = SDL_GetMouse();
   991     SDL_Window *window = _data->window;
   992     NSPoint point;
   993     int x, y;
   994 
   995     if ([self processHitTest:theEvent]) {
   996         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
   997         return;  /* dragging, drop event. */
   998     }
   999 
  1000     if (mouse->relative_mode) {
  1001         return;
  1002     }
  1003 
  1004     point = [theEvent locationInWindow];
  1005     x = (int)point.x;
  1006     y = (int)(window->h - point.y);
  1007 
  1008     if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
  1009         if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
  1010             if (x < 0) {
  1011                 x = 0;
  1012             } else if (x >= window->w) {
  1013                 x = window->w - 1;
  1014             }
  1015             if (y < 0) {
  1016                 y = 0;
  1017             } else if (y >= window->h) {
  1018                 y = window->h - 1;
  1019             }
  1020 
  1021 #if !SDL_MAC_NO_SANDBOX
  1022             CGPoint cgpoint;
  1023 
  1024             /* When SDL_MAC_NO_SANDBOX is set, this is handled by
  1025              * SDL_cocoamousetap.m.
  1026              */
  1027 
  1028             cgpoint.x = window->x + x;
  1029             cgpoint.y = window->y + y;
  1030 
  1031             CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1032             CGAssociateMouseAndMouseCursorPosition(YES);
  1033 
  1034             Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
  1035 #endif
  1036         }
  1037     }
  1038     SDL_SendMouseMotion(window, 0, 0, x, y);
  1039 }
  1040 
  1041 - (void)mouseDragged:(NSEvent *)theEvent
  1042 {
  1043     [self mouseMoved:theEvent];
  1044 }
  1045 
  1046 - (void)rightMouseDragged:(NSEvent *)theEvent
  1047 {
  1048     [self mouseMoved:theEvent];
  1049 }
  1050 
  1051 - (void)otherMouseDragged:(NSEvent *)theEvent
  1052 {
  1053     [self mouseMoved:theEvent];
  1054 }
  1055 
  1056 - (void)scrollWheel:(NSEvent *)theEvent
  1057 {
  1058     Cocoa_HandleMouseWheel(_data->window, theEvent);
  1059 }
  1060 
  1061 - (void)touchesBeganWithEvent:(NSEvent *) theEvent
  1062 {
  1063     NSSet *touches = [theEvent touchesMatchingPhase:NSTouchPhaseAny inView:nil];
  1064     int existingTouchCount = 0;
  1065 
  1066     for (NSTouch* touch in touches) {
  1067         if ([touch phase] != NSTouchPhaseBegan) {
  1068             existingTouchCount++;
  1069         }
  1070     }
  1071     if (existingTouchCount == 0) {
  1072         SDL_TouchID touchID = (SDL_TouchID)(intptr_t)[[touches anyObject] device];
  1073         int numFingers = SDL_GetNumTouchFingers(touchID);
  1074         DLog("Reset Lost Fingers: %d", numFingers);
  1075         for (--numFingers; numFingers >= 0; --numFingers) {
  1076             SDL_Finger* finger = SDL_GetTouchFinger(touchID, numFingers);
  1077             SDL_SendTouch(touchID, finger->id, SDL_FALSE, 0, 0, 0);
  1078         }
  1079     }
  1080 
  1081     DLog("Began Fingers: %lu .. existing: %d", (unsigned long)[touches count], existingTouchCount);
  1082     [self handleTouches:NSTouchPhaseBegan withEvent:theEvent];
  1083 }
  1084 
  1085 - (void)touchesMovedWithEvent:(NSEvent *) theEvent
  1086 {
  1087     [self handleTouches:NSTouchPhaseMoved withEvent:theEvent];
  1088 }
  1089 
  1090 - (void)touchesEndedWithEvent:(NSEvent *) theEvent
  1091 {
  1092     [self handleTouches:NSTouchPhaseEnded withEvent:theEvent];
  1093 }
  1094 
  1095 - (void)touchesCancelledWithEvent:(NSEvent *) theEvent
  1096 {
  1097     [self handleTouches:NSTouchPhaseCancelled withEvent:theEvent];
  1098 }
  1099 
  1100 - (void)handleTouches:(NSTouchPhase) phase withEvent:(NSEvent *) theEvent
  1101 {
  1102     NSSet *touches = [theEvent touchesMatchingPhase:phase inView:nil];
  1103 
  1104     for (NSTouch *touch in touches) {
  1105         const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device];
  1106         SDL_TouchDeviceType devtype = SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE;
  1107 
  1108 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101202 /* Added in the 10.12.2 SDK. */
  1109         if ([touch respondsToSelector:@selector(type)]) {
  1110             if ([touch type] == NSTouchTypeDirect) {
  1111                 devtype = SDL_TOUCH_DEVICE_DIRECT;
  1112             }
  1113         }
  1114 #endif
  1115 
  1116         if (SDL_AddTouch(touchId, devtype, "") < 0) {
  1117             return;
  1118         }
  1119 
  1120         const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
  1121         float x = [touch normalizedPosition].x;
  1122         float y = [touch normalizedPosition].y;
  1123         /* Make the origin the upper left instead of the lower left */
  1124         y = 1.0f - y;
  1125 
  1126         switch (phase) {
  1127         case NSTouchPhaseBegan:
  1128             SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
  1129             break;
  1130         case NSTouchPhaseEnded:
  1131         case NSTouchPhaseCancelled:
  1132             SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
  1133             break;
  1134         case NSTouchPhaseMoved:
  1135             SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
  1136             break;
  1137         default:
  1138             break;
  1139         }
  1140     }
  1141 }
  1142 
  1143 @end
  1144 
  1145 @interface SDLView : NSView {
  1146     SDL_Window *_sdlWindow;
  1147 }
  1148 
  1149 - (void)setSDLWindow:(SDL_Window*)window;
  1150 
  1151 /* The default implementation doesn't pass rightMouseDown to responder chain */
  1152 - (void)rightMouseDown:(NSEvent *)theEvent;
  1153 - (BOOL)mouseDownCanMoveWindow;
  1154 - (void)drawRect:(NSRect)dirtyRect;
  1155 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent;
  1156 - (BOOL)wantsUpdateLayer;
  1157 - (void)updateLayer;
  1158 @end
  1159 
  1160 @implementation SDLView
  1161 
  1162 - (void)setSDLWindow:(SDL_Window*)window
  1163 {
  1164     _sdlWindow = window;
  1165 }
  1166 
  1167 /* this is used on older macOS revisions. 10.8 and later use updateLayer. */
  1168 - (void)drawRect:(NSRect)dirtyRect
  1169 {
  1170     /* Force the graphics context to clear to black so we don't get a flash of
  1171        white until the app is ready to draw. In practice on modern macOS, this
  1172        only gets called for window creation and other extraordinary events. */
  1173     [[NSColor blackColor] setFill];
  1174     NSRectFill(dirtyRect);
  1175     SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0);
  1176 }
  1177 
  1178 -(BOOL) wantsUpdateLayer
  1179 {
  1180     return YES;
  1181 }
  1182 
  1183 -(void) updateLayer
  1184 {
  1185     /* Force the graphics context to clear to black so we don't get a flash of
  1186        white until the app is ready to draw. In practice on modern macOS, this
  1187        only gets called for window creation and other extraordinary events. */
  1188     self.layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
  1189     ScheduleContextUpdates((SDL_WindowData *) _sdlWindow->driverdata);
  1190     SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0);
  1191 }
  1192 
  1193 - (void)rightMouseDown:(NSEvent *)theEvent
  1194 {
  1195     [[self nextResponder] rightMouseDown:theEvent];
  1196 }
  1197 
  1198 - (BOOL)mouseDownCanMoveWindow
  1199 {
  1200     /* Always say YES, but this doesn't do anything until we call
  1201        -[NSWindow setMovableByWindowBackground:YES], which we ninja-toggle
  1202        during mouse events when we're using a drag area. */
  1203     return YES;
  1204 }
  1205 
  1206 - (void)resetCursorRects
  1207 {
  1208     [super resetCursorRects];
  1209     SDL_Mouse *mouse = SDL_GetMouse();
  1210 
  1211     if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
  1212         [self addCursorRect:[self bounds]
  1213                      cursor:mouse->cur_cursor->driverdata];
  1214     } else {
  1215         [self addCursorRect:[self bounds]
  1216                      cursor:[NSCursor invisibleCursor]];
  1217     }
  1218 }
  1219 
  1220 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
  1221 {
  1222     if (SDL_GetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH)) {
  1223         return SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE);
  1224     } else {
  1225         return SDL_GetHintBoolean("SDL_MAC_MOUSE_FOCUS_CLICKTHROUGH", SDL_FALSE);
  1226     }
  1227 }
  1228 @end
  1229 
  1230 static int
  1231 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
  1232 { @autoreleasepool
  1233 {
  1234     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1235     SDL_WindowData *data;
  1236 
  1237     /* Allocate the window data */
  1238     window->driverdata = data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
  1239     if (!data) {
  1240         return SDL_OutOfMemory();
  1241     }
  1242     data->window = window;
  1243     data->nswindow = nswindow;
  1244     data->created = created;
  1245     data->videodata = videodata;
  1246     data->nscontexts = [[NSMutableArray alloc] init];
  1247 
  1248     /* Create an event listener for the window */
  1249     data->listener = [[Cocoa_WindowListener alloc] init];
  1250 
  1251     /* Fill in the SDL window with the window data */
  1252     {
  1253         NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1254         ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1255         window->x = (int)rect.origin.x;
  1256         window->y = (int)rect.origin.y;
  1257         window->w = (int)rect.size.width;
  1258         window->h = (int)rect.size.height;
  1259     }
  1260 
  1261     /* Set up the listener after we create the view */
  1262     [data->listener listen:data];
  1263 
  1264     if ([nswindow isVisible]) {
  1265         window->flags |= SDL_WINDOW_SHOWN;
  1266     } else {
  1267         window->flags &= ~SDL_WINDOW_SHOWN;
  1268     }
  1269 
  1270     {
  1271         unsigned long style = [nswindow styleMask];
  1272 
  1273         if (style == NSWindowStyleMaskBorderless) {
  1274             window->flags |= SDL_WINDOW_BORDERLESS;
  1275         } else {
  1276             window->flags &= ~SDL_WINDOW_BORDERLESS;
  1277         }
  1278         if (style & NSWindowStyleMaskResizable) {
  1279             window->flags |= SDL_WINDOW_RESIZABLE;
  1280         } else {
  1281             window->flags &= ~SDL_WINDOW_RESIZABLE;
  1282         }
  1283     }
  1284 
  1285     /* isZoomed always returns true if the window is not resizable */
  1286     if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1287         window->flags |= SDL_WINDOW_MAXIMIZED;
  1288     } else {
  1289         window->flags &= ~SDL_WINDOW_MAXIMIZED;
  1290     }
  1291 
  1292     if ([nswindow isMiniaturized]) {
  1293         window->flags |= SDL_WINDOW_MINIMIZED;
  1294     } else {
  1295         window->flags &= ~SDL_WINDOW_MINIMIZED;
  1296     }
  1297 
  1298     if ([nswindow isKeyWindow]) {
  1299         window->flags |= SDL_WINDOW_INPUT_FOCUS;
  1300         SDL_SetKeyboardFocus(data->window);
  1301     }
  1302 
  1303     /* Prevents the window's "window device" from being destroyed when it is
  1304      * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
  1305      */
  1306     [nswindow setOneShot:NO];
  1307 
  1308     /* All done! */
  1309     window->driverdata = data;
  1310     return 0;
  1311 }}
  1312 
  1313 int
  1314 Cocoa_CreateWindow(_THIS, SDL_Window * window)
  1315 { @autoreleasepool
  1316 {
  1317     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1318     NSWindow *nswindow;
  1319     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1320     NSRect rect;
  1321     SDL_Rect bounds;
  1322     NSUInteger style;
  1323     NSArray *screens = [NSScreen screens];
  1324 
  1325     Cocoa_GetDisplayBounds(_this, display, &bounds);
  1326     rect.origin.x = window->x;
  1327     rect.origin.y = window->y;
  1328     rect.size.width = window->w;
  1329     rect.size.height = window->h;
  1330     ConvertNSRect([screens objectAtIndex:0], (window->flags & FULLSCREEN_MASK), &rect);
  1331 
  1332     style = GetWindowStyle(window);
  1333 
  1334     /* Figure out which screen to place this window */
  1335     NSScreen *screen = nil;
  1336     for (NSScreen *candidate in screens) {
  1337         NSRect screenRect = [candidate frame];
  1338         if (rect.origin.x >= screenRect.origin.x &&
  1339             rect.origin.x < screenRect.origin.x + screenRect.size.width &&
  1340             rect.origin.y >= screenRect.origin.y &&
  1341             rect.origin.y < screenRect.origin.y + screenRect.size.height) {
  1342             screen = candidate;
  1343             rect.origin.x -= screenRect.origin.x;
  1344             rect.origin.y -= screenRect.origin.y;
  1345         }
  1346     }
  1347 
  1348     @try {
  1349         nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
  1350     }
  1351     @catch (NSException *e) {
  1352         return SDL_SetError("%s", [[e reason] UTF8String]);
  1353     }
  1354 
  1355     if (videodata->allow_spaces) {
  1356         SDL_assert(floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6);
  1357         SDL_assert([nswindow respondsToSelector:@selector(toggleFullScreen:)]);
  1358         /* we put FULLSCREEN_DESKTOP windows in their own Space, without a toggle button or menubar, later */
  1359         if (window->flags & SDL_WINDOW_RESIZABLE) {
  1360             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
  1361             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
  1362         }
  1363     }
  1364 
  1365     /* Create a default view for this window */
  1366     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1367     SDLView *contentView = [[SDLView alloc] initWithFrame:rect];
  1368     [contentView setSDLWindow:window];
  1369 
  1370     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
  1371         if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
  1372             [contentView setWantsBestResolutionOpenGLSurface:YES];
  1373         }
  1374     }
  1375 
  1376 #if SDL_VIDEO_OPENGL_ES2
  1377 #if SDL_VIDEO_OPENGL_EGL
  1378     if ((window->flags & SDL_WINDOW_OPENGL) &&
  1379         _this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
  1380         [contentView setWantsLayer:TRUE];
  1381     }
  1382 #endif /* SDL_VIDEO_OPENGL_EGL */
  1383 #endif /* SDL_VIDEO_OPENGL_ES2 */
  1384     [nswindow setContentView:contentView];
  1385     [contentView release];
  1386 
  1387     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
  1388         [nswindow release];
  1389         return -1;
  1390     }
  1391 
  1392     if (!(window->flags & SDL_WINDOW_OPENGL)) {
  1393         return 0;
  1394     }
  1395     
  1396     /* The rest of this macro mess is for OpenGL or OpenGL ES windows */
  1397 #if SDL_VIDEO_OPENGL_ES2
  1398     if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
  1399 #if SDL_VIDEO_OPENGL_EGL
  1400         if (Cocoa_GLES_SetupWindow(_this, window) < 0) {
  1401             Cocoa_DestroyWindow(_this, window);
  1402             return -1;
  1403         }
  1404         return 0;
  1405 #else
  1406         return SDL_SetError("Could not create GLES window surface (EGL support not configured)");
  1407 #endif /* SDL_VIDEO_OPENGL_EGL */
  1408     }
  1409 #endif /* SDL_VIDEO_OPENGL_ES2 */
  1410     return 0;
  1411 }}
  1412 
  1413 int
  1414 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
  1415 { @autoreleasepool
  1416 {
  1417     NSWindow *nswindow = (NSWindow *) data;
  1418     NSString *title;
  1419 
  1420     /* Query the title from the existing window */
  1421     title = [nswindow title];
  1422     if (title) {
  1423         window->title = SDL_strdup([title UTF8String]);
  1424     }
  1425 
  1426     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
  1427 }}
  1428 
  1429 void
  1430 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
  1431 { @autoreleasepool
  1432 {
  1433     const char *title = window->title ? window->title : "";
  1434     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1435     NSString *string = [[NSString alloc] initWithUTF8String:title];
  1436     [nswindow setTitle:string];
  1437     [string release];
  1438 }}
  1439 
  1440 void
  1441 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
  1442 { @autoreleasepool
  1443 {
  1444     NSImage *nsimage = Cocoa_CreateImage(icon);
  1445 
  1446     if (nsimage) {
  1447         [NSApp setApplicationIconImage:nsimage];
  1448     }
  1449 }}
  1450 
  1451 void
  1452 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
  1453 { @autoreleasepool
  1454 {
  1455     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1456     NSWindow *nswindow = windata->nswindow;
  1457     NSRect rect;
  1458     Uint32 moveHack;
  1459 
  1460     rect.origin.x = window->x;
  1461     rect.origin.y = window->y;
  1462     rect.size.width = window->w;
  1463     rect.size.height = window->h;
  1464     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1465 
  1466     moveHack = s_moveHack;
  1467     s_moveHack = 0;
  1468     [nswindow setFrameOrigin:rect.origin];
  1469     s_moveHack = moveHack;
  1470 
  1471     ScheduleContextUpdates(windata);
  1472 }}
  1473 
  1474 void
  1475 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
  1476 { @autoreleasepool
  1477 {
  1478     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1479     NSWindow *nswindow = windata->nswindow;
  1480     NSRect rect;
  1481     Uint32 moveHack;
  1482 
  1483     /* Cocoa will resize the window from the bottom-left rather than the
  1484      * top-left when -[nswindow setContentSize:] is used, so we must set the
  1485      * entire frame based on the new size, in order to preserve the position.
  1486      */
  1487     rect.origin.x = window->x;
  1488     rect.origin.y = window->y;
  1489     rect.size.width = window->w;
  1490     rect.size.height = window->h;
  1491     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1492 
  1493     moveHack = s_moveHack;
  1494     s_moveHack = 0;
  1495     [nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES];
  1496     s_moveHack = moveHack;
  1497 
  1498     ScheduleContextUpdates(windata);
  1499 }}
  1500 
  1501 void
  1502 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
  1503 { @autoreleasepool
  1504 {
  1505     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1506 
  1507     NSSize minSize;
  1508     minSize.width = window->min_w;
  1509     minSize.height = window->min_h;
  1510 
  1511     [windata->nswindow setContentMinSize:minSize];
  1512 }}
  1513 
  1514 void
  1515 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
  1516 { @autoreleasepool
  1517 {
  1518     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1519 
  1520     NSSize maxSize;
  1521     maxSize.width = window->max_w;
  1522     maxSize.height = window->max_h;
  1523 
  1524     [windata->nswindow setContentMaxSize:maxSize];
  1525 }}
  1526 
  1527 void
  1528 Cocoa_ShowWindow(_THIS, SDL_Window * window)
  1529 { @autoreleasepool
  1530 {
  1531     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1532     NSWindow *nswindow = windowData->nswindow;
  1533 
  1534     if (![nswindow isMiniaturized]) {
  1535         [windowData->listener pauseVisibleObservation];
  1536         [nswindow makeKeyAndOrderFront:nil];
  1537         [windowData->listener resumeVisibleObservation];
  1538     }
  1539 }}
  1540 
  1541 void
  1542 Cocoa_HideWindow(_THIS, SDL_Window * window)
  1543 { @autoreleasepool
  1544 {
  1545     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1546 
  1547     [nswindow orderOut:nil];
  1548 }}
  1549 
  1550 void
  1551 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
  1552 { @autoreleasepool
  1553 {
  1554     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1555     NSWindow *nswindow = windowData->nswindow;
  1556 
  1557     /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
  1558        a minimized or hidden window, so check for that before showing it.
  1559      */
  1560     [windowData->listener pauseVisibleObservation];
  1561     if (![nswindow isMiniaturized] && [nswindow isVisible]) {
  1562         [NSApp activateIgnoringOtherApps:YES];
  1563         [nswindow makeKeyAndOrderFront:nil];
  1564     }
  1565     [windowData->listener resumeVisibleObservation];
  1566 }}
  1567 
  1568 void
  1569 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
  1570 { @autoreleasepool
  1571 {
  1572     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1573     NSWindow *nswindow = windata->nswindow;
  1574 
  1575     [nswindow zoom:nil];
  1576 
  1577     ScheduleContextUpdates(windata);
  1578 }}
  1579 
  1580 void
  1581 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
  1582 { @autoreleasepool
  1583 {
  1584     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1585     NSWindow *nswindow = data->nswindow;
  1586 
  1587     if ([data->listener isInFullscreenSpaceTransition]) {
  1588         [data->listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
  1589     } else {
  1590         [nswindow miniaturize:nil];
  1591     }
  1592 }}
  1593 
  1594 void
  1595 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
  1596 { @autoreleasepool
  1597 {
  1598     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1599 
  1600     if ([nswindow isMiniaturized]) {
  1601         [nswindow deminiaturize:nil];
  1602     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1603         [nswindow zoom:nil];
  1604     }
  1605 }}
  1606 
  1607 void
  1608 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
  1609 { @autoreleasepool
  1610 {
  1611     if (SetWindowStyle(window, GetWindowStyle(window))) {
  1612         if (bordered) {
  1613             Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
  1614         }
  1615     }
  1616 }}
  1617 
  1618 void
  1619 Cocoa_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable)
  1620 { @autoreleasepool
  1621 {
  1622     /* Don't set this if we're in a space!
  1623      * The window will get permanently stuck if resizable is false.
  1624      * -flibit
  1625      */
  1626     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1627     Cocoa_WindowListener *listener = data->listener;
  1628     if (![listener isInFullscreenSpace]) {
  1629         SetWindowStyle(window, GetWindowStyle(window));
  1630     }
  1631 }}
  1632 
  1633 void
  1634 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
  1635 { @autoreleasepool
  1636 {
  1637     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1638     NSWindow *nswindow = data->nswindow;
  1639     NSRect rect;
  1640 
  1641     /* The view responder chain gets messed with during setStyleMask */
  1642     if ([[nswindow contentView] nextResponder] == data->listener) {
  1643         [[nswindow contentView] setNextResponder:nil];
  1644     }
  1645 
  1646     if (fullscreen) {
  1647         SDL_Rect bounds;
  1648 
  1649         Cocoa_GetDisplayBounds(_this, display, &bounds);
  1650         rect.origin.x = bounds.x;
  1651         rect.origin.y = bounds.y;
  1652         rect.size.width = bounds.w;
  1653         rect.size.height = bounds.h;
  1654         ConvertNSRect([nswindow screen], fullscreen, &rect);
  1655 
  1656         /* Hack to fix origin on Mac OS X 10.4 */
  1657         NSRect screenRect = [[nswindow screen] frame];
  1658         if (screenRect.size.height >= 1.0f) {
  1659             rect.origin.y += (screenRect.size.height - rect.size.height);
  1660         }
  1661 
  1662         [nswindow setStyleMask:NSWindowStyleMaskBorderless];
  1663     } else {
  1664         rect.origin.x = window->windowed.x;
  1665         rect.origin.y = window->windowed.y;
  1666         rect.size.width = window->windowed.w;
  1667         rect.size.height = window->windowed.h;
  1668         ConvertNSRect([nswindow screen], fullscreen, &rect);
  1669 
  1670         [nswindow setStyleMask:GetWindowStyle(window)];
  1671 
  1672         /* Hack to restore window decorations on Mac OS X 10.10 */
  1673         NSRect frameRect = [nswindow frame];
  1674         [nswindow setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
  1675         [nswindow setFrame:frameRect display:NO];
  1676     }
  1677 
  1678     /* The view responder chain gets messed with during setStyleMask */
  1679     if ([[nswindow contentView] nextResponder] != data->listener) {
  1680         [[nswindow contentView] setNextResponder:data->listener];
  1681     }
  1682 
  1683     s_moveHack = 0;
  1684     [nswindow setContentSize:rect.size];
  1685     [nswindow setFrameOrigin:rect.origin];
  1686     s_moveHack = SDL_GetTicks();
  1687 
  1688     /* When the window style changes the title is cleared */
  1689     if (!fullscreen) {
  1690         Cocoa_SetWindowTitle(_this, window);
  1691     }
  1692 
  1693     if (SDL_ShouldAllowTopmost() && fullscreen) {
  1694         /* OpenGL is rendering to the window, so make it visible! */
  1695         [nswindow setLevel:CGShieldingWindowLevel()];
  1696     } else {
  1697         [nswindow setLevel:kCGNormalWindowLevel];
  1698     }
  1699 
  1700     if ([nswindow isVisible] || fullscreen) {
  1701         [data->listener pauseVisibleObservation];
  1702         [nswindow makeKeyAndOrderFront:nil];
  1703         [data->listener resumeVisibleObservation];
  1704     }
  1705 
  1706     ScheduleContextUpdates(data);
  1707 }}
  1708 
  1709 int
  1710 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1711 {
  1712     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1713     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1714     const uint32_t tableSize = 256;
  1715     CGGammaValue redTable[tableSize];
  1716     CGGammaValue greenTable[tableSize];
  1717     CGGammaValue blueTable[tableSize];
  1718     uint32_t i;
  1719     float inv65535 = 1.0f / 65535.0f;
  1720 
  1721     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1722     for (i = 0; i < 256; i++) {
  1723         redTable[i] = ramp[0*256+i] * inv65535;
  1724         greenTable[i] = ramp[1*256+i] * inv65535;
  1725         blueTable[i] = ramp[2*256+i] * inv65535;
  1726     }
  1727 
  1728     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1729                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1730         return SDL_SetError("CGSetDisplayTransferByTable()");
  1731     }
  1732     return 0;
  1733 }
  1734 
  1735 int
  1736 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1737 {
  1738     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1739     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1740     const uint32_t tableSize = 256;
  1741     CGGammaValue redTable[tableSize];
  1742     CGGammaValue greenTable[tableSize];
  1743     CGGammaValue blueTable[tableSize];
  1744     uint32_t i, tableCopied;
  1745 
  1746     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1747                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1748         return SDL_SetError("CGGetDisplayTransferByTable()");
  1749     }
  1750 
  1751     for (i = 0; i < tableCopied; i++) {
  1752         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1753         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1754         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1755     }
  1756     return 0;
  1757 }
  1758 
  1759 void
  1760 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1761 {
  1762     SDL_Mouse *mouse = SDL_GetMouse();
  1763     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1764 
  1765     /* Enable or disable the event tap as necessary */
  1766     Cocoa_EnableMouseEventTap(mouse->driverdata, grabbed);
  1767 
  1768     /* Move the cursor to the nearest point in the window */
  1769     if (grabbed && data && ![data->listener isMoving]) {
  1770         int x, y;
  1771         CGPoint cgpoint;
  1772 
  1773         SDL_GetMouseState(&x, &y);
  1774         cgpoint.x = window->x + x;
  1775         cgpoint.y = window->y + y;
  1776 
  1777         Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
  1778 
  1779         DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
  1780         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1781     }
  1782 
  1783     if ( data && (window->flags & SDL_WINDOW_FULLSCREEN) ) {
  1784         if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)
  1785             && ![data->listener isInFullscreenSpace]) {
  1786             /* OpenGL is rendering to the window, so make it visible! */
  1787             /* Doing this in 10.11 while in a Space breaks things (bug #3152) */
  1788             [data->nswindow setLevel:CGShieldingWindowLevel()];
  1789         } else {
  1790             [data->nswindow setLevel:kCGNormalWindowLevel];
  1791         }
  1792     }
  1793 }
  1794 
  1795 void
  1796 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1797 { @autoreleasepool
  1798 {
  1799     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1800 
  1801     if (data) {
  1802         if ([data->listener isInFullscreenSpace]) {
  1803             [NSMenu setMenuBarVisible:YES];
  1804         }
  1805         [data->listener close];
  1806         [data->listener release];
  1807         if (data->created) {
  1808             [data->nswindow close];
  1809         }
  1810 
  1811         NSArray *contexts = [[data->nscontexts copy] autorelease];
  1812         for (SDLOpenGLContext *context in contexts) {
  1813             /* Calling setWindow:NULL causes the context to remove itself from the context list. */            
  1814             [context setWindow:NULL];
  1815         }
  1816         [data->nscontexts release];
  1817 
  1818         SDL_free(data);
  1819     }
  1820     window->driverdata = NULL;
  1821 }}
  1822 
  1823 SDL_bool
  1824 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1825 {
  1826     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1827 
  1828     if (info->version.major <= SDL_MAJOR_VERSION) {
  1829         info->subsystem = SDL_SYSWM_COCOA;
  1830         info->info.cocoa.window = nswindow;
  1831         return SDL_TRUE;
  1832     } else {
  1833         SDL_SetError("Application not compiled with SDL %d.%d",
  1834                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1835         return SDL_FALSE;
  1836     }
  1837 }
  1838 
  1839 SDL_bool
  1840 Cocoa_IsWindowInFullscreenSpace(SDL_Window * window)
  1841 {
  1842     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1843 
  1844     if ([data->listener isInFullscreenSpace]) {
  1845         return SDL_TRUE;
  1846     } else {
  1847         return SDL_FALSE;
  1848     }
  1849 }
  1850 
  1851 SDL_bool
  1852 Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
  1853 { @autoreleasepool
  1854 {
  1855     SDL_bool succeeded = SDL_FALSE;
  1856     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1857 
  1858     if ([data->listener setFullscreenSpace:(state ? YES : NO)]) {
  1859         const int maxattempts = 3;
  1860         int attempt = 0;
  1861         while (++attempt <= maxattempts) {
  1862             /* Wait for the transition to complete, so application changes
  1863              take effect properly (e.g. setting the window size, etc.)
  1864              */
  1865             const int limit = 10000;
  1866             int count = 0;
  1867             while ([data->listener isInFullscreenSpaceTransition]) {
  1868                 if ( ++count == limit ) {
  1869                     /* Uh oh, transition isn't completing. Should we assert? */
  1870                     break;
  1871                 }
  1872                 SDL_Delay(1);
  1873                 SDL_PumpEvents();
  1874             }
  1875             if ([data->listener isInFullscreenSpace] == (state ? YES : NO))
  1876                 break;
  1877             /* Try again, the last attempt was interrupted by user gestures */
  1878             if (![data->listener setFullscreenSpace:(state ? YES : NO)])
  1879                 break; /* ??? */
  1880         }
  1881         /* Return TRUE to prevent non-space fullscreen logic from running */
  1882         succeeded = SDL_TRUE;
  1883     }
  1884 
  1885     return succeeded;
  1886 }}
  1887 
  1888 int
  1889 Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
  1890 {
  1891     return 0;  /* just succeed, the real work is done elsewhere. */
  1892 }
  1893 
  1894 void
  1895 Cocoa_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
  1896 {
  1897     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1898     if (accept) {
  1899         [data->nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
  1900     } else {
  1901         [data->nswindow unregisterDraggedTypes];
  1902     }
  1903 }
  1904 
  1905 int
  1906 Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity)
  1907 {
  1908     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1909     [data->nswindow setAlphaValue:opacity];
  1910     return 0;
  1911 }
  1912 
  1913 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1914 
  1915 /* vi: set ts=4 sw=4 expandtab: */