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