File indexing completed on 2024-11-10 04:57:56
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"