File indexing completed on 2024-05-12 04:00:24
0001 /* 0002 SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org> 0003 SPDX-FileCopyrightText: 2023 David Redondo <kde@david-redondo.de> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "shm.h" 0009 0010 #include <QGuiApplication> 0011 #include <QImage> 0012 0013 #include <fcntl.h> 0014 #include <sys/mman.h> 0015 #include <unistd.h> 0016 0017 #include <cstring> 0018 0019 static constexpr auto version = 1; 0020 0021 ShmBuffer::ShmBuffer(::wl_buffer *buffer) 0022 : QtWayland::wl_buffer(buffer) 0023 { 0024 } 0025 0026 ShmBuffer::~ShmBuffer() 0027 { 0028 destroy(); 0029 } 0030 0031 Shm::Shm(QObject *parent) 0032 : QWaylandClientExtensionTemplate(::version) 0033 { 0034 setParent(parent); 0035 connect(this, &QWaylandClientExtension::activeChanged, this, [this] { 0036 if (!isActive()) { 0037 wl_shm_destroy(object()); 0038 } 0039 }); 0040 initialize(); 0041 } 0042 0043 Shm *Shm::instance() 0044 { 0045 static Shm *instance = new Shm(qGuiApp); 0046 return instance; 0047 } 0048 0049 Shm::~Shm() noexcept 0050 { 0051 if (isActive()) { 0052 wl_shm_destroy(object()); 0053 } 0054 } 0055 0056 static wl_shm_format toWaylandFormat(QImage::Format format) 0057 { 0058 switch (format) { 0059 case QImage::Format_ARGB32_Premultiplied: 0060 return WL_SHM_FORMAT_ARGB8888; 0061 case QImage::Format_RGB32: 0062 return WL_SHM_FORMAT_XRGB8888; 0063 case QImage::Format_ARGB32: 0064 qCWarning(KWAYLAND_KWS()) << "Unsupported image format: " << format << ". expect slow performance. Use QImage::Format_ARGB32_Premultiplied"; 0065 return WL_SHM_FORMAT_ARGB8888; 0066 default: 0067 qCWarning(KWAYLAND_KWS()) << "Unsupported image format: " << format << ". expect slow performance."; 0068 return WL_SHM_FORMAT_ARGB8888; 0069 } 0070 } 0071 0072 std::unique_ptr<ShmBuffer> Shm::createBuffer(const QImage &image) 0073 { 0074 if (image.isNull()) { 0075 return {}; 0076 } 0077 auto format = toWaylandFormat(image.format()); 0078 const int stride = image.bytesPerLine(); 0079 const int32_t byteCount = image.size().height() * stride; 0080 0081 #if defined HAVE_MEMFD 0082 int fd = memfd_create("kwayland-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING); 0083 if (fd >= 0) { 0084 fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); 0085 } else 0086 #endif 0087 { 0088 char templateName[] = "/tmp/kwayland-shared-XXXXXX"; 0089 fd = mkstemp(templateName); 0090 if (fd >= 0) { 0091 unlink(templateName); 0092 0093 int flags = fcntl(fd, F_GETFD); 0094 if (flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { 0095 close(fd); 0096 fd = -1; 0097 } 0098 } 0099 } 0100 0101 if (fd == -1) { 0102 qCDebug(KWAYLAND_KWS) << "Could not open temporary file for Shm pool"; 0103 return {}; 0104 } 0105 0106 if (ftruncate(fd, byteCount) < 0) { 0107 qCDebug(KWAYLAND_KWS) << "Could not set size for Shm pool file"; 0108 close(fd); 0109 return {}; 0110 } 0111 auto data = mmap(nullptr, byteCount, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 0112 0113 if (data == MAP_FAILED) { 0114 qCDebug(KWAYLAND_KWS) << "Creating Shm pool failed"; 0115 close(fd); 0116 return {}; 0117 } 0118 0119 auto pool = create_pool(fd, byteCount); 0120 auto *buffer = wl_shm_pool_create_buffer(pool, 0, image.size().width(), image.size().height(), stride, format); 0121 wl_shm_pool_destroy(pool); 0122 0123 const QImage &srcImage = [format, &image] { 0124 if (format == WL_SHM_FORMAT_ARGB8888 && image.format() != QImage::Format_ARGB32_Premultiplied) { 0125 return image.convertToFormat(QImage::Format_ARGB32_Premultiplied); 0126 } else { 0127 return image; 0128 } 0129 }(); 0130 0131 std::memcpy(static_cast<char *>(data), srcImage.bits(), byteCount); 0132 0133 munmap(data, byteCount); 0134 close(fd); 0135 return std::make_unique<ShmBuffer>(buffer); 0136 }