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