Improved fullscreen support on OS X (close #343)
No hard resolution switch is done. Instead a view of the requested size is displayed in the center of the main screen (letter boxing). Running a stress test showed that the performance were equal to a hard switch without the inconvenience of resizing the user's apps. This also follows the guidelines defined by Apple: > Avoid changing the display resolution from that chosen by the user. source: https://developer.apple.com/library/mac/documentation/graphicsimaging/Conceptual/OpenGL-MacProgGuide/opengl_fullscreen/opengl_cgl.html
This commit is contained in:
parent
5a4b54cb9c
commit
8f0037d84d
@ -75,7 +75,7 @@ m_window(0)
|
||||
// Create the context.
|
||||
createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, settings);
|
||||
|
||||
// Create a dummy window/view pair (hidden) and asign it our context.
|
||||
// Create a dummy window/view pair (hidden) and assign it our context.
|
||||
m_window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height)
|
||||
styleMask:NSBorderlessWindowMask
|
||||
backing:NSBackingStoreBuffered
|
||||
|
@ -62,7 +62,6 @@ namespace sf {
|
||||
BOOL m_useKeyRepeat; ///< Key repeat setting
|
||||
BOOL m_mouseIsIn; ///< Mouse positional state
|
||||
NSTrackingArea* m_trackingArea; ///< Mouse tracking area
|
||||
NSSize m_realSize; ///< Actual size of the view
|
||||
|
||||
// Hidden text view used to convert key event to actual chars.
|
||||
// We use a silent responder to prevent sound alerts.
|
||||
@ -100,18 +99,6 @@ namespace sf {
|
||||
////////////////////////////////////////////////////////////
|
||||
-(void)setRequesterTo:(sf::priv::WindowImplCocoa*)requester;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Set the real size of view (it should be the back buffer size)
|
||||
///
|
||||
/// If not set, or set to its default value NSZeroSize, the view
|
||||
/// won't recompute the mouse coordinates before sending them
|
||||
/// to the requester.
|
||||
///
|
||||
/// \param newSize actual size of the view
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
-(void)setRealSize:(NSSize)newSize;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Compute the position in global coordinate
|
||||
///
|
||||
|
@ -113,7 +113,6 @@ BOOL isValidTextUnicode(NSEvent* event);
|
||||
{
|
||||
[self setRequesterTo:0];
|
||||
[self enableKeyRepeat];
|
||||
m_realSize = NSZeroSize;
|
||||
|
||||
// Register for mouse move event
|
||||
m_mouseIsIn = [self isMouseInside];
|
||||
@ -172,23 +171,9 @@ BOOL isValidTextUnicode(NSEvent* event);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
-(void)setRealSize:(NSSize)newSize
|
||||
{
|
||||
m_realSize = newSize;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
-(NSPoint)computeGlobalPositionOfRelativePoint:(NSPoint)point
|
||||
{
|
||||
// Recompute the mouse pos if required.
|
||||
if (!NSEqualSizes(m_realSize, NSZeroSize))
|
||||
{
|
||||
point.x = (point.x / m_realSize.width) * [self frame].size.width;
|
||||
point.y = (point.y / m_realSize.height) * [self frame].size.height;
|
||||
}
|
||||
|
||||
// Note : -[NSWindow convertBaseToScreen:] is deprecated on 10.7
|
||||
// but the recommended -[NSWindow convertRectToScreen] is not
|
||||
// available until 10.7.
|
||||
@ -538,13 +523,6 @@ BOOL isValidTextUnicode(NSEvent* event);
|
||||
float h = [self frame].size.height;
|
||||
loc.y = h - loc.y;
|
||||
|
||||
// Recompute the mouse pos if required.
|
||||
if (!NSEqualSizes(m_realSize, NSZeroSize))
|
||||
{
|
||||
loc.x = (loc.x * m_realSize.width) / [self frame].size.width;
|
||||
loc.y = (loc.y * m_realSize.height) / [self frame].size.height;
|
||||
}
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
|
@ -58,9 +58,7 @@ namespace sf {
|
||||
NSWindow* m_window; ///< Underlying Cocoa window to be controlled
|
||||
SFOpenGLView* m_oglView; ///< OpenGL view for rendering
|
||||
sf::priv::WindowImplCocoa* m_requester; ///< Requester
|
||||
sf::VideoMode* m_fullscreenMode; ///< Fullscreen mode
|
||||
|
||||
/// Note: C++ ctor/dtor are not called for Obj-C fields! Use manual allocation instead.
|
||||
BOOL m_fullscreen; ///< Indicate whether the window is fullscreen or not
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
@ -39,6 +39,24 @@
|
||||
#import <SFML/Window/OSX/SFWindowController.h>
|
||||
#import <OpenGL/OpenGL.h>
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// SFBlackView is a simple view filled with black, nothing more
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
@interface SFBlackView : NSView
|
||||
@end
|
||||
|
||||
@implementation SFBlackView
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
-(void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
[[NSColor blackColor] setFill];
|
||||
NSRectFill(dirtyRect);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// SFWindowController class: private interface
|
||||
///
|
||||
@ -73,15 +91,17 @@
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
m_window = nil;
|
||||
m_oglView = nil;
|
||||
m_requester = 0;
|
||||
m_fullscreenMode = new sf::VideoMode();
|
||||
m_fullscreen = NO;
|
||||
|
||||
// Retain the window for our own use.
|
||||
m_window = window;
|
||||
|
||||
if (m_window == nil)
|
||||
{
|
||||
sf::err() << "No window was given to initWithWindow:." << std::endl;
|
||||
sf::err() << "No window was given to -[SFWindowController -initWithWindow:]." << std::endl;
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -91,7 +111,7 @@
|
||||
if (m_oglView == nil)
|
||||
{
|
||||
sf::err() << "Could not create an instance of NSOpenGLView "
|
||||
<< "in (SFWindowController -initWithWindow:)."
|
||||
<< "in -[SFWindowController -initWithWindow:]."
|
||||
<< std::endl;
|
||||
return self;
|
||||
}
|
||||
@ -121,133 +141,156 @@
|
||||
|
||||
if ((self = [super init]))
|
||||
{
|
||||
m_window = nil;
|
||||
m_oglView = nil;
|
||||
m_requester = 0;
|
||||
m_fullscreenMode = new sf::VideoMode();
|
||||
m_fullscreen = style & sf::Style::Fullscreen;
|
||||
|
||||
// Create our window size.
|
||||
NSRect rect = NSZeroRect;
|
||||
if ((style & sf::Style::Fullscreen) && (mode != sf::VideoMode::getDesktopMode()))
|
||||
{
|
||||
// We use desktop mode to size the window
|
||||
// but we set the back buffer size to 'mode' in applyContext method.
|
||||
*m_fullscreenMode = mode;
|
||||
|
||||
sf::VideoMode dm = sf::VideoMode::getDesktopMode();
|
||||
rect = NSMakeRect(0, 0, dm.width, dm.height);
|
||||
}
|
||||
if (m_fullscreen)
|
||||
[self setupFullscreenViewWithMode:mode];
|
||||
else
|
||||
{
|
||||
// no fullscreen requested.
|
||||
rect = NSMakeRect(0, 0, mode.width, mode.height);
|
||||
}
|
||||
|
||||
// Convert the SFML window style to Cocoa window style.
|
||||
unsigned int nsStyle = NSBorderlessWindowMask;
|
||||
|
||||
// if fullscrean we keep our NSBorderlessWindowMask.
|
||||
if (!(style & sf::Style::Fullscreen))
|
||||
{
|
||||
if (style & sf::Style::Titlebar)
|
||||
nsStyle |= NSTitledWindowMask | NSMiniaturizableWindowMask;
|
||||
|
||||
if (style & sf::Style::Resize)
|
||||
nsStyle |= NSResizableWindowMask;
|
||||
|
||||
if (style & sf::Style::Close)
|
||||
nsStyle |= NSClosableWindowMask;
|
||||
|
||||
}
|
||||
|
||||
// Create the window.
|
||||
m_window = [[SFWindow alloc] initWithContentRect:rect
|
||||
styleMask:nsStyle
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO]; // Don't defer it!
|
||||
/*
|
||||
"YES" produces some "invalid drawable".
|
||||
See http://www.cocoabuilder.com/archive/cocoa/152482-nsviews-and-nsopenglcontext-invalid-drawable-error.html
|
||||
|
||||
[...]
|
||||
As best as I can figure, this is happening because the NSWindow (and
|
||||
hence my view) are not visible on screen yet, and the system doesn't like that.
|
||||
[...]
|
||||
*/
|
||||
|
||||
if (m_window == nil)
|
||||
{
|
||||
sf::err() << "Could not create an instance of NSWindow "
|
||||
<< "in (SFWindowController -initWithMode:andStyle:)."
|
||||
<< std::endl;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Apply special feature for fullscreen window.
|
||||
if (style & sf::Style::Fullscreen)
|
||||
{
|
||||
// We place the window above everything else.
|
||||
[m_window setOpaque:YES];
|
||||
[m_window setHidesOnDeactivate:YES];
|
||||
[m_window setLevel:NSMainMenuWindowLevel+1];
|
||||
|
||||
// And hide the menu bar
|
||||
[NSMenu setMenuBarVisible:NO];
|
||||
|
||||
/* ---------------------------
|
||||
* | Note for future version |
|
||||
* ---------------------------
|
||||
*
|
||||
* starting with OS 10.6 NSView provides
|
||||
* a new method -enterFullScreenMode:withOptions:
|
||||
* which could be a good alternative.
|
||||
*/
|
||||
}
|
||||
|
||||
// Centre the window to be cool =)
|
||||
[m_window center];
|
||||
|
||||
// Create the view.
|
||||
m_oglView = [[SFOpenGLView alloc] initWithFrame:[[m_window contentView] frame]];
|
||||
|
||||
if (m_oglView == nil)
|
||||
{
|
||||
sf::err() << "Could not create an instance of NSOpenGLView "
|
||||
<< "in (SFWindowController -initWithMode:andStyle:)."
|
||||
<< std::endl;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// If a fullscreen window was requested...
|
||||
if (style & sf::Style::Fullscreen)
|
||||
{
|
||||
/// ... we tell the OpenGL view
|
||||
[m_oglView enterFullscreen];
|
||||
|
||||
// ... and if the resolution is not the default one...
|
||||
if (mode != sf::VideoMode::getDesktopMode())
|
||||
{
|
||||
// ... we set the "real size" of the view (that is the back buffer size).
|
||||
[m_oglView setRealSize:NSMakeSize(m_fullscreenMode->width, m_fullscreenMode->height)];
|
||||
}
|
||||
}
|
||||
|
||||
// Set the view to the window as its content view.
|
||||
[m_window setContentView:m_oglView];
|
||||
|
||||
// Register for event.
|
||||
[m_window setDelegate:self];
|
||||
[m_window setAcceptsMouseMovedEvents:YES];
|
||||
[m_window setIgnoresMouseEvents:NO];
|
||||
|
||||
// And some other things...
|
||||
[m_window setAutodisplay:YES];
|
||||
[m_window setReleasedWhenClosed:NO];
|
||||
} // if super init ok
|
||||
|
||||
[self setupWindowWithMode:mode andStyle:style];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
-(void)setupFullscreenViewWithMode:(const sf::VideoMode&)mode
|
||||
{
|
||||
// Create a screen-sized window on the main display
|
||||
sf::VideoMode desktop = sf::VideoMode::getDesktopMode();
|
||||
NSRect windowRect = NSMakeRect(0, 0, desktop.width, desktop.height);
|
||||
m_window = [[SFWindow alloc] initWithContentRect:windowRect
|
||||
styleMask:NSBorderlessWindowMask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
|
||||
if (m_window == nil)
|
||||
{
|
||||
sf::err() << "Could not create an instance of NSWindow "
|
||||
<< "in -[SFWindowController setupFullscreenViewWithMode:]."
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the window level to be above the menu bar
|
||||
[m_window setLevel:NSMainMenuWindowLevel+1];
|
||||
|
||||
// More window configuration...
|
||||
[m_window setOpaque:YES];
|
||||
[m_window setHidesOnDeactivate:YES];
|
||||
[m_window setAutodisplay:YES];
|
||||
[m_window setReleasedWhenClosed:YES];
|
||||
|
||||
// Register for event
|
||||
[m_window setDelegate:self];
|
||||
[m_window setAcceptsMouseMovedEvents:YES];
|
||||
[m_window setIgnoresMouseEvents:NO];
|
||||
|
||||
// Create a master view containing our OpenGL view
|
||||
NSView* masterView = [[SFBlackView alloc] initWithFrame:windowRect];
|
||||
|
||||
if (masterView == nil)
|
||||
{
|
||||
sf::err() << "Could not create an instance of SFBlackView "
|
||||
<< "in -[SFWindowController setupFullscreenViewWithMode:]."
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create our OpenGL view size and the view
|
||||
CGFloat x = (desktop.width - mode.width) / 2.0;
|
||||
CGFloat y = (desktop.height - mode.height) / 2.0;
|
||||
NSRect oglRect = NSMakeRect(x, y, mode.width, mode.height);
|
||||
|
||||
m_oglView = [[SFOpenGLView alloc] initWithFrame:oglRect];
|
||||
|
||||
if (m_oglView == nil)
|
||||
{
|
||||
sf::err() << "Could not create an instance of NSOpenGLView "
|
||||
<< "in -[SFWindowController setupFullscreenViewWithMode:]."
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate the window and views
|
||||
[masterView addSubview:m_oglView];
|
||||
[m_window setContentView:masterView];
|
||||
|
||||
// Finalize setup
|
||||
[m_oglView enterFullscreen];
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
-(void)setupWindowWithMode:(const sf::VideoMode&)mode andStyle:(unsigned long)style
|
||||
{
|
||||
// We know that style & sf::Style::Fullscreen is false.
|
||||
|
||||
// Create our window size.
|
||||
NSRect rect = NSMakeRect(0, 0, mode.width, mode.height);
|
||||
|
||||
// Convert the SFML window style to Cocoa window style.
|
||||
unsigned int nsStyle = NSBorderlessWindowMask;
|
||||
if (style & sf::Style::Titlebar)
|
||||
nsStyle |= NSTitledWindowMask | NSMiniaturizableWindowMask;
|
||||
if (style & sf::Style::Resize)
|
||||
nsStyle |= NSResizableWindowMask;
|
||||
if (style & sf::Style::Close)
|
||||
nsStyle |= NSClosableWindowMask;
|
||||
|
||||
// Create the window.
|
||||
m_window = [[SFWindow alloc] initWithContentRect:rect
|
||||
styleMask:nsStyle
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO]; // Don't defer it!
|
||||
/*
|
||||
"YES" produces some "invalid drawable".
|
||||
See http://www.cocoabuilder.com/archive/cocoa/152482-nsviews-and-nsopenglcontext-invalid-drawable-error.html
|
||||
|
||||
[...]
|
||||
As best as I can figure, this is happening because the NSWindow (and
|
||||
hence my view) are not visible on screen yet, and the system doesn't like that.
|
||||
[...]
|
||||
*/
|
||||
|
||||
if (m_window == nil)
|
||||
{
|
||||
sf::err() << "Could not create an instance of NSWindow "
|
||||
<< "in -[SFWindowController setupWindowWithMode:andStyle:]."
|
||||
<< std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the view.
|
||||
m_oglView = [[SFOpenGLView alloc] initWithFrame:[[m_window contentView] frame]];
|
||||
|
||||
if (m_oglView == nil)
|
||||
{
|
||||
sf::err() << "Could not create an instance of NSOpenGLView "
|
||||
<< "in -[SFWindowController setupWindowWithMode:andStyle:]."
|
||||
<< std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the view to the window as its content view.
|
||||
[m_window setContentView:m_oglView];
|
||||
|
||||
// Register for event.
|
||||
[m_window setDelegate:self];
|
||||
[m_window setAcceptsMouseMovedEvents:YES];
|
||||
[m_window setIgnoresMouseEvents:NO];
|
||||
|
||||
// And some other things...
|
||||
[m_window center];
|
||||
[m_window setAutodisplay:YES];
|
||||
[m_window setReleasedWhenClosed:YES];
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
-(void)dealloc
|
||||
{
|
||||
@ -256,7 +299,6 @@
|
||||
|
||||
m_window = nil;
|
||||
m_oglView = nil;
|
||||
delete m_fullscreenMode;
|
||||
}
|
||||
|
||||
|
||||
@ -335,10 +377,7 @@
|
||||
////////////////////////////////////////////////////////
|
||||
-(NSSize)size
|
||||
{
|
||||
if (*m_fullscreenMode == sf::VideoMode())
|
||||
return [m_oglView frame].size;
|
||||
else
|
||||
return NSMakeSize(m_fullscreenMode->width, m_fullscreenMode->height);
|
||||
return [m_oglView frame].size;
|
||||
}
|
||||
|
||||
|
||||
@ -472,19 +511,6 @@
|
||||
{
|
||||
[m_oglView setOpenGLContext:context];
|
||||
[context setView:m_oglView];
|
||||
|
||||
// If fullscreen was requested and the mode used to create the window
|
||||
// was not the desktop mode, we change the back buffer size of the
|
||||
// context.
|
||||
if (*m_fullscreenMode != sf::VideoMode())
|
||||
{
|
||||
CGLContextObj cgcontext = (CGLContextObj)[context CGLContextObj];
|
||||
|
||||
GLint dim[2] = {m_fullscreenMode->width, m_fullscreenMode->height};
|
||||
|
||||
CGLSetParameter(cgcontext, kCGLCPSurfaceBackingSize, dim);
|
||||
CGLEnable(cgcontext, kCGLCESurfaceBackingSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -516,7 +542,7 @@
|
||||
|
||||
m_requester->windowGainedFocus();
|
||||
|
||||
if (*m_fullscreenMode != sf::VideoMode())
|
||||
if (m_fullscreen)
|
||||
[m_oglView enterFullscreen];
|
||||
}
|
||||
|
||||
@ -532,7 +558,7 @@
|
||||
|
||||
m_requester->windowLostFocus();
|
||||
|
||||
if (*m_fullscreenMode != sf::VideoMode())
|
||||
if (m_fullscreen)
|
||||
[m_oglView exitFullscreen];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user