KWin
Loading...
Searching...
No Matches
shadow.cpp
Go to the documentation of this file.
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"
14#include "internalwindow.h"
15#include "wayland/shadow.h"
16#include "wayland/surface.h"
17#include "wayland_server.h"
18#include "x11window.h"
19
20#include <KDecoration2/Decoration>
21#include <KDecoration2/DecorationShadow>
22
23#include <QWindow>
24
25Q_DECLARE_METATYPE(QMargins)
26
27namespace KWin
28{
29
31 : m_window(window)
32 , m_cachedSize(window->size())
33 , m_decorationShadow(nullptr)
34{
35 connect(m_window, &Window::frameGeometryChanged, this, &Shadow::geometryChanged);
36}
37
41
42std::unique_ptr<Shadow> Shadow::createShadow(Window *window)
43{
44 auto shadow = createShadowFromDecoration(window);
45 if (!shadow && waylandServer()) {
46 shadow = createShadowFromWayland(window);
47 }
48 if (!shadow && kwinApp()->x11Connection()) {
49 shadow = createShadowFromX11(window);
50 }
51 if (!shadow) {
52 shadow = createShadowFromInternalWindow(window);
53 }
54 return shadow;
55}
56
57std::unique_ptr<Shadow> Shadow::createShadowFromX11(Window *window)
58{
59 X11Window *x11Window = qobject_cast<X11Window *>(window);
60 if (!x11Window) {
61 return nullptr;
62 }
63 auto data = Shadow::readX11ShadowProperty(x11Window->window());
64 if (!data.isEmpty()) {
65 auto shadow = std::make_unique<Shadow>(window);
66 if (!shadow->init(data)) {
67 return nullptr;
68 }
69 return shadow;
70 } else {
71 return nullptr;
72 }
73}
74
75std::unique_ptr<Shadow> Shadow::createShadowFromDecoration(Window *window)
76{
77 if (!window->decoration()) {
78 return nullptr;
79 }
80 auto shadow = std::make_unique<Shadow>(window);
81 if (!shadow->init(window->decoration())) {
82 return nullptr;
83 }
84 return shadow;
85}
86
87std::unique_ptr<Shadow> Shadow::createShadowFromWayland(Window *window)
88{
89 auto surface = window->surface();
90 if (!surface) {
91 return nullptr;
92 }
93 const auto s = surface->shadow();
94 if (!s) {
95 return nullptr;
96 }
97 auto shadow = std::make_unique<Shadow>(window);
98 if (!shadow->init(s)) {
99 return nullptr;
100 }
101 return shadow;
102}
103
104std::unique_ptr<Shadow> Shadow::createShadowFromInternalWindow(Window *window)
105{
106 const InternalWindow *internalWindow = qobject_cast<InternalWindow *>(window);
107 if (!internalWindow) {
108 return nullptr;
109 }
110 const QWindow *handle = internalWindow->handle();
111 if (!handle) {
112 return nullptr;
113 }
114 auto shadow = std::make_unique<Shadow>(window);
115 if (!shadow->init(handle)) {
116 return nullptr;
117 }
118 return shadow;
119}
120
121QList<uint32_t> Shadow::readX11ShadowProperty(xcb_window_t id)
122{
123 QList<uint32_t> ret;
124 if (id != XCB_WINDOW_NONE) {
125 Xcb::Property property(false, id, atoms->kde_net_wm_shadow, XCB_ATOM_CARDINAL, 0, 12);
126 uint32_t *shadow = property.value<uint32_t *>();
127 if (shadow) {
128 ret.reserve(12);
129 for (int i = 0; i < 12; ++i) {
130 ret << shadow[i];
131 }
132 }
133 }
134 return ret;
135}
136
137bool Shadow::init(const QList<uint32_t> &data)
138{
139 QList<Xcb::WindowGeometry> pixmapGeometries(ShadowElementsCount);
140 QList<xcb_get_image_cookie_t> getImageCookies(ShadowElementsCount);
141 auto *c = kwinApp()->x11Connection();
142 for (int i = 0; i < ShadowElementsCount; ++i) {
143 pixmapGeometries[i] = Xcb::WindowGeometry(data[i]);
144 }
145 auto discardReplies = [&getImageCookies](int start) {
146 for (int i = start; i < getImageCookies.size(); ++i) {
147 xcb_discard_reply(kwinApp()->x11Connection(), getImageCookies.at(i).sequence);
148 }
149 };
150 for (int i = 0; i < ShadowElementsCount; ++i) {
151 auto &geo = pixmapGeometries[i];
152 if (geo.isNull()) {
153 discardReplies(0);
154 return false;
155 }
156 getImageCookies[i] = xcb_get_image_unchecked(c, XCB_IMAGE_FORMAT_Z_PIXMAP, data[i],
157 0, 0, geo->width, geo->height, ~0);
158 }
159 for (int i = 0; i < ShadowElementsCount; ++i) {
160 auto *reply = xcb_get_image_reply(c, getImageCookies.at(i), nullptr);
161 if (!reply) {
162 discardReplies(i + 1);
163 return false;
164 }
165 auto &geo = pixmapGeometries[i];
166 QImage image(xcb_get_image_data(reply), geo->width, geo->height, QImage::Format_ARGB32);
167 m_shadowElements[i] = image.copy();
168 free(reply);
169 }
170 m_offset = QMargins(data[ShadowElementsCount + 3],
172 data[ShadowElementsCount + 1],
173 data[ShadowElementsCount + 2]);
174 Q_EMIT offsetChanged();
175 Q_EMIT textureChanged();
176 return true;
177}
178
179bool Shadow::init(KDecoration2::Decoration *decoration)
180{
181 if (m_decorationShadow) {
182 // disconnect previous connections
183 disconnect(m_decorationShadow.get(), &KDecoration2::DecorationShadow::innerShadowRectChanged, m_window, &Window::updateShadow);
184 disconnect(m_decorationShadow.get(), &KDecoration2::DecorationShadow::shadowChanged, m_window, &Window::updateShadow);
185 disconnect(m_decorationShadow.get(), &KDecoration2::DecorationShadow::paddingChanged, m_window, &Window::updateShadow);
186 }
187 m_decorationShadow = decoration->shadow();
188 if (!m_decorationShadow) {
189 return false;
190 }
191 // setup connections - all just mapped to recreate
192 connect(m_decorationShadow.get(), &KDecoration2::DecorationShadow::innerShadowRectChanged, m_window, &Window::updateShadow);
193 connect(m_decorationShadow.get(), &KDecoration2::DecorationShadow::shadowChanged, m_window, &Window::updateShadow);
194 connect(m_decorationShadow.get(), &KDecoration2::DecorationShadow::paddingChanged, m_window, &Window::updateShadow);
195
196 m_offset = m_decorationShadow->padding();
197 Q_EMIT offsetChanged();
198 Q_EMIT textureChanged();
199 return true;
200}
201
202static QImage shadowTileForBuffer(GraphicsBuffer *buffer)
203{
204 if (buffer) {
205 const GraphicsBufferView view(buffer);
206 if (const QImage *image = view.image()) {
207 return image->copy();
208 }
209 }
210 return QImage();
211}
212
213bool Shadow::init(const QPointer<ShadowInterface> &shadow)
214{
215 if (!shadow) {
216 return false;
217 }
218
219 m_shadowElements[ShadowElementTop] = shadowTileForBuffer(shadow->top());
220 m_shadowElements[ShadowElementTopRight] = shadowTileForBuffer(shadow->topRight());
221 m_shadowElements[ShadowElementRight] = shadowTileForBuffer(shadow->right());
222 m_shadowElements[ShadowElementBottomRight] = shadowTileForBuffer(shadow->bottomRight());
223 m_shadowElements[ShadowElementBottom] = shadowTileForBuffer(shadow->bottom());
224 m_shadowElements[ShadowElementBottomLeft] = shadowTileForBuffer(shadow->bottomLeft());
225 m_shadowElements[ShadowElementLeft] = shadowTileForBuffer(shadow->left());
226 m_shadowElements[ShadowElementTopLeft] = shadowTileForBuffer(shadow->topLeft());
227
228 m_offset = shadow->offset().toMargins();
229 Q_EMIT offsetChanged();
230 Q_EMIT textureChanged();
231 return true;
232}
233
234bool Shadow::init(const QWindow *window)
235{
236 const bool isEnabled = window->property("kwin_shadow_enabled").toBool();
237 if (!isEnabled) {
238 return false;
239 }
240
241 const QImage leftTile = window->property("kwin_shadow_left_tile").value<QImage>();
242 const QImage topLeftTile = window->property("kwin_shadow_top_left_tile").value<QImage>();
243 const QImage topTile = window->property("kwin_shadow_top_tile").value<QImage>();
244 const QImage topRightTile = window->property("kwin_shadow_top_right_tile").value<QImage>();
245 const QImage rightTile = window->property("kwin_shadow_right_tile").value<QImage>();
246 const QImage bottomRightTile = window->property("kwin_shadow_bottom_right_tile").value<QImage>();
247 const QImage bottomTile = window->property("kwin_shadow_bottom_tile").value<QImage>();
248 const QImage bottomLeftTile = window->property("kwin_shadow_bottom_left_tile").value<QImage>();
249
250 m_shadowElements[ShadowElementLeft] = leftTile;
251 m_shadowElements[ShadowElementTopLeft] = topLeftTile;
252 m_shadowElements[ShadowElementTop] = topTile;
253 m_shadowElements[ShadowElementTopRight] = topRightTile;
254 m_shadowElements[ShadowElementRight] = rightTile;
255 m_shadowElements[ShadowElementBottomRight] = bottomRightTile;
256 m_shadowElements[ShadowElementBottom] = bottomTile;
257 m_shadowElements[ShadowElementBottomLeft] = bottomLeftTile;
258
259 m_offset = window->property("kwin_shadow_padding").value<QMargins>();
260 Q_EMIT offsetChanged();
261 Q_EMIT textureChanged();
262 return true;
263}
264
266{
267 if (!m_window) {
268 return false;
269 }
270
271 if (m_decorationShadow) {
272 if (m_window) {
273 if (m_window->decoration()) {
274 if (init(m_window->decoration())) {
275 return true;
276 }
277 }
278 }
279 return false;
280 }
281
282 if (waylandServer()) {
283 if (m_window && m_window->surface()) {
284 if (const auto &s = m_window->surface()->shadow()) {
285 if (init(s)) {
286 return true;
287 }
288 }
289 }
290 }
291
292 if (InternalWindow *window = qobject_cast<InternalWindow *>(m_window)) {
293 if (init(window->handle())) {
294 return true;
295 }
296 }
297
298 if (X11Window *window = qobject_cast<X11Window *>(m_window)) {
299 auto data = Shadow::readX11ShadowProperty(window->window());
300 if (!data.isEmpty()) {
301 init(data);
302 return true;
303 }
304 }
305
306 return false;
307}
308
310{
311 return m_window;
312}
313
315{
316 if (m_cachedSize == m_window->size()) {
317 return;
318 }
319 m_cachedSize = m_window->size();
320 Q_EMIT rectChanged();
321}
322
324{
325 if (!m_decorationShadow) {
326 return QImage();
327 }
328 return m_decorationShadow->shadow();
329}
330
332{
333 if (m_decorationShadow) {
334 switch (element) {
335 case ShadowElementTop:
336 return m_decorationShadow->topGeometry().size();
338 return m_decorationShadow->topRightGeometry().size();
340 return m_decorationShadow->rightGeometry().size();
342 return m_decorationShadow->bottomRightGeometry().size();
344 return m_decorationShadow->bottomGeometry().size();
346 return m_decorationShadow->bottomLeftGeometry().size();
348 return m_decorationShadow->leftGeometry().size();
350 return m_decorationShadow->topLeftGeometry().size();
351 default:
352 return QSize();
353 }
354 } else {
355 return m_shadowElements[element].size();
356 }
357}
358
359} // namespace
360
361#include "moc_shadow.cpp"
Xcb::Atom kde_net_wm_shadow
Definition atoms.h:58
void textureChanged()
QSize elementSize(ShadowElements element) const
Definition shadow.cpp:331
static std::unique_ptr< Shadow > createShadow(Window *window)
Definition shadow.cpp:42
Shadow(Window *window)
Definition shadow.cpp:30
@ ShadowElementLeft
Definition shadow.h:97
@ ShadowElementTopRight
Definition shadow.h:92
@ ShadowElementBottomLeft
Definition shadow.h:96
@ ShadowElementBottomRight
Definition shadow.h:94
@ ShadowElementTopLeft
Definition shadow.h:98
@ ShadowElementRight
Definition shadow.h:93
@ ShadowElementTop
Definition shadow.h:91
@ ShadowElementBottom
Definition shadow.h:95
@ ShadowElementsCount
Definition shadow.h:99
Window * window() const
Definition shadow.cpp:309
QImage decorationShadowImage() const
Definition shadow.cpp:323
void geometryChanged()
Definition shadow.cpp:314
bool updateShadow()
Definition shadow.cpp:265
void rectChanged()
void offsetChanged()
~Shadow() override
Definition shadow.cpp:38
ShadowInterface * shadow() const
Definition surface.cpp:871
QSizeF size
Definition window.h:84
SurfaceInterface * surface() const
Definition window.cpp:342
std::shared_ptr< KDecoration2::Decoration > decoration
Definition window.h:1820
void frameGeometryChanged(const QRectF &oldGeometry)
void updateShadow()
Definition window.cpp:264
xcb_window_t window() const
Q_DECLARE_METATYPE(KWin::SwitchEvent::State)
WaylandServer * waylandServer()
KWIN_EXPORT Atoms * atoms
Definition main.cpp:74