src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Mon, 22 Apr 2013 18:14:26 -0700
changeset 7087 5639ac726076
parent 7085 152cc7ddfa57
child 7098 f4b2c6fb0258
permissions -rw-r--r--
Fix Mac crash when creating fullscreen window introduced in 9d43403e9fc5.

makeKeyAndOrderFront: was sending three KVO transitions for isVisible,
for false -> true, true -> false, and then false -> true. This was
causing an infinite recursion.

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