From 7d4235a38fdda3804a706946d8de6bc192d3fdc2 Mon Sep 17 00:00:00 2001 From: Marco Antognini Date: Sat, 24 May 2014 15:22:24 +0200 Subject: [PATCH 1/3] Optimised OS X implementation regarding scaling factor This also adds support for changing the screen profile or moving the window to another screen. --- src/SFML/Window/OSX/SFOpenGLView.h | 9 +++ src/SFML/Window/OSX/SFOpenGLView.mm | 33 ++++++++++ src/SFML/Window/OSX/SFViewController.mm | 7 ++ src/SFML/Window/OSX/SFWindowController.mm | 7 ++ src/SFML/Window/OSX/WindowImplCocoa.mm | 65 +++++++++++-------- .../Window/OSX/WindowImplDelegateProtocol.h | 8 +++ 6 files changed, 101 insertions(+), 28 deletions(-) diff --git a/src/SFML/Window/OSX/SFOpenGLView.h b/src/SFML/Window/OSX/SFOpenGLView.h index 3d69ee5b..20518948 100644 --- a/src/SFML/Window/OSX/SFOpenGLView.h +++ b/src/SFML/Window/OSX/SFOpenGLView.h @@ -55,6 +55,7 @@ namespace sf { BOOL m_mouseIsIn; ///< Mouse positional state NSTrackingArea* m_trackingArea; ///< Mouse tracking area BOOL m_fullscreen; ///< Indicate whether the window is fullscreen or not + CGFloat m_scaleFactor; ///< Display scale factor (e.g. 1x for classic display, 2x for retina) // Hidden text view used to convert key event to actual chars. // We use a silent responder to prevent sound alerts. @@ -106,6 +107,14 @@ namespace sf { //////////////////////////////////////////////////////////// -(void)disableKeyRepeat; +//////////////////////////////////////////////////////////// +/// \brief Get the display scale factor +/// +/// \return e.g. 1.0 for classic display, 2.0 for retina display +/// +//////////////////////////////////////////////////////////// +-(CGFloat)displayScaleFactor; + //////////////////////////////////////////////////////////// /// \brief Compute the position of the cursor /// diff --git a/src/SFML/Window/OSX/SFOpenGLView.mm b/src/SFML/Window/OSX/SFOpenGLView.mm index c1fa0e40..deda683a 100644 --- a/src/SFML/Window/OSX/SFOpenGLView.mm +++ b/src/SFML/Window/OSX/SFOpenGLView.mm @@ -55,6 +55,12 @@ BOOL isValidTextUnicode(NSEvent* event); //////////////////////////////////////////////////////////// @interface SFOpenGLView () +//////////////////////////////////////////////////////////// +/// \brief Handle screen changed event +/// +//////////////////////////////////////////////////////////// +-(void)updateScaleFactor; + //////////////////////////////////////////////////////////// /// \brief Handle view resized event /// @@ -154,6 +160,7 @@ BOOL isValidTextUnicode(NSEvent* event); [self addTrackingArea:m_trackingArea]; m_fullscreen = isFullscreen; + [self updateScaleFactor]; // Register for window focus events [[NSNotificationCenter defaultCenter] addObserver:self @@ -169,6 +176,16 @@ BOOL isValidTextUnicode(NSEvent* event); name:NSWindowWillCloseNotification object:[self window]]; + // Register for changed screen and changed screen's profile events + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(updateScaleFactor) + name:NSWindowDidChangeScreenNotification + object:[self window]]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(updateScaleFactor) + name:NSWindowDidChangeScreenProfileNotification + object:[self window]]; + // Create a hidden text view for parsing key down event properly m_silentResponder = [[SFSilentResponder alloc] init]; m_hiddenTextView = [[NSTextView alloc] initWithFrame:NSZeroRect]; @@ -234,6 +251,22 @@ BOOL isValidTextUnicode(NSEvent* event); } +//////////////////////////////////////////////////////// +-(CGFloat)displayScaleFactor +{ + return m_scaleFactor; +} + + +//////////////////////////////////////////////////////// +-(void)updateScaleFactor +{ + NSWindow* window = [self window]; + NSScreen* screen = window ? [window screen] : [NSScreen mainScreen]; + m_scaleFactor = [screen backingScaleFactor]; +} + + //////////////////////////////////////////////////////// -(void)viewDidEndLiveResize { diff --git a/src/SFML/Window/OSX/SFViewController.mm b/src/SFML/Window/OSX/SFViewController.mm index bcb14efc..71004add 100644 --- a/src/SFML/Window/OSX/SFViewController.mm +++ b/src/SFML/Window/OSX/SFViewController.mm @@ -89,6 +89,13 @@ } +//////////////////////////////////////////////////////// +-(CGFloat)displayScaleFactor +{ + return [m_oglView displayScaleFactor]; +} + + //////////////////////////////////////////////////////// -(void)setRequesterTo:(sf::priv::WindowImplCocoa*)requester { diff --git a/src/SFML/Window/OSX/SFWindowController.mm b/src/SFML/Window/OSX/SFWindowController.mm index 35c6c528..b4ddc3e0 100644 --- a/src/SFML/Window/OSX/SFWindowController.mm +++ b/src/SFML/Window/OSX/SFWindowController.mm @@ -306,6 +306,13 @@ #pragma mark WindowImplDelegateProtocol's methods +//////////////////////////////////////////////////////// +-(CGFloat)displayScaleFactor +{ + return [m_oglView displayScaleFactor]; +} + + //////////////////////////////////////////////////////// -(void)setRequesterTo:(sf::priv::WindowImplCocoa*)requester { diff --git a/src/SFML/Window/OSX/WindowImplCocoa.mm b/src/SFML/Window/OSX/WindowImplCocoa.mm index 3502ecef..15e505b7 100644 --- a/src/SFML/Window/OSX/WindowImplCocoa.mm +++ b/src/SFML/Window/OSX/WindowImplCocoa.mm @@ -43,6 +43,15 @@ namespace sf namespace priv { +//////////////////////////////////////////////////////////// +/// \brief Get the scale factor of the main screen +/// +//////////////////////////////////////////////////////////// +CGFloat getDefaultScaleFactor() +{ + return [[NSScreen mainScreen] backingScaleFactor]; +} + //////////////////////////////////////////////////////////// /// \brief Scale SFML coordinates to backing coordinates /// @@ -50,27 +59,27 @@ namespace priv /// has a retina display or not. /// /// \param in SFML coordinates to be converted +/// \param delegate a object implementing WindowImplDelegateProtocol, or nil for default scale /// //////////////////////////////////////////////////////////// template -void scaleIn(T& in) +void scaleIn(T& in, id delegate) { - CGFloat scale = [[NSScreen mainScreen] backingScaleFactor]; - in /= scale; + in /= delegate ? [delegate displayScaleFactor] : getDefaultScaleFactor(); } template -void scaleInWidthHeight(T& in) +void scaleInWidthHeight(T& in, id delegate) { - scaleIn(in.width); - scaleIn(in.height); + scaleIn(in.width, delegate); + scaleIn(in.height, delegate); } template -void scaleInXY(T& in) +void scaleInXY(T& in, id delegate) { - scaleIn(in.x); - scaleIn(in.y); + scaleIn(in.x, delegate); + scaleIn(in.y, delegate); } //////////////////////////////////////////////////////////// @@ -80,27 +89,27 @@ void scaleInXY(T& in) /// has a retina display or not. /// /// \param out backing coordinates to be converted +/// \param delegate a object implementing WindowImplDelegateProtocol, or nil for default scale /// //////////////////////////////////////////////////////////// template -void scaleOut(T& out) +void scaleOut(T& out, id delegate) { - CGFloat scale = [[NSScreen mainScreen] backingScaleFactor]; - out *= scale; + out *= delegate ? [delegate displayScaleFactor] : getDefaultScaleFactor(); } template -void scaleOutWidthHeight(T& out) +void scaleOutWidthHeight(T& out, id delegate) { - scaleOut(out.width); - scaleOut(out.height); + scaleOut(out.width, delegate); + scaleOut(out.height, delegate); } template -void scaleOutXY(T& out) +void scaleOutXY(T& out, id delegate) { - scaleOut(out.x); - scaleOut(out.y); + scaleOut(out.x, delegate); + scaleOut(out.y, delegate); } #pragma mark @@ -159,7 +168,7 @@ m_showCursor(true) retainPool(); // Use backing size - scaleInWidthHeight(mode); + scaleInWidthHeight(mode, nil); m_delegate = [[SFWindowController alloc] initWithMode:mode andStyle:style]; [m_delegate changeTitle:sfStringToNSString(title)]; @@ -250,7 +259,7 @@ void WindowImplCocoa::windowResized(unsigned int width, unsigned int height) event.type = Event::Resized; event.size.width = width; event.size.height = height; - scaleOutWidthHeight(event.size); + scaleOutWidthHeight(event.size, m_delegate); pushEvent(event); } @@ -293,7 +302,7 @@ void WindowImplCocoa::mouseDownAt(Mouse::Button button, int x, int y) event.mouseButton.button = button; event.mouseButton.x = x; event.mouseButton.y = y; - scaleOutXY(event.mouseButton); + scaleOutXY(event.mouseButton, m_delegate); pushEvent(event); } @@ -307,7 +316,7 @@ void WindowImplCocoa::mouseUpAt(Mouse::Button button, int x, int y) event.mouseButton.button = button; event.mouseButton.x = x; event.mouseButton.y = y; - scaleOutXY(event.mouseButton); + scaleOutXY(event.mouseButton, m_delegate); pushEvent(event); } @@ -320,7 +329,7 @@ void WindowImplCocoa::mouseMovedAt(int x, int y) event.type = Event::MouseMoved; event.mouseMove.x = x; event.mouseMove.y = y; - scaleOutXY(event.mouseMove); + scaleOutXY(event.mouseMove, m_delegate); pushEvent(event); } @@ -333,7 +342,7 @@ void WindowImplCocoa::mouseWheelScrolledAt(float delta, int x, int y) event.mouseWheel.delta = delta; event.mouseWheel.x = x; event.mouseWheel.y = y; - scaleOutXY(event.mouseWheel); + scaleOutXY(event.mouseWheel, m_delegate); pushEvent(event); } @@ -424,7 +433,7 @@ Vector2i WindowImplCocoa::getPosition() const { NSPoint pos = [m_delegate position]; sf::Vector2i ret(pos.x, pos.y); - scaleOutXY(ret); + scaleOutXY(ret, m_delegate); return ret; } @@ -433,7 +442,7 @@ Vector2i WindowImplCocoa::getPosition() const void WindowImplCocoa::setPosition(const Vector2i& position) { sf::Vector2i backingPosition = position; - scaleInXY(backingPosition); + scaleInXY(backingPosition, m_delegate); [m_delegate setWindowPositionToX:backingPosition.x Y:backingPosition.y]; } @@ -443,7 +452,7 @@ Vector2u WindowImplCocoa::getSize() const { NSSize size = [m_delegate size]; Vector2u ret(size.width, size.height); - scaleOutXY(ret); + scaleOutXY(ret, m_delegate); return ret; } @@ -452,7 +461,7 @@ Vector2u WindowImplCocoa::getSize() const void WindowImplCocoa::setSize(const Vector2u& size) { sf::Vector2u backingSize = size; - scaleInXY(backingSize); + scaleInXY(backingSize, m_delegate); [m_delegate resizeTo:backingSize.x by:backingSize.y]; } diff --git a/src/SFML/Window/OSX/WindowImplDelegateProtocol.h b/src/SFML/Window/OSX/WindowImplDelegateProtocol.h index 2f3a63c7..0ffc0ffb 100644 --- a/src/SFML/Window/OSX/WindowImplDelegateProtocol.h +++ b/src/SFML/Window/OSX/WindowImplDelegateProtocol.h @@ -66,6 +66,14 @@ namespace sf { //////////////////////////////////////////////////////////// @protocol WindowImplDelegateProtocol +//////////////////////////////////////////////////////////// +/// \brief Get the display scale factor +/// +/// \return e.g. 1.0 for classic display, 2.0 for retina display +/// +//////////////////////////////////////////////////////////// +-(CGFloat)displayScaleFactor; + //////////////////////////////////////////////////////////// /// \brief Set the WindowImpl who requested this delegate /// From 3f61214571a2a3d950692ef6692d8314ef5eae00 Mon Sep 17 00:00:00 2001 From: Marco Antognini Date: Mon, 26 May 2014 15:31:45 +0200 Subject: [PATCH 2/3] Improved OS X implementation It makes sure the notifications sent to SFOpenGLView are only from its window. --- src/SFML/Window/OSX/SFOpenGLView.h | 10 ++++ src/SFML/Window/OSX/SFOpenGLView.mm | 61 +++++++++++++---------- src/SFML/Window/OSX/SFViewController.mm | 2 + src/SFML/Window/OSX/SFWindowController.mm | 2 + 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/SFML/Window/OSX/SFOpenGLView.h b/src/SFML/Window/OSX/SFOpenGLView.h index 20518948..f65da562 100644 --- a/src/SFML/Window/OSX/SFOpenGLView.h +++ b/src/SFML/Window/OSX/SFOpenGLView.h @@ -69,6 +69,8 @@ namespace sf { /// NB: -initWithFrame: is also implemented to default isFullscreen to NO /// in case SFOpenGLView is created with the standard message. /// +/// To finish the initialization -finishInit should be called too. +/// /// \param frameRect dimension of the view /// \param isFullscreen fullscreen flag /// @@ -77,6 +79,14 @@ namespace sf { //////////////////////////////////////////////////////////// -(id)initWithFrame:(NSRect)frameRect fullscreen:(BOOL)isFullscreen; +//////////////////////////////////////////////////////////// +/// \brief Finish the creation of the SFML OpenGL view +/// +/// This method should be called after the view was added to a window +/// +//////////////////////////////////////////////////////////// +-(void)finishInit; + //////////////////////////////////////////////////////////// /// \brief Apply the given requester to the view /// diff --git a/src/SFML/Window/OSX/SFOpenGLView.mm b/src/SFML/Window/OSX/SFOpenGLView.mm index deda683a..d08c63ca 100644 --- a/src/SFML/Window/OSX/SFOpenGLView.mm +++ b/src/SFML/Window/OSX/SFOpenGLView.mm @@ -160,31 +160,7 @@ BOOL isValidTextUnicode(NSEvent* event); [self addTrackingArea:m_trackingArea]; m_fullscreen = isFullscreen; - [self updateScaleFactor]; - - // Register for window focus events - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowDidBecomeKey:) - name:NSWindowDidBecomeKeyNotification - object:[self window]]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowDidResignKey:) - name:NSWindowDidResignKeyNotification - object:[self window]]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowDidResignKey:) - name:NSWindowWillCloseNotification - object:[self window]]; - - // Register for changed screen and changed screen's profile events - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(updateScaleFactor) - name:NSWindowDidChangeScreenNotification - object:[self window]]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(updateScaleFactor) - name:NSWindowDidChangeScreenProfileNotification - object:[self window]]; + m_scaleFactor = 1.0; // Default value; it will be updated in finishInit // Create a hidden text view for parsing key down event properly m_silentResponder = [[SFSilentResponder alloc] init]; @@ -193,12 +169,47 @@ BOOL isValidTextUnicode(NSEvent* event); // Request high resolution on high DPI displays [self setWantsBestResolutionOpenGLSurface:YES]; + + // At that point, the view isn't attached to a window. We defer the rest of + // the initialization process to later. } return self; } +//////////////////////////////////////////////////////// +-(void)finishInit +{ + // Register for window focus events + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidBecomeKey:) + name:NSWindowDidBecomeKeyNotification + object:[self window]]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidResignKey:) + name:NSWindowDidResignKeyNotification + object:[self window]]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowDidResignKey:) + name:NSWindowWillCloseNotification + object:[self window]]; + + // Register for changed screen and changed screen's profile events + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(updateScaleFactor) + name:NSWindowDidChangeScreenNotification + object:[self window]]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(updateScaleFactor) + name:NSWindowDidChangeScreenProfileNotification + object:[self window]]; + + // Now that we have a window, set up correctly the scale factor + [self updateScaleFactor]; +} + + //////////////////////////////////////////////////////// -(void)setRequesterTo:(sf::priv::WindowImplCocoa*)requester { diff --git a/src/SFML/Window/OSX/SFViewController.mm b/src/SFML/Window/OSX/SFViewController.mm index 71004add..05e2cccf 100644 --- a/src/SFML/Window/OSX/SFViewController.mm +++ b/src/SFML/Window/OSX/SFViewController.mm @@ -71,6 +71,8 @@ [m_view addSubview:m_oglView]; [m_oglView setAutoresizingMask:[m_view autoresizingMask]]; + + [m_oglView finishInit]; } return self; diff --git a/src/SFML/Window/OSX/SFWindowController.mm b/src/SFML/Window/OSX/SFWindowController.mm index b4ddc3e0..4f6473c9 100644 --- a/src/SFML/Window/OSX/SFWindowController.mm +++ b/src/SFML/Window/OSX/SFWindowController.mm @@ -149,6 +149,8 @@ [self setupFullscreenViewWithMode:mode]; else [self setupWindowWithMode:mode andStyle:style]; + + [m_oglView finishInit]; } return self; } From 46be2159cb5e3e10bd6bca5eae707e62ce693674 Mon Sep 17 00:00:00 2001 From: Marco Antognini Date: Sun, 1 Jun 2014 20:07:24 +0200 Subject: [PATCH 3/3] Fixed OS X implementation of sf::Mouse::(get|set)Position The code was not updated at all when support for retina display was introduced. --- src/SFML/Window/OSX/InputImpl.mm | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/SFML/Window/OSX/InputImpl.mm b/src/SFML/Window/OSX/InputImpl.mm index eb24fd08..f0fac750 100644 --- a/src/SFML/Window/OSX/InputImpl.mm +++ b/src/SFML/Window/OSX/InputImpl.mm @@ -133,7 +133,8 @@ Vector2i InputImpl::getMousePosition() NSPoint pos = [NSEvent mouseLocation]; pos.y = sf::VideoMode::getDesktopMode().height - pos.y; - return Vector2i(pos.x, pos.y); + int scale = [[NSScreen mainScreen] backingScaleFactor]; + return Vector2i(pos.x, pos.y) * scale; } @@ -149,7 +150,8 @@ Vector2i InputImpl::getMousePosition(const Window& relativeTo) // Use -cursorPositionFromEvent: with nil. NSPoint pos = [view cursorPositionFromEvent:nil]; - return Vector2i(pos.x, pos.y); + int scale = [view displayScaleFactor]; + return Vector2i(pos.x, pos.y) * scale; } @@ -157,7 +159,8 @@ Vector2i InputImpl::getMousePosition(const Window& relativeTo) void InputImpl::setMousePosition(const Vector2i& position) { // Here we don't need to reverse the coordinates. - CGPoint pos = CGPointMake(position.x, position.y); + int scale = [[NSScreen mainScreen] backingScaleFactor]; + CGPoint pos = CGPointMake(position.x / scale, position.y / scale); // Place the cursor. CGEventRef event = CGEventCreateMouseEvent(NULL, @@ -180,9 +183,10 @@ void InputImpl::setMousePosition(const Vector2i& position, const Window& relativ return; // Let SFOpenGLView compute the position in global coordinate - NSPoint p = NSMakePoint(position.x, position.y); + int scale = [view displayScaleFactor]; + NSPoint p = NSMakePoint(position.x / scale, position.y / scale); p = [view computeGlobalPositionOfRelativePoint:p]; - setMousePosition(sf::Vector2i(p.x, p.y)); + setMousePosition(sf::Vector2i(p.x, p.y) * scale); }