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