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 }