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