1/*
2 SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
3 SPDX-FileCopyrightText: 2023 David Redondo <kde@david-redondo.de>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "shm.h"
9
10#include <QGuiApplication>
11#include <QImage>
12
13#include <fcntl.h>
14#include <sys/mman.h>
15#include <unistd.h>
16
17#include <cstring>
18
19static constexpr auto version = 1;
20
21ShmBuffer::ShmBuffer(::wl_buffer *buffer)
22 : QtWayland::wl_buffer(buffer)
23{
24}
25
26ShmBuffer::~ShmBuffer()
27{
28 destroy();
29}
30
31Shm::Shm(QObject *parent)
32 : QWaylandClientExtensionTemplate(::version)
33{
34 setParent(parent);
35 connect(sender: this, signal: &QWaylandClientExtension::activeChanged, context: this, slot: [this] {
36 if (!isActive()) {
37 wl_shm_destroy(wl_shm: object());
38 }
39 });
40 initialize();
41}
42
43Shm *Shm::instance()
44{
45 static Shm *instance = new Shm(qGuiApp);
46 return instance;
47}
48
49Shm::~Shm() noexcept
50{
51 if (isActive()) {
52 wl_shm_destroy(wl_shm: object());
53 }
54}
55
56static wl_shm_format toWaylandFormat(QImage::Format format)
57{
58 switch (format) {
59 case QImage::Format_ARGB32_Premultiplied:
60 return WL_SHM_FORMAT_ARGB8888;
61 case QImage::Format_RGB32:
62 return WL_SHM_FORMAT_XRGB8888;
63 case QImage::Format_ARGB32:
64 qCWarning(KWAYLAND_KWS()) << "Unsupported image format: " << format << ". expect slow performance. Use QImage::Format_ARGB32_Premultiplied";
65 return WL_SHM_FORMAT_ARGB8888;
66 default:
67 qCWarning(KWAYLAND_KWS()) << "Unsupported image format: " << format << ". expect slow performance.";
68 return WL_SHM_FORMAT_ARGB8888;
69 }
70}
71
72std::unique_ptr<ShmBuffer> Shm::createBuffer(const QImage &image)
73{
74 if (image.isNull()) {
75 return {};
76 }
77 auto format = toWaylandFormat(format: image.format());
78 const int stride = image.bytesPerLine();
79 const int32_t byteCount = image.size().height() * stride;
80
81#if defined HAVE_MEMFD
82 int fd = memfd_create(name: "kwayland-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
83 if (fd >= 0) {
84 fcntl(fd: fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
85 } else
86#endif
87 {
88 char templateName[] = "/tmp/kwayland-shared-XXXXXX";
89 fd = mkstemp(template: templateName);
90 if (fd >= 0) {
91 unlink(name: templateName);
92
93 int flags = fcntl(fd: fd, F_GETFD);
94 if (flags == -1 || fcntl(fd: fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
95 close(fd: fd);
96 fd = -1;
97 }
98 }
99 }
100
101 if (fd == -1) {
102 qCDebug(KWAYLAND_KWS) << "Could not open temporary file for Shm pool";
103 return {};
104 }
105
106 if (ftruncate(fd: fd, length: byteCount) < 0) {
107 qCDebug(KWAYLAND_KWS) << "Could not set size for Shm pool file";
108 close(fd: fd);
109 return {};
110 }
111 auto data = mmap(addr: nullptr, len: byteCount, PROT_READ | PROT_WRITE, MAP_SHARED, fd: fd, offset: 0);
112
113 if (data == MAP_FAILED) {
114 qCDebug(KWAYLAND_KWS) << "Creating Shm pool failed";
115 close(fd: fd);
116 return {};
117 }
118
119 auto pool = create_pool(fd, size: byteCount);
120 auto *buffer = wl_shm_pool_create_buffer(wl_shm_pool: pool, offset: 0, width: image.size().width(), height: image.size().height(), stride, format);
121 wl_shm_pool_destroy(wl_shm_pool: pool);
122
123 const QImage &srcImage = [format, &image] {
124 if (format == WL_SHM_FORMAT_ARGB8888 && image.format() != QImage::Format_ARGB32_Premultiplied) {
125 return image.convertToFormat(f: QImage::Format_ARGB32_Premultiplied);
126 } else {
127 return image;
128 }
129 }();
130
131 std::memcpy(dest: static_cast<char *>(data), src: srcImage.bits(), n: byteCount);
132
133 munmap(addr: data, len: byteCount);
134 close(fd: fd);
135 return std::make_unique<ShmBuffer>(args&: buffer);
136}
137

source code of kwindowsystem/src/platforms/wayland/shm.cpp