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 "event_queue.h"
8 #include "buffer.h"
9 #include "buffer_p.h"
10 #include "logging.h"
11 #include "wayland_pointer_p.h"
12 // Qt
13 #include <QDebug>
14 #include <QImage>
15 #include <QTemporaryFile>
16 // system
17 #include <unistd.h>
18 #include <sys/mman.h>
19 // wayland
20 #include <wayland-client-protocol.h>
21 
22 namespace KWayland
23 {
24 namespace Client
25 {
26 
27 class Q_DECL_HIDDEN ShmPool::Private
28 {
29 public:
30  Private(ShmPool *q);
31  bool createPool();
32  bool resizePool(int32_t newSize);
33  QList<QSharedPointer<Buffer>>::iterator getBuffer(const QSize &size, int32_t stride, Buffer::Format format);
34  WaylandPointer<wl_shm, wl_shm_destroy> shm;
35  WaylandPointer<wl_shm_pool, wl_shm_pool_destroy> pool;
36  void *poolData = nullptr;
37  int32_t size = 1024;
39  bool valid = false;
40  int offset = 0;
42  EventQueue *queue = nullptr;
43 private:
44  ShmPool *q;
45 };
46 
47 ShmPool::Private::Private(ShmPool *q)
48  : tmpFile(new QTemporaryFile())
49  , q(q)
50 {
51 }
52 
53 
54 ShmPool::ShmPool(QObject *parent)
55  : QObject(parent)
56  , d(new Private(this))
57 {
58 }
59 
60 ShmPool::~ShmPool()
61 {
62  release();
63 }
64 
65 void ShmPool::release()
66 {
67  d->buffers.clear();
68  if (d->poolData) {
69  munmap(d->poolData, d->size);
70  d->poolData = nullptr;
71  }
72  d->pool.release();
73  d->shm.release();
74  d->tmpFile->close();
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  d->pool.destroy();
90  d->shm.destroy();
91  d->tmpFile->close();
92  d->valid = false;
93  d->offset = 0;
94 }
95 
96 void ShmPool::setup(wl_shm *shm)
97 {
98  Q_ASSERT(shm);
99  Q_ASSERT(!d->shm);
100  d->shm.setup(shm);
101  d->valid = d->createPool();
102 }
103 
104 void ShmPool::setEventQueue(EventQueue *queue)
105 {
106  d->queue = queue;
107 }
108 
109 EventQueue *ShmPool::eventQueue()
110 {
111  return d->queue;
112 }
113 
114 bool ShmPool::Private::createPool()
115 {
116  if (!tmpFile->open()) {
117  qCDebug(KWAYLAND_CLIENT) << "Could not open temporary file for Shm pool";
118  return false;
119  }
120  if (unlink(tmpFile->fileName().toUtf8().constData()) != 0) {
121  qCDebug(KWAYLAND_CLIENT) << "Unlinking temporary file for Shm pool from file system failed";
122  }
123  if (ftruncate(tmpFile->handle(), size) < 0) {
124  qCDebug(KWAYLAND_CLIENT) << "Could not set size for Shm pool file";
125  return false;
126  }
127  poolData = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile->handle(), 0);
128  pool.setup(wl_shm_create_pool(shm, tmpFile->handle(), size));
129 
130  if (!poolData || !pool) {
131  qCDebug(KWAYLAND_CLIENT) << "Creating Shm pool failed";
132  return false;
133  }
134  return true;
135 }
136 
137 bool ShmPool::Private::resizePool(int32_t newSize)
138 {
139  if (ftruncate(tmpFile->handle(), newSize) < 0) {
140  qCDebug(KWAYLAND_CLIENT) << "Could not set new size for Shm pool file";
141  return false;
142  }
143  wl_shm_pool_resize(pool, newSize);
144  munmap(poolData, size);
145  poolData = mmap(nullptr, newSize, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile->handle(), 0);
146  size = newSize;
147  if (!poolData) {
148  qCDebug(KWAYLAND_CLIENT) << "Resizing Shm pool failed";
149  return false;
150  }
151  emit q->poolResized();
152  return true;
153 }
154 
155 namespace {
156 static Buffer::Format toBufferFormat(const QImage &image)
157 {
158  switch (image.format()) {
160  return Buffer::Format::ARGB32;
162  return Buffer::Format::RGB32;
164  qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << ". expect slow performance. Use QImage::Format_ARGB32_Premultiplied";
165  return Buffer::Format::ARGB32;
166  default:
167  qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << ". expect slow performance.";
168  return Buffer::Format::ARGB32;
169  }
170 }
171 }
172 
173 Buffer::Ptr ShmPool::createBuffer(const QImage& image)
174 {
175  if (image.isNull() || !d->valid) {
176  return QWeakPointer<Buffer>();
177  }
178  auto format = toBufferFormat(image);
179  auto it = d->getBuffer(image.size(), image.bytesPerLine(), format);
180  if (it == d->buffers.end()) {
181  return QWeakPointer<Buffer>();
182  }
183  if (format == Buffer::Format::ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied) {
185  (*it)->copy(imageCopy.bits());
186  } else {
187  (*it)->copy(image.bits());
188  }
189  return QWeakPointer<Buffer>(*it);
190 }
191 
192 Buffer::Ptr ShmPool::createBuffer(const QSize &size, int32_t stride, const void *src, Buffer::Format format)
193 {
194  if (size.isEmpty() || !d->valid) {
195  return QWeakPointer<Buffer>();
196  }
197  auto it = d->getBuffer(size, stride, format);
198  if (it == d->buffers.end()) {
199  return QWeakPointer<Buffer>();
200  }
201  (*it)->copy(src);
202  return QWeakPointer<Buffer>(*it);
203 }
204 
205 namespace {
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(),
248  stride, toWaylandFormat(format));
249  if (!native) {
250  return buffers.end();
251  }
252  if (queue) {
253  queue->addProxy(native);
254  }
255  Buffer *buffer = new Buffer(q, native, s, stride, offset, format);
256  offset += byteCount;
257  auto it = buffers.insert(buffers.end(), QSharedPointer<Buffer>(buffer));
258  return it;
259 }
260 
261 bool ShmPool::isValid() const
262 {
263  return d->valid;
264 }
265 
266 void* ShmPool::poolAddress() const
267 {
268  return d->poolData;
269 }
270 
271 wl_shm *ShmPool::shm()
272 {
273  return d->shm;
274 }
275 
276 }
277 }
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:55
Wrapper class for wl_buffer interface.
Definition: buffer.h:31
bool isNull() const const
QImage copy(const QRect &rectangle) const const
Format
All image formats supported by the implementation.
Definition: buffer.h:37
int bytesPerLine() const const
int height() const const
QSize size() const const
uchar * bits()
QObject * parent() const const
QImage::Format format() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Aug 6 2020 22:49:26 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.