src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Sun, 02 Mar 2014 22:51:31 -0500
changeset 8293 b96005b05c90
parent 8292 2b0736ab4d5f
child 8294 7371078ee876
permissions -rw-r--r--
Mac: Don't lose the menu/dock when switching back to a resizable window Space.
     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 
    42 /* #define DEBUG_COCOAWINDOW */
    43 
    44 #ifdef DEBUG_COCOAWINDOW
    45 #define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
    46 #else
    47 #define DLog(...) do { } while (0)
    48 #endif
    49 
    50 
    51 @interface SDLWindow : NSWindow
    52 /* These are needed for borderless/fullscreen windows */
    53 - (BOOL)canBecomeKeyWindow;
    54 - (BOOL)canBecomeMainWindow;
    55 - (void)sendEvent:(NSEvent *)event;
    56 @end
    57 
    58 @implementation SDLWindow
    59 - (BOOL)canBecomeKeyWindow
    60 {
    61     return YES;
    62 }
    63 
    64 - (BOOL)canBecomeMainWindow
    65 {
    66     return YES;
    67 }
    68 
    69 - (void)sendEvent:(NSEvent *)event
    70 {
    71   [super sendEvent:event];
    72 
    73   if ([event type] != NSLeftMouseUp) {
    74       return;
    75   }
    76 
    77   id delegate = [self delegate];
    78   if (![delegate isKindOfClass:[Cocoa_WindowListener class]]) {
    79       return;
    80   }
    81 
    82   if ([delegate isMoving]) {
    83       [delegate windowDidFinishMoving];
    84   }
    85 }
    86 @end
    87 
    88 
    89 static Uint32 s_moveHack;
    90 
    91 static void ConvertNSRect(NSRect *r)
    92 {
    93     r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
    94 }
    95 
    96 static void
    97 ScheduleContextUpdates(SDL_WindowData *data)
    98 {
    99     NSOpenGLContext *currentContext = [NSOpenGLContext currentContext];
   100     NSMutableArray *contexts = data->nscontexts;
   101     @synchronized (contexts) {
   102         for (SDLOpenGLContext *context in contexts) {
   103             if (context == currentContext) {
   104                 [context update];
   105             } else {
   106                 [context scheduleUpdate];
   107             }
   108         }
   109     }
   110 }
   111 
   112 static int
   113 GetHintCtrlClickEmulateRightClick()
   114 {
   115 	const char *hint = SDL_GetHint( SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK );
   116 	return hint != NULL && *hint != '0';
   117 }
   118 
   119 static unsigned int
   120 GetWindowStyle(SDL_Window * window)
   121 {
   122     unsigned int style;
   123 
   124     if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
   125         style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
   126     } else if (window->flags & SDL_WINDOW_FULLSCREEN) {
   127         style = NSBorderlessWindowMask;
   128     } else {
   129         if (window->flags & SDL_WINDOW_BORDERLESS) {
   130             style = NSBorderlessWindowMask;
   131         } else {
   132             style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
   133         }
   134         if (window->flags & SDL_WINDOW_RESIZABLE) {
   135             style |= NSResizableWindowMask;
   136         }
   137     }
   138     return style;
   139 }
   140 
   141 static SDL_bool
   142 SetWindowStyle(SDL_Window * window, unsigned int style)
   143 {
   144     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   145     NSWindow *nswindow = data->nswindow;
   146 
   147     if (![nswindow respondsToSelector: @selector(setStyleMask:)]) {
   148         return SDL_FALSE;
   149     }
   150 
   151     /* The view responder chain gets messed with during setStyleMask */
   152     if ([[nswindow contentView] nextResponder] == data->listener) {
   153         [[nswindow contentView] setNextResponder:nil];
   154     }
   155 
   156     [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)style];
   157 
   158     /* The view responder chain gets messed with during setStyleMask */
   159     if ([[nswindow contentView] nextResponder] != data->listener) {
   160         [[nswindow contentView] setNextResponder:data->listener];
   161     }
   162 
   163     return SDL_TRUE;
   164 }
   165 
   166 
   167 @implementation Cocoa_WindowListener
   168 
   169 - (void)listen:(SDL_WindowData *)data
   170 {
   171     NSNotificationCenter *center;
   172     NSWindow *window = data->nswindow;
   173     NSView *view = [window contentView];
   174 
   175     _data = data;
   176     observingVisible = YES;
   177     wasCtrlLeft = NO;
   178     wasVisible = [window isVisible];
   179     isFullscreenSpace = NO;
   180     inFullscreenTransition = NO;
   181     pendingWindowOperation = PENDING_OPERATION_NONE;
   182     isMoving = NO;
   183 
   184     center = [NSNotificationCenter defaultCenter];
   185 
   186     if ([window delegate] != nil) {
   187         [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window];
   188         [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window];
   189         [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window];
   190         [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
   191         [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
   192         [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
   193         [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window];
   194         [center addObserver:self selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:window];
   195         [center addObserver:self selector:@selector(windowDidEnterFullScreen:) name:NSWindowDidEnterFullScreenNotification object:window];
   196         [center addObserver:self selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:window];
   197         [center addObserver:self selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:window];
   198     } else {
   199         [window setDelegate:self];
   200     }
   201 
   202     /* Haven't found a delegate / notification that triggers when the window is
   203      * ordered out (is not visible any more). You can be ordered out without
   204      * minimizing, so DidMiniaturize doesn't work. (e.g. -[NSWindow orderOut:])
   205      */
   206     [window addObserver:self
   207              forKeyPath:@"visible"
   208                 options:NSKeyValueObservingOptionNew
   209                 context:NULL];
   210 
   211     [window setNextResponder:self];
   212     [window setAcceptsMouseMovedEvents:YES];
   213 
   214     [view setNextResponder:self];
   215 
   216     if ([view respondsToSelector:@selector(setAcceptsTouchEvents:)]) {
   217         [view setAcceptsTouchEvents:YES];
   218     }
   219 }
   220 
   221 - (void)observeValueForKeyPath:(NSString *)keyPath
   222                       ofObject:(id)object
   223                         change:(NSDictionary *)change
   224                        context:(void *)context
   225 {
   226     if (!observingVisible) {
   227         return;
   228     }
   229 
   230     if (object == _data->nswindow && [keyPath isEqualToString:@"visible"]) {
   231         int newVisibility = [[change objectForKey:@"new"] intValue];
   232         if (newVisibility) {
   233             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   234         } else {
   235             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   236         }
   237     }
   238 }
   239 
   240 -(void) pauseVisibleObservation
   241 {
   242     observingVisible = NO;
   243     wasVisible = [_data->nswindow isVisible];
   244 }
   245 
   246 -(void) resumeVisibleObservation
   247 {
   248     BOOL isVisible = [_data->nswindow isVisible];
   249     observingVisible = YES;
   250     if (wasVisible != isVisible) {
   251         if (isVisible) {
   252             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   253         } else {
   254             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   255         }
   256 
   257         wasVisible = isVisible;
   258     }
   259 }
   260 
   261 -(BOOL) setFullscreenSpace:(BOOL) state
   262 {
   263     SDL_Window *window = _data->window;
   264     NSWindow *nswindow = _data->nswindow;
   265 
   266     if (state && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) {
   267         return NO;  /* we only allow you to make a Space on FULLSCREEN_DESKTOP windows. */
   268     } else if (![nswindow respondsToSelector: @selector(toggleFullScreen:)]) {
   269         return NO;  /* No Spaces support? Older Mac OS X? */
   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 windowDid[Enter|Exit]FullScreen. */
   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     NSWindow *nswindow = _data->nswindow;
   553 
   554     inFullscreenTransition = NO;
   555 
   556     if (pendingWindowOperation == PENDING_OPERATION_LEAVE_FULLSCREEN) {
   557         pendingWindowOperation = PENDING_OPERATION_NONE;
   558         [self setFullscreenSpace:NO];
   559     } else {
   560         if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
   561             [NSMenu setMenuBarVisible:NO];
   562         }
   563 
   564         pendingWindowOperation = PENDING_OPERATION_NONE;
   565         /* Force the size change event in case it was delivered earlier
   566            while the window was still animating into place.
   567          */
   568         window->w = 0;
   569         window->h = 0;
   570         [self windowDidResize:aNotification];
   571     }
   572 }
   573 
   574 - (void)windowWillExitFullScreen:(NSNotification *)aNotification
   575 {
   576     SDL_Window *window = _data->window;
   577 
   578     SetWindowStyle(window, GetWindowStyle(window));
   579 
   580     isFullscreenSpace = NO;
   581     inFullscreenTransition = YES;
   582 }
   583 
   584 - (void)windowDidExitFullScreen:(NSNotification *)aNotification
   585 {
   586     SDL_Window *window = _data->window;
   587     NSWindow *nswindow = _data->nswindow;
   588 
   589     inFullscreenTransition = NO;
   590 
   591     if (pendingWindowOperation == PENDING_OPERATION_ENTER_FULLSCREEN) {
   592         pendingWindowOperation = PENDING_OPERATION_NONE;
   593         [self setFullscreenSpace:YES];
   594     } else if (pendingWindowOperation == PENDING_OPERATION_MINIMIZE) {
   595         pendingWindowOperation = PENDING_OPERATION_NONE;
   596         [nswindow miniaturize:nil];
   597     } else {
   598         /* Adjust the fullscreen toggle button and readd menu now that we're here. */
   599         if (window->flags & SDL_WINDOW_RESIZABLE) {
   600             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
   601             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
   602         } else {
   603             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorManaged];
   604         }
   605         [NSMenu setMenuBarVisible:YES];
   606 
   607         pendingWindowOperation = PENDING_OPERATION_NONE;
   608         /* Force the size change event in case it was delivered earlier
   609            while the window was still animating into place.
   610          */
   611         window->w = 0;
   612         window->h = 0;
   613         [self windowDidResize:aNotification];
   614     }
   615 }
   616 
   617 -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
   618 {
   619     if ((_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
   620         return NSApplicationPresentationFullScreen | NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar;
   621     } else {
   622         return proposedOptions;
   623     }
   624 }
   625 
   626 
   627 /* We'll respond to key events by doing nothing so we don't beep.
   628  * We could handle key messages here, but we lose some in the NSApp dispatch,
   629  * where they get converted to action messages, etc.
   630  */
   631 - (void)flagsChanged:(NSEvent *)theEvent
   632 {
   633     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   634 }
   635 - (void)keyDown:(NSEvent *)theEvent
   636 {
   637     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   638 }
   639 - (void)keyUp:(NSEvent *)theEvent
   640 {
   641     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   642 }
   643 
   644 /* We'll respond to selectors by doing nothing so we don't beep.
   645  * The escape key gets converted to a "cancel" selector, etc.
   646  */
   647 - (void)doCommandBySelector:(SEL)aSelector
   648 {
   649     /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
   650 }
   651 
   652 - (void)mouseDown:(NSEvent *)theEvent
   653 {
   654     int button;
   655 
   656     switch ([theEvent buttonNumber]) {
   657     case 0:
   658         if (([theEvent modifierFlags] & NSControlKeyMask) &&
   659 		    GetHintCtrlClickEmulateRightClick()) {
   660             wasCtrlLeft = YES;
   661             button = SDL_BUTTON_RIGHT;
   662         } else {
   663             wasCtrlLeft = NO;
   664             button = SDL_BUTTON_LEFT;
   665         }
   666         break;
   667     case 1:
   668         button = SDL_BUTTON_RIGHT;
   669         break;
   670     case 2:
   671         button = SDL_BUTTON_MIDDLE;
   672         break;
   673     default:
   674         button = [theEvent buttonNumber] + 1;
   675         break;
   676     }
   677     SDL_SendMouseButton(_data->window, 0, SDL_PRESSED, button);
   678 }
   679 
   680 - (void)rightMouseDown:(NSEvent *)theEvent
   681 {
   682     [self mouseDown:theEvent];
   683 }
   684 
   685 - (void)otherMouseDown:(NSEvent *)theEvent
   686 {
   687     [self mouseDown:theEvent];
   688 }
   689 
   690 - (void)mouseUp:(NSEvent *)theEvent
   691 {
   692     int button;
   693 
   694     switch ([theEvent buttonNumber]) {
   695     case 0:
   696         if (wasCtrlLeft) {
   697             button = SDL_BUTTON_RIGHT;
   698             wasCtrlLeft = NO;
   699         } else {
   700             button = SDL_BUTTON_LEFT;
   701         }
   702         break;
   703     case 1:
   704         button = SDL_BUTTON_RIGHT;
   705         break;
   706     case 2:
   707         button = SDL_BUTTON_MIDDLE;
   708         break;
   709     default:
   710         button = [theEvent buttonNumber] + 1;
   711         break;
   712     }
   713     SDL_SendMouseButton(_data->window, 0, SDL_RELEASED, button);
   714 }
   715 
   716 - (void)rightMouseUp:(NSEvent *)theEvent
   717 {
   718     [self mouseUp:theEvent];
   719 }
   720 
   721 - (void)otherMouseUp:(NSEvent *)theEvent
   722 {
   723     [self mouseUp:theEvent];
   724 }
   725 
   726 - (void)mouseMoved:(NSEvent *)theEvent
   727 {
   728     SDL_Mouse *mouse = SDL_GetMouse();
   729     SDL_Window *window = _data->window;
   730     NSPoint point;
   731     int x, y;
   732 
   733     if (mouse->relative_mode) {
   734         return;
   735     }
   736 
   737     point = [theEvent locationInWindow];
   738     x = (int)point.x;
   739     y = (int)(window->h - point.y);
   740 
   741     if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
   742         if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   743             if (x < 0) {
   744                 x = 0;
   745             } else if (x >= window->w) {
   746                 x = window->w - 1;
   747             }
   748             if (y < 0) {
   749                 y = 0;
   750             } else if (y >= window->h) {
   751                 y = window->h - 1;
   752             }
   753 
   754 #if !SDL_MAC_NO_SANDBOX
   755             CGPoint cgpoint;
   756 
   757             /* When SDL_MAC_NO_SANDBOX is set, this is handled by
   758              * SDL_cocoamousetap.m.
   759              */
   760 
   761             cgpoint.x = window->x + x;
   762             cgpoint.y = window->y + y;
   763 
   764             /* According to the docs, this was deprecated in 10.6, but it's still
   765              * around. The substitute requires a CGEventSource, but I'm not entirely
   766              * sure how we'd procure the right one for this event.
   767              */
   768             CGSetLocalEventsSuppressionInterval(0.0);
   769             CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
   770             CGSetLocalEventsSuppressionInterval(0.25);
   771 
   772             Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
   773 #endif
   774         }
   775     }
   776     SDL_SendMouseMotion(window, 0, 0, x, y);
   777 }
   778 
   779 - (void)mouseDragged:(NSEvent *)theEvent
   780 {
   781     [self mouseMoved:theEvent];
   782 }
   783 
   784 - (void)rightMouseDragged:(NSEvent *)theEvent
   785 {
   786     [self mouseMoved:theEvent];
   787 }
   788 
   789 - (void)otherMouseDragged:(NSEvent *)theEvent
   790 {
   791     [self mouseMoved:theEvent];
   792 }
   793 
   794 - (void)scrollWheel:(NSEvent *)theEvent
   795 {
   796     Cocoa_HandleMouseWheel(_data->window, theEvent);
   797 }
   798 
   799 - (void)touchesBeganWithEvent:(NSEvent *) theEvent
   800 {
   801     [self handleTouches:COCOA_TOUCH_DOWN withEvent:theEvent];
   802 }
   803 
   804 - (void)touchesMovedWithEvent:(NSEvent *) theEvent
   805 {
   806     [self handleTouches:COCOA_TOUCH_MOVE withEvent:theEvent];
   807 }
   808 
   809 - (void)touchesEndedWithEvent:(NSEvent *) theEvent
   810 {
   811     [self handleTouches:COCOA_TOUCH_UP withEvent:theEvent];
   812 }
   813 
   814 - (void)touchesCancelledWithEvent:(NSEvent *) theEvent
   815 {
   816     [self handleTouches:COCOA_TOUCH_CANCELLED withEvent:theEvent];
   817 }
   818 
   819 - (void)handleTouches:(cocoaTouchType)type withEvent:(NSEvent *)event
   820 {
   821     NSSet *touches = 0;
   822     NSEnumerator *enumerator;
   823     NSTouch *touch;
   824 
   825     switch (type) {
   826         case COCOA_TOUCH_DOWN:
   827             touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:nil];
   828             break;
   829         case COCOA_TOUCH_UP:
   830             touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:nil];
   831             break;
   832         case COCOA_TOUCH_CANCELLED:
   833             touches = [event touchesMatchingPhase:NSTouchPhaseCancelled inView:nil];
   834             break;
   835         case COCOA_TOUCH_MOVE:
   836             touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:nil];
   837             break;
   838     }
   839 
   840     enumerator = [touches objectEnumerator];
   841     touch = (NSTouch*)[enumerator nextObject];
   842     while (touch) {
   843         const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device];
   844         if (!SDL_GetTouch(touchId)) {
   845             if (SDL_AddTouch(touchId, "") < 0) {
   846                 return;
   847             }
   848         }
   849 
   850         const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
   851         float x = [touch normalizedPosition].x;
   852         float y = [touch normalizedPosition].y;
   853         /* Make the origin the upper left instead of the lower left */
   854         y = 1.0f - y;
   855 
   856         switch (type) {
   857         case COCOA_TOUCH_DOWN:
   858             SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
   859             break;
   860         case COCOA_TOUCH_UP:
   861         case COCOA_TOUCH_CANCELLED:
   862             SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
   863             break;
   864         case COCOA_TOUCH_MOVE:
   865             SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
   866             break;
   867         }
   868 
   869         touch = (NSTouch*)[enumerator nextObject];
   870     }
   871 }
   872 
   873 @end
   874 
   875 @interface SDLView : NSView
   876 
   877 /* The default implementation doesn't pass rightMouseDown to responder chain */
   878 - (void)rightMouseDown:(NSEvent *)theEvent;
   879 @end
   880 
   881 @implementation SDLView
   882 - (void)rightMouseDown:(NSEvent *)theEvent
   883 {
   884     [[self nextResponder] rightMouseDown:theEvent];
   885 }
   886 
   887 - (void)resetCursorRects
   888 {
   889     [super resetCursorRects];
   890     SDL_Mouse *mouse = SDL_GetMouse();
   891 
   892     if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
   893         [self addCursorRect:[self bounds]
   894                      cursor:mouse->cur_cursor->driverdata];
   895     } else {
   896         [self addCursorRect:[self bounds]
   897                      cursor:[NSCursor invisibleCursor]];
   898     }
   899 }
   900 @end
   901 
   902 static int
   903 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
   904 {
   905     NSAutoreleasePool *pool;
   906     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   907     SDL_WindowData *data;
   908 
   909     /* Allocate the window data */
   910     data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
   911     if (!data) {
   912         return SDL_OutOfMemory();
   913     }
   914     data->window = window;
   915     data->nswindow = nswindow;
   916     data->created = created;
   917     data->videodata = videodata;
   918     data->nscontexts = [[NSMutableArray alloc] init];
   919 
   920     pool = [[NSAutoreleasePool alloc] init];
   921 
   922     /* Create an event listener for the window */
   923     data->listener = [[Cocoa_WindowListener alloc] init];
   924 
   925     /* Fill in the SDL window with the window data */
   926     {
   927         NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   928         ConvertNSRect(&rect);
   929         window->x = (int)rect.origin.x;
   930         window->y = (int)rect.origin.y;
   931         window->w = (int)rect.size.width;
   932         window->h = (int)rect.size.height;
   933     }
   934 
   935     /* Set up the listener after we create the view */
   936     [data->listener listen:data];
   937 
   938     if ([nswindow isVisible]) {
   939         window->flags |= SDL_WINDOW_SHOWN;
   940     } else {
   941         window->flags &= ~SDL_WINDOW_SHOWN;
   942     }
   943 
   944     {
   945         unsigned int style = [nswindow styleMask];
   946 
   947         if (style == NSBorderlessWindowMask) {
   948             window->flags |= SDL_WINDOW_BORDERLESS;
   949         } else {
   950             window->flags &= ~SDL_WINDOW_BORDERLESS;
   951         }
   952         if (style & NSResizableWindowMask) {
   953             window->flags |= SDL_WINDOW_RESIZABLE;
   954         } else {
   955             window->flags &= ~SDL_WINDOW_RESIZABLE;
   956         }
   957     }
   958 
   959     /* isZoomed always returns true if the window is not resizable */
   960     if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
   961         window->flags |= SDL_WINDOW_MAXIMIZED;
   962     } else {
   963         window->flags &= ~SDL_WINDOW_MAXIMIZED;
   964     }
   965 
   966     if ([nswindow isMiniaturized]) {
   967         window->flags |= SDL_WINDOW_MINIMIZED;
   968     } else {
   969         window->flags &= ~SDL_WINDOW_MINIMIZED;
   970     }
   971 
   972     if ([nswindow isKeyWindow]) {
   973         window->flags |= SDL_WINDOW_INPUT_FOCUS;
   974         SDL_SetKeyboardFocus(data->window);
   975     }
   976 
   977     /* Prevents the window's "window device" from being destroyed when it is
   978      * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
   979      */
   980     [nswindow setOneShot:NO];
   981 
   982     /* All done! */
   983     [pool release];
   984     window->driverdata = data;
   985     return 0;
   986 }
   987 
   988 int
   989 Cocoa_CreateWindow(_THIS, SDL_Window * window)
   990 {
   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 ([nswindow respondsToSelector: @selector(toggleFullScreen:)]) {
  1036         /* we put FULLSCREEN_DESKTOP windows in their own Space, without a toggle button or menubar, later */
  1037         if (window->flags & SDL_WINDOW_RESIZABLE) {
  1038             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
  1039             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
  1040         }
  1041     }
  1042 
  1043     /* Create a default view for this window */
  1044     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1045     NSView *contentView = [[SDLView alloc] initWithFrame:rect];
  1046 
  1047     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
  1048         if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
  1049             [contentView setWantsBestResolutionOpenGLSurface:YES];
  1050         }
  1051     }
  1052 
  1053     [nswindow setContentView: contentView];
  1054     [contentView release];
  1055 
  1056     [pool release];
  1057 
  1058     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
  1059         [nswindow release];
  1060         return -1;
  1061     }
  1062     return 0;
  1063 }
  1064 
  1065 int
  1066 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
  1067 {
  1068     NSAutoreleasePool *pool;
  1069     NSWindow *nswindow = (NSWindow *) data;
  1070     NSString *title;
  1071 
  1072     pool = [[NSAutoreleasePool alloc] init];
  1073 
  1074     /* Query the title from the existing window */
  1075     title = [nswindow title];
  1076     if (title) {
  1077         window->title = SDL_strdup([title UTF8String]);
  1078     }
  1079 
  1080     [pool release];
  1081 
  1082     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
  1083 }
  1084 
  1085 void
  1086 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
  1087 {
  1088     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1089     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1090     NSString *string;
  1091 
  1092     if(window->title) {
  1093         string = [[NSString alloc] initWithUTF8String:window->title];
  1094     } else {
  1095         string = [[NSString alloc] init];
  1096     }
  1097     [nswindow setTitle:string];
  1098     [string release];
  1099 
  1100     [pool release];
  1101 }
  1102 
  1103 void
  1104 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
  1105 {
  1106     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1107     NSImage *nsimage = Cocoa_CreateImage(icon);
  1108 
  1109     if (nsimage) {
  1110         [NSApp setApplicationIconImage:nsimage];
  1111     }
  1112 
  1113     [pool release];
  1114 }
  1115 
  1116 void
  1117 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
  1118 {
  1119     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1120     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1121     NSWindow *nswindow = windata->nswindow;
  1122     NSRect rect;
  1123     Uint32 moveHack;
  1124 
  1125     rect.origin.x = window->x;
  1126     rect.origin.y = window->y;
  1127     rect.size.width = window->w;
  1128     rect.size.height = window->h;
  1129     ConvertNSRect(&rect);
  1130 
  1131     moveHack = s_moveHack;
  1132     s_moveHack = 0;
  1133     [nswindow setFrameOrigin:rect.origin];
  1134     s_moveHack = moveHack;
  1135 
  1136     ScheduleContextUpdates(windata);
  1137 
  1138     [pool release];
  1139 }
  1140 
  1141 void
  1142 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
  1143 {
  1144     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1145     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1146     NSWindow *nswindow = windata->nswindow;
  1147     NSSize size;
  1148 
  1149     size.width = window->w;
  1150     size.height = window->h;
  1151     [nswindow setContentSize:size];
  1152 
  1153     ScheduleContextUpdates(windata);
  1154 
  1155     [pool release];
  1156 }
  1157 
  1158 void
  1159 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
  1160 {
  1161     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1162     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1163 
  1164     NSSize minSize;
  1165     minSize.width = window->min_w;
  1166     minSize.height = window->min_h;
  1167 
  1168     [windata->nswindow setContentMinSize:minSize];
  1169 
  1170     [pool release];
  1171 }
  1172 
  1173 void
  1174 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
  1175 {
  1176     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1177     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1178 
  1179     NSSize maxSize;
  1180     maxSize.width = window->max_w;
  1181     maxSize.height = window->max_h;
  1182 
  1183     [windata->nswindow setContentMaxSize:maxSize];
  1184 
  1185     [pool release];
  1186 }
  1187 
  1188 void
  1189 Cocoa_ShowWindow(_THIS, SDL_Window * window)
  1190 {
  1191     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1192     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1193     NSWindow *nswindow = windowData->nswindow;
  1194 
  1195     if (![nswindow isMiniaturized]) {
  1196         [windowData->listener pauseVisibleObservation];
  1197         [nswindow makeKeyAndOrderFront:nil];
  1198         [windowData->listener resumeVisibleObservation];
  1199     }
  1200     [pool release];
  1201 }
  1202 
  1203 void
  1204 Cocoa_HideWindow(_THIS, SDL_Window * window)
  1205 {
  1206     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1207     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1208 
  1209     [nswindow orderOut:nil];
  1210     [pool release];
  1211 }
  1212 
  1213 void
  1214 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
  1215 {
  1216     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1217     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1218     NSWindow *nswindow = windowData->nswindow;
  1219 
  1220     /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
  1221        a minimized or hidden window, so check for that before showing it.
  1222      */
  1223     [windowData->listener pauseVisibleObservation];
  1224     if (![nswindow isMiniaturized] && [nswindow isVisible]) {
  1225         [nswindow makeKeyAndOrderFront:nil];
  1226     }
  1227     [windowData->listener resumeVisibleObservation];
  1228 
  1229     [pool release];
  1230 }
  1231 
  1232 void
  1233 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
  1234 {
  1235     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1236     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1237     NSWindow *nswindow = windata->nswindow;
  1238 
  1239     [nswindow zoom:nil];
  1240 
  1241     ScheduleContextUpdates(windata);
  1242 
  1243     [pool release];
  1244 }
  1245 
  1246 void
  1247 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
  1248 {
  1249     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1250     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1251     NSWindow *nswindow = data->nswindow;
  1252 
  1253     if ([data->listener isInFullscreenSpaceTransition]) {
  1254         [data->listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
  1255     } else {
  1256         [nswindow miniaturize:nil];
  1257     }
  1258     [pool release];
  1259 }
  1260 
  1261 void
  1262 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
  1263 {
  1264     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1265     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1266 
  1267     if ([nswindow isMiniaturized]) {
  1268         [nswindow deminiaturize:nil];
  1269     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1270         [nswindow zoom:nil];
  1271     }
  1272     [pool release];
  1273 }
  1274 
  1275 static NSWindow *
  1276 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
  1277 {
  1278     if (!data->created) {
  1279         /* Don't mess with other people's windows... */
  1280         return nswindow;
  1281     }
  1282 
  1283     [data->listener close];
  1284     data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
  1285     [data->nswindow setContentView:[nswindow contentView]];
  1286     /* See comment in SetupWindowData. */
  1287     [data->nswindow setOneShot:NO];
  1288     [data->listener listen:data];
  1289 
  1290     [nswindow close];
  1291 
  1292     return data->nswindow;
  1293 }
  1294 
  1295 void
  1296 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
  1297 {
  1298     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1299     if (SetWindowStyle(window, GetWindowStyle(window))) {
  1300         if (bordered) {
  1301             Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
  1302         }
  1303     }
  1304     [pool release];
  1305 }
  1306 
  1307 
  1308 void
  1309 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
  1310 {
  1311     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1312     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1313     NSWindow *nswindow = data->nswindow;
  1314     NSRect rect;
  1315 
  1316     /* The view responder chain gets messed with during setStyleMask */
  1317     if ([[nswindow contentView] nextResponder] == data->listener) {
  1318         [[nswindow contentView] setNextResponder:nil];
  1319     }
  1320 
  1321     if (fullscreen) {
  1322         SDL_Rect bounds;
  1323 
  1324         Cocoa_GetDisplayBounds(_this, display, &bounds);
  1325         rect.origin.x = bounds.x;
  1326         rect.origin.y = bounds.y;
  1327         rect.size.width = bounds.w;
  1328         rect.size.height = bounds.h;
  1329         ConvertNSRect(&rect);
  1330 
  1331         /* Hack to fix origin on Mac OS X 10.4 */
  1332         NSRect screenRect = [[nswindow screen] frame];
  1333         if (screenRect.size.height >= 1.0f) {
  1334             rect.origin.y += (screenRect.size.height - rect.size.height);
  1335         }
  1336 
  1337         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1338             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
  1339         } else {
  1340             nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
  1341         }
  1342     } else {
  1343         rect.origin.x = window->windowed.x;
  1344         rect.origin.y = window->windowed.y;
  1345         rect.size.width = window->windowed.w;
  1346         rect.size.height = window->windowed.h;
  1347         ConvertNSRect(&rect);
  1348 
  1349         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1350             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
  1351         } else {
  1352             nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
  1353         }
  1354     }
  1355 
  1356     /* The view responder chain gets messed with during setStyleMask */
  1357     if ([[nswindow contentView] nextResponder] != data->listener) {
  1358         [[nswindow contentView] setNextResponder:data->listener];
  1359     }
  1360 
  1361     s_moveHack = 0;
  1362     [nswindow setContentSize:rect.size];
  1363     [nswindow setFrameOrigin:rect.origin];
  1364     s_moveHack = SDL_GetTicks();
  1365 
  1366     /* When the window style changes the title is cleared */
  1367     if (!fullscreen) {
  1368         Cocoa_SetWindowTitle(_this, window);
  1369     }
  1370 
  1371     if (SDL_ShouldAllowTopmost() && fullscreen) {
  1372         /* OpenGL is rendering to the window, so make it visible! */
  1373         [nswindow setLevel:CGShieldingWindowLevel()];
  1374     } else {
  1375         [nswindow setLevel:kCGNormalWindowLevel];
  1376     }
  1377 
  1378     if ([nswindow isVisible] || fullscreen) {
  1379         [data->listener pauseVisibleObservation];
  1380         [nswindow makeKeyAndOrderFront:nil];
  1381         [data->listener resumeVisibleObservation];
  1382     }
  1383 
  1384     ScheduleContextUpdates(data);
  1385 
  1386     [pool release];
  1387 }
  1388 
  1389 int
  1390 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1391 {
  1392     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1393     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1394     const uint32_t tableSize = 256;
  1395     CGGammaValue redTable[tableSize];
  1396     CGGammaValue greenTable[tableSize];
  1397     CGGammaValue blueTable[tableSize];
  1398     uint32_t i;
  1399     float inv65535 = 1.0f / 65535.0f;
  1400 
  1401     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1402     for (i = 0; i < 256; i++) {
  1403         redTable[i] = ramp[0*256+i] * inv65535;
  1404         greenTable[i] = ramp[1*256+i] * inv65535;
  1405         blueTable[i] = ramp[2*256+i] * inv65535;
  1406     }
  1407 
  1408     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1409                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1410         return SDL_SetError("CGSetDisplayTransferByTable()");
  1411     }
  1412     return 0;
  1413 }
  1414 
  1415 int
  1416 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1417 {
  1418     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1419     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1420     const uint32_t tableSize = 256;
  1421     CGGammaValue redTable[tableSize];
  1422     CGGammaValue greenTable[tableSize];
  1423     CGGammaValue blueTable[tableSize];
  1424     uint32_t i, tableCopied;
  1425 
  1426     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1427                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1428         return SDL_SetError("CGGetDisplayTransferByTable()");
  1429     }
  1430 
  1431     for (i = 0; i < tableCopied; i++) {
  1432         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1433         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1434         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1435     }
  1436     return 0;
  1437 }
  1438 
  1439 void
  1440 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1441 {
  1442     /* Move the cursor to the nearest point in the window */
  1443     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1444     if (grabbed && data && ![data->listener isMoving]) {
  1445         int x, y;
  1446         CGPoint cgpoint;
  1447 
  1448         SDL_GetMouseState(&x, &y);
  1449         cgpoint.x = window->x + x;
  1450         cgpoint.y = window->y + y;
  1451 
  1452         Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
  1453 
  1454         DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
  1455         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1456     }
  1457 
  1458     if ( window->flags & SDL_WINDOW_FULLSCREEN ) {
  1459         SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1460 
  1461         if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1462             /* OpenGL is rendering to the window, so make it visible! */
  1463             [data->nswindow setLevel:CGShieldingWindowLevel()];
  1464         } else {
  1465             [data->nswindow setLevel:kCGNormalWindowLevel];
  1466         }
  1467     }
  1468 }
  1469 
  1470 void
  1471 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1472 {
  1473     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1474     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1475 
  1476     if (data) {
  1477         [data->listener close];
  1478         [data->listener release];
  1479         if (data->created) {
  1480             [data->nswindow close];
  1481         }
  1482 
  1483         NSArray *contexts = [[data->nscontexts copy] autorelease];
  1484         for (SDLOpenGLContext *context in contexts) {
  1485             /* Calling setWindow:NULL causes the context to remove itself from the context list. */            
  1486             [context setWindow:NULL];
  1487         }
  1488         [data->nscontexts release];
  1489 
  1490         SDL_free(data);
  1491     }
  1492     [pool release];
  1493 }
  1494 
  1495 SDL_bool
  1496 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1497 {
  1498     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1499 
  1500     if (info->version.major <= SDL_MAJOR_VERSION) {
  1501         info->subsystem = SDL_SYSWM_COCOA;
  1502         info->info.cocoa.window = nswindow;
  1503         return SDL_TRUE;
  1504     } else {
  1505         SDL_SetError("Application not compiled with SDL %d.%d\n",
  1506                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1507         return SDL_FALSE;
  1508     }
  1509 }
  1510 
  1511 SDL_bool
  1512 Cocoa_IsWindowInFullscreenSpace(SDL_Window * window)
  1513 {
  1514     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1515 
  1516     if ([data->listener isInFullscreenSpace]) {
  1517         return SDL_TRUE;
  1518     } else {
  1519         return SDL_FALSE;
  1520     }
  1521 }
  1522 
  1523 SDL_bool
  1524 Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
  1525 {
  1526     SDL_bool succeeded = SDL_FALSE;
  1527     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1528     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1529 
  1530     if ([data->listener setFullscreenSpace:(state ? YES : NO)]) {
  1531         succeeded = SDL_TRUE;
  1532     }
  1533 
  1534     [pool release];
  1535 
  1536     return succeeded;
  1537 }
  1538 
  1539 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1540 
  1541 /* vi: set ts=4 sw=4 expandtab: */