src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Wed, 08 Apr 2015 02:00:14 -0400
changeset 9474 067b78126485
parent 9419 9763f689bced
child 9587 18979eac0a35
permissions -rw-r--r--
SDL_SetWindowTitle() should never set a NULL pointer for the title string.

Various backends reacted differently (or not at all) in the presence of a
NULL pointer. This simplifies things.

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