src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Sun, 10 Nov 2013 17:40:35 -0800
changeset 7946 57c34a54b7c2
parent 7915 86ad156a82ab
child 7948 64e133a8b15e
permissions -rw-r--r--
Fixed bug 2176 - SDL_CreateWindow(w=INT_MAX, h=INT_MAX) causes program to hang

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