src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Mon, 02 Jun 2014 09:01:10 -0700
changeset 8798 2703c0c19f45
parent 8738 354dabd2cb58
child 8801 98173814b673
permissions -rw-r--r--
Fixed bug 2479 - [OS X] SDL_SetWindowFullscreen fails to switch to windowed

Eric Wasylishen

The problem seems to be the spaces handling code in -setFullscreenSpace: (SDL_cocoawindow.m) is incorrectly reporting that the SDL_WINDOW_FULLSCREEN -> windowed transition has already happened.

i.e. I saw this case was getting hit when trying to leave SDL_WINDOW_FULLSCREEN:

"else if (state == isFullscreenSpace) {
return YES; /* already there. */
}"

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