2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #if SDL_VIDEO_DRIVER_COCOA
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 */
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"
44 /* #define DEBUG_COCOAWINDOW */
46 #ifdef DEBUG_COCOAWINDOW
47 #define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
49 #define DLog(...) do { } while (0)
53 #define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN)
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;
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;
69 @implementation SDLWindow
71 - (BOOL)canBecomeKeyWindow
76 - (BOOL)canBecomeMainWindow
81 - (void)sendEvent:(NSEvent *)event
83 [super sendEvent:event];
85 if ([event type] != NSLeftMouseUp) {
89 id delegate = [self delegate];
90 if (![delegate isKindOfClass:[Cocoa_WindowListener class]]) {
94 if ([delegate isMoving]) {
95 [delegate windowDidFinishMoving];
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.
102 - (void)doCommandBySelector:(SEL)aSelector
104 /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
107 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
109 if (([sender draggingSourceOperationMask] & NSDragOperationGeneric) == NSDragOperationGeneric) {
110 return NSDragOperationGeneric;
113 return NSDragOperationNone; /* no idea what to do with this, reject it. */
116 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
119 NSPasteboard *pasteboard = [sender draggingPasteboard];
120 NSArray *types = [NSArray arrayWithObject:NSFilenamesPboardType];
121 NSString *desiredType = [pasteboard availableTypeFromArray:types];
122 if (desiredType == nil) {
123 return NO; /* can't accept anything that's being dropped here. */
126 NSData *data = [pasteboard dataForType:desiredType];
131 SDL_assert([desiredType isEqualToString:NSFilenamesPboardType]);
132 NSArray *array = [pasteboard propertyListForType:@"NSFilenamesPboardType"];
134 for (NSString *path in array) {
135 NSURL *fileURL = [[NSURL fileURLWithPath:path] autorelease];
136 NSNumber *isAlias = nil;
138 /* Functionality for resolving URL aliases was added with OS X 10.6. */
139 if ([fileURL respondsToSelector:@selector(getResourceValue:forKey:error:)]) {
140 [fileURL getResourceValue:&isAlias forKey:NSURLIsAliasFileKey error:nil];
143 /* If the URL is an alias, resolve it. */
144 if ([isAlias boolValue]) {
145 NSURLBookmarkResolutionOptions opts = NSURLBookmarkResolutionWithoutMounting | NSURLBookmarkResolutionWithoutUI;
146 NSData *bookmark = [NSURL bookmarkDataWithContentsOfURL:fileURL error:nil];
147 if (bookmark != nil) {
148 NSURL *resolvedURL = [NSURL URLByResolvingBookmarkData:bookmark
151 bookmarkDataIsStale:nil
154 if (resolvedURL != nil) {
155 fileURL = resolvedURL;
160 if (!SDL_SendDropFile([[fileURL path] UTF8String])) {
168 - (BOOL)wantsPeriodicDraggingUpdates
176 static Uint32 s_moveHack;
178 static void ConvertNSRect(NSScreen *screen, BOOL fullscreen, NSRect *r)
180 r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
184 ScheduleContextUpdates(SDL_WindowData *data)
186 NSOpenGLContext *currentContext = [NSOpenGLContext currentContext];
187 NSMutableArray *contexts = data->nscontexts;
188 @synchronized (contexts) {
189 for (SDLOpenGLContext *context in contexts) {
190 if (context == currentContext) {
193 [context scheduleUpdate];
200 GetHintCtrlClickEmulateRightClick()
202 const char *hint = SDL_GetHint( SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK );
203 return hint != NULL && *hint != '0';
207 GetWindowStyle(SDL_Window * window)
211 if (window->flags & SDL_WINDOW_FULLSCREEN) {
212 style = NSBorderlessWindowMask;
214 if (window->flags & SDL_WINDOW_BORDERLESS) {
215 style = NSBorderlessWindowMask;
217 style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
219 if (window->flags & SDL_WINDOW_RESIZABLE) {
220 style |= NSResizableWindowMask;
227 SetWindowStyle(SDL_Window * window, unsigned int style)
229 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
230 NSWindow *nswindow = data->nswindow;
232 if (![nswindow respondsToSelector: @selector(setStyleMask:)]) {
236 /* The view responder chain gets messed with during setStyleMask */
237 if ([[nswindow contentView] nextResponder] == data->listener) {
238 [[nswindow contentView] setNextResponder:nil];
241 [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)style];
243 /* The view responder chain gets messed with during setStyleMask */
244 if ([[nswindow contentView] nextResponder] != data->listener) {
245 [[nswindow contentView] setNextResponder:data->listener];
252 @implementation Cocoa_WindowListener
254 - (void)listen:(SDL_WindowData *)data
256 NSNotificationCenter *center;
257 NSWindow *window = data->nswindow;
258 NSView *view = [window contentView];
261 observingVisible = YES;
263 wasVisible = [window isVisible];
264 isFullscreenSpace = NO;
265 inFullscreenTransition = NO;
266 pendingWindowOperation = PENDING_OPERATION_NONE;
268 isDragAreaRunning = NO;
270 center = [NSNotificationCenter defaultCenter];
272 if ([window delegate] != nil) {
273 [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window];
274 [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window];
275 [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window];
276 [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
277 [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
278 [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
279 [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window];
280 [center addObserver:self selector:@selector(windowDidChangeBackingProperties:) name:NSWindowDidChangeBackingPropertiesNotification object:window];
281 [center addObserver:self selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:window];
282 [center addObserver:self selector:@selector(windowDidEnterFullScreen:) name:NSWindowDidEnterFullScreenNotification object:window];
283 [center addObserver:self selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:window];
284 [center addObserver:self selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:window];
285 [center addObserver:self selector:@selector(windowDidFailToEnterFullScreen:) name:@"NSWindowDidFailToEnterFullScreenNotification" object:window];
286 [center addObserver:self selector:@selector(windowDidFailToExitFullScreen:) name:@"NSWindowDidFailToExitFullScreenNotification" object:window];
288 [window setDelegate:self];
291 /* Haven't found a delegate / notification that triggers when the window is
292 * ordered out (is not visible any more). You can be ordered out without
293 * minimizing, so DidMiniaturize doesn't work. (e.g. -[NSWindow orderOut:])
295 [window addObserver:self
296 forKeyPath:@"visible"
297 options:NSKeyValueObservingOptionNew
300 [window setNextResponder:self];
301 [window setAcceptsMouseMovedEvents:YES];
303 [view setNextResponder:self];
305 if ([view respondsToSelector:@selector(setAcceptsTouchEvents:)]) {
306 [view setAcceptsTouchEvents:YES];
310 - (void)observeValueForKeyPath:(NSString *)keyPath
312 change:(NSDictionary *)change
313 context:(void *)context
315 if (!observingVisible) {
319 if (object == _data->nswindow && [keyPath isEqualToString:@"visible"]) {
320 int newVisibility = [[change objectForKey:@"new"] intValue];
322 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
324 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
329 -(void) pauseVisibleObservation
331 observingVisible = NO;
332 wasVisible = [_data->nswindow isVisible];
335 -(void) resumeVisibleObservation
337 BOOL isVisible = [_data->nswindow isVisible];
338 observingVisible = YES;
339 if (wasVisible != isVisible) {
341 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
343 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
346 wasVisible = isVisible;
350 -(BOOL) setFullscreenSpace:(BOOL) state
352 SDL_Window *window = _data->window;
353 NSWindow *nswindow = _data->nswindow;
354 SDL_VideoData *videodata = ((SDL_WindowData *) window->driverdata)->videodata;
356 if (!videodata->allow_spaces) {
357 return NO; /* Spaces are forcibly disabled. */
358 } else if (state && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) {
359 return NO; /* we only allow you to make a Space on FULLSCREEN_DESKTOP windows. */
360 } else if (!state && ((window->last_fullscreen_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) {
361 return NO; /* we only handle leaving the Space on windows that were previously FULLSCREEN_DESKTOP. */
362 } else if (state == isFullscreenSpace) {
363 return YES; /* already there. */
366 if (inFullscreenTransition) {
368 [self addPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
370 [self addPendingWindowOperation:PENDING_OPERATION_LEAVE_FULLSCREEN];
374 inFullscreenTransition = YES;
376 /* you need to be FullScreenPrimary, or toggleFullScreen doesn't work. Unset it again in windowDidExitFullScreen. */
377 [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
378 [nswindow performSelectorOnMainThread: @selector(toggleFullScreen:) withObject:nswindow waitUntilDone:NO];
382 -(BOOL) isInFullscreenSpace
384 return isFullscreenSpace;
387 -(BOOL) isInFullscreenSpaceTransition
389 return inFullscreenTransition;
392 -(void) addPendingWindowOperation:(PendingWindowOperation) operation
394 pendingWindowOperation = operation;
399 NSNotificationCenter *center;
400 NSWindow *window = _data->nswindow;
401 NSView *view = [window contentView];
403 center = [NSNotificationCenter defaultCenter];
405 if ([window delegate] != self) {
406 [center removeObserver:self name:NSWindowDidExposeNotification object:window];
407 [center removeObserver:self name:NSWindowDidMoveNotification object:window];
408 [center removeObserver:self name:NSWindowDidResizeNotification object:window];
409 [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
410 [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
411 [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
412 [center removeObserver:self name:NSWindowDidResignKeyNotification object:window];
413 [center removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:window];
414 [center removeObserver:self name:NSWindowWillEnterFullScreenNotification object:window];
415 [center removeObserver:self name:NSWindowDidEnterFullScreenNotification object:window];
416 [center removeObserver:self name:NSWindowWillExitFullScreenNotification object:window];
417 [center removeObserver:self name:NSWindowDidExitFullScreenNotification object:window];
418 [center removeObserver:self name:@"NSWindowDidFailToEnterFullScreenNotification" object:window];
419 [center removeObserver:self name:@"NSWindowDidFailToExitFullScreenNotification" object:window];
421 [window setDelegate:nil];
424 [window removeObserver:self forKeyPath:@"visible"];
426 if ([window nextResponder] == self) {
427 [window setNextResponder:nil];
429 if ([view nextResponder] == self) {
430 [view setNextResponder:nil];
439 -(void) setPendingMoveX:(int)x Y:(int)y
441 pendingWindowWarpX = x;
442 pendingWindowWarpY = y;
445 - (void)windowDidFinishMoving
447 if ([self isMoving]) {
450 SDL_Mouse *mouse = SDL_GetMouse();
451 if (pendingWindowWarpX != INT_MAX && pendingWindowWarpY != INT_MAX) {
452 mouse->WarpMouseGlobal(pendingWindowWarpX, pendingWindowWarpY);
453 pendingWindowWarpX = pendingWindowWarpY = INT_MAX;
455 if (mouse->relative_mode && !mouse->relative_mode_warp && mouse->focus == _data->window) {
456 mouse->SetRelativeMouseMode(SDL_TRUE);
461 - (BOOL)windowShouldClose:(id)sender
463 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
467 - (void)windowDidExpose:(NSNotification *)aNotification
469 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
472 - (void)windowWillMove:(NSNotification *)aNotification
474 if ([_data->nswindow isKindOfClass:[SDLWindow class]]) {
475 pendingWindowWarpX = pendingWindowWarpY = INT_MAX;
480 - (void)windowDidMove:(NSNotification *)aNotification
483 SDL_Window *window = _data->window;
484 NSWindow *nswindow = _data->nswindow;
485 BOOL fullscreen = window->flags & FULLSCREEN_MASK;
486 NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
487 ConvertNSRect([nswindow screen], fullscreen, &rect);
490 SDL_bool blockMove = ((SDL_GetTicks() - s_moveHack) < 500);
495 /* Cocoa is adjusting the window in response to a mode change */
496 rect.origin.x = window->x;
497 rect.origin.y = window->y;
498 ConvertNSRect([nswindow screen], fullscreen, &rect);
499 [nswindow setFrameOrigin:rect.origin];
504 x = (int)rect.origin.x;
505 y = (int)rect.origin.y;
507 ScheduleContextUpdates(_data);
509 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
512 - (void)windowDidResize:(NSNotification *)aNotification
514 if (inFullscreenTransition) {
515 /* We'll take care of this at the end of the transition */
519 SDL_Window *window = _data->window;
520 NSWindow *nswindow = _data->nswindow;
522 NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
523 ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
524 x = (int)rect.origin.x;
525 y = (int)rect.origin.y;
526 w = (int)rect.size.width;
527 h = (int)rect.size.height;
529 if (SDL_IsShapedWindow(window)) {
530 Cocoa_ResizeWindowShape(window);
533 ScheduleContextUpdates(_data);
535 /* The window can move during a resize event, such as when maximizing
536 or resizing from a corner */
537 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
538 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
540 const BOOL zoomed = [nswindow isZoomed];
542 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
544 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
548 - (void)windowDidMiniaturize:(NSNotification *)aNotification
550 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
553 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
555 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
558 - (void)windowDidBecomeKey:(NSNotification *)aNotification
560 SDL_Window *window = _data->window;
561 SDL_Mouse *mouse = SDL_GetMouse();
563 /* We're going to get keyboard events, since we're key. */
564 /* This needs to be done before restoring the relative mouse mode. */
565 SDL_SetKeyboardFocus(window);
567 if (mouse->relative_mode && !mouse->relative_mode_warp && ![self isMoving]) {
568 mouse->SetRelativeMouseMode(SDL_TRUE);
571 /* If we just gained focus we need the updated mouse position */
572 if (!mouse->relative_mode) {
576 point = [_data->nswindow mouseLocationOutsideOfEventStream];
578 y = (int)(window->h - point.y);
580 if (x >= 0 && x < window->w && y >= 0 && y < window->h) {
581 SDL_SendMouseMotion(window, 0, 0, x, y);
585 /* Check to see if someone updated the clipboard */
586 Cocoa_CheckClipboardUpdate(_data->videodata);
588 if ((isFullscreenSpace) && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)) {
589 [NSMenu setMenuBarVisible:NO];
592 /* On pre-10.6, you might have the capslock key state wrong now because we can't check here. */
593 if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_6) {
594 const unsigned int newflags = [NSEvent modifierFlags] & NSAlphaShiftKeyMask;
595 _data->videodata->modifierFlags = (_data->videodata->modifierFlags & ~NSAlphaShiftKeyMask) | newflags;
596 SDL_ToggleModState(KMOD_CAPS, newflags != 0);
600 - (void)windowDidResignKey:(NSNotification *)aNotification
602 SDL_Mouse *mouse = SDL_GetMouse();
603 if (mouse->relative_mode && !mouse->relative_mode_warp) {
604 mouse->SetRelativeMouseMode(SDL_FALSE);
607 /* Some other window will get mouse events, since we're not key. */
608 if (SDL_GetMouseFocus() == _data->window) {
609 SDL_SetMouseFocus(NULL);
612 /* Some other window will get keyboard events, since we're not key. */
613 if (SDL_GetKeyboardFocus() == _data->window) {
614 SDL_SetKeyboardFocus(NULL);
617 if (isFullscreenSpace) {
618 [NSMenu setMenuBarVisible:YES];
622 - (void)windowDidChangeBackingProperties:(NSNotification *)aNotification
624 NSNumber *oldscale = [[aNotification userInfo] objectForKey:NSBackingPropertyOldScaleFactorKey];
626 if (inFullscreenTransition) {
630 if ([oldscale doubleValue] != [_data->nswindow backingScaleFactor]) {
631 /* Force a resize event when the backing scale factor changes. */
632 _data->window->w = 0;
633 _data->window->h = 0;
634 [self windowDidResize:aNotification];
638 - (void)windowWillEnterFullScreen:(NSNotification *)aNotification
640 SDL_Window *window = _data->window;
642 SetWindowStyle(window, (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask));
644 isFullscreenSpace = YES;
645 inFullscreenTransition = YES;
648 - (void)windowDidFailToEnterFullScreen:(NSNotification *)aNotification
650 SDL_Window *window = _data->window;
652 if (window->is_destroying) {
656 SetWindowStyle(window, GetWindowStyle(window));
658 isFullscreenSpace = NO;
659 inFullscreenTransition = NO;
661 [self windowDidExitFullScreen:nil];
664 - (void)windowDidEnterFullScreen:(NSNotification *)aNotification
666 SDL_Window *window = _data->window;
668 inFullscreenTransition = NO;
670 if (pendingWindowOperation == PENDING_OPERATION_LEAVE_FULLSCREEN) {
671 pendingWindowOperation = PENDING_OPERATION_NONE;
672 [self setFullscreenSpace:NO];
674 if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
675 [NSMenu setMenuBarVisible:NO];
678 pendingWindowOperation = PENDING_OPERATION_NONE;
679 /* Force the size change event in case it was delivered earlier
680 while the window was still animating into place.
684 [self windowDidResize:aNotification];
688 - (void)windowWillExitFullScreen:(NSNotification *)aNotification
690 SDL_Window *window = _data->window;
692 /* As of OS X 10.11, the window seems to need to be resizable when exiting
693 a Space, in order for it to resize back to its windowed-mode size.
695 SetWindowStyle(window, GetWindowStyle(window) | NSResizableWindowMask);
697 isFullscreenSpace = NO;
698 inFullscreenTransition = YES;
701 - (void)windowDidFailToExitFullScreen:(NSNotification *)aNotification
703 SDL_Window *window = _data->window;
705 if (window->is_destroying) {
709 SetWindowStyle(window, (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask));
711 isFullscreenSpace = YES;
712 inFullscreenTransition = NO;
714 [self windowDidEnterFullScreen:nil];
717 - (void)windowDidExitFullScreen:(NSNotification *)aNotification
719 SDL_Window *window = _data->window;
720 NSWindow *nswindow = _data->nswindow;
722 inFullscreenTransition = NO;
724 SetWindowStyle(window, GetWindowStyle(window));
726 [nswindow setLevel:kCGNormalWindowLevel];
728 if (pendingWindowOperation == PENDING_OPERATION_ENTER_FULLSCREEN) {
729 pendingWindowOperation = PENDING_OPERATION_NONE;
730 [self setFullscreenSpace:YES];
731 } else if (pendingWindowOperation == PENDING_OPERATION_MINIMIZE) {
732 pendingWindowOperation = PENDING_OPERATION_NONE;
733 [nswindow miniaturize:nil];
735 /* Adjust the fullscreen toggle button and readd menu now that we're here. */
736 if (window->flags & SDL_WINDOW_RESIZABLE) {
737 /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
738 [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
740 [nswindow setCollectionBehavior:NSWindowCollectionBehaviorManaged];
742 [NSMenu setMenuBarVisible:YES];
744 pendingWindowOperation = PENDING_OPERATION_NONE;
745 /* Force the size change event in case it was delivered earlier
746 while the window was still animating into place.
750 [self windowDidResize:aNotification];
752 /* FIXME: Why does the window get hidden? */
753 if (window->flags & SDL_WINDOW_SHOWN) {
754 Cocoa_ShowWindow(SDL_GetVideoDevice(), window);
759 -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
761 if ((_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
762 return NSApplicationPresentationFullScreen | NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar;
764 return proposedOptions;
769 /* We'll respond to key events by doing nothing so we don't beep.
770 * We could handle key messages here, but we lose some in the NSApp dispatch,
771 * where they get converted to action messages, etc.
773 - (void)flagsChanged:(NSEvent *)theEvent
775 /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
777 - (void)keyDown:(NSEvent *)theEvent
779 /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
781 - (void)keyUp:(NSEvent *)theEvent
783 /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
786 /* We'll respond to selectors by doing nothing so we don't beep.
787 * The escape key gets converted to a "cancel" selector, etc.
789 - (void)doCommandBySelector:(SEL)aSelector
791 /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
794 - (BOOL)processHitTest:(NSEvent *)theEvent
796 SDL_assert(isDragAreaRunning == [_data->nswindow isMovableByWindowBackground]);
798 if (_data->window->hit_test) { /* if no hit-test, skip this. */
799 const NSPoint location = [theEvent locationInWindow];
800 const SDL_Point point = { (int) location.x, _data->window->h - (((int) location.y)-1) };
801 const SDL_HitTestResult rc = _data->window->hit_test(_data->window, &point, _data->window->hit_test_data);
802 if (rc == SDL_HITTEST_DRAGGABLE) {
803 if (!isDragAreaRunning) {
804 isDragAreaRunning = YES;
805 [_data->nswindow setMovableByWindowBackground:YES];
807 return YES; /* dragging! */
811 if (isDragAreaRunning) {
812 isDragAreaRunning = NO;
813 [_data->nswindow setMovableByWindowBackground:NO];
814 return YES; /* was dragging, drop event. */
817 return NO; /* not a special area, carry on. */
820 - (void)mouseDown:(NSEvent *)theEvent
824 /* Ignore events that aren't inside the client area (i.e. title bar.) */
825 if ([theEvent window]) {
826 NSRect windowRect = [[[theEvent window] contentView] frame];
828 /* add one to size, since NSPointInRect is exclusive of the bottom
829 edges, which mean it misses the top of the window by one pixel
830 (as the origin is the bottom left). */
831 windowRect.size.width += 1;
832 windowRect.size.height += 1;
834 if (!NSPointInRect([theEvent locationInWindow], windowRect)) {
839 if ([self processHitTest:theEvent]) {
840 return; /* dragging, drop event. */
843 switch ([theEvent buttonNumber]) {
845 if (([theEvent modifierFlags] & NSControlKeyMask) &&
846 GetHintCtrlClickEmulateRightClick()) {
848 button = SDL_BUTTON_RIGHT;
851 button = SDL_BUTTON_LEFT;
855 button = SDL_BUTTON_RIGHT;
858 button = SDL_BUTTON_MIDDLE;
861 button = [theEvent buttonNumber] + 1;
864 SDL_SendMouseButton(_data->window, 0, SDL_PRESSED, button);
867 - (void)rightMouseDown:(NSEvent *)theEvent
869 [self mouseDown:theEvent];
872 - (void)otherMouseDown:(NSEvent *)theEvent
874 [self mouseDown:theEvent];
877 - (void)mouseUp:(NSEvent *)theEvent
881 if ([self processHitTest:theEvent]) {
882 return; /* stopped dragging, drop event. */
885 switch ([theEvent buttonNumber]) {
888 button = SDL_BUTTON_RIGHT;
891 button = SDL_BUTTON_LEFT;
895 button = SDL_BUTTON_RIGHT;
898 button = SDL_BUTTON_MIDDLE;
901 button = [theEvent buttonNumber] + 1;
904 SDL_SendMouseButton(_data->window, 0, SDL_RELEASED, button);
907 - (void)rightMouseUp:(NSEvent *)theEvent
909 [self mouseUp:theEvent];
912 - (void)otherMouseUp:(NSEvent *)theEvent
914 [self mouseUp:theEvent];
917 - (void)mouseMoved:(NSEvent *)theEvent
919 SDL_Mouse *mouse = SDL_GetMouse();
920 SDL_Window *window = _data->window;
924 if ([self processHitTest:theEvent]) {
925 return; /* dragging, drop event. */
928 if (mouse->relative_mode) {
932 point = [theEvent locationInWindow];
934 y = (int)(window->h - point.y);
936 if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
937 if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
940 } else if (x >= window->w) {
945 } else if (y >= window->h) {
949 #if !SDL_MAC_NO_SANDBOX
952 /* When SDL_MAC_NO_SANDBOX is set, this is handled by
953 * SDL_cocoamousetap.m.
956 cgpoint.x = window->x + x;
957 cgpoint.y = window->y + y;
959 /* According to the docs, this was deprecated in 10.6, but it's still
960 * around. The substitute requires a CGEventSource, but I'm not entirely
961 * sure how we'd procure the right one for this event.
963 CGSetLocalEventsSuppressionInterval(0.0);
964 CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
965 CGSetLocalEventsSuppressionInterval(0.25);
967 Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
971 SDL_SendMouseMotion(window, 0, 0, x, y);
974 - (void)mouseDragged:(NSEvent *)theEvent
976 [self mouseMoved:theEvent];
979 - (void)rightMouseDragged:(NSEvent *)theEvent
981 [self mouseMoved:theEvent];
984 - (void)otherMouseDragged:(NSEvent *)theEvent
986 [self mouseMoved:theEvent];
989 - (void)scrollWheel:(NSEvent *)theEvent
991 Cocoa_HandleMouseWheel(_data->window, theEvent);
994 - (void)touchesBeganWithEvent:(NSEvent *) theEvent
996 NSSet *touches = [theEvent touchesMatchingPhase:NSTouchPhaseAny inView:nil];
997 int existingTouchCount = 0;
999 for (NSTouch* touch in touches) {
1000 if ([touch phase] != NSTouchPhaseBegan) {
1001 existingTouchCount++;
1004 if (existingTouchCount == 0) {
1005 SDL_TouchID touchID = (SDL_TouchID)(intptr_t)[[touches anyObject] device];
1006 int numFingers = SDL_GetNumTouchFingers(touchID);
1007 DLog("Reset Lost Fingers: %d", numFingers);
1008 for (--numFingers; numFingers >= 0; --numFingers) {
1009 SDL_Finger* finger = SDL_GetTouchFinger(touchID, numFingers);
1010 SDL_SendTouch(touchID, finger->id, SDL_FALSE, 0, 0, 0);
1014 DLog("Began Fingers: %lu .. existing: %d", (unsigned long)[touches count], existingTouchCount);
1015 [self handleTouches:NSTouchPhaseBegan withEvent:theEvent];
1018 - (void)touchesMovedWithEvent:(NSEvent *) theEvent
1020 [self handleTouches:NSTouchPhaseMoved withEvent:theEvent];
1023 - (void)touchesEndedWithEvent:(NSEvent *) theEvent
1025 [self handleTouches:NSTouchPhaseEnded withEvent:theEvent];
1028 - (void)touchesCancelledWithEvent:(NSEvent *) theEvent
1030 [self handleTouches:NSTouchPhaseCancelled withEvent:theEvent];
1033 - (void)handleTouches:(NSTouchPhase) phase withEvent:(NSEvent *) theEvent
1035 NSSet *touches = [theEvent touchesMatchingPhase:phase inView:nil];
1037 for (NSTouch *touch in touches) {
1038 const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device];
1039 if (SDL_AddTouch(touchId, "") < 0) {
1043 const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
1044 float x = [touch normalizedPosition].x;
1045 float y = [touch normalizedPosition].y;
1046 /* Make the origin the upper left instead of the lower left */
1050 case NSTouchPhaseBegan:
1051 SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
1053 case NSTouchPhaseEnded:
1054 case NSTouchPhaseCancelled:
1055 SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
1057 case NSTouchPhaseMoved:
1058 SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
1068 @interface SDLView : NSView {
1069 SDL_Window *_sdlWindow;
1072 - (void)setSDLWindow:(SDL_Window*)window;
1074 /* The default implementation doesn't pass rightMouseDown to responder chain */
1075 - (void)rightMouseDown:(NSEvent *)theEvent;
1076 - (BOOL)mouseDownCanMoveWindow;
1077 - (void)drawRect:(NSRect)dirtyRect;
1080 @implementation SDLView
1081 - (void)setSDLWindow:(SDL_Window*)window
1083 _sdlWindow = window;
1086 - (void)drawRect:(NSRect)dirtyRect
1088 SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0);
1091 - (void)rightMouseDown:(NSEvent *)theEvent
1093 [[self nextResponder] rightMouseDown:theEvent];
1096 - (BOOL)mouseDownCanMoveWindow
1098 /* Always say YES, but this doesn't do anything until we call
1099 -[NSWindow setMovableByWindowBackground:YES], which we ninja-toggle
1100 during mouse events when we're using a drag area. */
1104 - (void)resetCursorRects
1106 [super resetCursorRects];
1107 SDL_Mouse *mouse = SDL_GetMouse();
1109 if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
1110 [self addCursorRect:[self bounds]
1111 cursor:mouse->cur_cursor->driverdata];
1113 [self addCursorRect:[self bounds]
1114 cursor:[NSCursor invisibleCursor]];
1120 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
1123 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
1124 SDL_WindowData *data;
1126 /* Allocate the window data */
1127 window->driverdata = data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
1129 return SDL_OutOfMemory();
1131 data->window = window;
1132 data->nswindow = nswindow;
1133 data->created = created;
1134 data->videodata = videodata;
1135 data->nscontexts = [[NSMutableArray alloc] init];
1137 /* Create an event listener for the window */
1138 data->listener = [[Cocoa_WindowListener alloc] init];
1140 /* Fill in the SDL window with the window data */
1142 NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
1143 ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
1144 window->x = (int)rect.origin.x;
1145 window->y = (int)rect.origin.y;
1146 window->w = (int)rect.size.width;
1147 window->h = (int)rect.size.height;
1150 /* Set up the listener after we create the view */
1151 [data->listener listen:data];
1153 if ([nswindow isVisible]) {
1154 window->flags |= SDL_WINDOW_SHOWN;
1156 window->flags &= ~SDL_WINDOW_SHOWN;
1160 unsigned int style = [nswindow styleMask];
1162 if (style == NSBorderlessWindowMask) {
1163 window->flags |= SDL_WINDOW_BORDERLESS;
1165 window->flags &= ~SDL_WINDOW_BORDERLESS;
1167 if (style & NSResizableWindowMask) {
1168 window->flags |= SDL_WINDOW_RESIZABLE;
1170 window->flags &= ~SDL_WINDOW_RESIZABLE;
1174 /* isZoomed always returns true if the window is not resizable */
1175 if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
1176 window->flags |= SDL_WINDOW_MAXIMIZED;
1178 window->flags &= ~SDL_WINDOW_MAXIMIZED;
1181 if ([nswindow isMiniaturized]) {
1182 window->flags |= SDL_WINDOW_MINIMIZED;
1184 window->flags &= ~SDL_WINDOW_MINIMIZED;
1187 if ([nswindow isKeyWindow]) {
1188 window->flags |= SDL_WINDOW_INPUT_FOCUS;
1189 SDL_SetKeyboardFocus(data->window);
1192 /* Prevents the window's "window device" from being destroyed when it is
1193 * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
1195 [nswindow setOneShot:NO];
1198 window->driverdata = data;
1203 Cocoa_CreateWindow(_THIS, SDL_Window * window)
1206 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
1208 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1212 NSArray *screens = [NSScreen screens];
1214 Cocoa_GetDisplayBounds(_this, display, &bounds);
1215 rect.origin.x = window->x;
1216 rect.origin.y = window->y;
1217 rect.size.width = window->w;
1218 rect.size.height = window->h;
1219 ConvertNSRect([screens objectAtIndex:0], (window->flags & FULLSCREEN_MASK), &rect);
1221 style = GetWindowStyle(window);
1223 /* Figure out which screen to place this window */
1224 NSScreen *screen = nil;
1225 for (NSScreen *candidate in screens) {
1226 NSRect screenRect = [candidate frame];
1227 if (rect.origin.x >= screenRect.origin.x &&
1228 rect.origin.x < screenRect.origin.x + screenRect.size.width &&
1229 rect.origin.y >= screenRect.origin.y &&
1230 rect.origin.y < screenRect.origin.y + screenRect.size.height) {
1232 rect.origin.x -= screenRect.origin.x;
1233 rect.origin.y -= screenRect.origin.y;
1238 nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
1240 @catch (NSException *e) {
1241 return SDL_SetError("%s", [[e reason] UTF8String]);
1243 [nswindow setBackgroundColor:[NSColor blackColor]];
1245 if (videodata->allow_spaces) {
1246 SDL_assert(floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6);
1247 SDL_assert([nswindow respondsToSelector:@selector(toggleFullScreen:)]);
1248 /* we put FULLSCREEN_DESKTOP windows in their own Space, without a toggle button or menubar, later */
1249 if (window->flags & SDL_WINDOW_RESIZABLE) {
1250 /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
1251 [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
1255 /* Create a default view for this window */
1256 rect = [nswindow contentRectForFrameRect:[nswindow frame]];
1257 SDLView *contentView = [[SDLView alloc] initWithFrame:rect];
1258 [contentView setSDLWindow:window];
1260 if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
1261 if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
1262 [contentView setWantsBestResolutionOpenGLSurface:YES];
1266 [nswindow setContentView: contentView];
1267 [contentView release];
1269 /* Allow files and folders to be dragged onto the window by users */
1270 [nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
1272 if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
1280 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
1283 NSWindow *nswindow = (NSWindow *) data;
1286 /* Query the title from the existing window */
1287 title = [nswindow title];
1289 window->title = SDL_strdup([title UTF8String]);
1292 return SetupWindowData(_this, window, nswindow, SDL_FALSE);
1296 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
1299 const char *title = window->title ? window->title : "";
1300 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
1301 NSString *string = [[NSString alloc] initWithUTF8String:title];
1302 [nswindow setTitle:string];
1307 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
1310 NSImage *nsimage = Cocoa_CreateImage(icon);
1313 [NSApp setApplicationIconImage:nsimage];
1318 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
1321 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
1322 NSWindow *nswindow = windata->nswindow;
1326 rect.origin.x = window->x;
1327 rect.origin.y = window->y;
1328 rect.size.width = window->w;
1329 rect.size.height = window->h;
1330 ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
1332 moveHack = s_moveHack;
1334 [nswindow setFrameOrigin:rect.origin];
1335 s_moveHack = moveHack;
1337 ScheduleContextUpdates(windata);
1341 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
1344 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
1345 NSWindow *nswindow = windata->nswindow;
1349 /* Cocoa will resize the window from the bottom-left rather than the
1350 * top-left when -[nswindow setContentSize:] is used, so we must set the
1351 * entire frame based on the new size, in order to preserve the position.
1353 rect.origin.x = window->x;
1354 rect.origin.y = window->y;
1355 rect.size.width = window->w;
1356 rect.size.height = window->h;
1357 ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
1359 moveHack = s_moveHack;
1361 [nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES];
1362 s_moveHack = moveHack;
1364 ScheduleContextUpdates(windata);
1368 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
1371 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
1374 minSize.width = window->min_w;
1375 minSize.height = window->min_h;
1377 [windata->nswindow setContentMinSize:minSize];
1381 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
1384 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
1387 maxSize.width = window->max_w;
1388 maxSize.height = window->max_h;
1390 [windata->nswindow setContentMaxSize:maxSize];
1394 Cocoa_ShowWindow(_THIS, SDL_Window * window)
1397 SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
1398 NSWindow *nswindow = windowData->nswindow;
1400 if (![nswindow isMiniaturized]) {
1401 [windowData->listener pauseVisibleObservation];
1402 [nswindow makeKeyAndOrderFront:nil];
1403 [windowData->listener resumeVisibleObservation];
1408 Cocoa_HideWindow(_THIS, SDL_Window * window)
1411 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
1413 [nswindow orderOut:nil];
1417 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
1420 SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
1421 NSWindow *nswindow = windowData->nswindow;
1423 /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
1424 a minimized or hidden window, so check for that before showing it.
1426 [windowData->listener pauseVisibleObservation];
1427 if (![nswindow isMiniaturized] && [nswindow isVisible]) {
1428 [NSApp activateIgnoringOtherApps:YES];
1429 [nswindow makeKeyAndOrderFront:nil];
1431 [windowData->listener resumeVisibleObservation];
1435 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
1438 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
1439 NSWindow *nswindow = windata->nswindow;
1441 [nswindow zoom:nil];
1443 ScheduleContextUpdates(windata);
1447 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
1450 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1451 NSWindow *nswindow = data->nswindow;
1453 if ([data->listener isInFullscreenSpaceTransition]) {
1454 [data->listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
1456 [nswindow miniaturize:nil];
1461 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
1464 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
1466 if ([nswindow isMiniaturized]) {
1467 [nswindow deminiaturize:nil];
1468 } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
1469 [nswindow zoom:nil];
1474 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
1476 if (!data->created) {
1477 /* Don't mess with other people's windows... */
1481 [data->listener close];
1482 data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
1483 [data->nswindow setContentView:[nswindow contentView]];
1484 [data->nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
1485 /* See comment in SetupWindowData. */
1486 [data->nswindow setOneShot:NO];
1487 [data->listener listen:data];
1491 return data->nswindow;
1495 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
1498 if (SetWindowStyle(window, GetWindowStyle(window))) {
1500 Cocoa_SetWindowTitle(_this, window); /* this got blanked out. */
1507 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
1510 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1511 NSWindow *nswindow = data->nswindow;
1514 /* The view responder chain gets messed with during setStyleMask */
1515 if ([[nswindow contentView] nextResponder] == data->listener) {
1516 [[nswindow contentView] setNextResponder:nil];
1522 Cocoa_GetDisplayBounds(_this, display, &bounds);
1523 rect.origin.x = bounds.x;
1524 rect.origin.y = bounds.y;
1525 rect.size.width = bounds.w;
1526 rect.size.height = bounds.h;
1527 ConvertNSRect([nswindow screen], fullscreen, &rect);
1529 /* Hack to fix origin on Mac OS X 10.4 */
1530 NSRect screenRect = [[nswindow screen] frame];
1531 if (screenRect.size.height >= 1.0f) {
1532 rect.origin.y += (screenRect.size.height - rect.size.height);
1535 if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
1536 [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
1538 nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
1541 rect.origin.x = window->windowed.x;
1542 rect.origin.y = window->windowed.y;
1543 rect.size.width = window->windowed.w;
1544 rect.size.height = window->windowed.h;
1545 ConvertNSRect([nswindow screen], fullscreen, &rect);
1547 if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
1548 [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
1550 /* Hack to restore window decorations on Mac OS X 10.10 */
1551 NSRect frameRect = [nswindow frame];
1552 [nswindow setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
1553 [nswindow setFrame:frameRect display:NO];
1555 nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
1559 /* The view responder chain gets messed with during setStyleMask */
1560 if ([[nswindow contentView] nextResponder] != data->listener) {
1561 [[nswindow contentView] setNextResponder:data->listener];
1565 [nswindow setContentSize:rect.size];
1566 [nswindow setFrameOrigin:rect.origin];
1567 s_moveHack = SDL_GetTicks();
1569 /* When the window style changes the title is cleared */
1571 Cocoa_SetWindowTitle(_this, window);
1574 if (SDL_ShouldAllowTopmost() && fullscreen) {
1575 /* OpenGL is rendering to the window, so make it visible! */
1576 [nswindow setLevel:CGShieldingWindowLevel()];
1578 [nswindow setLevel:kCGNormalWindowLevel];
1581 if ([nswindow isVisible] || fullscreen) {
1582 [data->listener pauseVisibleObservation];
1583 [nswindow makeKeyAndOrderFront:nil];
1584 [data->listener resumeVisibleObservation];
1587 ScheduleContextUpdates(data);
1591 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
1593 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1594 CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
1595 const uint32_t tableSize = 256;
1596 CGGammaValue redTable[tableSize];
1597 CGGammaValue greenTable[tableSize];
1598 CGGammaValue blueTable[tableSize];
1600 float inv65535 = 1.0f / 65535.0f;
1602 /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
1603 for (i = 0; i < 256; i++) {
1604 redTable[i] = ramp[0*256+i] * inv65535;
1605 greenTable[i] = ramp[1*256+i] * inv65535;
1606 blueTable[i] = ramp[2*256+i] * inv65535;
1609 if (CGSetDisplayTransferByTable(display_id, tableSize,
1610 redTable, greenTable, blueTable) != CGDisplayNoErr) {
1611 return SDL_SetError("CGSetDisplayTransferByTable()");
1617 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
1619 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1620 CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
1621 const uint32_t tableSize = 256;
1622 CGGammaValue redTable[tableSize];
1623 CGGammaValue greenTable[tableSize];
1624 CGGammaValue blueTable[tableSize];
1625 uint32_t i, tableCopied;
1627 if (CGGetDisplayTransferByTable(display_id, tableSize,
1628 redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
1629 return SDL_SetError("CGGetDisplayTransferByTable()");
1632 for (i = 0; i < tableCopied; i++) {
1633 ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
1634 ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
1635 ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
1641 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
1643 /* Move the cursor to the nearest point in the window */
1644 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1645 if (grabbed && data && ![data->listener isMoving]) {
1649 SDL_GetMouseState(&x, &y);
1650 cgpoint.x = window->x + x;
1651 cgpoint.y = window->y + y;
1653 Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
1655 DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
1656 CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
1659 if ( data && (window->flags & SDL_WINDOW_FULLSCREEN) ) {
1660 if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)
1661 && ![data->listener isInFullscreenSpace]) {
1662 /* OpenGL is rendering to the window, so make it visible! */
1663 /* Doing this in 10.11 while in a Space breaks things (bug #3152) */
1664 [data->nswindow setLevel:CGShieldingWindowLevel()];
1666 [data->nswindow setLevel:kCGNormalWindowLevel];
1672 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
1675 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1678 if ([data->listener isInFullscreenSpace]) {
1679 [NSMenu setMenuBarVisible:YES];
1681 [data->listener close];
1682 [data->listener release];
1683 if (data->created) {
1684 [data->nswindow close];
1687 NSArray *contexts = [[data->nscontexts copy] autorelease];
1688 for (SDLOpenGLContext *context in contexts) {
1689 /* Calling setWindow:NULL causes the context to remove itself from the context list. */
1690 [context setWindow:NULL];
1692 [data->nscontexts release];
1696 window->driverdata = NULL;
1700 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
1702 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
1704 if (info->version.major <= SDL_MAJOR_VERSION) {
1705 info->subsystem = SDL_SYSWM_COCOA;
1706 info->info.cocoa.window = nswindow;
1709 SDL_SetError("Application not compiled with SDL %d.%d\n",
1710 SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
1716 Cocoa_IsWindowInFullscreenSpace(SDL_Window * window)
1718 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1720 if ([data->listener isInFullscreenSpace]) {
1728 Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
1731 SDL_bool succeeded = SDL_FALSE;
1732 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1734 if ([data->listener setFullscreenSpace:(state ? YES : NO)]) {
1735 const int maxattempts = 3;
1737 while (++attempt <= maxattempts) {
1738 /* Wait for the transition to complete, so application changes
1739 take effect properly (e.g. setting the window size, etc.)
1741 const int limit = 10000;
1743 while ([data->listener isInFullscreenSpaceTransition]) {
1744 if ( ++count == limit ) {
1745 /* Uh oh, transition isn't completing. Should we assert? */
1751 if ([data->listener isInFullscreenSpace] == (state ? YES : NO))
1753 /* Try again, the last attempt was interrupted by user gestures */
1754 if (![data->listener setFullscreenSpace:(state ? YES : NO)])
1757 /* Return TRUE to prevent non-space fullscreen logic from running */
1758 succeeded = SDL_TRUE;
1765 Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
1767 return 0; /* just succeed, the real work is done elsewhere. */
1770 #endif /* SDL_VIDEO_DRIVER_COCOA */
1772 /* vi: set ts=4 sw=4 expandtab: */