File indexing completed on 2024-05-12 05:46:44

0001 /*
0002 *   Copyright 2011 by Aaron Seigo <aseigo@kde.org>
0003 *
0004 *   This program is free software; you can redistribute it and/or modify
0005 *   it under the terms of the GNU Library General Public License version 2,
0006 *   or (at your option) any later version.
0007 *
0008 *   This program is distributed in the hope that it will be useful,
0009 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0010 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0011 *   GNU General Public License for more details
0012 *
0013 *   You should have received a copy of the GNU Library General Public
0014 *   License along with this program; if not, write to the
0015 *   Free Software Foundation, Inc.,
0016 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
0017 */
0018 
0019 #include "dialogshadows_p.h"
0020 
0021 #include <QWindow>
0022 #include <QPainter>
0023 #include <config-plasma.h>
0024 
0025 #include <KWindowSystem>
0026 #if HAVE_X11
0027 #include <QX11Info>
0028 #include <X11/Xatom.h>
0029 #include <X11/Xlib.h>
0030 #include <X11/Xlib-xcb.h>
0031 #include <fixx11h.h>
0032 #endif
0033 
0034 #if HAVE_KWAYLAND
0035 #include <KWayland/Client/connection_thread.h>
0036 #include <KWayland/Client/registry.h>
0037 #include <KWayland/Client/shadow.h>
0038 #include <KWayland/Client/shm_pool.h>
0039 #include <KWayland/Client/surface.h>
0040 #include <KWayland/Client/plasmashell.h>
0041 #endif
0042 
0043 #include <qdebug.h>
0044 
0045 class DialogShadows::Private
0046 {
0047 public:
0048     Private(DialogShadows *shadows)
0049         : q(shadows)
0050 #if HAVE_X11
0051         , _connection(nullptr)
0052         , _gc(0x0)
0053         , m_isX11(KWindowSystem::isPlatformX11())
0054 #endif
0055     {
0056         setupWaylandIntegration();
0057     }
0058 
0059     ~Private()
0060     {
0061         // Do not call clearPixmaps() from here: it creates new QPixmap(),
0062         // which causes a crash when application is stopping.
0063         freeX11Pixmaps();
0064     }
0065 
0066     void freeX11Pixmaps();
0067     void freeWaylandBuffers();
0068     void clearPixmaps();
0069     void setupPixmaps();
0070     Qt::HANDLE createPixmap(const QPixmap &source);
0071     void initPixmap(const QString &element);
0072     QPixmap initEmptyPixmap(const QSize &size);
0073     void updateShadow(const QWindow *window, Plasma::FrameSvg::EnabledBorders);
0074     void updateShadowX11(const QWindow *window, Plasma::FrameSvg::EnabledBorders);
0075     void updateShadowWayland(const QWindow *window, Plasma::FrameSvg::EnabledBorders);
0076     void clearShadow(const QWindow *window);
0077     void clearShadowX11(const QWindow *window);
0078     void clearShadowWayland(const QWindow *window);
0079     void updateShadows();
0080     void windowDestroyed(QObject *deletedObject);
0081     void setupData(Plasma::FrameSvg::EnabledBorders enabledBorders);
0082 
0083     void setupWaylandIntegration();
0084 
0085     DialogShadows *q;
0086     QList<QPixmap> m_shadowPixmaps;
0087 
0088     QPixmap m_emptyCornerPix;
0089     QPixmap m_emptyCornerLeftPix;
0090     QPixmap m_emptyCornerTopPix;
0091     QPixmap m_emptyCornerRightPix;
0092     QPixmap m_emptyCornerBottomPix;
0093     QPixmap m_emptyVerticalPix;
0094     QPixmap m_emptyHorizontalPix;
0095 
0096 #if HAVE_X11
0097     //! xcb connection
0098     xcb_connection_t *_connection;
0099 
0100     //! graphical context
0101     xcb_gcontext_t _gc;
0102     bool m_isX11;
0103 #endif
0104 
0105 #if HAVE_KWAYLAND
0106     struct Wayland {
0107         KWayland::Client::ShadowManager *manager = nullptr;
0108         KWayland::Client::ShmPool *shmPool = nullptr;
0109         KWayland::Client::PlasmaShell *plasmaShell = nullptr;
0110 
0111         QList<KWayland::Client::Buffer::Ptr> shadowBuffers;
0112     };
0113     Wayland m_wayland;
0114 #endif
0115 
0116     QHash<Plasma::FrameSvg::EnabledBorders, QVector<unsigned long> > data;
0117     QHash<const QWindow *, Plasma::FrameSvg::EnabledBorders> m_windows;
0118 };
0119 
0120 class DialogShadowsSingleton
0121 {
0122 public:
0123     DialogShadowsSingleton()
0124     {
0125     }
0126 
0127     DialogShadows self;
0128 };
0129 
0130 Q_GLOBAL_STATIC(DialogShadowsSingleton, privateDialogShadowsSelf)
0131 
0132 DialogShadows::DialogShadows(QObject *parent, const QString &prefix)
0133     : Plasma::Svg(parent),
0134       d(new Private(this))
0135 {
0136     setImagePath(prefix);
0137     connect(this, SIGNAL(repaintNeeded()), this, SLOT(updateShadows()));
0138 }
0139 
0140 DialogShadows::~DialogShadows()
0141 {
0142     delete d;
0143 }
0144 
0145 DialogShadows *DialogShadows::self()
0146 {
0147     return &privateDialogShadowsSelf->self;
0148 }
0149 
0150 void DialogShadows::addWindow(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
0151 {
0152     if (!window) {
0153         return;
0154     }
0155 
0156     d->m_windows[window] = enabledBorders;
0157     d->updateShadow(window, enabledBorders);
0158     connect(window, SIGNAL(destroyed(QObject*)),
0159             this, SLOT(windowDestroyed(QObject*)), Qt::UniqueConnection);
0160 }
0161 
0162 void DialogShadows::removeWindow(const QWindow *window)
0163 {
0164     if (!d->m_windows.contains(window)) {
0165         return;
0166     }
0167 
0168     d->m_windows.remove(window);
0169     disconnect(window, nullptr, this, nullptr);
0170     d->clearShadow(window);
0171 
0172     if (d->m_windows.isEmpty()) {
0173         d->clearPixmaps();
0174     }
0175 }
0176 
0177 void DialogShadows::setEnabledBorders(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
0178 {
0179     if (!window || !d->m_windows.contains(window)) {
0180         return;
0181     }
0182 
0183     d->updateShadow(window, enabledBorders);
0184 }
0185 
0186 
0187 void DialogShadows::Private::windowDestroyed(QObject *deletedObject)
0188 {
0189     m_windows.remove(static_cast<QWindow *>(deletedObject));
0190 
0191     if (m_windows.isEmpty()) {
0192         clearPixmaps();
0193     }
0194 }
0195 
0196 void DialogShadows::Private::updateShadows()
0197 {
0198     setupPixmaps();
0199     QHash<const QWindow *, Plasma::FrameSvg::EnabledBorders>::const_iterator i;
0200     for (i = m_windows.constBegin(); i != m_windows.constEnd(); ++i) {
0201         updateShadow(i.key(), i.value());
0202     }
0203 }
0204 
0205 Qt::HANDLE DialogShadows::Private::createPixmap(const QPixmap &source)
0206 {
0207 
0208     // do nothing for invalid pixmaps
0209     if (source.isNull()) {
0210         return nullptr;
0211     }
0212 
0213     /*
0214     in some cases, pixmap handle is invalid. This is the case notably
0215     when Qt uses to RasterEngine. In this case, we create an X11 Pixmap
0216     explicitly and draw the source pixmap on it.
0217     */
0218 
0219 #if HAVE_X11
0220     if (!m_isX11) {
0221         return nullptr;
0222     }
0223 
0224     // check connection
0225     if (!_connection) {
0226         _connection = QX11Info::connection();
0227     }
0228 
0229     const int width(source.width());
0230     const int height(source.height());
0231 
0232     // create X11 pixmap
0233     Pixmap pixmap = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), width, height, 32);
0234 
0235     // check gc
0236     if (!_gc) {
0237         _gc = xcb_generate_id(_connection);
0238         xcb_create_gc(_connection, _gc, pixmap, 0, nullptr);
0239     }
0240 
0241 //         // create explicitly shared QPixmap from it
0242 //         QPixmap dest( QPixmap::fromX11Pixmap( pixmap, QPixmap::ExplicitlyShared ) );
0243 //
0244 //         // create surface for pixmap
0245 //         {
0246 //             QPainter painter( &dest );
0247 //             painter.setCompositionMode( QPainter::CompositionMode_Source );
0248 //             painter.drawPixmap( 0, 0, source );
0249 //         }
0250 //
0251 //
0252 //         return pixmap;
0253     QImage image(source.toImage());
0254     xcb_put_image(
0255         _connection, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, _gc,
0256         image.width(), image.height(), 0, 0,
0257         0, 32,
0258         image.sizeInBytes(), image.constBits());
0259 
0260     return (Qt::HANDLE)pixmap;
0261 
0262 #else
0263     return nullptr;
0264 #endif
0265 
0266 }
0267 
0268 void DialogShadows::Private::initPixmap(const QString &element)
0269 {
0270     m_shadowPixmaps << q->pixmap(element);
0271 }
0272 
0273 QPixmap DialogShadows::Private::initEmptyPixmap(const QSize &size)
0274 {
0275 #if HAVE_X11
0276     if (!m_isX11) {
0277         return QPixmap();
0278     }
0279     QPixmap tempEmptyPix(size);
0280     if (!size.isEmpty()) {
0281         tempEmptyPix.fill(Qt::transparent);
0282     }
0283     return tempEmptyPix;
0284 #else
0285     Q_UNUSED(size)
0286     return QPixmap();
0287 #endif
0288 }
0289 
0290 void DialogShadows::Private::setupPixmaps()
0291 {
0292     clearPixmaps();
0293     initPixmap(QStringLiteral("shadow-top"));
0294     initPixmap(QStringLiteral("shadow-topright"));
0295     initPixmap(QStringLiteral("shadow-right"));
0296     initPixmap(QStringLiteral("shadow-bottomright"));
0297     initPixmap(QStringLiteral("shadow-bottom"));
0298     initPixmap(QStringLiteral("shadow-bottomleft"));
0299     initPixmap(QStringLiteral("shadow-left"));
0300     initPixmap(QStringLiteral("shadow-topleft"));
0301 
0302     m_emptyCornerPix = initEmptyPixmap(QSize(1, 1));
0303     m_emptyCornerLeftPix = initEmptyPixmap(QSize(q->elementSize(QStringLiteral("shadow-topleft")).width(), 1));
0304     m_emptyCornerTopPix = initEmptyPixmap(QSize(1, q->elementSize(QStringLiteral("shadow-topleft")).height()));
0305     m_emptyCornerRightPix = initEmptyPixmap(QSize(q->elementSize(QStringLiteral("shadow-bottomright")).width(), 1));
0306     m_emptyCornerBottomPix = initEmptyPixmap(QSize(1, q->elementSize(QStringLiteral("shadow-bottomright")).height()));
0307     m_emptyVerticalPix = initEmptyPixmap(QSize(1, q->elementSize(QStringLiteral("shadow-left")).height()));
0308     m_emptyHorizontalPix = initEmptyPixmap(QSize(q->elementSize(QStringLiteral("shadow-top")).width(), 1));
0309 
0310 #if HAVE_KWAYLAND
0311     if (m_wayland.shmPool) {
0312         for (auto it = m_shadowPixmaps.constBegin(); it != m_shadowPixmaps.constEnd(); ++it) {
0313             m_wayland.shadowBuffers << m_wayland.shmPool->createBuffer(it->toImage());
0314         }
0315     }
0316 #endif
0317 }
0318 
0319 void DialogShadows::Private::setupData(Plasma::FrameSvg::EnabledBorders enabledBorders)
0320 {
0321 #if HAVE_X11
0322     if (!m_isX11) {
0323         return;
0324     }
0325     //shadow-top
0326     if (enabledBorders & Plasma::FrameSvg::TopBorder) {
0327         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[0]));
0328     } else {
0329         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyHorizontalPix));
0330     }
0331 
0332     //shadow-topright
0333     if (enabledBorders & Plasma::FrameSvg::TopBorder &&
0334             enabledBorders & Plasma::FrameSvg::RightBorder) {
0335         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[1]));
0336     } else if (enabledBorders & Plasma::FrameSvg::TopBorder) {
0337         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerTopPix));
0338     } else if (enabledBorders & Plasma::FrameSvg::RightBorder) {
0339         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerRightPix));
0340     } else {
0341         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix));
0342     }
0343 
0344     //shadow-right
0345     if (enabledBorders & Plasma::FrameSvg::RightBorder) {
0346         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[2]));
0347     } else {
0348         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyVerticalPix));
0349     }
0350 
0351     //shadow-bottomright
0352     if (enabledBorders & Plasma::FrameSvg::BottomBorder &&
0353             enabledBorders & Plasma::FrameSvg::RightBorder) {
0354         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[3]));
0355     } else if (enabledBorders & Plasma::FrameSvg::BottomBorder) {
0356         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerBottomPix));
0357     } else if (enabledBorders & Plasma::FrameSvg::RightBorder) {
0358         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerRightPix));
0359     } else {
0360         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix));
0361     }
0362 
0363     //shadow-bottom
0364     if (enabledBorders & Plasma::FrameSvg::BottomBorder) {
0365         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[4]));
0366     } else {
0367         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyHorizontalPix));
0368     }
0369 
0370     //shadow-bottomleft
0371     if (enabledBorders & Plasma::FrameSvg::BottomBorder &&
0372             enabledBorders & Plasma::FrameSvg::LeftBorder) {
0373         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[5]));
0374     } else if (enabledBorders & Plasma::FrameSvg::BottomBorder) {
0375         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerBottomPix));
0376     } else if (enabledBorders & Plasma::FrameSvg::LeftBorder) {
0377         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerLeftPix));
0378     } else {
0379         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix));
0380     }
0381 
0382     //shadow-left
0383     if (enabledBorders & Plasma::FrameSvg::LeftBorder) {
0384         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[6]));
0385     } else {
0386         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyVerticalPix));
0387     }
0388 
0389     //shadow-topleft
0390     if (enabledBorders & Plasma::FrameSvg::TopBorder &&
0391             enabledBorders & Plasma::FrameSvg::LeftBorder) {
0392         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_shadowPixmaps[7]));
0393     } else if (enabledBorders & Plasma::FrameSvg::TopBorder) {
0394         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerTopPix));
0395     } else if (enabledBorders & Plasma::FrameSvg::LeftBorder) {
0396         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerLeftPix));
0397     } else {
0398         data[enabledBorders] << reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix));
0399     }
0400 #endif
0401 
0402     int left, top, right, bottom = 0;
0403 
0404     QSize marginHint;
0405     if (enabledBorders & Plasma::FrameSvg::TopBorder) {
0406         marginHint = q->elementSize(QStringLiteral("shadow-hint-top-margin"));
0407         if (marginHint.isValid()) {
0408             top = marginHint.height();
0409         } else {
0410             top = m_shadowPixmaps[0].height(); // top
0411         }
0412     } else {
0413         top = 1;
0414     }
0415 
0416     if (enabledBorders & Plasma::FrameSvg::RightBorder) {
0417         marginHint = q->elementSize(QStringLiteral("shadow-hint-right-margin"));
0418         if (marginHint.isValid()) {
0419             right = marginHint.width();
0420         } else {
0421             right = m_shadowPixmaps[2].width(); // right
0422         }
0423     } else {
0424         right = 1;
0425     }
0426 
0427     if (enabledBorders & Plasma::FrameSvg::BottomBorder) {
0428         marginHint = q->elementSize(QStringLiteral("shadow-hint-bottom-margin"));
0429         if (marginHint.isValid()) {
0430             bottom = marginHint.height();
0431         } else {
0432             bottom = m_shadowPixmaps[4].height(); // bottom
0433         }
0434     } else {
0435         bottom = 1;
0436     }
0437 
0438     if (enabledBorders & Plasma::FrameSvg::LeftBorder) {
0439         marginHint = q->elementSize(QStringLiteral("shadow-hint-left-margin"));
0440         if (marginHint.isValid()) {
0441             left = marginHint.width();
0442         } else {
0443             left = m_shadowPixmaps[6].width(); // left
0444         }
0445     } else {
0446         left = 1;
0447     }
0448 
0449     data[enabledBorders] << top << right << bottom << left;
0450 }
0451 
0452 void DialogShadows::Private::freeX11Pixmaps()
0453 {
0454 #if HAVE_X11
0455     if (!m_isX11) {
0456         return;
0457     }
0458 
0459     auto *display = QX11Info::display();
0460     if (!display) {
0461         return;
0462     }
0463 
0464     foreach (const QPixmap &pixmap, m_shadowPixmaps) {
0465         if (!pixmap.isNull()) {
0466             XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(pixmap)));
0467         }
0468     }
0469 
0470     if (!m_emptyCornerPix.isNull()) {
0471         XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerPix)));
0472     }
0473     if (!m_emptyCornerBottomPix.isNull()) {
0474         XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerBottomPix)));
0475     }
0476     if (!m_emptyCornerLeftPix.isNull()) {
0477         XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerLeftPix)));
0478     }
0479     if (!m_emptyCornerRightPix.isNull()) {
0480         XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerRightPix)));
0481     }
0482     if (!m_emptyCornerTopPix.isNull()) {
0483         XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyCornerTopPix)));
0484     }
0485     if (!m_emptyVerticalPix.isNull()) {
0486         XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyVerticalPix)));
0487     }
0488     if (!m_emptyHorizontalPix.isNull()) {
0489         XFreePixmap(display, reinterpret_cast<unsigned long>(createPixmap(m_emptyHorizontalPix)));
0490     }
0491 #endif
0492 }
0493 
0494 void DialogShadows::Private::clearPixmaps()
0495 {
0496 #if HAVE_X11
0497     freeX11Pixmaps();
0498 
0499     m_emptyCornerPix = QPixmap();
0500     m_emptyCornerBottomPix = QPixmap();
0501     m_emptyCornerLeftPix = QPixmap();
0502     m_emptyCornerRightPix = QPixmap();
0503     m_emptyCornerTopPix = QPixmap();
0504     m_emptyVerticalPix = QPixmap();
0505     m_emptyHorizontalPix = QPixmap();
0506 #endif
0507     freeWaylandBuffers();
0508     m_shadowPixmaps.clear();
0509     data.clear();
0510 }
0511 
0512 void DialogShadows::Private::freeWaylandBuffers()
0513 {
0514 #if HAVE_KWAYLAND
0515     m_wayland.shadowBuffers.clear();
0516 #endif
0517 }
0518 
0519 void DialogShadows::Private::updateShadow(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
0520 {
0521 #if HAVE_X11
0522     if (m_isX11) {
0523         updateShadowX11(window, enabledBorders);
0524     }
0525 #endif
0526 #if HAVE_KWAYLAND
0527     if (m_wayland.manager) {
0528         updateShadowWayland(window, enabledBorders);
0529     }
0530 #endif
0531 }
0532 
0533 void DialogShadows::Private::updateShadowX11(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
0534 {
0535 #if HAVE_X11
0536     if (m_shadowPixmaps.isEmpty()) {
0537         setupPixmaps();
0538     }
0539 
0540     if (!data.contains(enabledBorders)) {
0541         setupData(enabledBorders);
0542     }
0543 
0544     Display *dpy = QX11Info::display();
0545     Atom atom = XInternAtom(dpy, "_KDE_NET_WM_SHADOW", False);
0546 
0547     //qDebug() << "going to set the shadow of" << window->winId() << "to" << data;
0548     XChangeProperty(dpy, window->winId(), atom, XA_CARDINAL, 32, PropModeReplace,
0549                     reinterpret_cast<const unsigned char *>(data[enabledBorders].constData()), data[enabledBorders].size());
0550 #endif
0551 }
0552 
0553 void DialogShadows::Private::updateShadowWayland(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
0554 {
0555 #if HAVE_KWAYLAND
0556     if (!m_wayland.shmPool) {
0557         return;
0558     }
0559     if (m_wayland.shadowBuffers.isEmpty()) {
0560         setupPixmaps();
0561     }
0562     // TODO: check whether the surface already has a shadow
0563     KWayland::Client::Surface *surface = KWayland::Client::Surface::fromWindow(const_cast<QWindow*>(window));
0564     if (!surface) {
0565         return;
0566     }
0567     auto shadow = m_wayland.manager->createShadow(surface, surface);
0568 
0569     //shadow-top
0570     if (enabledBorders & Plasma::FrameSvg::TopBorder) {
0571         shadow->attachTop(m_wayland.shadowBuffers.at(0));
0572     }
0573 
0574     //shadow-topright
0575     if (enabledBorders & Plasma::FrameSvg::TopBorder &&
0576         enabledBorders & Plasma::FrameSvg::RightBorder) {
0577         shadow->attachTopRight(m_wayland.shadowBuffers.at(1));
0578     }
0579 
0580     //shadow-right
0581     if (enabledBorders & Plasma::FrameSvg::RightBorder) {
0582         shadow->attachRight(m_wayland.shadowBuffers.at(2));
0583     }
0584 
0585     //shadow-bottomright
0586     if (enabledBorders & Plasma::FrameSvg::BottomBorder &&
0587         enabledBorders & Plasma::FrameSvg::RightBorder) {
0588         shadow->attachBottomRight(m_wayland.shadowBuffers.at(3));
0589     }
0590 
0591     //shadow-bottom
0592     if (enabledBorders & Plasma::FrameSvg::BottomBorder) {
0593         shadow->attachBottom(m_wayland.shadowBuffers.at(4));
0594     }
0595 
0596     //shadow-bottomleft
0597     if (enabledBorders & Plasma::FrameSvg::BottomBorder &&
0598         enabledBorders & Plasma::FrameSvg::LeftBorder) {
0599         shadow->attachBottomLeft(m_wayland.shadowBuffers.at(5));
0600     }
0601 
0602     //shadow-left
0603     if (enabledBorders & Plasma::FrameSvg::LeftBorder) {
0604         shadow->attachLeft(m_wayland.shadowBuffers.at(6));
0605     }
0606 
0607     //shadow-topleft
0608     if (enabledBorders & Plasma::FrameSvg::TopBorder &&
0609         enabledBorders & Plasma::FrameSvg::LeftBorder) {
0610         shadow->attachTopLeft(m_wayland.shadowBuffers.at(7));
0611     }
0612 
0613     QSize marginHint;
0614     QMarginsF margins;
0615     if (enabledBorders & Plasma::FrameSvg::TopBorder) {
0616         marginHint = q->elementSize(QStringLiteral("shadow-hint-top-margin"));
0617         if (marginHint.isValid()) {
0618             margins.setTop(marginHint.height());
0619         } else {
0620             margins.setTop(m_shadowPixmaps[0].height());
0621         }
0622     }
0623 
0624     if (enabledBorders & Plasma::FrameSvg::RightBorder) {
0625         marginHint = q->elementSize(QStringLiteral("shadow-hint-right-margin"));
0626         if (marginHint.isValid()) {
0627             margins.setRight(marginHint.width());
0628         } else {
0629             margins.setRight(m_shadowPixmaps[2].width());
0630         }
0631     }
0632 
0633     if (enabledBorders & Plasma::FrameSvg::BottomBorder) {
0634         marginHint = q->elementSize(QStringLiteral("shadow-hint-bottom-margin"));
0635         if (marginHint.isValid()) {
0636             margins.setBottom(marginHint.height());
0637         } else {
0638             margins.setBottom(m_shadowPixmaps[4].height());
0639         }
0640     }
0641 
0642     if (enabledBorders & Plasma::FrameSvg::LeftBorder) {
0643         marginHint = q->elementSize(QStringLiteral("shadow-hint-left-margin"));
0644         if (marginHint.isValid()) {
0645             margins.setLeft(marginHint.width());
0646         } else {
0647             margins.setLeft(m_shadowPixmaps[6].width());
0648         }
0649     }
0650 
0651     shadow->setOffsets(margins);
0652     shadow->commit();
0653     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0654 #endif
0655 }
0656 
0657 void DialogShadows::Private::clearShadow(const QWindow *window)
0658 {
0659     if (!static_cast<const QSurface*>(window)->surfaceHandle()) {
0660         qWarning() << "Cannot clear shadow from window without native surface!";
0661         return;
0662     }
0663 #if HAVE_X11
0664     if (m_isX11) {
0665         clearShadowX11(window);
0666     }
0667 #endif
0668 #if HAVE_KWAYLAND
0669     if (m_wayland.manager) {
0670         clearShadowWayland(window);
0671     }
0672 #endif
0673 }
0674 
0675 void DialogShadows::Private::clearShadowX11(const QWindow* window)
0676 {
0677 #if HAVE_X11
0678     Display *dpy = QX11Info::display();
0679     Atom atom = XInternAtom(dpy, "_KDE_NET_WM_SHADOW", False);
0680     XDeleteProperty(dpy, window->winId(), atom);
0681 #endif
0682 }
0683 
0684 void DialogShadows::Private::clearShadowWayland(const QWindow *window)
0685 {
0686 #if HAVE_KWAYLAND
0687     KWayland::Client::Surface *surface = KWayland::Client::Surface::fromWindow(const_cast<QWindow*>(window));
0688     if (!surface) {
0689         return;
0690     }
0691     m_wayland.manager->removeShadow(surface);
0692     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0693 #endif
0694 }
0695 
0696 bool DialogShadows::enabled() const
0697 {
0698     return hasElement(QStringLiteral("shadow-left"));
0699 }
0700 
0701 KWayland::Client::PlasmaShell *DialogShadows::waylandPlasmaShellInterface() const
0702 {
0703 #if HAVE_KWAYLAND
0704     return d->m_wayland.plasmaShell;
0705 #else
0706     return nullptr;
0707 #endif
0708 }
0709 
0710 void DialogShadows::Private::setupWaylandIntegration()
0711 {
0712 #if HAVE_KWAYLAND
0713     if (!KWindowSystem::isPlatformWayland()) {
0714         return;
0715     }
0716     using namespace KWayland::Client;
0717     ConnectionThread *connection = ConnectionThread::fromApplication(q);
0718     if (!connection) {
0719         return;
0720     }
0721     Registry *registry = new Registry(q);
0722     registry->create(connection);
0723     connect(registry, &Registry::shadowAnnounced, q,
0724         [this, registry] (quint32 name, quint32 version) {
0725             m_wayland.manager = registry->createShadowManager(name, version, q);
0726             updateShadows();
0727         }, Qt::QueuedConnection
0728     );
0729     connect(registry, &Registry::shmAnnounced, q,
0730         [this, registry] (quint32 name, quint32 version) {
0731             m_wayland.shmPool = registry->createShmPool(name, version, q);
0732             updateShadows();
0733         }, Qt::QueuedConnection
0734     );
0735     connect(registry, &Registry::plasmaShellAnnounced, q,
0736         [this, registry] (quint32 name, quint32 version) {
0737             m_wayland.plasmaShell = registry->createPlasmaShell(name, version, q);
0738         }
0739     );
0740     registry->setup();
0741     connection->roundtrip();
0742 #endif
0743 }
0744 
0745 #include "moc_dialogshadows_p.cpp"
0746