src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Mon, 07 Jul 2014 12:48:25 -0700
changeset 8986 1c81316ac642
parent 8978 7753e4fd3d1d
child 9041 89e97caa2387
permissions -rw-r--r--
Fixed bug 2631 - Mac: minor code cleanup

Alex Szpakowski

Some minor changes to the Mac-specific backend code:

- Fixed up some code style issues (mostly brace style inconsistencies).

- Fixed a compiler warning in SDL_cocoaevents.m.

- Removed some useless code now that the 10.7 SDK is required to build SDL.

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