src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Wed, 25 Jun 2014 02:08:37 -0700
changeset 8926 38e89ab78465
parent 8905 1519c462cee6
child 8953 dc80dc0bd22e
permissions -rw-r--r--
Fixed bug 2525 - Keyboard focus crash

Todd Seiler

Call Stack:
#0 0x0000000101c29291 in Cocoa_StartTextInput at /Users/Todd/Desktop/codes/sources/SDL/src/video/cocoa/SDL_cocoakeyboard.m:512
#1 0x0000000101c110c5 in SDL_SetKeyboardFocus at /Users/Todd/Desktop/codes/sources/SDL/src/events/SDL_keyboard.c:643
#2 0x0000000101c32be4 in SetupWindowData at /Users/Todd/Desktop/codes/sources/SDL/src/video/cocoa/SDL_cocoawindow.m:981
#3 0x0000000101c32d2a in Cocoa_CreateWindowFrom at /Users/Todd/Desktop/codes/sources/SDL/src/video/cocoa/SDL_cocoawindow.m:1092
#4 0x0000000101c99999 in SDL_CreateWindowFrom_REAL at /Users/Todd/Desktop/codes/sources/SDL/src/video/SDL_video.c:1338
#5 0x0000000101ce1484 in SDL_CreateWindowFrom at /Users/Todd/Desktop/codes/sources/SDL/src/dynapi/SDL_dynapi_procs.h:547
#6 0x0000000100018a5e in SceneRenderer at /Users/Todd/Desktop/codes/sources/tseiler_Todds-MacBook-Pro_3405/AppName/src/SceneRenderer.cpp:138
#7 0x0000000100017ca5 in SceneRenderer at /Users/Todd/Desktop/codes/sources/tseiler_Todds-MacBook-Pro_3405/AppName/src/SceneRenderer.cpp:145
#8 0x000000010000cd96 in App::execute(int, char**) at /Users/Todd/Desktop/codes/sources/tseiler_Todds-MacBook-Pro_3405/AppName/src/App.cpp:28
#9 0x0000000100004402 in main at /Users/Todd/Desktop/codes/sources/tseiler_Todds-MacBook-Pro_3405/AppName/src/main.cpp:8


This issue occurred when using Ogre3D Graphics engine on Mac (cocoa) to create the window. Then handing the window handle off to SDL_CreateWindowFrom().

In Ogre3D application you do the following:
window_ = root_->initialise(true, "Ogre Window 2");
loadOgreResources();
Ogre::WindowEventUtilities::addWindowEventListener(window_, this);

#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
NSWindow* Data = 0;
window_->getCustomAttribute("WINDOW", &Data);
sdl_window_ = SDL_CreateWindowFrom((void*)Data);
#endif

It results in a crash in this function:
SDL_cocoakeyboard.m

void
Cocoa_StartTextInput(_THIS)
{
SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
SDL_Window *window = SDL_GetKeyboardFocus();
NSWindow *nswindow = nil;
if (window)
nswindow = ((SDL_WindowData*)window->driverdata)->nswindow;

// ...
}

The crash occurred because "driverdata" was nil. Before this function call, a call to SetupWindowData is called:

SDL_cocoawindow.m

static int
SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
{
// ...

if ([nswindow isKeyWindow]) {
window->flags |= SDL_WINDOW_INPUT_FOCUS;
SDL_SetKeyboardFocus(data->window);
}

/* Prevents the window's "window device" from being destroyed when it is
* hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
*/
[nswindow setOneShot:NO];

/* All done! */
[pool release];
window->driverdata = data;
return 0;
}

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