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