src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Mon, 07 Jul 2014 10:33:32 -0700
changeset 8978 7753e4fd3d1d
parent 8953 dc80dc0bd22e
child 8986 1c81316ac642
permissions -rw-r--r--
Fixed bug 2629 - Mac: crash when calling SDL_DestroyWindow with an active OpenGL context

Alex Szpakowski

Since this commit https://hg.libsdl.org/SDL/rev/1519c462cee6 , calling SDL_DestroyWindow will crash the program if the window has an active OpenGL context.

This is because the Cocoa_DestroyWindow code sets the window's driverdata to NULL and then calls [context setWindow:NULL], which tries to access the window's driverdata, resulting in a null pointer dereference.

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