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