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