src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Tue, 27 May 2014 01:27:42 -0400
changeset 8931 44d8a2f4b431
parent 8929 7b459b49fa92
child 8935 9d2f0236322b
permissions -rw-r--r--
First shot at SDL_SetWindowDragAreas().

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