Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
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 <memory>
20
21#include <fcntl.h>
22#include <unistd.h>
23#include <sys/mman.h>
24
25#ifdef Q_OS_LINUX
26# include <sys/syscall.h>
27// from linux/memfd.h:
28# ifndef MFD_CLOEXEC
29# define MFD_CLOEXEC 0x0001U
30# endif
31# ifndef MFD_ALLOW_SEALING
32# define MFD_ALLOW_SEALING 0x0002U
33# endif
34// from bits/fcntl-linux.h
35# ifndef F_ADD_SEALS
36# define F_ADD_SEALS 1033
37# endif
38# ifndef F_SEAL_SEAL
39# define F_SEAL_SEAL 0x0001
40# endif
41# ifndef F_SEAL_SHRINK
42# define F_SEAL_SHRINK 0x0002
43# endif
44#endif
45
47
48extern void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &);
49
50namespace QtWaylandClient {
51
55{
56 int stride = size.width() * 4;
57 int alloc = stride * size.height();
58 int fd = -1;
59
60#ifdef SYS_memfd_create
62 if (fd >= 0)
64#endif
65
67 bool opened;
68
69 if (fd == -1) {
70 auto tmpFile =
72 QLatin1String("/wayland-shm-XXXXXX"));
73 opened = tmpFile->open();
75 } else {
76 auto file = std::make_unique<QFile>();
79 }
80 // NOTE beginPaint assumes a new buffer be all zeroes, which QFile::resize does.
81 if (!opened || !filePointer->resize(alloc)) {
82 qWarning("QWaylandShmBuffer: failed: %s", qUtf8Printable(filePointer->errorString()));
83 return;
84 }
86
87 // map ourselves: QFile::map() will unmap when the object is destroyed,
88 // but we want this mapping to persist (unmapping in destructor)
89 uchar *data = (uchar *)
90 mmap(nullptr, alloc, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
91 if (data == (uchar *) MAP_FAILED) {
92 qErrnoWarning("QWaylandShmBuffer: mmap failed");
93 return;
94 }
95
100
103 stride, wl_format));
104}
105
114
116{
118
119 if (!margins.isNull() && margins != mMargins) {
120 if (mMarginsImage) {
121 delete mMarginsImage;
122 }
123 uchar *bits = const_cast<uchar *>(mImage.constBits());
129 }
130 if (margins.isNull()) {
131 delete mMarginsImage;
132 mMarginsImage = nullptr;
133 }
134
136 if (!mMarginsImage)
137 return &mImage;
138
139 return mMarginsImage;
140
141}
142
146{
148 auto copy = mBuffers;
149 // clear available buffers so we create new ones
150 // actual deletion is deferred till after resize call so we can copy
151 // contents from the back buffer
152 mBuffers.clear();
153 mFrontBuffer = nullptr;
154 // recreateBackBufferIfNeeded always resets mBackBuffer
158 });
159}
160
162{
164 w->setBackingStore(nullptr);
165
166// if (mFrontBuffer == waylandWindow()->attached())
167// waylandWindow()->attach(0);
168
170}
171
176
178{
179 // Update dirty state of buffers based on what was painted. The back buffer will
180 // not be dirty since we already painted on it, while other buffers will become dirty.
182 if (b != mBackBuffer)
183 b->dirtyRegion() += region;
184 }
185}
186
188{
189 mPainting = true;
192
195
196 // Although undocumented, QBackingStore::beginPaint expects the painted region
197 // to be cleared before use if the window has a surface format with an alpha.
198 // Fresh QWaylandShmBuffer are already cleared, so we don't need to clear those.
199 if (!bufferWasRecreated && window()->format().hasAlpha()) {
202 const QColor blank = Qt::transparent;
203 for (const QRect &rect : region)
205 }
206}
207
209{
210 mPainting = false;
211 if (mPendingFlush)
213}
214
215// Inspired by QCALayerBackingStore.
217{
218 if (!mBackBuffer)
219 return false;
220
223
224 // On Wayland, the window can have a device pixel ratio different from
225 // the window/screen, therefore we cannot rely on QHighDpi here, cf. QBackingStore::scroll.
226 // With fractional scaling we cannot easily scroll the existing pixels.
227 if (!qFuzzyIsNull(devicePixelRatio - static_cast<int>(devicePixelRatio)))
228 return false;
229
230 const QPoint scrollDelta(dx, dy);
233
236
241
242 // We do not mark the source region as dirty, even though it technically has "moved".
243 // This matches the behavior of other backingstore implementations using qt_scrollRectInImage.
245
246 return true;
247}
248
250{
252 // Invoked when the window is of type RasterSurface or when the window is
253 // RasterGLSurface and there are no child widgets requiring OpenGL composition.
254
255 // For the case of RasterGLSurface + having to compose, the composeAndFlush() is
256 // called instead. The default implementation from QPlatformBackingStore is sufficient
257 // however so no need to reimplement that.
258 if (window != this->window()) {
259 auto waylandWindow = static_cast<QWaylandWindow *>(window->handle());
266 return;
267 }
268
269 if (mPainting) {
271 mPendingFlush = true;
272 return;
273 }
274
275 mPendingFlush = false;
277
280
282
285}
286
288{
290}
291
293{
294 static const int MAX_BUFFERS = 5;
295 static const int MAX_AGE = 10 * MAX_BUFFERS;
296 bufferWasRecreated = false;
297
298 // Prune buffers that have not been used in a while or with different size.
299 for (auto i = mBuffers.size() - 1; i >= 0; --i) {
301 if (buffer->age() > MAX_AGE || buffer->size() != size) {
303 if (mBackBuffer == buffer)
304 mBackBuffer = nullptr;
305 delete buffer;
306 }
307 }
308
309 QWaylandShmBuffer *buffer = nullptr;
311 if (candidate->busy())
312 continue;
313
314 if (!buffer || candidate->age() < buffer->age())
316 }
317
318 if (buffer)
319 return buffer;
320
321 if (mBuffers.size() < MAX_BUFFERS) {
323 if (!waylandWindow()->format().hasAlpha())
326 bufferWasRecreated = true;
328 return b;
329 }
330 return nullptr;
331}
332
334{
335 bool bufferWasRecreated = false;
339
340 // We look for a free buffer to draw into. If the buffer is not the last buffer we used,
341 // that is mBackBuffer, and the size is the same we copy the damaged content into the new
342 // buffer so that QPainter is happy to find the stuff it had drawn before. If the new
343 // buffer has a different size it needs to be redrawn completely anyway, and if the buffer
344 // is the same the stuff is there already.
345 // You can exercise the different codepaths with weston, switching between the gl and the
346 // pixman renderer. With the gl renderer release events are sent early so we can effectively
347 // run single buffered, while with the pixman renderer we have to use two.
349 while (!buffer) {
350 qCDebug(lcWaylandBackingstore, "QWaylandShmBackingStore: stalling waiting for a buffer to be released from the compositor...");
351
354 }
355
358
359 // mBackBuffer may have been deleted here but if so it means its size was different so we wouldn't copy it anyway
364
369 }
370
372
374 if (mBackBuffer == buffer) {
375 buffer->setAge(0);
376 } else {
377 buffer->setAge(buffer->age() + 1);
378 }
379 }
380
383
385
386 return bufferWasRecreated;
387}
388
393
398
400{
404
406 int dpWidth = int(sourceImage.width() / dp);
407 int dpHeight = int(sourceImage.height() / dp);
410 QRect target; // needs to be in device independent pixels
412
413 //Top
414 target.setX(0);
415 target.setY(0);
420
421 //Left
426
427 //Right
432
433 //Bottom
434 target.setX(0);
440
442}
443
448
455
457{
458 return static_cast<QWaylandWindow *>(window()->handle());
459}
460
461#if QT_CONFIG(opengl)
463{
464 // Invoked from QPlatformBackingStore::composeAndFlush() that is called
465 // instead of flush() for widgets that have renderToTexture children
466 // (QOpenGLWidget, QQuickWidget).
467
468 return *contentSurface();
469}
470#endif // opengl
471
472}
473
474QT_END_NAMESPACE
#define MAP_FAILED
QT_BEGIN_NAMESPACE void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &)