src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 23 Jul 2013 17:38:59 -0700
changeset 7507 885b4aab4190
parent 7469 4310d5aee6fe
child 7534 f0f5143b27cf
permissions -rw-r--r--
Mac: Codify SDK and OS requirements, and clean up.

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