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