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 if (!mFrontBuffer)
245 return false;
246
247 const QPoint scrollDelta(dx, dy);
250
253
254 if (!inPlaceRegion.isEmpty()) {
257
262 }
263
264 if (!frontBufferRegion.isEmpty()) {
268 for (const QRect &rect : frontBufferRegion) {
274 }
275 }
276
277 // We do not mark the source region as dirty, even though it technically has "moved".
278 // This matches the behavior of other backingstore implementations using qt_scrollRectInImage.
280
281 return true;
282}
283
285{
286 // Invoked when the window is of type RasterSurface.
287
288 if (window != this->window()) {
289 auto waylandWindow = static_cast<QWaylandWindow *>(window->handle());
290 const auto scale = waylandWindow->scale();
291 auto newBuffer = new QWaylandShmBuffer(
299 return;
300 }
301
302 if (mPainting) {
304 mPendingFlush = true;
305 return;
306 }
307
308 mPendingFlush = false;
310
313
315
317
320}
321
323{
325}
326
328{
329 static const int MAX_BUFFERS = 5;
330 static const int MAX_AGE = 10 * MAX_BUFFERS;
331 bufferWasRecreated = false;
332
333 // Prune buffers that have not been used in a while or with different size.
334 for (auto i = mBuffers.size() - 1; i >= 0; --i) {
336 if (buffer->age() > MAX_AGE || buffer->size() != size) {
338 if (mBackBuffer == buffer)
339 mBackBuffer = nullptr;
340 if (mFrontBuffer == buffer)
341 mFrontBuffer = nullptr;
342 delete buffer;
343 }
344 }
345
346 QWaylandShmBuffer *buffer = nullptr;
348 if (candidate->busy())
349 continue;
350
351 if (!buffer || candidate->age() < buffer->age())
353 }
354
355 if (buffer)
356 return buffer;
357
358 if (mBuffers.size() < MAX_BUFFERS) {
360 if (!waylandWindow()->format().hasAlpha())
363 bufferWasRecreated = true;
365 return b;
366 }
367 return nullptr;
368}
369
371{
373
374 bool bufferWasRecreated = false;
378
379 // We look for a free buffer to draw into. If the buffer is not the last buffer we used,
380 // that is mBackBuffer, and the size is the same we copy the damaged content into the new
381 // buffer so that QPainter is happy to find the stuff it had drawn before. If the new
382 // buffer has a different size it needs to be redrawn completely anyway, and if the buffer
383 // is the same the stuff is there already.
384 // You can exercise the different codepaths with weston, switching between the gl and the
385 // pixman renderer. With the gl renderer release events are sent early so we can effectively
386 // run single buffered, while with the pixman renderer we have to use two.
388 while (!buffer) {
389 struct ::wl_display *display = mDisplay->wl_display();
392 if ((ecode == EPIPE || ecode == ECONNRESET))
393 qWarning("The Wayland connection broke during blocking read event. Did the Wayland compositor die?");
394 else
395 qWarning("The Wayland connection experienced a fatal error during blocking read event: %s", strerror(ecode));
396 _exit(-1);
397 }
399 }
400
403
405
407 if (mBackBuffer == buffer) {
408 buffer->setAge(0);
409 } else {
410 buffer->setAge(buffer->age() + 1);
411 }
412 }
413
416
417 return bufferWasRecreated;
418}
419
421{
423
425 if (clipRegion.isEmpty())
426 return;
427
429 return;
430
433
437 const auto clipRects = clipRegion.rects();
438 for (const QRect &clipRect : clipRects) { // Iterate clip rects, because complicated clip region causes higher CPU usage
439 if (clipRects.size() > 1)
440 painter.save();
444 if (clipRects.size() > 1)
446 }
447
449}
450
455
460
462{
466
468 int dpWidth = int(sourceImage.width() / dp);
469 int dpHeight = int(sourceImage.height() / dp);
472 QRect target; // needs to be in device independent pixels
474
475 //Top
476 target.setX(0);
477 target.setY(0);
482
483 //Left
488
489 //Right
494
495 //Bottom
496 target.setX(0);
502
504}
505
510
517
519{
520 return static_cast<QWaylandWindow *>(window()->handle());
521}
522
523#if QT_CONFIG(opengl)
525{
526 // Invoked from QPlatformBackingStore::composeAndFlush() that is called
527 // instead of flush() for widgets that have renderToTexture children
528 // (QOpenGLWidget, QQuickWidget).
529
530 const_cast<QWaylandShmBackingStore *>(this)->finalizeBackBuffer();
531
532 return *contentSurface();
533}
534#endif // opengl
535
536}
537
538QT_END_NAMESPACE
#define MAP_FAILED
QT_BEGIN_NAMESPACE void qt_scrollRectInImage(QImage &, const QRect &, const QPoint &)