src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Tue, 26 May 2015 06:27:46 -0700
changeset 9619 b94b6d0bff0f
parent 9587 18979eac0a35
child 9623 b381999d8944
permissions -rw-r--r--
Updated the copyright year to 2015
     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(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 { @autoreleasepool
  1004 {
  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     /* Create an event listener for the window */
  1020     data->listener = [[Cocoa_WindowListener alloc] init];
  1021 
  1022     /* Fill in the SDL window with the window data */
  1023     {
  1024         NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1025         ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1026         window->x = (int)rect.origin.x;
  1027         window->y = (int)rect.origin.y;
  1028         window->w = (int)rect.size.width;
  1029         window->h = (int)rect.size.height;
  1030     }
  1031 
  1032     /* Set up the listener after we create the view */
  1033     [data->listener listen:data];
  1034 
  1035     if ([nswindow isVisible]) {
  1036         window->flags |= SDL_WINDOW_SHOWN;
  1037     } else {
  1038         window->flags &= ~SDL_WINDOW_SHOWN;
  1039     }
  1040 
  1041     {
  1042         unsigned int style = [nswindow styleMask];
  1043 
  1044         if (style == NSBorderlessWindowMask) {
  1045             window->flags |= SDL_WINDOW_BORDERLESS;
  1046         } else {
  1047             window->flags &= ~SDL_WINDOW_BORDERLESS;
  1048         }
  1049         if (style & NSResizableWindowMask) {
  1050             window->flags |= SDL_WINDOW_RESIZABLE;
  1051         } else {
  1052             window->flags &= ~SDL_WINDOW_RESIZABLE;
  1053         }
  1054     }
  1055 
  1056     /* isZoomed always returns true if the window is not resizable */
  1057     if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1058         window->flags |= SDL_WINDOW_MAXIMIZED;
  1059     } else {
  1060         window->flags &= ~SDL_WINDOW_MAXIMIZED;
  1061     }
  1062 
  1063     if ([nswindow isMiniaturized]) {
  1064         window->flags |= SDL_WINDOW_MINIMIZED;
  1065     } else {
  1066         window->flags &= ~SDL_WINDOW_MINIMIZED;
  1067     }
  1068 
  1069     if ([nswindow isKeyWindow]) {
  1070         window->flags |= SDL_WINDOW_INPUT_FOCUS;
  1071         SDL_SetKeyboardFocus(data->window);
  1072     }
  1073 
  1074     /* Prevents the window's "window device" from being destroyed when it is
  1075      * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
  1076      */
  1077     [nswindow setOneShot:NO];
  1078 
  1079     /* All done! */
  1080     window->driverdata = data;
  1081     return 0;
  1082 }}
  1083 
  1084 int
  1085 Cocoa_CreateWindow(_THIS, SDL_Window * window)
  1086 { @autoreleasepool
  1087 {
  1088     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1089     NSWindow *nswindow;
  1090     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1091     NSRect rect;
  1092     SDL_Rect bounds;
  1093     unsigned int style;
  1094     NSArray *screens = [NSScreen screens];
  1095 
  1096     Cocoa_GetDisplayBounds(_this, display, &bounds);
  1097     rect.origin.x = window->x;
  1098     rect.origin.y = window->y;
  1099     rect.size.width = window->w;
  1100     rect.size.height = window->h;
  1101     ConvertNSRect([screens objectAtIndex:0], (window->flags & FULLSCREEN_MASK), &rect);
  1102 
  1103     style = GetWindowStyle(window);
  1104 
  1105     /* Figure out which screen to place this window */
  1106     NSScreen *screen = nil;
  1107     for (NSScreen *candidate in screens) {
  1108         NSRect screenRect = [candidate frame];
  1109         if (rect.origin.x >= screenRect.origin.x &&
  1110             rect.origin.x < screenRect.origin.x + screenRect.size.width &&
  1111             rect.origin.y >= screenRect.origin.y &&
  1112             rect.origin.y < screenRect.origin.y + screenRect.size.height) {
  1113             screen = candidate;
  1114             rect.origin.x -= screenRect.origin.x;
  1115             rect.origin.y -= screenRect.origin.y;
  1116         }
  1117     }
  1118 
  1119     @try {
  1120         nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
  1121     }
  1122     @catch (NSException *e) {
  1123         return SDL_SetError("%s", [[e reason] UTF8String]);
  1124     }
  1125     [nswindow setBackgroundColor:[NSColor blackColor]];
  1126 
  1127     if (videodata->allow_spaces) {
  1128         SDL_assert(floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6);
  1129         SDL_assert([nswindow respondsToSelector:@selector(toggleFullScreen:)]);
  1130         /* we put FULLSCREEN_DESKTOP windows in their own Space, without a toggle button or menubar, later */
  1131         if (window->flags & SDL_WINDOW_RESIZABLE) {
  1132             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
  1133             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
  1134         }
  1135     }
  1136 
  1137     /* Create a default view for this window */
  1138     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1139     NSView *contentView = [[SDLView alloc] initWithFrame:rect];
  1140 
  1141     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
  1142         if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
  1143             [contentView setWantsBestResolutionOpenGLSurface:YES];
  1144         }
  1145     }
  1146 
  1147     [nswindow setContentView: contentView];
  1148     [contentView release];
  1149 
  1150     /* Allow files and folders to be dragged onto the window by users */
  1151     [nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
  1152 
  1153     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
  1154         [nswindow release];
  1155         return -1;
  1156     }
  1157     return 0;
  1158 }}
  1159 
  1160 int
  1161 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
  1162 { @autoreleasepool
  1163 {
  1164     NSWindow *nswindow = (NSWindow *) data;
  1165     NSString *title;
  1166 
  1167     /* Query the title from the existing window */
  1168     title = [nswindow title];
  1169     if (title) {
  1170         window->title = SDL_strdup([title UTF8String]);
  1171     }
  1172 
  1173     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
  1174 }}
  1175 
  1176 void
  1177 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
  1178 { @autoreleasepool
  1179 {
  1180     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1181     NSString *string = [[NSString alloc] initWithUTF8String:window->title];
  1182     [nswindow setTitle:string];
  1183     [string release];
  1184 }}
  1185 
  1186 void
  1187 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
  1188 { @autoreleasepool
  1189 {
  1190     NSImage *nsimage = Cocoa_CreateImage(icon);
  1191 
  1192     if (nsimage) {
  1193         [NSApp setApplicationIconImage:nsimage];
  1194     }
  1195 }}
  1196 
  1197 void
  1198 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
  1199 { @autoreleasepool
  1200 {
  1201     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1202     NSWindow *nswindow = windata->nswindow;
  1203     NSRect rect;
  1204     Uint32 moveHack;
  1205 
  1206     rect.origin.x = window->x;
  1207     rect.origin.y = window->y;
  1208     rect.size.width = window->w;
  1209     rect.size.height = window->h;
  1210     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1211 
  1212     moveHack = s_moveHack;
  1213     s_moveHack = 0;
  1214     [nswindow setFrameOrigin:rect.origin];
  1215     s_moveHack = moveHack;
  1216 
  1217     ScheduleContextUpdates(windata);
  1218 }}
  1219 
  1220 void
  1221 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
  1222 { @autoreleasepool
  1223 {
  1224     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1225     NSWindow *nswindow = windata->nswindow;
  1226     NSSize size;
  1227 
  1228     size.width = window->w;
  1229     size.height = window->h;
  1230     [nswindow setContentSize:size];
  1231 
  1232     ScheduleContextUpdates(windata);
  1233 }}
  1234 
  1235 void
  1236 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
  1237 { @autoreleasepool
  1238 {
  1239     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1240 
  1241     NSSize minSize;
  1242     minSize.width = window->min_w;
  1243     minSize.height = window->min_h;
  1244 
  1245     [windata->nswindow setContentMinSize:minSize];
  1246 }}
  1247 
  1248 void
  1249 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
  1250 { @autoreleasepool
  1251 {
  1252     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1253 
  1254     NSSize maxSize;
  1255     maxSize.width = window->max_w;
  1256     maxSize.height = window->max_h;
  1257 
  1258     [windata->nswindow setContentMaxSize:maxSize];
  1259 }}
  1260 
  1261 void
  1262 Cocoa_ShowWindow(_THIS, SDL_Window * window)
  1263 { @autoreleasepool
  1264 {
  1265     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1266     NSWindow *nswindow = windowData->nswindow;
  1267 
  1268     if (![nswindow isMiniaturized]) {
  1269         [windowData->listener pauseVisibleObservation];
  1270         [nswindow makeKeyAndOrderFront:nil];
  1271         [windowData->listener resumeVisibleObservation];
  1272     }
  1273 }}
  1274 
  1275 void
  1276 Cocoa_HideWindow(_THIS, SDL_Window * window)
  1277 { @autoreleasepool
  1278 {
  1279     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1280 
  1281     [nswindow orderOut:nil];
  1282 }}
  1283 
  1284 void
  1285 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
  1286 { @autoreleasepool
  1287 {
  1288     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1289     NSWindow *nswindow = windowData->nswindow;
  1290 
  1291     /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
  1292        a minimized or hidden window, so check for that before showing it.
  1293      */
  1294     [windowData->listener pauseVisibleObservation];
  1295     if (![nswindow isMiniaturized] && [nswindow isVisible]) {
  1296         [NSApp activateIgnoringOtherApps:YES];
  1297         [nswindow makeKeyAndOrderFront:nil];
  1298     }
  1299     [windowData->listener resumeVisibleObservation];
  1300 }}
  1301 
  1302 void
  1303 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
  1304 { @autoreleasepool
  1305 {
  1306     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1307     NSWindow *nswindow = windata->nswindow;
  1308 
  1309     [nswindow zoom:nil];
  1310 
  1311     ScheduleContextUpdates(windata);
  1312 }}
  1313 
  1314 void
  1315 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
  1316 { @autoreleasepool
  1317 {
  1318     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1319     NSWindow *nswindow = data->nswindow;
  1320 
  1321     if ([data->listener isInFullscreenSpaceTransition]) {
  1322         [data->listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
  1323     } else {
  1324         [nswindow miniaturize:nil];
  1325     }
  1326 }}
  1327 
  1328 void
  1329 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
  1330 { @autoreleasepool
  1331 {
  1332     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1333 
  1334     if ([nswindow isMiniaturized]) {
  1335         [nswindow deminiaturize:nil];
  1336     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1337         [nswindow zoom:nil];
  1338     }
  1339 }}
  1340 
  1341 static NSWindow *
  1342 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
  1343 {
  1344     if (!data->created) {
  1345         /* Don't mess with other people's windows... */
  1346         return nswindow;
  1347     }
  1348 
  1349     [data->listener close];
  1350     data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
  1351     [data->nswindow setContentView:[nswindow contentView]];
  1352     [data->nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
  1353     /* See comment in SetupWindowData. */
  1354     [data->nswindow setOneShot:NO];
  1355     [data->listener listen:data];
  1356 
  1357     [nswindow close];
  1358 
  1359     return data->nswindow;
  1360 }
  1361 
  1362 void
  1363 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
  1364 { @autoreleasepool
  1365 {
  1366     if (SetWindowStyle(window, GetWindowStyle(window))) {
  1367         if (bordered) {
  1368             Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
  1369         }
  1370     }
  1371 }}
  1372 
  1373 
  1374 void
  1375 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
  1376 { @autoreleasepool
  1377 {
  1378     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1379     NSWindow *nswindow = data->nswindow;
  1380     NSRect rect;
  1381 
  1382     /* The view responder chain gets messed with during setStyleMask */
  1383     if ([[nswindow contentView] nextResponder] == data->listener) {
  1384         [[nswindow contentView] setNextResponder:nil];
  1385     }
  1386 
  1387     if (fullscreen) {
  1388         SDL_Rect bounds;
  1389 
  1390         Cocoa_GetDisplayBounds(_this, display, &bounds);
  1391         rect.origin.x = bounds.x;
  1392         rect.origin.y = bounds.y;
  1393         rect.size.width = bounds.w;
  1394         rect.size.height = bounds.h;
  1395         ConvertNSRect([nswindow screen], fullscreen, &rect);
  1396 
  1397         /* Hack to fix origin on Mac OS X 10.4 */
  1398         NSRect screenRect = [[nswindow screen] frame];
  1399         if (screenRect.size.height >= 1.0f) {
  1400             rect.origin.y += (screenRect.size.height - rect.size.height);
  1401         }
  1402 
  1403         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1404             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
  1405         } else {
  1406             nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
  1407         }
  1408     } else {
  1409         rect.origin.x = window->windowed.x;
  1410         rect.origin.y = window->windowed.y;
  1411         rect.size.width = window->windowed.w;
  1412         rect.size.height = window->windowed.h;
  1413         ConvertNSRect([nswindow screen], fullscreen, &rect);
  1414 
  1415         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1416             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
  1417         } else {
  1418             nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
  1419         }
  1420     }
  1421 
  1422     /* The view responder chain gets messed with during setStyleMask */
  1423     if ([[nswindow contentView] nextResponder] != data->listener) {
  1424         [[nswindow contentView] setNextResponder:data->listener];
  1425     }
  1426 
  1427     s_moveHack = 0;
  1428     [nswindow setContentSize:rect.size];
  1429     [nswindow setFrameOrigin:rect.origin];
  1430     s_moveHack = SDL_GetTicks();
  1431 
  1432     /* When the window style changes the title is cleared */
  1433     if (!fullscreen) {
  1434         Cocoa_SetWindowTitle(_this, window);
  1435     }
  1436 
  1437     if (SDL_ShouldAllowTopmost() && fullscreen) {
  1438         /* OpenGL is rendering to the window, so make it visible! */
  1439         [nswindow setLevel:CGShieldingWindowLevel()];
  1440     } else {
  1441         [nswindow setLevel:kCGNormalWindowLevel];
  1442     }
  1443 
  1444     if ([nswindow isVisible] || fullscreen) {
  1445         [data->listener pauseVisibleObservation];
  1446         [nswindow makeKeyAndOrderFront:nil];
  1447         [data->listener resumeVisibleObservation];
  1448     }
  1449 
  1450     ScheduleContextUpdates(data);
  1451 }}
  1452 
  1453 int
  1454 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1455 {
  1456     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1457     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1458     const uint32_t tableSize = 256;
  1459     CGGammaValue redTable[tableSize];
  1460     CGGammaValue greenTable[tableSize];
  1461     CGGammaValue blueTable[tableSize];
  1462     uint32_t i;
  1463     float inv65535 = 1.0f / 65535.0f;
  1464 
  1465     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1466     for (i = 0; i < 256; i++) {
  1467         redTable[i] = ramp[0*256+i] * inv65535;
  1468         greenTable[i] = ramp[1*256+i] * inv65535;
  1469         blueTable[i] = ramp[2*256+i] * inv65535;
  1470     }
  1471 
  1472     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1473                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1474         return SDL_SetError("CGSetDisplayTransferByTable()");
  1475     }
  1476     return 0;
  1477 }
  1478 
  1479 int
  1480 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1481 {
  1482     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1483     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1484     const uint32_t tableSize = 256;
  1485     CGGammaValue redTable[tableSize];
  1486     CGGammaValue greenTable[tableSize];
  1487     CGGammaValue blueTable[tableSize];
  1488     uint32_t i, tableCopied;
  1489 
  1490     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1491                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1492         return SDL_SetError("CGGetDisplayTransferByTable()");
  1493     }
  1494 
  1495     for (i = 0; i < tableCopied; i++) {
  1496         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1497         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1498         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1499     }
  1500     return 0;
  1501 }
  1502 
  1503 void
  1504 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1505 {
  1506     /* Move the cursor to the nearest point in the window */
  1507     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1508     if (grabbed && data && ![data->listener isMoving]) {
  1509         int x, y;
  1510         CGPoint cgpoint;
  1511 
  1512         SDL_GetMouseState(&x, &y);
  1513         cgpoint.x = window->x + x;
  1514         cgpoint.y = window->y + y;
  1515 
  1516         Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
  1517 
  1518         DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
  1519         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1520     }
  1521 
  1522     if ( data && (window->flags & SDL_WINDOW_FULLSCREEN) ) {
  1523         if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1524             /* OpenGL is rendering to the window, so make it visible! */
  1525             [data->nswindow setLevel:CGShieldingWindowLevel()];
  1526         } else {
  1527             [data->nswindow setLevel:kCGNormalWindowLevel];
  1528         }
  1529     }
  1530 }
  1531 
  1532 void
  1533 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1534 { @autoreleasepool
  1535 {
  1536     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1537 
  1538     if (data) {
  1539         [data->listener close];
  1540         [data->listener release];
  1541         if (data->created) {
  1542             [data->nswindow close];
  1543         }
  1544 
  1545         NSArray *contexts = [[data->nscontexts copy] autorelease];
  1546         for (SDLOpenGLContext *context in contexts) {
  1547             /* Calling setWindow:NULL causes the context to remove itself from the context list. */            
  1548             [context setWindow:NULL];
  1549         }
  1550         [data->nscontexts release];
  1551 
  1552         SDL_free(data);
  1553     }
  1554     window->driverdata = NULL;
  1555 }}
  1556 
  1557 SDL_bool
  1558 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1559 {
  1560     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1561 
  1562     if (info->version.major <= SDL_MAJOR_VERSION) {
  1563         info->subsystem = SDL_SYSWM_COCOA;
  1564         info->info.cocoa.window = nswindow;
  1565         return SDL_TRUE;
  1566     } else {
  1567         SDL_SetError("Application not compiled with SDL %d.%d\n",
  1568                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1569         return SDL_FALSE;
  1570     }
  1571 }
  1572 
  1573 SDL_bool
  1574 Cocoa_IsWindowInFullscreenSpace(SDL_Window * window)
  1575 {
  1576     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1577 
  1578     if ([data->listener isInFullscreenSpace]) {
  1579         return SDL_TRUE;
  1580     } else {
  1581         return SDL_FALSE;
  1582     }
  1583 }
  1584 
  1585 SDL_bool
  1586 Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
  1587 { @autoreleasepool
  1588 {
  1589     SDL_bool succeeded = SDL_FALSE;
  1590     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1591 
  1592     if ([data->listener setFullscreenSpace:(state ? YES : NO)]) {
  1593         succeeded = SDL_TRUE;
  1594 
  1595         /* Wait for the transition to complete, so application changes
  1596            take effect properly (e.g. setting the window size, etc.)
  1597          */
  1598         const int limit = 10000;
  1599         int count = 0;
  1600         while ([data->listener isInFullscreenSpaceTransition]) {
  1601             if ( ++count == limit ) {
  1602                 /* Uh oh, transition isn't completing. Should we assert? */
  1603                 break;
  1604             }
  1605             SDL_Delay(1);
  1606             SDL_PumpEvents();
  1607         }
  1608     }
  1609 
  1610     return succeeded;
  1611 }}
  1612 
  1613 int
  1614 Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
  1615 {
  1616     return 0;  /* just succeed, the real work is done elsewhere. */
  1617 }
  1618 
  1619 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1620 
  1621 /* vi: set ts=4 sw=4 expandtab: */