src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 25 Feb 2014 15:28:12 -0800
changeset 8258 569354dec4e9
parent 8149 681eb46b8ac4
child 8260 028ed8da2524
permissions -rw-r--r--
Mac: Immediately update current OpenGL context's shape.

Previously we were postponing our -[NSOpenGLContext update] call to the next
SDL_GL_SwapWindow, even if the context was current on the current thread. This
changes it so that we will do the update immediately if it's the current
context.

If you're rendering on another thread, you need to call SDL_GL_SwapWindow once
after a resize event to ensure your drawable will produce non-garbage data.

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