src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgenpt@gmail.com>
Sat, 19 Apr 2014 13:15:53 -0700
changeset 8729 94af945dbb57
parent 8652 8514fc57f124
child 8738 354dabd2cb58
permissions -rw-r--r--
Mac: SDL_SetWindowPosition is now relative to the menubar.

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