src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Mon, 22 Jul 2013 20:55:07 -0400
changeset 7534 f0f5143b27cf
parent 7507 885b4aab4190
child 7535 c515121a3df7
permissions -rw-r--r--
Cocoa: Make the next-highest window gain focus when a window is closing.

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