src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Mon, 03 Mar 2014 21:25:16 -0500
changeset 8295 56f7e38ff0bc
parent 8294 7371078ee876
child 8618 06ee748d59b2
permissions -rw-r--r--
Mac: Added a hint to opt-out of new Spaces code.
     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     if (pendingWindowOperation == PENDING_OPERATION_ENTER_FULLSCREEN) {
   591         pendingWindowOperation = PENDING_OPERATION_NONE;
   592         [self setFullscreenSpace:YES];
   593     } else if (pendingWindowOperation == PENDING_OPERATION_MINIMIZE) {
   594         pendingWindowOperation = PENDING_OPERATION_NONE;
   595         [nswindow miniaturize:nil];
   596     } else {
   597         /* Adjust the fullscreen toggle button and readd menu now that we're here. */
   598         if (window->flags & SDL_WINDOW_RESIZABLE) {
   599             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
   600             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
   601         } else {
   602             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorManaged];
   603         }
   604         [NSMenu setMenuBarVisible:YES];
   605 
   606         pendingWindowOperation = PENDING_OPERATION_NONE;
   607         /* Force the size change event in case it was delivered earlier
   608            while the window was still animating into place.
   609          */
   610         window->w = 0;
   611         window->h = 0;
   612         [self windowDidResize:aNotification];
   613     }
   614 }
   615 
   616 -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
   617 {
   618     if ((_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
   619         return NSApplicationPresentationFullScreen | NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar;
   620     } else {
   621         return proposedOptions;
   622     }
   623 }
   624 
   625 
   626 /* We'll respond to key events by doing nothing so we don't beep.
   627  * We could handle key messages here, but we lose some in the NSApp dispatch,
   628  * where they get converted to action messages, etc.
   629  */
   630 - (void)flagsChanged:(NSEvent *)theEvent
   631 {
   632     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   633 }
   634 - (void)keyDown:(NSEvent *)theEvent
   635 {
   636     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   637 }
   638 - (void)keyUp:(NSEvent *)theEvent
   639 {
   640     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   641 }
   642 
   643 /* We'll respond to selectors by doing nothing so we don't beep.
   644  * The escape key gets converted to a "cancel" selector, etc.
   645  */
   646 - (void)doCommandBySelector:(SEL)aSelector
   647 {
   648     /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
   649 }
   650 
   651 - (void)mouseDown:(NSEvent *)theEvent
   652 {
   653     int button;
   654 
   655     switch ([theEvent buttonNumber]) {
   656     case 0:
   657         if (([theEvent modifierFlags] & NSControlKeyMask) &&
   658 		    GetHintCtrlClickEmulateRightClick()) {
   659             wasCtrlLeft = YES;
   660             button = SDL_BUTTON_RIGHT;
   661         } else {
   662             wasCtrlLeft = NO;
   663             button = SDL_BUTTON_LEFT;
   664         }
   665         break;
   666     case 1:
   667         button = SDL_BUTTON_RIGHT;
   668         break;
   669     case 2:
   670         button = SDL_BUTTON_MIDDLE;
   671         break;
   672     default:
   673         button = [theEvent buttonNumber] + 1;
   674         break;
   675     }
   676     SDL_SendMouseButton(_data->window, 0, SDL_PRESSED, button);
   677 }
   678 
   679 - (void)rightMouseDown:(NSEvent *)theEvent
   680 {
   681     [self mouseDown:theEvent];
   682 }
   683 
   684 - (void)otherMouseDown:(NSEvent *)theEvent
   685 {
   686     [self mouseDown:theEvent];
   687 }
   688 
   689 - (void)mouseUp:(NSEvent *)theEvent
   690 {
   691     int button;
   692 
   693     switch ([theEvent buttonNumber]) {
   694     case 0:
   695         if (wasCtrlLeft) {
   696             button = SDL_BUTTON_RIGHT;
   697             wasCtrlLeft = NO;
   698         } else {
   699             button = SDL_BUTTON_LEFT;
   700         }
   701         break;
   702     case 1:
   703         button = SDL_BUTTON_RIGHT;
   704         break;
   705     case 2:
   706         button = SDL_BUTTON_MIDDLE;
   707         break;
   708     default:
   709         button = [theEvent buttonNumber] + 1;
   710         break;
   711     }
   712     SDL_SendMouseButton(_data->window, 0, SDL_RELEASED, button);
   713 }
   714 
   715 - (void)rightMouseUp:(NSEvent *)theEvent
   716 {
   717     [self mouseUp:theEvent];
   718 }
   719 
   720 - (void)otherMouseUp:(NSEvent *)theEvent
   721 {
   722     [self mouseUp:theEvent];
   723 }
   724 
   725 - (void)mouseMoved:(NSEvent *)theEvent
   726 {
   727     SDL_Mouse *mouse = SDL_GetMouse();
   728     SDL_Window *window = _data->window;
   729     NSPoint point;
   730     int x, y;
   731 
   732     if (mouse->relative_mode) {
   733         return;
   734     }
   735 
   736     point = [theEvent locationInWindow];
   737     x = (int)point.x;
   738     y = (int)(window->h - point.y);
   739 
   740     if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
   741         if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   742             if (x < 0) {
   743                 x = 0;
   744             } else if (x >= window->w) {
   745                 x = window->w - 1;
   746             }
   747             if (y < 0) {
   748                 y = 0;
   749             } else if (y >= window->h) {
   750                 y = window->h - 1;
   751             }
   752 
   753 #if !SDL_MAC_NO_SANDBOX
   754             CGPoint cgpoint;
   755 
   756             /* When SDL_MAC_NO_SANDBOX is set, this is handled by
   757              * SDL_cocoamousetap.m.
   758              */
   759 
   760             cgpoint.x = window->x + x;
   761             cgpoint.y = window->y + y;
   762 
   763             /* According to the docs, this was deprecated in 10.6, but it's still
   764              * around. The substitute requires a CGEventSource, but I'm not entirely
   765              * sure how we'd procure the right one for this event.
   766              */
   767             CGSetLocalEventsSuppressionInterval(0.0);
   768             CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
   769             CGSetLocalEventsSuppressionInterval(0.25);
   770 
   771             Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
   772 #endif
   773         }
   774     }
   775     SDL_SendMouseMotion(window, 0, 0, x, y);
   776 }
   777 
   778 - (void)mouseDragged:(NSEvent *)theEvent
   779 {
   780     [self mouseMoved:theEvent];
   781 }
   782 
   783 - (void)rightMouseDragged:(NSEvent *)theEvent
   784 {
   785     [self mouseMoved:theEvent];
   786 }
   787 
   788 - (void)otherMouseDragged:(NSEvent *)theEvent
   789 {
   790     [self mouseMoved:theEvent];
   791 }
   792 
   793 - (void)scrollWheel:(NSEvent *)theEvent
   794 {
   795     Cocoa_HandleMouseWheel(_data->window, theEvent);
   796 }
   797 
   798 - (void)touchesBeganWithEvent:(NSEvent *) theEvent
   799 {
   800     [self handleTouches:COCOA_TOUCH_DOWN withEvent:theEvent];
   801 }
   802 
   803 - (void)touchesMovedWithEvent:(NSEvent *) theEvent
   804 {
   805     [self handleTouches:COCOA_TOUCH_MOVE withEvent:theEvent];
   806 }
   807 
   808 - (void)touchesEndedWithEvent:(NSEvent *) theEvent
   809 {
   810     [self handleTouches:COCOA_TOUCH_UP withEvent:theEvent];
   811 }
   812 
   813 - (void)touchesCancelledWithEvent:(NSEvent *) theEvent
   814 {
   815     [self handleTouches:COCOA_TOUCH_CANCELLED withEvent:theEvent];
   816 }
   817 
   818 - (void)handleTouches:(cocoaTouchType)type withEvent:(NSEvent *)event
   819 {
   820     NSSet *touches = 0;
   821     NSEnumerator *enumerator;
   822     NSTouch *touch;
   823 
   824     switch (type) {
   825         case COCOA_TOUCH_DOWN:
   826             touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:nil];
   827             break;
   828         case COCOA_TOUCH_UP:
   829             touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:nil];
   830             break;
   831         case COCOA_TOUCH_CANCELLED:
   832             touches = [event touchesMatchingPhase:NSTouchPhaseCancelled inView:nil];
   833             break;
   834         case COCOA_TOUCH_MOVE:
   835             touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:nil];
   836             break;
   837     }
   838 
   839     enumerator = [touches objectEnumerator];
   840     touch = (NSTouch*)[enumerator nextObject];
   841     while (touch) {
   842         const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device];
   843         if (!SDL_GetTouch(touchId)) {
   844             if (SDL_AddTouch(touchId, "") < 0) {
   845                 return;
   846             }
   847         }
   848 
   849         const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
   850         float x = [touch normalizedPosition].x;
   851         float y = [touch normalizedPosition].y;
   852         /* Make the origin the upper left instead of the lower left */
   853         y = 1.0f - y;
   854 
   855         switch (type) {
   856         case COCOA_TOUCH_DOWN:
   857             SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
   858             break;
   859         case COCOA_TOUCH_UP:
   860         case COCOA_TOUCH_CANCELLED:
   861             SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
   862             break;
   863         case COCOA_TOUCH_MOVE:
   864             SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
   865             break;
   866         }
   867 
   868         touch = (NSTouch*)[enumerator nextObject];
   869     }
   870 }
   871 
   872 @end
   873 
   874 @interface SDLView : NSView
   875 
   876 /* The default implementation doesn't pass rightMouseDown to responder chain */
   877 - (void)rightMouseDown:(NSEvent *)theEvent;
   878 @end
   879 
   880 @implementation SDLView
   881 - (void)rightMouseDown:(NSEvent *)theEvent
   882 {
   883     [[self nextResponder] rightMouseDown:theEvent];
   884 }
   885 
   886 - (void)resetCursorRects
   887 {
   888     [super resetCursorRects];
   889     SDL_Mouse *mouse = SDL_GetMouse();
   890 
   891     if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
   892         [self addCursorRect:[self bounds]
   893                      cursor:mouse->cur_cursor->driverdata];
   894     } else {
   895         [self addCursorRect:[self bounds]
   896                      cursor:[NSCursor invisibleCursor]];
   897     }
   898 }
   899 @end
   900 
   901 static int
   902 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
   903 {
   904     NSAutoreleasePool *pool;
   905     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   906     SDL_WindowData *data;
   907 
   908     /* Allocate the window data */
   909     data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
   910     if (!data) {
   911         return SDL_OutOfMemory();
   912     }
   913     data->window = window;
   914     data->nswindow = nswindow;
   915     data->created = created;
   916     data->videodata = videodata;
   917     data->nscontexts = [[NSMutableArray alloc] init];
   918 
   919     pool = [[NSAutoreleasePool alloc] init];
   920 
   921     /* Create an event listener for the window */
   922     data->listener = [[Cocoa_WindowListener alloc] init];
   923 
   924     /* Fill in the SDL window with the window data */
   925     {
   926         NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   927         ConvertNSRect(&rect);
   928         window->x = (int)rect.origin.x;
   929         window->y = (int)rect.origin.y;
   930         window->w = (int)rect.size.width;
   931         window->h = (int)rect.size.height;
   932     }
   933 
   934     /* Set up the listener after we create the view */
   935     [data->listener listen:data];
   936 
   937     if ([nswindow isVisible]) {
   938         window->flags |= SDL_WINDOW_SHOWN;
   939     } else {
   940         window->flags &= ~SDL_WINDOW_SHOWN;
   941     }
   942 
   943     {
   944         unsigned int style = [nswindow styleMask];
   945 
   946         if (style == NSBorderlessWindowMask) {
   947             window->flags |= SDL_WINDOW_BORDERLESS;
   948         } else {
   949             window->flags &= ~SDL_WINDOW_BORDERLESS;
   950         }
   951         if (style & NSResizableWindowMask) {
   952             window->flags |= SDL_WINDOW_RESIZABLE;
   953         } else {
   954             window->flags &= ~SDL_WINDOW_RESIZABLE;
   955         }
   956     }
   957 
   958     /* isZoomed always returns true if the window is not resizable */
   959     if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
   960         window->flags |= SDL_WINDOW_MAXIMIZED;
   961     } else {
   962         window->flags &= ~SDL_WINDOW_MAXIMIZED;
   963     }
   964 
   965     if ([nswindow isMiniaturized]) {
   966         window->flags |= SDL_WINDOW_MINIMIZED;
   967     } else {
   968         window->flags &= ~SDL_WINDOW_MINIMIZED;
   969     }
   970 
   971     if ([nswindow isKeyWindow]) {
   972         window->flags |= SDL_WINDOW_INPUT_FOCUS;
   973         SDL_SetKeyboardFocus(data->window);
   974     }
   975 
   976     /* Prevents the window's "window device" from being destroyed when it is
   977      * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
   978      */
   979     [nswindow setOneShot:NO];
   980 
   981     /* All done! */
   982     [pool release];
   983     window->driverdata = data;
   984     return 0;
   985 }
   986 
   987 int
   988 Cocoa_CreateWindow(_THIS, SDL_Window * window)
   989 {
   990     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   991     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   992     NSWindow *nswindow;
   993     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   994     NSRect rect;
   995     SDL_Rect bounds;
   996     unsigned int style;
   997 
   998     Cocoa_GetDisplayBounds(_this, display, &bounds);
   999     rect.origin.x = window->x;
  1000     rect.origin.y = window->y;
  1001     rect.size.width = window->w;
  1002     rect.size.height = window->h;
  1003     ConvertNSRect(&rect);
  1004 
  1005     style = GetWindowStyle(window);
  1006 
  1007     /* Figure out which screen to place this window */
  1008     NSArray *screens = [NSScreen screens];
  1009     NSScreen *screen = nil;
  1010     NSScreen *candidate;
  1011     int i, count = [screens count];
  1012     for (i = 0; i < count; ++i) {
  1013         candidate = [screens objectAtIndex:i];
  1014         NSRect screenRect = [candidate frame];
  1015         if (rect.origin.x >= screenRect.origin.x &&
  1016             rect.origin.x < screenRect.origin.x + screenRect.size.width &&
  1017             rect.origin.y >= screenRect.origin.y &&
  1018             rect.origin.y < screenRect.origin.y + screenRect.size.height) {
  1019             screen = candidate;
  1020             rect.origin.x -= screenRect.origin.x;
  1021             rect.origin.y -= screenRect.origin.y;
  1022         }
  1023     }
  1024 
  1025     @try {
  1026         nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
  1027     }
  1028     @catch (NSException *e) {
  1029         SDL_SetError("%s", [[e reason] UTF8String]);
  1030         [pool release];
  1031         return -1;
  1032     }
  1033     [nswindow setBackgroundColor:[NSColor blackColor]];
  1034 
  1035     if (videodata->allow_spaces) {
  1036         SDL_assert(videodata->osversion >= 0x1070);
  1037         SDL_assert([nswindow respondsToSelector:@selector(toggleFullScreen:)]);
  1038         /* we put FULLSCREEN_DESKTOP windows in their own Space, without a toggle button or menubar, later */
  1039         if (window->flags & SDL_WINDOW_RESIZABLE) {
  1040             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
  1041             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
  1042         }
  1043     }
  1044 
  1045     /* Create a default view for this window */
  1046     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1047     NSView *contentView = [[SDLView alloc] initWithFrame:rect];
  1048 
  1049     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
  1050         if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
  1051             [contentView setWantsBestResolutionOpenGLSurface:YES];
  1052         }
  1053     }
  1054 
  1055     [nswindow setContentView: contentView];
  1056     [contentView release];
  1057 
  1058     [pool release];
  1059 
  1060     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
  1061         [nswindow release];
  1062         return -1;
  1063     }
  1064     return 0;
  1065 }
  1066 
  1067 int
  1068 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
  1069 {
  1070     NSAutoreleasePool *pool;
  1071     NSWindow *nswindow = (NSWindow *) data;
  1072     NSString *title;
  1073 
  1074     pool = [[NSAutoreleasePool alloc] init];
  1075 
  1076     /* Query the title from the existing window */
  1077     title = [nswindow title];
  1078     if (title) {
  1079         window->title = SDL_strdup([title UTF8String]);
  1080     }
  1081 
  1082     [pool release];
  1083 
  1084     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
  1085 }
  1086 
  1087 void
  1088 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
  1089 {
  1090     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1091     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1092     NSString *string;
  1093 
  1094     if(window->title) {
  1095         string = [[NSString alloc] initWithUTF8String:window->title];
  1096     } else {
  1097         string = [[NSString alloc] init];
  1098     }
  1099     [nswindow setTitle:string];
  1100     [string release];
  1101 
  1102     [pool release];
  1103 }
  1104 
  1105 void
  1106 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
  1107 {
  1108     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1109     NSImage *nsimage = Cocoa_CreateImage(icon);
  1110 
  1111     if (nsimage) {
  1112         [NSApp setApplicationIconImage:nsimage];
  1113     }
  1114 
  1115     [pool release];
  1116 }
  1117 
  1118 void
  1119 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
  1120 {
  1121     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1122     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1123     NSWindow *nswindow = windata->nswindow;
  1124     NSRect rect;
  1125     Uint32 moveHack;
  1126 
  1127     rect.origin.x = window->x;
  1128     rect.origin.y = window->y;
  1129     rect.size.width = window->w;
  1130     rect.size.height = window->h;
  1131     ConvertNSRect(&rect);
  1132 
  1133     moveHack = s_moveHack;
  1134     s_moveHack = 0;
  1135     [nswindow setFrameOrigin:rect.origin];
  1136     s_moveHack = moveHack;
  1137 
  1138     ScheduleContextUpdates(windata);
  1139 
  1140     [pool release];
  1141 }
  1142 
  1143 void
  1144 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
  1145 {
  1146     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1147     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1148     NSWindow *nswindow = windata->nswindow;
  1149     NSSize size;
  1150 
  1151     size.width = window->w;
  1152     size.height = window->h;
  1153     [nswindow setContentSize:size];
  1154 
  1155     ScheduleContextUpdates(windata);
  1156 
  1157     [pool release];
  1158 }
  1159 
  1160 void
  1161 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
  1162 {
  1163     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1164     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1165 
  1166     NSSize minSize;
  1167     minSize.width = window->min_w;
  1168     minSize.height = window->min_h;
  1169 
  1170     [windata->nswindow setContentMinSize:minSize];
  1171 
  1172     [pool release];
  1173 }
  1174 
  1175 void
  1176 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
  1177 {
  1178     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1179     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1180 
  1181     NSSize maxSize;
  1182     maxSize.width = window->max_w;
  1183     maxSize.height = window->max_h;
  1184 
  1185     [windata->nswindow setContentMaxSize:maxSize];
  1186 
  1187     [pool release];
  1188 }
  1189 
  1190 void
  1191 Cocoa_ShowWindow(_THIS, SDL_Window * window)
  1192 {
  1193     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1194     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1195     NSWindow *nswindow = windowData->nswindow;
  1196 
  1197     if (![nswindow isMiniaturized]) {
  1198         [windowData->listener pauseVisibleObservation];
  1199         [nswindow makeKeyAndOrderFront:nil];
  1200         [windowData->listener resumeVisibleObservation];
  1201     }
  1202     [pool release];
  1203 }
  1204 
  1205 void
  1206 Cocoa_HideWindow(_THIS, SDL_Window * window)
  1207 {
  1208     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1209     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1210 
  1211     [nswindow orderOut:nil];
  1212     [pool release];
  1213 }
  1214 
  1215 void
  1216 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
  1217 {
  1218     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1219     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1220     NSWindow *nswindow = windowData->nswindow;
  1221 
  1222     /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
  1223        a minimized or hidden window, so check for that before showing it.
  1224      */
  1225     [windowData->listener pauseVisibleObservation];
  1226     if (![nswindow isMiniaturized] && [nswindow isVisible]) {
  1227         [nswindow makeKeyAndOrderFront:nil];
  1228     }
  1229     [windowData->listener resumeVisibleObservation];
  1230 
  1231     [pool release];
  1232 }
  1233 
  1234 void
  1235 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
  1236 {
  1237     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1238     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1239     NSWindow *nswindow = windata->nswindow;
  1240 
  1241     [nswindow zoom:nil];
  1242 
  1243     ScheduleContextUpdates(windata);
  1244 
  1245     [pool release];
  1246 }
  1247 
  1248 void
  1249 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
  1250 {
  1251     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1252     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1253     NSWindow *nswindow = data->nswindow;
  1254 
  1255     if ([data->listener isInFullscreenSpaceTransition]) {
  1256         [data->listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
  1257     } else {
  1258         [nswindow miniaturize:nil];
  1259     }
  1260     [pool release];
  1261 }
  1262 
  1263 void
  1264 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
  1265 {
  1266     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1267     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1268 
  1269     if ([nswindow isMiniaturized]) {
  1270         [nswindow deminiaturize:nil];
  1271     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1272         [nswindow zoom:nil];
  1273     }
  1274     [pool release];
  1275 }
  1276 
  1277 static NSWindow *
  1278 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
  1279 {
  1280     if (!data->created) {
  1281         /* Don't mess with other people's windows... */
  1282         return nswindow;
  1283     }
  1284 
  1285     [data->listener close];
  1286     data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
  1287     [data->nswindow setContentView:[nswindow contentView]];
  1288     /* See comment in SetupWindowData. */
  1289     [data->nswindow setOneShot:NO];
  1290     [data->listener listen:data];
  1291 
  1292     [nswindow close];
  1293 
  1294     return data->nswindow;
  1295 }
  1296 
  1297 void
  1298 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
  1299 {
  1300     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1301     if (SetWindowStyle(window, GetWindowStyle(window))) {
  1302         if (bordered) {
  1303             Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
  1304         }
  1305     }
  1306     [pool release];
  1307 }
  1308 
  1309 
  1310 void
  1311 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
  1312 {
  1313     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1314     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1315     NSWindow *nswindow = data->nswindow;
  1316     NSRect rect;
  1317 
  1318     /* The view responder chain gets messed with during setStyleMask */
  1319     if ([[nswindow contentView] nextResponder] == data->listener) {
  1320         [[nswindow contentView] setNextResponder:nil];
  1321     }
  1322 
  1323     if (fullscreen) {
  1324         SDL_Rect bounds;
  1325 
  1326         Cocoa_GetDisplayBounds(_this, display, &bounds);
  1327         rect.origin.x = bounds.x;
  1328         rect.origin.y = bounds.y;
  1329         rect.size.width = bounds.w;
  1330         rect.size.height = bounds.h;
  1331         ConvertNSRect(&rect);
  1332 
  1333         /* Hack to fix origin on Mac OS X 10.4 */
  1334         NSRect screenRect = [[nswindow screen] frame];
  1335         if (screenRect.size.height >= 1.0f) {
  1336             rect.origin.y += (screenRect.size.height - rect.size.height);
  1337         }
  1338 
  1339         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1340             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
  1341         } else {
  1342             nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
  1343         }
  1344     } else {
  1345         rect.origin.x = window->windowed.x;
  1346         rect.origin.y = window->windowed.y;
  1347         rect.size.width = window->windowed.w;
  1348         rect.size.height = window->windowed.h;
  1349         ConvertNSRect(&rect);
  1350 
  1351         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1352             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
  1353         } else {
  1354             nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
  1355         }
  1356     }
  1357 
  1358     /* The view responder chain gets messed with during setStyleMask */
  1359     if ([[nswindow contentView] nextResponder] != data->listener) {
  1360         [[nswindow contentView] setNextResponder:data->listener];
  1361     }
  1362 
  1363     s_moveHack = 0;
  1364     [nswindow setContentSize:rect.size];
  1365     [nswindow setFrameOrigin:rect.origin];
  1366     s_moveHack = SDL_GetTicks();
  1367 
  1368     /* When the window style changes the title is cleared */
  1369     if (!fullscreen) {
  1370         Cocoa_SetWindowTitle(_this, window);
  1371     }
  1372 
  1373     if (SDL_ShouldAllowTopmost() && fullscreen) {
  1374         /* OpenGL is rendering to the window, so make it visible! */
  1375         [nswindow setLevel:CGShieldingWindowLevel()];
  1376     } else {
  1377         [nswindow setLevel:kCGNormalWindowLevel];
  1378     }
  1379 
  1380     if ([nswindow isVisible] || fullscreen) {
  1381         [data->listener pauseVisibleObservation];
  1382         [nswindow makeKeyAndOrderFront:nil];
  1383         [data->listener resumeVisibleObservation];
  1384     }
  1385 
  1386     ScheduleContextUpdates(data);
  1387 
  1388     [pool release];
  1389 }
  1390 
  1391 int
  1392 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1393 {
  1394     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1395     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1396     const uint32_t tableSize = 256;
  1397     CGGammaValue redTable[tableSize];
  1398     CGGammaValue greenTable[tableSize];
  1399     CGGammaValue blueTable[tableSize];
  1400     uint32_t i;
  1401     float inv65535 = 1.0f / 65535.0f;
  1402 
  1403     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1404     for (i = 0; i < 256; i++) {
  1405         redTable[i] = ramp[0*256+i] * inv65535;
  1406         greenTable[i] = ramp[1*256+i] * inv65535;
  1407         blueTable[i] = ramp[2*256+i] * inv65535;
  1408     }
  1409 
  1410     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1411                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1412         return SDL_SetError("CGSetDisplayTransferByTable()");
  1413     }
  1414     return 0;
  1415 }
  1416 
  1417 int
  1418 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1419 {
  1420     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1421     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1422     const uint32_t tableSize = 256;
  1423     CGGammaValue redTable[tableSize];
  1424     CGGammaValue greenTable[tableSize];
  1425     CGGammaValue blueTable[tableSize];
  1426     uint32_t i, tableCopied;
  1427 
  1428     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1429                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1430         return SDL_SetError("CGGetDisplayTransferByTable()");
  1431     }
  1432 
  1433     for (i = 0; i < tableCopied; i++) {
  1434         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1435         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1436         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1437     }
  1438     return 0;
  1439 }
  1440 
  1441 void
  1442 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1443 {
  1444     /* Move the cursor to the nearest point in the window */
  1445     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1446     if (grabbed && data && ![data->listener isMoving]) {
  1447         int x, y;
  1448         CGPoint cgpoint;
  1449 
  1450         SDL_GetMouseState(&x, &y);
  1451         cgpoint.x = window->x + x;
  1452         cgpoint.y = window->y + y;
  1453 
  1454         Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
  1455 
  1456         DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
  1457         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1458     }
  1459 
  1460     if ( window->flags & SDL_WINDOW_FULLSCREEN ) {
  1461         SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1462 
  1463         if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1464             /* OpenGL is rendering to the window, so make it visible! */
  1465             [data->nswindow setLevel:CGShieldingWindowLevel()];
  1466         } else {
  1467             [data->nswindow setLevel:kCGNormalWindowLevel];
  1468         }
  1469     }
  1470 }
  1471 
  1472 void
  1473 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1474 {
  1475     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1476     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1477 
  1478     if (data) {
  1479         [data->listener close];
  1480         [data->listener release];
  1481         if (data->created) {
  1482             [data->nswindow close];
  1483         }
  1484 
  1485         NSArray *contexts = [[data->nscontexts copy] autorelease];
  1486         for (SDLOpenGLContext *context in contexts) {
  1487             /* Calling setWindow:NULL causes the context to remove itself from the context list. */            
  1488             [context setWindow:NULL];
  1489         }
  1490         [data->nscontexts release];
  1491 
  1492         SDL_free(data);
  1493     }
  1494     [pool release];
  1495 }
  1496 
  1497 SDL_bool
  1498 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1499 {
  1500     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1501 
  1502     if (info->version.major <= SDL_MAJOR_VERSION) {
  1503         info->subsystem = SDL_SYSWM_COCOA;
  1504         info->info.cocoa.window = nswindow;
  1505         return SDL_TRUE;
  1506     } else {
  1507         SDL_SetError("Application not compiled with SDL %d.%d\n",
  1508                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1509         return SDL_FALSE;
  1510     }
  1511 }
  1512 
  1513 SDL_bool
  1514 Cocoa_IsWindowInFullscreenSpace(SDL_Window * window)
  1515 {
  1516     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1517 
  1518     if ([data->listener isInFullscreenSpace]) {
  1519         return SDL_TRUE;
  1520     } else {
  1521         return SDL_FALSE;
  1522     }
  1523 }
  1524 
  1525 SDL_bool
  1526 Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
  1527 {
  1528     SDL_bool succeeded = SDL_FALSE;
  1529     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1530     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1531 
  1532     if ([data->listener setFullscreenSpace:(state ? YES : NO)]) {
  1533         succeeded = SDL_TRUE;
  1534     }
  1535 
  1536     [pool release];
  1537 
  1538     return succeeded;
  1539 }
  1540 
  1541 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1542 
  1543 /* vi: set ts=4 sw=4 expandtab: */