Changeset View
Changeset View
Standalone View
Standalone View
src/plasmaquick/dialogshadows.cpp
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * Copyright 2011 by Aaron Seigo <aseigo@kde.org> | 2 | * Copyright 2011 by Aaron Seigo <aseigo@kde.org> | ||
3 | * Copyright 2020 by Vlad Zahorodnii <vladzzag@gmail.com> | ||||
3 | * | 4 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU Library General Public License version 2, | 6 | * it under the terms of the GNU Library General Public License version 2, | ||
6 | * or (at your option) any later version. | 7 | * or (at your option) any later version. | ||
7 | * | 8 | * | ||
8 | * This program is distributed in the hope that it will be useful, | 9 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details | 12 | * GNU General Public License for more details | ||
12 | * | 13 | * | ||
13 | * You should have received a copy of the GNU Library General Public | 14 | * You should have received a copy of the GNU Library General Public | ||
14 | * License along with this program; if not, write to the | 15 | * License along with this program; if not, write to the | ||
15 | * Free Software Foundation, Inc., | 16 | * Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | 18 | */ | ||
18 | 19 | | |||
19 | #include "dialogshadows_p.h" | 20 | #include "dialogshadows_p.h" | ||
21 | #include "debug_p.h" | ||||
20 | 22 | | |||
21 | #include <QWindow> | 23 | #include <KWindowShadow> | ||
22 | #include <QPainter> | | |||
23 | #include <config-plasma.h> | | |||
24 | | ||||
25 | #include <KWindowSystem> | | |||
26 | #if HAVE_X11 | | |||
27 | #include <QX11Info> | | |||
28 | #include <X11/Xatom.h> | | |||
29 | #include <X11/Xlib.h> | | |||
30 | #include <X11/Xlib-xcb.h> | | |||
31 | #include <fixx11h.h> | | |||
32 | #endif | | |||
33 | | ||||
34 | #if HAVE_KWAYLAND | | |||
35 | #include "waylandintegration_p.h" | | |||
36 | #include <KWayland/Client/shadow.h> | | |||
37 | #include <KWayland/Client/shm_pool.h> | | |||
38 | #include <KWayland/Client/surface.h> | | |||
39 | #endif | | |||
40 | | ||||
41 | #include <qdebug.h> | | |||
42 | 24 | | |||
43 | class DialogShadows::Private | 25 | class DialogShadows::Private | ||
44 | { | 26 | { | ||
45 | public: | 27 | public: | ||
46 | Private(DialogShadows *shadows) | 28 | Private(DialogShadows *shadows) | ||
47 | : q(shadows) | 29 | : q(shadows) | ||
48 | #if HAVE_X11 | | |||
49 | , _connection(nullptr) | | |||
50 | , _gc(0x0) | | |||
51 | , m_isX11(KWindowSystem::isPlatformX11()) | | |||
52 | #endif | | |||
53 | { | 30 | { | ||
54 | } | 31 | } | ||
55 | 32 | | |||
56 | ~Private() | 33 | ~Private() | ||
57 | { | 34 | { | ||
58 | // Do not call clearPixmaps() from here: it creates new QPixmap(), | 35 | qDeleteAll(m_shadows); | ||
59 | // which causes a crash when application is stopping. | | |||
60 | freeX11Pixmaps(); | | |||
61 | } | 36 | } | ||
62 | 37 | | |||
63 | void freeX11Pixmaps(); | 38 | void clearTiles(); | ||
64 | void freeWaylandBuffers(); | 39 | void setupTiles(); | ||
65 | void clearPixmaps(); | 40 | void initTile(const QString &element); | ||
66 | void setupPixmaps(); | | |||
67 | Qt::HANDLE createPixmap(const QPixmap &source); | | |||
68 | void initPixmap(const QString &element); | | |||
69 | QPixmap initEmptyPixmap(const QSize &size); | | |||
70 | void updateShadow(const QWindow *window, Plasma::FrameSvg::EnabledBorders); | 41 | void updateShadow(const QWindow *window, Plasma::FrameSvg::EnabledBorders); | ||
71 | void updateShadowX11(const QWindow *window, Plasma::FrameSvg::EnabledBorders); | | |||
72 | void updateShadowWayland(const QWindow *window, Plasma::FrameSvg::EnabledBorders); | | |||
73 | void clearShadow(const QWindow *window); | 42 | void clearShadow(const QWindow *window); | ||
74 | void clearShadowX11(const QWindow *window); | | |||
75 | void clearShadowWayland(const QWindow *window); | | |||
76 | void updateShadows(); | 43 | void updateShadows(); | ||
77 | void windowDestroyed(QObject *deletedObject); | 44 | void windowDestroyed(QObject *deletedObject); | ||
78 | void setupData(Plasma::FrameSvg::EnabledBorders enabledBorders); | | |||
79 | 45 | | |||
80 | DialogShadows *q; | 46 | DialogShadows *q; | ||
81 | QList<QPixmap> m_shadowPixmaps; | | |||
82 | | ||||
83 | QPixmap m_emptyCornerPix; | | |||
84 | QPixmap m_emptyCornerLeftPix; | | |||
85 | QPixmap m_emptyCornerTopPix; | | |||
86 | QPixmap m_emptyCornerRightPix; | | |||
87 | QPixmap m_emptyCornerBottomPix; | | |||
88 | QPixmap m_emptyVerticalPix; | | |||
89 | QPixmap m_emptyHorizontalPix; | | |||
90 | | ||||
91 | #if HAVE_X11 | | |||
92 | //! xcb connection | | |||
93 | xcb_connection_t *_connection; | | |||
94 | | ||||
95 | //! graphical context | | |||
96 | xcb_gcontext_t _gc; | | |||
97 | bool m_isX11; | | |||
98 | #endif | | |||
99 | | ||||
100 | #if HAVE_KWAYLAND | | |||
101 | struct Wayland { | | |||
102 | QList<KWayland::Client::Buffer::Ptr> shadowBuffers; | | |||
103 | }; | | |||
104 | Wayland m_wayland; | | |||
105 | #endif | | |||
106 | 47 | | |||
107 | QHash<Plasma::FrameSvg::EnabledBorders, QVector<unsigned long> > data; | | |||
108 | QHash<const QWindow *, Plasma::FrameSvg::EnabledBorders> m_windows; | 48 | QHash<const QWindow *, Plasma::FrameSvg::EnabledBorders> m_windows; | ||
49 | QHash<const QWindow *, KWindowShadow *> m_shadows; | ||||
50 | QVector<KWindowShadowTile::Ptr> m_tiles; | ||||
109 | }; | 51 | }; | ||
110 | 52 | | |||
111 | class DialogShadowsSingleton | 53 | class DialogShadowsSingleton | ||
112 | { | 54 | { | ||
113 | public: | 55 | public: | ||
114 | DialogShadowsSingleton() | 56 | DialogShadowsSingleton() | ||
115 | { | 57 | { | ||
116 | } | 58 | } | ||
Show All 39 Lines | 97 | if (!d->m_windows.contains(window)) { | |||
156 | return; | 98 | return; | ||
157 | } | 99 | } | ||
158 | 100 | | |||
159 | d->m_windows.remove(window); | 101 | d->m_windows.remove(window); | ||
160 | disconnect(window, nullptr, this, nullptr); | 102 | disconnect(window, nullptr, this, nullptr); | ||
161 | d->clearShadow(window); | 103 | d->clearShadow(window); | ||
162 | 104 | | |||
163 | if (d->m_windows.isEmpty()) { | 105 | if (d->m_windows.isEmpty()) { | ||
164 | d->clearPixmaps(); | 106 | d->clearTiles(); | ||
165 | } | 107 | } | ||
166 | } | 108 | } | ||
167 | 109 | | |||
168 | void DialogShadows::setEnabledBorders(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | 110 | void DialogShadows::setEnabledBorders(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | ||
169 | { | 111 | { | ||
170 | if (!window || !d->m_windows.contains(window)) { | 112 | if (!window || !d->m_windows.contains(window)) { | ||
171 | return; | 113 | return; | ||
172 | } | 114 | } | ||
173 | 115 | | |||
174 | d->updateShadow(window, enabledBorders); | 116 | d->updateShadow(window, enabledBorders); | ||
175 | } | 117 | } | ||
176 | 118 | | |||
177 | 119 | | |||
178 | void DialogShadows::Private::windowDestroyed(QObject *deletedObject) | 120 | void DialogShadows::Private::windowDestroyed(QObject *deletedObject) | ||
179 | { | 121 | { | ||
180 | m_windows.remove(static_cast<QWindow *>(deletedObject)); | 122 | const QWindow *window = static_cast<QWindow *>(deletedObject); | ||
123 | | ||||
124 | m_windows.remove(window); | ||||
125 | clearShadow(window); | ||||
181 | 126 | | |||
182 | if (m_windows.isEmpty()) { | 127 | if (m_windows.isEmpty()) { | ||
183 | clearPixmaps(); | 128 | clearTiles(); | ||
184 | } | 129 | } | ||
185 | } | 130 | } | ||
186 | 131 | | |||
187 | void DialogShadows::Private::updateShadows() | 132 | void DialogShadows::Private::updateShadows() | ||
188 | { | 133 | { | ||
189 | setupPixmaps(); | 134 | setupTiles(); | ||
190 | QHash<const QWindow *, Plasma::FrameSvg::EnabledBorders>::const_iterator i; | 135 | QHash<const QWindow *, Plasma::FrameSvg::EnabledBorders>::const_iterator i; | ||
191 | for (i = m_windows.constBegin(); i != m_windows.constEnd(); ++i) { | 136 | for (i = m_windows.constBegin(); i != m_windows.constEnd(); ++i) { | ||
192 | updateShadow(i.key(), i.value()); | 137 | updateShadow(i.key(), i.value()); | ||
193 | } | 138 | } | ||
194 | } | 139 | } | ||
195 | 140 | | |||
196 | Qt::HANDLE DialogShadows::Private::createPixmap(const QPixmap &source) | 141 | void DialogShadows::Private::initTile(const QString &element) | ||
197 | { | 142 | { | ||
143 | const QImage image = q->pixmap(element).toImage(); | ||||
198 | 144 | | |||
199 | // do nothing for invalid pixmaps | 145 | KWindowShadowTile::Ptr tile = KWindowShadowTile::Ptr::create(); | ||
200 | if (source.isNull()) { | 146 | tile->setImage(image); | ||
201 | return nullptr; | | |||
202 | } | | |||
203 | | ||||
204 | /* | | |||
205 | in some cases, pixmap handle is invalid. This is the case notably | | |||
206 | when Qt uses to RasterEngine. In this case, we create an X11 Pixmap | | |||
207 | explicitly and draw the source pixmap on it. | | |||
208 | */ | | |||
209 | | ||||
210 | #if HAVE_X11 | | |||
211 | if (!m_isX11) { | | |||
212 | return nullptr; | | |||
213 | } | | |||
214 | | ||||
215 | // check connection | | |||
216 | if (!_connection) { | | |||
217 | _connection = QX11Info::connection(); | | |||
218 | } | | |||
219 | | ||||
220 | const int width(source.width()); | | |||
221 | const int height(source.height()); | | |||
222 | | ||||
223 | // create X11 pixmap | | |||
224 | Pixmap pixmap = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), width, height, 32); | | |||
225 | 147 | | |||
226 | // check gc | 148 | m_tiles << tile; | ||
227 | if (!_gc) { | | |||
228 | _gc = xcb_generate_id(_connection); | | |||
229 | xcb_create_gc(_connection, _gc, pixmap, 0, nullptr); | | |||
230 | } | 149 | } | ||
231 | 150 | | |||
232 | // // create explicitly shared QPixmap from it | 151 | void DialogShadows::Private::setupTiles() | ||
233 | // QPixmap dest( QPixmap::fromX11Pixmap( pixmap, QPixmap::ExplicitlyShared ) ); | 152 | { | ||
234 | // | 153 | clearTiles(); | ||
235 | // // create surface for pixmap | | |||
236 | // { | | |||
237 | // QPainter painter( &dest ); | | |||
238 | // painter.setCompositionMode( QPainter::CompositionMode_Source ); | | |||
239 | // painter.drawPixmap( 0, 0, source ); | | |||
240 | // } | | |||
241 | // | | |||
242 | // | | |||
243 | // return pixmap; | | |||
244 | QImage image(source.toImage()); | | |||
245 | xcb_put_image( | | |||
246 | _connection, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, _gc, | | |||
247 | image.width(), image.height(), 0, 0, | | |||
248 | 0, 32, | | |||
249 | image.sizeInBytes(), image.constBits()); | | |||
250 | | ||||
251 | return (Qt::HANDLE)pixmap; | | |||
252 | | ||||
253 | #else | | |||
254 | return nullptr; | | |||
255 | #endif | | |||
256 | 154 | | |||
155 | initTile(QStringLiteral("shadow-top")); | ||||
156 | initTile(QStringLiteral("shadow-topright")); | ||||
157 | initTile(QStringLiteral("shadow-right")); | ||||
158 | initTile(QStringLiteral("shadow-bottomright")); | ||||
159 | initTile(QStringLiteral("shadow-bottom")); | ||||
160 | initTile(QStringLiteral("shadow-bottomleft")); | ||||
161 | initTile(QStringLiteral("shadow-left")); | ||||
162 | initTile(QStringLiteral("shadow-topleft")); | ||||
257 | } | 163 | } | ||
258 | 164 | | |||
259 | void DialogShadows::Private::initPixmap(const QString &element) | 165 | void DialogShadows::Private::clearTiles() | ||
260 | { | 166 | { | ||
261 | m_shadowPixmaps << q->pixmap(element); | 167 | m_tiles.clear(); | ||
262 | } | 168 | } | ||
263 | 169 | | |||
264 | QPixmap DialogShadows::Private::initEmptyPixmap(const QSize &size) | 170 | void DialogShadows::Private::updateShadow(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | ||
265 | { | 171 | { | ||
266 | #if HAVE_X11 | 172 | if (m_tiles.isEmpty()) { | ||
267 | if (!m_isX11) { | 173 | setupTiles(); | ||
268 | return QPixmap(); | | |||
269 | } | | |||
270 | QPixmap tempEmptyPix(size); | | |||
271 | if (!size.isEmpty()) { | | |||
272 | tempEmptyPix.fill(Qt::transparent); | | |||
273 | } | | |||
274 | return tempEmptyPix; | | |||
275 | #else | | |||
276 | Q_UNUSED(size) | | |||
277 | return QPixmap(); | | |||
278 | #endif | | |||
279 | } | 174 | } | ||
280 | 175 | | |||
281 | void DialogShadows::Private::setupPixmaps() | 176 | KWindowShadow *&shadow = m_shadows[window]; | ||
282 | { | | |||
283 | clearPixmaps(); | | |||
284 | initPixmap(QStringLiteral("shadow-top")); | | |||
285 | initPixmap(QStringLiteral("shadow-topright")); | | |||
286 | initPixmap(QStringLiteral("shadow-right")); | | |||
287 | initPixmap(QStringLiteral("shadow-bottomright")); | | |||
288 | initPixmap(QStringLiteral("shadow-bottom")); | | |||
289 | initPixmap(QStringLiteral("shadow-bottomleft")); | | |||
290 | initPixmap(QStringLiteral("shadow-left")); | | |||
291 | initPixmap(QStringLiteral("shadow-topleft")); | | |||
292 | 177 | | |||
293 | m_emptyCornerPix = initEmptyPixmap(QSize(1, 1)); | 178 | if (!shadow) { | ||
294 | m_emptyCornerLeftPix = initEmptyPixmap(QSize(q->elementSize(QStringLiteral("shadow-topleft")).width(), 1)); | 179 | shadow = new KWindowShadow(); | ||
295 | m_emptyCornerTopPix = initEmptyPixmap(QSize(1, q->elementSize(QStringLiteral("shadow-topleft")).height())); | | |||
296 | m_emptyCornerRightPix = initEmptyPixmap(QSize(q->elementSize(QStringLiteral("shadow-bottomright")).width(), 1)); | | |||
297 | m_emptyCornerBottomPix = initEmptyPixmap(QSize(1, q->elementSize(QStringLiteral("shadow-bottomright")).height())); | | |||
298 | m_emptyVerticalPix = initEmptyPixmap(QSize(1, q->elementSize(QStringLiteral("shadow-left")).height())); | | |||
299 | m_emptyHorizontalPix = initEmptyPixmap(QSize(q->elementSize(QStringLiteral("shadow-top")).width(), 1)); | | |||
300 | | ||||
301 | #if HAVE_KWAYLAND | | |||
302 | if (KWayland::Client::ShmPool *shmPool = WaylandIntegration::self()->waylandShmPool()) { | | |||
303 | for (auto it = m_shadowPixmaps.constBegin(); it != m_shadowPixmaps.constEnd(); ++it) { | | |||
304 | m_wayland.shadowBuffers << shmPool->createBuffer(it->toImage()); | | |||
305 | } | | |||
306 | } | | |||
307 | #endif | | |||
308 | } | 180 | } | ||
309 | 181 | | |||
310 | void DialogShadows::Private::setupData(Plasma::FrameSvg::EnabledBorders enabledBorders) | 182 | if (shadow->isCreated()) { | ||
311 | { | 183 | shadow->destroy(); | ||
312 | #if HAVE_X11 | | |||
313 | if (!m_isX11) { | | |||
314 | return; | | |||
315 | } | 184 | } | ||
316 | //shadow-top | 185 | | ||
317 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | 186 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | ||
318 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[0])); | 187 | shadow->setTopTile(m_tiles.at(0)); | ||
319 | } else { | 188 | } else { | ||
320 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyHorizontalPix)); | 189 | shadow->setTopTile(nullptr); | ||
321 | } | 190 | } | ||
322 | 191 | | |||
323 | //shadow-topright | | |||
324 | if (enabledBorders & Plasma::FrameSvg::TopBorder && | 192 | if (enabledBorders & Plasma::FrameSvg::TopBorder && | ||
325 | enabledBorders & Plasma::FrameSvg::RightBorder) { | 193 | enabledBorders & Plasma::FrameSvg::RightBorder) { | ||
326 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[1])); | 194 | shadow->setTopRightTile(m_tiles.at(1)); | ||
327 | } else if (enabledBorders & Plasma::FrameSvg::TopBorder) { | | |||
328 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerTopPix)); | | |||
329 | } else if (enabledBorders & Plasma::FrameSvg::RightBorder) { | | |||
330 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerRightPix)); | | |||
331 | } else { | 195 | } else { | ||
332 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix)); | 196 | shadow->setTopRightTile(nullptr); | ||
333 | } | 197 | } | ||
334 | 198 | | |||
335 | //shadow-right | | |||
336 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | 199 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | ||
337 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[2])); | 200 | shadow->setRightTile(m_tiles.at(2)); | ||
338 | } else { | 201 | } else { | ||
339 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyVerticalPix)); | 202 | shadow->setRightTile(nullptr); | ||
340 | } | 203 | } | ||
341 | 204 | | |||
342 | //shadow-bottomright | | |||
343 | if (enabledBorders & Plasma::FrameSvg::BottomBorder && | 205 | if (enabledBorders & Plasma::FrameSvg::BottomBorder && | ||
344 | enabledBorders & Plasma::FrameSvg::RightBorder) { | 206 | enabledBorders & Plasma::FrameSvg::RightBorder) { | ||
345 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[3])); | 207 | shadow->setBottomRightTile(m_tiles.at(3)); | ||
346 | } else if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | | |||
347 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerBottomPix)); | | |||
348 | } else if (enabledBorders & Plasma::FrameSvg::RightBorder) { | | |||
349 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerRightPix)); | | |||
350 | } else { | 208 | } else { | ||
351 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix)); | 209 | shadow->setBottomRightTile(nullptr); | ||
352 | } | 210 | } | ||
353 | 211 | | |||
354 | //shadow-bottom | | |||
355 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | 212 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | ||
356 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[4])); | 213 | shadow->setBottomTile(m_tiles.at(4)); | ||
357 | } else { | 214 | } else { | ||
358 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyHorizontalPix)); | 215 | shadow->setBottomTile(nullptr); | ||
359 | } | 216 | } | ||
360 | 217 | | |||
361 | //shadow-bottomleft | | |||
362 | if (enabledBorders & Plasma::FrameSvg::BottomBorder && | 218 | if (enabledBorders & Plasma::FrameSvg::BottomBorder && | ||
363 | enabledBorders & Plasma::FrameSvg::LeftBorder) { | 219 | enabledBorders & Plasma::FrameSvg::LeftBorder) { | ||
364 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[5])); | 220 | shadow->setBottomLeftTile(m_tiles.at(5)); | ||
365 | } else if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | | |||
366 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerBottomPix)); | | |||
367 | } else if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | | |||
368 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerLeftPix)); | | |||
369 | } else { | 221 | } else { | ||
370 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix)); | 222 | shadow->setBottomLeftTile(nullptr); | ||
371 | } | 223 | } | ||
372 | 224 | | |||
373 | //shadow-left | | |||
374 | if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | 225 | if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | ||
375 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[6])); | 226 | shadow->setLeftTile(m_tiles.at(6)); | ||
376 | } else { | 227 | } else { | ||
377 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyVerticalPix)); | 228 | shadow->setLeftTile(nullptr); | ||
378 | } | 229 | } | ||
379 | 230 | | |||
380 | //shadow-topleft | | |||
381 | if (enabledBorders & Plasma::FrameSvg::TopBorder && | 231 | if (enabledBorders & Plasma::FrameSvg::TopBorder && | ||
382 | enabledBorders & Plasma::FrameSvg::LeftBorder) { | 232 | enabledBorders & Plasma::FrameSvg::LeftBorder) { | ||
383 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[7])); | 233 | shadow->setTopLeftTile(m_tiles.at(7)); | ||
384 | } else if (enabledBorders & Plasma::FrameSvg::TopBorder) { | | |||
385 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerTopPix)); | | |||
386 | } else if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | | |||
387 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerLeftPix)); | | |||
388 | } else { | 234 | } else { | ||
389 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix)); | 235 | shadow->setTopLeftTile(nullptr); | ||
390 | } | 236 | } | ||
391 | #endif | | |||
392 | 237 | | |||
393 | int left, top, right, bottom = 0; | 238 | QMargins padding; | ||
394 | 239 | | |||
395 | QSize marginHint; | | |||
396 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | 240 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | ||
397 | marginHint = q->elementSize(QStringLiteral("shadow-hint-top-margin")); | 241 | const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-top-margin")); | ||
398 | if (marginHint.isValid()) { | 242 | if (marginHint.isValid()) { | ||
399 | top = marginHint.height(); | 243 | padding.setTop(marginHint.height()); | ||
400 | } else { | 244 | } else { | ||
401 | top = m_shadowPixmaps[0].height(); // top | 245 | padding.setTop(m_tiles[0]->image().height()); | ||
402 | } | 246 | } | ||
403 | } else { | | |||
404 | top = 1; | | |||
405 | } | 247 | } | ||
406 | 248 | | |||
407 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | 249 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | ||
408 | marginHint = q->elementSize(QStringLiteral("shadow-hint-right-margin")); | 250 | const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-right-margin")); | ||
409 | if (marginHint.isValid()) { | 251 | if (marginHint.isValid()) { | ||
410 | right = marginHint.width(); | 252 | padding.setRight(marginHint.width()); | ||
411 | } else { | 253 | } else { | ||
412 | right = m_shadowPixmaps[2].width(); // right | 254 | padding.setRight(m_tiles[2]->image().width()); | ||
413 | } | 255 | } | ||
414 | } else { | | |||
415 | right = 1; | | |||
416 | } | 256 | } | ||
417 | 257 | | |||
418 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | 258 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | ||
419 | marginHint = q->elementSize(QStringLiteral("shadow-hint-bottom-margin")); | 259 | const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-bottom-margin")); | ||
420 | if (marginHint.isValid()) { | | |||
421 | bottom = marginHint.height(); | | |||
422 | } else { | | |||
423 | bottom = m_shadowPixmaps[4].height(); // bottom | | |||
424 | } | | |||
425 | } else { | | |||
426 | bottom = 1; | | |||
427 | } | | |||
428 | | ||||
429 | if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | | |||
430 | marginHint = q->elementSize(QStringLiteral("shadow-hint-left-margin")); | | |||
431 | if (marginHint.isValid()) { | 260 | if (marginHint.isValid()) { | ||
432 | left = marginHint.width(); | 261 | padding.setBottom(marginHint.height()); | ||
433 | } else { | 262 | } else { | ||
434 | left = m_shadowPixmaps[6].width(); // left | 263 | padding.setBottom(m_tiles[4]->image().height()); | ||
435 | } | | |||
436 | } else { | | |||
437 | left = 1; | | |||
438 | } | | |||
439 | | ||||
440 | data[enabledBorders] << top << right << bottom << left; | | |||
441 | } | | |||
442 | | ||||
443 | void DialogShadows::Private::freeX11Pixmaps() | | |||
444 | { | | |||
445 | #if HAVE_X11 | | |||
446 | if (!m_isX11) { | | |||
447 | return; | | |||
448 | } | | |||
449 | | ||||
450 | auto *display = QX11Info::display(); | | |||
451 | if (!display) { | | |||
452 | return; | | |||
453 | } | | |||
454 | | ||||
455 | foreach (const QPixmap &pixmap, m_shadowPixmaps) { | | |||
456 | if (!pixmap.isNull()) { | | |||
457 | XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(pixmap))); | | |||
458 | } | | |||
459 | } | | |||
460 | | ||||
461 | if (!m_emptyCornerPix.isNull()) { | | |||
462 | XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix))); | | |||
463 | } | | |||
464 | if (!m_emptyCornerBottomPix.isNull()) { | | |||
465 | XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerBottomPix))); | | |||
466 | } | | |||
467 | if (!m_emptyCornerLeftPix.isNull()) { | | |||
468 | XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerLeftPix))); | | |||
469 | } | | |||
470 | if (!m_emptyCornerRightPix.isNull()) { | | |||
471 | XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerRightPix))); | | |||
472 | } | | |||
473 | if (!m_emptyCornerTopPix.isNull()) { | | |||
474 | XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerTopPix))); | | |||
475 | } | | |||
476 | if (!m_emptyVerticalPix.isNull()) { | | |||
477 | XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyVerticalPix))); | | |||
478 | } | | |||
479 | if (!m_emptyHorizontalPix.isNull()) { | | |||
480 | XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyHorizontalPix))); | | |||
481 | } | | |||
482 | #endif | | |||
483 | } | | |||
484 | | ||||
485 | void DialogShadows::Private::clearPixmaps() | | |||
486 | { | | |||
487 | #if HAVE_X11 | | |||
488 | freeX11Pixmaps(); | | |||
489 | | ||||
490 | m_emptyCornerPix = QPixmap(); | | |||
491 | m_emptyCornerBottomPix = QPixmap(); | | |||
492 | m_emptyCornerLeftPix = QPixmap(); | | |||
493 | m_emptyCornerRightPix = QPixmap(); | | |||
494 | m_emptyCornerTopPix = QPixmap(); | | |||
495 | m_emptyVerticalPix = QPixmap(); | | |||
496 | m_emptyHorizontalPix = QPixmap(); | | |||
497 | #endif | | |||
498 | freeWaylandBuffers(); | | |||
499 | m_shadowPixmaps.clear(); | | |||
500 | data.clear(); | | |||
501 | } | | |||
502 | | ||||
503 | void DialogShadows::Private::freeWaylandBuffers() | | |||
504 | { | | |||
505 | #if HAVE_KWAYLAND | | |||
506 | m_wayland.shadowBuffers.clear(); | | |||
507 | #endif | | |||
508 | } | | |||
509 | | ||||
510 | void DialogShadows::Private::updateShadow(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | | |||
511 | { | | |||
512 | #if HAVE_X11 | | |||
513 | if (m_isX11) { | | |||
514 | updateShadowX11(window, enabledBorders); | | |||
515 | } | | |||
516 | #endif | | |||
517 | #if HAVE_KWAYLAND | | |||
518 | if (WaylandIntegration::self()->waylandShadowManager()) { | | |||
519 | updateShadowWayland(window, enabledBorders); | | |||
520 | } | 264 | } | ||
521 | #endif | | |||
522 | } | 265 | } | ||
523 | 266 | | |||
524 | void DialogShadows::Private::updateShadowX11(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | | |||
525 | { | | |||
526 | #if HAVE_X11 | | |||
527 | if (m_shadowPixmaps.isEmpty()) { | | |||
528 | setupPixmaps(); | | |||
529 | } | | |||
530 | | ||||
531 | if (!data.contains(enabledBorders)) { | | |||
532 | setupData(enabledBorders); | | |||
533 | } | | |||
534 | | ||||
535 | Display *dpy = QX11Info::display(); | | |||
536 | Atom atom = XInternAtom(dpy, "_KDE_NET_WM_SHADOW", False); | | |||
537 | | ||||
538 | //qDebug() << "going to set the shadow of" << window->winId() << "to" << data; | | |||
539 | XChangeProperty(dpy, window->winId(), atom, XA_CARDINAL, 32, PropModeReplace, | | |||
540 | reinterpret_cast<const unsigned char *>(data[enabledBorders].constData()), data[enabledBorders].size()); | | |||
541 | #endif | | |||
542 | } | | |||
543 | | ||||
544 | void DialogShadows::Private::updateShadowWayland(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | | |||
545 | { | | |||
546 | #if HAVE_KWAYLAND | | |||
547 | if (!WaylandIntegration::self()->waylandShmPool()) { | | |||
548 | return; | | |||
549 | } | | |||
550 | if (m_wayland.shadowBuffers.isEmpty()) { | | |||
551 | setupPixmaps(); | | |||
552 | } | | |||
553 | // TODO: check whether the surface already has a shadow | | |||
554 | KWayland::Client::Surface *surface = KWayland::Client::Surface::fromWindow(const_cast<QWindow*>(window)); | | |||
555 | if (!surface) { | | |||
556 | return; | | |||
557 | } | | |||
558 | KWayland::Client::ShadowManager *manager = WaylandIntegration::self()->waylandShadowManager(); | | |||
559 | auto shadow = manager->createShadow(surface, surface); | | |||
560 | | ||||
561 | //shadow-top | | |||
562 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | | |||
563 | shadow->attachTop(m_wayland.shadowBuffers.at(0)); | | |||
564 | } | | |||
565 | | ||||
566 | //shadow-topright | | |||
567 | if (enabledBorders & Plasma::FrameSvg::TopBorder && | | |||
568 | enabledBorders & Plasma::FrameSvg::RightBorder) { | | |||
569 | shadow->attachTopRight(m_wayland.shadowBuffers.at(1)); | | |||
570 | } | | |||
571 | | ||||
572 | //shadow-right | | |||
573 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | | |||
574 | shadow->attachRight(m_wayland.shadowBuffers.at(2)); | | |||
575 | } | | |||
576 | | ||||
577 | //shadow-bottomright | | |||
578 | if (enabledBorders & Plasma::FrameSvg::BottomBorder && | | |||
579 | enabledBorders & Plasma::FrameSvg::RightBorder) { | | |||
580 | shadow->attachBottomRight(m_wayland.shadowBuffers.at(3)); | | |||
581 | } | | |||
582 | | ||||
583 | //shadow-bottom | | |||
584 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | | |||
585 | shadow->attachBottom(m_wayland.shadowBuffers.at(4)); | | |||
586 | } | | |||
587 | | ||||
588 | //shadow-bottomleft | | |||
589 | if (enabledBorders & Plasma::FrameSvg::BottomBorder && | | |||
590 | enabledBorders & Plasma::FrameSvg::LeftBorder) { | | |||
591 | shadow->attachBottomLeft(m_wayland.shadowBuffers.at(5)); | | |||
592 | } | | |||
593 | | ||||
594 | //shadow-left | | |||
595 | if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | 267 | if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | ||
596 | shadow->attachLeft(m_wayland.shadowBuffers.at(6)); | 268 | const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-left-margin")); | ||
597 | } | | |||
598 | | ||||
599 | //shadow-topleft | | |||
600 | if (enabledBorders & Plasma::FrameSvg::TopBorder && | | |||
601 | enabledBorders & Plasma::FrameSvg::LeftBorder) { | | |||
602 | shadow->attachTopLeft(m_wayland.shadowBuffers.at(7)); | | |||
603 | } | | |||
604 | | ||||
605 | QSize marginHint; | | |||
606 | QMarginsF margins; | | |||
607 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | | |||
608 | marginHint = q->elementSize(QStringLiteral("shadow-hint-top-margin")); | | |||
609 | if (marginHint.isValid()) { | 269 | if (marginHint.isValid()) { | ||
610 | margins.setTop(marginHint.height()); | 270 | padding.setLeft(marginHint.width()); | ||
611 | } else { | 271 | } else { | ||
612 | margins.setTop(m_shadowPixmaps[0].height()); | 272 | padding.setLeft(m_tiles[6]->image().width()); | ||
613 | } | 273 | } | ||
614 | } | 274 | } | ||
615 | 275 | | |||
616 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | 276 | shadow->setPadding(padding); | ||
617 | marginHint = q->elementSize(QStringLiteral("shadow-hint-right-margin")); | 277 | shadow->setWindow(const_cast<QWindow *>(window)); | ||
618 | if (marginHint.isValid()) { | | |||
619 | margins.setRight(marginHint.width()); | | |||
620 | } else { | | |||
621 | margins.setRight(m_shadowPixmaps[2].width()); | | |||
622 | } | | |||
623 | } | | |||
624 | 278 | | |||
625 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | 279 | if (!shadow->create()) { | ||
626 | marginHint = q->elementSize(QStringLiteral("shadow-hint-bottom-margin")); | 280 | qCWarning(LOG_PLASMAQUICK) << "Couldn't create KWindowShadow for" << window; | ||
627 | if (marginHint.isValid()) { | | |||
628 | margins.setBottom(marginHint.height()); | | |||
629 | } else { | | |||
630 | margins.setBottom(m_shadowPixmaps[4].height()); | | |||
631 | } | 281 | } | ||
632 | } | 282 | } | ||
633 | 283 | | |||
634 | if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | | |||
635 | marginHint = q->elementSize(QStringLiteral("shadow-hint-left-margin")); | | |||
636 | if (marginHint.isValid()) { | | |||
637 | margins.setLeft(marginHint.width()); | | |||
638 | } else { | | |||
639 | margins.setLeft(m_shadowPixmaps[6].width()); | | |||
640 | } | | |||
641 | } | | |||
642 | | ||||
643 | shadow->setOffsets(margins); | | |||
644 | shadow->commit(); | | |||
645 | surface->commit(KWayland::Client::Surface::CommitFlag::None); | | |||
646 | #endif | | |||
647 | } | | |||
648 | | ||||
649 | void DialogShadows::Private::clearShadow(const QWindow *window) | 284 | void DialogShadows::Private::clearShadow(const QWindow *window) | ||
650 | { | 285 | { | ||
651 | if (!static_cast<const QSurface*>(window)->surfaceHandle()) { | 286 | delete m_shadows.take(window); | ||
652 | qWarning() << "Cannot clear shadow from window without native surface!"; | | |||
653 | return; | | |||
654 | } | | |||
655 | #if HAVE_X11 | | |||
656 | if (m_isX11) { | | |||
657 | clearShadowX11(window); | | |||
658 | } | | |||
659 | #endif | | |||
660 | #if HAVE_KWAYLAND | | |||
661 | if (WaylandIntegration::self()->waylandShadowManager()) { | | |||
662 | clearShadowWayland(window); | | |||
663 | } | | |||
664 | #endif | | |||
665 | } | | |||
666 | | ||||
667 | void DialogShadows::Private::clearShadowX11(const QWindow* window) | | |||
668 | { | | |||
669 | #if HAVE_X11 | | |||
670 | Display *dpy = QX11Info::display(); | | |||
671 | Atom atom = XInternAtom(dpy, "_KDE_NET_WM_SHADOW", False); | | |||
672 | XDeleteProperty(dpy, window->winId(), atom); | | |||
673 | #endif | | |||
674 | } | | |||
675 | | ||||
676 | void DialogShadows::Private::clearShadowWayland(const QWindow *window) | | |||
677 | { | | |||
678 | #if HAVE_KWAYLAND | | |||
679 | KWayland::Client::Surface *surface = KWayland::Client::Surface::fromWindow(const_cast<QWindow*>(window)); | | |||
680 | if (!surface) { | | |||
681 | return; | | |||
682 | } | | |||
683 | KWayland::Client::ShadowManager *manager = WaylandIntegration::self()->waylandShadowManager(); | | |||
684 | manager->removeShadow(surface); | | |||
685 | surface->commit(KWayland::Client::Surface::CommitFlag::None); | | |||
686 | #endif | | |||
687 | } | 287 | } | ||
688 | 288 | | |||
689 | bool DialogShadows::enabled() const | 289 | bool DialogShadows::enabled() const | ||
690 | { | 290 | { | ||
691 | return hasElement(QStringLiteral("shadow-left")); | 291 | return hasElement(QStringLiteral("shadow-left")); | ||
692 | } | 292 | } | ||
693 | 293 | | |||
694 | #include "moc_dialogshadows_p.cpp" | 294 | #include "moc_dialogshadows_p.cpp" | ||
695 | 295 | |