| 1 | /* |
| 2 | SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> |
| 3 | |
| 4 | SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
| 5 | */ |
| 6 | #ifndef WAYLAND_SHM_POOL_H |
| 7 | #define WAYLAND_SHM_POOL_H |
| 8 | |
| 9 | #include <QObject> |
| 10 | |
| 11 | #include "KWayland/Client/kwaylandclient_export.h" |
| 12 | #include "buffer.h" |
| 13 | |
| 14 | class QImage; |
| 15 | class QSize; |
| 16 | |
| 17 | struct wl_shm; |
| 18 | |
| 19 | namespace KWayland |
| 20 | { |
| 21 | namespace Client |
| 22 | { |
| 23 | class EventQueue; |
| 24 | |
| 25 | /** |
| 26 | * @short Wrapper class for wl_shm interface. |
| 27 | * |
| 28 | * This class holds a shared memory pool together with the Wayland server. |
| 29 | * |
| 30 | * To use this class one needs to interact with the Registry. There are two |
| 31 | * possible ways to create a ShmPool instance: |
| 32 | * @code |
| 33 | * ShmPool *s = registry->createShmPool(name, version); |
| 34 | * @endcode |
| 35 | * |
| 36 | * This creates the ShmPool and sets it up directly. As an alternative this |
| 37 | * can also be done in a more low level way: |
| 38 | * @code |
| 39 | * ShmPool *s = new ShmPool; |
| 40 | * s->setup(registry->bindShm(name, version)); |
| 41 | * @endcode |
| 42 | * |
| 43 | * The ShmPool holds a memory-mapped file from which it provides Buffers. |
| 44 | * All Buffers are held by the ShmPool and can be reused. Whenever a Buffer |
| 45 | * is requested the ShmPool tries to reuse an existing Buffer. A Buffer can |
| 46 | * be reused if the following conditions hold |
| 47 | * @li it's no longer marked as used |
| 48 | * @li the server released the buffer |
| 49 | * @li the size matches |
| 50 | * @li the stride matches |
| 51 | * @li the format matches |
| 52 | * |
| 53 | * The ownership of a Buffer stays with ShmPool. The ShmPool might destroy the |
| 54 | * Buffer at any given time. Because of that ShmPool only provides QWeakPointer |
| 55 | * for Buffers. Users should always check whether the pointer is still valid and |
| 56 | * only promote to a QSharedPointer for a short time, e.g. to set new data. |
| 57 | * |
| 58 | * The ShmPool can provide Buffers for different purposes. One can create a Buffer |
| 59 | * from an existing QImage. This will use a Buffer with same size, stride and image |
| 60 | * format as the QImage and <b>copy</b> the content of the QImage into the Buffer. |
| 61 | * The memory is <b>not</b> shared: |
| 62 | * @code |
| 63 | * QImage image(24, 24, QImage::Format_ARG32); |
| 64 | * image.fill(Qt::transparent); |
| 65 | * Buffer::Ptr buffer = s->createBuffer(image); |
| 66 | * @endcode |
| 67 | * |
| 68 | * It is also possible to create a Buffer and copy the content from a generic location. |
| 69 | * Like above this doesn't share the content but copies it: |
| 70 | * @code |
| 71 | * QImage image(24, 24, QImage::Format_ARG32); |
| 72 | * image.fill(Qt::transparent); |
| 73 | * Buffer::Ptr buffer = s->createBuffer(image.size(), image.bytesPerLine(), image.constBits()); |
| 74 | * @endcode |
| 75 | * |
| 76 | * Last but not least it is possible to get a Buffer without copying content directly to it. |
| 77 | * This means an empty area is just reserved and can be used to e.g. share the memory with a |
| 78 | * QImage: |
| 79 | * @code |
| 80 | * const QSize size = QSize(24, 24); |
| 81 | * const int stride = size.width() * 4; |
| 82 | * Buffer::Ptr buffer = s->getBuffer(size, stride, Buffer::Format::RGB32); |
| 83 | * if (!buffer) { |
| 84 | * qDebug() << "Didn't get a valid Buffer"; |
| 85 | * return; |
| 86 | * } |
| 87 | * QImage image(buffer.toStrongRef()->address(), size.width(), size.height(), stride, QImage::Format_RGB32); |
| 88 | * image.fill(Qt::black); |
| 89 | * @endcode |
| 90 | * |
| 91 | * A Buffer can be attached to a Surface: |
| 92 | * @code |
| 93 | * Compositor *c = registry.createCompositor(name, version); |
| 94 | * Surface *s = c->createSurface(); |
| 95 | * s->attachBuffer(buffer); |
| 96 | * s->damage(QRect(QPoint(0, 0), size)); |
| 97 | * @endcode |
| 98 | * |
| 99 | * Once a Buffer is attached to a Surface and the Surface is committed, it might be released |
| 100 | * by the Wayland server and thus is free to be reused again. If the client code wants to |
| 101 | * continue using the Buffer it must call Buffer::setUsed on it. This is important if the memory |
| 102 | * is shared for example with a QImage as the memory buffer for a QImage must remain valid |
| 103 | * throughout the life time of the QImage: |
| 104 | * @code |
| 105 | * buffer.toStrongRef()->setUsed(true); |
| 106 | * @endcode |
| 107 | * |
| 108 | * This is also important for the case that the shared memory pool needs to be resized. |
| 109 | * The ShmPool will automatically resize if it cannot provide a new Buffer. During the resize |
| 110 | * all existing Buffers are unmapped and any shared objects must be recreated. The ShmPool emits |
| 111 | * the signal poolResized() after the pool got resized. |
| 112 | * |
| 113 | * @see Buffer |
| 114 | **/ |
| 115 | class KWAYLANDCLIENT_EXPORT ShmPool : public QObject |
| 116 | { |
| 117 | Q_OBJECT |
| 118 | public: |
| 119 | explicit ShmPool(QObject *parent = nullptr); |
| 120 | ~ShmPool() override; |
| 121 | /** |
| 122 | * @returns @c true if the ShmPool references a wl_shm interface and the shared memory pool |
| 123 | * is setup. |
| 124 | **/ |
| 125 | bool isValid() const; |
| 126 | /** |
| 127 | * Setup this ShmPool to manage the @p shm. |
| 128 | * This also creates the shared memory pool. |
| 129 | * When using Registry::createShmPool there is no need to call this |
| 130 | * method. |
| 131 | **/ |
| 132 | void setup(wl_shm *shm); |
| 133 | /** |
| 134 | * Releases the wl_shm interface. |
| 135 | * After the interface has been released the ShmPool instance is no |
| 136 | * longer valid and can be setup with another wl_shm interface. |
| 137 | * |
| 138 | * This also destroys the shared memory pool and all Buffers are destroyed. |
| 139 | **/ |
| 140 | void release(); |
| 141 | /** |
| 142 | * Destroys the data held by this ShmPool. |
| 143 | * This method is supposed to be used when the connection to the Wayland |
| 144 | * server goes away. If the connection is not valid anymore, it's not |
| 145 | * possible to call release anymore as that calls into the Wayland |
| 146 | * connection and the call would fail. This method cleans up the data, so |
| 147 | * that the instance can be deleted or set up to a new wl_shm interface |
| 148 | * once there is a new connection available. |
| 149 | * |
| 150 | * All Buffers are destroyed! |
| 151 | * |
| 152 | * This method is automatically invoked when the Registry which created this |
| 153 | * ShmPool gets destroyed. |
| 154 | * |
| 155 | * @see release |
| 156 | **/ |
| 157 | void destroy(); |
| 158 | |
| 159 | /** |
| 160 | * Sets the @p queue to use for creating a Buffer. |
| 161 | **/ |
| 162 | void setEventQueue(EventQueue *queue); |
| 163 | /** |
| 164 | * @returns The event queue to use for creating a Buffer. |
| 165 | **/ |
| 166 | EventQueue *eventQueue(); |
| 167 | |
| 168 | /** |
| 169 | * Provides a Buffer with: |
| 170 | * @li same size as @p image |
| 171 | * @li same stride as @p image |
| 172 | * @li same format as @p image |
| 173 | * |
| 174 | * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned. |
| 175 | * The content of the @p image is <b>copied</b> into the buffer. The @p image and |
| 176 | * returned Buffer do <b>not</b> share memory. |
| 177 | * |
| 178 | * @param image The image which should be copied into the Buffer |
| 179 | * @return Buffer with copied content of @p image in success case, a @c null Buffer::Ptr otherwise |
| 180 | * @see getBuffer |
| 181 | **/ |
| 182 | Buffer::Ptr createBuffer(const QImage &image); |
| 183 | /** |
| 184 | * Provides a Buffer with @p size, @p stride and @p format. |
| 185 | * |
| 186 | * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned. |
| 187 | * A memory copy is performed from @p src into the Buffer. The Buffer does <b>not</b> share |
| 188 | * memory with @p src. |
| 189 | * |
| 190 | * @param size The requested size for the Buffer |
| 191 | * @param stride The requested stride for the Buffer |
| 192 | * @param src The source memory location to copy from |
| 193 | * @param format The requested format for the Buffer |
| 194 | * @return Buffer with copied content of @p src in success case, a @c null Buffer::Ptr otherwise |
| 195 | * @see getBuffer |
| 196 | **/ |
| 197 | Buffer::Ptr createBuffer(const QSize &size, int32_t stride, const void *src, Buffer::Format format = Buffer::Format::ARGB32); |
| 198 | void *poolAddress() const; |
| 199 | /** |
| 200 | * Provides a Buffer with @p size, @p stride and @p format. |
| 201 | * |
| 202 | * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned. |
| 203 | * Unlike with createBuffer there is no memory copy performed. This provides a bare Buffer |
| 204 | * to be used by the user. |
| 205 | * |
| 206 | * @param size The requested size for the Buffer |
| 207 | * @param stride The requested stride for the Buffer |
| 208 | * @param format The requested format for the Buffer |
| 209 | * @return Buffer as requested in success case, a @c null Buffer::Ptr otherwise. |
| 210 | * @see createBuffer |
| 211 | **/ |
| 212 | Buffer::Ptr getBuffer(const QSize &size, int32_t stride, Buffer::Format format = Buffer::Format::ARGB32); |
| 213 | wl_shm *shm(); |
| 214 | Q_SIGNALS: |
| 215 | /** |
| 216 | * This signal is emitted whenever the shared memory pool gets resized. |
| 217 | * Any used Buffer must be remapped. |
| 218 | **/ |
| 219 | void poolResized(); |
| 220 | |
| 221 | /** |
| 222 | * The corresponding global for this interface on the Registry got removed. |
| 223 | * |
| 224 | * This signal gets only emitted if the Compositor got created by |
| 225 | * Registry::createShmPool |
| 226 | * |
| 227 | * @since 5.5 |
| 228 | **/ |
| 229 | void removed(); |
| 230 | |
| 231 | private: |
| 232 | class Private; |
| 233 | QScopedPointer<Private> d; |
| 234 | }; |
| 235 | |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | #endif |
| 240 | |