src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Wed, 07 Aug 2013 16:29:21 -0700
changeset 7594 6abcf951af68
parent 7593 20298a0d8631
child 7595 ede2237fcebf
permissions -rw-r--r--
Mac: Don't -[NSOpenGLContext update] on (potentially) the wrong thread.

If the user is using their context from a non-main thread, we could be
calling -[NSOpenGLContext update] on our thread, while they were
accessing it on their thread.

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