src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 30 Jul 2013 16:58:16 -0700
changeset 8727 6b4f9d40414b
parent 7636 d5167d0818b3
permissions -rw-r--r--
Mac: SDL_SetWindowPosition is now relative to the menubar.

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