File indexing completed on 2024-05-19 16:34:45
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 "composite.h" 0010 #include "core/output.h" 0011 #include "decorations/decoratedclient.h" 0012 #include "deleted.h" 0013 #include "scene/workspacescene.h" 0014 #include "utils/common.h" 0015 #include "window.h" 0016 0017 #include <cmath> 0018 0019 #include <KDecoration2/DecoratedClient> 0020 #include <KDecoration2/Decoration> 0021 0022 #include <QPainter> 0023 0024 namespace KWin 0025 { 0026 0027 DecorationRenderer::DecorationRenderer(Decoration::DecoratedClientImpl *client) 0028 : m_client(client) 0029 , m_imageSizesDirty(true) 0030 { 0031 connect(client->decoration(), &KDecoration2::Decoration::damaged, 0032 this, &DecorationRenderer::addDamage); 0033 0034 connect(client->decoration(), &KDecoration2::Decoration::bordersChanged, 0035 this, &DecorationRenderer::invalidate); 0036 connect(client->decoratedClient(), &KDecoration2::DecoratedClient::sizeChanged, 0037 this, &DecorationRenderer::invalidate); 0038 0039 invalidate(); 0040 } 0041 0042 Decoration::DecoratedClientImpl *DecorationRenderer::client() const 0043 { 0044 return m_client; 0045 } 0046 0047 void DecorationRenderer::invalidate() 0048 { 0049 if (m_client) { 0050 addDamage(m_client->window()->rect().toAlignedRect()); 0051 } 0052 m_imageSizesDirty = true; 0053 } 0054 0055 QRegion DecorationRenderer::damage() const 0056 { 0057 return m_damage; 0058 } 0059 0060 void DecorationRenderer::addDamage(const QRegion ®ion) 0061 { 0062 m_damage += region; 0063 Q_EMIT damaged(region); 0064 } 0065 0066 void DecorationRenderer::resetDamage() 0067 { 0068 m_damage = QRegion(); 0069 } 0070 0071 qreal DecorationRenderer::effectiveDevicePixelRatio() const 0072 { 0073 // QPainter won't let paint with a device pixel ratio less than 1. 0074 return std::max(qreal(1.0), devicePixelRatio()); 0075 } 0076 0077 qreal DecorationRenderer::devicePixelRatio() const 0078 { 0079 return m_devicePixelRatio; 0080 } 0081 0082 void DecorationRenderer::setDevicePixelRatio(qreal dpr) 0083 { 0084 if (m_devicePixelRatio != dpr) { 0085 m_devicePixelRatio = dpr; 0086 invalidate(); 0087 } 0088 } 0089 0090 QImage DecorationRenderer::renderToImage(const QRect &geo) 0091 { 0092 Q_ASSERT(m_client); 0093 0094 // Guess the pixel format of the X pixmap into which the QImage will be copied. 0095 QImage::Format format; 0096 const int depth = client()->window()->depth(); 0097 switch (depth) { 0098 case 30: 0099 format = QImage::Format_A2RGB30_Premultiplied; 0100 break; 0101 case 24: 0102 case 32: 0103 format = QImage::Format_ARGB32_Premultiplied; 0104 break; 0105 default: 0106 qCCritical(KWIN_CORE) << "Unsupported client depth" << depth; 0107 format = QImage::Format_ARGB32_Premultiplied; 0108 break; 0109 }; 0110 0111 QImage image(geo.width() * m_devicePixelRatio, geo.height() * m_devicePixelRatio, format); 0112 image.setDevicePixelRatio(m_devicePixelRatio); 0113 image.fill(Qt::transparent); 0114 QPainter p(&image); 0115 p.setRenderHint(QPainter::Antialiasing); 0116 p.setWindow(QRect(geo.topLeft(), geo.size() * effectiveDevicePixelRatio())); 0117 p.setClipRect(geo); 0118 renderToPainter(&p, geo); 0119 return image; 0120 } 0121 0122 void DecorationRenderer::renderToPainter(QPainter *painter, const QRect &rect) 0123 { 0124 client()->decoration()->paint(painter, rect); 0125 } 0126 0127 DecorationItem::DecorationItem(KDecoration2::Decoration *decoration, Window *window, Scene *scene, Item *parent) 0128 : Item(scene, parent) 0129 , m_window(window) 0130 { 0131 m_renderer.reset(Compositor::self()->scene()->createDecorationRenderer(window->decoratedClient())); 0132 0133 connect(window, &Window::frameGeometryChanged, 0134 this, &DecorationItem::handleFrameGeometryChanged); 0135 connect(window, &Window::windowClosed, 0136 this, &DecorationItem::handleWindowClosed); 0137 connect(window, &Window::screenChanged, 0138 this, &DecorationItem::handleOutputChanged); 0139 0140 connect(decoration, &KDecoration2::Decoration::bordersChanged, 0141 this, &DecorationItem::discardQuads); 0142 0143 connect(renderer(), &DecorationRenderer::damaged, 0144 this, qOverload<const QRegion &>(&Item::scheduleRepaint)); 0145 0146 // this toSize is to match that DecoratedWindow also rounds 0147 setSize(window->size().toSize()); 0148 handleOutputChanged(); 0149 } 0150 0151 QVector<QRectF> DecorationItem::shape() const 0152 { 0153 QRectF left, top, right, bottom; 0154 m_window->layoutDecorationRects(left, top, right, bottom); 0155 return {left, top, right, bottom}; 0156 } 0157 0158 QRegion DecorationItem::opaque() const 0159 { 0160 if (m_window->decorationHasAlpha()) { 0161 return QRegion(); 0162 } 0163 QRectF left, top, right, bottom; 0164 m_window->layoutDecorationRects(left, top, right, bottom); 0165 return QRegion(left.toRect()).united(top.toRect()).united(right.toRect()).united(bottom.toRect()); 0166 } 0167 0168 void DecorationItem::preprocess() 0169 { 0170 const QRegion damage = m_renderer->damage(); 0171 if (!damage.isEmpty()) { 0172 m_renderer->render(damage); 0173 m_renderer->resetDamage(); 0174 } 0175 } 0176 0177 void DecorationItem::handleOutputChanged() 0178 { 0179 if (m_output) { 0180 disconnect(m_output, &Output::scaleChanged, this, &DecorationItem::handleOutputScaleChanged); 0181 } 0182 0183 m_output = m_window->output(); 0184 0185 if (m_output) { 0186 handleOutputScaleChanged(); 0187 connect(m_output, &Output::scaleChanged, this, &DecorationItem::handleOutputScaleChanged); 0188 } 0189 } 0190 0191 void DecorationItem::handleOutputScaleChanged() 0192 { 0193 const qreal dpr = m_output->scale(); 0194 if (m_renderer->devicePixelRatio() != dpr) { 0195 m_renderer->setDevicePixelRatio(dpr); 0196 discardQuads(); 0197 } 0198 } 0199 0200 void DecorationItem::handleFrameGeometryChanged() 0201 { 0202 setSize(m_window->size().toSize()); 0203 } 0204 0205 void DecorationItem::handleWindowClosed(Window *original, Deleted *deleted) 0206 { 0207 m_window = deleted; 0208 0209 // If the decoration is about to be destroyed, render the decoration for the last time. 0210 effects->makeOpenGLContextCurrent(); 0211 preprocess(); 0212 } 0213 0214 DecorationRenderer *DecorationItem::renderer() const 0215 { 0216 return m_renderer.get(); 0217 } 0218 0219 Window *DecorationItem::window() const 0220 { 0221 return m_window; 0222 } 0223 0224 WindowQuad buildQuad(const QRectF &partRect, const QPoint &textureOffset, 0225 const qreal devicePixelRatio, bool rotated) 0226 { 0227 const QRectF &r = partRect; 0228 const int p = DecorationRenderer::TexturePad; 0229 0230 const int x0 = r.x(); 0231 const int y0 = r.y(); 0232 const int x1 = r.x() + r.width(); 0233 const int y1 = r.y() + r.height(); 0234 0235 WindowQuad quad; 0236 if (rotated) { 0237 const int u0 = textureOffset.y() + p; 0238 const int v0 = textureOffset.x() + p; 0239 const int u1 = textureOffset.y() + p + (r.width() * devicePixelRatio); 0240 const int v1 = textureOffset.x() + p + (r.height() * devicePixelRatio); 0241 0242 quad[0] = WindowVertex(x0, y0, v0, u1); // Top-left 0243 quad[1] = WindowVertex(x1, y0, v0, u0); // Top-right 0244 quad[2] = WindowVertex(x1, y1, v1, u0); // Bottom-right 0245 quad[3] = WindowVertex(x0, y1, v1, u1); // Bottom-left 0246 } else { 0247 const int u0 = textureOffset.x() + p; 0248 const int v0 = textureOffset.y() + p; 0249 const int u1 = textureOffset.x() + p + (r.width() * devicePixelRatio); 0250 const int v1 = textureOffset.y() + p + (r.height() * devicePixelRatio); 0251 0252 quad[0] = WindowVertex(x0, y0, u0, v0); // Top-left 0253 quad[1] = WindowVertex(x1, y0, u1, v0); // Top-right 0254 quad[2] = WindowVertex(x1, y1, u1, v1); // Bottom-right 0255 quad[3] = WindowVertex(x0, y1, u0, v1); // Bottom-left 0256 } 0257 return quad; 0258 } 0259 0260 WindowQuadList DecorationItem::buildQuads() const 0261 { 0262 if (m_window->frameMargins().isNull()) { 0263 return WindowQuadList(); 0264 } 0265 0266 QRectF left, top, right, bottom; 0267 const qreal devicePixelRatio = m_renderer->effectiveDevicePixelRatio(); 0268 const int texturePad = DecorationRenderer::TexturePad; 0269 0270 m_window->layoutDecorationRects(left, top, right, bottom); 0271 0272 const int topHeight = std::ceil(top.height() * devicePixelRatio); 0273 const int bottomHeight = std::ceil(bottom.height() * devicePixelRatio); 0274 const int leftWidth = std::ceil(left.width() * devicePixelRatio); 0275 0276 const QPoint topPosition(0, 0); 0277 const QPoint bottomPosition(0, topPosition.y() + topHeight + (2 * texturePad)); 0278 const QPoint leftPosition(0, bottomPosition.y() + bottomHeight + (2 * texturePad)); 0279 const QPoint rightPosition(0, leftPosition.y() + leftWidth + (2 * texturePad)); 0280 0281 WindowQuadList list; 0282 if (left.isValid()) { 0283 list.append(buildQuad(left, leftPosition, devicePixelRatio, true)); 0284 } 0285 if (top.isValid()) { 0286 list.append(buildQuad(top, topPosition, devicePixelRatio, false)); 0287 } 0288 if (right.isValid()) { 0289 list.append(buildQuad(right, rightPosition, devicePixelRatio, true)); 0290 } 0291 if (bottom.isValid()) { 0292 list.append(buildQuad(bottom, bottomPosition, devicePixelRatio, false)); 0293 } 0294 return list; 0295 } 0296 0297 } // namespace KWin