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 <vlad.zahorodnii@kde.org> | ||||
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 | } | ||
59 | // which causes a crash when application is stopping. | 36 | | ||
60 | freeX11Pixmaps(); | 37 | void clearTiles(); | ||
61 | } | 38 | void setupTiles(); | ||
62 | 39 | void initTile(const QString &element); | |||
63 | void freeX11Pixmaps(); | 40 | void updateShadow(QWindow *window, Plasma::FrameSvg::EnabledBorders); | ||
64 | void freeWaylandBuffers(); | 41 | void clearShadow(QWindow *window); | ||
65 | void clearPixmaps(); | | |||
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); | | |||
71 | void updateShadowX11(const QWindow *window, Plasma::FrameSvg::EnabledBorders); | | |||
72 | void updateShadowWayland(const QWindow *window, Plasma::FrameSvg::EnabledBorders); | | |||
73 | void clearShadow(const QWindow *window); | | |||
74 | void clearShadowX11(const QWindow *window); | | |||
75 | void clearShadowWayland(const QWindow *window); | | |||
76 | void updateShadows(); | 42 | void updateShadows(); | ||
77 | void windowDestroyed(QObject *deletedObject); | 43 | void windowDestroyed(QObject *deletedObject); | ||
78 | void setupData(Plasma::FrameSvg::EnabledBorders enabledBorders); | | |||
79 | 44 | | |||
80 | DialogShadows *q; | 45 | 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 | 46 | | |||
107 | QHash<Plasma::FrameSvg::EnabledBorders, QVector<unsigned long> > data; | 47 | QHash<QWindow *, Plasma::FrameSvg::EnabledBorders> m_windows; | ||
108 | QHash<const QWindow *, Plasma::FrameSvg::EnabledBorders> m_windows; | 48 | QHash<QWindow *, KWindowShadow *> m_shadows; | ||
49 | QVector<KWindowShadowTile::Ptr> m_tiles; | ||||
109 | }; | 50 | }; | ||
110 | 51 | | |||
111 | class DialogShadowsSingleton | 52 | class DialogShadowsSingleton | ||
112 | { | 53 | { | ||
113 | public: | 54 | public: | ||
114 | DialogShadowsSingleton() | 55 | DialogShadowsSingleton() | ||
115 | { | 56 | { | ||
116 | } | 57 | } | ||
Show All 16 Lines | 73 | { | |||
133 | delete d; | 74 | delete d; | ||
134 | } | 75 | } | ||
135 | 76 | | |||
136 | DialogShadows *DialogShadows::self() | 77 | DialogShadows *DialogShadows::self() | ||
137 | { | 78 | { | ||
138 | return &privateDialogShadowsSelf->self; | 79 | return &privateDialogShadowsSelf->self; | ||
139 | } | 80 | } | ||
140 | 81 | | |||
141 | void DialogShadows::addWindow(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | 82 | void DialogShadows::addWindow(QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | ||
142 | { | 83 | { | ||
143 | if (!window) { | 84 | if (!window) { | ||
144 | return; | 85 | return; | ||
145 | } | 86 | } | ||
146 | 87 | | |||
147 | d->m_windows[window] = enabledBorders; | 88 | d->m_windows[window] = enabledBorders; | ||
148 | d->updateShadow(window, enabledBorders); | 89 | d->updateShadow(window, enabledBorders); | ||
149 | connect(window, SIGNAL(destroyed(QObject*)), | 90 | connect(window, SIGNAL(destroyed(QObject*)), | ||
150 | this, SLOT(windowDestroyed(QObject*)), Qt::UniqueConnection); | 91 | this, SLOT(windowDestroyed(QObject*)), Qt::UniqueConnection); | ||
151 | } | 92 | } | ||
152 | 93 | | |||
153 | void DialogShadows::removeWindow(const QWindow *window) | 94 | void DialogShadows::removeWindow(QWindow *window) | ||
154 | { | 95 | { | ||
155 | if (!d->m_windows.contains(window)) { | 96 | if (!d->m_windows.contains(window)) { | ||
156 | return; | 97 | return; | ||
157 | } | 98 | } | ||
158 | 99 | | |||
159 | d->m_windows.remove(window); | 100 | d->m_windows.remove(window); | ||
160 | disconnect(window, nullptr, this, nullptr); | 101 | disconnect(window, nullptr, this, nullptr); | ||
161 | d->clearShadow(window); | 102 | d->clearShadow(window); | ||
162 | 103 | | |||
163 | if (d->m_windows.isEmpty()) { | 104 | if (d->m_windows.isEmpty()) { | ||
164 | d->clearPixmaps(); | 105 | d->clearTiles(); | ||
165 | } | 106 | } | ||
166 | } | 107 | } | ||
167 | 108 | | |||
168 | void DialogShadows::setEnabledBorders(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | 109 | void DialogShadows::setEnabledBorders(QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | ||
169 | { | 110 | { | ||
170 | if (!window || !d->m_windows.contains(window)) { | 111 | if (!window || !d->m_windows.contains(window)) { | ||
171 | return; | 112 | return; | ||
172 | } | 113 | } | ||
173 | 114 | | |||
174 | d->updateShadow(window, enabledBorders); | 115 | d->updateShadow(window, enabledBorders); | ||
175 | } | 116 | } | ||
176 | 117 | | |||
177 | 118 | | |||
178 | void DialogShadows::Private::windowDestroyed(QObject *deletedObject) | 119 | void DialogShadows::Private::windowDestroyed(QObject *deletedObject) | ||
179 | { | 120 | { | ||
180 | m_windows.remove(static_cast<QWindow *>(deletedObject)); | 121 | QWindow *window = static_cast<QWindow *>(deletedObject); | ||
122 | | ||||
123 | m_windows.remove(window); | ||||
124 | clearShadow(window); | ||||
181 | 125 | | |||
182 | if (m_windows.isEmpty()) { | 126 | if (m_windows.isEmpty()) { | ||
183 | clearPixmaps(); | 127 | clearTiles(); | ||
184 | } | 128 | } | ||
185 | } | 129 | } | ||
186 | 130 | | |||
187 | void DialogShadows::Private::updateShadows() | 131 | void DialogShadows::Private::updateShadows() | ||
188 | { | 132 | { | ||
189 | setupPixmaps(); | 133 | setupTiles(); | ||
190 | QHash<const QWindow *, Plasma::FrameSvg::EnabledBorders>::const_iterator i; | 134 | QHash<QWindow *, Plasma::FrameSvg::EnabledBorders>::const_iterator i; | ||
191 | for (i = m_windows.constBegin(); i != m_windows.constEnd(); ++i) { | 135 | for (i = m_windows.constBegin(); i != m_windows.constEnd(); ++i) { | ||
192 | updateShadow(i.key(), i.value()); | 136 | updateShadow(i.key(), i.value()); | ||
193 | } | 137 | } | ||
194 | } | 138 | } | ||
195 | 139 | | |||
196 | Qt::HANDLE DialogShadows::Private::createPixmap(const QPixmap &source) | 140 | void DialogShadows::Private::initTile(const QString &element) | ||
197 | { | 141 | { | ||
142 | const QImage image = q->pixmap(element).toImage(); | ||||
198 | 143 | | |||
199 | // do nothing for invalid pixmaps | 144 | KWindowShadowTile::Ptr tile = KWindowShadowTile::Ptr::create(); | ||
200 | if (source.isNull()) { | 145 | 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 | 146 | | |||
215 | // check connection | 147 | m_tiles << tile; | ||
216 | if (!_connection) { | | |||
217 | _connection = QX11Info::connection(); | | |||
218 | } | 148 | } | ||
219 | 149 | | |||
220 | const int width(source.width()); | 150 | void DialogShadows::Private::setupTiles() | ||
221 | const int height(source.height()); | 151 | { | ||
222 | 152 | clearTiles(); | |||
223 | // create X11 pixmap | | |||
224 | Pixmap pixmap = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), width, height, 32); | | |||
225 | | ||||
226 | // check gc | | |||
227 | if (!_gc) { | | |||
228 | _gc = xcb_generate_id(_connection); | | |||
229 | xcb_create_gc(_connection, _gc, pixmap, 0, nullptr); | | |||
230 | } | | |||
231 | | ||||
232 | // // create explicitly shared QPixmap from it | | |||
233 | // QPixmap dest( QPixmap::fromX11Pixmap( pixmap, QPixmap::ExplicitlyShared ) ); | | |||
234 | // | | |||
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 | 153 | | |||
154 | initTile(QStringLiteral("shadow-top")); | ||||
155 | initTile(QStringLiteral("shadow-topright")); | ||||
156 | initTile(QStringLiteral("shadow-right")); | ||||
157 | initTile(QStringLiteral("shadow-bottomright")); | ||||
158 | initTile(QStringLiteral("shadow-bottom")); | ||||
159 | initTile(QStringLiteral("shadow-bottomleft")); | ||||
160 | initTile(QStringLiteral("shadow-left")); | ||||
161 | initTile(QStringLiteral("shadow-topleft")); | ||||
257 | } | 162 | } | ||
258 | 163 | | |||
259 | void DialogShadows::Private::initPixmap(const QString &element) | 164 | void DialogShadows::Private::clearTiles() | ||
260 | { | 165 | { | ||
261 | m_shadowPixmaps << q->pixmap(element); | 166 | m_tiles.clear(); | ||
262 | } | 167 | } | ||
263 | 168 | | |||
264 | QPixmap DialogShadows::Private::initEmptyPixmap(const QSize &size) | 169 | void DialogShadows::Private::updateShadow(QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | ||
265 | { | 170 | { | ||
266 | #if HAVE_X11 | 171 | if (m_tiles.isEmpty()) { | ||
267 | if (!m_isX11) { | 172 | 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 | } | 173 | } | ||
280 | 174 | | |||
281 | void DialogShadows::Private::setupPixmaps() | 175 | 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 | | ||||
293 | m_emptyCornerPix = initEmptyPixmap(QSize(1, 1)); | | |||
294 | m_emptyCornerLeftPix = initEmptyPixmap(QSize(q->elementSize(QStringLiteral("shadow-topleft")).width(), 1)); | | |||
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 | 176 | | |||
301 | #if HAVE_KWAYLAND | 177 | if (!shadow) { | ||
302 | if (KWayland::Client::ShmPool *shmPool = WaylandIntegration::self()->waylandShmPool()) { | 178 | shadow = new KWindowShadow(q); | ||
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 | } | 179 | } | ||
309 | 180 | | |||
310 | void DialogShadows::Private::setupData(Plasma::FrameSvg::EnabledBorders enabledBorders) | 181 | if (shadow->isCreated()) { | ||
311 | { | 182 | shadow->destroy(); | ||
312 | #if HAVE_X11 | | |||
313 | if (!m_isX11) { | | |||
314 | return; | | |||
315 | } | 183 | } | ||
316 | //shadow-top | 184 | | ||
317 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | 185 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | ||
318 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[0])); | 186 | shadow->setTopTile(m_tiles.at(0)); | ||
319 | } else { | 187 | } else { | ||
320 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyHorizontalPix)); | 188 | shadow->setTopTile(nullptr); | ||
321 | } | 189 | } | ||
322 | 190 | | |||
323 | //shadow-topright | | |||
324 | if (enabledBorders & Plasma::FrameSvg::TopBorder && | 191 | if (enabledBorders & Plasma::FrameSvg::TopBorder && | ||
325 | enabledBorders & Plasma::FrameSvg::RightBorder) { | 192 | enabledBorders & Plasma::FrameSvg::RightBorder) { | ||
326 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[1])); | 193 | 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 { | 194 | } else { | ||
332 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix)); | 195 | shadow->setTopRightTile(nullptr); | ||
333 | } | 196 | } | ||
334 | 197 | | |||
335 | //shadow-right | | |||
336 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | 198 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | ||
337 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[2])); | 199 | shadow->setRightTile(m_tiles.at(2)); | ||
338 | } else { | 200 | } else { | ||
339 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyVerticalPix)); | 201 | shadow->setRightTile(nullptr); | ||
340 | } | 202 | } | ||
341 | 203 | | |||
342 | //shadow-bottomright | | |||
343 | if (enabledBorders & Plasma::FrameSvg::BottomBorder && | 204 | if (enabledBorders & Plasma::FrameSvg::BottomBorder && | ||
344 | enabledBorders & Plasma::FrameSvg::RightBorder) { | 205 | enabledBorders & Plasma::FrameSvg::RightBorder) { | ||
345 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[3])); | 206 | 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 { | 207 | } else { | ||
351 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix)); | 208 | shadow->setBottomRightTile(nullptr); | ||
352 | } | 209 | } | ||
353 | 210 | | |||
354 | //shadow-bottom | | |||
355 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | 211 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | ||
356 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[4])); | 212 | shadow->setBottomTile(m_tiles.at(4)); | ||
357 | } else { | 213 | } else { | ||
358 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyHorizontalPix)); | 214 | shadow->setBottomTile(nullptr); | ||
359 | } | 215 | } | ||
360 | 216 | | |||
361 | //shadow-bottomleft | | |||
362 | if (enabledBorders & Plasma::FrameSvg::BottomBorder && | 217 | if (enabledBorders & Plasma::FrameSvg::BottomBorder && | ||
363 | enabledBorders & Plasma::FrameSvg::LeftBorder) { | 218 | enabledBorders & Plasma::FrameSvg::LeftBorder) { | ||
364 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[5])); | 219 | 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 { | 220 | } else { | ||
370 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix)); | 221 | shadow->setBottomLeftTile(nullptr); | ||
371 | } | 222 | } | ||
372 | 223 | | |||
373 | //shadow-left | | |||
374 | if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | 224 | if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | ||
375 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[6])); | 225 | shadow->setLeftTile(m_tiles.at(6)); | ||
376 | } else { | 226 | } else { | ||
377 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyVerticalPix)); | 227 | shadow->setLeftTile(nullptr); | ||
378 | } | 228 | } | ||
379 | 229 | | |||
380 | //shadow-topleft | | |||
381 | if (enabledBorders & Plasma::FrameSvg::TopBorder && | 230 | if (enabledBorders & Plasma::FrameSvg::TopBorder && | ||
382 | enabledBorders & Plasma::FrameSvg::LeftBorder) { | 231 | enabledBorders & Plasma::FrameSvg::LeftBorder) { | ||
383 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[7])); | 232 | 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 { | | |||
389 | data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix)); | | |||
390 | } | | |||
391 | #endif | | |||
392 | | ||||
393 | int left, top, right, bottom = 0; | | |||
394 | | ||||
395 | QSize marginHint; | | |||
396 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | | |||
397 | marginHint = q->elementSize(QStringLiteral("shadow-hint-top-margin")); | | |||
398 | if (marginHint.isValid()) { | | |||
399 | top = marginHint.height(); | | |||
400 | } else { | | |||
401 | top = m_shadowPixmaps[0].height(); // top | | |||
402 | } | | |||
403 | } else { | 233 | } else { | ||
404 | top = 1; | 234 | shadow->setTopLeftTile(nullptr); | ||
405 | } | | |||
406 | | ||||
407 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | | |||
408 | marginHint = q->elementSize(QStringLiteral("shadow-hint-right-margin")); | | |||
409 | if (marginHint.isValid()) { | | |||
410 | right = marginHint.width(); | | |||
411 | } else { | | |||
412 | right = m_shadowPixmaps[2].width(); // right | | |||
413 | } | | |||
414 | } else { | | |||
415 | right = 1; | | |||
416 | } | | |||
417 | | ||||
418 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | | |||
419 | 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()) { | | |||
432 | left = marginHint.width(); | | |||
433 | } else { | | |||
434 | left = m_shadowPixmaps[6].width(); // left | | |||
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 | for (const QPixmap &pixmap : qAsConst(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 | } | | |||
521 | #endif | | |||
522 | } | | |||
523 | | ||||
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 | } | 235 | } | ||
543 | 236 | | |||
544 | void DialogShadows::Private::updateShadowWayland(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) | 237 | QMargins padding; | ||
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 | 238 | | |||
561 | //shadow-top | | |||
562 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | 239 | if (enabledBorders & Plasma::FrameSvg::TopBorder) { | ||
563 | shadow->attachTop(m_wayland.shadowBuffers.at(0)); | 240 | const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-top-margin")); | ||
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) { | | |||
596 | shadow->attachLeft(m_wayland.shadowBuffers.at(6)); | | |||
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()) { | 241 | if (marginHint.isValid()) { | ||
610 | margins.setTop(marginHint.height()); | 242 | padding.setTop(marginHint.height()); | ||
611 | } else { | 243 | } else { | ||
612 | margins.setTop(m_shadowPixmaps[0].height()); | 244 | padding.setTop(m_tiles[0]->image().height()); | ||
613 | } | 245 | } | ||
614 | } | 246 | } | ||
615 | 247 | | |||
616 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | 248 | if (enabledBorders & Plasma::FrameSvg::RightBorder) { | ||
617 | marginHint = q->elementSize(QStringLiteral("shadow-hint-right-margin")); | 249 | const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-right-margin")); | ||
618 | if (marginHint.isValid()) { | 250 | if (marginHint.isValid()) { | ||
619 | margins.setRight(marginHint.width()); | 251 | padding.setRight(marginHint.width()); | ||
620 | } else { | 252 | } else { | ||
621 | margins.setRight(m_shadowPixmaps[2].width()); | 253 | padding.setRight(m_tiles[2]->image().width()); | ||
622 | } | 254 | } | ||
623 | } | 255 | } | ||
624 | 256 | | |||
625 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | 257 | if (enabledBorders & Plasma::FrameSvg::BottomBorder) { | ||
626 | marginHint = q->elementSize(QStringLiteral("shadow-hint-bottom-margin")); | 258 | const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-bottom-margin")); | ||
627 | if (marginHint.isValid()) { | 259 | if (marginHint.isValid()) { | ||
628 | margins.setBottom(marginHint.height()); | 260 | padding.setBottom(marginHint.height()); | ||
629 | } else { | 261 | } else { | ||
630 | margins.setBottom(m_shadowPixmaps[4].height()); | 262 | padding.setBottom(m_tiles[4]->image().height()); | ||
631 | } | 263 | } | ||
632 | } | 264 | } | ||
633 | 265 | | |||
634 | if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | 266 | if (enabledBorders & Plasma::FrameSvg::LeftBorder) { | ||
635 | marginHint = q->elementSize(QStringLiteral("shadow-hint-left-margin")); | 267 | const QSize marginHint = q->elementSize(QStringLiteral("shadow-hint-left-margin")); | ||
636 | if (marginHint.isValid()) { | 268 | if (marginHint.isValid()) { | ||
637 | margins.setLeft(marginHint.width()); | 269 | padding.setLeft(marginHint.width()); | ||
638 | } else { | 270 | } else { | ||
639 | margins.setLeft(m_shadowPixmaps[6].width()); | 271 | padding.setLeft(m_tiles[6]->image().width()); | ||
640 | } | 272 | } | ||
641 | } | 273 | } | ||
642 | 274 | | |||
643 | shadow->setOffsets(margins); | 275 | shadow->setPadding(padding); | ||
644 | shadow->commit(); | 276 | shadow->setWindow(window); | ||
645 | surface->commit(KWayland::Client::Surface::CommitFlag::None); | | |||
646 | #endif | | |||
647 | } | | |||
648 | 277 | | |||
649 | void DialogShadows::Private::clearShadow(const QWindow *window) | 278 | if (!shadow->create()) { | ||
650 | { | 279 | qCWarning(LOG_PLASMAQUICK) << "Couldn't create KWindowShadow for" << window; | ||
651 | if (!static_cast<const QSurface*>(window)->surfaceHandle()) { | | |||
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 | } | 280 | } | ||
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 | } | 281 | } | ||
675 | 282 | | |||
676 | void DialogShadows::Private::clearShadowWayland(const QWindow *window) | 283 | void DialogShadows::Private::clearShadow(QWindow *window) | ||
677 | { | 284 | { | ||
678 | #if HAVE_KWAYLAND | 285 | delete m_shadows.take(window); | ||
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 | } | 286 | } | ||
688 | 287 | | |||
689 | bool DialogShadows::enabled() const | 288 | bool DialogShadows::enabled() const | ||
690 | { | 289 | { | ||
691 | return hasElement(QStringLiteral("shadow-left")); | 290 | return hasElement(QStringLiteral("shadow-left")); | ||
692 | } | 291 | } | ||
693 | 292 | | |||
694 | #include "moc_dialogshadows_p.cpp" | 293 | #include "moc_dialogshadows_p.cpp" | ||
695 | 294 | |