src/video/cocoa/SDL_cocoawindow.m
author David Ludwig <dludwig@pobox.com>
Sun, 03 Sep 2017 17:33:49 -0400
changeset 11447 f76299105635
parent 11436 37b2fcd8fc8c
child 11482 05aa6d232dca
permissions -rw-r--r--
macOS: bug-fix for #3793, "fullscreen toggle does not maintain SDL_Renderer's logical size"

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