src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Thu, 18 Oct 2018 12:05:05 -0400
branchSDL-ryan-batching-renderer
changeset 12351 3c003852825a
parent 12261 4e77fde5bbc7
permissions -rw-r--r--
cocoa: Fix OpenGL rendering on macOS 10.14 ("Mojave").

Fixes Bugzilla #4272.
(transplanted from 86dcfbbcacaf0c4a556501644af11b7f99b4352d)
     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         if (SDL_AddTouch(touchId, "") < 0) {
  1107             return;
  1108         }
  1109 
  1110         const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
  1111         float x = [touch normalizedPosition].x;
  1112         float y = [touch normalizedPosition].y;
  1113         /* Make the origin the upper left instead of the lower left */
  1114         y = 1.0f - y;
  1115 
  1116         switch (phase) {
  1117         case NSTouchPhaseBegan:
  1118             SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
  1119             break;
  1120         case NSTouchPhaseEnded:
  1121         case NSTouchPhaseCancelled:
  1122             SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
  1123             break;
  1124         case NSTouchPhaseMoved:
  1125             SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
  1126             break;
  1127         default:
  1128             break;
  1129         }
  1130     }
  1131 }
  1132 
  1133 @end
  1134 
  1135 @interface SDLView : NSView {
  1136     SDL_Window *_sdlWindow;
  1137 }
  1138 
  1139 - (void)setSDLWindow:(SDL_Window*)window;
  1140 
  1141 /* The default implementation doesn't pass rightMouseDown to responder chain */
  1142 - (void)rightMouseDown:(NSEvent *)theEvent;
  1143 - (BOOL)mouseDownCanMoveWindow;
  1144 - (void)drawRect:(NSRect)dirtyRect;
  1145 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent;
  1146 - (BOOL)wantsUpdateLayer;
  1147 - (void)updateLayer;
  1148 @end
  1149 
  1150 @implementation SDLView
  1151 
  1152 - (void)setSDLWindow:(SDL_Window*)window
  1153 {
  1154     _sdlWindow = window;
  1155 }
  1156 
  1157 /* this is used on older macOS revisions. 10.8 and later use updateLayer. */
  1158 - (void)drawRect:(NSRect)dirtyRect
  1159 {
  1160     /* Force the graphics context to clear to black so we don't get a flash of
  1161        white until the app is ready to draw. In practice on modern macOS, this
  1162        only gets called for window creation and other extraordinary events. */
  1163     [[NSColor blackColor] setFill];
  1164     NSRectFill(dirtyRect);
  1165     SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0);
  1166 }
  1167 
  1168 -(BOOL) wantsUpdateLayer
  1169 {
  1170     return YES;
  1171 }
  1172 
  1173 -(void) updateLayer
  1174 {
  1175     /* Force the graphics context to clear to black so we don't get a flash of
  1176        white until the app is ready to draw. In practice on modern macOS, this
  1177        only gets called for window creation and other extraordinary events. */
  1178     self.layer.backgroundColor = NSColor.blackColor.CGColor;
  1179     ScheduleContextUpdates((SDL_WindowData *) _sdlWindow->driverdata);
  1180     SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0);
  1181 }
  1182 
  1183 - (void)rightMouseDown:(NSEvent *)theEvent
  1184 {
  1185     [[self nextResponder] rightMouseDown:theEvent];
  1186 }
  1187 
  1188 - (BOOL)mouseDownCanMoveWindow
  1189 {
  1190     /* Always say YES, but this doesn't do anything until we call
  1191        -[NSWindow setMovableByWindowBackground:YES], which we ninja-toggle
  1192        during mouse events when we're using a drag area. */
  1193     return YES;
  1194 }
  1195 
  1196 - (void)resetCursorRects
  1197 {
  1198     [super resetCursorRects];
  1199     SDL_Mouse *mouse = SDL_GetMouse();
  1200 
  1201     if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
  1202         [self addCursorRect:[self bounds]
  1203                      cursor:mouse->cur_cursor->driverdata];
  1204     } else {
  1205         [self addCursorRect:[self bounds]
  1206                      cursor:[NSCursor invisibleCursor]];
  1207     }
  1208 }
  1209 
  1210 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
  1211 {
  1212     if (SDL_GetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH)) {
  1213         return SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, SDL_FALSE);
  1214     } else {
  1215         return SDL_GetHintBoolean("SDL_MAC_MOUSE_FOCUS_CLICKTHROUGH", SDL_FALSE);
  1216     }
  1217 }
  1218 @end
  1219 
  1220 static int
  1221 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
  1222 { @autoreleasepool
  1223 {
  1224     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1225     SDL_WindowData *data;
  1226 
  1227     /* Allocate the window data */
  1228     window->driverdata = data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
  1229     if (!data) {
  1230         return SDL_OutOfMemory();
  1231     }
  1232     data->window = window;
  1233     data->nswindow = nswindow;
  1234     data->created = created;
  1235     data->videodata = videodata;
  1236     data->nscontexts = [[NSMutableArray alloc] init];
  1237 
  1238     /* Create an event listener for the window */
  1239     data->listener = [[Cocoa_WindowListener alloc] init];
  1240 
  1241     /* Fill in the SDL window with the window data */
  1242     {
  1243         NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1244         ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1245         window->x = (int)rect.origin.x;
  1246         window->y = (int)rect.origin.y;
  1247         window->w = (int)rect.size.width;
  1248         window->h = (int)rect.size.height;
  1249     }
  1250 
  1251     /* Set up the listener after we create the view */
  1252     [data->listener listen:data];
  1253 
  1254     if ([nswindow isVisible]) {
  1255         window->flags |= SDL_WINDOW_SHOWN;
  1256     } else {
  1257         window->flags &= ~SDL_WINDOW_SHOWN;
  1258     }
  1259 
  1260     {
  1261         unsigned long style = [nswindow styleMask];
  1262 
  1263         if (style == NSWindowStyleMaskBorderless) {
  1264             window->flags |= SDL_WINDOW_BORDERLESS;
  1265         } else {
  1266             window->flags &= ~SDL_WINDOW_BORDERLESS;
  1267         }
  1268         if (style & NSWindowStyleMaskResizable) {
  1269             window->flags |= SDL_WINDOW_RESIZABLE;
  1270         } else {
  1271             window->flags &= ~SDL_WINDOW_RESIZABLE;
  1272         }
  1273     }
  1274 
  1275     /* isZoomed always returns true if the window is not resizable */
  1276     if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1277         window->flags |= SDL_WINDOW_MAXIMIZED;
  1278     } else {
  1279         window->flags &= ~SDL_WINDOW_MAXIMIZED;
  1280     }
  1281 
  1282     if ([nswindow isMiniaturized]) {
  1283         window->flags |= SDL_WINDOW_MINIMIZED;
  1284     } else {
  1285         window->flags &= ~SDL_WINDOW_MINIMIZED;
  1286     }
  1287 
  1288     if ([nswindow isKeyWindow]) {
  1289         window->flags |= SDL_WINDOW_INPUT_FOCUS;
  1290         SDL_SetKeyboardFocus(data->window);
  1291     }
  1292 
  1293     /* Prevents the window's "window device" from being destroyed when it is
  1294      * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
  1295      */
  1296     [nswindow setOneShot:NO];
  1297 
  1298     /* All done! */
  1299     window->driverdata = data;
  1300     return 0;
  1301 }}
  1302 
  1303 int
  1304 Cocoa_CreateWindow(_THIS, SDL_Window * window)
  1305 { @autoreleasepool
  1306 {
  1307     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1308     NSWindow *nswindow;
  1309     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1310     NSRect rect;
  1311     SDL_Rect bounds;
  1312     NSUInteger style;
  1313     NSArray *screens = [NSScreen screens];
  1314 
  1315     Cocoa_GetDisplayBounds(_this, display, &bounds);
  1316     rect.origin.x = window->x;
  1317     rect.origin.y = window->y;
  1318     rect.size.width = window->w;
  1319     rect.size.height = window->h;
  1320     ConvertNSRect([screens objectAtIndex:0], (window->flags & FULLSCREEN_MASK), &rect);
  1321 
  1322     style = GetWindowStyle(window);
  1323 
  1324     /* Figure out which screen to place this window */
  1325     NSScreen *screen = nil;
  1326     for (NSScreen *candidate in screens) {
  1327         NSRect screenRect = [candidate frame];
  1328         if (rect.origin.x >= screenRect.origin.x &&
  1329             rect.origin.x < screenRect.origin.x + screenRect.size.width &&
  1330             rect.origin.y >= screenRect.origin.y &&
  1331             rect.origin.y < screenRect.origin.y + screenRect.size.height) {
  1332             screen = candidate;
  1333             rect.origin.x -= screenRect.origin.x;
  1334             rect.origin.y -= screenRect.origin.y;
  1335         }
  1336     }
  1337 
  1338     @try {
  1339         nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
  1340     }
  1341     @catch (NSException *e) {
  1342         return SDL_SetError("%s", [[e reason] UTF8String]);
  1343     }
  1344 
  1345     if (videodata->allow_spaces) {
  1346         SDL_assert(floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6);
  1347         SDL_assert([nswindow respondsToSelector:@selector(toggleFullScreen:)]);
  1348         /* we put FULLSCREEN_DESKTOP windows in their own Space, without a toggle button or menubar, later */
  1349         if (window->flags & SDL_WINDOW_RESIZABLE) {
  1350             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
  1351             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
  1352         }
  1353     }
  1354 
  1355     /* Create a default view for this window */
  1356     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1357     SDLView *contentView = [[SDLView alloc] initWithFrame:rect];
  1358     [contentView setSDLWindow:window];
  1359 
  1360     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
  1361         if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
  1362             [contentView setWantsBestResolutionOpenGLSurface:YES];
  1363         }
  1364     }
  1365 
  1366 #if SDL_VIDEO_OPENGL_ES2
  1367 #if SDL_VIDEO_OPENGL_EGL
  1368     if ((window->flags & SDL_WINDOW_OPENGL) &&
  1369         _this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
  1370         [contentView setWantsLayer:TRUE];
  1371     }
  1372 #endif /* SDL_VIDEO_OPENGL_EGL */
  1373 #endif /* SDL_VIDEO_OPENGL_ES2 */
  1374     [nswindow setContentView:contentView];
  1375     [contentView release];
  1376 
  1377     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
  1378         [nswindow release];
  1379         return -1;
  1380     }
  1381 
  1382     if (!(window->flags & SDL_WINDOW_OPENGL)) {
  1383         return 0;
  1384     }
  1385     
  1386     /* The rest of this macro mess is for OpenGL or OpenGL ES windows */
  1387 #if SDL_VIDEO_OPENGL_ES2
  1388     if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
  1389 #if SDL_VIDEO_OPENGL_EGL
  1390         if (Cocoa_GLES_SetupWindow(_this, window) < 0) {
  1391             Cocoa_DestroyWindow(_this, window);
  1392             return -1;
  1393         }
  1394         return 0;
  1395 #else
  1396         return SDL_SetError("Could not create GLES window surface (EGL support not configured)");
  1397 #endif /* SDL_VIDEO_OPENGL_EGL */
  1398     }
  1399 #endif /* SDL_VIDEO_OPENGL_ES2 */
  1400     return 0;
  1401 }}
  1402 
  1403 int
  1404 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
  1405 { @autoreleasepool
  1406 {
  1407     NSWindow *nswindow = (NSWindow *) data;
  1408     NSString *title;
  1409 
  1410     /* Query the title from the existing window */
  1411     title = [nswindow title];
  1412     if (title) {
  1413         window->title = SDL_strdup([title UTF8String]);
  1414     }
  1415 
  1416     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
  1417 }}
  1418 
  1419 void
  1420 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
  1421 { @autoreleasepool
  1422 {
  1423     const char *title = window->title ? window->title : "";
  1424     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1425     NSString *string = [[NSString alloc] initWithUTF8String:title];
  1426     [nswindow setTitle:string];
  1427     [string release];
  1428 }}
  1429 
  1430 void
  1431 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
  1432 { @autoreleasepool
  1433 {
  1434     NSImage *nsimage = Cocoa_CreateImage(icon);
  1435 
  1436     if (nsimage) {
  1437         [NSApp setApplicationIconImage:nsimage];
  1438     }
  1439 }}
  1440 
  1441 void
  1442 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
  1443 { @autoreleasepool
  1444 {
  1445     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1446     NSWindow *nswindow = windata->nswindow;
  1447     NSRect rect;
  1448     Uint32 moveHack;
  1449 
  1450     rect.origin.x = window->x;
  1451     rect.origin.y = window->y;
  1452     rect.size.width = window->w;
  1453     rect.size.height = window->h;
  1454     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1455 
  1456     moveHack = s_moveHack;
  1457     s_moveHack = 0;
  1458     [nswindow setFrameOrigin:rect.origin];
  1459     s_moveHack = moveHack;
  1460 
  1461     ScheduleContextUpdates(windata);
  1462 }}
  1463 
  1464 void
  1465 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
  1466 { @autoreleasepool
  1467 {
  1468     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1469     NSWindow *nswindow = windata->nswindow;
  1470     NSRect rect;
  1471     Uint32 moveHack;
  1472 
  1473     /* Cocoa will resize the window from the bottom-left rather than the
  1474      * top-left when -[nswindow setContentSize:] is used, so we must set the
  1475      * entire frame based on the new size, in order to preserve the position.
  1476      */
  1477     rect.origin.x = window->x;
  1478     rect.origin.y = window->y;
  1479     rect.size.width = window->w;
  1480     rect.size.height = window->h;
  1481     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1482 
  1483     moveHack = s_moveHack;
  1484     s_moveHack = 0;
  1485     [nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES];
  1486     s_moveHack = moveHack;
  1487 
  1488     ScheduleContextUpdates(windata);
  1489 }}
  1490 
  1491 void
  1492 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
  1493 { @autoreleasepool
  1494 {
  1495     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1496 
  1497     NSSize minSize;
  1498     minSize.width = window->min_w;
  1499     minSize.height = window->min_h;
  1500 
  1501     [windata->nswindow setContentMinSize:minSize];
  1502 }}
  1503 
  1504 void
  1505 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
  1506 { @autoreleasepool
  1507 {
  1508     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1509 
  1510     NSSize maxSize;
  1511     maxSize.width = window->max_w;
  1512     maxSize.height = window->max_h;
  1513 
  1514     [windata->nswindow setContentMaxSize:maxSize];
  1515 }}
  1516 
  1517 void
  1518 Cocoa_ShowWindow(_THIS, SDL_Window * window)
  1519 { @autoreleasepool
  1520 {
  1521     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1522     NSWindow *nswindow = windowData->nswindow;
  1523 
  1524     if (![nswindow isMiniaturized]) {
  1525         [windowData->listener pauseVisibleObservation];
  1526         [nswindow makeKeyAndOrderFront:nil];
  1527         [windowData->listener resumeVisibleObservation];
  1528     }
  1529 }}
  1530 
  1531 void
  1532 Cocoa_HideWindow(_THIS, SDL_Window * window)
  1533 { @autoreleasepool
  1534 {
  1535     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1536 
  1537     [nswindow orderOut:nil];
  1538 }}
  1539 
  1540 void
  1541 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
  1542 { @autoreleasepool
  1543 {
  1544     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1545     NSWindow *nswindow = windowData->nswindow;
  1546 
  1547     /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
  1548        a minimized or hidden window, so check for that before showing it.
  1549      */
  1550     [windowData->listener pauseVisibleObservation];
  1551     if (![nswindow isMiniaturized] && [nswindow isVisible]) {
  1552         [NSApp activateIgnoringOtherApps:YES];
  1553         [nswindow makeKeyAndOrderFront:nil];
  1554     }
  1555     [windowData->listener resumeVisibleObservation];
  1556 }}
  1557 
  1558 void
  1559 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
  1560 { @autoreleasepool
  1561 {
  1562     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1563     NSWindow *nswindow = windata->nswindow;
  1564 
  1565     [nswindow zoom:nil];
  1566 
  1567     ScheduleContextUpdates(windata);
  1568 }}
  1569 
  1570 void
  1571 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
  1572 { @autoreleasepool
  1573 {
  1574     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1575     NSWindow *nswindow = data->nswindow;
  1576 
  1577     if ([data->listener isInFullscreenSpaceTransition]) {
  1578         [data->listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
  1579     } else {
  1580         [nswindow miniaturize:nil];
  1581     }
  1582 }}
  1583 
  1584 void
  1585 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
  1586 { @autoreleasepool
  1587 {
  1588     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1589 
  1590     if ([nswindow isMiniaturized]) {
  1591         [nswindow deminiaturize:nil];
  1592     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1593         [nswindow zoom:nil];
  1594     }
  1595 }}
  1596 
  1597 void
  1598 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
  1599 { @autoreleasepool
  1600 {
  1601     if (SetWindowStyle(window, GetWindowStyle(window))) {
  1602         if (bordered) {
  1603             Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
  1604         }
  1605     }
  1606 }}
  1607 
  1608 void
  1609 Cocoa_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable)
  1610 { @autoreleasepool
  1611 {
  1612     /* Don't set this if we're in a space!
  1613      * The window will get permanently stuck if resizable is false.
  1614      * -flibit
  1615      */
  1616     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1617     Cocoa_WindowListener *listener = data->listener;
  1618     if (![listener isInFullscreenSpace]) {
  1619         SetWindowStyle(window, GetWindowStyle(window));
  1620     }
  1621 }}
  1622 
  1623 void
  1624 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
  1625 { @autoreleasepool
  1626 {
  1627     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1628     NSWindow *nswindow = data->nswindow;
  1629     NSRect rect;
  1630 
  1631     /* The view responder chain gets messed with during setStyleMask */
  1632     if ([[nswindow contentView] nextResponder] == data->listener) {
  1633         [[nswindow contentView] setNextResponder:nil];
  1634     }
  1635 
  1636     if (fullscreen) {
  1637         SDL_Rect bounds;
  1638 
  1639         Cocoa_GetDisplayBounds(_this, display, &bounds);
  1640         rect.origin.x = bounds.x;
  1641         rect.origin.y = bounds.y;
  1642         rect.size.width = bounds.w;
  1643         rect.size.height = bounds.h;
  1644         ConvertNSRect([nswindow screen], fullscreen, &rect);
  1645 
  1646         /* Hack to fix origin on Mac OS X 10.4 */
  1647         NSRect screenRect = [[nswindow screen] frame];
  1648         if (screenRect.size.height >= 1.0f) {
  1649             rect.origin.y += (screenRect.size.height - rect.size.height);
  1650         }
  1651 
  1652         [nswindow setStyleMask:NSWindowStyleMaskBorderless];
  1653     } else {
  1654         rect.origin.x = window->windowed.x;
  1655         rect.origin.y = window->windowed.y;
  1656         rect.size.width = window->windowed.w;
  1657         rect.size.height = window->windowed.h;
  1658         ConvertNSRect([nswindow screen], fullscreen, &rect);
  1659 
  1660         [nswindow setStyleMask:GetWindowStyle(window)];
  1661 
  1662         /* Hack to restore window decorations on Mac OS X 10.10 */
  1663         NSRect frameRect = [nswindow frame];
  1664         [nswindow setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
  1665         [nswindow setFrame:frameRect display:NO];
  1666     }
  1667 
  1668     /* The view responder chain gets messed with during setStyleMask */
  1669     if ([[nswindow contentView] nextResponder] != data->listener) {
  1670         [[nswindow contentView] setNextResponder:data->listener];
  1671     }
  1672 
  1673     s_moveHack = 0;
  1674     [nswindow setContentSize:rect.size];
  1675     [nswindow setFrameOrigin:rect.origin];
  1676     s_moveHack = SDL_GetTicks();
  1677 
  1678     /* When the window style changes the title is cleared */
  1679     if (!fullscreen) {
  1680         Cocoa_SetWindowTitle(_this, window);
  1681     }
  1682 
  1683     if (SDL_ShouldAllowTopmost() && fullscreen) {
  1684         /* OpenGL is rendering to the window, so make it visible! */
  1685         [nswindow setLevel:CGShieldingWindowLevel()];
  1686     } else {
  1687         [nswindow setLevel:kCGNormalWindowLevel];
  1688     }
  1689 
  1690     if ([nswindow isVisible] || fullscreen) {
  1691         [data->listener pauseVisibleObservation];
  1692         [nswindow makeKeyAndOrderFront:nil];
  1693         [data->listener resumeVisibleObservation];
  1694     }
  1695 
  1696     ScheduleContextUpdates(data);
  1697 }}
  1698 
  1699 int
  1700 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1701 {
  1702     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1703     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1704     const uint32_t tableSize = 256;
  1705     CGGammaValue redTable[tableSize];
  1706     CGGammaValue greenTable[tableSize];
  1707     CGGammaValue blueTable[tableSize];
  1708     uint32_t i;
  1709     float inv65535 = 1.0f / 65535.0f;
  1710 
  1711     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1712     for (i = 0; i < 256; i++) {
  1713         redTable[i] = ramp[0*256+i] * inv65535;
  1714         greenTable[i] = ramp[1*256+i] * inv65535;
  1715         blueTable[i] = ramp[2*256+i] * inv65535;
  1716     }
  1717 
  1718     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1719                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1720         return SDL_SetError("CGSetDisplayTransferByTable()");
  1721     }
  1722     return 0;
  1723 }
  1724 
  1725 int
  1726 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1727 {
  1728     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1729     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1730     const uint32_t tableSize = 256;
  1731     CGGammaValue redTable[tableSize];
  1732     CGGammaValue greenTable[tableSize];
  1733     CGGammaValue blueTable[tableSize];
  1734     uint32_t i, tableCopied;
  1735 
  1736     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1737                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1738         return SDL_SetError("CGGetDisplayTransferByTable()");
  1739     }
  1740 
  1741     for (i = 0; i < tableCopied; i++) {
  1742         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1743         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1744         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1745     }
  1746     return 0;
  1747 }
  1748 
  1749 void
  1750 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1751 {
  1752     SDL_Mouse *mouse = SDL_GetMouse();
  1753     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1754 
  1755     /* Enable or disable the event tap as necessary */
  1756     Cocoa_EnableMouseEventTap(mouse->driverdata, grabbed);
  1757 
  1758     /* Move the cursor to the nearest point in the window */
  1759     if (grabbed && data && ![data->listener isMoving]) {
  1760         int x, y;
  1761         CGPoint cgpoint;
  1762 
  1763         SDL_GetMouseState(&x, &y);
  1764         cgpoint.x = window->x + x;
  1765         cgpoint.y = window->y + y;
  1766 
  1767         Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
  1768 
  1769         DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
  1770         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1771     }
  1772 
  1773     if ( data && (window->flags & SDL_WINDOW_FULLSCREEN) ) {
  1774         if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)
  1775             && ![data->listener isInFullscreenSpace]) {
  1776             /* OpenGL is rendering to the window, so make it visible! */
  1777             /* Doing this in 10.11 while in a Space breaks things (bug #3152) */
  1778             [data->nswindow setLevel:CGShieldingWindowLevel()];
  1779         } else {
  1780             [data->nswindow setLevel:kCGNormalWindowLevel];
  1781         }
  1782     }
  1783 }
  1784 
  1785 void
  1786 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1787 { @autoreleasepool
  1788 {
  1789     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1790 
  1791     if (data) {
  1792         if ([data->listener isInFullscreenSpace]) {
  1793             [NSMenu setMenuBarVisible:YES];
  1794         }
  1795         [data->listener close];
  1796         [data->listener release];
  1797         if (data->created) {
  1798             [data->nswindow close];
  1799         }
  1800 
  1801         NSArray *contexts = [[data->nscontexts copy] autorelease];
  1802         for (SDLOpenGLContext *context in contexts) {
  1803             /* Calling setWindow:NULL causes the context to remove itself from the context list. */            
  1804             [context setWindow:NULL];
  1805         }
  1806         [data->nscontexts release];
  1807 
  1808         SDL_free(data);
  1809     }
  1810     window->driverdata = NULL;
  1811 }}
  1812 
  1813 SDL_bool
  1814 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1815 {
  1816     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1817 
  1818     if (info->version.major <= SDL_MAJOR_VERSION) {
  1819         info->subsystem = SDL_SYSWM_COCOA;
  1820         info->info.cocoa.window = nswindow;
  1821         return SDL_TRUE;
  1822     } else {
  1823         SDL_SetError("Application not compiled with SDL %d.%d",
  1824                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1825         return SDL_FALSE;
  1826     }
  1827 }
  1828 
  1829 SDL_bool
  1830 Cocoa_IsWindowInFullscreenSpace(SDL_Window * window)
  1831 {
  1832     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1833 
  1834     if ([data->listener isInFullscreenSpace]) {
  1835         return SDL_TRUE;
  1836     } else {
  1837         return SDL_FALSE;
  1838     }
  1839 }
  1840 
  1841 SDL_bool
  1842 Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
  1843 { @autoreleasepool
  1844 {
  1845     SDL_bool succeeded = SDL_FALSE;
  1846     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1847 
  1848     if ([data->listener setFullscreenSpace:(state ? YES : NO)]) {
  1849         const int maxattempts = 3;
  1850         int attempt = 0;
  1851         while (++attempt <= maxattempts) {
  1852             /* Wait for the transition to complete, so application changes
  1853              take effect properly (e.g. setting the window size, etc.)
  1854              */
  1855             const int limit = 10000;
  1856             int count = 0;
  1857             while ([data->listener isInFullscreenSpaceTransition]) {
  1858                 if ( ++count == limit ) {
  1859                     /* Uh oh, transition isn't completing. Should we assert? */
  1860                     break;
  1861                 }
  1862                 SDL_Delay(1);
  1863                 SDL_PumpEvents();
  1864             }
  1865             if ([data->listener isInFullscreenSpace] == (state ? YES : NO))
  1866                 break;
  1867             /* Try again, the last attempt was interrupted by user gestures */
  1868             if (![data->listener setFullscreenSpace:(state ? YES : NO)])
  1869                 break; /* ??? */
  1870         }
  1871         /* Return TRUE to prevent non-space fullscreen logic from running */
  1872         succeeded = SDL_TRUE;
  1873     }
  1874 
  1875     return succeeded;
  1876 }}
  1877 
  1878 int
  1879 Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
  1880 {
  1881     return 0;  /* just succeed, the real work is done elsewhere. */
  1882 }
  1883 
  1884 void
  1885 Cocoa_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
  1886 {
  1887     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1888     if (accept) {
  1889         [data->nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
  1890     } else {
  1891         [data->nswindow unregisterDraggedTypes];
  1892     }
  1893 }
  1894 
  1895 int
  1896 Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity)
  1897 {
  1898     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1899     [data->nswindow setAlphaValue:opacity];
  1900     return 0;
  1901 }
  1902 
  1903 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1904 
  1905 /* vi: set ts=4 sw=4 expandtab: */