File indexing completed on 2024-11-17 05:01:38
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 // QWaylandClientExtensionTemplate invokes this with a QueuedConnection 0031 QMetaObject::invokeMethod(this, "addRegistryListener"); 0032 } 0033 }; 0034 0035 class AppMenu : public QtWayland::org_kde_kwin_appmenu 0036 { 0037 public: 0038 using org_kde_kwin_appmenu::org_kde_kwin_appmenu; 0039 ~AppMenu() 0040 { 0041 release(); 0042 } 0043 }; 0044 0045 class ServerSideDecorationPaletteManager : public QWaylandClientExtensionTemplate<ServerSideDecorationPaletteManager>, 0046 public QtWayland::org_kde_kwin_server_decoration_palette_manager 0047 { 0048 Q_OBJECT 0049 public: 0050 ServerSideDecorationPaletteManager() 0051 : QWaylandClientExtensionTemplate<ServerSideDecorationPaletteManager>(1) 0052 { 0053 QMetaObject::invokeMethod(this, "addRegistryListener"); 0054 } 0055 ~ServerSideDecorationPaletteManager() override 0056 { 0057 if (isActive()) { 0058 org_kde_kwin_server_decoration_palette_manager_destroy(object()); 0059 } 0060 } 0061 }; 0062 0063 class ServerSideDecorationPalette : public QtWayland::org_kde_kwin_server_decoration_palette 0064 { 0065 public: 0066 using org_kde_kwin_server_decoration_palette::org_kde_kwin_server_decoration_palette; 0067 ~ServerSideDecorationPalette() override 0068 { 0069 release(); 0070 } 0071 }; 0072 0073 Q_DECLARE_METATYPE(AppMenu *); 0074 Q_DECLARE_METATYPE(ServerSideDecorationPalette *); 0075 0076 KWaylandIntegration::KWaylandIntegration(KdePlatformTheme *platformTheme) 0077 : QObject() 0078 , m_platformTheme(platformTheme) 0079 { 0080 QCoreApplication::instance()->installEventFilter(this); 0081 } 0082 0083 KWaylandIntegration::~KWaylandIntegration() = default; 0084 0085 bool KWaylandIntegration::isRelevantTopLevel(QWindow *w) 0086 { 0087 if (!w || w->parent()) { 0088 return false; 0089 } 0090 0091 // ignore windows that map to XdgPopup 0092 if (w->type() == Qt::ToolTip || w->type() == Qt::Popup) { 0093 return false; 0094 } 0095 return true; 0096 } 0097 0098 bool KWaylandIntegration::eventFilter(QObject *watched, QEvent *event) 0099 { 0100 if (event->type() == QEvent::Expose) { 0101 auto ee = static_cast<QExposeEvent *>(event); 0102 if (ee->region().isNull()) { 0103 return false; 0104 } 0105 QWindow *w = qobject_cast<QWindow *>(watched); 0106 if (!isRelevantTopLevel(w)) { 0107 return false; 0108 } 0109 if (!w->isVisible()) { 0110 return false; 0111 } 0112 0113 if (w->property("org.kde.plasma.integration.shellSurfaceCreated").isNull()) { 0114 shellSurfaceCreated(w); 0115 } 0116 } else if (event->type() == QEvent::Hide) { 0117 QWindow *w = qobject_cast<QWindow *>(watched); 0118 if (!isRelevantTopLevel(w)) { 0119 return false; 0120 } 0121 shellSurfaceDestroyed(w); 0122 } else if (event->type() == QEvent::ApplicationPaletteChange) { 0123 if (watched != QGuiApplication::instance()) { 0124 return false; 0125 } 0126 const auto topLevelWindows = QGuiApplication::topLevelWindows(); 0127 for (QWindow *w : topLevelWindows) { 0128 if (isRelevantTopLevel(w)) { 0129 installColorScheme(w); 0130 } 0131 } 0132 } else if (event->type() == QEvent::PlatformSurface) { 0133 QWindow *w = qobject_cast<QWindow *>(watched); 0134 QPlatformSurfaceEvent *pe = static_cast<QPlatformSurfaceEvent *>(event); 0135 if (w && !w->flags().testFlag(Qt::ForeignWindow) && pe->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) { 0136 m_platformTheme->windowCreated(w); 0137 } 0138 } 0139 0140 return false; 0141 } 0142 0143 void KWaylandIntegration::shellSurfaceCreated(QWindow *w) 0144 { 0145 // set colorscheme hint 0146 if (qApp->property(s_schemePropertyName.constData()).isValid()) { 0147 installColorScheme(w); 0148 } 0149 const auto blurBehindProperty = w->property(s_blurBehindPropertyName.constData()); 0150 if (blurBehindProperty.isValid()) { 0151 KWindowEffects::enableBlurBehind(w, blurBehindProperty.toBool()); 0152 } 0153 // create deco 0154 wl_surface *s = surfaceFromWindow(w); 0155 if (!s) { 0156 return; 0157 } 0158 0159 w->setProperty("org.kde.plasma.integration.shellSurfaceCreated", true); 0160 0161 #ifndef KF6_TODO_DBUS_MENUBAR 0162 if (!m_appMenuManager) { 0163 m_appMenuManager.reset(new AppMenuManager()); 0164 } 0165 if (m_appMenuManager->isActive()) { 0166 auto menu = new AppMenu(m_appMenuManager->create(s)); 0167 w->setProperty("org.kde.plasma.integration.appmenu", QVariant::fromValue(menu)); 0168 auto menuBar = QDBusMenuBar::menuBarForWindow(w); 0169 if (!menuBar) { 0170 menuBar = QDBusMenuBar::globalMenuBar(); 0171 } 0172 if (menuBar) { 0173 menu->set_address(QDBusConnection::sessionBus().baseService(), menuBar->objectPath()); 0174 } 0175 } 0176 #endif 0177 } 0178 0179 void KWaylandIntegration::shellSurfaceDestroyed(QWindow *w) 0180 { 0181 w->setProperty("org.kde.plasma.integration.shellSurfaceCreated", QVariant()); 0182 0183 auto appMenu = w->property("org.kde.plasma.integration.appmenu").value<AppMenu *>(); 0184 if (appMenu) { 0185 delete appMenu; 0186 } 0187 w->setProperty("org.kde.plasma.integration.appmenu", QVariant()); 0188 0189 auto decoPallete = w->property("org.kde.plasma.integration.palette").value<ServerSideDecorationPalette *>(); 0190 if (decoPallete) { 0191 delete decoPallete; 0192 } 0193 w->setProperty("org.kde.plasma.integration.palette", QVariant()); 0194 } 0195 0196 void KWaylandIntegration::installColorScheme(QWindow *w) 0197 { 0198 if (!m_paletteManager) { 0199 m_paletteManager.reset(new ServerSideDecorationPaletteManager()); 0200 } 0201 if (!m_paletteManager->isActive()) { 0202 return; 0203 } 0204 auto palette = w->property("org.kde.plasma.integration.palette").value<ServerSideDecorationPalette *>(); 0205 if (!palette) { 0206 auto s = surfaceFromWindow(w); 0207 if (!s) { 0208 return; 0209 } 0210 palette = new ServerSideDecorationPalette(m_paletteManager->create(s)); 0211 w->setProperty("org.kde.plasma.integration.palette", QVariant::fromValue(palette)); 0212 } 0213 if (palette) { 0214 palette->set_palette(qApp->property(s_schemePropertyName.constData()).toString()); 0215 } 0216 } 0217 0218 void KWaylandIntegration::setAppMenu(QWindow *window, const QString &serviceName, const QString &objectPath) 0219 { 0220 auto menu = window->property("org.kde.plasma.integration.appmenu").value<AppMenu *>(); 0221 if (menu) { 0222 menu->set_address(serviceName, objectPath); 0223 } 0224 } 0225 0226 wl_surface *KWaylandIntegration::surfaceFromWindow(QWindow *window) 0227 { 0228 QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); 0229 if (!nativeInterface) { 0230 return nullptr; 0231 } 0232 return static_cast<wl_surface *>(nativeInterface->nativeResourceForWindow("surface", window)); 0233 } 0234 0235 #include "kwaylandintegration.moc"