src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Feb 2014 00:53:27 -0800
changeset 8149 681eb46b8ac4
parent 8111 426e78ce52e4
child 8258 569354dec4e9
permissions -rw-r--r--
Fixed bug 2374 - Update copyright for 2014...

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