Warning, file /plasma/kwin/src/shadow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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