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