1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org>
6     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
7 
8     SPDX-License-Identifier: GPL-2.0-or-later
9 */
10 #include "shadow.h"
11 // kwin
12 #include "atoms.h"
13 #include "abstract_client.h"
14 #include "composite.h"
15 #include "internal_client.h"
16 #include "scene.h"
17 #include "toplevel.h"
18 #include "wayland_server.h"
19 
20 #include <KDecoration2/Decoration>
21 #include <KDecoration2/DecorationShadow>
22 
23 #include <KWaylandServer/shmclientbuffer.h>
24 #include <KWaylandServer/shadow_interface.h>
25 #include <KWaylandServer/surface_interface.h>
26 
27 #include <QWindow>
28 
29 Q_DECLARE_METATYPE(QMargins)
30 
31 namespace KWin
32 {
33 
Shadow(Toplevel * toplevel)34 Shadow::Shadow(Toplevel *toplevel)
35     : m_topLevel(toplevel)
36     , m_cachedSize(toplevel->size())
37     , m_decorationShadow(nullptr)
38 {
39     connect(m_topLevel, &Toplevel::frameGeometryChanged, this, &Shadow::geometryChanged);
40 }
41 
~Shadow()42 Shadow::~Shadow()
43 {
44 }
45 
createShadow(Toplevel * toplevel)46 Shadow *Shadow::createShadow(Toplevel *toplevel)
47 {
48     Shadow *shadow = createShadowFromDecoration(toplevel);
49     if (!shadow && waylandServer()) {
50         shadow = createShadowFromWayland(toplevel);
51     }
52     if (!shadow && kwinApp()->x11Connection()) {
53         shadow = createShadowFromX11(toplevel);
54     }
55     if (!shadow) {
56         shadow = createShadowFromInternalWindow(toplevel);
57     }
58     return shadow;
59 }
60 
createShadowFromX11(Toplevel * toplevel)61 Shadow *Shadow::createShadowFromX11(Toplevel *toplevel)
62 {
63     auto data = Shadow::readX11ShadowProperty(toplevel->window());
64     if (!data.isEmpty()) {
65         Shadow *shadow = Compositor::self()->scene()->createShadow(toplevel);
66 
67         if (!shadow->init(data)) {
68             delete shadow;
69             return nullptr;
70         }
71         return shadow;
72     } else {
73         return nullptr;
74     }
75 }
76 
createShadowFromDecoration(Toplevel * toplevel)77 Shadow *Shadow::createShadowFromDecoration(Toplevel *toplevel)
78 {
79     AbstractClient *c = qobject_cast<AbstractClient*>(toplevel);
80     if (!c) {
81         return nullptr;
82     }
83     if (!c->decoration()) {
84         return nullptr;
85     }
86     Shadow *shadow = Compositor::self()->scene()->createShadow(toplevel);
87     if (!shadow->init(c->decoration())) {
88         delete shadow;
89         return nullptr;
90     }
91     return shadow;
92 }
93 
createShadowFromWayland(Toplevel * toplevel)94 Shadow *Shadow::createShadowFromWayland(Toplevel *toplevel)
95 {
96     auto surface = toplevel->surface();
97     if (!surface) {
98         return nullptr;
99     }
100     const auto s = surface->shadow();
101     if (!s) {
102         return nullptr;
103     }
104     Shadow *shadow = Compositor::self()->scene()->createShadow(toplevel);
105     if (!shadow->init(s)) {
106         delete shadow;
107         return nullptr;
108     }
109     return shadow;
110 }
111 
createShadowFromInternalWindow(Toplevel * toplevel)112 Shadow *Shadow::createShadowFromInternalWindow(Toplevel *toplevel)
113 {
114     const InternalClient *client = qobject_cast<InternalClient *>(toplevel);
115     if (!client) {
116         return nullptr;
117     }
118     const QWindow *window = client->internalWindow();
119     if (!window) {
120         return nullptr;
121     }
122     Shadow *shadow = Compositor::self()->scene()->createShadow(toplevel);
123     if (!shadow->init(window)) {
124         delete shadow;
125         return nullptr;
126     }
127     return shadow;
128 }
129 
readX11ShadowProperty(xcb_window_t id)130 QVector< uint32_t > Shadow::readX11ShadowProperty(xcb_window_t id)
131 {
132     QVector<uint32_t> ret;
133     if (id != XCB_WINDOW_NONE) {
134         Xcb::Property property(false, id, atoms->kde_net_wm_shadow, XCB_ATOM_CARDINAL, 0, 12);
135         uint32_t *shadow = property.value<uint32_t*>();
136         if (shadow) {
137             ret.reserve(12);
138             for (int i=0; i<12; ++i) {
139                 ret << shadow[i];
140             }
141         }
142     }
143     return ret;
144 }
145 
init(const QVector<uint32_t> & data)146 bool Shadow::init(const QVector< uint32_t > &data)
147 {
148     QVector<Xcb::WindowGeometry> pixmapGeometries(ShadowElementsCount);
149     QVector<xcb_get_image_cookie_t> getImageCookies(ShadowElementsCount);
150     auto *c = connection();
151     for (int i = 0; i < ShadowElementsCount; ++i) {
152         pixmapGeometries[i] = Xcb::WindowGeometry(data[i]);
153     }
154     auto discardReplies = [&getImageCookies](int start) {
155         for (int i = start; i < getImageCookies.size(); ++i) {
156             xcb_discard_reply(connection(), getImageCookies.at(i).sequence);
157         }
158     };
159     for (int i = 0; i < ShadowElementsCount; ++i) {
160         auto &geo = pixmapGeometries[i];
161         if (geo.isNull()) {
162             discardReplies(0);
163             return false;
164         }
165         getImageCookies[i] = xcb_get_image_unchecked(c, XCB_IMAGE_FORMAT_Z_PIXMAP, data[i],
166                                                      0, 0, geo->width, geo->height, ~0);
167     }
168     for (int i = 0; i < ShadowElementsCount; ++i) {
169         auto *reply = xcb_get_image_reply(c, getImageCookies.at(i), nullptr);
170         if (!reply) {
171             discardReplies(i+1);
172             return false;
173         }
174         auto &geo = pixmapGeometries[i];
175         QImage image(xcb_get_image_data(reply), geo->width, geo->height, QImage::Format_ARGB32);
176         m_shadowElements[i] = QPixmap::fromImage(image);
177         free(reply);
178     }
179     m_offset = QMargins(data[ShadowElementsCount + 3],
180                         data[ShadowElementsCount],
181                         data[ShadowElementsCount + 1],
182                         data[ShadowElementsCount + 2]);
183     Q_EMIT offsetChanged();
184     if (!prepareBackend()) {
185         return false;
186     }
187     Q_EMIT textureChanged();
188     return true;
189 }
190 
init(KDecoration2::Decoration * decoration)191 bool Shadow::init(KDecoration2::Decoration *decoration)
192 {
193     if (m_decorationShadow) {
194         // disconnect previous connections
195         disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::innerShadowRectChanged, m_topLevel, &Toplevel::updateShadow);
196         disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::shadowChanged,        m_topLevel, &Toplevel::updateShadow);
197         disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::paddingChanged,       m_topLevel, &Toplevel::updateShadow);
198     }
199     m_decorationShadow = decoration->shadow();
200     if (!m_decorationShadow) {
201         return false;
202     }
203     // setup connections - all just mapped to recreate
204     connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::innerShadowRectChanged, m_topLevel, &Toplevel::updateShadow);
205     connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::shadowChanged,        m_topLevel, &Toplevel::updateShadow);
206     connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::paddingChanged,       m_topLevel, &Toplevel::updateShadow);
207 
208     m_offset = m_decorationShadow->padding();
209     Q_EMIT offsetChanged();
210     if (!prepareBackend()) {
211         return false;
212     }
213     Q_EMIT textureChanged();
214     return true;
215 }
216 
shadowTileForBuffer(KWaylandServer::ClientBuffer * buffer)217 static QPixmap shadowTileForBuffer(KWaylandServer::ClientBuffer *buffer)
218 {
219     auto shmBuffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(buffer);
220     if (shmBuffer) {
221         return QPixmap::fromImage(shmBuffer->data().copy());
222     }
223     return QPixmap();
224 }
225 
init(const QPointer<KWaylandServer::ShadowInterface> & shadow)226 bool Shadow::init(const QPointer< KWaylandServer::ShadowInterface > &shadow)
227 {
228     if (!shadow) {
229         return false;
230     }
231 
232     m_shadowElements[ShadowElementTop] = shadowTileForBuffer(shadow->top());
233     m_shadowElements[ShadowElementTopRight] = shadowTileForBuffer(shadow->topRight());
234     m_shadowElements[ShadowElementRight] = shadowTileForBuffer(shadow->right());
235     m_shadowElements[ShadowElementBottomRight] = shadowTileForBuffer(shadow->bottomRight());
236     m_shadowElements[ShadowElementBottom] = shadowTileForBuffer(shadow->bottom());
237     m_shadowElements[ShadowElementBottomLeft] = shadowTileForBuffer(shadow->bottomLeft());
238     m_shadowElements[ShadowElementLeft] = shadowTileForBuffer(shadow->left());
239     m_shadowElements[ShadowElementTopLeft] = shadowTileForBuffer(shadow->topLeft());
240 
241     m_offset = shadow->offset().toMargins();
242     Q_EMIT offsetChanged();
243     if (!prepareBackend()) {
244         return false;
245     }
246     Q_EMIT textureChanged();
247     return true;
248 }
249 
init(const QWindow * window)250 bool Shadow::init(const QWindow *window)
251 {
252     const bool isEnabled = window->property("kwin_shadow_enabled").toBool();
253     if (!isEnabled) {
254         return false;
255     }
256 
257     const QImage leftTile = window->property("kwin_shadow_left_tile").value<QImage>();
258     const QImage topLeftTile = window->property("kwin_shadow_top_left_tile").value<QImage>();
259     const QImage topTile = window->property("kwin_shadow_top_tile").value<QImage>();
260     const QImage topRightTile = window->property("kwin_shadow_top_right_tile").value<QImage>();
261     const QImage rightTile = window->property("kwin_shadow_right_tile").value<QImage>();
262     const QImage bottomRightTile = window->property("kwin_shadow_bottom_right_tile").value<QImage>();
263     const QImage bottomTile = window->property("kwin_shadow_bottom_tile").value<QImage>();
264     const QImage bottomLeftTile = window->property("kwin_shadow_bottom_left_tile").value<QImage>();
265 
266     m_shadowElements[ShadowElementLeft] = QPixmap::fromImage(leftTile);
267     m_shadowElements[ShadowElementTopLeft] = QPixmap::fromImage(topLeftTile);
268     m_shadowElements[ShadowElementTop] = QPixmap::fromImage(topTile);
269     m_shadowElements[ShadowElementTopRight] = QPixmap::fromImage(topRightTile);
270     m_shadowElements[ShadowElementRight] = QPixmap::fromImage(rightTile);
271     m_shadowElements[ShadowElementBottomRight] = QPixmap::fromImage(bottomRightTile);
272     m_shadowElements[ShadowElementBottom] = QPixmap::fromImage(bottomTile);
273     m_shadowElements[ShadowElementBottomLeft] = QPixmap::fromImage(bottomLeftTile);
274 
275     m_offset = window->property("kwin_shadow_padding").value<QMargins>();
276     Q_EMIT offsetChanged();
277 
278     if (!prepareBackend()) {
279         return false;
280     }
281     Q_EMIT textureChanged();
282     return true;
283 }
284 
updateShadow()285 bool Shadow::updateShadow()
286 {
287     if (!m_topLevel) {
288         return false;
289     }
290 
291     if (m_decorationShadow) {
292         if (AbstractClient *c = qobject_cast<AbstractClient*>(m_topLevel)) {
293             if (c->decoration()) {
294                 if (init(c->decoration())) {
295                     return true;
296                 }
297             }
298         }
299         return false;
300     }
301 
302     if (waylandServer()) {
303         if (m_topLevel && m_topLevel->surface()) {
304             if (const auto &s = m_topLevel->surface()->shadow()) {
305                 if (init(s)) {
306                     return true;
307                 }
308             }
309         }
310     }
311 
312     if (InternalClient *client = qobject_cast<InternalClient *>(m_topLevel)) {
313         if (init(client->internalWindow())) {
314             return true;
315         }
316     }
317 
318     auto data = Shadow::readX11ShadowProperty(m_topLevel->window());
319     if (data.isEmpty()) {
320         return false;
321     }
322 
323     init(data);
324 
325     return true;
326 }
327 
toplevel() const328 Toplevel *Shadow::toplevel() const
329 {
330     return m_topLevel;
331 }
332 
setToplevel(Toplevel * topLevel)333 void Shadow::setToplevel(Toplevel *topLevel)
334 {
335     m_topLevel = topLevel;
336     connect(m_topLevel, &Toplevel::frameGeometryChanged, this, &Shadow::geometryChanged);
337 }
geometryChanged()338 void Shadow::geometryChanged()
339 {
340     if (m_cachedSize == m_topLevel->size()) {
341         return;
342     }
343     m_cachedSize = m_topLevel->size();
344     Q_EMIT rectChanged();
345 }
346 
decorationShadowImage() const347 QImage Shadow::decorationShadowImage() const
348 {
349     if (!m_decorationShadow) {
350         return QImage();
351     }
352     return m_decorationShadow->shadow();
353 }
354 
elementSize(Shadow::ShadowElements element) const355 QSize Shadow::elementSize(Shadow::ShadowElements element) const
356 {
357     if (m_decorationShadow) {
358         switch (element) {
359         case ShadowElementTop:
360             return m_decorationShadow->topGeometry().size();
361         case ShadowElementTopRight:
362             return m_decorationShadow->topRightGeometry().size();
363         case ShadowElementRight:
364             return m_decorationShadow->rightGeometry().size();
365         case ShadowElementBottomRight:
366             return m_decorationShadow->bottomRightGeometry().size();
367         case ShadowElementBottom:
368             return m_decorationShadow->bottomGeometry().size();
369         case ShadowElementBottomLeft:
370             return m_decorationShadow->bottomLeftGeometry().size();
371         case ShadowElementLeft:
372             return m_decorationShadow->leftGeometry().size();
373         case ShadowElementTopLeft:
374             return m_decorationShadow->topLeftGeometry().size();
375         default:
376             return QSize();
377         }
378     } else {
379         return m_shadowElements[element].size();
380     }
381 }
382 
setShadowElement(const QPixmap & shadow,Shadow::ShadowElements element)383 void Shadow::setShadowElement(const QPixmap &shadow, Shadow::ShadowElements element)
384 {
385     m_shadowElements[element] = shadow;
386 }
387 
388 } // namespace
389