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 #include <QTemporaryFile>
15 // system
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  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  : tmpFile(new QTemporaryFile())
48  , q(q)
49 {
50 }
51 
52 ShmPool::ShmPool(QObject *parent)
53  : QObject(parent)
54  , d(new Private(this))
55 {
56 }
57 
58 ShmPool::~ShmPool()
59 {
60  release();
61 }
62 
63 void ShmPool::release()
64 {
65  d->buffers.clear();
66  if (d->poolData) {
67  munmap(d->poolData, d->size);
68  d->poolData = nullptr;
69  }
70  d->pool.release();
71  d->shm.release();
72  d->tmpFile->close();
73  d->valid = false;
74  d->offset = 0;
75 }
76 
77 void ShmPool::destroy()
78 {
79  for (auto b : d->buffers) {
80  b->d->destroy();
81  }
82  d->buffers.clear();
83  if (d->poolData) {
84  munmap(d->poolData, d->size);
85  d->poolData = nullptr;
86  }
87  d->pool.destroy();
88  d->shm.destroy();
89  d->tmpFile->close();
90  d->valid = false;
91  d->offset = 0;
92 }
93 
94 void ShmPool::setup(wl_shm *shm)
95 {
96  Q_ASSERT(shm);
97  Q_ASSERT(!d->shm);
98  d->shm.setup(shm);
99  d->valid = d->createPool();
100 }
101 
102 void ShmPool::setEventQueue(EventQueue *queue)
103 {
104  d->queue = queue;
105 }
106 
107 EventQueue *ShmPool::eventQueue()
108 {
109  return d->queue;
110 }
111 
112 bool ShmPool::Private::createPool()
113 {
114  if (!tmpFile->open()) {
115  qCDebug(KWAYLAND_CLIENT) << "Could not open temporary file for Shm pool";
116  return false;
117  }
118  if (unlink(tmpFile->fileName().toUtf8().constData()) != 0) {
119  qCDebug(KWAYLAND_CLIENT) << "Unlinking temporary file for Shm pool from file system failed";
120  }
121  if (ftruncate(tmpFile->handle(), size) < 0) {
122  qCDebug(KWAYLAND_CLIENT) << "Could not set size for Shm pool file";
123  return false;
124  }
125  poolData = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile->handle(), 0);
126  pool.setup(wl_shm_create_pool(shm, tmpFile->handle(), size));
127 
128  if (poolData == MAP_FAILED || !pool) {
129  qCDebug(KWAYLAND_CLIENT) << "Creating Shm pool failed";
130  return false;
131  }
132  return true;
133 }
134 
135 bool ShmPool::Private::resizePool(int32_t newSize)
136 {
137  if (ftruncate(tmpFile->handle(), newSize) < 0) {
138  qCDebug(KWAYLAND_CLIENT) << "Could not set new size for Shm pool file";
139  return false;
140  }
141  wl_shm_pool_resize(pool, newSize);
142  munmap(poolData, size);
143  poolData = mmap(nullptr, newSize, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile->handle(), 0);
144  size = newSize;
145  if (poolData == MAP_FAILED) {
146  qCDebug(KWAYLAND_CLIENT) << "Resizing Shm pool failed";
147  return false;
148  }
149  Q_EMIT q->poolResized();
150  return true;
151 }
152 
153 namespace
154 {
155 static Buffer::Format toBufferFormat(const QImage &image)
156 {
157  switch (image.format()) {
159  return Buffer::Format::ARGB32;
161  return Buffer::Format::RGB32;
163  qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << ". expect slow performance. Use QImage::Format_ARGB32_Premultiplied";
164  return Buffer::Format::ARGB32;
165  default:
166  qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << ". expect slow performance.";
167  return Buffer::Format::ARGB32;
168  }
169 }
170 }
171 
172 Buffer::Ptr ShmPool::createBuffer(const QImage &image)
173 {
174  if (image.isNull() || !d->valid) {
175  return QWeakPointer<Buffer>();
176  }
177  auto format = toBufferFormat(image);
178  auto it = d->getBuffer(image.size(), image.bytesPerLine(), format);
179  if (it == d->buffers.end()) {
180  return QWeakPointer<Buffer>();
181  }
182  if (format == Buffer::Format::ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied) {
184  (*it)->copy(imageCopy.bits());
185  } else {
186  (*it)->copy(image.bits());
187  }
188  return QWeakPointer<Buffer>(*it);
189 }
190 
191 Buffer::Ptr ShmPool::createBuffer(const QSize &size, int32_t stride, const void *src, Buffer::Format format)
192 {
193  if (size.isEmpty() || !d->valid) {
194  return QWeakPointer<Buffer>();
195  }
196  auto it = d->getBuffer(size, stride, format);
197  if (it == d->buffers.end()) {
198  return QWeakPointer<Buffer>();
199  }
200  (*it)->copy(src);
201  return QWeakPointer<Buffer>(*it);
202 }
203 
204 namespace
205 {
206 static wl_shm_format toWaylandFormat(Buffer::Format format)
207 {
208  switch (format) {
209  case Buffer::Format::ARGB32:
210  return WL_SHM_FORMAT_ARGB8888;
211  case Buffer::Format::RGB32:
212  return WL_SHM_FORMAT_XRGB8888;
213  }
214  abort();
215 }
216 }
217 
218 Buffer::Ptr ShmPool::getBuffer(const QSize &size, int32_t stride, Buffer::Format format)
219 {
220  auto it = d->getBuffer(size, stride, format);
221  if (it == d->buffers.end()) {
222  return QWeakPointer<Buffer>();
223  }
224  return QWeakPointer<Buffer>(*it);
225 }
226 
227 QList<QSharedPointer<Buffer>>::iterator ShmPool::Private::getBuffer(const QSize &s, int32_t stride, Buffer::Format format)
228 {
229  for (auto it = buffers.begin(); it != buffers.end(); ++it) {
230  auto buffer = *it;
231  if (!buffer->isReleased() || buffer->isUsed()) {
232  continue;
233  }
234  if (buffer->size() != s || buffer->stride() != stride || buffer->format() != format) {
235  continue;
236  }
237  buffer->setReleased(false);
238  return it;
239  }
240  const int32_t byteCount = s.height() * stride;
241  if (offset + byteCount > size) {
242  if (!resizePool(size + byteCount)) {
243  return buffers.end();
244  }
245  }
246  // we don't have a buffer which we could reuse - need to create a new one
247  wl_buffer *native = wl_shm_pool_create_buffer(pool, offset, s.width(), s.height(), stride, toWaylandFormat(format));
248  if (!native) {
249  return buffers.end();
250  }
251  if (queue) {
252  queue->addProxy(native);
253  }
254  Buffer *buffer = new Buffer(q, native, s, stride, offset, format);
255  offset += byteCount;
256  auto it = buffers.insert(buffers.end(), QSharedPointer<Buffer>(buffer));
257  return it;
258 }
259 
260 bool ShmPool::isValid() const
261 {
262  return d->valid;
263 }
264 
265 void *ShmPool::poolAddress() const
266 {
267  return d->poolData;
268 }
269 
270 wl_shm *ShmPool::shm()
271 {
272  return d->shm;
273 }
274 
275 }
276 }
QImage convertToFormat(QImage::Format format, Qt::ImageConversionFlags flags) const &const
int width() const const
Format_ARGB32_Premultiplied
bool isEmpty() const const
Wrapper class for wl_event_queue interface.
Definition: event_queue.h:54
Wrapper class for wl_buffer interface.
Definition: buffer.h:30
bool isNull() const const
QImage copy(const QRect &rectangle) const const
Format
All image formats supported by the implementation.
Definition: buffer.h:36
int bytesPerLine() const const
int height() const const
QSize size() const const
uchar * bits()
QObject * parent() const const
QImage::Format format() const const
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Nov 29 2021 22:45:34 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.