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 }