File indexing completed on 2024-05-12 04:00:25
0001 /* 0002 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org> 0003 SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 #include "windowsystem.h" 0008 #include "logging.h" 0009 #include "surfacehelper.h" 0010 #include "waylandxdgactivationv1_p.h" 0011 #include "waylandxdgforeignv2_p.h" 0012 0013 #include <KWaylandExtras> 0014 #include <KWindowSystem> 0015 0016 #include "qwayland-plasma-window-management.h" 0017 #include <QEvent> 0018 #include <QGuiApplication> 0019 #include <QPixmap> 0020 #include <QPoint> 0021 #include <QString> 0022 #include <QWaylandClientExtensionTemplate> 0023 #include <QWindow> 0024 #include <private/qwaylanddisplay_p.h> 0025 #include <private/qwaylandinputdevice_p.h> 0026 #include <private/qwaylandwindow_p.h> 0027 #include <qpa/qplatformnativeinterface.h> 0028 #include <qwaylandclientextension.h> 0029 0030 constexpr const char *c_kdeXdgForeignExportedProperty("_kde_xdg_foreign_exported_v2"); 0031 constexpr const char *c_kdeXdgForeignImportedProperty("_kde_xdg_foreign_imported_v2"); 0032 constexpr const char *c_kdeXdgForeignPendingHandleProperty("_kde_xdg_foreign_pending_handle"); 0033 0034 class WindowManagement : public QWaylandClientExtensionTemplate<WindowManagement>, public QtWayland::org_kde_plasma_window_management 0035 { 0036 public: 0037 WindowManagement() 0038 : QWaylandClientExtensionTemplate<WindowManagement>(16) 0039 { 0040 } 0041 0042 void org_kde_plasma_window_management_show_desktop_changed(uint32_t state) override 0043 { 0044 showingDesktop = state == show_desktop_enabled; 0045 KWindowSystem::self()->showingDesktopChanged(showingDesktop); 0046 } 0047 0048 bool showingDesktop = false; 0049 }; 0050 0051 WindowSystem::WindowSystem() 0052 : QObject() 0053 , KWindowSystemPrivateV2() 0054 , m_lastToken(qEnvironmentVariable("XDG_ACTIVATION_TOKEN")) 0055 { 0056 m_windowManagement = new WindowManagement; 0057 } 0058 0059 WindowSystem::~WindowSystem() 0060 { 0061 delete m_windowManagement; 0062 } 0063 0064 void WindowSystem::activateWindow(QWindow *win, long int time) 0065 { 0066 Q_UNUSED(time); 0067 auto s = surfaceForWindow(win); 0068 if (!s) { 0069 return; 0070 } 0071 WaylandXdgActivationV1 *activation = WaylandXdgActivationV1::self(); 0072 if (!activation->isActive()) { 0073 return; 0074 } 0075 activation->activate(m_lastToken, s); 0076 } 0077 0078 void WindowSystem::requestToken(QWindow *window, uint32_t serial, const QString &app_id) 0079 { 0080 if (window) { 0081 window->create(); 0082 } 0083 wl_surface *wlSurface = surfaceForWindow(window); 0084 0085 WaylandXdgActivationV1 *activation = WaylandXdgActivationV1::self(); 0086 if (!activation->isActive()) { 0087 // Ensure that xdgActivationTokenArrived is always emitted asynchronously 0088 QTimer::singleShot(0, [serial] { 0089 Q_EMIT KWaylandExtras::self()->xdgActivationTokenArrived(serial, {}); 0090 }); 0091 return; 0092 } 0093 0094 auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>(); 0095 auto seat = waylandApp ? waylandApp->lastInputSeat() : nullptr; 0096 auto tokenReq = activation->requestXdgActivationToken(seat, wlSurface, serial, app_id); 0097 connect(tokenReq, &WaylandXdgActivationTokenV1::failed, KWindowSystem::self(), [serial, app_id]() { 0098 Q_EMIT KWaylandExtras::self()->xdgActivationTokenArrived(serial, {}); 0099 }); 0100 connect(tokenReq, &WaylandXdgActivationTokenV1::done, KWindowSystem::self(), [serial](const QString &token) { 0101 Q_EMIT KWaylandExtras::self()->xdgActivationTokenArrived(serial, token); 0102 }); 0103 } 0104 0105 void WindowSystem::setCurrentToken(const QString &token) 0106 { 0107 m_lastToken = token; 0108 } 0109 0110 quint32 WindowSystem::lastInputSerial(QWindow *window) 0111 { 0112 Q_UNUSED(window) 0113 if (auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>()) { 0114 return waylandApp->lastInputSerial(); 0115 } 0116 return 0; 0117 } 0118 0119 void WindowSystem::setShowingDesktop(bool showing) 0120 { 0121 if (!m_windowManagement->isActive()) { 0122 return; 0123 } 0124 m_windowManagement->show_desktop(showing ? WindowManagement::show_desktop_enabled : WindowManagement::show_desktop_disabled); 0125 } 0126 0127 bool WindowSystem::showingDesktop() 0128 { 0129 if (!m_windowManagement->isActive()) { 0130 return false; 0131 } 0132 return m_windowManagement->showingDesktop; 0133 } 0134 0135 void WindowSystem::exportWindow(QWindow *window) 0136 { 0137 auto emitHandle = [window](const QString &handle) { 0138 // Ensure that windowExported is always emitted asynchronously. 0139 QMetaObject::invokeMethod( 0140 window, 0141 [window, handle] { 0142 Q_EMIT KWaylandExtras::self()->windowExported(window, handle); 0143 }, 0144 Qt::QueuedConnection); 0145 }; 0146 0147 if (!window) { 0148 return; 0149 } 0150 0151 window->create(); 0152 0153 auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle()); 0154 if (!waylandWindow) { 0155 emitHandle({}); 0156 return; 0157 } 0158 0159 auto &exporter = WaylandXdgForeignExporterV2::self(); 0160 if (!exporter.isActive()) { 0161 emitHandle({}); 0162 return; 0163 } 0164 0165 // We want to use QObject::property(char*) and use dynamic properties on the object rather than 0166 // call QWaylandWindow::property(QString) and send it around. 0167 auto *waylandWindowQObject = static_cast<QObject *>(waylandWindow); 0168 WaylandXdgForeignExportedV2 *exported = waylandWindowQObject->property(c_kdeXdgForeignExportedProperty).value<WaylandXdgForeignExportedV2 *>(); 0169 if (!exported) { 0170 exported = exporter.exportToplevel(surfaceForWindow(window)); 0171 exported->setParent(waylandWindow); 0172 0173 waylandWindowQObject->setProperty(c_kdeXdgForeignExportedProperty, QVariant::fromValue(exported)); 0174 connect(exported, &QObject::destroyed, waylandWindow, [waylandWindowQObject] { 0175 waylandWindowQObject->setProperty(c_kdeXdgForeignExportedProperty, QVariant()); 0176 }); 0177 0178 connect(exported, &WaylandXdgForeignExportedV2::handleReceived, window, [window](const QString &handle) { 0179 Q_EMIT KWaylandExtras::self()->windowExported(window, handle); 0180 }); 0181 } 0182 0183 if (!exported->handle().isEmpty()) { 0184 emitHandle(exported->handle()); 0185 } 0186 } 0187 0188 void WindowSystem::unexportWindow(QWindow *window) 0189 { 0190 auto waylandWindow = window ? dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle()) : nullptr; 0191 if (!waylandWindow) { 0192 return; 0193 } 0194 0195 auto *waylandWindowQObject = static_cast<QObject *>(waylandWindow); 0196 WaylandXdgForeignExportedV2 *exported = waylandWindowQObject->property(c_kdeXdgForeignExportedProperty).value<WaylandXdgForeignExportedV2 *>(); 0197 delete exported; 0198 Q_ASSERT(!waylandWindowQObject->property(c_kdeXdgForeignExportedProperty).isValid()); 0199 } 0200 0201 void WindowSystem::setMainWindow(QWindow *window, const QString &handle) 0202 { 0203 if (!window) { 0204 return; 0205 } 0206 0207 window->create(); 0208 auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle()); 0209 if (!waylandWindow) { 0210 return; 0211 } 0212 0213 // We want to use QObject::property(char*) and use dynamic properties on the object rather than 0214 // call QWaylandWindow::property(QString) and send it around. 0215 auto *waylandWindowQObject = static_cast<QObject *>(waylandWindow); 0216 auto *imported = waylandWindowQObject->property(c_kdeXdgForeignImportedProperty).value<WaylandXdgForeignImportedV2 *>(); 0217 // Window already parented with a different handle? Delete imported so we import the new one later. 0218 if (imported && imported->handle() != handle) { 0219 delete imported; 0220 imported = nullptr; 0221 Q_ASSERT(!waylandWindowQObject->property(c_kdeXdgForeignImportedProperty).isValid()); 0222 } 0223 0224 // Don't bother. 0225 if (handle.isEmpty()) { 0226 return; 0227 } 0228 0229 if (window->isExposed()) { 0230 doSetMainWindow(window, handle); 0231 } else { 0232 // We can only import an XDG toplevel. QtWayland currently has no proper signal 0233 // for shell surface creation. wlSurfaceCreated() is too early. 0234 // Instead, we wait for it being exposed and then set its parent. 0235 window->setProperty(c_kdeXdgForeignPendingHandleProperty, handle); 0236 window->installEventFilter(this); 0237 } 0238 } 0239 0240 bool WindowSystem::eventFilter(QObject *watched, QEvent *event) 0241 { 0242 if (event->type() == QEvent::Expose) { 0243 auto *window = static_cast<QWindow *>(watched); 0244 if (window->isExposed()) { 0245 const QString handle = window->property(c_kdeXdgForeignPendingHandleProperty).toString(); 0246 if (!handle.isEmpty()) { 0247 doSetMainWindow(window, handle); 0248 window->setProperty(c_kdeXdgForeignPendingHandleProperty, QVariant()); 0249 } 0250 0251 window->removeEventFilter(this); 0252 } 0253 } 0254 0255 return QObject::eventFilter(watched, event); 0256 } 0257 0258 void WindowSystem::doSetMainWindow(QWindow *window, const QString &handle) 0259 { 0260 Q_ASSERT(window); 0261 Q_ASSERT(!handle.isEmpty()); 0262 0263 auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle()); 0264 if (!waylandWindow) { 0265 return; 0266 } 0267 0268 auto &importer = WaylandXdgForeignImporterV2::self(); 0269 if (!importer.isActive()) { 0270 return; 0271 } 0272 0273 auto *waylandWindowQObject = static_cast<QObject *>(waylandWindow); 0274 0275 Q_ASSERT(!waylandWindowQObject->property(c_kdeXdgForeignImportedProperty).isValid()); 0276 0277 WaylandXdgForeignImportedV2 *imported = importer.importToplevel(handle); 0278 imported->set_parent_of(surfaceForWindow(window)); // foreign parent. 0279 imported->setParent(waylandWindow); // memory owner. 0280 0281 waylandWindowQObject->setProperty(c_kdeXdgForeignImportedProperty, QVariant::fromValue(imported)); 0282 connect(imported, &QObject::destroyed, waylandWindow, [waylandWindowQObject] { 0283 waylandWindowQObject->setProperty(c_kdeXdgForeignImportedProperty, QVariant()); 0284 }); 0285 }