File indexing completed on 2024-05-19 16:35:27

0001 /*
0002     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "shmclientbuffer.h"
0008 #include "clientbuffer_p.h"
0009 #include "display.h"
0010 
0011 #include <wayland-server-core.h>
0012 #include <wayland-server-protocol.h>
0013 
0014 namespace KWaylandServer
0015 {
0016 static const ShmClientBuffer *s_accessedBuffer = nullptr;
0017 static int s_accessCounter = 0;
0018 
0019 class ShmClientBufferPrivate : public ClientBufferPrivate
0020 {
0021 public:
0022     ShmClientBufferPrivate(ShmClientBuffer *q);
0023 
0024     static void buffer_destroy_callback(wl_listener *listener, void *data);
0025 
0026     ShmClientBuffer *q;
0027     QImage::Format format = QImage::Format_Invalid;
0028     uint32_t width = 0;
0029     uint32_t height = 0;
0030     bool hasAlphaChannel = false;
0031     QImage savedData;
0032 
0033     struct DestroyListener
0034     {
0035         wl_listener listener;
0036         ShmClientBufferPrivate *receiver;
0037     };
0038     DestroyListener destroyListener;
0039 };
0040 
0041 ShmClientBufferPrivate::ShmClientBufferPrivate(ShmClientBuffer *q)
0042     : q(q)
0043 {
0044 }
0045 
0046 static void cleanupShmPool(void *poolHandle)
0047 {
0048     wl_shm_pool_unref(static_cast<wl_shm_pool *>(poolHandle));
0049 }
0050 
0051 void ShmClientBufferPrivate::buffer_destroy_callback(wl_listener *listener, void *data)
0052 {
0053     auto bufferPrivate = reinterpret_cast<ShmClientBufferPrivate::DestroyListener *>(listener)->receiver;
0054     wl_shm_buffer *buffer = wl_shm_buffer_get(bufferPrivate->q->resource());
0055     wl_shm_pool *pool = wl_shm_buffer_ref_pool(buffer);
0056 
0057     wl_list_remove(&bufferPrivate->destroyListener.listener.link);
0058     wl_list_init(&bufferPrivate->destroyListener.listener.link);
0059 
0060     bufferPrivate->savedData = QImage(static_cast<const uchar *>(wl_shm_buffer_get_data(buffer)),
0061                                       bufferPrivate->width,
0062                                       bufferPrivate->height,
0063                                       wl_shm_buffer_get_stride(buffer),
0064                                       bufferPrivate->format,
0065                                       cleanupShmPool,
0066                                       pool);
0067 }
0068 
0069 static bool alphaChannelFromFormat(uint32_t format)
0070 {
0071     switch (format) {
0072     case WL_SHM_FORMAT_ABGR16161616:
0073     case WL_SHM_FORMAT_ABGR2101010:
0074     case WL_SHM_FORMAT_ARGB2101010:
0075     case WL_SHM_FORMAT_ARGB8888:
0076         return true;
0077     case WL_SHM_FORMAT_XBGR16161616:
0078     case WL_SHM_FORMAT_XBGR2101010:
0079     case WL_SHM_FORMAT_XRGB2101010:
0080     case WL_SHM_FORMAT_XRGB8888:
0081     default:
0082         return false;
0083     }
0084 }
0085 
0086 static QImage::Format imageFormatForShmFormat(uint32_t format)
0087 {
0088     switch (format) {
0089 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
0090     case WL_SHM_FORMAT_ABGR16161616:
0091         return QImage::Format_RGBA64_Premultiplied;
0092     case WL_SHM_FORMAT_XBGR16161616:
0093         return QImage::Format_RGBX64;
0094     case WL_SHM_FORMAT_ARGB2101010:
0095         return QImage::Format_A2RGB30_Premultiplied;
0096     case WL_SHM_FORMAT_XRGB2101010:
0097         return QImage::Format_RGB30;
0098     case WL_SHM_FORMAT_ABGR2101010:
0099         return QImage::Format_A2BGR30_Premultiplied;
0100     case WL_SHM_FORMAT_XBGR2101010:
0101         return QImage::Format_BGR30;
0102 #endif
0103     case WL_SHM_FORMAT_ARGB8888:
0104         return QImage::Format_ARGB32_Premultiplied;
0105     case WL_SHM_FORMAT_XRGB8888:
0106         return QImage::Format_RGB32;
0107     default:
0108         return QImage::Format_Invalid;
0109     }
0110 }
0111 
0112 ShmClientBuffer::ShmClientBuffer(wl_resource *resource)
0113     : ClientBuffer(resource, *new ShmClientBufferPrivate(this))
0114 {
0115     Q_D(ShmClientBuffer);
0116 
0117     wl_shm_buffer *buffer = wl_shm_buffer_get(resource);
0118     d->width = wl_shm_buffer_get_width(buffer);
0119     d->height = wl_shm_buffer_get_height(buffer);
0120     d->hasAlphaChannel = alphaChannelFromFormat(wl_shm_buffer_get_format(buffer));
0121     d->format = imageFormatForShmFormat(wl_shm_buffer_get_format(buffer));
0122 
0123     // The underlying shm pool will be referenced if the wl_shm_buffer is destroyed so the
0124     // compositor can access buffer data even after the buffer is gone.
0125     d->destroyListener.receiver = d;
0126     d->destroyListener.listener.notify = ShmClientBufferPrivate::buffer_destroy_callback;
0127     wl_resource_add_destroy_listener(resource, &d->destroyListener.listener);
0128 }
0129 
0130 QSize ShmClientBuffer::size() const
0131 {
0132     Q_D(const ShmClientBuffer);
0133     return QSize(d->width, d->height);
0134 }
0135 
0136 bool ShmClientBuffer::hasAlphaChannel() const
0137 {
0138     Q_D(const ShmClientBuffer);
0139     return d->hasAlphaChannel;
0140 }
0141 
0142 ClientBuffer::Origin ShmClientBuffer::origin() const
0143 {
0144     return Origin::TopLeft;
0145 }
0146 
0147 static void cleanupShmData(void *bufferHandle)
0148 {
0149     Q_ASSERT_X(s_accessCounter > 0, "cleanup", "access counter must be positive");
0150     s_accessCounter--;
0151     if (s_accessCounter == 0) {
0152         s_accessedBuffer = nullptr;
0153     }
0154     wl_shm_buffer_end_access(static_cast<wl_shm_buffer *>(bufferHandle));
0155 }
0156 
0157 QImage ShmClientBuffer::data() const
0158 {
0159     if (s_accessedBuffer && s_accessedBuffer != this) {
0160         return QImage();
0161     }
0162 
0163     Q_D(const ShmClientBuffer);
0164     if (wl_shm_buffer *buffer = wl_shm_buffer_get(resource())) {
0165         s_accessedBuffer = this;
0166         s_accessCounter++;
0167         wl_shm_buffer_begin_access(buffer);
0168         const uchar *data = static_cast<const uchar *>(wl_shm_buffer_get_data(buffer));
0169         const uint32_t stride = wl_shm_buffer_get_stride(buffer);
0170         return QImage(data, d->width, d->height, stride, d->format, cleanupShmData, buffer);
0171     }
0172     return d->savedData;
0173 }
0174 
0175 ShmClientBufferIntegration::ShmClientBufferIntegration(Display *display)
0176     : ClientBufferIntegration(display)
0177 {
0178 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
0179     wl_display_add_shm_format(*display, WL_SHM_FORMAT_ARGB2101010);
0180     wl_display_add_shm_format(*display, WL_SHM_FORMAT_XRGB2101010);
0181     wl_display_add_shm_format(*display, WL_SHM_FORMAT_ABGR2101010);
0182     wl_display_add_shm_format(*display, WL_SHM_FORMAT_XBGR2101010);
0183     wl_display_add_shm_format(*display, WL_SHM_FORMAT_ABGR16161616);
0184     wl_display_add_shm_format(*display, WL_SHM_FORMAT_XBGR16161616);
0185 #endif
0186     wl_display_init_shm(*display);
0187 }
0188 
0189 ClientBuffer *ShmClientBufferIntegration::createBuffer(::wl_resource *resource)
0190 {
0191     if (wl_shm_buffer_get(resource)) {
0192         return new ShmClientBuffer(resource);
0193     }
0194     return nullptr;
0195 }
0196 
0197 } // namespace KWaylandServer