File indexing completed on 2024-05-12 04:00:25
0001 /* 0002 SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 SPDX-FileCopyrightText: 2023 David Redondo <kde@david-redondo.de> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "windowshadow.h" 0009 #include "logging.h" 0010 #include "shm.h" 0011 #include "surfacehelper.h" 0012 0013 #include <qwayland-shadow.h> 0014 0015 #include <QDebug> 0016 #include <QExposeEvent> 0017 #include <QWaylandClientExtension> 0018 0019 #include <private/qwaylandwindow_p.h> 0020 0021 class ShadowManager : public QWaylandClientExtensionTemplate<ShadowManager>, public QtWayland::org_kde_kwin_shadow_manager 0022 { 0023 Q_OBJECT 0024 static constexpr int version = 2; 0025 explicit ShadowManager(QObject *parent = nullptr) 0026 : QWaylandClientExtensionTemplate(version) 0027 { 0028 setParent(parent); 0029 initialize(); 0030 0031 connect(this, &QWaylandClientExtension::activeChanged, this, [this] { 0032 if (!isActive()) { 0033 destroy(); 0034 } 0035 }); 0036 } 0037 0038 public: 0039 ~ShadowManager() 0040 { 0041 if (isActive()) { 0042 destroy(); 0043 } 0044 } 0045 static ShadowManager *instance() 0046 { 0047 static ShadowManager *instance = new ShadowManager(qGuiApp); 0048 return instance; 0049 } 0050 }; 0051 0052 class Shadow : public QtWayland::org_kde_kwin_shadow 0053 { 0054 public: 0055 using QtWayland::org_kde_kwin_shadow::org_kde_kwin_shadow; 0056 ~Shadow() 0057 { 0058 destroy(); 0059 } 0060 }; 0061 0062 WindowShadowTile::WindowShadowTile() 0063 { 0064 connect(Shm::instance(), &Shm::activeChanged, this, [this] { 0065 if (Shm::instance()->isActive()) { 0066 buffer.reset(); 0067 } 0068 }); 0069 } 0070 WindowShadowTile::~WindowShadowTile() 0071 { 0072 } 0073 0074 bool WindowShadowTile::create() 0075 { 0076 if (!Shm::instance()->isActive()) { 0077 return false; 0078 } 0079 0080 buffer = Shm::instance()->createBuffer(image); 0081 return true; 0082 } 0083 0084 void WindowShadowTile::destroy() 0085 { 0086 buffer.reset(); 0087 } 0088 0089 WindowShadowTile *WindowShadowTile::get(const KWindowShadowTile *tile) 0090 { 0091 KWindowShadowTilePrivate *d = KWindowShadowTilePrivate::get(tile); 0092 return static_cast<WindowShadowTile *>(d); 0093 } 0094 0095 static wl_buffer *bufferForTile(const KWindowShadowTile::Ptr &tile) 0096 { 0097 if (!tile) { 0098 return nullptr; 0099 } 0100 WindowShadowTile *d = WindowShadowTile::get(tile.data()); 0101 // Our buffer has been deleted in the meantime, try to create it again 0102 if (!d->buffer && d->isCreated) { 0103 d->buffer = Shm::instance()->createBuffer(d->image); 0104 } 0105 return d->buffer ? d->buffer.get()->object() : nullptr; 0106 } 0107 0108 WindowShadow::WindowShadow() 0109 { 0110 } 0111 WindowShadow::~WindowShadow() 0112 { 0113 } 0114 0115 bool WindowShadow::eventFilter(QObject *watched, QEvent *event) 0116 { 0117 Q_UNUSED(watched) 0118 if (event->type() == QEvent::Expose) { 0119 if (auto window = qobject_cast<QWindow *>(watched); window && window->isExposed()) { 0120 if (!internalCreate()) { 0121 qCWarning(KWAYLAND_KWS) << "Failed to recreate shadow for" << window; 0122 } 0123 } 0124 } 0125 return false; 0126 } 0127 0128 bool WindowShadow::internalCreate() 0129 { 0130 if (shadow) { 0131 return true; 0132 } 0133 if (!ShadowManager::instance()->isActive()) { 0134 return false; 0135 } 0136 auto surface = surfaceForWindow(window); 0137 if (!surface) { 0138 return false; 0139 } 0140 0141 shadow = std::make_unique<Shadow>(ShadowManager::instance()->create(surface)); 0142 auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle()); 0143 if (waylandWindow) { 0144 connect(waylandWindow, &QtWaylandClient::QWaylandWindow::wlSurfaceDestroyed, this, &WindowShadow::internalDestroy, Qt::UniqueConnection); 0145 } 0146 0147 auto attach = [](const std::unique_ptr<Shadow> &shadow, auto attach_func, const KWindowShadowTile::Ptr &tile) { 0148 if (auto buffer = bufferForTile(tile)) { 0149 (*shadow.*attach_func)(buffer); 0150 } 0151 }; 0152 attach(shadow, &Shadow::attach_left, leftTile); 0153 attach(shadow, &Shadow::attach_top_left, topLeftTile); 0154 attach(shadow, &Shadow::attach_top, topTile); 0155 attach(shadow, &Shadow::attach_top_right, topRightTile); 0156 attach(shadow, &Shadow::attach_right, rightTile); 0157 attach(shadow, &Shadow::attach_bottom_right, bottomRightTile); 0158 attach(shadow, &Shadow::attach_bottom, bottomTile); 0159 attach(shadow, &Shadow::attach_bottom_left, bottomLeftTile); 0160 0161 shadow->set_left_offset(wl_fixed_from_double(padding.left())); 0162 shadow->set_top_offset(wl_fixed_from_double(padding.top())); 0163 shadow->set_right_offset(wl_fixed_from_double(padding.right())); 0164 shadow->set_bottom_offset(wl_fixed_from_double(padding.bottom())); 0165 0166 shadow->commit(); 0167 0168 // Commit wl_surface at the next available time. 0169 window->requestUpdate(); 0170 0171 return true; 0172 } 0173 0174 bool WindowShadow::create() 0175 { 0176 if (!ShadowManager::instance()->isActive()) { 0177 return false; 0178 } 0179 0180 internalCreate(); 0181 window->installEventFilter(this); 0182 return true; 0183 } 0184 0185 void WindowShadow::internalDestroy() 0186 { 0187 if (!shadow) { 0188 return; 0189 } 0190 0191 // Only call surfaceForWindow and unset the surface if the native window is alive. 0192 // Otherwise window->create() might be called when the window is being destroyed, leading to a crash. 0193 if (window && window->nativeInterface<QNativeInterface::Private::QWaylandWindow>() && ShadowManager::instance()->isActive()) { 0194 if (auto surface = surfaceForWindow(window)) { 0195 ShadowManager::instance()->unset(surface); 0196 } 0197 } 0198 0199 shadow.reset(); 0200 0201 if (window && window->isVisible()) { 0202 window->requestUpdate(); 0203 } 0204 } 0205 0206 void WindowShadow::destroy() 0207 { 0208 if (window) { 0209 window->removeEventFilter(this); 0210 } 0211 internalDestroy(); 0212 } 0213 0214 #include "windowshadow.moc"