File indexing completed on 2024-04-28 16:49:01

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org>
0006     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 #include "shadow.h"
0011 // kwin
0012 #include "atoms.h"
0013 #include "composite.h"
0014 #include "internalwindow.h"
0015 #include "scene/workspacescene.h"
0016 #include "wayland/shadow_interface.h"
0017 #include "wayland/shmclientbuffer.h"
0018 #include "wayland/surface_interface.h"
0019 #include "wayland_server.h"
0020 #include "window.h"
0021 
0022 #include <KDecoration2/Decoration>
0023 #include <KDecoration2/DecorationShadow>
0024 
0025 #include <QWindow>
0026 
0027 Q_DECLARE_METATYPE(QMargins)
0028 
0029 namespace KWin
0030 {
0031 
0032 Shadow::Shadow(Window *window)
0033     : m_window(window)
0034     , m_cachedSize(window->size())
0035     , m_decorationShadow(nullptr)
0036 {
0037     connect(m_window, &Window::frameGeometryChanged, this, &Shadow::geometryChanged);
0038 }
0039 
0040 Shadow::~Shadow()
0041 {
0042 }
0043 
0044 std::unique_ptr<Shadow> Shadow::createShadow(Window *window)
0045 {
0046     auto shadow = createShadowFromDecoration(window);
0047     if (!shadow && waylandServer()) {
0048         shadow = createShadowFromWayland(window);
0049     }
0050     if (!shadow && kwinApp()->x11Connection()) {
0051         shadow = createShadowFromX11(window);
0052     }
0053     if (!shadow) {
0054         shadow = createShadowFromInternalWindow(window);
0055     }
0056     return shadow;
0057 }
0058 
0059 std::unique_ptr<Shadow> Shadow::createShadowFromX11(Window *window)
0060 {
0061     auto data = Shadow::readX11ShadowProperty(window->window());
0062     if (!data.isEmpty()) {
0063         auto shadow = Compositor::self()->scene()->createShadow(window);
0064         if (!shadow->init(data)) {
0065             return nullptr;
0066         }
0067         return shadow;
0068     } else {
0069         return nullptr;
0070     }
0071 }
0072 
0073 std::unique_ptr<Shadow> Shadow::createShadowFromDecoration(Window *window)
0074 {
0075     if (!window->decoration()) {
0076         return nullptr;
0077     }
0078     auto shadow = Compositor::self()->scene()->createShadow(window);
0079     if (!shadow->init(window->decoration())) {
0080         return nullptr;
0081     }
0082     return shadow;
0083 }
0084 
0085 std::unique_ptr<Shadow> Shadow::createShadowFromWayland(Window *window)
0086 {
0087     auto surface = window->surface();
0088     if (!surface) {
0089         return nullptr;
0090     }
0091     const auto s = surface->shadow();
0092     if (!s) {
0093         return nullptr;
0094     }
0095     auto shadow = Compositor::self()->scene()->createShadow(window);
0096     if (!shadow->init(s)) {
0097         return nullptr;
0098     }
0099     return shadow;
0100 }
0101 
0102 std::unique_ptr<Shadow> Shadow::createShadowFromInternalWindow(Window *window)
0103 {
0104     const InternalWindow *internalWindow = qobject_cast<InternalWindow *>(window);
0105     if (!internalWindow) {
0106         return nullptr;
0107     }
0108     const QWindow *handle = internalWindow->handle();
0109     if (!handle) {
0110         return nullptr;
0111     }
0112     auto shadow = Compositor::self()->scene()->createShadow(window);
0113     if (!shadow->init(handle)) {
0114         return nullptr;
0115     }
0116     return shadow;
0117 }
0118 
0119 QVector<uint32_t> Shadow::readX11ShadowProperty(xcb_window_t id)
0120 {
0121     QVector<uint32_t> ret;
0122     if (id != XCB_WINDOW_NONE) {
0123         Xcb::Property property(false, id, atoms->kde_net_wm_shadow, XCB_ATOM_CARDINAL, 0, 12);
0124         uint32_t *shadow = property.value<uint32_t *>();
0125         if (shadow) {
0126             ret.reserve(12);
0127             for (int i = 0; i < 12; ++i) {
0128                 ret << shadow[i];
0129             }
0130         }
0131     }
0132     return ret;
0133 }
0134 
0135 bool Shadow::init(const QVector<uint32_t> &data)
0136 {
0137     QVector<Xcb::WindowGeometry> pixmapGeometries(ShadowElementsCount);
0138     QVector<xcb_get_image_cookie_t> getImageCookies(ShadowElementsCount);
0139     auto *c = kwinApp()->x11Connection();
0140     for (int i = 0; i < ShadowElementsCount; ++i) {
0141         pixmapGeometries[i] = Xcb::WindowGeometry(data[i]);
0142     }
0143     auto discardReplies = [&getImageCookies](int start) {
0144         for (int i = start; i < getImageCookies.size(); ++i) {
0145             xcb_discard_reply(kwinApp()->x11Connection(), getImageCookies.at(i).sequence);
0146         }
0147     };
0148     for (int i = 0; i < ShadowElementsCount; ++i) {
0149         auto &geo = pixmapGeometries[i];
0150         if (geo.isNull()) {
0151             discardReplies(0);
0152             return false;
0153         }
0154         getImageCookies[i] = xcb_get_image_unchecked(c, XCB_IMAGE_FORMAT_Z_PIXMAP, data[i],
0155                                                      0, 0, geo->width, geo->height, ~0);
0156     }
0157     for (int i = 0; i < ShadowElementsCount; ++i) {
0158         auto *reply = xcb_get_image_reply(c, getImageCookies.at(i), nullptr);
0159         if (!reply) {
0160             discardReplies(i + 1);
0161             return false;
0162         }
0163         auto &geo = pixmapGeometries[i];
0164         QImage image(xcb_get_image_data(reply), geo->width, geo->height, QImage::Format_ARGB32);
0165         m_shadowElements[i] = image.copy();
0166         free(reply);
0167     }
0168     m_offset = QMargins(data[ShadowElementsCount + 3],
0169                         data[ShadowElementsCount],
0170                         data[ShadowElementsCount + 1],
0171                         data[ShadowElementsCount + 2]);
0172     Q_EMIT offsetChanged();
0173     if (!prepareBackend()) {
0174         return false;
0175     }
0176     Q_EMIT textureChanged();
0177     return true;
0178 }
0179 
0180 bool Shadow::init(KDecoration2::Decoration *decoration)
0181 {
0182     if (m_decorationShadow) {
0183         // disconnect previous connections
0184         disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::innerShadowRectChanged, m_window, &Window::updateShadow);
0185         disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::shadowChanged, m_window, &Window::updateShadow);
0186         disconnect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::paddingChanged, m_window, &Window::updateShadow);
0187     }
0188     m_decorationShadow = decoration->shadow();
0189     if (!m_decorationShadow) {
0190         return false;
0191     }
0192     // setup connections - all just mapped to recreate
0193     connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::innerShadowRectChanged, m_window, &Window::updateShadow);
0194     connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::shadowChanged, m_window, &Window::updateShadow);
0195     connect(m_decorationShadow.data(), &KDecoration2::DecorationShadow::paddingChanged, m_window, &Window::updateShadow);
0196 
0197     m_offset = m_decorationShadow->padding();
0198     Q_EMIT offsetChanged();
0199     if (!prepareBackend()) {
0200         return false;
0201     }
0202     Q_EMIT textureChanged();
0203     return true;
0204 }
0205 
0206 static QImage shadowTileForBuffer(KWaylandServer::ClientBuffer *buffer)
0207 {
0208     auto shmBuffer = qobject_cast<KWaylandServer::ShmClientBuffer *>(buffer);
0209     if (shmBuffer) {
0210         return shmBuffer->data().copy();
0211     }
0212     return QImage();
0213 }
0214 
0215 bool Shadow::init(const QPointer<KWaylandServer::ShadowInterface> &shadow)
0216 {
0217     if (!shadow) {
0218         return false;
0219     }
0220 
0221     m_shadowElements[ShadowElementTop] = shadowTileForBuffer(shadow->top());
0222     m_shadowElements[ShadowElementTopRight] = shadowTileForBuffer(shadow->topRight());
0223     m_shadowElements[ShadowElementRight] = shadowTileForBuffer(shadow->right());
0224     m_shadowElements[ShadowElementBottomRight] = shadowTileForBuffer(shadow->bottomRight());
0225     m_shadowElements[ShadowElementBottom] = shadowTileForBuffer(shadow->bottom());
0226     m_shadowElements[ShadowElementBottomLeft] = shadowTileForBuffer(shadow->bottomLeft());
0227     m_shadowElements[ShadowElementLeft] = shadowTileForBuffer(shadow->left());
0228     m_shadowElements[ShadowElementTopLeft] = shadowTileForBuffer(shadow->topLeft());
0229 
0230     m_offset = shadow->offset().toMargins();
0231     Q_EMIT offsetChanged();
0232     if (!prepareBackend()) {
0233         return false;
0234     }
0235     Q_EMIT textureChanged();
0236     return true;
0237 }
0238 
0239 bool Shadow::init(const QWindow *window)
0240 {
0241     const bool isEnabled = window->property("kwin_shadow_enabled").toBool();
0242     if (!isEnabled) {
0243         return false;
0244     }
0245 
0246     const QImage leftTile = window->property("kwin_shadow_left_tile").value<QImage>();
0247     const QImage topLeftTile = window->property("kwin_shadow_top_left_tile").value<QImage>();
0248     const QImage topTile = window->property("kwin_shadow_top_tile").value<QImage>();
0249     const QImage topRightTile = window->property("kwin_shadow_top_right_tile").value<QImage>();
0250     const QImage rightTile = window->property("kwin_shadow_right_tile").value<QImage>();
0251     const QImage bottomRightTile = window->property("kwin_shadow_bottom_right_tile").value<QImage>();
0252     const QImage bottomTile = window->property("kwin_shadow_bottom_tile").value<QImage>();
0253     const QImage bottomLeftTile = window->property("kwin_shadow_bottom_left_tile").value<QImage>();
0254 
0255     m_shadowElements[ShadowElementLeft] = leftTile;
0256     m_shadowElements[ShadowElementTopLeft] = topLeftTile;
0257     m_shadowElements[ShadowElementTop] = topTile;
0258     m_shadowElements[ShadowElementTopRight] = topRightTile;
0259     m_shadowElements[ShadowElementRight] = rightTile;
0260     m_shadowElements[ShadowElementBottomRight] = bottomRightTile;
0261     m_shadowElements[ShadowElementBottom] = bottomTile;
0262     m_shadowElements[ShadowElementBottomLeft] = bottomLeftTile;
0263 
0264     m_offset = window->property("kwin_shadow_padding").value<QMargins>();
0265     Q_EMIT offsetChanged();
0266 
0267     if (!prepareBackend()) {
0268         return false;
0269     }
0270     Q_EMIT textureChanged();
0271     return true;
0272 }
0273 
0274 bool Shadow::updateShadow()
0275 {
0276     if (!m_window) {
0277         return false;
0278     }
0279 
0280     if (m_decorationShadow) {
0281         if (m_window) {
0282             if (m_window->decoration()) {
0283                 if (init(m_window->decoration())) {
0284                     return true;
0285                 }
0286             }
0287         }
0288         return false;
0289     }
0290 
0291     if (waylandServer()) {
0292         if (m_window && m_window->surface()) {
0293             if (const auto &s = m_window->surface()->shadow()) {
0294                 if (init(s)) {
0295                     return true;
0296                 }
0297             }
0298         }
0299     }
0300 
0301     if (InternalWindow *window = qobject_cast<InternalWindow *>(m_window)) {
0302         if (init(window->handle())) {
0303             return true;
0304         }
0305     }
0306 
0307     auto data = Shadow::readX11ShadowProperty(m_window->window());
0308     if (data.isEmpty()) {
0309         return false;
0310     }
0311 
0312     init(data);
0313 
0314     return true;
0315 }
0316 
0317 Window *Shadow::window() const
0318 {
0319     return m_window;
0320 }
0321 
0322 void Shadow::setWindow(Window *window)
0323 {
0324     m_window = window;
0325     connect(m_window, &Window::frameGeometryChanged, this, &Shadow::geometryChanged);
0326 }
0327 void Shadow::geometryChanged()
0328 {
0329     if (m_cachedSize == m_window->size()) {
0330         return;
0331     }
0332     m_cachedSize = m_window->size();
0333     Q_EMIT rectChanged();
0334 }
0335 
0336 QImage Shadow::decorationShadowImage() const
0337 {
0338     if (!m_decorationShadow) {
0339         return QImage();
0340     }
0341     return m_decorationShadow->shadow();
0342 }
0343 
0344 QSize Shadow::elementSize(Shadow::ShadowElements element) const
0345 {
0346     if (m_decorationShadow) {
0347         switch (element) {
0348         case ShadowElementTop:
0349             return m_decorationShadow->topGeometry().size();
0350         case ShadowElementTopRight:
0351             return m_decorationShadow->topRightGeometry().size();
0352         case ShadowElementRight:
0353             return m_decorationShadow->rightGeometry().size();
0354         case ShadowElementBottomRight:
0355             return m_decorationShadow->bottomRightGeometry().size();
0356         case ShadowElementBottom:
0357             return m_decorationShadow->bottomGeometry().size();
0358         case ShadowElementBottomLeft:
0359             return m_decorationShadow->bottomLeftGeometry().size();
0360         case ShadowElementLeft:
0361             return m_decorationShadow->leftGeometry().size();
0362         case ShadowElementTopLeft:
0363             return m_decorationShadow->topLeftGeometry().size();
0364         default:
0365             return QSize();
0366         }
0367     } else {
0368         return m_shadowElements[element].size();
0369     }
0370 }
0371 
0372 } // namespace