File indexing completed on 2024-05-05 17:42:26
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 #include "kwaylandintegration.h" 0007 0008 #include <QExposeEvent> 0009 #include <QGuiApplication> 0010 #include <QWindow> 0011 #include <qpa/qplatformnativeinterface.h> 0012 #include <qtwaylandclientversion.h> 0013 0014 #include "qdbusmenubar_p.h" 0015 #include "qwayland-appmenu.h" 0016 #include "qwayland-server-decoration-palette.h" 0017 0018 #include <KWindowEffects> 0019 0020 static const QByteArray s_schemePropertyName = QByteArrayLiteral("KDE_COLOR_SCHEME_PATH"); 0021 static const QByteArray s_blurBehindPropertyName = QByteArrayLiteral("ENABLE_BLUR_BEHIND_HINT"); 0022 0023 class AppMenuManager : public QWaylandClientExtensionTemplate<AppMenuManager>, public QtWayland::org_kde_kwin_appmenu_manager 0024 { 0025 Q_OBJECT 0026 public: 0027 AppMenuManager() 0028 : QWaylandClientExtensionTemplate<AppMenuManager>(1) 0029 { 0030 #if QTWAYLANDCLIENT_VERSION >= QT_VERSION_CHECK(6, 2, 0) 0031 initialize(); 0032 #else 0033 // QWaylandClientExtensionTemplate invokes this with a QueuedConnection 0034 QMetaObject::invokeMethod(this, "addRegistryListener"); 0035 #endif 0036 } 0037 }; 0038 0039 class ServerSideDecorationPaletteManager : public QWaylandClientExtensionTemplate<ServerSideDecorationPaletteManager>, 0040 public QtWayland::org_kde_kwin_server_decoration_palette_manager 0041 { 0042 Q_OBJECT 0043 public: 0044 ServerSideDecorationPaletteManager() 0045 : QWaylandClientExtensionTemplate<ServerSideDecorationPaletteManager>(1) 0046 { 0047 #if QTWAYLANDCLIENT_VERSION >= QT_VERSION_CHECK(6, 2, 0) 0048 initialize(); 0049 #else 0050 // QWaylandClientExtensionTemplate invokes this with a QueuedConnection 0051 QMetaObject::invokeMethod(this, "addRegistryListener"); 0052 #endif 0053 } 0054 }; 0055 0056 using AppMenu = QtWayland::org_kde_kwin_appmenu; 0057 using ServerSideDecorationPalette = QtWayland::org_kde_kwin_server_decoration_palette; 0058 0059 Q_DECLARE_METATYPE(AppMenu *); 0060 Q_DECLARE_METATYPE(ServerSideDecorationPalette *); 0061 0062 KWaylandIntegration::KWaylandIntegration(KdePlatformTheme *platformTheme) 0063 : QObject() 0064 , m_appMenuManager(new AppMenuManager) 0065 , m_paletteManager(new ServerSideDecorationPaletteManager) 0066 , m_platformTheme(platformTheme) 0067 { 0068 QCoreApplication::instance()->installEventFilter(this); 0069 } 0070 0071 KWaylandIntegration::~KWaylandIntegration() = default; 0072 0073 bool KWaylandIntegration::isRelevantTopLevel(QWindow *w) 0074 { 0075 if (!w || w->parent()) { 0076 return false; 0077 } 0078 0079 // ignore windows that map to XdgPopup 0080 if (w->type() == Qt::ToolTip || w->type() == Qt::Popup) { 0081 return false; 0082 } 0083 return true; 0084 } 0085 0086 bool KWaylandIntegration::eventFilter(QObject *watched, QEvent *event) 0087 { 0088 if (event->type() == QEvent::Expose) { 0089 auto ee = static_cast<QExposeEvent *>(event); 0090 if (ee->region().isNull()) { 0091 return false; 0092 } 0093 QWindow *w = qobject_cast<QWindow *>(watched); 0094 if (!isRelevantTopLevel(w)) { 0095 return false; 0096 } 0097 if (!w->isVisible()) { 0098 return false; 0099 } 0100 0101 if (w->property("org.kde.plasma.integration.shellSurfaceCreated").isNull()) { 0102 shellSurfaceCreated(w); 0103 } 0104 } else if (event->type() == QEvent::Hide) { 0105 QWindow *w = qobject_cast<QWindow *>(watched); 0106 if (!isRelevantTopLevel(w)) { 0107 return false; 0108 } 0109 shellSurfaceDestroyed(w); 0110 } else if (event->type() == QEvent::ApplicationPaletteChange) { 0111 if (watched != QGuiApplication::instance()) { 0112 return false; 0113 } 0114 const auto topLevelWindows = QGuiApplication::topLevelWindows(); 0115 for (QWindow *w : topLevelWindows) { 0116 if (isRelevantTopLevel(w)) { 0117 installColorScheme(w); 0118 } 0119 } 0120 } else if (event->type() == QEvent::PlatformSurface) { 0121 if (QWindow *w = qobject_cast<QWindow *>(watched)) { 0122 QPlatformSurfaceEvent *pe = static_cast<QPlatformSurfaceEvent *>(event); 0123 if (!w->flags().testFlag(Qt::ForeignWindow)) { 0124 if (pe->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) { 0125 m_platformTheme->windowCreated(w); 0126 } 0127 } 0128 } 0129 } 0130 0131 return false; 0132 } 0133 0134 void KWaylandIntegration::shellSurfaceCreated(QWindow *w) 0135 { 0136 // set colorscheme hint 0137 if (qApp->property(s_schemePropertyName.constData()).isValid()) { 0138 installColorScheme(w); 0139 } 0140 const auto blurBehindProperty = w->property(s_blurBehindPropertyName.constData()); 0141 if (blurBehindProperty.isValid()) { 0142 KWindowEffects::enableBlurBehind(w, blurBehindProperty.toBool()); 0143 } 0144 // create deco 0145 wl_surface *s = surfaceFromWindow(w); 0146 if (!s) { 0147 return; 0148 } 0149 0150 w->setProperty("org.kde.plasma.integration.shellSurfaceCreated", true); 0151 0152 #ifndef KF6_TODO_DBUS_MENUBAR 0153 if (m_appMenuManager->isActive()) { 0154 auto menu = new AppMenu(m_appMenuManager->create(s)); 0155 w->setProperty("org.kde.plasma.integration.appmenu", QVariant::fromValue(menu)); 0156 auto menuBar = QDBusMenuBar::menuBarForWindow(w); 0157 if (!menuBar) { 0158 menuBar = QDBusMenuBar::globalMenuBar(); 0159 } 0160 if (menuBar) { 0161 menu->set_address(QDBusConnection::sessionBus().baseService(), menuBar->objectPath()); 0162 } 0163 } 0164 #endif 0165 } 0166 0167 void KWaylandIntegration::shellSurfaceDestroyed(QWindow *w) 0168 { 0169 w->setProperty("org.kde.plasma.integration.shellSurfaceCreated", QVariant()); 0170 0171 auto appMenu = w->property("org.kde.plasma.integration.appmenu").value<AppMenu *>(); 0172 if (appMenu) { 0173 appMenu->release(); 0174 delete appMenu; 0175 } 0176 w->setProperty("org.kde.plasma.integration.appmenu", QVariant()); 0177 0178 auto decoPallete = w->property("org.kde.plasma.integration.palette").value<ServerSideDecorationPalette *>(); 0179 if (decoPallete) { 0180 decoPallete->release(); 0181 delete decoPallete; 0182 } 0183 w->setProperty("org.kde.plasma.integration.palette", QVariant()); 0184 } 0185 0186 void KWaylandIntegration::installColorScheme(QWindow *w) 0187 { 0188 if (!m_paletteManager->isActive()) { 0189 return; 0190 } 0191 auto palette = w->property("org.kde.plasma.integration.palette").value<ServerSideDecorationPalette *>(); 0192 if (!palette) { 0193 auto s = surfaceFromWindow(w); 0194 if (!s) { 0195 return; 0196 } 0197 palette = new ServerSideDecorationPalette(m_paletteManager->create(s)); 0198 w->setProperty("org.kde.plasma.integration.palette", QVariant::fromValue(palette)); 0199 } 0200 if (palette) { 0201 palette->set_palette(qApp->property(s_schemePropertyName.constData()).toString()); 0202 } 0203 } 0204 0205 void KWaylandIntegration::setAppMenu(QWindow *window, const QString &serviceName, const QString &objectPath) 0206 { 0207 auto menu = window->property("org.kde.plasma.integration.appmenu").value<AppMenu *>(); 0208 if (menu) { 0209 menu->set_address(serviceName, objectPath); 0210 } 0211 } 0212 0213 wl_surface *KWaylandIntegration::surfaceFromWindow(QWindow *window) 0214 { 0215 QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); 0216 if (!nativeInterface) { 0217 return nullptr; 0218 } 0219 return static_cast<wl_surface *>(nativeInterface->nativeResourceForWindow("surface", window)); 0220 } 0221 0222 #include "kwaylandintegration.moc"