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