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