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