src/video/cocoa/SDL_cocoawindow.m
author Alex Szpakowski
Tue, 26 May 2015 21:51:47 -0300
changeset 9643 413b222c4b64
parent 9628 065e4ddc8753
child 9679 7fc4a8be47a8
permissions -rw-r--r--
Mac: Send a window resize event when the window's backing scale factor changes.

The backing scale factor can change when the window moves between retina and non-retina displays.

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