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

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