File indexing completed on 2026-06-14 15:32:08

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
0006     SPDX-FileCopyrightText: 2013, 2015 Martin Gräßlin <mgraesslin@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 #include "wayland_qpainter_backend.h"
0011 #include "wayland_backend.h"
0012 #include "wayland_display.h"
0013 #include "wayland_logging.h"
0014 #include "wayland_output.h"
0015 
0016 #include <KWayland/Client/buffer.h>
0017 #include <KWayland/Client/shm_pool.h>
0018 #include <KWayland/Client/surface.h>
0019 
0020 #include <cmath>
0021 
0022 namespace KWin
0023 {
0024 namespace Wayland
0025 {
0026 
0027 WaylandQPainterBufferSlot::WaylandQPainterBufferSlot(QSharedPointer<KWayland::Client::Buffer> buffer)
0028     : buffer(buffer)
0029     , image(buffer->address(), buffer->size().width(), buffer->size().height(), QImage::Format_RGB32)
0030 {
0031     buffer->setUsed(true);
0032 }
0033 
0034 WaylandQPainterBufferSlot::~WaylandQPainterBufferSlot()
0035 {
0036     buffer->setUsed(false);
0037 }
0038 
0039 WaylandQPainterPrimaryLayer::WaylandQPainterPrimaryLayer(WaylandOutput *output)
0040     : m_waylandOutput(output)
0041     , m_pool(output->backend()->display()->shmPool())
0042 {
0043     connect(m_pool, &KWayland::Client::ShmPool::poolResized, this, &WaylandQPainterPrimaryLayer::remapBuffer);
0044 }
0045 
0046 WaylandQPainterPrimaryLayer::~WaylandQPainterPrimaryLayer()
0047 {
0048     m_slots.clear();
0049 }
0050 
0051 void WaylandQPainterPrimaryLayer::remapBuffer()
0052 {
0053     qCDebug(KWIN_WAYLAND_BACKEND) << "Remapped back buffer of surface" << m_waylandOutput->surface();
0054 
0055     const QSize nativeSize(m_waylandOutput->geometry().size() * m_waylandOutput->scale());
0056     for (const auto &slot : m_slots) {
0057         slot->image = QImage(slot->buffer->address(), nativeSize.width(), nativeSize.height(), QImage::Format_RGB32);
0058     }
0059 }
0060 
0061 void WaylandQPainterPrimaryLayer::present()
0062 {
0063     for (const auto &slot : m_slots) {
0064         if (slot.get() == m_back) {
0065             slot->age = 1;
0066         } else if (slot->age > 0) {
0067             slot->age++;
0068         }
0069     }
0070 
0071     auto s = m_waylandOutput->surface();
0072     s->attachBuffer(m_back->buffer);
0073     s->damage(m_damageJournal.lastDamage());
0074     s->setScale(std::ceil(m_waylandOutput->scale()));
0075     s->commit();
0076 }
0077 
0078 WaylandQPainterBufferSlot *WaylandQPainterPrimaryLayer::back() const
0079 {
0080     return m_back;
0081 }
0082 
0083 WaylandQPainterBufferSlot *WaylandQPainterPrimaryLayer::acquire()
0084 {
0085     const QSize nativeSize(m_waylandOutput->pixelSize());
0086     if (m_swapchainSize != nativeSize) {
0087         m_swapchainSize = nativeSize;
0088         m_slots.clear();
0089     }
0090 
0091     for (const auto &slot : m_slots) {
0092         if (slot->buffer->isReleased()) {
0093             m_back = slot.get();
0094             slot->buffer->setReleased(false);
0095             return m_back;
0096         }
0097     }
0098 
0099     auto buffer = m_pool->getBuffer(nativeSize, nativeSize.width() * 4, KWayland::Client::Buffer::Format::RGB32).toStrongRef();
0100     if (!buffer) {
0101         qCDebug(KWIN_WAYLAND_BACKEND) << "Did not get a new Buffer from Shm Pool";
0102         return nullptr;
0103     }
0104 
0105     m_slots.push_back(std::make_unique<WaylandQPainterBufferSlot>(buffer));
0106     m_back = m_slots.back().get();
0107 
0108     //    qCDebug(KWIN_WAYLAND_BACKEND) << "Created a new back buffer for output surface" << m_waylandOutput->surface();
0109     return m_back;
0110 }
0111 
0112 QRegion WaylandQPainterPrimaryLayer::accumulateDamage(int bufferAge) const
0113 {
0114     return m_damageJournal.accumulate(bufferAge, infiniteRegion());
0115 }
0116 
0117 std::optional<OutputLayerBeginFrameInfo> WaylandQPainterPrimaryLayer::beginFrame()
0118 {
0119     WaylandQPainterBufferSlot *slot = acquire();
0120     return OutputLayerBeginFrameInfo{
0121         .renderTarget = RenderTarget(&slot->image),
0122         .repaint = accumulateDamage(slot->age),
0123     };
0124 }
0125 
0126 bool WaylandQPainterPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
0127 {
0128     m_damageJournal.add(damagedRegion);
0129     return true;
0130 }
0131 
0132 WaylandQPainterCursorLayer::WaylandQPainterCursorLayer(WaylandOutput *output)
0133     : m_output(output)
0134 {
0135 }
0136 
0137 WaylandQPainterCursorLayer::~WaylandQPainterCursorLayer()
0138 {
0139 }
0140 
0141 qreal WaylandQPainterCursorLayer::scale() const
0142 {
0143     return m_scale;
0144 }
0145 
0146 void WaylandQPainterCursorLayer::setScale(qreal scale)
0147 {
0148     m_scale = scale;
0149 }
0150 
0151 QPoint WaylandQPainterCursorLayer::hotspot() const
0152 {
0153     return m_hotspot;
0154 }
0155 
0156 void WaylandQPainterCursorLayer::setHotspot(const QPoint &hotspot)
0157 {
0158     m_hotspot = hotspot;
0159 }
0160 
0161 QSize WaylandQPainterCursorLayer::size() const
0162 {
0163     return m_size;
0164 }
0165 
0166 void WaylandQPainterCursorLayer::setSize(const QSize &size)
0167 {
0168     m_size = size;
0169 }
0170 
0171 std::optional<OutputLayerBeginFrameInfo> WaylandQPainterCursorLayer::beginFrame()
0172 {
0173     const QSize bufferSize = m_size.expandedTo(QSize(64, 64));
0174     if (m_backingStore.size() != bufferSize) {
0175         m_backingStore = QImage(bufferSize, QImage::Format_ARGB32_Premultiplied);
0176     }
0177 
0178     return OutputLayerBeginFrameInfo{
0179         .renderTarget = RenderTarget(&m_backingStore),
0180         .repaint = infiniteRegion(),
0181     };
0182 }
0183 
0184 bool WaylandQPainterCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
0185 {
0186     KWayland::Client::Buffer::Ptr buffer = m_output->backend()->display()->shmPool()->createBuffer(m_backingStore);
0187     m_output->cursor()->update(*buffer.lock(), m_scale, m_hotspot);
0188     return true;
0189 }
0190 
0191 WaylandQPainterBackend::WaylandQPainterBackend(Wayland::WaylandBackend *b)
0192     : QPainterBackend()
0193     , m_backend(b)
0194 {
0195 
0196     const auto waylandOutputs = m_backend->waylandOutputs();
0197     for (auto *output : waylandOutputs) {
0198         createOutput(output);
0199     }
0200     connect(m_backend, &WaylandBackend::outputAdded, this, &WaylandQPainterBackend::createOutput);
0201     connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *waylandOutput) {
0202         m_outputs.erase(waylandOutput);
0203     });
0204 }
0205 
0206 WaylandQPainterBackend::~WaylandQPainterBackend()
0207 {
0208 }
0209 
0210 void WaylandQPainterBackend::createOutput(Output *waylandOutput)
0211 {
0212     m_outputs[waylandOutput] = Layers{
0213         .primaryLayer = std::make_unique<WaylandQPainterPrimaryLayer>(static_cast<WaylandOutput *>(waylandOutput)),
0214         .cursorLayer = std::make_unique<WaylandQPainterCursorLayer>(static_cast<WaylandOutput *>(waylandOutput)),
0215     };
0216 }
0217 
0218 void WaylandQPainterBackend::present(Output *output)
0219 {
0220     m_outputs[output].primaryLayer->present();
0221 }
0222 
0223 OutputLayer *WaylandQPainterBackend::primaryLayer(Output *output)
0224 {
0225     return m_outputs[output].primaryLayer.get();
0226 }
0227 
0228 WaylandQPainterCursorLayer *WaylandQPainterBackend::cursorLayer(Output *output)
0229 {
0230     return m_outputs[output].cursorLayer.get();
0231 }
0232 
0233 }
0234 }