src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Tue, 21 Apr 2015 10:10:59 -0400
changeset 10028 da668b612d10
parent 10025 bf4f8cde1c54
child 10140 6dd5f49e4a77
permissions -rw-r--r--
Added SDL_WINDOWEVENT_HIT_TEST.

This lets windows know when they are dropping a mouse event because their
hit test reported something other than SDL_HITTEST_NORMAL. It lets them know
exactly where in the event queue this happened.

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         SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
   856         return;  /* dragging, drop event. */
   857     }
   858 
   859     switch ([theEvent buttonNumber]) {
   860     case 0:
   861         if (([theEvent modifierFlags] & NSControlKeyMask) &&
   862 		    GetHintCtrlClickEmulateRightClick()) {
   863             wasCtrlLeft = YES;
   864             button = SDL_BUTTON_RIGHT;
   865         } else {
   866             wasCtrlLeft = NO;
   867             button = SDL_BUTTON_LEFT;
   868         }
   869         break;
   870     case 1:
   871         button = SDL_BUTTON_RIGHT;
   872         break;
   873     case 2:
   874         button = SDL_BUTTON_MIDDLE;
   875         break;
   876     default:
   877         button = [theEvent buttonNumber] + 1;
   878         break;
   879     }
   880     SDL_SendMouseButton(_data->window, 0, SDL_PRESSED, button);
   881 }
   882 
   883 - (void)rightMouseDown:(NSEvent *)theEvent
   884 {
   885     [self mouseDown:theEvent];
   886 }
   887 
   888 - (void)otherMouseDown:(NSEvent *)theEvent
   889 {
   890     [self mouseDown:theEvent];
   891 }
   892 
   893 - (void)mouseUp:(NSEvent *)theEvent
   894 {
   895     int button;
   896 
   897     if ([self processHitTest:theEvent]) {
   898         SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
   899         return;  /* stopped dragging, drop event. */
   900     }
   901 
   902     switch ([theEvent buttonNumber]) {
   903     case 0:
   904         if (wasCtrlLeft) {
   905             button = SDL_BUTTON_RIGHT;
   906             wasCtrlLeft = NO;
   907         } else {
   908             button = SDL_BUTTON_LEFT;
   909         }
   910         break;
   911     case 1:
   912         button = SDL_BUTTON_RIGHT;
   913         break;
   914     case 2:
   915         button = SDL_BUTTON_MIDDLE;
   916         break;
   917     default:
   918         button = [theEvent buttonNumber] + 1;
   919         break;
   920     }
   921     SDL_SendMouseButton(_data->window, 0, SDL_RELEASED, button);
   922 }
   923 
   924 - (void)rightMouseUp:(NSEvent *)theEvent
   925 {
   926     [self mouseUp:theEvent];
   927 }
   928 
   929 - (void)otherMouseUp:(NSEvent *)theEvent
   930 {
   931     [self mouseUp:theEvent];
   932 }
   933 
   934 - (void)mouseMoved:(NSEvent *)theEvent
   935 {
   936     SDL_Mouse *mouse = SDL_GetMouse();
   937     SDL_Window *window = _data->window;
   938     NSPoint point;
   939     int x, y;
   940 
   941     if ([self processHitTest:theEvent]) {
   942         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
   943         return;  /* dragging, drop event. */
   944     }
   945 
   946     if (mouse->relative_mode) {
   947         return;
   948     }
   949 
   950     point = [theEvent locationInWindow];
   951     x = (int)point.x;
   952     y = (int)(window->h - point.y);
   953 
   954     if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   955         if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
   956             if (x < 0) {
   957                 x = 0;
   958             } else if (x >= window->w) {
   959                 x = window->w - 1;
   960             }
   961             if (y < 0) {
   962                 y = 0;
   963             } else if (y >= window->h) {
   964                 y = window->h - 1;
   965             }
   966 
   967 #if !SDL_MAC_NO_SANDBOX
   968             CGPoint cgpoint;
   969 
   970             /* When SDL_MAC_NO_SANDBOX is set, this is handled by
   971              * SDL_cocoamousetap.m.
   972              */
   973 
   974             cgpoint.x = window->x + x;
   975             cgpoint.y = window->y + y;
   976 
   977             /* According to the docs, this was deprecated in 10.6, but it's still
   978              * around. The substitute requires a CGEventSource, but I'm not entirely
   979              * sure how we'd procure the right one for this event.
   980              */
   981             CGSetLocalEventsSuppressionInterval(0.0);
   982             CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
   983             CGSetLocalEventsSuppressionInterval(0.25);
   984 
   985             Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
   986 #endif
   987         }
   988     }
   989     SDL_SendMouseMotion(window, 0, 0, x, y);
   990 }
   991 
   992 - (void)mouseDragged:(NSEvent *)theEvent
   993 {
   994     [self mouseMoved:theEvent];
   995 }
   996 
   997 - (void)rightMouseDragged:(NSEvent *)theEvent
   998 {
   999     [self mouseMoved:theEvent];
  1000 }
  1001 
  1002 - (void)otherMouseDragged:(NSEvent *)theEvent
  1003 {
  1004     [self mouseMoved:theEvent];
  1005 }
  1006 
  1007 - (void)scrollWheel:(NSEvent *)theEvent
  1008 {
  1009     Cocoa_HandleMouseWheel(_data->window, theEvent);
  1010 }
  1011 
  1012 - (void)touchesBeganWithEvent:(NSEvent *) theEvent
  1013 {
  1014     NSSet *touches = [theEvent touchesMatchingPhase:NSTouchPhaseAny inView:nil];
  1015     int existingTouchCount = 0;
  1016 
  1017     for (NSTouch* touch in touches) {
  1018         if ([touch phase] != NSTouchPhaseBegan) {
  1019             existingTouchCount++;
  1020         }
  1021     }
  1022     if (existingTouchCount == 0) {
  1023         SDL_TouchID touchID = (SDL_TouchID)(intptr_t)[[touches anyObject] device];
  1024         int numFingers = SDL_GetNumTouchFingers(touchID);
  1025         DLog("Reset Lost Fingers: %d", numFingers);
  1026         for (--numFingers; numFingers >= 0; --numFingers) {
  1027             SDL_Finger* finger = SDL_GetTouchFinger(touchID, numFingers);
  1028             SDL_SendTouch(touchID, finger->id, SDL_FALSE, 0, 0, 0);
  1029         }
  1030     }
  1031 
  1032     DLog("Began Fingers: %lu .. existing: %d", (unsigned long)[touches count], existingTouchCount);
  1033     [self handleTouches:NSTouchPhaseBegan withEvent:theEvent];
  1034 }
  1035 
  1036 - (void)touchesMovedWithEvent:(NSEvent *) theEvent
  1037 {
  1038     [self handleTouches:NSTouchPhaseMoved withEvent:theEvent];
  1039 }
  1040 
  1041 - (void)touchesEndedWithEvent:(NSEvent *) theEvent
  1042 {
  1043     [self handleTouches:NSTouchPhaseEnded withEvent:theEvent];
  1044 }
  1045 
  1046 - (void)touchesCancelledWithEvent:(NSEvent *) theEvent
  1047 {
  1048     [self handleTouches:NSTouchPhaseCancelled withEvent:theEvent];
  1049 }
  1050 
  1051 - (void)handleTouches:(NSTouchPhase) phase withEvent:(NSEvent *) theEvent
  1052 {
  1053     NSSet *touches = [theEvent touchesMatchingPhase:phase inView:nil];
  1054 
  1055     for (NSTouch *touch in touches) {
  1056         const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device];
  1057         if (SDL_AddTouch(touchId, "") < 0) {
  1058             return;
  1059         }
  1060 
  1061         const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
  1062         float x = [touch normalizedPosition].x;
  1063         float y = [touch normalizedPosition].y;
  1064         /* Make the origin the upper left instead of the lower left */
  1065         y = 1.0f - y;
  1066 
  1067         switch (phase) {
  1068         case NSTouchPhaseBegan:
  1069             SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
  1070             break;
  1071         case NSTouchPhaseEnded:
  1072         case NSTouchPhaseCancelled:
  1073             SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
  1074             break;
  1075         case NSTouchPhaseMoved:
  1076             SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
  1077             break;
  1078         default:
  1079             break;
  1080         }
  1081     }
  1082 }
  1083 
  1084 @end
  1085 
  1086 @interface SDLView : NSView {
  1087     SDL_Window *_sdlWindow;
  1088 }
  1089 
  1090 - (void)setSDLWindow:(SDL_Window*)window;
  1091 
  1092 /* The default implementation doesn't pass rightMouseDown to responder chain */
  1093 - (void)rightMouseDown:(NSEvent *)theEvent;
  1094 - (BOOL)mouseDownCanMoveWindow;
  1095 - (void)drawRect:(NSRect)dirtyRect;
  1096 @end
  1097 
  1098 @implementation SDLView
  1099 - (void)setSDLWindow:(SDL_Window*)window
  1100 {
  1101     _sdlWindow = window;
  1102 }
  1103 
  1104 - (void)drawRect:(NSRect)dirtyRect
  1105 {
  1106     SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0);
  1107 }
  1108 
  1109 - (void)rightMouseDown:(NSEvent *)theEvent
  1110 {
  1111     [[self nextResponder] rightMouseDown:theEvent];
  1112 }
  1113 
  1114 - (BOOL)mouseDownCanMoveWindow
  1115 {
  1116     /* Always say YES, but this doesn't do anything until we call
  1117        -[NSWindow setMovableByWindowBackground:YES], which we ninja-toggle
  1118        during mouse events when we're using a drag area. */
  1119     return YES;
  1120 }
  1121 
  1122 - (void)resetCursorRects
  1123 {
  1124     [super resetCursorRects];
  1125     SDL_Mouse *mouse = SDL_GetMouse();
  1126 
  1127     if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
  1128         [self addCursorRect:[self bounds]
  1129                      cursor:mouse->cur_cursor->driverdata];
  1130     } else {
  1131         [self addCursorRect:[self bounds]
  1132                      cursor:[NSCursor invisibleCursor]];
  1133     }
  1134 }
  1135 @end
  1136 
  1137 static int
  1138 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
  1139 { @autoreleasepool
  1140 {
  1141     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1142     SDL_WindowData *data;
  1143 
  1144     /* Allocate the window data */
  1145     window->driverdata = data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
  1146     if (!data) {
  1147         return SDL_OutOfMemory();
  1148     }
  1149     data->window = window;
  1150     data->nswindow = nswindow;
  1151     data->created = created;
  1152     data->videodata = videodata;
  1153     data->nscontexts = [[NSMutableArray alloc] init];
  1154 
  1155     /* Create an event listener for the window */
  1156     data->listener = [[Cocoa_WindowListener alloc] init];
  1157 
  1158     /* Fill in the SDL window with the window data */
  1159     {
  1160         NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1161         ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1162         window->x = (int)rect.origin.x;
  1163         window->y = (int)rect.origin.y;
  1164         window->w = (int)rect.size.width;
  1165         window->h = (int)rect.size.height;
  1166     }
  1167 
  1168     /* Set up the listener after we create the view */
  1169     [data->listener listen:data];
  1170 
  1171     if ([nswindow isVisible]) {
  1172         window->flags |= SDL_WINDOW_SHOWN;
  1173     } else {
  1174         window->flags &= ~SDL_WINDOW_SHOWN;
  1175     }
  1176 
  1177     {
  1178         unsigned int style = [nswindow styleMask];
  1179 
  1180         if (style == NSBorderlessWindowMask) {
  1181             window->flags |= SDL_WINDOW_BORDERLESS;
  1182         } else {
  1183             window->flags &= ~SDL_WINDOW_BORDERLESS;
  1184         }
  1185         if (style & NSResizableWindowMask) {
  1186             window->flags |= SDL_WINDOW_RESIZABLE;
  1187         } else {
  1188             window->flags &= ~SDL_WINDOW_RESIZABLE;
  1189         }
  1190     }
  1191 
  1192     /* isZoomed always returns true if the window is not resizable */
  1193     if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1194         window->flags |= SDL_WINDOW_MAXIMIZED;
  1195     } else {
  1196         window->flags &= ~SDL_WINDOW_MAXIMIZED;
  1197     }
  1198 
  1199     if ([nswindow isMiniaturized]) {
  1200         window->flags |= SDL_WINDOW_MINIMIZED;
  1201     } else {
  1202         window->flags &= ~SDL_WINDOW_MINIMIZED;
  1203     }
  1204 
  1205     if ([nswindow isKeyWindow]) {
  1206         window->flags |= SDL_WINDOW_INPUT_FOCUS;
  1207         SDL_SetKeyboardFocus(data->window);
  1208     }
  1209 
  1210     /* Prevents the window's "window device" from being destroyed when it is
  1211      * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
  1212      */
  1213     [nswindow setOneShot:NO];
  1214 
  1215     /* All done! */
  1216     window->driverdata = data;
  1217     return 0;
  1218 }}
  1219 
  1220 int
  1221 Cocoa_CreateWindow(_THIS, SDL_Window * window)
  1222 { @autoreleasepool
  1223 {
  1224     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1225     NSWindow *nswindow;
  1226     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1227     NSRect rect;
  1228     SDL_Rect bounds;
  1229     unsigned int style;
  1230     NSArray *screens = [NSScreen screens];
  1231 
  1232     Cocoa_GetDisplayBounds(_this, display, &bounds);
  1233     rect.origin.x = window->x;
  1234     rect.origin.y = window->y;
  1235     rect.size.width = window->w;
  1236     rect.size.height = window->h;
  1237     ConvertNSRect([screens objectAtIndex:0], (window->flags & FULLSCREEN_MASK), &rect);
  1238 
  1239     style = GetWindowStyle(window);
  1240 
  1241     /* Figure out which screen to place this window */
  1242     NSScreen *screen = nil;
  1243     for (NSScreen *candidate in screens) {
  1244         NSRect screenRect = [candidate frame];
  1245         if (rect.origin.x >= screenRect.origin.x &&
  1246             rect.origin.x < screenRect.origin.x + screenRect.size.width &&
  1247             rect.origin.y >= screenRect.origin.y &&
  1248             rect.origin.y < screenRect.origin.y + screenRect.size.height) {
  1249             screen = candidate;
  1250             rect.origin.x -= screenRect.origin.x;
  1251             rect.origin.y -= screenRect.origin.y;
  1252         }
  1253     }
  1254 
  1255     @try {
  1256         nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
  1257     }
  1258     @catch (NSException *e) {
  1259         return SDL_SetError("%s", [[e reason] UTF8String]);
  1260     }
  1261     [nswindow setBackgroundColor:[NSColor blackColor]];
  1262 
  1263     if (videodata->allow_spaces) {
  1264         SDL_assert(floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6);
  1265         SDL_assert([nswindow respondsToSelector:@selector(toggleFullScreen:)]);
  1266         /* we put FULLSCREEN_DESKTOP windows in their own Space, without a toggle button or menubar, later */
  1267         if (window->flags & SDL_WINDOW_RESIZABLE) {
  1268             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
  1269             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
  1270         }
  1271     }
  1272 
  1273     /* Create a default view for this window */
  1274     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1275     SDLView *contentView = [[SDLView alloc] initWithFrame:rect];
  1276     [contentView setSDLWindow:window];
  1277 
  1278     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
  1279         if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
  1280             [contentView setWantsBestResolutionOpenGLSurface:YES];
  1281         }
  1282     }
  1283 
  1284     [nswindow setContentView: contentView];
  1285     [contentView release];
  1286 
  1287     /* Allow files and folders to be dragged onto the window by users */
  1288     [nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
  1289 
  1290     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
  1291         [nswindow release];
  1292         return -1;
  1293     }
  1294     return 0;
  1295 }}
  1296 
  1297 int
  1298 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
  1299 { @autoreleasepool
  1300 {
  1301     NSWindow *nswindow = (NSWindow *) data;
  1302     NSString *title;
  1303 
  1304     /* Query the title from the existing window */
  1305     title = [nswindow title];
  1306     if (title) {
  1307         window->title = SDL_strdup([title UTF8String]);
  1308     }
  1309 
  1310     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
  1311 }}
  1312 
  1313 void
  1314 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
  1315 { @autoreleasepool
  1316 {
  1317     const char *title = window->title ? window->title : "";
  1318     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1319     NSString *string = [[NSString alloc] initWithUTF8String:title];
  1320     [nswindow setTitle:string];
  1321     [string release];
  1322 }}
  1323 
  1324 void
  1325 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
  1326 { @autoreleasepool
  1327 {
  1328     NSImage *nsimage = Cocoa_CreateImage(icon);
  1329 
  1330     if (nsimage) {
  1331         [NSApp setApplicationIconImage:nsimage];
  1332     }
  1333 }}
  1334 
  1335 void
  1336 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
  1337 { @autoreleasepool
  1338 {
  1339     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1340     NSWindow *nswindow = windata->nswindow;
  1341     NSRect rect;
  1342     Uint32 moveHack;
  1343 
  1344     rect.origin.x = window->x;
  1345     rect.origin.y = window->y;
  1346     rect.size.width = window->w;
  1347     rect.size.height = window->h;
  1348     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1349 
  1350     moveHack = s_moveHack;
  1351     s_moveHack = 0;
  1352     [nswindow setFrameOrigin:rect.origin];
  1353     s_moveHack = moveHack;
  1354 
  1355     ScheduleContextUpdates(windata);
  1356 }}
  1357 
  1358 void
  1359 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
  1360 { @autoreleasepool
  1361 {
  1362     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1363     NSWindow *nswindow = windata->nswindow;
  1364     NSRect rect;
  1365     Uint32 moveHack;
  1366 
  1367     /* Cocoa will resize the window from the bottom-left rather than the
  1368      * top-left when -[nswindow setContentSize:] is used, so we must set the
  1369      * entire frame based on the new size, in order to preserve the position.
  1370      */
  1371     rect.origin.x = window->x;
  1372     rect.origin.y = window->y;
  1373     rect.size.width = window->w;
  1374     rect.size.height = window->h;
  1375     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1376 
  1377     moveHack = s_moveHack;
  1378     s_moveHack = 0;
  1379     [nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES];
  1380     s_moveHack = moveHack;
  1381 
  1382     ScheduleContextUpdates(windata);
  1383 }}
  1384 
  1385 void
  1386 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
  1387 { @autoreleasepool
  1388 {
  1389     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1390 
  1391     NSSize minSize;
  1392     minSize.width = window->min_w;
  1393     minSize.height = window->min_h;
  1394 
  1395     [windata->nswindow setContentMinSize:minSize];
  1396 }}
  1397 
  1398 void
  1399 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
  1400 { @autoreleasepool
  1401 {
  1402     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1403 
  1404     NSSize maxSize;
  1405     maxSize.width = window->max_w;
  1406     maxSize.height = window->max_h;
  1407 
  1408     [windata->nswindow setContentMaxSize:maxSize];
  1409 }}
  1410 
  1411 void
  1412 Cocoa_ShowWindow(_THIS, SDL_Window * window)
  1413 { @autoreleasepool
  1414 {
  1415     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1416     NSWindow *nswindow = windowData->nswindow;
  1417 
  1418     if (![nswindow isMiniaturized]) {
  1419         [windowData->listener pauseVisibleObservation];
  1420         [nswindow makeKeyAndOrderFront:nil];
  1421         [windowData->listener resumeVisibleObservation];
  1422     }
  1423 }}
  1424 
  1425 void
  1426 Cocoa_HideWindow(_THIS, SDL_Window * window)
  1427 { @autoreleasepool
  1428 {
  1429     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1430 
  1431     [nswindow orderOut:nil];
  1432 }}
  1433 
  1434 void
  1435 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
  1436 { @autoreleasepool
  1437 {
  1438     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1439     NSWindow *nswindow = windowData->nswindow;
  1440 
  1441     /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
  1442        a minimized or hidden window, so check for that before showing it.
  1443      */
  1444     [windowData->listener pauseVisibleObservation];
  1445     if (![nswindow isMiniaturized] && [nswindow isVisible]) {
  1446         [NSApp activateIgnoringOtherApps:YES];
  1447         [nswindow makeKeyAndOrderFront:nil];
  1448     }
  1449     [windowData->listener resumeVisibleObservation];
  1450 }}
  1451 
  1452 void
  1453 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
  1454 { @autoreleasepool
  1455 {
  1456     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1457     NSWindow *nswindow = windata->nswindow;
  1458 
  1459     [nswindow zoom:nil];
  1460 
  1461     ScheduleContextUpdates(windata);
  1462 }}
  1463 
  1464 void
  1465 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
  1466 { @autoreleasepool
  1467 {
  1468     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1469     NSWindow *nswindow = data->nswindow;
  1470 
  1471     if ([data->listener isInFullscreenSpaceTransition]) {
  1472         [data->listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
  1473     } else {
  1474         [nswindow miniaturize:nil];
  1475     }
  1476 }}
  1477 
  1478 void
  1479 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
  1480 { @autoreleasepool
  1481 {
  1482     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1483 
  1484     if ([nswindow isMiniaturized]) {
  1485         [nswindow deminiaturize:nil];
  1486     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1487         [nswindow zoom:nil];
  1488     }
  1489 }}
  1490 
  1491 static NSWindow *
  1492 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
  1493 {
  1494     if (!data->created) {
  1495         /* Don't mess with other people's windows... */
  1496         return nswindow;
  1497     }
  1498 
  1499     [data->listener close];
  1500     data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
  1501     [data->nswindow setContentView:[nswindow contentView]];
  1502     [data->nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
  1503     /* See comment in SetupWindowData. */
  1504     [data->nswindow setOneShot:NO];
  1505     [data->listener listen:data];
  1506 
  1507     [nswindow close];
  1508 
  1509     return data->nswindow;
  1510 }
  1511 
  1512 void
  1513 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
  1514 { @autoreleasepool
  1515 {
  1516     if (SetWindowStyle(window, GetWindowStyle(window))) {
  1517         if (bordered) {
  1518             Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
  1519         }
  1520     }
  1521 }}
  1522 
  1523 
  1524 void
  1525 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
  1526 { @autoreleasepool
  1527 {
  1528     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1529     NSWindow *nswindow = data->nswindow;
  1530     NSRect rect;
  1531 
  1532     /* The view responder chain gets messed with during setStyleMask */
  1533     if ([[nswindow contentView] nextResponder] == data->listener) {
  1534         [[nswindow contentView] setNextResponder:nil];
  1535     }
  1536 
  1537     if (fullscreen) {
  1538         SDL_Rect bounds;
  1539 
  1540         Cocoa_GetDisplayBounds(_this, display, &bounds);
  1541         rect.origin.x = bounds.x;
  1542         rect.origin.y = bounds.y;
  1543         rect.size.width = bounds.w;
  1544         rect.size.height = bounds.h;
  1545         ConvertNSRect([nswindow screen], fullscreen, &rect);
  1546 
  1547         /* Hack to fix origin on Mac OS X 10.4 */
  1548         NSRect screenRect = [[nswindow screen] frame];
  1549         if (screenRect.size.height >= 1.0f) {
  1550             rect.origin.y += (screenRect.size.height - rect.size.height);
  1551         }
  1552 
  1553         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1554             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
  1555         } else {
  1556             nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
  1557         }
  1558     } else {
  1559         rect.origin.x = window->windowed.x;
  1560         rect.origin.y = window->windowed.y;
  1561         rect.size.width = window->windowed.w;
  1562         rect.size.height = window->windowed.h;
  1563         ConvertNSRect([nswindow screen], fullscreen, &rect);
  1564 
  1565         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1566             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
  1567 
  1568             /* Hack to restore window decorations on Mac OS X 10.10 */
  1569             NSRect frameRect = [nswindow frame];
  1570             [nswindow setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
  1571             [nswindow setFrame:frameRect display:NO];
  1572         } else {
  1573             nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
  1574         }
  1575     }
  1576 
  1577     /* The view responder chain gets messed with during setStyleMask */
  1578     if ([[nswindow contentView] nextResponder] != data->listener) {
  1579         [[nswindow contentView] setNextResponder:data->listener];
  1580     }
  1581 
  1582     s_moveHack = 0;
  1583     [nswindow setContentSize:rect.size];
  1584     [nswindow setFrameOrigin:rect.origin];
  1585     s_moveHack = SDL_GetTicks();
  1586 
  1587     /* When the window style changes the title is cleared */
  1588     if (!fullscreen) {
  1589         Cocoa_SetWindowTitle(_this, window);
  1590     }
  1591 
  1592     if (SDL_ShouldAllowTopmost() && fullscreen) {
  1593         /* OpenGL is rendering to the window, so make it visible! */
  1594         [nswindow setLevel:CGShieldingWindowLevel()];
  1595     } else {
  1596         [nswindow setLevel:kCGNormalWindowLevel];
  1597     }
  1598 
  1599     if ([nswindow isVisible] || fullscreen) {
  1600         [data->listener pauseVisibleObservation];
  1601         [nswindow makeKeyAndOrderFront:nil];
  1602         [data->listener resumeVisibleObservation];
  1603     }
  1604 
  1605     ScheduleContextUpdates(data);
  1606 }}
  1607 
  1608 int
  1609 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1610 {
  1611     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1612     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1613     const uint32_t tableSize = 256;
  1614     CGGammaValue redTable[tableSize];
  1615     CGGammaValue greenTable[tableSize];
  1616     CGGammaValue blueTable[tableSize];
  1617     uint32_t i;
  1618     float inv65535 = 1.0f / 65535.0f;
  1619 
  1620     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1621     for (i = 0; i < 256; i++) {
  1622         redTable[i] = ramp[0*256+i] * inv65535;
  1623         greenTable[i] = ramp[1*256+i] * inv65535;
  1624         blueTable[i] = ramp[2*256+i] * inv65535;
  1625     }
  1626 
  1627     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1628                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1629         return SDL_SetError("CGSetDisplayTransferByTable()");
  1630     }
  1631     return 0;
  1632 }
  1633 
  1634 int
  1635 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1636 {
  1637     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1638     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1639     const uint32_t tableSize = 256;
  1640     CGGammaValue redTable[tableSize];
  1641     CGGammaValue greenTable[tableSize];
  1642     CGGammaValue blueTable[tableSize];
  1643     uint32_t i, tableCopied;
  1644 
  1645     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1646                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1647         return SDL_SetError("CGGetDisplayTransferByTable()");
  1648     }
  1649 
  1650     for (i = 0; i < tableCopied; i++) {
  1651         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1652         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1653         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1654     }
  1655     return 0;
  1656 }
  1657 
  1658 void
  1659 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1660 {
  1661     /* Move the cursor to the nearest point in the window */
  1662     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1663     if (grabbed && data && ![data->listener isMoving]) {
  1664         int x, y;
  1665         CGPoint cgpoint;
  1666 
  1667         SDL_GetMouseState(&x, &y);
  1668         cgpoint.x = window->x + x;
  1669         cgpoint.y = window->y + y;
  1670 
  1671         Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
  1672 
  1673         DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
  1674         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1675     }
  1676 
  1677     if ( data && (window->flags & SDL_WINDOW_FULLSCREEN) ) {
  1678         if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)
  1679             && ![data->listener isInFullscreenSpace]) {
  1680             /* OpenGL is rendering to the window, so make it visible! */
  1681             /* Doing this in 10.11 while in a Space breaks things (bug #3152) */
  1682             [data->nswindow setLevel:CGShieldingWindowLevel()];
  1683         } else {
  1684             [data->nswindow setLevel:kCGNormalWindowLevel];
  1685         }
  1686     }
  1687 }
  1688 
  1689 void
  1690 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1691 { @autoreleasepool
  1692 {
  1693     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1694 
  1695     if (data) {
  1696         if ([data->listener isInFullscreenSpace]) {
  1697             [NSMenu setMenuBarVisible:YES];
  1698         }
  1699         [data->listener close];
  1700         [data->listener release];
  1701         if (data->created) {
  1702             [data->nswindow close];
  1703         }
  1704 
  1705         NSArray *contexts = [[data->nscontexts copy] autorelease];
  1706         for (SDLOpenGLContext *context in contexts) {
  1707             /* Calling setWindow:NULL causes the context to remove itself from the context list. */            
  1708             [context setWindow:NULL];
  1709         }
  1710         [data->nscontexts release];
  1711 
  1712         SDL_free(data);
  1713     }
  1714     window->driverdata = NULL;
  1715 }}
  1716 
  1717 SDL_bool
  1718 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1719 {
  1720     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1721 
  1722     if (info->version.major <= SDL_MAJOR_VERSION) {
  1723         info->subsystem = SDL_SYSWM_COCOA;
  1724         info->info.cocoa.window = nswindow;
  1725         return SDL_TRUE;
  1726     } else {
  1727         SDL_SetError("Application not compiled with SDL %d.%d\n",
  1728                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1729         return SDL_FALSE;
  1730     }
  1731 }
  1732 
  1733 SDL_bool
  1734 Cocoa_IsWindowInFullscreenSpace(SDL_Window * window)
  1735 {
  1736     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1737 
  1738     if ([data->listener isInFullscreenSpace]) {
  1739         return SDL_TRUE;
  1740     } else {
  1741         return SDL_FALSE;
  1742     }
  1743 }
  1744 
  1745 SDL_bool
  1746 Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
  1747 { @autoreleasepool
  1748 {
  1749     SDL_bool succeeded = SDL_FALSE;
  1750     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1751 
  1752     if ([data->listener setFullscreenSpace:(state ? YES : NO)]) {
  1753         const int maxattempts = 3;
  1754         int attempt = 0;
  1755         while (++attempt <= maxattempts) {
  1756             /* Wait for the transition to complete, so application changes
  1757              take effect properly (e.g. setting the window size, etc.)
  1758              */
  1759             const int limit = 10000;
  1760             int count = 0;
  1761             while ([data->listener isInFullscreenSpaceTransition]) {
  1762                 if ( ++count == limit ) {
  1763                     /* Uh oh, transition isn't completing. Should we assert? */
  1764                     break;
  1765                 }
  1766                 SDL_Delay(1);
  1767                 SDL_PumpEvents();
  1768             }
  1769             if ([data->listener isInFullscreenSpace] == (state ? YES : NO))
  1770                 break;
  1771             /* Try again, the last attempt was interrupted by user gestures */
  1772             if (![data->listener setFullscreenSpace:(state ? YES : NO)])
  1773                 break; /* ??? */
  1774         }
  1775         /* Return TRUE to prevent non-space fullscreen logic from running */
  1776         succeeded = SDL_TRUE;
  1777     }
  1778 
  1779     return succeeded;
  1780 }}
  1781 
  1782 int
  1783 Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
  1784 {
  1785     return 0;  /* just succeed, the real work is done elsewhere. */
  1786 }
  1787 
  1788 int
  1789 Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity)
  1790 {
  1791     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1792     [data->nswindow setAlphaValue:opacity];
  1793     return 0;
  1794 }
  1795 
  1796 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1797 
  1798 /* vi: set ts=4 sw=4 expandtab: */