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