src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 04 Jun 2013 13:53:55 -0700
changeset 7270 76df5476a6ce
parent 7205 b1ca5be78064
child 7271 963d2ba35cfe
permissions -rw-r--r--
Mac: Hide cursor in relative mode.

This hides the cursor when you SDL_SetRelativeMouseMode, as intended.

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