src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Fri, 27 Dec 2013 10:18:11 -0800
changeset 8083 3782a12331d6
parent 7990 a05a48e493c0
child 8093 b43765095a6f
permissions -rw-r--r--
Bump SDL to build with 10.7 SDK.

This also bumps the minimum requirement for building SDL to 10.7, and
removes some checking we no longer need.

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