File indexing completed on 2024-04-14 15:37:21

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