src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Sat, 03 Aug 2013 02:20:00 -0400
changeset 7566 b14d00c30095
parent 7535 c515121a3df7
child 7593 20298a0d8631
permissions -rw-r--r--
Some fixes for SDL_MaximizeWindow().

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