/* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-FileCopyrightText: 2021 Vlad Zahorodnii SPDX-License-Identifier: GPL-2.0-or-later */ #include "decorationitem.h" #include "abstract_client.h" #include "abstract_output.h" #include "composite.h" #include "decorations/decoratedclient.h" #include "deleted.h" #include "scene.h" #include "utils.h" #include #include #include namespace KWin { DecorationRenderer::DecorationRenderer(Decoration::DecoratedClientImpl *client) : m_client(client) , m_imageSizesDirty(true) { connect(client->decoration(), &KDecoration2::Decoration::damaged, this, &DecorationRenderer::addDamage); connect(client->decoration(), &KDecoration2::Decoration::bordersChanged, this, &DecorationRenderer::invalidate); connect(client->decoratedClient(), &KDecoration2::DecoratedClient::sizeChanged, this, &DecorationRenderer::invalidate); invalidate(); } Decoration::DecoratedClientImpl *DecorationRenderer::client() const { return m_client; } void DecorationRenderer::invalidate() { if (m_client) { addDamage(m_client->client()->rect()); } m_imageSizesDirty = true; } QRegion DecorationRenderer::damage() const { return m_damage; } void DecorationRenderer::addDamage(const QRegion ®ion) { m_damage += region; Q_EMIT damaged(region); } void DecorationRenderer::resetDamage() { m_damage = QRegion(); } qreal DecorationRenderer::devicePixelRatio() const { return m_devicePixelRatio; } void DecorationRenderer::setDevicePixelRatio(qreal dpr) { if (m_devicePixelRatio != dpr) { m_devicePixelRatio = dpr; invalidate(); } } QImage DecorationRenderer::renderToImage(const QRect &geo) { Q_ASSERT(m_client); // Guess the pixel format of the X pixmap into which the QImage will be copied. QImage::Format format; const int depth = client()->client()->depth(); switch (depth) { case 30: format = QImage::Format_A2RGB30_Premultiplied; break; case 24: case 32: format = QImage::Format_ARGB32_Premultiplied; break; default: qCCritical(KWIN_CORE) << "Unsupported client depth" << depth; format = QImage::Format_ARGB32_Premultiplied; break; }; QImage image(geo.width() * m_devicePixelRatio, geo.height() * m_devicePixelRatio, format); image.setDevicePixelRatio(m_devicePixelRatio); image.fill(Qt::transparent); QPainter p(&image); p.setRenderHint(QPainter::Antialiasing); p.setWindow(QRect(geo.topLeft(), geo.size() * qPainterEffectiveDevicePixelRatio(&p))); p.setClipRect(geo); renderToPainter(&p, geo); return image; } void DecorationRenderer::renderToPainter(QPainter *painter, const QRect &rect) { client()->decoration()->paint(painter, rect); } DecorationItem::DecorationItem(KDecoration2::Decoration *decoration, AbstractClient *window, Item *parent) : Item(parent) , m_window(window) { m_renderer.reset(Compositor::self()->scene()->createDecorationRenderer(window->decoratedClient())); connect(window, &Toplevel::frameGeometryChanged, this, &DecorationItem::handleFrameGeometryChanged); connect(window, &Toplevel::windowClosed, this, &DecorationItem::handleWindowClosed); connect(window, &Toplevel::screenChanged, this, &DecorationItem::handleOutputChanged); connect(decoration, &KDecoration2::Decoration::bordersChanged, this, &DecorationItem::discardQuads); connect(renderer(), &DecorationRenderer::damaged, this, &DecorationItem::scheduleRepaint); setSize(window->size()); handleOutputChanged(); } void DecorationItem::preprocess() { const QRegion damage = m_renderer->damage(); if (!damage.isEmpty()) { m_renderer->render(damage); m_renderer->resetDamage(); } } void DecorationItem::handleOutputChanged() { if (m_output) { disconnect(m_output, &AbstractOutput::scaleChanged, this, &DecorationItem::handleOutputScaleChanged); } m_output = m_window->output(); if (m_output) { handleOutputScaleChanged(); connect(m_output, &AbstractOutput::scaleChanged, this, &DecorationItem::handleOutputScaleChanged); } } void DecorationItem::handleOutputScaleChanged() { const qreal dpr = m_output->scale(); if (m_renderer->devicePixelRatio() != dpr) { m_renderer->setDevicePixelRatio(dpr); discardQuads(); } } void DecorationItem::handleFrameGeometryChanged() { setSize(m_window->size()); } void DecorationItem::handleWindowClosed(Toplevel *original, Deleted *deleted) { Q_UNUSED(original) m_window = deleted; // If the decoration is about to be destroyed, render the decoration for the last time. preprocess(); } DecorationRenderer *DecorationItem::renderer() const { return m_renderer.data(); } WindowQuadList DecorationItem::buildQuads() const { if (m_window->frameMargins().isNull()) { return WindowQuadList(); } QRect rects[4]; if (const AbstractClient *client = qobject_cast(m_window)) { client->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3]); } else if (const Deleted *deleted = qobject_cast(m_window)) { deleted->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3]); } const qreal textureScale = m_renderer->devicePixelRatio(); const int padding = 1; const QPoint topSpritePosition(padding, padding); const QPoint bottomSpritePosition(padding, topSpritePosition.y() + rects[1].height() + 2 * padding); const QPoint leftSpritePosition(bottomSpritePosition.y() + rects[3].height() + 2 * padding, padding); const QPoint rightSpritePosition(leftSpritePosition.x() + rects[0].width() + 2 * padding, padding); const QPoint offsets[4] = { QPoint(-rects[0].x(), -rects[0].y()) + leftSpritePosition, QPoint(-rects[1].x(), -rects[1].y()) + topSpritePosition, QPoint(-rects[2].x(), -rects[2].y()) + rightSpritePosition, QPoint(-rects[3].x(), -rects[3].y()) + bottomSpritePosition, }; const Qt::Orientation orientations[4] = { Qt::Vertical, // Left Qt::Horizontal, // Top Qt::Vertical, // Right Qt::Horizontal, // Bottom }; WindowQuadList list; list.reserve(4); for (int i = 0; i < 4; ++i) { const QRect &r = rects[i]; if (!r.isValid()) { continue; } const int x0 = r.x(); const int y0 = r.y(); const int x1 = r.x() + r.width(); const int y1 = r.y() + r.height(); const int u0 = (x0 + offsets[i].x()) * textureScale; const int v0 = (y0 + offsets[i].y()) * textureScale; const int u1 = (x1 + offsets[i].x()) * textureScale; const int v1 = (y1 + offsets[i].y()) * textureScale; WindowQuad quad; if (orientations[i] == Qt::Vertical) { quad[0] = WindowVertex(x0, y0, v0, u0); // Top-left quad[1] = WindowVertex(x1, y0, v0, u1); // Top-right quad[2] = WindowVertex(x1, y1, v1, u1); // Bottom-right quad[3] = WindowVertex(x0, y1, v1, u0); // Bottom-left } else { quad[0] = WindowVertex(x0, y0, u0, v0); // Top-left quad[1] = WindowVertex(x1, y0, u1, v0); // Top-right quad[2] = WindowVertex(x1, y1, u1, v1); // Bottom-right quad[3] = WindowVertex(x0, y1, u0, v1); // Bottom-left } list.append(quad); } return list; } } // namespace KWin