summaryrefslogtreecommitdiffstats
path: root/src/widgets/kernel/qwidgetbackingstore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/kernel/qwidgetbackingstore.cpp')
-rw-r--r--src/widgets/kernel/qwidgetbackingstore.cpp228
1 files changed, 151 insertions, 77 deletions
diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp
index 69958636fd..55b8513072 100644
--- a/src/widgets/kernel/qwidgetbackingstore.cpp
+++ b/src/widgets/kernel/qwidgetbackingstore.cpp
@@ -79,7 +79,6 @@ void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion &region, QBack
Q_ASSERT(widget);
Q_ASSERT(backingStore);
Q_ASSERT(tlw);
-
#if !defined(QT_NO_PAINT_DEBUG)
static int flushUpdate = qEnvironmentVariableIntValue("QT_FLUSH_UPDATE");
if (flushUpdate > 0)
@@ -105,13 +104,17 @@ void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion &region, QBack
#ifndef QT_NO_OPENGL
if (widgetTextures) {
+ Q_ASSERT(!widgetTextures->isEmpty());
+ qt_window_private(tlw->windowHandle())->compositing = true;
widget->window()->d_func()->sendComposeStatus(widget->window(), false);
// A window may have alpha even when the app did not request
// WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends
// to rely on translucency, in order to decide if it should clear to transparent or opaque.
const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground);
+ // Use the tlw's context, not widget's. The difference is important with native child
+ // widgets where tlw != widget.
backingStore->handle()->composeAndFlush(widget->windowHandle(), region, offset, widgetTextures,
- widget->d_func()->shareContext(), translucentBackground);
+ tlw->d_func()->shareContext(), translucentBackground);
widget->window()->d_func()->sendComposeStatus(widget->window(), true);
} else
#endif
@@ -741,7 +744,6 @@ void QWidgetBackingStore::updateLists(QWidget *cur)
QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
: tlw(topLevel),
dirtyOnScreenWidgets(0),
- widgetTextures(0),
fullUpdatePending(0),
updateRequestSent(0),
textureListWatcher(0),
@@ -761,9 +763,6 @@ QWidgetBackingStore::~QWidgetBackingStore()
for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c)
resetWidget(dirtyRenderToTextureWidgets.at(c));
-#ifndef QT_NO_OPENGL
- delete widgetTextures;
-#endif
delete dirtyOnScreenWidgets;
}
@@ -792,8 +791,9 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
destRect = destRect.translated(dx, dy).intersected(clipR);
const QRect sourceRect(destRect.translated(-dx, -dy));
const QRect parentRect(rect & clipR);
+ const bool nativeWithTextureChild = textureChildSeen && q->internalWinId();
- bool accelerateMove = accelEnv && isOpaque
+ bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild
#ifndef QT_NO_GRAPHICSVIEW
// No accelerate move for proxy widgets.
&& !tlw->d_func()->extra->proxyWidget
@@ -913,6 +913,95 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
}
}
+#ifndef QT_NO_OPENGL
+static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures, QVector<QWidget *> *nativeChildren)
+{
+ QWidgetPrivate *wd = QWidgetPrivate::get(widget);
+ if (wd->renderToTexture) {
+ QPlatformTextureList::Flags flags = 0;
+ if (widget->testAttribute(Qt::WA_AlwaysStackOnTop))
+ flags |= QPlatformTextureList::StacksOnTop;
+ const QRect rect(widget->mapTo(tlw, QPoint()), widget->size());
+ widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags);
+ }
+
+ for (int i = 0; i < wd->children.size(); ++i) {
+ QWidget *w = qobject_cast<QWidget *>(wd->children.at(i));
+ // Stop at native widgets but store them. Stop at hidden widgets too.
+ if (w && !w->isWindow() && w->internalWinId())
+ nativeChildren->append(w);
+ if (w && !w->isWindow() && !w->internalWinId() && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen)
+ findTextureWidgetsRecursively(tlw, w, widgetTextures, nativeChildren);
+ }
+}
+
+static void findAllTextureWidgetsRecursively(QWidget *tlw, QWidget *widget)
+{
+ // textureChildSeen does not take native child widgets into account and that's good.
+ if (QWidgetPrivate::get(widget)->textureChildSeen) {
+ QVector<QWidget *> nativeChildren;
+ QScopedPointer<QPlatformTextureList> tl(new QPlatformTextureList);
+ // Look for texture widgets (incl. widget itself) from 'widget' down,
+ // but skip subtrees with a parent of a native child widget.
+ findTextureWidgetsRecursively(tlw, widget, tl.data(), &nativeChildren);
+ // tl may be empty regardless of textureChildSeen if we have native or hidden children.
+ if (!tl->isEmpty())
+ QWidgetPrivate::get(tlw)->topData()->widgetTextures.append(tl.take());
+ // Native child widgets, if there was any, get their own separate QPlatformTextureList.
+ foreach (QWidget *ncw, nativeChildren) {
+ if (QWidgetPrivate::get(ncw)->textureChildSeen)
+ findAllTextureWidgetsRecursively(tlw, ncw);
+ }
+ }
+}
+
+static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget)
+{
+ foreach (QPlatformTextureList *tl, QWidgetPrivate::get(tlw)->topData()->widgetTextures) {
+ Q_ASSERT(!tl->isEmpty());
+ for (int i = 0; i < tl->count(); ++i) {
+ QWidget *w = static_cast<QWidget *>(tl->source(i));
+ if ((w->internalWinId() && w == widget) || (!w->internalWinId() && w->nativeParentWidget() == widget))
+ return tl;
+ }
+ }
+ return 0;
+}
+
+// Watches one or more QPlatformTextureLists for changes in the lock state and
+// triggers a backingstore sync when all the registered lists turn into
+// unlocked state. This is essential when a custom composeAndFlush()
+// implementation in a platform plugin is not synchronous and keeps
+// holding on to the textures for some time even after returning from there.
+QPlatformTextureListWatcher::QPlatformTextureListWatcher(QWidgetBackingStore *backingStore)
+ : m_backingStore(backingStore)
+{
+}
+
+void QPlatformTextureListWatcher::watch(QPlatformTextureList *textureList)
+{
+ connect(textureList, SIGNAL(locked(bool)), SLOT(onLockStatusChanged(bool)));
+ m_locked[textureList] = textureList->isLocked();
+}
+
+bool QPlatformTextureListWatcher::isLocked() const
+{
+ foreach (bool v, m_locked) {
+ if (v)
+ return true;
+ }
+ return false;
+}
+
+void QPlatformTextureListWatcher::onLockStatusChanged(bool locked)
+{
+ QPlatformTextureList *tl = static_cast<QPlatformTextureList *>(sender());
+ m_locked[tl] = locked;
+ if (!isLocked())
+ m_backingStore->sync();
+}
+#endif // QT_NO_OPENGL
+
static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra)
{
if (!tlw || !tlwExtra || !tlw->testAttribute(Qt::WA_Mapped) || !tlw->isVisible())
@@ -941,7 +1030,7 @@ void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedReg
// Nothing to repaint.
if (!isDirty() && store->size().isValid()) {
- qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset, widgetTextures, this);
+ qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset, widgetTexturesFor(tlw, tlw), this);
return;
}
@@ -953,45 +1042,6 @@ void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedReg
doSync();
}
-#ifndef QT_NO_OPENGL
-static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures)
-{
- QWidgetPrivate *wd = QWidgetPrivate::get(widget);
- if (wd->renderToTexture) {
- QPlatformTextureList::Flags flags = 0;
- if (widget->testAttribute(Qt::WA_AlwaysStackOnTop))
- flags |= QPlatformTextureList::StacksOnTop;
- const QRect rect(widget->mapTo(tlw, QPoint()), widget->size());
- widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags);
- }
-
- for (int i = 0; i < wd->children.size(); ++i) {
- QWidget *w = qobject_cast<QWidget *>(wd->children.at(i));
- if (w && !w->isWindow() && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen)
- findTextureWidgetsRecursively(tlw, w, widgetTextures);
- }
-}
-
-QPlatformTextureListWatcher::QPlatformTextureListWatcher(QWidgetBackingStore *backingStore)
- : m_locked(false),
- m_backingStore(backingStore)
-{
-}
-
-void QPlatformTextureListWatcher::watch(QPlatformTextureList *textureList)
-{
- connect(textureList, SIGNAL(locked(bool)), SLOT(onLockStatusChanged(bool)));
- m_locked = textureList->isLocked();
-}
-
-void QPlatformTextureListWatcher::onLockStatusChanged(bool locked)
-{
- m_locked = locked;
- if (!locked)
- m_backingStore->sync();
-}
-#endif // QT_NO_OPENGL
-
/*!
Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
*/
@@ -1019,12 +1069,19 @@ void QWidgetBackingStore::sync()
if (textureListWatcher && !textureListWatcher->isLocked()) {
textureListWatcher->deleteLater();
textureListWatcher = 0;
- } else if (widgetTextures && widgetTextures->isLocked()) {
- if (!textureListWatcher)
- textureListWatcher = new QPlatformTextureListWatcher(this);
- if (!textureListWatcher->isLocked())
- textureListWatcher->watch(widgetTextures);
- return;
+ } else if (!tlwExtra->widgetTextures.isEmpty()) {
+ bool skipSync = false;
+ foreach (QPlatformTextureList *tl, tlwExtra->widgetTextures) {
+ if (tl->isLocked()) {
+ if (!textureListWatcher)
+ textureListWatcher = new QPlatformTextureListWatcher(this);
+ if (!textureListWatcher->isLocked())
+ textureListWatcher->watch(tl);
+ skipSync = true;
+ }
+ }
+ if (skipSync) // cannot compose due to widget textures being in use
+ return;
}
#endif
@@ -1117,13 +1174,14 @@ void QWidgetBackingStore::doSync()
dirtyWidgets.clear();
#ifndef QT_NO_OPENGL
- delete widgetTextures;
- widgetTextures = 0;
- if (tlw->d_func()->textureChildSeen) {
- widgetTextures = new QPlatformTextureList;
- findTextureWidgetsRecursively(tlw, tlw, widgetTextures);
- }
- qt_window_private(tlw->windowHandle())->compositing = widgetTextures;
+ // Find all render-to-texture child widgets (including self).
+ // The search is cut at native widget boundaries, meaning that each native child widget
+ // has its own list for the subtree below it.
+ QTLWExtra *tlwExtra = tlw->d_func()->topData();
+ qDeleteAll(tlwExtra->widgetTextures);
+ tlwExtra->widgetTextures.clear();
+ findAllTextureWidgetsRecursively(tlw, tlw);
+ qt_window_private(tlw->windowHandle())->compositing = false; // will get updated in qt_flush()
fullUpdatePending = false;
#endif
@@ -1143,6 +1201,9 @@ void QWidgetBackingStore::doSync()
for (int i = 0; i < paintPending.count(); ++i) {
QWidget *w = paintPending[i];
w->d_func()->sendPaintEvent(w->rect());
+ QWidget *npw = w->nativeParentWidget();
+ if (w->internalWinId() || (npw && npw != tlw))
+ markDirtyOnScreen(w->rect(), w, w->mapTo(tlw, QPoint()));
}
// We might have newly exposed areas on the screen if this function was
@@ -1154,18 +1215,23 @@ void QWidgetBackingStore::doSync()
}
#ifndef QT_NO_OPENGL
- if (widgetTextures && widgetTextures->count()) {
- for (int i = 0; i < widgetTextures->count(); ++i) {
- QWidget *w = static_cast<QWidget *>(widgetTextures->source(i));
+ foreach (QPlatformTextureList *tl, tlwExtra->widgetTextures) {
+ for (int i = 0; i < tl->count(); ++i) {
+ QWidget *w = static_cast<QWidget *>(tl->source(i));
if (dirtyRenderToTextureWidgets.contains(w)) {
- const QRect rect = widgetTextures->geometry(i); // mapped to the tlw already
+ const QRect rect = tl->geometry(i); // mapped to the tlw already
dirty += rect;
toClean += rect;
}
}
}
- for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i)
- resetWidget(dirtyRenderToTextureWidgets.at(i));
+ for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i) {
+ QWidget *w = dirtyRenderToTextureWidgets.at(i);
+ resetWidget(w);
+ QWidget *npw = w->nativeParentWidget();
+ if (w->internalWinId() || (npw && npw != tlw))
+ markDirtyOnScreen(w->rect(), w, w->mapTo(tlw, QPoint()));
+ }
dirtyRenderToTextureWidgets.clear();
#endif
@@ -1235,31 +1301,39 @@ void QWidgetBackingStore::doSync()
*/
void QWidgetBackingStore::flush(QWidget *widget)
{
+ const bool hasDirtyOnScreenWidgets = dirtyOnScreenWidgets && !dirtyOnScreenWidgets->isEmpty();
+ bool flushed = false;
+
+ // Flush the region in dirtyOnScreen.
if (!dirtyOnScreen.isEmpty()) {
QWidget *target = widget ? widget : tlw;
- qt_flush(target, dirtyOnScreen, store, tlw, tlwOffset, widgetTextures, this);
+ qt_flush(target, dirtyOnScreen, store, tlw, tlwOffset, widgetTexturesFor(tlw, tlw), this);
dirtyOnScreen = QRegion();
-#ifndef QT_NO_OPENGL
- if (widgetTextures && widgetTextures->count())
- return;
-#endif
+ flushed = true;
}
- if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty()) {
+ // Render-to-texture widgets are not in dirtyOnScreen so flush if we have not done it above.
+ if (!flushed && !hasDirtyOnScreenWidgets) {
#ifndef QT_NO_OPENGL
- if (widgetTextures && widgetTextures->count()) {
- QWidget *target = widget ? widget : tlw;
- qt_flush(target, QRegion(), store, tlw, tlwOffset, widgetTextures, this);
+ if (!tlw->d_func()->topData()->widgetTextures.isEmpty()) {
+ QPlatformTextureList *tl = widgetTexturesFor(tlw, tlw);
+ if (tl) {
+ QWidget *target = widget ? widget : tlw;
+ qt_flush(target, QRegion(), store, tlw, tlwOffset, tl, this);
+ }
}
#endif
- return;
}
+ if (!hasDirtyOnScreenWidgets)
+ return;
+
for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
QWidget *w = dirtyOnScreenWidgets->at(i);
QWidgetPrivate *wd = w->d_func();
Q_ASSERT(wd->needsFlush);
- qt_flush(w, *wd->needsFlush, store, tlw, tlwOffset, 0, this);
+ QPlatformTextureList *widgetTexturesForNative = wd->textureChildSeen ? widgetTexturesFor(tlw, w) : 0;
+ qt_flush(w, *wd->needsFlush, store, tlw, tlwOffset, widgetTexturesForNative, this);
*wd->needsFlush = QRegion();
}
dirtyOnScreenWidgets->clear();