Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwaylandshmbackingstore.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4#include "qwaylandwindow_p.h"
6#include "qwaylanddisplay_p.h"
7#include "qwaylandscreen_p.h"
9
10#include <QtCore/qdebug.h>
11#include <QtCore/qstandardpaths.h>
12#include <QtCore/qtemporaryfile.h>
13#include <QtGui/QPainter>
14#include <QtGui/QTransform>
15#include <QMutexLocker>
16
17#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
18
19#include <fcntl.h>
20#include <unistd.h>
21#include <sys/mman.h>
22
23#ifdef Q_OS_LINUX
24# include <sys/syscall.h>
25// from linux/memfd.h:
26# ifndef MFD_CLOEXEC
27# define MFD_CLOEXEC 0x0001U
28# endif
29# ifndef MFD_ALLOW_SEALING
30# define MFD_ALLOW_SEALING 0x0002U
31# endif
32// from bits/fcntl-linux.h
33# ifndef F_ADD_SEALS
34# define F_ADD_SEALS 1033
35# endif
36# ifndef F_SEAL_SEAL
37# define F_SEAL_SEAL 0x0001
38# endif
39# ifndef F_SEAL_SHRINK
40# define F_SEAL_SHRINK 0x0002
41# endif
42#endif
43
45
46namespace QtWaylandClient {
47
50 : mDirtyRegion(QRect(QPoint(0, 0), size / scale))
51{
52 int stride = size.width() * 4;
53 int alloc = stride * size.height();
54 int fd = -1;
55
56#ifdef SYS_memfd_create
57 fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING);
58 if (fd >= 0)
59 fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
60#endif
61
62 QScopedPointer<QFile> filePointer;
63
64 if (fd == -1) {
65 auto tmpFile = new QTemporaryFile (QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) +
66 QLatin1String("/wayland-shm-XXXXXX"));
67 tmpFile->open();
68 filePointer.reset(tmpFile);
69 } else {
70 auto file = new QFile;
72 filePointer.reset(file);
73 }
74 // NOTE beginPaint assumes a new buffer be all zeroes, which QFile::resize does.
75 if (!filePointer->isOpen() || !filePointer->resize(alloc)) {
76 qWarning("QWaylandShmBuffer: failed: %s", qUtf8Printable(filePointer->errorString()));
77 return;
78 }
79 fd = filePointer->handle();
80
81 // map ourselves: QFile::map() will unmap when the object is destroyed,
82 // but we want this mapping to persist (unmapping in destructor)
83 uchar *data = (uchar *)
84 mmap(nullptr, alloc, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
85 if (data == (uchar *) MAP_FAILED) {
86 qErrnoWarning("QWaylandShmBuffer: mmap failed");
87 return;
88 }
89
90 QWaylandShm* shm = display->shm();
91 wl_shm_format wl_format = shm->formatFrom(format);
92 mImage = QImage(data, size.width(), size.height(), stride, format);
94
95 mShmPool = wl_shm_create_pool(shm->object(), fd, alloc);
96 init(wl_shm_pool_create_buffer(mShmPool,0, size.width(), size.height(),
97 stride, wl_format));
98}
99
101{
102 delete mMarginsImage;
103 if (mImage.constBits())
104 munmap((void *) mImage.constBits(), mImage.sizeInBytes());
105 if (mShmPool)
106 wl_shm_pool_destroy(mShmPool);
107}
108
110{
111 QMargins margins = marginsIn * mImage.devicePixelRatio();
112
113 if (!margins.isNull() && margins != mMargins) {
114 if (mMarginsImage) {
115 delete mMarginsImage;
116 }
117 uchar *bits = const_cast<uchar *>(mImage.constBits());
118 uchar *b_s_data = bits + margins.top() * mImage.bytesPerLine() + margins.left() * 4;
119 int b_s_width = mImage.size().width() - margins.left() - margins.right();
120 int b_s_height = mImage.size().height() - margins.top() - margins.bottom();
121 mMarginsImage = new QImage(b_s_data, b_s_width,b_s_height,mImage.bytesPerLine(),mImage.format());
122 mMarginsImage->setDevicePixelRatio(mImage.devicePixelRatio());
123 }
124 if (margins.isNull()) {
125 delete mMarginsImage;
126 mMarginsImage = nullptr;
127 }
128
129 mMargins = margins;
130 if (!mMarginsImage)
131 return &mImage;
132
133 return mMarginsImage;
134
135}
136
139 , mDisplay(display)
140{
142 auto copy = mBuffers;
143 // clear available buffers so we create new ones
144 // actual deletion is deferred till after resize call so we can copy
145 // contents from the back buffer
146 mBuffers.clear();
147 mFrontBuffer = nullptr;
148 // recreateBackBufferIfNeeded always resets mBackBuffer
149 if (mRequestedSize.isValid() && waylandWindow())
152 });
153}
154
156{
158 w->setBackingStore(nullptr);
159
160// if (mFrontBuffer == waylandWindow()->attached())
161// waylandWindow()->attach(0);
162
163 qDeleteAll(mBuffers);
164}
165
170
171void QWaylandShmBackingStore::updateDirtyStates(const QRegion &region)
172{
173 // Update dirty state of buffers based on what was painted. The back buffer will
174 // not be dirty since we already painted on it, while other buffers will become dirty.
175 for (QWaylandShmBuffer *b : std::as_const(mBuffers)) {
176 if (b != mBackBuffer)
177 b->dirtyRegion() += region;
178 }
179}
180
182{
183 mPainting = true;
185 const bool bufferWasRecreated = recreateBackBufferIfNeeded();
186
187 const QMargins margins = windowDecorationMargins();
188 updateDirtyStates(region.translated(margins.left(), margins.top()));
189
190 // Although undocumented, QBackingStore::beginPaint expects the painted region
191 // to be cleared before use if the window has a surface format with an alpha.
192 // Fresh QWaylandShmBuffer are already cleared, so we don't need to clear those.
193 if (!bufferWasRecreated && mBackBuffer->image()->hasAlphaChannel()) {
195 p.setCompositionMode(QPainter::CompositionMode_Source);
196 const QColor blank = Qt::transparent;
197 for (const QRect &rect : region)
198 p.fillRect(rect, blank);
199 }
200}
201
203{
204 mPainting = false;
205 if (mPendingFlush)
206 flush(window(), mPendingRegion, QPoint());
207}
208
210{
211 // Invoked when the window is of type RasterSurface or when the window is
212 // RasterGLSurface and there are no child widgets requiring OpenGL composition.
213
214 // For the case of RasterGLSurface + having to compose, the composeAndFlush() is
215 // called instead. The default implementation from QPlatformBackingStore is sufficient
216 // however so no need to reimplement that.
217
218
221
222 if (mPainting) {
223 mPendingRegion |= region;
224 mPendingFlush = true;
225 return;
226 }
227
228 mPendingFlush = false;
229 mPendingRegion = QRegion();
230
231 if (windowDecoration() && windowDecoration()->isDirty())
232 updateDecorations();
233
234 mFrontBuffer = mBackBuffer;
235
237 waylandWindow()->safeCommit(mFrontBuffer, region.translated(margins.left(), margins.top()));
238}
239
241{
242 mRequestedSize = size;
243}
244
245QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size, bool &bufferWasRecreated)
246{
247 bufferWasRecreated = false;
248
249 const auto copy = mBuffers; // remove when ported to vector<unique_ptr> + remove_if
250 for (QWaylandShmBuffer *b : copy) {
251 if (!b->busy()) {
252 if (b->size() == size) {
253 return b;
254 } else {
255 mBuffers.remove(b);
256 if (mBackBuffer == b)
257 mBackBuffer = nullptr;
258 delete b;
259 }
260 }
261 }
262
263 static const size_t MAX_BUFFERS = 5;
264 if (mBuffers.size() < MAX_BUFFERS) {
266 QWaylandShmBuffer *b = new QWaylandShmBuffer(mDisplay, size, format, waylandWindow()->scale());
267 bufferWasRecreated = true;
268 mBuffers.push_front(b);
269 return b;
270 }
271 return nullptr;
272}
273
275{
276 bool bufferWasRecreated = false;
279 const QSize sizeWithMargins = (mRequestedSize + QSize(margins.left() + margins.right(), margins.top() + margins.bottom())) * scale;
280
281 // We look for a free buffer to draw into. If the buffer is not the last buffer we used,
282 // that is mBackBuffer, and the size is the same we copy the damaged content into the new
283 // buffer so that QPainter is happy to find the stuff it had drawn before. If the new
284 // buffer has a different size it needs to be redrawn completely anyway, and if the buffer
285 // is the same the stuff is there already.
286 // You can exercise the different codepaths with weston, switching between the gl and the
287 // pixman renderer. With the gl renderer release events are sent early so we can effectively
288 // run single buffered, while with the pixman renderer we have to use two.
289 QWaylandShmBuffer *buffer = getBuffer(sizeWithMargins, bufferWasRecreated);
290 while (!buffer) {
291 qCDebug(lcWaylandBackingstore, "QWaylandShmBackingStore: stalling waiting for a buffer to be released from the compositor...");
292
293 mDisplay->blockingReadEvents();
294 buffer = getBuffer(sizeWithMargins, bufferWasRecreated);
295 }
296
297 qsizetype oldSizeInBytes = mBackBuffer ? mBackBuffer->image()->sizeInBytes() : 0;
298 qsizetype newSizeInBytes = buffer->image()->sizeInBytes();
299
300 // mBackBuffer may have been deleted here but if so it means its size was different so we wouldn't copy it anyway
301 if (mBackBuffer != buffer && oldSizeInBytes == newSizeInBytes) {
302 Q_ASSERT(mBackBuffer);
303 const QImage *sourceImage = mBackBuffer->image();
304 QImage *targetImage = buffer->image();
305
306 QPainter painter(targetImage);
308
309 // Let painter operate in device pixels, to make it easier to compare coordinates
310 const qreal sourceDevicePixelRatio = sourceImage->devicePixelRatio();
311 const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio();
312 painter.scale(1.0 / targetDevicePixelRatio, 1.0 / targetDevicePixelRatio);
313
314 for (const QRect &rect : buffer->dirtyRegion()) {
315 QRectF sourceRect(QPointF(rect.topLeft()) * sourceDevicePixelRatio,
316 QSizeF(rect.size()) * sourceDevicePixelRatio);
317 QRectF targetRect(QPointF(rect.topLeft()) * targetDevicePixelRatio,
318 QSizeF(rect.size()) * targetDevicePixelRatio);
319 painter.drawImage(targetRect, *sourceImage, sourceRect);
320 }
321 }
322
323 mBackBuffer = buffer;
324
325 // ensure the new buffer is at the beginning of the list so next time getBuffer() will pick
326 // it if possible
327 if (mBuffers.front() != buffer) {
328 mBuffers.remove(buffer);
329 mBuffers.push_front(buffer);
330 }
331
332 if (windowDecoration() && window()->isVisible() && oldSizeInBytes != newSizeInBytes)
334
335 buffer->dirtyRegion() = QRegion();
336
337 return bufferWasRecreated;
338}
339
341{
342 return mBackBuffer->image();
343}
344
346{
347 return windowDecoration() ? mBackBuffer->imageInsideMargins(windowDecorationMargins()) : mBackBuffer->image();
348}
349
350void QWaylandShmBackingStore::updateDecorations()
351{
352 QPainter decorationPainter(entireSurface());
353 decorationPainter.setCompositionMode(QPainter::CompositionMode_Source);
354 QImage sourceImage = windowDecoration()->contentImage();
355
356 qreal dp = sourceImage.devicePixelRatio();
357 int dpWidth = int(sourceImage.width() / dp);
358 int dpHeight = int(sourceImage.height() / dp);
359 QTransform sourceMatrix;
360 sourceMatrix.scale(dp, dp);
361 QRect target; // needs to be in device independent pixels
362 QRegion dirtyRegion;
363
364 //Top
365 target.setX(0);
366 target.setY(0);
367 target.setWidth(dpWidth);
368 target.setHeight(windowDecorationMargins().top());
369 decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
370 dirtyRegion += target;
371
372 //Left
373 target.setWidth(windowDecorationMargins().left());
374 target.setHeight(dpHeight);
375 decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
376 dirtyRegion += target;
377
378 //Right
379 target.setX(dpWidth - windowDecorationMargins().right());
381 decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
382 dirtyRegion += target;
383
384 //Bottom
385 target.setX(0);
386 target.setY(dpHeight - windowDecorationMargins().bottom());
387 target.setWidth(dpWidth);
389 decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
390 dirtyRegion += target;
391
392 updateDirtyStates(dirtyRegion);
393}
394
399
406
408{
409 return static_cast<QWaylandWindow *>(window()->handle());
410}
411
412#if QT_CONFIG(opengl)
414{
415 // Invoked from QPlatformBackingStore::composeAndFlush() that is called
416 // instead of flush() for widgets that have renderToTexture children
417 // (QOpenGLWidget, QQuickWidget).
418
419 return *contentSurface();
420}
421#endif // opengl
422
423}
424
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
\inmodule QtGui
Definition qimage.h:37
bool hasAlphaChannel() const
Returns true if the image has a format that respects the alpha channel, otherwise returns false.
Definition qimage.cpp:4589
qsizetype bytesPerLine() const
Returns the number of bytes per image scanline.
Definition qimage.cpp:1560
qsizetype sizeInBytes() const
Definition qimage.cpp:1548
QSize size() const
Returns the size of the image, i.e.
Format
The following image formats are available in Qt.
Definition qimage.h:41
Format format() const
Returns the format of the image.
Definition qimage.cpp:2162
void setDevicePixelRatio(qreal scaleFactor)
Sets the device pixel ratio for the image.
Definition qimage.cpp:1510
qreal devicePixelRatio() const
Returns the device pixel ratio for the image.
Definition qimage.cpp:1482
const uchar * constBits() const
Returns a pointer to the first pixel data.
Definition qimage.cpp:1733
\inmodule QtCore
Definition qmargins.h:24
constexpr int bottom() const noexcept
Returns the bottom margin.
Definition qmargins.h:115
constexpr bool isNull() const noexcept
Returns true if all margins are is 0; otherwise returns false.
Definition qmargins.h:103
constexpr int left() const noexcept
Returns the left margin.
Definition qmargins.h:106
constexpr int right() const noexcept
Returns the right margin.
Definition qmargins.h:112
constexpr int top() const noexcept
Returns the top margin.
Definition qmargins.h:109
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
qreal devicePixelRatio() const
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
QPaintDevice * device() const
Returns the paint device on which this painter is currently painting, or \nullptr if the painter is n...
void scale(qreal sx, qreal sy)
Scales the coordinate system by ({sx}, {sy}).
void setCompositionMode(CompositionMode mode)
Sets the composition mode to the given mode.
void drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect, Qt::ImageConversionFlags flags=Qt::AutoColor)
Draws the rectangular portion source of the given image into the target rectangle in the paint device...
@ CompositionMode_Source
Definition qpainter.h:101
The QPlatformBackingStore class provides the drawing area for top-level windows.
virtual QImage toImage() const
Implemented in subclasses to return the content of the backingstore as a QImage.
QWindow * window() const
Returns a pointer to the top-level window associated with this surface.
static QPlatformScreen * platformScreenForWindow(const QWindow *window)
\inmodule QtCore\reentrant
Definition qpoint.h:217
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore\reentrant
Definition qrect.h:30
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
QRegion translated(int dx, int dy) const
Definition qregion.cpp:593
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
constexpr bool isValid() const noexcept
Returns true if both the width and height is equal to or greater than 0; otherwise returns false.
Definition qsize.h:127
static QString writableLocation(StandardLocation type)
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QTransform & scale(qreal sx, qreal sy)
Scales the coordinate system by sx horizontally and sy vertically, and returns a reference to the mat...
\inmodule QtGui
Definition qwindow.h:63
virtual QMargins margins(MarginsType marginsType=Full) const =0
void beginPaint(const QRegion &region) override
This function is called before painting onto the surface begins, with the region in which the paintin...
QPaintDevice * paintDevice() override
Implement this function to return the appropriate paint device.
void endPaint() override
This function is called after painting onto the surface has ended.
void flush(QWindow *window, const QRegion &region, const QPoint &offset) override
Flushes the given region from the specified window.
void resize(const QSize &size, const QRegion &staticContents) override
QWaylandShmBackingStore(QWindow *window, QWaylandDisplay *display)
QWaylandAbstractDecoration * windowDecoration() const
QWaylandShmBuffer(QWaylandDisplay *display, const QSize &size, QImage::Format format, qreal scale=1)
QImage * imageInsideMargins(const QMargins &margins)
static wl_shm_format formatFrom(QImage::Format format)
void setBackingStore(QWaylandShmBackingStore *backingStore)
QWaylandAbstractDecoration * decoration() const
void safeCommit(QWaylandBuffer *buffer, const QRegion &damage)
qDeleteAll(list.begin(), list.end())
rect
[4]
void qErrnoWarning(const char *msg,...)
struct wl_display * display
Definition linuxdmabuf.h:41
Combined button and popup list for selecting options.
@ transparent
Definition qnamespace.h:47
static jboolean copy(JNIEnv *, jobject)
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
GLboolean GLboolean GLboolean b
GLfloat GLfloat GLfloat w
[0]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLdouble GLdouble GLdouble GLdouble top
GLdouble GLdouble right
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
const void GLsizei GLsizei stride
GLenum GLuint buffer
GLint left
GLint GLint bottom
GLenum target
GLenum GLuint GLintptr offset
GLuint64 GLenum GLint fd
GLint GLsizei GLsizei GLenum format
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
GLfloat GLfloat p
[1]
GLenum GLenum GLenum GLenum GLenum scale
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define MAP_FAILED
#define qUtf8Printable(string)
Definition qstring.h:1535
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qsizetype
Definition qtypes.h:165
double qreal
Definition qtypes.h:187
QFile file
[0]
edit isVisible()
QPainter painter(this)
[7]
aWidget window() -> setWindowTitle("New Window Title")
[2]