src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 07 May 2013 16:52:39 -0700
changeset 7158 ff52fba70795
parent 7113 7b4b596b3cfb
child 7191 75360622e65f
permissions -rw-r--r--
Mac: Use cursor rects instead of NSCursor hide/unhide.

This should hopefully fix a class of problems around cursor hiding not behaving correctly on Mac.

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