src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Wed, 28 May 2014 01:22:47 -0400
changeset 8935 9d2f0236322b
parent 8931 44d8a2f4b431
child 8953 dc80dc0bd22e
permissions -rw-r--r--
Changed drag area API to a hit-testing API.

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