Qt 6.x
The Qt SDK
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{
51 int stride = size.width() * 4;
52 int alloc = stride * size.height();
53 int fd = -1;
54
55#ifdef SYS_memfd_create
56 fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING);
57 if (fd >= 0)
58 fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
59#endif
60
61 QScopedPointer<QFile> filePointer;
62
63 if (fd == -1) {
65 QLatin1String("/wayland-shm-XXXXXX"));
66 tmpFile->open();
67 filePointer.reset(tmpFile);
68 } else {
69 auto file = new QFile;
71 filePointer.reset(file);
72 }
73 if (!filePointer->isOpen() || !filePointer->resize(alloc)) {
74 qWarning("QWaylandShmBuffer: failed: %s", qUtf8Printable(filePointer->errorString()));
75 return;
76 }
77 fd = filePointer->handle();
78
79 // map ourselves: QFile::map() will unmap when the object is destroyed,
80 // but we want this mapping to persist (unmapping in destructor)
81 uchar *data = (uchar *)
82 mmap(nullptr, alloc, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
83 if (data == (uchar *) MAP_FAILED) {
84 qErrnoWarning("QWaylandShmBuffer: mmap failed");
85 return;
86 }
87
88 QWaylandShm* shm = display->shm();
89 wl_shm_format wl_format = shm->formatFrom(format);
90 mImage = QImage(data, size.width(), size.height(), stride, format);
92
93 mShmPool = wl_shm_create_pool(shm->object(), fd, alloc);
94 init(wl_shm_pool_create_buffer(mShmPool,0, size.width(), size.height(),
95 stride, wl_format));
96}
97
99{
100 delete mMarginsImage;
101 if (mImage.constBits())
102 munmap((void *) mImage.constBits(), mImage.sizeInBytes());
103 if (mShmPool)
104 wl_shm_pool_destroy(mShmPool);
105}
106
108{
109 QMargins margins = marginsIn * mImage.devicePixelRatio();
110
111 if (!margins.isNull() && margins != mMargins) {
112 if (mMarginsImage) {
113 delete mMarginsImage;
114 }
115 uchar *bits = const_cast<uchar *>(mImage.constBits());
116 uchar *b_s_data = bits + margins.top() * mImage.bytesPerLine() + margins.left() * 4;
117 int b_s_width = mImage.size().width() - margins.left() - margins.right();
118 int b_s_height = mImage.size().height() - margins.top() - margins.bottom();
119 mMarginsImage = new QImage(b_s_data, b_s_width,b_s_height,mImage.bytesPerLine(),mImage.format());
120 mMarginsImage->setDevicePixelRatio(mImage.devicePixelRatio());
121 }
122 if (margins.isNull()) {
123 delete mMarginsImage;
124 mMarginsImage = nullptr;
125 }
126
127 mMargins = margins;
128 if (!mMarginsImage)
129 return &mImage;
130
131 return mMarginsImage;
132
133}
134
137 , mDisplay(display)
138{
140 auto copy = mBuffers;
141 // clear available buffers so we create new ones
142 // actual deletion is deferred till after resize call so we can copy
143 // contents from the back buffer
144 mBuffers.clear();
145 mFrontBuffer = nullptr;
146 // resize always resets mBackBuffer
147 if (mRequestedSize.isValid() && waylandWindow())
148 resize(mRequestedSize);
150 });
151}
152
154{
156 w->setBackingStore(nullptr);
157
158// if (mFrontBuffer == waylandWindow()->attached())
159// waylandWindow()->attach(0);
160
161 qDeleteAll(mBuffers);
162}
163
165{
166 return contentSurface();
167}
168
170{
171 mPainting = true;
172 ensureSize();
173
174 waylandWindow()->setCanResize(false);
175
176 if (mBackBuffer->image()->hasAlphaChannel()) {
178 p.setCompositionMode(QPainter::CompositionMode_Source);
179 const QColor blank = Qt::transparent;
180 for (const QRect &rect : region)
181 p.fillRect(rect, blank);
182 }
183}
184
186{
187 mPainting = false;
188 if (mPendingFlush)
189 flush(window(), mPendingRegion, QPoint());
191}
192
194{
197 resize(mRequestedSize);
198}
199
201{
202 // Invoked when the window is of type RasterSurface or when the window is
203 // RasterGLSurface and there are no child widgets requiring OpenGL composition.
204
205 // For the case of RasterGLSurface + having to compose, the composeAndFlush() is
206 // called instead. The default implementation from QPlatformBackingStore is sufficient
207 // however so no need to reimplement that.
208
209
212
213 if (mPainting) {
214 mPendingRegion |= region;
215 mPendingFlush = true;
216 return;
217 }
218
219 mPendingFlush = false;
220 mPendingRegion = QRegion();
221
222 if (windowDecoration() && windowDecoration()->isDirty())
223 updateDecorations();
224
225 mFrontBuffer = mBackBuffer;
226
228 waylandWindow()->safeCommit(mFrontBuffer, region.translated(margins.left(), margins.top()));
229}
230
232{
233 mRequestedSize = size;
234}
235
236QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size)
237{
238 const auto copy = mBuffers; // remove when ported to vector<unique_ptr> + remove_if
239 for (QWaylandShmBuffer *b : copy) {
240 if (!b->busy()) {
241 if (b->size() == size) {
242 return b;
243 } else {
244 mBuffers.remove(b);
245 if (mBackBuffer == b)
246 mBackBuffer = nullptr;
247 delete b;
248 }
249 }
250 }
251
252 static const size_t MAX_BUFFERS = 5;
253 if (mBuffers.size() < MAX_BUFFERS) {
255 QWaylandShmBuffer *b = new QWaylandShmBuffer(mDisplay, size, format, waylandWindow()->scale());
256 mBuffers.push_front(b);
257 return b;
258 }
259 return nullptr;
260}
261
263{
266 QSize sizeWithMargins = (size + QSize(margins.left()+margins.right(),margins.top()+margins.bottom())) * scale;
267
268 // We look for a free buffer to draw into. If the buffer is not the last buffer we used,
269 // that is mBackBuffer, and the size is the same we memcpy the old content into the new
270 // buffer so that QPainter is happy to find the stuff it had drawn before. If the new
271 // buffer has a different size it needs to be redrawn completely anyway, and if the buffer
272 // is the same the stuff is there already.
273 // You can exercise the different codepaths with weston, switching between the gl and the
274 // pixman renderer. With the gl renderer release events are sent early so we can effectively
275 // run single buffered, while with the pixman renderer we have to use two.
276 QWaylandShmBuffer *buffer = getBuffer(sizeWithMargins);
277 while (!buffer) {
278 qCDebug(lcWaylandBackingstore, "QWaylandShmBackingStore: stalling waiting for a buffer to be released from the compositor...");
279
280 mDisplay->blockingReadEvents();
281 buffer = getBuffer(sizeWithMargins);
282 }
283
284 qsizetype oldSizeInBytes = mBackBuffer ? mBackBuffer->image()->sizeInBytes() : 0;
285 qsizetype newSizeInBytes = buffer->image()->sizeInBytes();
286
287 // mBackBuffer may have been deleted here but if so it means its size was different so we wouldn't copy it anyway
288 if (mBackBuffer != buffer && oldSizeInBytes == newSizeInBytes)
289 memcpy(buffer->image()->bits(), mBackBuffer->image()->constBits(), newSizeInBytes);
290
291 mBackBuffer = buffer;
292
293 // ensure the new buffer is at the beginning of the list so next time getBuffer() will pick
294 // it if possible
295 if (mBuffers.front() != buffer) {
296 mBuffers.remove(buffer);
297 mBuffers.push_front(buffer);
298 }
299
300 if (windowDecoration() && window()->isVisible() && oldSizeInBytes != newSizeInBytes)
302}
303
305{
306 return mBackBuffer->image();
307}
308
310{
311 return windowDecoration() ? mBackBuffer->imageInsideMargins(windowDecorationMargins()) : mBackBuffer->image();
312}
313
314void QWaylandShmBackingStore::updateDecorations()
315{
316 QPainter decorationPainter(entireSurface());
317 decorationPainter.setCompositionMode(QPainter::CompositionMode_Source);
318 QImage sourceImage = windowDecoration()->contentImage();
319
320 qreal dp = sourceImage.devicePixelRatio();
321 int dpWidth = int(sourceImage.width() / dp);
322 int dpHeight = int(sourceImage.height() / dp);
323 QTransform sourceMatrix;
324 sourceMatrix.scale(dp, dp);
325 QRect target; // needs to be in device independent pixels
326
327 //Top
328 target.setX(0);
329 target.setY(0);
330 target.setWidth(dpWidth);
331 target.setHeight(windowDecorationMargins().top());
332 decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
333
334 //Left
335 target.setWidth(windowDecorationMargins().left());
336 target.setHeight(dpHeight);
337 decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
338
339 //Right
340 target.setX(dpWidth - windowDecorationMargins().right());
342 decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
343
344 //Bottom
345 target.setX(0);
346 target.setY(dpHeight - windowDecorationMargins().bottom());
347 target.setWidth(dpWidth);
349 decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target));
350}
351
353{
354 return waylandWindow()->decoration();
355}
356
358{
359 if (windowDecoration())
360 return windowDecoration()->margins();
361 return QMargins();
362}
363
365{
366 return static_cast<QWaylandWindow *>(window()->handle());
367}
368
369#if QT_CONFIG(opengl)
371{
372 // Invoked from QPlatformBackingStore::composeAndFlush() that is called
373 // instead of flush() for widgets that have renderToTexture children
374 // (QOpenGLWidget, QQuickWidget).
375
376 return *contentSurface();
377}
378#endif // opengl
379
380}
381
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
\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:4571
qsizetype bytesPerLine() const
Returns the number of bytes per image scanline.
Definition qimage.cpp:1538
qsizetype sizeInBytes() const
Definition qimage.cpp:1526
QSize size() const
Returns the size of the image, i.e.
int width() const
Returns the width of the image.
int height() const
Returns the height of the image.
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:2146
void setDevicePixelRatio(qreal scaleFactor)
Sets the device pixel ratio for the image.
Definition qimage.cpp:1488
qreal devicePixelRatio() const
Returns the device pixel ratio for the image.
Definition qimage.cpp:1460
const uchar * constBits() const
Returns a pointer to the first pixel data.
Definition qimage.cpp:1713
\inmodule QtCore
Definition qmargins.h:23
constexpr int bottom() const noexcept
Returns the bottom margin.
Definition qmargins.h:119
constexpr bool isNull() const noexcept
Returns true if all margins are is 0; otherwise returns false.
Definition qmargins.h:107
constexpr int left() const noexcept
Returns the left margin.
Definition qmargins.h:110
constexpr int right() const noexcept
Returns the right margin.
Definition qmargins.h:116
constexpr int top() const noexcept
Returns the top margin.
Definition qmargins.h:113
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
@ 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)
virtual QImage::Format format() const =0
Reimplement in subclass to return the image format which corresponds to the screen format.
\inmodule QtCore\reentrant
Definition qpoint.h:23
\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
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
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:126
static QString writableLocation(StandardLocation type)
\inmodule QtCore \reentrant
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...
QRect mapRect(const QRect &) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
\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:46
static jboolean copy(JNIEnv *, jobject)
#define qWarning
Definition qlogging.h:162
#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
const void GLsizei GLsizei stride
GLenum GLuint buffer
GLint left
GLint GLint bottom
GLenum target
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
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 MAP_FAILED
#define qUtf8Printable(string)
Definition qstring.h:1395
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:27
ptrdiff_t qsizetype
Definition qtypes.h:70
double qreal
Definition qtypes.h:92
QFile file
[0]
edit isVisible()
aWidget window() -> setWindowTitle("New Window Title")
[2]