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));
105 wl_proxy_set_queue(reinterpret_cast<struct wl_proxy *>(buffer()), customEventQueue);
106}
107
116
118{
120
121 if (!margins.isNull() && margins != mMargins) {
122 if (mMarginsImage) {
123 delete mMarginsImage;
124 }
125 uchar *bits = const_cast<uchar *>(mImage.constBits());
131 }
132 if (margins.isNull()) {
133 delete mMarginsImage;
134 mMarginsImage = nullptr;
135 }
136
138 if (!mMarginsImage)
139 return &mImage;
140
141 return mMarginsImage;
142
143}
144
148{
153 auto copy = mBuffers;
154 // clear available buffers so we create new ones
155 // actual deletion is deferred till after resize call so we can copy
156 // contents from the back buffer
157 mBuffers.clear();
158 mFrontBuffer = nullptr;
159 // recreateBackBufferIfNeeded always resets mBackBuffer
162 else
163 mBackBuffer = nullptr;
166 });
167}
168
170{
172 w->setBackingStore(nullptr);
173
174// if (mFrontBuffer == waylandWindow()->attached())
175// waylandWindow()->attach(0);
176
179}
180
185
187{
188 // Update dirty state of buffers based on what was painted. The back buffer will be
189 // less dirty, since we painted to it, while other buffers will become more dirty.
190 // This allows us to minimize copies between front and back buffers on swap in the
191 // cases where the painted region overlaps with the previous frame (front buffer).
193 if (b != mBackBuffer)
194 b->dirtyRegion() += region;
195 else
196 b->dirtyRegion() -= region;
197 }
198}
199
201{
202 mPainting = true;
204
209
210 // Although undocumented, QBackingStore::beginPaint expects the painted region
211 // to be cleared before use if the window has a surface format with an alpha.
212 // Fresh QWaylandShmBuffer are already cleared, so we don't need to clear those.
213 if (!bufferWasRecreated && window()->format().hasAlpha()) {
216 const QColor blank = Qt::transparent;
217 for (const QRect &rect : region)
219 }
220}
221
223{
224 mPainting = false;
225 if (mPendingFlush)
227}
228
229// Inspired by QCALayerBackingStore.
231{
233 return false;
234
236
237 // On Wayland, the window can have a device pixel ratio different from
238 // the window/screen, therefore we cannot rely on QHighDpi here, cf. QBackingStore::scroll.
239 // With fractional scaling we cannot easily scroll the existing pixels.
240 if (!qFuzzyIsNull(devicePixelRatio - static_cast<int>(devicePixelRatio)))
241 return false;
242
244
245 const QPoint scrollDelta(dx, dy);
248
251
252 if (!inPlaceRegion.isEmpty()) {
255
260 }
261
262 if (!frontBufferRegion.isEmpty()) {
266 for (const QRect &rect : frontBufferRegion) {
272 }
273 }
274
275 // We do not mark the source region as dirty, even though it technically has "moved".
276 // This matches the behavior of other backingstore implementations using qt_scrollRectInImage.
278
279 return true;
280}
281
283{
284 // Invoked when the window is of type RasterSurface.
285
286 if (window != this->window()) {
287 auto waylandWindow = static_cast<QWaylandWindow *>(window->handle());
288 const auto scale = waylandWindow->scale();
289 auto newBuffer = new QWaylandShmBuffer(
297 return;
298 }
299
300 if (mPainting) {
302 mPendingFlush = true;
303 return;
304 }
305
306 mPendingFlush = false;
308
311
313
315
318}
319
321{
323}
324
326{
327 static const int MAX_BUFFERS = 5;
328 static const int MAX_AGE = 10 * MAX_BUFFERS;
329 bufferWasRecreated = false;
330
331 // Prune buffers that have not been used in a while or with different size.
332 for (auto i = mBuffers.size() - 1; i >= 0; --i) {
334 if (buffer->age() > MAX_AGE || buffer->size() != size) {
336 if (mBackBuffer == buffer)
337 mBackBuffer = nullptr;
338 if (mFrontBuffer == buffer)
339 mFrontBuffer = nullptr;
340 delete buffer;
341 }
342 }
343
344 QWaylandShmBuffer *buffer = nullptr;
346 if (candidate->busy())
347 continue;
348
349 if (!buffer || candidate->age() < buffer->age())
351 }
352
353 if (buffer)
354 return buffer;
355
356 if (mBuffers.size() < MAX_BUFFERS) {
358 if (!waylandWindow()->format().hasAlpha())
361 bufferWasRecreated = true;
363 return b;
364 }
365 return nullptr;
366}
367
369{
371
372 bool bufferWasRecreated = false;
376
377 // We look for a free buffer to draw into. If the buffer is not the last buffer we used,
378 // that is mBackBuffer, and the size is the same we copy the damaged content into the new
379 // buffer so that QPainter is happy to find the stuff it had drawn before. If the new
380 // buffer has a different size it needs to be redrawn completely anyway, and if the buffer
381 // is the same the stuff is there already.
382 // You can exercise the different codepaths with weston, switching between the gl and the
383 // pixman renderer. With the gl renderer release events are sent early so we can effectively
384 // run single buffered, while with the pixman renderer we have to use two.
386 while (!buffer) {
387 struct ::wl_display *display = mDisplay->wl_display();
390 if ((ecode == EPIPE || ecode == ECONNRESET))
391 qWarning("The Wayland connection broke during blocking read event. Did the Wayland compositor die?");
392 else
393 qWarning("The Wayland connection experienced a fatal error during blocking read event: %s", strerror(ecode));
394 _exit(-1);
395 }
397 }
398
401
403
405 if (mBackBuffer == buffer) {
406 buffer->setAge(0);
407 } else {
408 buffer->setAge(buffer->age() + 1);
409 }
410 }
411
414
415 return bufferWasRecreated;
416}
417
419{
421
423 if (clipRegion.isEmpty())
424 return;
425
427 return;
428
431
435 const auto clipRects = clipRegion.rects();
436 for (const QRect &clipRect : clipRects) { // Iterate clip rects, because complicated clip region causes higher CPU usage
437 if (clipRects.size() > 1)
438 painter.save();
442 if (clipRects.size() > 1)
444 }
445
447}
448
453
458
460{
464
466 int dpWidth = int(sourceImage.width() / dp);
467 int dpHeight = int(sourceImage.height() / dp);
470 QRect target; // needs to be in device independent pixels
472
473 //Top
474 target.setX(0);
475 target.setY(0);
480
481 //Left
486
487 //Right
492
493 //Bottom
494 target.setX(0);
500
502}
503
508
515
517{
518 return static_cast<QWaylandWindow *>(window()->handle());
519}
520
521#if QT_CONFIG(opengl)
523{
524 // Invoked from QPlatformBackingStore::composeAndFlush() that is called
525 // instead of flush() for widgets that have renderToTexture children
526 // (QOpenGLWidget, QQuickWidget).
527
528 const_cast<QWaylandShmBackingStore *>(this)->finalizeBackBuffer();
529
530 return *contentSurface();
531}
532#endif // opengl
533
534}
535
536QT_END_NAMESPACE
#define MAP_FAILED
QT_BEGIN_NAMESPACE void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &)