1 /*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "decorationitem.h"
9 #include "abstract_client.h"
10 #include "composite.h"
11 #include "decorations/decoratedclient.h"
12 #include "deleted.h"
13 #include "scene.h"
14 #include "utils.h"
15
16 #include <KDecoration2/Decoration>
17 #include <KDecoration2/DecoratedClient>
18
19 #include <QPainter>
20
21 namespace KWin
22 {
23
DecorationRenderer(Decoration::DecoratedClientImpl * client)24 DecorationRenderer::DecorationRenderer(Decoration::DecoratedClientImpl *client)
25 : m_client(client)
26 , m_imageSizesDirty(true)
27 {
28 connect(client->decoration(), &KDecoration2::Decoration::damaged,
29 this, &DecorationRenderer::addDamage);
30
31 connect(client->client(), &AbstractClient::screenScaleChanged,
32 this, &DecorationRenderer::invalidate);
33 connect(client->decoration(), &KDecoration2::Decoration::bordersChanged,
34 this, &DecorationRenderer::invalidate);
35 connect(client->decoratedClient(), &KDecoration2::DecoratedClient::sizeChanged,
36 this, &DecorationRenderer::invalidate);
37
38 invalidate();
39 }
40
client() const41 Decoration::DecoratedClientImpl *DecorationRenderer::client() const
42 {
43 return m_client;
44 }
45
invalidate()46 void DecorationRenderer::invalidate()
47 {
48 if (m_client) {
49 addDamage(m_client->client()->rect());
50 }
51 m_imageSizesDirty = true;
52 }
53
damage() const54 QRegion DecorationRenderer::damage() const
55 {
56 return m_damage;
57 }
58
addDamage(const QRegion & region)59 void DecorationRenderer::addDamage(const QRegion ®ion)
60 {
61 m_damage += region;
62 Q_EMIT damaged(region);
63 }
64
resetDamage()65 void DecorationRenderer::resetDamage()
66 {
67 m_damage = QRegion();
68 }
69
renderToImage(const QRect & geo)70 QImage DecorationRenderer::renderToImage(const QRect &geo)
71 {
72 Q_ASSERT(m_client);
73 auto dpr = client()->client()->screenScale();
74
75 // Guess the pixel format of the X pixmap into which the QImage will be copied.
76 QImage::Format format;
77 const int depth = client()->client()->depth();
78 switch (depth) {
79 case 30:
80 format = QImage::Format_A2RGB30_Premultiplied;
81 break;
82 case 24:
83 case 32:
84 format = QImage::Format_ARGB32_Premultiplied;
85 break;
86 default:
87 qCCritical(KWIN_CORE) << "Unsupported client depth" << depth;
88 format = QImage::Format_ARGB32_Premultiplied;
89 break;
90 };
91
92 QImage image(geo.width() * dpr, geo.height() * dpr, format);
93 image.setDevicePixelRatio(dpr);
94 image.fill(Qt::transparent);
95 QPainter p(&image);
96 p.setRenderHint(QPainter::Antialiasing);
97 p.setWindow(QRect(geo.topLeft(), geo.size() * qPainterEffectiveDevicePixelRatio(&p)));
98 p.setClipRect(geo);
99 renderToPainter(&p, geo);
100 return image;
101 }
102
renderToPainter(QPainter * painter,const QRect & rect)103 void DecorationRenderer::renderToPainter(QPainter *painter, const QRect &rect)
104 {
105 client()->decoration()->paint(painter, rect);
106 }
107
DecorationItem(KDecoration2::Decoration * decoration,AbstractClient * window,Item * parent)108 DecorationItem::DecorationItem(KDecoration2::Decoration *decoration, AbstractClient *window, Item *parent)
109 : Item(parent)
110 , m_window(window)
111 {
112 m_renderer.reset(Compositor::self()->scene()->createDecorationRenderer(window->decoratedClient()));
113
114 connect(window, &Toplevel::frameGeometryChanged,
115 this, &DecorationItem::handleFrameGeometryChanged);
116 connect(window, &Toplevel::windowClosed,
117 this, &DecorationItem::handleWindowClosed);
118
119 connect(window, &Toplevel::screenScaleChanged,
120 this, &DecorationItem::discardQuads);
121 connect(decoration, &KDecoration2::Decoration::bordersChanged,
122 this, &DecorationItem::discardQuads);
123
124 connect(renderer(), &DecorationRenderer::damaged,
125 this, &DecorationItem::scheduleRepaint);
126
127 setSize(window->size());
128 }
129
preprocess()130 void DecorationItem::preprocess()
131 {
132 const QRegion damage = m_renderer->damage();
133 if (!damage.isEmpty()) {
134 m_renderer->render(damage);
135 m_renderer->resetDamage();
136 }
137 }
138
handleFrameGeometryChanged()139 void DecorationItem::handleFrameGeometryChanged()
140 {
141 setSize(m_window->size());
142 }
143
handleWindowClosed(Toplevel * original,Deleted * deleted)144 void DecorationItem::handleWindowClosed(Toplevel *original, Deleted *deleted)
145 {
146 Q_UNUSED(original)
147 m_window = deleted;
148
149 // If the decoration is about to be destroyed, render the decoration for the last time.
150 preprocess();
151 }
152
renderer() const153 DecorationRenderer *DecorationItem::renderer() const
154 {
155 return m_renderer.data();
156 }
157
buildQuads() const158 WindowQuadList DecorationItem::buildQuads() const
159 {
160 if (m_window->frameMargins().isNull()) {
161 return WindowQuadList();
162 }
163
164 QRect rects[4];
165
166 if (const AbstractClient *client = qobject_cast<const AbstractClient *>(m_window)) {
167 client->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3]);
168 } else if (const Deleted *deleted = qobject_cast<const Deleted *>(m_window)) {
169 deleted->layoutDecorationRects(rects[0], rects[1], rects[2], rects[3]);
170 }
171
172 const qreal textureScale = m_window->screenScale();
173 const int padding = 1;
174
175 const QPoint topSpritePosition(padding, padding);
176 const QPoint bottomSpritePosition(padding, topSpritePosition.y() + rects[1].height() + 2 * padding);
177 const QPoint leftSpritePosition(bottomSpritePosition.y() + rects[3].height() + 2 * padding, padding);
178 const QPoint rightSpritePosition(leftSpritePosition.x() + rects[0].width() + 2 * padding, padding);
179
180 const QPoint offsets[4] = {
181 QPoint(-rects[0].x(), -rects[0].y()) + leftSpritePosition,
182 QPoint(-rects[1].x(), -rects[1].y()) + topSpritePosition,
183 QPoint(-rects[2].x(), -rects[2].y()) + rightSpritePosition,
184 QPoint(-rects[3].x(), -rects[3].y()) + bottomSpritePosition,
185 };
186
187 const Qt::Orientation orientations[4] = {
188 Qt::Vertical, // Left
189 Qt::Horizontal, // Top
190 Qt::Vertical, // Right
191 Qt::Horizontal, // Bottom
192 };
193
194 WindowQuadList list;
195 list.reserve(4);
196
197 for (int i = 0; i < 4; ++i) {
198 const QRect &r = rects[i];
199 if (!r.isValid()) {
200 continue;
201 }
202
203 const int x0 = r.x();
204 const int y0 = r.y();
205 const int x1 = r.x() + r.width();
206 const int y1 = r.y() + r.height();
207
208 const int u0 = (x0 + offsets[i].x()) * textureScale;
209 const int v0 = (y0 + offsets[i].y()) * textureScale;
210 const int u1 = (x1 + offsets[i].x()) * textureScale;
211 const int v1 = (y1 + offsets[i].y()) * textureScale;
212
213 WindowQuad quad;
214
215 if (orientations[i] == Qt::Vertical) {
216 quad[0] = WindowVertex(x0, y0, v0, u0); // Top-left
217 quad[1] = WindowVertex(x1, y0, v0, u1); // Top-right
218 quad[2] = WindowVertex(x1, y1, v1, u1); // Bottom-right
219 quad[3] = WindowVertex(x0, y1, v1, u0); // Bottom-left
220 } else {
221 quad[0] = WindowVertex(x0, y0, u0, v0); // Top-left
222 quad[1] = WindowVertex(x1, y0, u1, v0); // Top-right
223 quad[2] = WindowVertex(x1, y1, u1, v1); // Bottom-right
224 quad[3] = WindowVertex(x0, y1, u0, v1); // Bottom-left
225 }
226
227 list.append(quad);
228 }
229
230 return list;
231 }
232
233 } // namespace KWin
234