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
3// Qt-Security score:significant reason:default
10
11#include <QtCore/qdebug.h>
12#include <QtCore/qstandardpaths.h>
13#include <QtCore/qtemporaryfile.h>
14#include <QtGui/QPainter>
15#include <QtGui/QTransform>
16#include <QMutexLocker>
17
18#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
19
20#include <memory>
21
22#include <fcntl.h>
23#include <unistd.h>
24#include <sys/mman.h>
25
26#ifdef Q_OS_LINUX
27# include <sys/syscall.h>
28// from linux/memfd.h:
29# ifndef MFD_CLOEXEC
30# define MFD_CLOEXEC 0x0001U
31# endif
32# ifndef MFD_ALLOW_SEALING
33# define MFD_ALLOW_SEALING 0x0002U
34# endif
35// from bits/fcntl-linux.h
36# ifndef F_ADD_SEALS
37# define F_ADD_SEALS 1033
38# endif
39# ifndef F_SEAL_SEAL
40# define F_SEAL_SEAL 0x0001
41# endif
42# ifndef F_SEAL_SHRINK
43# define F_SEAL_SHRINK 0x0002
44# endif
45#endif
46
48
49extern void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &);
50
51namespace QtWaylandClient {
52
56{
57 int stride = size.width() * 4;
58 int alloc = stride * size.height();
59 int fd = -1;
60
61#ifdef SYS_memfd_create
63 if (fd >= 0)
65#endif
66
68 bool opened;
69
70 if (fd == -1) {
71 auto tmpFile =
73 QLatin1String("/wayland-shm-XXXXXX"));
74 opened = tmpFile->open();
76 } else {
77 auto file = std::make_unique<QFile>();
80 }
81 // NOTE beginPaint assumes a new buffer be all zeroes, which QFile::resize does.
82 if (!opened || !filePointer->resize(alloc)) {
83 qWarning("QWaylandShmBuffer: failed: %s", qUtf8Printable(filePointer->errorString()));
84 return;
85 }
87
88 // map ourselves: QFile::map() will unmap when the object is destroyed,
89 // but we want this mapping to persist (unmapping in destructor)
90 uchar *data = (uchar *)
91 mmap(nullptr, alloc, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
92 if (data == (uchar *) MAP_FAILED) {
93 qErrnoWarning("QWaylandShmBuffer: mmap failed");
94 return;
95 }
96
101
104 stride, wl_format));
106 wl_proxy_set_queue(reinterpret_cast<struct wl_proxy *>(buffer()), customEventQueue);
107}
108
117
119{
121
122 if (!margins.isNull() && margins != mMargins) {
123 if (mMarginsImage) {
124 delete mMarginsImage;
125 }
126 uchar *bits = const_cast<uchar *>(mImage.constBits());
132 }
133 if (margins.isNull()) {
134 delete mMarginsImage;
135 mMarginsImage = nullptr;
136 }
137
139 if (!mMarginsImage)
140 return &mImage;
141
142 return mMarginsImage;
143
144}
145
149{
154 auto copy = mBuffers;
155 // clear available buffers so we create new ones
156 // actual deletion is deferred till after resize call so we can copy
157 // contents from the back buffer
158 mBuffers.clear();
159 mFrontBuffer = nullptr;
160 // recreateBackBufferIfNeeded always resets mBackBuffer
163 else
164 mBackBuffer = nullptr;
167 });
168}
169
171{
173 w->setBackingStore(nullptr);
174
175// if (mFrontBuffer == waylandWindow()->attached())
176// waylandWindow()->attach(0);
177
180}
181
186
188{
189 // Update dirty state of buffers based on what was painted. The back buffer will be
190 // less dirty, since we painted to it, while other buffers will become more dirty.
191 // This allows us to minimize copies between front and back buffers on swap in the
192 // cases where the painted region overlaps with the previous frame (front buffer).
194 if (b != mBackBuffer)
195 b->dirtyRegion() += region;
196 else
197 b->dirtyRegion() -= region;
198 }
199}
200
202{
203 mPainting = true;
205
210
211 // Although undocumented, QBackingStore::beginPaint expects the painted region
212 // to be cleared before use if the window has a surface format with an alpha.
213 // Fresh QWaylandShmBuffer are already cleared, so we don't need to clear those.
214 if (!bufferWasRecreated && window()->format().hasAlpha()) {
217 const QColor blank = Qt::transparent;
218 for (const QRect &rect : region)
220 }
221}
222
224{
225 mPainting = false;
226 if (mPendingFlush)
228}
229
230// Inspired by QCALayerBackingStore.
232{
234 return false;
235
237
238 // On Wayland, the window can have a device pixel ratio different from
239 // the window/screen, therefore we cannot rely on QHighDpi here, cf. QBackingStore::scroll.
240 // With fractional scaling we cannot easily scroll the existing pixels.
241 if (!qFuzzyIsNull(devicePixelRatio - static_cast<int>(devicePixelRatio)))
242 return false;
243
245 if (!mFrontBuffer)
246 return false;
247
248 const QPoint scrollDelta(dx, dy);
251
254
255 if (!inPlaceRegion.isEmpty()) {
258
263 }
264
265 if (!frontBufferRegion.isEmpty()) {
269 for (const QRect &rect : frontBufferRegion) {
275 }
276 }
277
278 // We do not mark the source region as dirty, even though it technically has "moved".
279 // This matches the behavior of other backingstore implementations using qt_scrollRectInImage.
281
282 return true;
283}
284
286{
287 // Invoked when the window is of type RasterSurface.
288
289 if (window != this->window()) {
290 auto waylandWindow = static_cast<QWaylandWindow *>(window->handle());
291 const auto scale = waylandWindow->scale();
292 auto newBuffer = new QWaylandShmBuffer(
300 return;
301 }
302
303 if (mPainting) {
305 mPendingFlush = true;
306 return;
307 }
308
309 mPendingFlush = false;
311
314
316
318
321}
322
324{
326}
327
329{
330 static const int MAX_BUFFERS = 5;
331 static const int MAX_AGE = 10 * MAX_BUFFERS;
332 bufferWasRecreated = false;
333
334 // Prune buffers that have not been used in a while or with different size.
335 for (auto i = mBuffers.size() - 1; i >= 0; --i) {
337 if (buffer->age() > MAX_AGE || buffer->size() != size) {
339 if (mBackBuffer == buffer)
340 mBackBuffer = nullptr;
341 if (mFrontBuffer == buffer)
342 mFrontBuffer = nullptr;
343 delete buffer;
344 }
345 }
346
347 QWaylandShmBuffer *buffer = nullptr;
349 if (candidate->busy())
350 continue;
351
352 if (!buffer || candidate->age() < buffer->age())
354 }
355
356 if (buffer)
357 return buffer;
358
359 if (mBuffers.size() < MAX_BUFFERS) {
361 if (!waylandWindow()->format().hasAlpha())
364 bufferWasRecreated = true;
366 return b;
367 }
368 return nullptr;
369}
370
372{
374
375 bool bufferWasRecreated = false;
379
380 // We look for a free buffer to draw into. If the buffer is not the last buffer we used,
381 // that is mBackBuffer, and the size is the same we copy the damaged content into the new
382 // buffer so that QPainter is happy to find the stuff it had drawn before. If the new
383 // buffer has a different size it needs to be redrawn completely anyway, and if the buffer
384 // is the same the stuff is there already.
385 // You can exercise the different codepaths with weston, switching between the gl and the
386 // pixman renderer. With the gl renderer release events are sent early so we can effectively
387 // run single buffered, while with the pixman renderer we have to use two.
389 while (!buffer) {
390 struct ::wl_display *display = mDisplay->wl_display();
393 if ((ecode == EPIPE || ecode == ECONNRESET))
394 qWarning("The Wayland connection broke during blocking read event. Did the Wayland compositor die?");
395 else
396 qWarning("The Wayland connection experienced a fatal error during blocking read event: %s", strerror(ecode));
397 _exit(-1);
398 }
400 }
401
404
406
408 if (mBackBuffer == buffer) {
409 buffer->setAge(0);
410 } else {
411 buffer->setAge(buffer->age() + 1);
412 }
413 }
414
417
418 return bufferWasRecreated;
419}
420
422{
424
426 if (clipRegion.isEmpty())
427 return;
428
430 return;
431
434
438 const auto clipRects = clipRegion.rects();
439 for (const QRect &clipRect : clipRects) { // Iterate clip rects, because complicated clip region causes higher CPU usage
440 if (clipRects.size() > 1)
441 painter.save();
445 if (clipRects.size() > 1)
447 }
448
450}
451
456
461
463{
467
469 int dpWidth = int(sourceImage.width() / dp);
470 int dpHeight = int(sourceImage.height() / dp);
473 QRect target; // needs to be in device independent pixels
475
476 //Top
477 target.setX(0);
478 target.setY(0);
483
484 //Left
489
490 //Right
495
496 //Bottom
497 target.setX(0);
503
505}
506
511
518
520{
521 return static_cast<QWaylandWindow *>(window()->handle());
522}
523
524#if QT_CONFIG(opengl)
526{
527 // Invoked from QPlatformBackingStore::composeAndFlush() that is called
528 // instead of flush() for widgets that have renderToTexture children
529 // (QOpenGLWidget, QQuickWidget).
530
531 const_cast<QWaylandShmBackingStore *>(this)->finalizeBackBuffer();
532
533 return *contentSurface();
534}
535#endif // opengl
536
537}
538
539QT_END_NAMESPACE
#define MAP_FAILED
QT_BEGIN_NAMESPACE void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &)