src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 16 Jul 2013 01:02:51 -0700
changeset 7469 4310d5aee6fe
parent 7389 361d1a0c90f6
child 7507 885b4aab4190
permissions -rw-r--r--
Mac: Handle SDL_CreateWindow with SDL_WINDOW_MINIMZED.

This fixes bug #1446. You can now create a window with SDL_CreateWindow(...,
SDL_WINDOW_MINIMIZED), and not have it immediately restore itself.

It also changes SDL_RaiseWindow() to be a no-op on minimized or hidden windows,
which is how it behaves on Windows.
     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     // makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
   870     // a minimized or hidden window, so check for that before showing it.
   871     [windowData->listener pauseVisibleObservation];
   872     if (![nswindow isMiniaturized] && [nswindow isVisible]) {
   873         [nswindow makeKeyAndOrderFront:nil];
   874     }
   875     [windowData->listener resumeVisibleObservation];
   876 
   877     [pool release];
   878 }
   879 
   880 void
   881 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
   882 {
   883     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   884     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   885 
   886     [nswindow zoom:nil];
   887 
   888     if (window == _this->current_glwin) {
   889         [((NSOpenGLContext *) _this->current_glctx) update];
   890     }
   891 
   892     [pool release];
   893 }
   894 
   895 void
   896 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
   897 {
   898     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   899     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   900 
   901     [nswindow miniaturize:nil];
   902     [pool release];
   903 }
   904 
   905 void
   906 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
   907 {
   908     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   909     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   910 
   911     if ([nswindow isMiniaturized]) {
   912         [nswindow deminiaturize:nil];
   913     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
   914         [nswindow zoom:nil];
   915     }
   916     [pool release];
   917 }
   918 
   919 static NSWindow *
   920 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
   921 {
   922     if (!data->created) {
   923         /* Don't mess with other people's windows... */
   924         return nswindow;
   925     }
   926 
   927     [data->listener close];
   928     data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
   929     [data->nswindow setContentView:[nswindow contentView]];
   930     /* See comment in SetupWindowData. */
   931     [data->nswindow setOneShot:NO];
   932     [data->listener listen:data];
   933 
   934     [nswindow close];
   935 
   936     return data->nswindow;
   937 }
   938 
   939 void
   940 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
   941 {
   942     /* this message arrived in 10.6. You're out of luck on older OSes. */
   943 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   944     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   945     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   946     if ([nswindow respondsToSelector:@selector(setStyleMask:)]) {
   947         [nswindow setStyleMask:GetWindowStyle(window)];
   948         if (bordered) {
   949             Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
   950         }
   951     }
   952     [pool release];
   953 #endif
   954 }
   955 
   956 void
   957 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
   958 {
   959     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   960     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   961     NSWindow *nswindow = data->nswindow;
   962     NSRect rect;
   963 
   964     /* The view responder chain gets messed with during setStyleMask */
   965     if ([[nswindow contentView] nextResponder] == data->listener) {
   966         [[nswindow contentView] setNextResponder:nil];
   967     }
   968 
   969     if (fullscreen) {
   970         SDL_Rect bounds;
   971 
   972         Cocoa_GetDisplayBounds(_this, display, &bounds);
   973         rect.origin.x = bounds.x;
   974         rect.origin.y = bounds.y;
   975         rect.size.width = bounds.w;
   976         rect.size.height = bounds.h;
   977         ConvertNSRect(&rect);
   978 
   979         /* Hack to fix origin on Mac OS X 10.4 */
   980         NSRect screenRect = [[nswindow screen] frame];
   981         if (screenRect.size.height >= 1.0f) {
   982             rect.origin.y += (screenRect.size.height - rect.size.height);
   983         }
   984 
   985         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
   986             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
   987         } else {
   988             nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
   989         }
   990     } else {
   991         rect.origin.x = window->windowed.x;
   992         rect.origin.y = window->windowed.y;
   993         rect.size.width = window->windowed.w;
   994         rect.size.height = window->windowed.h;
   995         ConvertNSRect(&rect);
   996 
   997         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
   998             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
   999         } else {
  1000             nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
  1001         }
  1002     }
  1003 
  1004     /* The view responder chain gets messed with during setStyleMask */
  1005     if ([[nswindow contentView] nextResponder] != data->listener) {
  1006         [[nswindow contentView] setNextResponder:data->listener];
  1007     }
  1008 
  1009     s_moveHack = 0;
  1010     [nswindow setFrameOrigin:rect.origin];
  1011     [nswindow setContentSize:rect.size];
  1012     s_moveHack = SDL_GetTicks();
  1013 
  1014     /* When the window style changes the title is cleared */
  1015     if (!fullscreen) {
  1016         Cocoa_SetWindowTitle(_this, window);
  1017     }
  1018 
  1019     if (SDL_ShouldAllowTopmost() && fullscreen) {
  1020         /* OpenGL is rendering to the window, so make it visible! */
  1021         [nswindow setLevel:CGShieldingWindowLevel()];
  1022     } else {
  1023         [nswindow setLevel:kCGNormalWindowLevel];
  1024     }
  1025 
  1026     [data->listener pauseVisibleObservation];
  1027     [nswindow makeKeyAndOrderFront:nil];
  1028     [data->listener resumeVisibleObservation];
  1029 
  1030     if (window == _this->current_glwin) {
  1031         [((NSOpenGLContext *) _this->current_glctx) update];
  1032     }
  1033 
  1034     [pool release];
  1035 }
  1036 
  1037 int
  1038 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1039 {
  1040     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1041     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1042     const uint32_t tableSize = 256;
  1043     CGGammaValue redTable[tableSize];
  1044     CGGammaValue greenTable[tableSize];
  1045     CGGammaValue blueTable[tableSize];
  1046     uint32_t i;
  1047     float inv65535 = 1.0f / 65535.0f;
  1048 
  1049     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1050     for (i = 0; i < 256; i++) {
  1051         redTable[i] = ramp[0*256+i] * inv65535;
  1052         greenTable[i] = ramp[1*256+i] * inv65535;
  1053         blueTable[i] = ramp[2*256+i] * inv65535;
  1054     }
  1055 
  1056     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1057                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1058         return SDL_SetError("CGSetDisplayTransferByTable()");
  1059     }
  1060     return 0;
  1061 }
  1062 
  1063 int
  1064 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1065 {
  1066     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1067     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1068     const uint32_t tableSize = 256;
  1069     CGGammaValue redTable[tableSize];
  1070     CGGammaValue greenTable[tableSize];
  1071     CGGammaValue blueTable[tableSize];
  1072     uint32_t i, tableCopied;
  1073 
  1074     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1075                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1076         return SDL_SetError("CGGetDisplayTransferByTable()");
  1077     }
  1078 
  1079     for (i = 0; i < tableCopied; i++) {
  1080         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1081         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1082         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1083     }
  1084     return 0;
  1085 }
  1086 
  1087 void
  1088 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1089 {
  1090     /* Move the cursor to the nearest point in the window */
  1091     if (grabbed) {
  1092         int x, y;
  1093         CGPoint cgpoint;
  1094 
  1095         SDL_GetMouseState(&x, &y);
  1096         cgpoint.x = window->x + x;
  1097         cgpoint.y = window->y + y;
  1098         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1099     }
  1100 
  1101     if ( window->flags & SDL_WINDOW_FULLSCREEN ) {
  1102         SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1103 
  1104         if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1105             /* OpenGL is rendering to the window, so make it visible! */
  1106             [data->nswindow setLevel:CGShieldingWindowLevel()];
  1107         } else {
  1108             [data->nswindow setLevel:kCGNormalWindowLevel];
  1109         }
  1110     }
  1111 }
  1112 
  1113 void
  1114 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1115 {
  1116     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1117     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1118 
  1119     if (data) {
  1120         [data->listener close];
  1121         [data->listener release];
  1122         if (data->created) {
  1123             [data->nswindow close];
  1124         }
  1125         SDL_free(data);
  1126     }
  1127     [pool release];
  1128 }
  1129 
  1130 SDL_bool
  1131 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1132 {
  1133     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1134 
  1135     if (info->version.major <= SDL_MAJOR_VERSION) {
  1136         info->subsystem = SDL_SYSWM_COCOA;
  1137         info->info.cocoa.window = nswindow;
  1138         return SDL_TRUE;
  1139     } else {
  1140         SDL_SetError("Application not compiled with SDL %d.%d\n",
  1141                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1142         return SDL_FALSE;
  1143     }
  1144 }
  1145 
  1146 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1147 
  1148 /* vi: set ts=4 sw=4 expandtab: */