KWayland

shm_pool.cpp
1 /*
2  SPDX-FileCopyrightText: 2013 Martin Gräßlin <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 #include "shm_pool.h"
7 #include "buffer_p.h"
8 #include "event_queue.h"
9 #include "logging.h"
10 #include "wayland_pointer_p.h"
11 // Qt
12 #include <QDebug>
13 #include <QImage>
14 // system
15 #include <fcntl.h>
16 #include <sys/mman.h>
17 #include <unistd.h>
18 // wayland
19 #include <wayland-client-protocol.h>
20 
21 namespace KWayland
22 {
23 namespace Client
24 {
25 class Q_DECL_HIDDEN ShmPool::Private
26 {
27 public:
28  Private(ShmPool *q);
29  bool createPool();
30  bool resizePool(int32_t newSize);
31  QList<QSharedPointer<Buffer>>::iterator getBuffer(const QSize &size, int32_t stride, Buffer::Format format);
32  WaylandPointer<wl_shm, wl_shm_destroy> shm;
33  WaylandPointer<wl_shm_pool, wl_shm_pool_destroy> pool;
34  void *poolData = nullptr;
35  int fd = -1;
36  int32_t size = 1024;
37  bool valid = false;
38  int offset = 0;
40  EventQueue *queue = nullptr;
41 
42 private:
43  ShmPool *q;
44 };
45 
46 ShmPool::Private::Private(ShmPool *q)
47  : q(q)
48 {
49 }
50 
51 ShmPool::ShmPool(QObject *parent)
52  : QObject(parent)
53  , d(new Private(this))
54 {
55 }
56 
57 ShmPool::~ShmPool()
58 {
59  release();
60 }
61 
63 {
64  d->buffers.clear();
65  if (d->poolData) {
66  munmap(d->poolData, d->size);
67  d->poolData = nullptr;
68  }
69  if (d->fd != -1) {
70  close(d->fd);
71  d->fd = -1;
72  }
73  d->pool.release();
74  d->shm.release();
75  d->valid = false;
76  d->offset = 0;
77 }
78 
79 void ShmPool::destroy()
80 {
81  for (auto b : d->buffers) {
82  b->d->destroy();
83  }
84  d->buffers.clear();
85  if (d->poolData) {
86  munmap(d->poolData, d->size);
87  d->poolData = nullptr;
88  }
89  if (d->fd != -1) {
90  close(d->fd);
91  d->fd = -1;
92  }
93  d->pool.destroy();
94  d->shm.destroy();
95  d->valid = false;
96  d->offset = 0;
97 }
98 
99 void ShmPool::setup(wl_shm *shm)
100 {
101  Q_ASSERT(shm);
102  Q_ASSERT(!d->shm);
103  d->shm.setup(shm);
104  d->valid = d->createPool();
105 }
106 
107 void ShmPool::setEventQueue(EventQueue *queue)
108 {
109  d->queue = queue;
110 }
111 
112 EventQueue *ShmPool::eventQueue()
113 {
114  return d->queue;
115 }
116 
117 bool ShmPool::Private::createPool()
118 {
119 #if HAVE_MEMFD
120  fd = memfd_create("kwayland-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
121  if (fd >= 0) {
122  fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
123  } else
124 #endif
125  {
126  char templateName[] = "/tmp/kwayland-shared-XXXXXX";
127  fd = mkstemp(templateName);
128  if (fd >= 0) {
129  unlink(templateName);
130 
131  int flags = fcntl(fd, F_GETFD);
132  if (flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
133  close(fd);
134  fd = -1;
135  }
136  }
137  }
138 
139  if (fd == -1) {
140  qCDebug(KWAYLAND_CLIENT) << "Could not open temporary file for Shm pool";
141  return false;
142  }
143 
144  if (ftruncate(fd, size) < 0) {
145  qCDebug(KWAYLAND_CLIENT) << "Could not set size for Shm pool file";
146  return false;
147  }
148  poolData = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
149  pool.setup(wl_shm_create_pool(shm, fd, size));
150 
151  if (poolData == MAP_FAILED || !pool) {
152  qCDebug(KWAYLAND_CLIENT) << "Creating Shm pool failed";
153  return false;
154  }
155  return true;
156 }
157 
158 bool ShmPool::Private::resizePool(int32_t newSize)
159 {
160  if (ftruncate(fd, newSize) < 0) {
161  qCDebug(KWAYLAND_CLIENT) << "Could not set new size for Shm pool file";
162  return false;
163  }
164  wl_shm_pool_resize(pool, newSize);
165  munmap(poolData, size);
166  poolData = mmap(nullptr, newSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
167  size = newSize;
168  if (poolData == MAP_FAILED) {
169  qCDebug(KWAYLAND_CLIENT) << "Resizing Shm pool failed";
170  return false;
171  }
172  Q_EMIT q->poolResized();
173  return true;
174 }
175 
176 namespace
177 {
178 static Buffer::Format toBufferFormat(const QImage &image)
179 {
180  switch (image.format()) {
182  return Buffer::Format::ARGB32;
184  return Buffer::Format::RGB32;
186  qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << ". expect slow performance. Use QImage::Format_ARGB32_Premultiplied";
187  return Buffer::Format::ARGB32;
188  default:
189  qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << ". expect slow performance.";
190  return Buffer::Format::ARGB32;
191  }
192 }
193 }
194 
195 Buffer::Ptr ShmPool::createBuffer(const QImage &image)
196 {
197  if (image.isNull() || !d->valid) {
198  return QWeakPointer<Buffer>();
199  }
200  auto format = toBufferFormat(image);
201  auto it = d->getBuffer(image.size(), image.bytesPerLine(), format);
202  if (it == d->buffers.end()) {
203  return QWeakPointer<Buffer>();
204  }
205  if (format == Buffer::Format::ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied) {
207  (*it)->copy(imageCopy.bits());
208  } else {
209  (*it)->copy(image.bits());
210  }
211  return QWeakPointer<Buffer>(*it);
212 }
213 
214 Buffer::Ptr ShmPool::createBuffer(const QSize &size, int32_t stride, const void *src, Buffer::Format format)
215 {
216  if (size.isEmpty() || !d->valid) {
217  return QWeakPointer<Buffer>();
218  }
219  auto it = d->getBuffer(size, stride, format);
220  if (it == d->buffers.end()) {
221  return QWeakPointer<Buffer>();
222  }
223  (*it)->copy(src);
224  return QWeakPointer<Buffer>(*it);
225 }
226 
227 namespace
228 {
229 static wl_shm_format toWaylandFormat(Buffer::Format format)
230 {
231  switch (format) {
232  case Buffer::Format::ARGB32:
233  return WL_SHM_FORMAT_ARGB8888;
234  case Buffer::Format::RGB32:
235  return WL_SHM_FORMAT_XRGB8888;
236  }
237  abort();
238 }
239 }
240 
241 Buffer::Ptr ShmPool::getBuffer(const QSize &size, int32_t stride, Buffer::Format format)
242 {
243  auto it = d->getBuffer(size, stride, format);
244  if (it == d->buffers.end()) {
245  return QWeakPointer<Buffer>();
246  }
247  return QWeakPointer<Buffer>(*it);
248 }
249 
250 QList<QSharedPointer<Buffer>>::iterator ShmPool::Private::getBuffer(const QSize &s, int32_t stride, Buffer::Format format)
251 {
252  for (auto it = buffers.begin(); it != buffers.end(); ++it) {
253  auto buffer = *it;
254  if (!buffer->isReleased() || buffer->isUsed()) {
255  continue;
256  }
257  if (buffer->size() != s || buffer->stride() != stride || buffer->format() != format) {
258  continue;
259  }
260  buffer->setReleased(false);
261  return it;
262  }
263  const int32_t byteCount = s.height() * stride;
264  if (offset + byteCount > size) {
265  if (!resizePool(size + byteCount)) {
266  return buffers.end();
267  }
268  }
269  // we don't have a buffer which we could reuse - need to create a new one
270  wl_buffer *native = wl_shm_pool_create_buffer(pool, offset, s.width(), s.height(), stride, toWaylandFormat(format));
271  if (!native) {
272  return buffers.end();
273  }
274  if (queue) {
275  queue->addProxy(native);
276  }
277  Buffer *buffer = new Buffer(q, native, s, stride, offset, format);
278  offset += byteCount;
279  auto it = buffers.insert(buffers.end(), QSharedPointer<Buffer>(buffer));
280  return it;
281 }
282 
283 bool ShmPool::isValid() const
284 {
285  return d->valid;
286 }
287 
288 void *ShmPool::poolAddress() const
289 {
290  return d->poolData;
291 }
292 
293 wl_shm *ShmPool::shm()
294 {
295  return d->shm;
296 }
297 
298 }
299 }
Format_ARGB32_Premultiplied
virtual void release(quint64 objid)
bool isEmpty() const const
QImage::Format format() const const
int width() const const
Wrapper class for wl_event_queue interface.
Definition: event_queue.h:54
int height() const const
uchar * bits()
QImage convertToFormat(QImage::Format format, Qt::ImageConversionFlags flags) const &const
bool isNull() const const
Format
All image formats supported by the implementation.
Definition: buffer.h:36
int bytesPerLine() const const
QSize size() const const
Q_SCRIPTABLE Q_NOREPLY void abort()
QImage copy(const QRect &rectangle) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Feb 7 2023 03:56:22 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.