File indexing completed on 2024-04-28 05:30:30

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