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