src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Sun, 10 Nov 2013 19:19:44 -0800
changeset 7948 64e133a8b15e
parent 7946 57c34a54b7c2
child 7952 5ac1a895b313
permissions -rw-r--r--
Fixed pool memory leak
     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         [pool release];
   758         return -1;
   759     }
   760     [nswindow setBackgroundColor:[NSColor blackColor]];
   761 
   762     /* Create a default view for this window */
   763     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   764     NSView *contentView = [[SDLView alloc] initWithFrame:rect];
   765 
   766     if ((window->flags & SDL_WINDOW_ALLOW_HIGHDPI) > 0) {
   767         if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
   768             [contentView setWantsBestResolutionOpenGLSurface:YES];
   769         }
   770     }
   771 
   772     [nswindow setContentView: contentView];
   773     [contentView release];
   774 
   775     [pool release];
   776 
   777     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
   778         [nswindow release];
   779         return -1;
   780     }
   781     return 0;
   782 }
   783 
   784 int
   785 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   786 {
   787     NSAutoreleasePool *pool;
   788     NSWindow *nswindow = (NSWindow *) data;
   789     NSString *title;
   790 
   791     pool = [[NSAutoreleasePool alloc] init];
   792 
   793     /* Query the title from the existing window */
   794     title = [nswindow title];
   795     if (title) {
   796         window->title = SDL_strdup([title UTF8String]);
   797     }
   798 
   799     [pool release];
   800 
   801     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
   802 }
   803 
   804 void
   805 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
   806 {
   807     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   808     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   809     NSString *string;
   810 
   811     if(window->title) {
   812         string = [[NSString alloc] initWithUTF8String:window->title];
   813     } else {
   814         string = [[NSString alloc] init];
   815     }
   816     [nswindow setTitle:string];
   817     [string release];
   818 
   819     [pool release];
   820 }
   821 
   822 void
   823 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
   824 {
   825     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   826     NSImage *nsimage = Cocoa_CreateImage(icon);
   827 
   828     if (nsimage) {
   829         [NSApp setApplicationIconImage:nsimage];
   830     }
   831 
   832     [pool release];
   833 }
   834 
   835 void
   836 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
   837 {
   838     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   839     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   840     NSWindow *nswindow = windata->nswindow;
   841     NSRect rect;
   842     Uint32 moveHack;
   843 
   844     rect.origin.x = window->x;
   845     rect.origin.y = window->y;
   846     rect.size.width = window->w;
   847     rect.size.height = window->h;
   848     ConvertNSRect(&rect);
   849 
   850     moveHack = s_moveHack;
   851     s_moveHack = 0;
   852     [nswindow setFrameOrigin:rect.origin];
   853     s_moveHack = moveHack;
   854 
   855     ScheduleContextUpdates(windata);
   856 
   857     [pool release];
   858 }
   859 
   860 void
   861 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
   862 {
   863     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   864     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   865     NSWindow *nswindow = windata->nswindow;
   866     NSSize size;
   867 
   868     size.width = window->w;
   869     size.height = window->h;
   870     [nswindow setContentSize:size];
   871 
   872     ScheduleContextUpdates(windata);
   873 
   874     [pool release];
   875 }
   876 
   877 void
   878 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
   879 {
   880     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   881     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   882 
   883     NSSize minSize;
   884     minSize.width = window->min_w;
   885     minSize.height = window->min_h;
   886 
   887     [windata->nswindow setContentMinSize:minSize];
   888 
   889     [pool release];
   890 }
   891 
   892 void
   893 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
   894 {
   895     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   896     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   897 
   898     NSSize maxSize;
   899     maxSize.width = window->max_w;
   900     maxSize.height = window->max_h;
   901 
   902     [windata->nswindow setContentMaxSize:maxSize];
   903 
   904     [pool release];
   905 }
   906 
   907 void
   908 Cocoa_ShowWindow(_THIS, SDL_Window * window)
   909 {
   910     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   911     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
   912     NSWindow *nswindow = windowData->nswindow;
   913 
   914     if (![nswindow isMiniaturized]) {
   915         [windowData->listener pauseVisibleObservation];
   916         [nswindow makeKeyAndOrderFront:nil];
   917         [windowData->listener resumeVisibleObservation];
   918     }
   919     [pool release];
   920 }
   921 
   922 void
   923 Cocoa_HideWindow(_THIS, SDL_Window * window)
   924 {
   925     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   926     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   927 
   928     [nswindow orderOut:nil];
   929     [pool release];
   930 }
   931 
   932 void
   933 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
   934 {
   935     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   936     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
   937     NSWindow *nswindow = windowData->nswindow;
   938 
   939     /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
   940        a minimized or hidden window, so check for that before showing it.
   941      */
   942     [windowData->listener pauseVisibleObservation];
   943     if (![nswindow isMiniaturized] && [nswindow isVisible]) {
   944         [nswindow makeKeyAndOrderFront:nil];
   945     }
   946     [windowData->listener resumeVisibleObservation];
   947 
   948     [pool release];
   949 }
   950 
   951 void
   952 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
   953 {
   954     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   955     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   956     NSWindow *nswindow = windata->nswindow;
   957 
   958     [nswindow zoom:nil];
   959 
   960     ScheduleContextUpdates(windata);
   961 
   962     [pool release];
   963 }
   964 
   965 void
   966 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
   967 {
   968     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   969     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   970 
   971     [nswindow miniaturize:nil];
   972     [pool release];
   973 }
   974 
   975 void
   976 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
   977 {
   978     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   979     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   980 
   981     if ([nswindow isMiniaturized]) {
   982         [nswindow deminiaturize:nil];
   983     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
   984         [nswindow zoom:nil];
   985     }
   986     [pool release];
   987 }
   988 
   989 static NSWindow *
   990 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
   991 {
   992     if (!data->created) {
   993         /* Don't mess with other people's windows... */
   994         return nswindow;
   995     }
   996 
   997     [data->listener close];
   998     data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
   999     [data->nswindow setContentView:[nswindow contentView]];
  1000     /* See comment in SetupWindowData. */
  1001     [data->nswindow setOneShot:NO];
  1002     [data->listener listen:data];
  1003 
  1004     [nswindow close];
  1005 
  1006     return data->nswindow;
  1007 }
  1008 
  1009 void
  1010 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
  1011 {
  1012     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1013     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1014     if ([nswindow respondsToSelector:@selector(setStyleMask:)]) {
  1015         [nswindow setStyleMask:GetWindowStyle(window)];
  1016         if (bordered) {
  1017             Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
  1018         }
  1019     }
  1020     [pool release];
  1021 }
  1022 
  1023 void
  1024 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
  1025 {
  1026     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1027     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1028     NSWindow *nswindow = data->nswindow;
  1029     NSRect rect;
  1030 
  1031     /* The view responder chain gets messed with during setStyleMask */
  1032     if ([[nswindow contentView] nextResponder] == data->listener) {
  1033         [[nswindow contentView] setNextResponder:nil];
  1034     }
  1035 
  1036     if (fullscreen) {
  1037         SDL_Rect bounds;
  1038 
  1039         Cocoa_GetDisplayBounds(_this, display, &bounds);
  1040         rect.origin.x = bounds.x;
  1041         rect.origin.y = bounds.y;
  1042         rect.size.width = bounds.w;
  1043         rect.size.height = bounds.h;
  1044         ConvertNSRect(&rect);
  1045 
  1046         /* Hack to fix origin on Mac OS X 10.4 */
  1047         NSRect screenRect = [[nswindow screen] frame];
  1048         if (screenRect.size.height >= 1.0f) {
  1049             rect.origin.y += (screenRect.size.height - rect.size.height);
  1050         }
  1051 
  1052         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1053             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
  1054         } else {
  1055             nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
  1056         }
  1057     } else {
  1058         rect.origin.x = window->windowed.x;
  1059         rect.origin.y = window->windowed.y;
  1060         rect.size.width = window->windowed.w;
  1061         rect.size.height = window->windowed.h;
  1062         ConvertNSRect(&rect);
  1063 
  1064         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1065             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
  1066         } else {
  1067             nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
  1068         }
  1069     }
  1070 
  1071     /* The view responder chain gets messed with during setStyleMask */
  1072     if ([[nswindow contentView] nextResponder] != data->listener) {
  1073         [[nswindow contentView] setNextResponder:data->listener];
  1074     }
  1075 
  1076     s_moveHack = 0;
  1077     [nswindow setContentSize:rect.size];
  1078     [nswindow setFrameOrigin:rect.origin];
  1079     s_moveHack = SDL_GetTicks();
  1080 
  1081     /* When the window style changes the title is cleared */
  1082     if (!fullscreen) {
  1083         Cocoa_SetWindowTitle(_this, window);
  1084     }
  1085 
  1086     if (SDL_ShouldAllowTopmost() && fullscreen) {
  1087         /* OpenGL is rendering to the window, so make it visible! */
  1088         [nswindow setLevel:CGShieldingWindowLevel()];
  1089     } else {
  1090         [nswindow setLevel:kCGNormalWindowLevel];
  1091     }
  1092 
  1093     if ([nswindow isVisible] || fullscreen) {
  1094         [data->listener pauseVisibleObservation];
  1095         [nswindow makeKeyAndOrderFront:nil];
  1096         [data->listener resumeVisibleObservation];
  1097     }
  1098 
  1099     ScheduleContextUpdates(data);
  1100 
  1101     [pool release];
  1102 }
  1103 
  1104 int
  1105 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1106 {
  1107     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1108     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1109     const uint32_t tableSize = 256;
  1110     CGGammaValue redTable[tableSize];
  1111     CGGammaValue greenTable[tableSize];
  1112     CGGammaValue blueTable[tableSize];
  1113     uint32_t i;
  1114     float inv65535 = 1.0f / 65535.0f;
  1115 
  1116     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1117     for (i = 0; i < 256; i++) {
  1118         redTable[i] = ramp[0*256+i] * inv65535;
  1119         greenTable[i] = ramp[1*256+i] * inv65535;
  1120         blueTable[i] = ramp[2*256+i] * inv65535;
  1121     }
  1122 
  1123     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1124                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1125         return SDL_SetError("CGSetDisplayTransferByTable()");
  1126     }
  1127     return 0;
  1128 }
  1129 
  1130 int
  1131 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1132 {
  1133     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1134     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1135     const uint32_t tableSize = 256;
  1136     CGGammaValue redTable[tableSize];
  1137     CGGammaValue greenTable[tableSize];
  1138     CGGammaValue blueTable[tableSize];
  1139     uint32_t i, tableCopied;
  1140 
  1141     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1142                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1143         return SDL_SetError("CGGetDisplayTransferByTable()");
  1144     }
  1145 
  1146     for (i = 0; i < tableCopied; i++) {
  1147         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1148         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1149         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1150     }
  1151     return 0;
  1152 }
  1153 
  1154 void
  1155 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1156 {
  1157     /* Move the cursor to the nearest point in the window */
  1158     if (grabbed) {
  1159         int x, y;
  1160         CGPoint cgpoint;
  1161 
  1162         SDL_GetMouseState(&x, &y);
  1163         cgpoint.x = window->x + x;
  1164         cgpoint.y = window->y + y;
  1165         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1166     }
  1167 
  1168     if ( window->flags & SDL_WINDOW_FULLSCREEN ) {
  1169         SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1170 
  1171         if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1172             /* OpenGL is rendering to the window, so make it visible! */
  1173             [data->nswindow setLevel:CGShieldingWindowLevel()];
  1174         } else {
  1175             [data->nswindow setLevel:kCGNormalWindowLevel];
  1176         }
  1177     }
  1178 }
  1179 
  1180 void
  1181 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1182 {
  1183     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1184     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1185 
  1186     if (data) {
  1187         [data->listener close];
  1188         [data->listener release];
  1189         if (data->created) {
  1190             [data->nswindow close];
  1191         }
  1192 
  1193         NSArray *contexts = [[data->nscontexts copy] autorelease];
  1194         for (SDLOpenGLContext *context in contexts) {
  1195             /* Calling setWindow:NULL causes the context to remove itself from the context list. */            
  1196             [context setWindow:NULL];
  1197         }
  1198         [data->nscontexts release];
  1199 
  1200         SDL_free(data);
  1201     }
  1202     [pool release];
  1203 }
  1204 
  1205 SDL_bool
  1206 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1207 {
  1208     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1209 
  1210     if (info->version.major <= SDL_MAJOR_VERSION) {
  1211         info->subsystem = SDL_SYSWM_COCOA;
  1212         info->info.cocoa.window = nswindow;
  1213         return SDL_TRUE;
  1214     } else {
  1215         SDL_SetError("Application not compiled with SDL %d.%d\n",
  1216                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1217         return SDL_FALSE;
  1218     }
  1219 }
  1220 
  1221 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1222 
  1223 /* vi: set ts=4 sw=4 expandtab: */