diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp index 6ccda7d265f..da6a8b73a7c 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -406,8 +406,6 @@ bool QWaylandXdgSurface::handleExpose(const QRegion ®ion) void QWaylandXdgSurface::applyConfigure() { - bool wasExposed = isExposed(); - // It is a redundant ack_configure, so skipped. if (m_pendingConfigureSerial == m_appliedConfigureSerial) return; @@ -421,8 +419,7 @@ void QWaylandXdgSurface::applyConfigure() m_configured = true; ack_configure(m_appliedConfigureSerial); - if (!wasExposed && isExposed()) - m_window->sendRecursiveExposeEvent(); + window()->updateExposure(); } bool QWaylandXdgSurface::wantsDecorations() const diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 4e2163df785..3aea7bbb35d 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -326,7 +326,7 @@ void QWaylandWindow::reset() mCanResize = true; mResizeDirty = false; mScale = std::nullopt; - + mExposed = false; mOpaqueArea = QRegion(); mMask = QRegion(); @@ -544,11 +544,12 @@ void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, cons void QWaylandWindow::sendExposeEvent(const QRect &rect) { - if (!(mShellSurface && mShellSurface->handleExpose(rect))) + if (!(mShellSurface && mShellSurface->handleExpose(rect))) { QWindowSystemInterface::handleExposeEvent(window(), rect); + mLastExposeGeometry = rect; + } else qCDebug(lcQpaWayland) << "sendExposeEvent: intercepted by shell extension, not sending"; - mLastExposeGeometry = rect; } QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const @@ -572,6 +573,7 @@ void QWaylandWindow::setVisible(bool visible) initWindow(); setGeometry(windowGeometry()); + updateExposure(); // Don't flush the events here, or else the newly visible window may start drawing, but since // there was no frame before it will be stuck at the waitForFrameSync() in // QWaylandShmBackingStore::beginPaint(). @@ -579,6 +581,8 @@ void QWaylandWindow::setVisible(bool visible) if (mShellSurface) mShellSurface->requestActivateOnShow(); } else { + // make sure isExposed is false during the next event dispatch + mExposed = false; sendExposeEvent(QRect()); reset(); } @@ -654,6 +658,8 @@ void QWaylandWindow::doApplyConfigure() mShellSurface->applyConfigure(); mWaitingToApplyConfigure = false; + + sendExposeEvent(QRect(QPoint(), geometry().size())); } void QWaylandWindow::doApplyConfigureFromOtherThread() @@ -662,7 +668,6 @@ void QWaylandWindow::doApplyConfigureFromOtherThread() if (!mCanResize || !mWaitingToApplyConfigure) return; doApplyConfigure(); - sendRecursiveExposeEvent(); } void QWaylandWindow::setCanResize(bool canResize) @@ -678,13 +683,11 @@ void QWaylandWindow::setCanResize(bool canResize) bool inGuiThread = QThread::currentThreadId() == QThreadData::get2(thread())->threadId.loadRelaxed(); if (inGuiThread) { doApplyConfigure(); - sendRecursiveExposeEvent(); } else { QMetaObject::invokeMethod(this, &QWaylandWindow::doApplyConfigureFromOtherThread, Qt::QueuedConnection); } } else if (mResizeDirty) { mResizeDirty = false; - sendExposeEvent(QRect(QPoint(), geometry().size())); } } } @@ -697,23 +700,9 @@ void QWaylandWindow::applyConfigure() doApplyConfigure(); lock.unlock(); - sendRecursiveExposeEvent(); QWindowSystemInterface::flushWindowSystemEvents(); } -void QWaylandWindow::sendRecursiveExposeEvent() -{ - if (!isExposed()) - sendExposeEvent(QRect()); - else - sendExposeEvent(QRect(QPoint(), geometry().size())); - - for (QWaylandSubSurface *subSurface : std::as_const(mChildren)) { - auto subWindow = subSurface->window(); - subWindow->sendRecursiveExposeEvent(); - } -} - void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y) { QReadLocker locker(&mSurfaceLock); @@ -843,8 +832,8 @@ void QWaylandWindow::doHandleFrameCallback() mWaitingForUpdateDelivery.storeRelease(false); bool wasExposed = isExposed(); mFrameCallbackTimedOut = false; - if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? - sendExposeEvent(QRect(QPoint(), geometry().size())); + // Did setting mFrameCallbackTimedOut make the window exposed? + updateExposure(); if (wasExposed && hasPendingUpdateRequest()) deliverUpdateRequest(); @@ -861,7 +850,7 @@ bool QWaylandWindow::waitForFrameSync(int timeout) qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; mFrameCallbackTimedOut = true; mWaitingForUpdate = false; - sendExposeEvent(QRect()); + updateExposure(); } return !mWaitingForFrameCallback; @@ -1517,7 +1506,7 @@ void QWaylandWindow::requestActivateWindow() mShellSurface->requestActivate(); } -bool QWaylandWindow::isExposed() const +bool QWaylandWindow::calculateExposure() const { if (!window()->isVisible()) return false; @@ -1534,6 +1523,30 @@ bool QWaylandWindow::isExposed() const return !(shouldCreateShellSurface() || shouldCreateSubSurface()); } +void QWaylandWindow::updateExposure() +{ + bool exposed = calculateExposure(); + if (exposed == mExposed) + return; + + mExposed = exposed; + + if (!exposed) + sendExposeEvent(QRect()); + else + sendExposeEvent(QRect(QPoint(), geometry().size())); + + for (QWaylandSubSurface *subSurface : std::as_const(mChildren)) { + auto subWindow = subSurface->window(); + subWindow->updateExposure(); + } +} + +bool QWaylandWindow::isExposed() const +{ + return mExposed; +} + bool QWaylandWindow::isActive() const { return mDisplay->isWindowActivated(this); @@ -1647,7 +1660,7 @@ void QWaylandWindow::timerEvent(QTimerEvent *event) qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; mFrameCallbackTimedOut = true; mWaitingForUpdate = false; - sendExposeEvent(QRect()); + updateExposure(); } void QWaylandWindow::requestUpdate() diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 5eec5b5d136..38423f79b1a 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -232,7 +232,10 @@ public: void endFrame(); void closeChildPopups(); - void sendRecursiveExposeEvent(); + + // should be invoked whenever a property that potentially affects + // exposure changes + void updateExposure(); virtual void reinit(); void reset(); @@ -249,6 +252,9 @@ Q_SIGNALS: protected: virtual void doHandleFrameCallback(); virtual QRect defaultGeometry() const; + + // this should be called directly for buffer size changes only + // use updateExposure for anything affecting the on/off state void sendExposeEvent(const QRect &rect); QWaylandDisplay *mDisplay = nullptr; @@ -299,6 +305,7 @@ protected: // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer bool mWaitingForUpdate = false; + bool mExposed = false; QRecursiveMutex mResizeLock; bool mWaitingToApplyConfigure = false; @@ -349,6 +356,7 @@ private: bool isOpaque() const; void updateInputRegion(); void updateViewport(); + bool calculateExposure() const; void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e); void handleScreensChanged();