src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Sun, 22 Mar 2015 01:25:12 -0400
changeset 9419 9763f689bced
parent 9237 2cc90bb31777
child 9474 067b78126485
permissions -rw-r--r--
Cocoa: Handle more cases of lost focus when Key window closes (thanks, Alex!).

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