src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Tue, 05 Jan 2016 02:46:10 -0500
changeset 10025 bf4f8cde1c54
parent 10022 30807689ca1b
child 10028 da668b612d10
permissions -rw-r--r--
Added SDL_SetWindowOpacity() and SDL_GetWindowOpacity().

This is currently implemented for X11, Cocoa, Windows, and DirectFB.

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