From c766e5da6dfb06ecddc448af061453fc7da0b76d Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Sat, 7 Nov 2020 21:34:55 +0200 Subject: [PATCH] Introduce infrastructure for compositor extensions The scripting api is not suitable for implementing all features that should not be implemented in libkwin. For example, the krunner integration or screencasting are the things that don't belong to be compiled right into kwin and yet we don't have any other choice. This change introduces a quick and dirty plugin infrastructure that can be used to implement things such as colord integration, krunner integration, etc. --- CMakeLists.txt | 35 +--- autotests/integration/kwin_wayland_test.cpp | 2 + config-kwin.h.cmake | 4 + dbusinterface.cpp | 33 +++ dbusinterface.h | 23 +++ main_wayland.cpp | 11 +- main_x11.cpp | 2 + org.kde.KWin.Plugins.xml | 32 +++ plugin.cpp | 22 ++ plugin.h | 52 +++++ pluginmanager.cpp | 188 ++++++++++++++++++ pluginmanager.h | 51 +++++ plugins/CMakeLists.txt | 3 + plugins/screencast/CMakeLists.txt | 18 ++ .../screencast}/eglnativefence.cpp | 0 .../screencast}/eglnativefence.h | 0 plugins/screencast/main.cpp | 46 +++++ plugins/screencast/metadata.json | 6 + .../screencast}/pipewirecore.cpp | 0 .../screencast}/pipewirecore.h | 0 .../screencast}/pipewirestream.cpp | 0 .../screencast}/pipewirestream.h | 0 .../screencast}/screencastmanager.cpp | 2 +- .../screencast}/screencastmanager.h | 4 +- workspace.cpp | 15 ++ 25 files changed, 516 insertions(+), 33 deletions(-) create mode 100644 org.kde.KWin.Plugins.xml create mode 100644 plugin.cpp create mode 100644 plugin.h create mode 100644 pluginmanager.cpp create mode 100644 pluginmanager.h create mode 100644 plugins/screencast/CMakeLists.txt rename {screencast => plugins/screencast}/eglnativefence.cpp (100%) rename {screencast => plugins/screencast}/eglnativefence.h (100%) create mode 100644 plugins/screencast/main.cpp create mode 100644 plugins/screencast/metadata.json rename {screencast => plugins/screencast}/pipewirecore.cpp (100%) rename {screencast => plugins/screencast}/pipewirecore.h (100%) rename {screencast => plugins/screencast}/pipewirestream.cpp (100%) rename {screencast => plugins/screencast}/pipewirestream.h (100%) rename {screencast => plugins/screencast}/screencastmanager.cpp (99%) rename {screencast => plugins/screencast}/screencastmanager.h (93%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c8d1b354e2..caa2104520 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) -project(KWin) -set(PROJECT_VERSION "5.20.80") -set(PROJECT_VERSION_MAJOR 5) +set(PROJECT_VERSION "5.20.80") # Handled by release scripts +project(KWin VERSION ${PROJECT_VERSION}) set(QT_MIN_VERSION "5.15.0") set(KF5_MIN_VERSION "5.74") @@ -506,6 +505,8 @@ set(kwin_SRCS overlaywindow.cpp placement.cpp platform.cpp + plugin.cpp + pluginmanager.cpp pointer_input.cpp popup_input_filter.cpp rootinfo_filter.cpp @@ -633,6 +634,7 @@ qt5_add_dbus_adaptor(kwin_SRCS org.kde.kwin.ColorCorrect.xml colorcorrection/col qt5_add_dbus_adaptor(kwin_SRCS ${kwin_effects_dbus_xml} effects.h KWin::EffectsHandlerImpl) qt5_add_dbus_adaptor(kwin_SRCS org.kde.KWin.VirtualDesktopManager.xml dbusinterface.h KWin::VirtualDesktopManagerDBusInterface) qt5_add_dbus_adaptor(kwin_SRCS org.kde.KWin.Session.xml sm.h KWin::SessionManager) +qt5_add_dbus_adaptor(kwin_SRCS org.kde.KWin.Plugins.xml dbusinterface.h KWin::PluginManagerDBusInterface) if (KWIN_BUILD_RUNNERS) qt5_add_dbus_adaptor(kwin_SRCS "runners/org.kde.krunner1.xml" runners/windowsrunnerinterface.h KWin::WindowsRunner) endif() @@ -781,30 +783,8 @@ set(kwin_WAYLAND_SRCS main_wayland.cpp tabletmodemanager.cpp ) -ecm_qt_declare_logging_category(kwin_WAYLAND_SRCS - HEADER - kwinscreencast_logging.h - IDENTIFIER - KWIN_SCREENCAST - CATEGORY_NAME - kwin_screencast - DEFAULT_SEVERITY - Warning -) add_executable(kwin_wayland ${kwin_WAYLAND_SRCS} ${kwin_XWAYLAND_SRCS}) - - -if (PipeWire_FOUND) - target_sources(kwin_wayland - PRIVATE - screencast/eglnativefence.cpp - screencast/screencastmanager.cpp - screencast/pipewirecore.cpp - screencast/pipewirestream.cpp) - target_link_libraries(kwin_wayland PkgConfig::PipeWire) -endif() - target_link_libraries(kwin_wayland kwin KF5::Crash @@ -834,6 +814,10 @@ target_link_libraries(kwin_wayland KF5IdleTimeKWinPlugin ) +if (PipeWire_FOUND) + target_link_libraries(kwin_wayland KWinScreencastPlugin) +endif() + ########### install files ############### install(FILES kwin.kcfg DESTINATION ${KCFG_INSTALL_DIR} RENAME ${KWIN_NAME}.kcfg) @@ -846,6 +830,7 @@ install( org.kde.kwin.ColorCorrect.xml org.kde.kwin.Compositing.xml org.kde.kwin.Effects.xml + org.kde.KWin.Plugins.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} ) diff --git a/autotests/integration/kwin_wayland_test.cpp b/autotests/integration/kwin_wayland_test.cpp index 3ba87233df..99fddee84b 100644 --- a/autotests/integration/kwin_wayland_test.cpp +++ b/autotests/integration/kwin_wayland_test.cpp @@ -8,6 +8,7 @@ */ #include "kwin_wayland_test.h" #include "../../platform.h" +#include "../../pluginmanager.h" #include "../../composite.h" #include "../../effects.h" #include "../../wayland_server.h" @@ -130,6 +131,7 @@ void WaylandTestApplication::performStartup() // try creating the Wayland Backend createInput(); createBackend(); + PluginManager::create(this); } void WaylandTestApplication::createBackend() diff --git a/config-kwin.h.cmake b/config-kwin.h.cmake index 25003e01bd..68b1ec1084 100644 --- a/config-kwin.h.cmake +++ b/config-kwin.h.cmake @@ -1,3 +1,7 @@ +#define KWIN_VERSION_MAJOR ${PROJECT_VERSION_MAJOR} +#define KWIN_VERSION_MINOR ${PROJECT_VERSION_MINOR} +#define KWIN_VERSION_PATCH ${PROJECT_VERSION_PATCH} + #cmakedefine KWIN_BUILD_DECORATIONS 1 #cmakedefine KWIN_BUILD_TABBOX 1 #cmakedefine KWIN_BUILD_ACTIVITIES 1 diff --git a/dbusinterface.cpp b/dbusinterface.cpp index fcd9502068..ef66a3d79f 100644 --- a/dbusinterface.cpp +++ b/dbusinterface.cpp @@ -10,6 +10,7 @@ // own #include "dbusinterface.h" #include "compositingadaptor.h" +#include "pluginsadaptor.h" #include "virtualdesktopmanageradaptor.h" // kwin @@ -20,6 +21,7 @@ #include "main.h" #include "placement.h" #include "platform.h" +#include "pluginmanager.h" #include "kwinadaptor.h" #include "scene.h" #include "unmanaged.h" @@ -515,4 +517,35 @@ void VirtualDesktopManagerDBusInterface::removeDesktop(const QString &id) m_manager->removeVirtualDesktop(id.toUtf8()); } +PluginManagerDBusInterface::PluginManagerDBusInterface(PluginManager *manager) + : QObject(manager) + , m_manager(manager) +{ + new PluginsAdaptor(this); + + QDBusConnection::sessionBus().registerObject(QStringLiteral("/Plugins"), + QStringLiteral("org.kde.KWin.Plugins"), + this); +} + +QStringList PluginManagerDBusInterface::loadedPlugins() const +{ + return m_manager->loadedPlugins(); +} + +QStringList PluginManagerDBusInterface::availablePlugins() const +{ + return m_manager->availablePlugins(); +} + +bool PluginManagerDBusInterface::LoadPlugin(const QString &name) +{ + return m_manager->loadPlugin(name); +} + +void PluginManagerDBusInterface::UnloadPlugin(const QString &name) +{ + m_manager->unloadPlugin(name); +} + } // namespace diff --git a/dbusinterface.h b/dbusinterface.h index 6b8e2b4fa4..85644f85b2 100644 --- a/dbusinterface.h +++ b/dbusinterface.h @@ -19,6 +19,7 @@ namespace KWin { class Compositor; +class PluginManager; class VirtualDesktopManager; /** @@ -238,6 +239,28 @@ private: VirtualDesktopManager *m_manager; }; +class PluginManagerDBusInterface : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.Plugins") + + Q_PROPERTY(QStringList LoadedPlugins READ loadedPlugins) + Q_PROPERTY(QStringList AvailablePlugins READ availablePlugins) + +public: + explicit PluginManagerDBusInterface(PluginManager *manager); + + QStringList loadedPlugins() const; + QStringList availablePlugins() const; + +public Q_SLOTS: + bool LoadPlugin(const QString &name); + void UnloadPlugin(const QString &name); + +private: + PluginManager *m_manager; +}; + } // namespace #endif // KWIN_DBUS_INTERFACE_H diff --git a/main_wayland.cpp b/main_wayland.cpp index aa9223fc11..216dda0d5d 100644 --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -14,11 +14,9 @@ // kwin #include "platform.h" #include "effects.h" +#include "pluginmanager.h" #include "tabletmodemanager.h" -#ifdef PipeWire_FOUND -#include "screencast/screencastmanager.h" -#endif #include "wayland_server.h" #include "xwl/xwayland.h" @@ -64,6 +62,9 @@ Q_IMPORT_PLUGIN(KWinIntegrationPlugin) Q_IMPORT_PLUGIN(KGlobalAccelImpl) Q_IMPORT_PLUGIN(KWindowSystemKWinPlugin) Q_IMPORT_PLUGIN(KWinIdleTimePoller) +#ifdef PipeWire_FOUND +Q_IMPORT_PLUGIN(ScreencastManagerFactory) +#endif namespace KWin { @@ -157,9 +158,7 @@ void ApplicationWayland::performStartup() InputMethod::create(this); createBackend(); TabletModeManager::create(this); -#ifdef PipeWire_FOUND - new ScreencastManager(this); -#endif + PluginManager::create(this); } void ApplicationWayland::createBackend() diff --git a/main_x11.cpp b/main_x11.cpp index 1f356d4fd3..32d27a4637 100644 --- a/main_x11.cpp +++ b/main_x11.cpp @@ -13,6 +13,7 @@ #include #include "platform.h" +#include "pluginmanager.h" #include "sm.h" #include "workspace.h" #include "xcbutils.h" @@ -248,6 +249,7 @@ void ApplicationX11::performStartup() connect(platform(), &Platform::screensQueried, this, [this] { createWorkspace(); + PluginManager::create(this); Xcb::sync(); // Trigger possible errors, there's still a chance to abort diff --git a/org.kde.KWin.Plugins.xml b/org.kde.KWin.Plugins.xml new file mode 100644 index 0000000000..43a3f736d6 --- /dev/null +++ b/org.kde.KWin.Plugins.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin.cpp b/plugin.cpp new file mode 100644 index 0000000000..cc869fab1d --- /dev/null +++ b/plugin.cpp @@ -0,0 +1,22 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "plugin.h" + +namespace KWin +{ + +Plugin::Plugin(QObject *parent) + : QObject(parent) +{ +} + +PluginFactory::PluginFactory(QObject *parent) + : QObject(parent) +{ +} + +} // namespace KWin diff --git a/plugin.h b/plugin.h new file mode 100644 index 0000000000..09a7891384 --- /dev/null +++ b/plugin.h @@ -0,0 +1,52 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include + +#include "kwinglobals.h" + +#include + +namespace KWin +{ + +#define KWIN_PLUGIN_API_VERSION QT_VERSION_CHECK(KWIN_VERSION_MAJOR, \ + KWIN_VERSION_MINOR, \ + KWIN_VERSION_PATCH) + +#define PluginFactory_iid "org.kde.kwin.PluginFactoryInterface" + +/** + * The Plugin class is the baseclass for all binary compositor extensions. + * + * Note that a binary extension must be recompiled with every new KWin release. + */ +class KWIN_EXPORT Plugin : public QObject +{ + Q_OBJECT + +public: + explicit Plugin(QObject *parent = nullptr); +}; + +/** + * The PluginFactory class creates binary compositor extensions. + */ +class KWIN_EXPORT PluginFactory : public QObject +{ + Q_OBJECT + +public: + explicit PluginFactory(QObject *parent = nullptr); + + virtual Plugin *create() const = 0; +}; + +} // namespace KWin + +Q_DECLARE_INTERFACE(KWin::PluginFactory, PluginFactory_iid) diff --git a/pluginmanager.cpp b/pluginmanager.cpp new file mode 100644 index 0000000000..3b7903779f --- /dev/null +++ b/pluginmanager.cpp @@ -0,0 +1,188 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "pluginmanager.h" +#include "dbusinterface.h" +#include "main.h" +#include "plugin.h" +#include "utils.h" + +#include +#include +#include +#include + +namespace KWin +{ + +KWIN_SINGLETON_FACTORY(PluginManager) + +static const QString s_pluginDirectory = QStringLiteral("kwin/plugins"); + +static QJsonValue readPluginInfo(const QJsonObject &metadata, const QString &key) +{ + return metadata.value(QLatin1String("KPlugin")).toObject().value(key); +} + +PluginManager::PluginManager(QObject *parent) + : QObject(parent) +{ + const KConfigGroup config(kwinApp()->config(), QStringLiteral("Plugins")); + + auto checkEnabled = [&config](const QString &pluginId, const QJsonObject &metadata) { + const QString configKey = pluginId + QLatin1String("Enabled"); + if (config.hasKey(configKey)) { + return config.readEntry(configKey, false); + } + return readPluginInfo(metadata, QStringLiteral("EnabledByDefault")).toBool(false); + }; + + const QVector staticPlugins = QPluginLoader::staticPlugins(); + for (const QStaticPlugin &staticPlugin : staticPlugins) { + const QJsonObject rootMetaData = staticPlugin.metaData(); + if (rootMetaData.value(QLatin1String("IID")) != QLatin1String(PluginFactory_iid)) { + continue; + } + + const QJsonObject pluginMetaData = rootMetaData.value(QLatin1String("MetaData")).toObject(); + const QString pluginId = readPluginInfo(pluginMetaData, QStringLiteral("Id")).toString(); + if (pluginId.isEmpty()) { + continue; + } + if (m_staticPlugins.contains(pluginId)) { + qCWarning(KWIN_CORE) << "Conflicting plugin id" << pluginId; + continue; + } + + m_staticPlugins.insert(pluginId, staticPlugin); + + if (checkEnabled(pluginId, pluginMetaData)) { + loadStaticPlugin(pluginId); + } + } + + const QVector plugins = KPluginLoader::findPlugins(s_pluginDirectory); + for (const KPluginMetaData &metadata : plugins) { + if (m_plugins.contains(metadata.pluginId())) { + qCWarning(KWIN_CORE) << "Conflicting plugin id" << metadata.pluginId(); + continue; + } + if (checkEnabled(metadata.pluginId(), metadata.rawData())) { + loadDynamicPlugin(metadata); + } + } + + new PluginManagerDBusInterface(this); +} + +PluginManager::~PluginManager() +{ + s_self = nullptr; +} + +QStringList PluginManager::loadedPlugins() const +{ + return m_plugins.keys(); +} + +QStringList PluginManager::availablePlugins() const +{ + QStringList ret = m_staticPlugins.keys(); + + const QVector plugins = KPluginLoader::findPlugins(s_pluginDirectory); + for (const KPluginMetaData &metadata : plugins) { + ret.append(metadata.pluginId()); + } + + return ret; +} + +bool PluginManager::loadPlugin(const QString &pluginId) +{ + if (m_plugins.contains(pluginId)) { + qCDebug(KWIN_CORE) << "Plugin with id" << pluginId << "is already loaded"; + return false; + } + return loadStaticPlugin(pluginId) || loadDynamicPlugin(pluginId); +} + +bool PluginManager::loadStaticPlugin(const QString &pluginId) +{ + auto staticIt = m_staticPlugins.find(pluginId); + if (staticIt == m_staticPlugins.end()) { + return false; + } + + QScopedPointer factory(qobject_cast(staticIt->instance())); + if (!factory) { + qCWarning(KWIN_CORE) << "Failed to get plugin factory for" << pluginId; + return false; + } + + return instantiatePlugin(pluginId, factory.data()); +} + +bool PluginManager::loadDynamicPlugin(const QString &pluginId) +{ + const auto offers = KPluginLoader::findPluginsById(s_pluginDirectory, pluginId); + for (const KPluginMetaData &metadata : offers) { + if (loadDynamicPlugin(metadata)) { + return true; + } + } + return false; +} + +bool PluginManager::loadDynamicPlugin(const KPluginMetaData &metadata) +{ + if (!metadata.isValid()) { + qCDebug(KWIN_CORE) << "PluginManager::loadPlugin needs a valid plugin metadata"; + return false; + } + + const QString pluginId = metadata.pluginId(); + KPluginLoader pluginLoader(metadata.fileName()); + if (pluginLoader.pluginVersion() != KWIN_PLUGIN_API_VERSION) { + qCWarning(KWIN_CORE) << pluginId << "has mismatching plugin version"; + return false; + } + + QScopedPointer factory(qobject_cast(pluginLoader.instance())); + if (!factory) { + qCWarning(KWIN_CORE) << "Failed to get plugin factory for" << pluginId; + return false; + } + + return instantiatePlugin(pluginId, factory.data()); +} + +bool PluginManager::instantiatePlugin(const QString &pluginId, PluginFactory *factory) +{ + Plugin *plugin = factory->create(); + if (!plugin) { + return false; + } + + m_plugins.insert(pluginId, plugin); + plugin->setParent(this); + + connect(plugin, &QObject::destroyed, this, [this, pluginId]() { + m_plugins.remove(pluginId); + }); + + return true; +} + +void PluginManager::unloadPlugin(const QString &pluginId) +{ + Plugin *plugin = m_plugins.take(pluginId); + if (!plugin) { + qCWarning(KWIN_CORE) << "No plugin with the specified id:" << pluginId; + } + delete plugin; +} + +} // namespace KWin diff --git a/pluginmanager.h b/pluginmanager.h new file mode 100644 index 0000000000..b90fa368a1 --- /dev/null +++ b/pluginmanager.h @@ -0,0 +1,51 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "kwinglobals.h" + +#include +#include +#include + +#include + +namespace KWin +{ + +class Plugin; +class PluginFactory; + +/** + * The PluginManager class loads and unloads binary compositor extensions. + */ +class KWIN_EXPORT PluginManager : public QObject +{ + Q_OBJECT + +public: + ~PluginManager() override; + + QStringList loadedPlugins() const; + QStringList availablePlugins() const; + +public Q_SLOTS: + bool loadPlugin(const QString &pluginId); + void unloadPlugin(const QString &pluginId); + +private: + bool loadStaticPlugin(const QString &pluginId); + bool loadDynamicPlugin(const KPluginMetaData &metadata); + bool loadDynamicPlugin(const QString &pluginId); + bool instantiatePlugin(const QString &pluginId, PluginFactory *factory); + + QHash m_plugins; + QHash m_staticPlugins; + KWIN_SINGLETON(PluginManager) +}; + +} // namespace KWin diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 9f26622d9d..a80b502cdd 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -9,3 +9,6 @@ add_subdirectory(kpackage) if (KWIN_BUILD_DECORATIONS) add_subdirectory(kdecorations) endif() +if (PipeWire_FOUND) + add_subdirectory(screencast) +endif() diff --git a/plugins/screencast/CMakeLists.txt b/plugins/screencast/CMakeLists.txt new file mode 100644 index 0000000000..a44c6eecb0 --- /dev/null +++ b/plugins/screencast/CMakeLists.txt @@ -0,0 +1,18 @@ +set(screencast_SOURCES + eglnativefence.cpp + main.cpp + pipewirecore.cpp + pipewirestream.cpp + screencastmanager.cpp +) + +ecm_qt_declare_logging_category(screencast_SOURCES + HEADER kwinscreencast_logging.h + IDENTIFIER KWIN_SCREENCAST + CATEGORY_NAME kwin_screencast + DEFAULT_SEVERITY Warning +) + +add_library(KWinScreencastPlugin OBJECT ${screencast_SOURCES}) +target_compile_definitions(KWinScreencastPlugin PRIVATE QT_STATICPLUGIN) +target_link_libraries(KWinScreencastPlugin kwin PkgConfig::PipeWire) diff --git a/screencast/eglnativefence.cpp b/plugins/screencast/eglnativefence.cpp similarity index 100% rename from screencast/eglnativefence.cpp rename to plugins/screencast/eglnativefence.cpp diff --git a/screencast/eglnativefence.h b/plugins/screencast/eglnativefence.h similarity index 100% rename from screencast/eglnativefence.h rename to plugins/screencast/eglnativefence.h diff --git a/plugins/screencast/main.cpp b/plugins/screencast/main.cpp new file mode 100644 index 0000000000..001c7718a1 --- /dev/null +++ b/plugins/screencast/main.cpp @@ -0,0 +1,46 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "screencastmanager.h" +#include "main.h" + +#include + +using namespace KWin; + +class KWIN_EXPORT ScreencastManagerFactory : public PluginFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID PluginFactory_iid FILE "metadata.json") + Q_INTERFACES(KWin::PluginFactory) + +public: + explicit ScreencastManagerFactory(QObject *parent = nullptr); + + Plugin *create() const override; +}; + +ScreencastManagerFactory::ScreencastManagerFactory(QObject *parent) + : PluginFactory(parent) +{ +} + +Plugin *ScreencastManagerFactory::create() const +{ + switch (kwinApp()->operationMode()) { + case Application::OperationModeX11: + return nullptr; + case Application::OperationModeXwayland: + case Application::OperationModeWaylandOnly: + return new ScreencastManager(); + default: + return nullptr; + } +} + +K_EXPORT_PLUGIN_VERSION(KWIN_PLUGIN_API_VERSION) + +#include "main.moc" diff --git a/plugins/screencast/metadata.json b/plugins/screencast/metadata.json new file mode 100644 index 0000000000..819d9268c8 --- /dev/null +++ b/plugins/screencast/metadata.json @@ -0,0 +1,6 @@ +{ + "KPlugin": { + "EnabledByDefault": true, + "Id": "kwin5_plugin_screencast" + } +} diff --git a/screencast/pipewirecore.cpp b/plugins/screencast/pipewirecore.cpp similarity index 100% rename from screencast/pipewirecore.cpp rename to plugins/screencast/pipewirecore.cpp diff --git a/screencast/pipewirecore.h b/plugins/screencast/pipewirecore.h similarity index 100% rename from screencast/pipewirecore.h rename to plugins/screencast/pipewirecore.h diff --git a/screencast/pipewirestream.cpp b/plugins/screencast/pipewirestream.cpp similarity index 100% rename from screencast/pipewirestream.cpp rename to plugins/screencast/pipewirestream.cpp diff --git a/screencast/pipewirestream.h b/plugins/screencast/pipewirestream.h similarity index 100% rename from screencast/pipewirestream.h rename to plugins/screencast/pipewirestream.h diff --git a/screencast/screencastmanager.cpp b/plugins/screencast/screencastmanager.cpp similarity index 99% rename from screencast/screencastmanager.cpp rename to plugins/screencast/screencastmanager.cpp index 691a5efafe..0db18a5f97 100644 --- a/screencast/screencastmanager.cpp +++ b/plugins/screencast/screencastmanager.cpp @@ -28,7 +28,7 @@ namespace KWin { ScreencastManager::ScreencastManager(QObject *parent) - : QObject(parent) + : Plugin(parent) , m_screencast(waylandServer()->display()->createScreencastV1Interface(this)) { connect(m_screencast, &KWaylandServer::ScreencastV1Interface::windowScreencastRequested, diff --git a/screencast/screencastmanager.h b/plugins/screencast/screencastmanager.h similarity index 93% rename from screencast/screencastmanager.h rename to plugins/screencast/screencastmanager.h index 8229ce44aa..b8b9d14348 100644 --- a/screencast/screencastmanager.h +++ b/plugins/screencast/screencastmanager.h @@ -8,6 +8,8 @@ #pragma once +#include "plugin.h" + #include namespace KWin @@ -15,7 +17,7 @@ namespace KWin class PipeWireStream; -class ScreencastManager : public QObject +class ScreencastManager : public Plugin { Q_OBJECT diff --git a/workspace.cpp b/workspace.cpp index 3443005a49..687288343f 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -33,6 +33,7 @@ #include "netinfo.h" #include "outline.h" #include "placement.h" +#include "pluginmanager.h" #include "rules.h" #include "screenedge.h" #include "screens.h" @@ -1669,6 +1670,20 @@ QString Workspace::supportInformation() const support.append(static_cast(effects)->supportInformation(effect)); support.append(QStringLiteral("\n")); } + support.append(QLatin1String("\nLoaded Plugins:\n")); + support.append(QLatin1String("---------------\n")); + QStringList loadedPlugins = PluginManager::self()->loadedPlugins(); + loadedPlugins.sort(); + for (const QString &plugin : qAsConst(loadedPlugins)) { + support.append(plugin + QLatin1Char('\n')); + } + support.append(QLatin1String("\nAvailable Plugins:\n")); + support.append(QLatin1String("------------------\n")); + QStringList availablePlugins = PluginManager::self()->availablePlugins(); + availablePlugins.sort(); + for (const QString &plugin : qAsConst(availablePlugins)) { + support.append(plugin + QLatin1Char('\n')); + } } else { support.append(QStringLiteral("Compositing is not active\n")); }