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