File indexing completed on 2024-05-19 05:32:19

0001 /*
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "scene/decorationitem.h"
0009 #include "compositor.h"
0010 #include "core/output.h"
0011 #include "decorations/decoratedclient.h"
0012 #include "scene/workspacescene.h"
0013 #include "window.h"
0014 
0015 #include <cmath>
0016 
0017 #include <KDecoration2/DecoratedClient>
0018 #include <KDecoration2/Decoration>
0019 
0020 namespace KWin
0021 {
0022 
0023 DecorationRenderer::DecorationRenderer(Decoration::DecoratedClientImpl *client)
0024     : m_client(client)
0025     , m_imageSizesDirty(true)
0026 {
0027     connect(client->decoration(), &KDecoration2::Decoration::damaged,
0028             this, &DecorationRenderer::addDamage);
0029 
0030     connect(client->decoration(), &KDecoration2::Decoration::bordersChanged,
0031             this, &DecorationRenderer::invalidate);
0032     connect(client->decoratedClient(), &KDecoration2::DecoratedClient::sizeChanged,
0033             this, &DecorationRenderer::invalidate);
0034 
0035     invalidate();
0036 }
0037 
0038 Decoration::DecoratedClientImpl *DecorationRenderer::client() const
0039 {
0040     return m_client;
0041 }
0042 
0043 void DecorationRenderer::invalidate()
0044 {
0045     if (m_client) {
0046         addDamage(m_client->window()->rect().toAlignedRect());
0047     }
0048     m_imageSizesDirty = true;
0049 }
0050 
0051 QRegion DecorationRenderer::damage() const
0052 {
0053     return m_damage;
0054 }
0055 
0056 void DecorationRenderer::addDamage(const QRegion &region)
0057 {
0058     m_damage += region;
0059     Q_EMIT damaged(region);
0060 }
0061 
0062 void DecorationRenderer::resetDamage()
0063 {
0064     m_damage = QRegion();
0065 }
0066 
0067 qreal DecorationRenderer::effectiveDevicePixelRatio() const
0068 {
0069     // QPainter won't let paint with a device pixel ratio less than 1.
0070     return std::max(qreal(1.0), devicePixelRatio());
0071 }
0072 
0073 qreal DecorationRenderer::devicePixelRatio() const
0074 {
0075     return m_devicePixelRatio;
0076 }
0077 
0078 void DecorationRenderer::setDevicePixelRatio(qreal dpr)
0079 {
0080     if (m_devicePixelRatio != dpr) {
0081         m_devicePixelRatio = dpr;
0082         invalidate();
0083     }
0084 }
0085 
0086 void DecorationRenderer::renderToPainter(QPainter *painter, const QRect &rect)
0087 {
0088     client()->decoration()->paint(painter, rect);
0089 }
0090 
0091 DecorationItem::DecorationItem(KDecoration2::Decoration *decoration, Window *window, Scene *scene, Item *parent)
0092     : Item(scene, parent)
0093     , m_window(window)
0094 {
0095     m_renderer = Compositor::self()->scene()->createDecorationRenderer(window->decoratedClient());
0096 
0097     connect(window, &Window::frameGeometryChanged,
0098             this, &DecorationItem::handleFrameGeometryChanged);
0099     connect(window, &Window::outputChanged,
0100             this, &DecorationItem::handleOutputChanged);
0101 
0102     connect(decoration, &KDecoration2::Decoration::bordersChanged,
0103             this, &DecorationItem::discardQuads);
0104 
0105     connect(renderer(), &DecorationRenderer::damaged,
0106             this, qOverload<const QRegion &>(&Item::scheduleRepaint));
0107 
0108     // this toSize is to match that DecoratedWindow also rounds
0109     setSize(window->size().toSize());
0110     handleOutputChanged();
0111 }
0112 
0113 QList<QRectF> DecorationItem::shape() const
0114 {
0115     QRectF left, top, right, bottom;
0116     m_window->layoutDecorationRects(left, top, right, bottom);
0117     return {left, top, right, bottom};
0118 }
0119 
0120 QRegion DecorationItem::opaque() const
0121 {
0122     if (m_window->decorationHasAlpha()) {
0123         return QRegion();
0124     }
0125     QRectF left, top, right, bottom;
0126     m_window->layoutDecorationRects(left, top, right, bottom);
0127     return QRegion(left.toRect()).united(top.toRect()).united(right.toRect()).united(bottom.toRect());
0128 }
0129 
0130 void DecorationItem::preprocess()
0131 {
0132     const QRegion damage = m_renderer->damage();
0133     if (!damage.isEmpty()) {
0134         m_renderer->render(damage);
0135         m_renderer->resetDamage();
0136     }
0137 }
0138 
0139 void DecorationItem::handleOutputChanged()
0140 {
0141     if (m_output) {
0142         disconnect(m_output, &Output::scaleChanged, this, &DecorationItem::handleOutputScaleChanged);
0143     }
0144 
0145     m_output = m_window->output();
0146 
0147     if (m_output) {
0148         handleOutputScaleChanged();
0149         connect(m_output, &Output::scaleChanged, this, &DecorationItem::handleOutputScaleChanged);
0150     }
0151 }
0152 
0153 void DecorationItem::handleOutputScaleChanged()
0154 {
0155     const qreal dpr = m_output->scale();
0156     if (m_renderer->devicePixelRatio() != dpr) {
0157         m_renderer->setDevicePixelRatio(dpr);
0158         discardQuads();
0159     }
0160 }
0161 
0162 void DecorationItem::handleFrameGeometryChanged()
0163 {
0164     setSize(m_window->size().toSize());
0165 }
0166 
0167 DecorationRenderer *DecorationItem::renderer() const
0168 {
0169     return m_renderer.get();
0170 }
0171 
0172 Window *DecorationItem::window() const
0173 {
0174     return m_window;
0175 }
0176 
0177 WindowQuad buildQuad(const QRectF &partRect, const QPoint &textureOffset,
0178                      const qreal devicePixelRatio, bool rotated)
0179 {
0180     const QRectF &r = partRect;
0181     const int p = DecorationRenderer::TexturePad;
0182 
0183     const int x0 = r.x();
0184     const int y0 = r.y();
0185     const int x1 = r.x() + r.width();
0186     const int y1 = r.y() + r.height();
0187 
0188     WindowQuad quad;
0189     if (rotated) {
0190         const int u0 = textureOffset.y() + p;
0191         const int v0 = textureOffset.x() + p;
0192         const int u1 = textureOffset.y() + p + std::round(r.width() * devicePixelRatio);
0193         const int v1 = textureOffset.x() + p + std::round(r.height() * devicePixelRatio);
0194 
0195         quad[0] = WindowVertex(x0, y0, v0, u1); // Top-left
0196         quad[1] = WindowVertex(x1, y0, v0, u0); // Top-right
0197         quad[2] = WindowVertex(x1, y1, v1, u0); // Bottom-right
0198         quad[3] = WindowVertex(x0, y1, v1, u1); // Bottom-left
0199     } else {
0200         const int u0 = textureOffset.x() + p;
0201         const int v0 = textureOffset.y() + p;
0202         const int u1 = textureOffset.x() + p + std::round(r.width() * devicePixelRatio);
0203         const int v1 = textureOffset.y() + p + std::round(r.height() * devicePixelRatio);
0204 
0205         quad[0] = WindowVertex(x0, y0, u0, v0); // Top-left
0206         quad[1] = WindowVertex(x1, y0, u1, v0); // Top-right
0207         quad[2] = WindowVertex(x1, y1, u1, v1); // Bottom-right
0208         quad[3] = WindowVertex(x0, y1, u0, v1); // Bottom-left
0209     }
0210     return quad;
0211 }
0212 
0213 WindowQuadList DecorationItem::buildQuads() const
0214 {
0215     if (m_window->frameMargins().isNull()) {
0216         return WindowQuadList();
0217     }
0218 
0219     QRectF left, top, right, bottom;
0220     const qreal devicePixelRatio = m_renderer->effectiveDevicePixelRatio();
0221     const int texturePad = DecorationRenderer::TexturePad;
0222 
0223     m_window->layoutDecorationRects(left, top, right, bottom);
0224 
0225     const int topHeight = std::round(top.height() * devicePixelRatio);
0226     const int bottomHeight = std::round(bottom.height() * devicePixelRatio);
0227     const int leftWidth = std::round(left.width() * devicePixelRatio);
0228 
0229     const QPoint topPosition(0, 0);
0230     const QPoint bottomPosition(0, topPosition.y() + topHeight + (2 * texturePad));
0231     const QPoint leftPosition(0, bottomPosition.y() + bottomHeight + (2 * texturePad));
0232     const QPoint rightPosition(0, leftPosition.y() + leftWidth + (2 * texturePad));
0233 
0234     WindowQuadList list;
0235     if (left.isValid()) {
0236         list.append(buildQuad(left, leftPosition, devicePixelRatio, true));
0237     }
0238     if (top.isValid()) {
0239         list.append(buildQuad(top, topPosition, devicePixelRatio, false));
0240     }
0241     if (right.isValid()) {
0242         list.append(buildQuad(right, rightPosition, devicePixelRatio, true));
0243     }
0244     if (bottom.isValid()) {
0245         list.append(buildQuad(bottom, bottomPosition, devicePixelRatio, false));
0246     }
0247     return list;
0248 }
0249 
0250 } // namespace KWin
0251 
0252 #include "moc_decorationitem.cpp"