summaryrefslogtreecommitdiffstats
path: root/src/client/qwaylandshmbackingstore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/qwaylandshmbackingstore.cpp')
-rw-r--r--src/client/qwaylandshmbackingstore.cpp167
1 files changed, 101 insertions, 66 deletions
diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp
index 9b5971a21..d77c548a0 100644
--- a/src/client/qwaylandshmbackingstore.cpp
+++ b/src/client/qwaylandshmbackingstore.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qwaylandshmbackingstore_p.h"
#include "qwaylandwindow_p.h"
#include "qwaylandsubsurface_p.h"
@@ -47,10 +11,12 @@
#include <QtCore/qstandardpaths.h>
#include <QtCore/qtemporaryfile.h>
#include <QtGui/QPainter>
+#include <QtGui/QTransform>
#include <QMutexLocker>
#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
+#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
@@ -60,6 +26,19 @@
# ifndef MFD_CLOEXEC
# define MFD_CLOEXEC 0x0001U
# endif
+# ifndef MFD_ALLOW_SEALING
+# define MFD_ALLOW_SEALING 0x0002U
+# endif
+// from bits/fcntl-linux.h
+# ifndef F_ADD_SEALS
+# define F_ADD_SEALS 1033
+# endif
+# ifndef F_SEAL_SEAL
+# define F_SEAL_SEAL 0x0001
+# endif
+# ifndef F_SEAL_SHRINK
+# define F_SEAL_SHRINK 0x0002
+# endif
#endif
QT_BEGIN_NAMESPACE
@@ -67,14 +46,17 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display,
- const QSize &size, QImage::Format format, int scale)
+ const QSize &size, QImage::Format format, qreal scale)
+ : mDirtyRegion(QRect(QPoint(0, 0), size / scale))
{
int stride = size.width() * 4;
int alloc = stride * size.height();
int fd = -1;
#ifdef SYS_memfd_create
- fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC);
+ fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ if (fd >= 0)
+ fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
#endif
QScopedPointer<QFile> filePointer;
@@ -89,6 +71,7 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display,
file->open(fd, QIODevice::ReadWrite | QIODevice::Unbuffered, QFile::AutoCloseHandle);
filePointer.reset(file);
}
+ // NOTE beginPaint assumes a new buffer be all zeroes, which QFile::resize does.
if (!filePointer->isOpen() || !filePointer->resize(alloc)) {
qWarning("QWaylandShmBuffer: failed: %s", qUtf8Printable(filePointer->errorString()));
return;
@@ -107,7 +90,7 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display,
QWaylandShm* shm = display->shm();
wl_shm_format wl_format = shm->formatFrom(format);
mImage = QImage(data, size.width(), size.height(), stride, format);
- mImage.setDevicePixelRatio(qreal(scale));
+ mImage.setDevicePixelRatio(scale);
mShmPool = wl_shm_create_pool(shm->object(), fd, alloc);
init(wl_shm_pool_create_buffer(mShmPool,0, size.width(), size.height(),
@@ -125,7 +108,7 @@ QWaylandShmBuffer::~QWaylandShmBuffer(void)
QImage *QWaylandShmBuffer::imageInsideMargins(const QMargins &marginsIn)
{
- QMargins margins = marginsIn * int(mImage.devicePixelRatio());
+ QMargins margins = marginsIn * mImage.devicePixelRatio();
if (!margins.isNull() && margins != mMargins) {
if (mMarginsImage) {
@@ -151,11 +134,22 @@ QImage *QWaylandShmBuffer::imageInsideMargins(const QMargins &marginsIn)
}
-QWaylandShmBackingStore::QWaylandShmBackingStore(QWindow *window)
+QWaylandShmBackingStore::QWaylandShmBackingStore(QWindow *window, QWaylandDisplay *display)
: QPlatformBackingStore(window)
- , mDisplay(QWaylandScreen::waylandScreenFromWindow(window)->display())
+ , mDisplay(display)
{
-
+ QObject::connect(mDisplay, &QWaylandDisplay::connected, window, [this]() {
+ auto copy = mBuffers;
+ // clear available buffers so we create new ones
+ // actual deletion is deferred till after resize call so we can copy
+ // contents from the back buffer
+ mBuffers.clear();
+ mFrontBuffer = nullptr;
+ // recreateBackBufferIfNeeded always resets mBackBuffer
+ if (mRequestedSize.isValid() && waylandWindow())
+ recreateBackBufferIfNeeded();
+ qDeleteAll(copy);
+ });
}
QWaylandShmBackingStore::~QWaylandShmBackingStore()
@@ -174,14 +168,29 @@ QPaintDevice *QWaylandShmBackingStore::paintDevice()
return contentSurface();
}
+void QWaylandShmBackingStore::updateDirtyStates(const QRegion &region)
+{
+ // Update dirty state of buffers based on what was painted. The back buffer will
+ // not be dirty since we already painted on it, while other buffers will become dirty.
+ for (QWaylandShmBuffer *b : std::as_const(mBuffers)) {
+ if (b != mBackBuffer)
+ b->dirtyRegion() += region;
+ }
+}
+
void QWaylandShmBackingStore::beginPaint(const QRegion &region)
{
mPainting = true;
- ensureSize();
+ waylandWindow()->setBackingStore(this);
+ const bool bufferWasRecreated = recreateBackBufferIfNeeded();
- waylandWindow()->setCanResize(false);
+ const QMargins margins = windowDecorationMargins();
+ updateDirtyStates(region.translated(margins.left(), margins.top()));
- if (mBackBuffer->image()->hasAlphaChannel()) {
+ // Although undocumented, QBackingStore::beginPaint expects the painted region
+ // to be cleared before use if the window has a surface format with an alpha.
+ // Fresh QWaylandShmBuffer are already cleared, so we don't need to clear those.
+ if (!bufferWasRecreated && mBackBuffer->image()->hasAlphaChannel()) {
QPainter p(paintDevice());
p.setCompositionMode(QPainter::CompositionMode_Source);
const QColor blank = Qt::transparent;
@@ -195,14 +204,6 @@ void QWaylandShmBackingStore::endPaint()
mPainting = false;
if (mPendingFlush)
flush(window(), mPendingRegion, QPoint());
- waylandWindow()->setCanResize(true);
-}
-
-void QWaylandShmBackingStore::ensureSize()
-{
- waylandWindow()->setBackingStore(this);
- waylandWindow()->createDecoration();
- resize(mRequestedSize);
}
void QWaylandShmBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
@@ -241,8 +242,10 @@ void QWaylandShmBackingStore::resize(const QSize &size, const QRegion &)
mRequestedSize = size;
}
-QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size)
+QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size, bool &bufferWasRecreated)
{
+ bufferWasRecreated = false;
+
const auto copy = mBuffers; // remove when ported to vector<unique_ptr> + remove_if
for (QWaylandShmBuffer *b : copy) {
if (!b->busy()) {
@@ -261,40 +264,61 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size)
if (mBuffers.size() < MAX_BUFFERS) {
QImage::Format format = QPlatformScreen::platformScreenForWindow(window())->format();
QWaylandShmBuffer *b = new QWaylandShmBuffer(mDisplay, size, format, waylandWindow()->scale());
+ bufferWasRecreated = true;
mBuffers.push_front(b);
return b;
}
return nullptr;
}
-void QWaylandShmBackingStore::resize(const QSize &size)
+bool QWaylandShmBackingStore::recreateBackBufferIfNeeded()
{
+ bool bufferWasRecreated = false;
QMargins margins = windowDecorationMargins();
- int scale = waylandWindow()->scale();
- QSize sizeWithMargins = (size + QSize(margins.left()+margins.right(),margins.top()+margins.bottom())) * scale;
+ qreal scale = waylandWindow()->scale();
+ const QSize sizeWithMargins = (mRequestedSize + QSize(margins.left() + margins.right(), margins.top() + margins.bottom())) * scale;
// We look for a free buffer to draw into. If the buffer is not the last buffer we used,
- // that is mBackBuffer, and the size is the same we memcpy the old content into the new
+ // that is mBackBuffer, and the size is the same we copy the damaged content into the new
// buffer so that QPainter is happy to find the stuff it had drawn before. If the new
// buffer has a different size it needs to be redrawn completely anyway, and if the buffer
// is the same the stuff is there already.
// You can exercise the different codepaths with weston, switching between the gl and the
// pixman renderer. With the gl renderer release events are sent early so we can effectively
// run single buffered, while with the pixman renderer we have to use two.
- QWaylandShmBuffer *buffer = getBuffer(sizeWithMargins);
+ QWaylandShmBuffer *buffer = getBuffer(sizeWithMargins, bufferWasRecreated);
while (!buffer) {
qCDebug(lcWaylandBackingstore, "QWaylandShmBackingStore: stalling waiting for a buffer to be released from the compositor...");
mDisplay->blockingReadEvents();
- buffer = getBuffer(sizeWithMargins);
+ buffer = getBuffer(sizeWithMargins, bufferWasRecreated);
}
qsizetype oldSizeInBytes = mBackBuffer ? mBackBuffer->image()->sizeInBytes() : 0;
qsizetype newSizeInBytes = buffer->image()->sizeInBytes();
// mBackBuffer may have been deleted here but if so it means its size was different so we wouldn't copy it anyway
- if (mBackBuffer != buffer && oldSizeInBytes == newSizeInBytes)
- memcpy(buffer->image()->bits(), mBackBuffer->image()->constBits(), newSizeInBytes);
+ if (mBackBuffer != buffer && oldSizeInBytes == newSizeInBytes) {
+ Q_ASSERT(mBackBuffer);
+ const QImage *sourceImage = mBackBuffer->image();
+ QImage *targetImage = buffer->image();
+
+ QPainter painter(targetImage);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+
+ // Let painter operate in device pixels, to make it easier to compare coordinates
+ const qreal sourceDevicePixelRatio = sourceImage->devicePixelRatio();
+ const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio();
+ painter.scale(1.0 / targetDevicePixelRatio, 1.0 / targetDevicePixelRatio);
+
+ for (const QRect &rect : buffer->dirtyRegion()) {
+ QRectF sourceRect(QPointF(rect.topLeft()) * sourceDevicePixelRatio,
+ QSizeF(rect.size()) * sourceDevicePixelRatio);
+ QRectF targetRect(QPointF(rect.topLeft()) * targetDevicePixelRatio,
+ QSizeF(rect.size()) * targetDevicePixelRatio);
+ painter.drawImage(targetRect, *sourceImage, sourceRect);
+ }
+ }
mBackBuffer = buffer;
@@ -307,6 +331,10 @@ void QWaylandShmBackingStore::resize(const QSize &size)
if (windowDecoration() && window()->isVisible() && oldSizeInBytes != newSizeInBytes)
windowDecoration()->update();
+
+ buffer->dirtyRegion() = QRegion();
+
+ return bufferWasRecreated;
}
QImage *QWaylandShmBackingStore::entireSurface() const
@@ -328,9 +356,10 @@ void QWaylandShmBackingStore::updateDecorations()
qreal dp = sourceImage.devicePixelRatio();
int dpWidth = int(sourceImage.width() / dp);
int dpHeight = int(sourceImage.height() / dp);
- QMatrix sourceMatrix;
+ QTransform sourceMatrix;
sourceMatrix.scale(dp, dp);
QRect target; // needs to be in device independent pixels
+ QRegion dirtyRegion;
//Top
target.setX(0);
@@ -338,16 +367,19 @@ void QWaylandShmBackingStore::updateDecorations()
target.setWidth(dpWidth);
target.setHeight(windowDecorationMargins().top());
decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
+ dirtyRegion += target;
//Left
target.setWidth(windowDecorationMargins().left());
target.setHeight(dpHeight);
decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
+ dirtyRegion += target;
//Right
target.setX(dpWidth - windowDecorationMargins().right());
target.setWidth(windowDecorationMargins().right());
decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
+ dirtyRegion += target;
//Bottom
target.setX(0);
@@ -355,6 +387,9 @@ void QWaylandShmBackingStore::updateDecorations()
target.setWidth(dpWidth);
target.setHeight(windowDecorationMargins().bottom());
decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
+ dirtyRegion += target;
+
+ updateDirtyStates(dirtyRegion);
}
QWaylandAbstractDecoration *QWaylandShmBackingStore::windowDecoration() const