src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Wed, 07 Aug 2013 16:29:15 -0700
changeset 7593 20298a0d8631
parent 7566 b14d00c30095
child 7594 6abcf951af68
permissions -rw-r--r--
Mac: Better mouse-grab if you define SDL_MAC_NO_SANDBOX.

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