src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 25 Feb 2014 17:27:41 -0800
changeset 8260 028ed8da2524
parent 8258 569354dec4e9
child 8261 841b66e4397a
permissions -rw-r--r--
Mac: Improve moving relative mode windows.

This makes it possible to move windows by their title bar, even if they're in
relative mode, if you click the title bar when the window does not have focus.

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