Fixed bug 2073 - Mac: window moves unexpectedly when exiting SDL_WINDOW_FULLSCREEN_DESKTOP mode
Alex Szpakowski
In Mac OS X, when SDL_SetWindowFullscreen(window, 0) is called on a window which was in SDL_WINDOW_FULLSCREEN_DESKTOP mode, its original size is restored but its position is moved to the bottom of the screen.
I tracked down the issue to these two lines: http://hg.libsdl.org/SDL/file/66b5b8446275/src/video/cocoa/SDL_cocoawindow.m#l1034
I believe [nswindow setFrameOrigin:rect.origin] implicitly calls [nswindow constrainFrameRect:rect toScreen:screen], which will attempt to constrain the window to the screen, but at that point the window size is still full-screen rather than the restored window size, so the constrainFrameRect function operates on the wrong window size.
https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSWindow_Class/Reference/Reference.html#//apple_ref/occ/instm/NSWindow/constrainFrameRect:toScreen:
I resolved the issue by swapping the order of the function calls, like so:
[nswindow setContentSize:rect.size];
[nswindow setFrameOrigin:rect.origin];
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2013 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_config.h"
23 #if SDL_VIDEO_DRIVER_COCOA
25 #include "SDL_syswm.h"
26 #include "SDL_timer.h" /* For SDL_GetTicks() */
27 #include "../SDL_sysvideo.h"
28 #include "../../events/SDL_keyboard_c.h"
29 #include "../../events/SDL_mouse_c.h"
30 #include "../../events/SDL_touch_c.h"
31 #include "../../events/SDL_windowevents_c.h"
32 #include "SDL_cocoavideo.h"
33 #include "SDL_cocoashape.h"
34 #include "SDL_cocoamouse.h"
35 #include "SDL_cocoaopengl.h"
37 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
38 /* Taken from AppKit/NSOpenGLView.h in 10.8 SDK. */
39 @interface NSView (NSOpenGLSurfaceResolution)
40 - (BOOL)wantsBestResolutionOpenGLSurface;
41 - (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
45 static Uint32 s_moveHack;
47 static void ConvertNSRect(NSRect *r)
49 r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
52 static void ScheduleContextUpdates(SDL_WindowData *data)
54 NSMutableArray *contexts = data->nscontexts;
55 @synchronized (contexts) {
56 for (SDLOpenGLContext *context in contexts) {
57 [context scheduleUpdate];
62 @implementation Cocoa_WindowListener
64 - (void)listen:(SDL_WindowData *)data
66 NSNotificationCenter *center;
67 NSWindow *window = data->nswindow;
68 NSView *view = [window contentView];
71 observingVisible = YES;
73 wasVisible = [window isVisible];
75 center = [NSNotificationCenter defaultCenter];
77 if ([window delegate] != nil) {
78 [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window];
79 [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window];
80 [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window];
81 [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
82 [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
83 [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
84 [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window];
86 [window setDelegate:self];
89 /* Haven't found a delegate / notification that triggers when the window is
90 * ordered out (is not visible any more). You can be ordered out without
91 * minimizing, so DidMiniaturize doesn't work. (e.g. -[NSWindow orderOut:])
93 [window addObserver:self
95 options:NSKeyValueObservingOptionNew
98 [window setNextResponder:self];
99 [window setAcceptsMouseMovedEvents:YES];
101 [view setNextResponder:self];
103 if ([view respondsToSelector:@selector(setAcceptsTouchEvents:)]) {
104 [view setAcceptsTouchEvents:YES];
108 - (void)observeValueForKeyPath:(NSString *)keyPath
110 change:(NSDictionary *)change
111 context:(void *)context
113 if (!observingVisible) {
117 if (object == _data->nswindow && [keyPath isEqualToString:@"visible"]) {
118 int newVisibility = [[change objectForKey:@"new"] intValue];
120 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
122 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
127 -(void) pauseVisibleObservation
129 observingVisible = NO;
130 wasVisible = [_data->nswindow isVisible];
133 -(void) resumeVisibleObservation
135 BOOL isVisible = [_data->nswindow isVisible];
136 observingVisible = YES;
137 if (wasVisible != isVisible) {
139 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
141 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
144 wasVisible = isVisible;
150 NSNotificationCenter *center;
151 NSWindow *window = _data->nswindow;
152 NSView *view = [window contentView];
153 NSArray *windows = nil;
155 center = [NSNotificationCenter defaultCenter];
157 if ([window delegate] != self) {
158 [center removeObserver:self name:NSWindowDidExposeNotification object:window];
159 [center removeObserver:self name:NSWindowDidMoveNotification object:window];
160 [center removeObserver:self name:NSWindowDidResizeNotification object:window];
161 [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
162 [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
163 [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
164 [center removeObserver:self name:NSWindowDidResignKeyNotification object:window];
166 [window setDelegate:nil];
169 [window removeObserver:self
170 forKeyPath:@"visible"];
172 if ([window nextResponder] == self) {
173 [window setNextResponder:nil];
175 if ([view nextResponder] == self) {
176 [view setNextResponder:nil];
179 /* Make the next window in the z-order Key. If we weren't the foreground
180 when closed, this is a no-op.
181 !!! FIXME: Note that this is a hack, and there are corner cases where
182 !!! FIXME: this fails (such as the About box). The typical nib+RunLoop
183 !!! FIXME: handles this for Cocoa apps, but we bypass all that in SDL.
184 !!! FIXME: We should remove this code when we find a better way to
185 !!! FIXME: have the system do this for us. See discussion in
186 !!! FIXME: http://bugzilla.libsdl.org/show_bug.cgi?id=1825
188 windows = [NSApp orderedWindows];
189 if ([windows count] > 0) {
190 NSWindow *win = (NSWindow *) [windows objectAtIndex:0];
191 [win makeKeyAndOrderFront:self];
195 - (BOOL)windowShouldClose:(id)sender
197 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
201 - (void)windowDidExpose:(NSNotification *)aNotification
203 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
206 - (void)windowDidMove:(NSNotification *)aNotification
209 SDL_Window *window = _data->window;
210 NSWindow *nswindow = _data->nswindow;
211 NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
212 ConvertNSRect(&rect);
215 SDL_bool blockMove = ((SDL_GetTicks() - s_moveHack) < 500);
220 /* Cocoa is adjusting the window in response to a mode change */
221 rect.origin.x = window->x;
222 rect.origin.y = window->y;
223 ConvertNSRect(&rect);
224 [nswindow setFrameOrigin:rect.origin];
229 x = (int)rect.origin.x;
230 y = (int)rect.origin.y;
232 ScheduleContextUpdates(_data);
234 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
237 - (void)windowDidResize:(NSNotification *)aNotification
240 NSRect rect = [_data->nswindow contentRectForFrameRect:[_data->nswindow frame]];
241 ConvertNSRect(&rect);
242 x = (int)rect.origin.x;
243 y = (int)rect.origin.y;
244 w = (int)rect.size.width;
245 h = (int)rect.size.height;
246 if (SDL_IsShapedWindow(_data->window))
247 Cocoa_ResizeWindowShape(_data->window);
249 ScheduleContextUpdates(_data);
251 /* The window can move during a resize event, such as when maximizing
252 or resizing from a corner */
253 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MOVED, x, y);
254 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESIZED, w, h);
256 const BOOL zoomed = [_data->nswindow isZoomed];
258 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
260 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
264 - (void)windowDidMiniaturize:(NSNotification *)aNotification
266 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
269 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
271 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
274 - (void)windowDidBecomeKey:(NSNotification *)aNotification
276 SDL_Window *window = _data->window;
277 SDL_Mouse *mouse = SDL_GetMouse();
279 /* We're going to get keyboard events, since we're key. */
280 SDL_SetKeyboardFocus(window);
282 /* If we just gained focus we need the updated mouse position */
283 if (!mouse->relative_mode) {
287 point = [_data->nswindow mouseLocationOutsideOfEventStream];
289 y = (int)(window->h - point.y);
291 if (x >= 0 && x < window->w && y >= 0 && y < window->h) {
292 SDL_SendMouseMotion(window, 0, 0, x, y);
296 /* Check to see if someone updated the clipboard */
297 Cocoa_CheckClipboardUpdate(_data->videodata);
300 - (void)windowDidResignKey:(NSNotification *)aNotification
302 /* Some other window will get mouse events, since we're not key. */
303 if (SDL_GetMouseFocus() == _data->window) {
304 SDL_SetMouseFocus(NULL);
307 /* Some other window will get keyboard events, since we're not key. */
308 if (SDL_GetKeyboardFocus() == _data->window) {
309 SDL_SetKeyboardFocus(NULL);
313 /* We'll respond to key events by doing nothing so we don't beep.
314 * We could handle key messages here, but we lose some in the NSApp dispatch,
315 * where they get converted to action messages, etc.
317 - (void)flagsChanged:(NSEvent *)theEvent
319 /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
321 - (void)keyDown:(NSEvent *)theEvent
323 /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
325 - (void)keyUp:(NSEvent *)theEvent
327 /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
330 /* We'll respond to selectors by doing nothing so we don't beep.
331 * The escape key gets converted to a "cancel" selector, etc.
333 - (void)doCommandBySelector:(SEL)aSelector
335 /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
338 - (void)mouseDown:(NSEvent *)theEvent
342 switch ([theEvent buttonNumber]) {
344 if ([theEvent modifierFlags] & NSControlKeyMask) {
346 button = SDL_BUTTON_RIGHT;
349 button = SDL_BUTTON_LEFT;
353 button = SDL_BUTTON_RIGHT;
356 button = SDL_BUTTON_MIDDLE;
359 button = [theEvent buttonNumber] + 1;
362 SDL_SendMouseButton(_data->window, 0, SDL_PRESSED, button);
365 - (void)rightMouseDown:(NSEvent *)theEvent
367 [self mouseDown:theEvent];
370 - (void)otherMouseDown:(NSEvent *)theEvent
372 [self mouseDown:theEvent];
375 - (void)mouseUp:(NSEvent *)theEvent
379 switch ([theEvent buttonNumber]) {
382 button = SDL_BUTTON_RIGHT;
385 button = SDL_BUTTON_LEFT;
389 button = SDL_BUTTON_RIGHT;
392 button = SDL_BUTTON_MIDDLE;
395 button = [theEvent buttonNumber] + 1;
398 SDL_SendMouseButton(_data->window, 0, SDL_RELEASED, button);
401 - (void)rightMouseUp:(NSEvent *)theEvent
403 [self mouseUp:theEvent];
406 - (void)otherMouseUp:(NSEvent *)theEvent
408 [self mouseUp:theEvent];
411 - (void)mouseMoved:(NSEvent *)theEvent
413 SDL_Mouse *mouse = SDL_GetMouse();
414 SDL_Window *window = _data->window;
418 if (mouse->relative_mode) {
422 point = [theEvent locationInWindow];
424 y = (int)(window->h - point.y);
426 if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
427 if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
432 } else if (x >= window->w) {
437 } else if (y >= window->h) {
441 #if !SDL_MAC_NO_SANDBOX
442 /* When SDL_MAC_NO_SANDBOX is set, this is handled by
443 * SDL_cocoamousetap.m.
446 cgpoint.x = window->x + x;
447 cgpoint.y = window->y + y;
449 /* According to the docs, this was deprecated in 10.6, but it's still
450 * around. The substitute requires a CGEventSource, but I'm not entirely
451 * sure how we'd procure the right one for this event.
453 CGSetLocalEventsSuppressionInterval(0.0);
454 CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
455 CGSetLocalEventsSuppressionInterval(0.25);
459 SDL_SendMouseMotion(window, 0, 0, x, y);
462 - (void)mouseDragged:(NSEvent *)theEvent
464 [self mouseMoved:theEvent];
467 - (void)rightMouseDragged:(NSEvent *)theEvent
469 [self mouseMoved:theEvent];
472 - (void)otherMouseDragged:(NSEvent *)theEvent
474 [self mouseMoved:theEvent];
477 - (void)scrollWheel:(NSEvent *)theEvent
479 Cocoa_HandleMouseWheel(_data->window, theEvent);
482 - (void)touchesBeganWithEvent:(NSEvent *) theEvent
484 [self handleTouches:COCOA_TOUCH_DOWN withEvent:theEvent];
487 - (void)touchesMovedWithEvent:(NSEvent *) theEvent
489 [self handleTouches:COCOA_TOUCH_MOVE withEvent:theEvent];
492 - (void)touchesEndedWithEvent:(NSEvent *) theEvent
494 [self handleTouches:COCOA_TOUCH_UP withEvent:theEvent];
497 - (void)touchesCancelledWithEvent:(NSEvent *) theEvent
499 [self handleTouches:COCOA_TOUCH_CANCELLED withEvent:theEvent];
502 - (void)handleTouches:(cocoaTouchType)type withEvent:(NSEvent *)event
505 NSEnumerator *enumerator;
509 case COCOA_TOUCH_DOWN:
510 touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:nil];
513 case COCOA_TOUCH_CANCELLED:
514 touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:nil];
516 case COCOA_TOUCH_MOVE:
517 touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:nil];
521 enumerator = [touches objectEnumerator];
522 touch = (NSTouch*)[enumerator nextObject];
524 const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device];
525 if (!SDL_GetTouch(touchId)) {
526 if (SDL_AddTouch(touchId, "") < 0) {
531 const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
532 float x = [touch normalizedPosition].x;
533 float y = [touch normalizedPosition].y;
534 /* Make the origin the upper left instead of the lower left */
538 case COCOA_TOUCH_DOWN:
539 SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
542 case COCOA_TOUCH_CANCELLED:
543 SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
545 case COCOA_TOUCH_MOVE:
546 SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
550 touch = (NSTouch*)[enumerator nextObject];
556 @interface SDLWindow : NSWindow
557 /* These are needed for borderless/fullscreen windows */
558 - (BOOL)canBecomeKeyWindow;
559 - (BOOL)canBecomeMainWindow;
562 @implementation SDLWindow
563 - (BOOL)canBecomeKeyWindow
568 - (BOOL)canBecomeMainWindow
574 @interface SDLView : NSView
576 /* The default implementation doesn't pass rightMouseDown to responder chain */
577 - (void)rightMouseDown:(NSEvent *)theEvent;
580 @implementation SDLView
581 - (void)rightMouseDown:(NSEvent *)theEvent
583 [[self nextResponder] rightMouseDown:theEvent];
586 - (void)resetCursorRects
588 [super resetCursorRects];
589 SDL_Mouse *mouse = SDL_GetMouse();
591 if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
592 [self addCursorRect:[self bounds]
593 cursor:mouse->cur_cursor->driverdata];
595 [self addCursorRect:[self bounds]
596 cursor:[NSCursor invisibleCursor]];
602 GetWindowStyle(SDL_Window * window)
606 if (window->flags & SDL_WINDOW_FULLSCREEN) {
607 style = NSBorderlessWindowMask;
609 if (window->flags & SDL_WINDOW_BORDERLESS) {
610 style = NSBorderlessWindowMask;
612 style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
614 if (window->flags & SDL_WINDOW_RESIZABLE) {
615 style |= NSResizableWindowMask;
622 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
624 NSAutoreleasePool *pool;
625 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
626 SDL_WindowData *data;
628 /* Allocate the window data */
629 data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
631 return SDL_OutOfMemory();
633 data->window = window;
634 data->nswindow = nswindow;
635 data->created = created;
636 data->videodata = videodata;
637 data->nscontexts = [[NSMutableArray alloc] init];
639 pool = [[NSAutoreleasePool alloc] init];
641 /* Create an event listener for the window */
642 data->listener = [[Cocoa_WindowListener alloc] init];
644 /* Fill in the SDL window with the window data */
646 NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
647 ConvertNSRect(&rect);
648 window->x = (int)rect.origin.x;
649 window->y = (int)rect.origin.y;
650 window->w = (int)rect.size.width;
651 window->h = (int)rect.size.height;
654 /* Set up the listener after we create the view */
655 [data->listener listen:data];
657 if ([nswindow isVisible]) {
658 window->flags |= SDL_WINDOW_SHOWN;
660 window->flags &= ~SDL_WINDOW_SHOWN;
664 unsigned int style = [nswindow styleMask];
666 if (style == NSBorderlessWindowMask) {
667 window->flags |= SDL_WINDOW_BORDERLESS;
669 window->flags &= ~SDL_WINDOW_BORDERLESS;
671 if (style & NSResizableWindowMask) {
672 window->flags |= SDL_WINDOW_RESIZABLE;
674 window->flags &= ~SDL_WINDOW_RESIZABLE;
678 /* isZoomed always returns true if the window is not resizable */
679 if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
680 window->flags |= SDL_WINDOW_MAXIMIZED;
682 window->flags &= ~SDL_WINDOW_MAXIMIZED;
685 if ([nswindow isMiniaturized]) {
686 window->flags |= SDL_WINDOW_MINIMIZED;
688 window->flags &= ~SDL_WINDOW_MINIMIZED;
691 if ([nswindow isKeyWindow]) {
692 window->flags |= SDL_WINDOW_INPUT_FOCUS;
693 SDL_SetKeyboardFocus(data->window);
696 /* Prevents the window's "window device" from being destroyed when it is
697 * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
699 [nswindow setOneShot:NO];
703 window->driverdata = data;
708 Cocoa_CreateWindow(_THIS, SDL_Window * window)
710 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
712 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
717 Cocoa_GetDisplayBounds(_this, display, &bounds);
718 rect.origin.x = window->x;
719 rect.origin.y = window->y;
720 rect.size.width = window->w;
721 rect.size.height = window->h;
722 ConvertNSRect(&rect);
724 style = GetWindowStyle(window);
726 /* Figure out which screen to place this window */
727 NSArray *screens = [NSScreen screens];
728 NSScreen *screen = nil;
730 int i, count = [screens count];
731 for (i = 0; i < count; ++i) {
732 candidate = [screens objectAtIndex:i];
733 NSRect screenRect = [candidate frame];
734 if (rect.origin.x >= screenRect.origin.x &&
735 rect.origin.x < screenRect.origin.x + screenRect.size.width &&
736 rect.origin.y >= screenRect.origin.y &&
737 rect.origin.y < screenRect.origin.y + screenRect.size.height) {
739 rect.origin.x -= screenRect.origin.x;
740 rect.origin.y -= screenRect.origin.y;
743 nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
744 [nswindow setBackgroundColor:[NSColor blackColor]];
746 /* Create a default view for this window */
747 rect = [nswindow contentRectForFrameRect:[nswindow frame]];
748 NSView *contentView = [[SDLView alloc] initWithFrame:rect];
750 if ((window->flags & SDL_WINDOW_ALLOW_HIGHDPI) > 0) {
751 if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
752 [contentView setWantsBestResolutionOpenGLSurface:YES];
756 [nswindow setContentView: contentView];
757 [contentView release];
761 if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
769 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
771 NSAutoreleasePool *pool;
772 NSWindow *nswindow = (NSWindow *) data;
775 pool = [[NSAutoreleasePool alloc] init];
777 /* Query the title from the existing window */
778 title = [nswindow title];
780 window->title = SDL_strdup([title UTF8String]);
785 return SetupWindowData(_this, window, nswindow, SDL_FALSE);
789 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
791 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
792 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
796 string = [[NSString alloc] initWithUTF8String:window->title];
798 string = [[NSString alloc] init];
800 [nswindow setTitle:string];
807 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
809 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
810 NSImage *nsimage = Cocoa_CreateImage(icon);
813 [NSApp setApplicationIconImage:nsimage];
820 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
822 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
823 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
824 NSWindow *nswindow = windata->nswindow;
828 rect.origin.x = window->x;
829 rect.origin.y = window->y;
830 rect.size.width = window->w;
831 rect.size.height = window->h;
832 ConvertNSRect(&rect);
834 moveHack = s_moveHack;
836 [nswindow setFrameOrigin:rect.origin];
837 s_moveHack = moveHack;
839 ScheduleContextUpdates(windata);
845 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
847 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
848 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
849 NSWindow *nswindow = windata->nswindow;
852 size.width = window->w;
853 size.height = window->h;
854 [nswindow setContentSize:size];
856 ScheduleContextUpdates(windata);
862 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
864 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
865 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
868 minSize.width = window->min_w;
869 minSize.height = window->min_h;
871 [windata->nswindow setContentMinSize:minSize];
877 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
879 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
880 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
883 maxSize.width = window->max_w;
884 maxSize.height = window->max_h;
886 [windata->nswindow setContentMaxSize:maxSize];
892 Cocoa_ShowWindow(_THIS, SDL_Window * window)
894 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
895 SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
896 NSWindow *nswindow = windowData->nswindow;
898 if (![nswindow isMiniaturized]) {
899 [windowData->listener pauseVisibleObservation];
900 [nswindow makeKeyAndOrderFront:nil];
901 [windowData->listener resumeVisibleObservation];
907 Cocoa_HideWindow(_THIS, SDL_Window * window)
909 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
910 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
912 [nswindow orderOut:nil];
917 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
919 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
920 SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
921 NSWindow *nswindow = windowData->nswindow;
923 /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
924 a minimized or hidden window, so check for that before showing it.
926 [windowData->listener pauseVisibleObservation];
927 if (![nswindow isMiniaturized] && [nswindow isVisible]) {
928 [nswindow makeKeyAndOrderFront:nil];
930 [windowData->listener resumeVisibleObservation];
936 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
938 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
939 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
940 NSWindow *nswindow = windata->nswindow;
944 ScheduleContextUpdates(windata);
950 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
952 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
953 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
955 [nswindow miniaturize:nil];
960 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
962 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
963 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
965 if ([nswindow isMiniaturized]) {
966 [nswindow deminiaturize:nil];
967 } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
974 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
976 if (!data->created) {
977 /* Don't mess with other people's windows... */
981 [data->listener close];
982 data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
983 [data->nswindow setContentView:[nswindow contentView]];
984 /* See comment in SetupWindowData. */
985 [data->nswindow setOneShot:NO];
986 [data->listener listen:data];
990 return data->nswindow;
994 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
996 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
997 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
998 if ([nswindow respondsToSelector:@selector(setStyleMask:)]) {
999 [nswindow setStyleMask:GetWindowStyle(window)];
1001 Cocoa_SetWindowTitle(_this, window); /* this got blanked out. */
1008 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
1010 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1011 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1012 NSWindow *nswindow = data->nswindow;
1015 /* The view responder chain gets messed with during setStyleMask */
1016 if ([[nswindow contentView] nextResponder] == data->listener) {
1017 [[nswindow contentView] setNextResponder:nil];
1023 Cocoa_GetDisplayBounds(_this, display, &bounds);
1024 rect.origin.x = bounds.x;
1025 rect.origin.y = bounds.y;
1026 rect.size.width = bounds.w;
1027 rect.size.height = bounds.h;
1028 ConvertNSRect(&rect);
1030 /* Hack to fix origin on Mac OS X 10.4 */
1031 NSRect screenRect = [[nswindow screen] frame];
1032 if (screenRect.size.height >= 1.0f) {
1033 rect.origin.y += (screenRect.size.height - rect.size.height);
1036 if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
1037 [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
1039 nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
1042 rect.origin.x = window->windowed.x;
1043 rect.origin.y = window->windowed.y;
1044 rect.size.width = window->windowed.w;
1045 rect.size.height = window->windowed.h;
1046 ConvertNSRect(&rect);
1048 if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
1049 [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
1051 nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
1055 /* The view responder chain gets messed with during setStyleMask */
1056 if ([[nswindow contentView] nextResponder] != data->listener) {
1057 [[nswindow contentView] setNextResponder:data->listener];
1061 [nswindow setContentSize:rect.size];
1062 [nswindow setFrameOrigin:rect.origin];
1063 s_moveHack = SDL_GetTicks();
1065 /* When the window style changes the title is cleared */
1067 Cocoa_SetWindowTitle(_this, window);
1070 if (SDL_ShouldAllowTopmost() && fullscreen) {
1071 /* OpenGL is rendering to the window, so make it visible! */
1072 [nswindow setLevel:CGShieldingWindowLevel()];
1074 [nswindow setLevel:kCGNormalWindowLevel];
1077 if ([nswindow isVisible] || fullscreen) {
1078 [data->listener pauseVisibleObservation];
1079 [nswindow makeKeyAndOrderFront:nil];
1080 [data->listener resumeVisibleObservation];
1083 ScheduleContextUpdates(data);
1089 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
1091 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1092 CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
1093 const uint32_t tableSize = 256;
1094 CGGammaValue redTable[tableSize];
1095 CGGammaValue greenTable[tableSize];
1096 CGGammaValue blueTable[tableSize];
1098 float inv65535 = 1.0f / 65535.0f;
1100 /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
1101 for (i = 0; i < 256; i++) {
1102 redTable[i] = ramp[0*256+i] * inv65535;
1103 greenTable[i] = ramp[1*256+i] * inv65535;
1104 blueTable[i] = ramp[2*256+i] * inv65535;
1107 if (CGSetDisplayTransferByTable(display_id, tableSize,
1108 redTable, greenTable, blueTable) != CGDisplayNoErr) {
1109 return SDL_SetError("CGSetDisplayTransferByTable()");
1115 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
1117 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1118 CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
1119 const uint32_t tableSize = 256;
1120 CGGammaValue redTable[tableSize];
1121 CGGammaValue greenTable[tableSize];
1122 CGGammaValue blueTable[tableSize];
1123 uint32_t i, tableCopied;
1125 if (CGGetDisplayTransferByTable(display_id, tableSize,
1126 redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
1127 return SDL_SetError("CGGetDisplayTransferByTable()");
1130 for (i = 0; i < tableCopied; i++) {
1131 ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
1132 ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
1133 ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
1139 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
1141 /* Move the cursor to the nearest point in the window */
1146 SDL_GetMouseState(&x, &y);
1147 cgpoint.x = window->x + x;
1148 cgpoint.y = window->y + y;
1149 CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
1152 if ( window->flags & SDL_WINDOW_FULLSCREEN ) {
1153 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1155 if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
1156 /* OpenGL is rendering to the window, so make it visible! */
1157 [data->nswindow setLevel:CGShieldingWindowLevel()];
1159 [data->nswindow setLevel:kCGNormalWindowLevel];
1165 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
1167 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1168 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1171 [data->listener close];
1172 [data->listener release];
1173 if (data->created) {
1174 [data->nswindow close];
1177 NSArray *contexts = [[data->nscontexts copy] autorelease];
1178 for (SDLOpenGLContext *context in contexts) {
1179 /* Calling setWindow:NULL causes the context to remove itself from the context list. */
1180 [context setWindow:NULL];
1182 [data->nscontexts release];
1190 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
1192 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
1194 if (info->version.major <= SDL_MAJOR_VERSION) {
1195 info->subsystem = SDL_SYSWM_COCOA;
1196 info->info.cocoa.window = nswindow;
1199 SDL_SetError("Application not compiled with SDL %d.%d\n",
1200 SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
1205 #endif /* SDL_VIDEO_DRIVER_COCOA */
1207 /* vi: set ts=4 sw=4 expandtab: */