File indexing completed on 2024-11-10 04:57:14
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 ®ion) 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"