src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Sat, 18 May 2013 14:17:52 -0700
changeset 7191 75360622e65f
parent 7158 ff52fba70795
child 7200 54c9f310f7c0
permissions -rw-r--r--
File style cleanup for the SDL 2.0 release
     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) {
   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 
   694     /* Create a default view for this window */
   695     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   696     NSView *contentView = [[SDLView alloc] initWithFrame:rect];
   697     [nswindow setContentView: contentView];
   698     [contentView release];
   699 
   700     [pool release];
   701 
   702     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
   703         [nswindow release];
   704         return -1;
   705     }
   706     return 0;
   707 }
   708 
   709 int
   710 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   711 {
   712     NSAutoreleasePool *pool;
   713     NSWindow *nswindow = (NSWindow *) data;
   714     NSString *title;
   715 
   716     pool = [[NSAutoreleasePool alloc] init];
   717 
   718     /* Query the title from the existing window */
   719     title = [nswindow title];
   720     if (title) {
   721         window->title = SDL_strdup([title UTF8String]);
   722     }
   723 
   724     [pool release];
   725 
   726     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
   727 }
   728 
   729 void
   730 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
   731 {
   732     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   733     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   734     NSString *string;
   735 
   736     if(window->title) {
   737         string = [[NSString alloc] initWithUTF8String:window->title];
   738     } else {
   739         string = [[NSString alloc] init];
   740     }
   741     [nswindow setTitle:string];
   742     [string release];
   743 
   744     [pool release];
   745 }
   746 
   747 void
   748 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
   749 {
   750     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   751     NSImage *nsimage = Cocoa_CreateImage(icon);
   752 
   753     if (nsimage) {
   754         [NSApp setApplicationIconImage:nsimage];
   755     }
   756 
   757     [pool release];
   758 }
   759 
   760 void
   761 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
   762 {
   763     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   764     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   765     NSRect rect;
   766     Uint32 moveHack;
   767 
   768     rect.origin.x = window->x;
   769     rect.origin.y = window->y;
   770     rect.size.width = window->w;
   771     rect.size.height = window->h;
   772     ConvertNSRect(&rect);
   773 
   774     moveHack = s_moveHack;
   775     s_moveHack = 0;
   776     [nswindow setFrameOrigin:rect.origin];
   777     s_moveHack = moveHack;
   778 
   779     if (window == _this->current_glwin) {
   780         [((NSOpenGLContext *) _this->current_glctx) update];
   781     }
   782 
   783     [pool release];
   784 }
   785 
   786 void
   787 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
   788 {
   789     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   790     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   791     NSWindow *nswindow = windata->nswindow;
   792     NSSize size;
   793 
   794     size.width = window->w;
   795     size.height = window->h;
   796     [nswindow setContentSize:size];
   797 
   798     if (window == _this->current_glwin) {
   799         [((NSOpenGLContext *) _this->current_glctx) update];
   800     }
   801 
   802     [pool release];
   803 }
   804 
   805 void
   806 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
   807 {
   808     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   809     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   810 
   811     NSSize minSize;
   812     minSize.width = window->min_w;
   813     minSize.height = window->min_h;
   814 
   815     [windata->nswindow setContentMinSize:minSize];
   816 
   817     [pool release];
   818 }
   819 
   820 void
   821 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
   822 {
   823     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   824     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   825 
   826     NSSize maxSize;
   827     maxSize.width = window->max_w;
   828     maxSize.height = window->max_h;
   829 
   830     [windata->nswindow setContentMaxSize:maxSize];
   831 
   832     [pool release];
   833 }
   834 
   835 void
   836 Cocoa_ShowWindow(_THIS, SDL_Window * window)
   837 {
   838     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   839     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
   840     NSWindow *nswindow = windowData->nswindow;
   841 
   842     if (![nswindow isMiniaturized]) {
   843         [windowData->listener pauseVisibleObservation];
   844         [nswindow makeKeyAndOrderFront:nil];
   845         [windowData->listener resumeVisibleObservation];
   846     }
   847     [pool release];
   848 }
   849 
   850 void
   851 Cocoa_HideWindow(_THIS, SDL_Window * window)
   852 {
   853     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   854     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   855 
   856     [nswindow orderOut:nil];
   857     [pool release];
   858 }
   859 
   860 void
   861 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
   862 {
   863     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   864     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
   865     NSWindow *nswindow = windowData->nswindow;
   866 
   867     [windowData->listener pauseVisibleObservation];
   868     [nswindow makeKeyAndOrderFront:nil];
   869     [windowData->listener resumeVisibleObservation];
   870 
   871     [pool release];
   872 }
   873 
   874 void
   875 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
   876 {
   877     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   878     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   879 
   880     [nswindow zoom:nil];
   881 
   882     if (window == _this->current_glwin) {
   883         [((NSOpenGLContext *) _this->current_glctx) update];
   884     }
   885 
   886     [pool release];
   887 }
   888 
   889 void
   890 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
   891 {
   892     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   893     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   894 
   895     [nswindow miniaturize:nil];
   896     [pool release];
   897 }
   898 
   899 void
   900 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
   901 {
   902     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   903     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   904 
   905     if ([nswindow isMiniaturized]) {
   906         [nswindow deminiaturize:nil];
   907     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
   908         [nswindow zoom:nil];
   909     }
   910     [pool release];
   911 }
   912 
   913 static NSWindow *
   914 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
   915 {
   916     if (!data->created) {
   917         /* Don't mess with other people's windows... */
   918         return nswindow;
   919     }
   920 
   921     [data->listener close];
   922     data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
   923     [data->nswindow setContentView:[nswindow contentView]];
   924     /* See comment in SetupWindowData. */
   925     [data->nswindow setOneShot:NO];
   926     [data->listener listen:data];
   927 
   928     [nswindow close];
   929 
   930     return data->nswindow;
   931 }
   932 
   933 void
   934 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
   935 {
   936     /* this message arrived in 10.6. You're out of luck on older OSes. */
   937 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   938     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   939     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   940     if ([nswindow respondsToSelector:@selector(setStyleMask:)]) {
   941         [nswindow setStyleMask:GetWindowStyle(window)];
   942         if (bordered) {
   943             Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
   944         }
   945     }
   946     [pool release];
   947 #endif
   948 }
   949 
   950 void
   951 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
   952 {
   953     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   954     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   955     NSWindow *nswindow = data->nswindow;
   956     NSRect rect;
   957 
   958     /* The view responder chain gets messed with during setStyleMask */
   959     if ([[nswindow contentView] nextResponder] == data->listener) {
   960         [[nswindow contentView] setNextResponder:nil];
   961     }
   962 
   963     if (fullscreen) {
   964         SDL_Rect bounds;
   965 
   966         Cocoa_GetDisplayBounds(_this, display, &bounds);
   967         rect.origin.x = bounds.x;
   968         rect.origin.y = bounds.y;
   969         rect.size.width = bounds.w;
   970         rect.size.height = bounds.h;
   971         ConvertNSRect(&rect);
   972 
   973         /* Hack to fix origin on Mac OS X 10.4 */
   974         NSRect screenRect = [[nswindow screen] frame];
   975         if (screenRect.size.height >= 1.0f) {
   976             rect.origin.y += (screenRect.size.height - rect.size.height);
   977         }
   978 
   979         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
   980             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
   981         } else {
   982             nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
   983         }
   984     } else {
   985         rect.origin.x = window->windowed.x;
   986         rect.origin.y = window->windowed.y;
   987         rect.size.width = window->windowed.w;
   988         rect.size.height = window->windowed.h;
   989         ConvertNSRect(&rect);
   990 
   991         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
   992             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
   993         } else {
   994             nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
   995         }
   996     }
   997 
   998     /* The view responder chain gets messed with during setStyleMask */
   999     if ([[nswindow contentView] nextResponder] != data->listener) {
  1000         [[nswindow contentView] setNextResponder:data->listener];
  1001     }
  1002 
  1003     s_moveHack = 0;
  1004     [nswindow setFrameOrigin:rect.origin];
  1005     [nswindow setContentSize:rect.size];
  1006     s_moveHack = SDL_GetTicks();
  1007 
  1008     /* When the window style changes the title is cleared */
  1009     if (!fullscreen) {
  1010         Cocoa_SetWindowTitle(_this, window);
  1011     }
  1012 
  1013 #ifdef FULLSCREEN_TOGGLEABLE
  1014     if (SDL_ShouldAllowTopmost() && fullscreen) {
  1015         /* OpenGL is rendering to the window, so make it visible! */
  1016         [nswindow setLevel:CGShieldingWindowLevel()];
  1017     } else {
  1018         [nswindow setLevel:kCGNormalWindowLevel];
  1019     }
  1020 #endif
  1021 
  1022     [data->listener pauseVisibleObservation];
  1023     [nswindow makeKeyAndOrderFront:nil];
  1024     [data->listener resumeVisibleObservation];
  1025 
  1026     if (window == _this->current_glwin) {
  1027         [((NSOpenGLContext *) _this->current_glctx) update];
  1028     }
  1029 
  1030     [pool release];
  1031 }
  1032 
  1033 int
  1034 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1035 {
  1036     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1037     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1038     const uint32_t tableSize = 256;
  1039     CGGammaValue redTable[tableSize];
  1040     CGGammaValue greenTable[tableSize];
  1041     CGGammaValue blueTable[tableSize];
  1042     uint32_t i;
  1043     float inv65535 = 1.0f / 65535.0f;
  1044 
  1045     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1046     for (i = 0; i < 256; i++) {
  1047         redTable[i] = ramp[0*256+i] * inv65535;
  1048         greenTable[i] = ramp[1*256+i] * inv65535;
  1049         blueTable[i] = ramp[2*256+i] * inv65535;
  1050     }
  1051 
  1052     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1053                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1054         return SDL_SetError("CGSetDisplayTransferByTable()");
  1055     }
  1056     return 0;
  1057 }
  1058 
  1059 int
  1060 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1061 {
  1062     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1063     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1064     const uint32_t tableSize = 256;
  1065     CGGammaValue redTable[tableSize];
  1066     CGGammaValue greenTable[tableSize];
  1067     CGGammaValue blueTable[tableSize];
  1068     uint32_t i, tableCopied;
  1069 
  1070     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1071                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1072         return SDL_SetError("CGGetDisplayTransferByTable()");
  1073     }
  1074 
  1075     for (i = 0; i < tableCopied; i++) {
  1076         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1077         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1078         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1079     }
  1080     return 0;
  1081 }
  1082 
  1083 void
  1084 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1085 {
  1086     /* Move the cursor to the nearest point in the window */
  1087     if (grabbed) {
  1088         int x, y;
  1089         CGPoint cgpoint;
  1090 
  1091         SDL_GetMouseState(&x, &y);
  1092         cgpoint.x = window->x + x;
  1093         cgpoint.y = window->y + y;
  1094         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1095     }
  1096 
  1097     if ( window->flags & SDL_WINDOW_FULLSCREEN ) {
  1098         SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1099 
  1100         if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1101             /* OpenGL is rendering to the window, so make it visible! */
  1102             [data->nswindow setLevel:CGShieldingWindowLevel()];
  1103         } else {
  1104             [data->nswindow setLevel:kCGNormalWindowLevel];
  1105         }
  1106     }
  1107 }
  1108 
  1109 void
  1110 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1111 {
  1112     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1113     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1114 
  1115     if (data) {
  1116         [data->listener close];
  1117         [data->listener release];
  1118         if (data->created) {
  1119             [data->nswindow close];
  1120         }
  1121         SDL_free(data);
  1122     }
  1123     [pool release];
  1124 }
  1125 
  1126 SDL_bool
  1127 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1128 {
  1129     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1130 
  1131     if (info->version.major <= SDL_MAJOR_VERSION) {
  1132         info->subsystem = SDL_SYSWM_COCOA;
  1133         info->info.cocoa.window = nswindow;
  1134         return SDL_TRUE;
  1135     } else {
  1136         SDL_SetError("Application not compiled with SDL %d.%d\n",
  1137                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1138         return SDL_FALSE;
  1139     }
  1140 }
  1141 
  1142 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1143 
  1144 /* vi: set ts=4 sw=4 expandtab: */