summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa/qcocoawindow.mm
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2017-07-20 14:46:55 +0200
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2017-07-25 17:47:08 +0000
commit8719660416986702c1ee9f65c4e347efa37ac770 (patch)
treeb1840aa77b09b2f3ee7c12900eec220aedddede0 /src/plugins/platforms/cocoa/qcocoawindow.mm
parentcff39f181897520f92829adc1e4ed78233d8b7c5 (diff)
macOS: Send expose event at drawRect and trigger updates via setNeedsDisplay
This changes the drawing model on macOS from the following: 1. Sending synchronous expose events directly from callbacks such as windowDidOrderOnScreen and windowDidChangeOcclusionState 2. Waiting for a resulting flush of the backing store, and issuing setNeedsDisplay as a response 3. Waiting for the asynchronous drawRect call in response to setNeedsDisplay, where the backing store is finally drawn to the window To the following: 1. Issue setNeedsDisplay as a response to callbacks such as windowDidOrderOnScreen and windowDidChangeOcclusionState, when needed (in many cases this is automatic by AppKit) 2. Send synchronous expose events from the resulting drawRect callback 3. Draw the backing store to the window when flushed The new model matches how normal Cocoa application draw in response to drawRect, and makes the backing store flush synchronous instead of having to trigger a async setNeedsDisplay. This gives AppKit more information about how much time we're spending in drawRect, as the actual drawing and flushing all happens within the synchronous expose event. Qt applications that draw outside of drawRect, e.g. in response to timers, are still supported by manually locking focus of the view and flushing the window at the end of the backingstore flush. Task-number: QTBUG-50414 Change-Id: I2efb9ff8df51ab6e840ad20c497b71f53e21e1c2 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoawindow.mm')
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm113
1 files changed, 28 insertions, 85 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 6de316560e..86599a2529 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -152,7 +152,6 @@ QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle)
, m_windowCursor(0)
, m_hasModalSession(false)
, m_frameStrutEventsEnabled(false)
- , m_geometryUpdateExposeAllowed(false)
, m_isExposed(false)
, m_registerTouchCount(0)
, m_resizableTransientParent(false)
@@ -347,13 +346,6 @@ void QCocoaWindow::setVisible(bool visible)
}
- // This call is here to handle initial window show correctly:
- // - top-level windows need to have backing store content ready when the
- // window is shown, sendin the expose event here makes that more likely.
- // - QNSViews for child windows are initialy not hidden and won't get the
- // viewDidUnhide message.
- exposeWindow();
-
if (isContentView()) {
QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
@@ -877,7 +869,7 @@ void QCocoaWindow::viewDidChangeFrame()
*/
void QCocoaWindow::viewDidChangeGlobalFrame()
{
- updateExposedGeometry();
+ [m_view setNeedsDisplay:YES];
}
void QCocoaWindow::windowDidEndLiveResize()
@@ -975,20 +967,20 @@ void QCocoaWindow::windowDidExitFullScreen()
void QCocoaWindow::windowDidOrderOffScreen()
{
- obscureWindow();
+ handleExposeEvent(QRegion());
}
void QCocoaWindow::windowDidOrderOnScreen()
{
- exposeWindow();
+ [m_view setNeedsDisplay:YES];
}
void QCocoaWindow::windowDidChangeOcclusionState()
{
if (m_view.window.occlusionState & NSWindowOcclusionStateVisible)
- exposeWindow();
+ [m_view setNeedsDisplay:YES];
else
- obscureWindow();
+ handleExposeEvent(QRegion());
}
void QCocoaWindow::windowDidChangeScreen()
@@ -998,8 +990,6 @@ void QCocoaWindow::windowDidChangeScreen()
if (QCocoaScreen *cocoaScreen = QCocoaIntegration::instance()->screenForNSScreen(m_view.window.screen))
QWindowSystemInterface::handleWindowScreenChanged(window(), cocoaScreen->screen());
-
- updateExposedGeometry();
}
void QCocoaWindow::windowWillClose()
@@ -1064,7 +1054,6 @@ void QCocoaWindow::handleGeometryChange()
<< "current" << geometry() << "new" << newGeometry;
QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
- updateExposedGeometry();
// Guard against processing window system events during QWindow::setGeometry
// calls, which Qt and Qt applications do not expect.
@@ -1074,6 +1063,29 @@ void QCocoaWindow::handleGeometryChange()
[qnsview_cast(m_view) clearBackingStore];
}
+void QCocoaWindow::handleExposeEvent(const QRegion &region)
+{
+ // Ideally we'd implement isExposed() in terms of these properties,
+ // plus the occlusionState of the NSWindow, and let the expose event
+ // pull the exposed state out when needed. However, when the window
+ // is first shown we receive a drawRect call where the occlusionState
+ // of the window is still hidden, but we still want to prepare the
+ // window for display by issuing an expose event to Qt. To work around
+ // this we don't use the occlusionState directly, but instead base
+ // the exposed state on the region we get in, which in the case of
+ // a window being obscured is an empty region, and in the case of
+ // a drawRect call is a non-null region, even if occlusionState
+ // is still hidden. This ensures the window is prepared for display.
+ m_isExposed = m_view.window.visible
+ && m_view.window.screen
+ && !geometry().size().isEmpty()
+ && !region.isEmpty()
+ && !m_view.hiddenOrHasHiddenAncestor;
+
+ qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::handleExposeEvent" << window() << region << "isExposed" << isExposed();
+ QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
+}
+
void QCocoaWindow::handleWindowStateChanged(HandleFlags flags)
{
Qt::WindowState currentState = windowState();
@@ -1655,75 +1667,6 @@ qreal QCocoaWindow::devicePixelRatio() const
return backingSize.height;
}
-// Returns whether the window can be expose, which it can
-// if it is on screen and has a valid geometry.
-bool QCocoaWindow::isWindowExposable()
-{
- QSize size = geometry().size();
- bool validGeometry = (size.width() > 0 && size.height() > 0);
- bool validScreen = ([[m_view window] screen] != 0);
- bool nonHiddenSuperView = ![[m_view superview] isHidden];
- return (validGeometry && validScreen && nonHiddenSuperView);
-}
-
-// Exposes the window by posting an expose event to QWindowSystemInterface
-void QCocoaWindow::exposeWindow()
-{
- m_geometryUpdateExposeAllowed = true;
-
- if (!isWindowExposable())
- return;
-
- if (!m_isExposed) {
- m_isExposed = true;
- m_exposedGeometry = geometry();
- m_exposedDevicePixelRatio = devicePixelRatio();
- QRect geometry(QPoint(0, 0), m_exposedGeometry.size());
- qCDebug(lcQpaCocoaWindow) << "QCocoaWindow: exposeWindow" << window() << geometry;
- QWindowSystemInterface::handleExposeEvent(window(), geometry);
- }
-}
-
-// Obscures the window by posting an empty expose event to QWindowSystemInterface
-void QCocoaWindow::obscureWindow()
-{
- if (m_isExposed) {
- m_geometryUpdateExposeAllowed = false;
- m_isExposed = false;
-
- qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::obscureWindow" << window();
- QWindowSystemInterface::handleExposeEvent(window(), QRegion());
- }
-}
-
-// Updates window geometry by posting an expose event to QWindowSystemInterface
-void QCocoaWindow::updateExposedGeometry()
-{
- // updateExposedGeometry is not allowed to send the initial expose. If you want
- // that call exposeWindow();
- if (!m_geometryUpdateExposeAllowed)
- return;
-
- // Do not send incorrect exposes in case the window is not even visible yet.
- // We might get here as a result of a resize() from QWidget's show(), for instance.
- if (!window()->isVisible())
- return;
-
- if (!isWindowExposable())
- return;
-
- if (m_exposedGeometry.size() == geometry().size() && m_exposedDevicePixelRatio == devicePixelRatio())
- return;
-
- m_isExposed = true;
- m_exposedGeometry = geometry();
- m_exposedDevicePixelRatio = devicePixelRatio();
-
- QRect geometry(QPoint(0, 0), m_exposedGeometry.size());
- qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::updateExposedGeometry" << window() << geometry;
- QWindowSystemInterface::handleExposeEvent(window(), geometry);
-}
-
QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
{
QWindow *targetWindow = window();