src/video/cocoa/SDL_cocoawindow.m
author Alex Szpakowski <slime73@gmail.com>
Sat, 02 Apr 2016 11:54:05 -0300
changeset 10140 6dd5f49e4a77
parent 10028 da668b612d10
child 10158 941261e0db9f
permissions -rw-r--r--
Add a new hint SDL_HINT_MAC_MOUSE_FOCUS_CLICKTHROUGH, which allows mouse click events to occur when clicking to focus a window in Mac OS X.

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