File indexing completed on 2024-10-06 08:03:53

0001 // SPDX-FileCopyrightText: 2023 Méven Car <meven@kde.org>
0002 // SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
0003 // SPDX-License-Identifier: GPL-2.0-or-later
0004 
0005 #include "wallpaperplugin.h"
0006 
0007 #include <QApplication>
0008 #include <QDBusConnection>
0009 #include <QDBusInterface>
0010 #include <QDBusMessage>
0011 #include <QDBusReply>
0012 
0013 #include <KLocalizedString>
0014 #include <KPackage/Package>
0015 #include <KPackage/PackageLoader>
0016 #include <KPluginFactory>
0017 
0018 #include <QCoroDBusPendingCall>
0019 #include <QFile>
0020 #include <QFileInfo>
0021 
0022 WallpaperPlugin::WallpaperPlugin(QObject *parent)
0023     : QObject{parent}
0024     , m_homescreenConfig{new QQmlPropertyMap{this}}
0025     , m_lockscreenConfig{new QQmlPropertyMap{this}}
0026     , m_homescreenConfigFile{KSharedConfig::openConfig("plasma-org.kde.plasma.mobileshell-appletsrc", KConfig::SimpleConfig)}
0027     , m_lockscreenConfigFile{KSharedConfig::openConfig("kscreenlockerrc", KConfig::SimpleConfig)}
0028 {
0029     m_lockscreenConfigWatcher = KConfigWatcher::create(m_lockscreenConfigFile);
0030 
0031     const bool connected = QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.plasmashell"),
0032                                                                  QStringLiteral("/PlasmaShell"),
0033                                                                  QStringLiteral("org.kde.PlasmaShell"),
0034                                                                  QStringLiteral("wallpaperChanged"),
0035                                                                  this,
0036                                                                  SLOT(loadHomescreenSettings()));
0037     if (!connected) {
0038         qWarning() << "Could not connect to dbus service org.kde.plasmashell to listen to wallpaperChanged";
0039     }
0040 
0041     connect(m_lockscreenConfigWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) {
0042         loadLockscreenSettings();
0043     });
0044 
0045     loadLockscreenSettings();
0046     loadHomescreenSettings();
0047 }
0048 
0049 PlasmaQuick::ConfigModel *WallpaperPlugin::wallpaperPluginModel()
0050 {
0051     if (!m_wallpaperPluginModel) {
0052         m_wallpaperPluginModel = new WallpaperConfigModel(this);
0053         QDBusConnection::sessionBus().connect(QString(),
0054                                               QStringLiteral("/KPackage/Plasma/Wallpaper"),
0055                                               QStringLiteral("org.kde.plasma.kpackage"),
0056                                               QStringLiteral("packageInstalled"),
0057                                               m_wallpaperPluginModel,
0058                                               SLOT(repopulate()));
0059         QDBusConnection::sessionBus().connect(QString(),
0060                                               QStringLiteral("/KPackage/Plasma/Wallpaper"),
0061                                               QStringLiteral("org.kde.plasma.kpackage"),
0062                                               QStringLiteral("packageUpdated"),
0063                                               m_wallpaperPluginModel,
0064                                               SLOT(repopulate()));
0065         QDBusConnection::sessionBus().connect(QString(),
0066                                               QStringLiteral("/KPackage/Plasma/Wallpaper"),
0067                                               QStringLiteral("org.kde.plasma.kpackage"),
0068                                               QStringLiteral("packageUninstalled"),
0069                                               m_wallpaperPluginModel,
0070                                               SLOT(repopulate()));
0071     }
0072     return m_wallpaperPluginModel;
0073 }
0074 
0075 QQmlPropertyMap *WallpaperPlugin::homescreenConfiguration() const
0076 {
0077     return m_homescreenConfig;
0078 }
0079 
0080 QQmlPropertyMap *WallpaperPlugin::lockscreenConfiguration() const
0081 {
0082     return m_lockscreenConfig;
0083 }
0084 
0085 QString WallpaperPlugin::homescreenWallpaperPlugin() const
0086 {
0087     return m_homescreenWallpaperPlugin;
0088 }
0089 
0090 QString WallpaperPlugin::homescreenWallpaperPluginSource()
0091 {
0092     if (m_homescreenWallpaperPlugin.isEmpty()) {
0093         return QString();
0094     }
0095 
0096     const auto model = wallpaperPluginModel();
0097     const auto wallpaperPluginCount = model->count();
0098     for (int i = 0; i < wallpaperPluginCount; ++i) {
0099         if (model->data(model->index(i), PlasmaQuick::ConfigModel::PluginNameRole) == m_homescreenWallpaperPlugin) {
0100             return model->data(model->index(i), PlasmaQuick::ConfigModel::SourceRole).toString();
0101         }
0102     }
0103 
0104     return QString();
0105 }
0106 
0107 void WallpaperPlugin::setHomescreenWallpaperPlugin(const QString &wallpaperPlugin)
0108 {
0109     auto containmentsGroup = m_homescreenConfigFile->group(QStringLiteral("Containments"));
0110 
0111     for (const auto &contIndex : containmentsGroup.groupList()) {
0112         const auto contConfig = containmentsGroup.group(contIndex);
0113         if (contConfig.readEntry("activityId").isEmpty()) {
0114             continue;
0115         }
0116 
0117         QString containmentIdx = contIndex;
0118         auto containmentConfigGroup = containmentsGroup.group(containmentIdx);
0119 
0120         // pick first screen that is found to load the wallpaper plugin
0121         m_homescreenConfig = loadConfiguration(containmentConfigGroup, wallpaperPlugin, true);
0122         m_homescreenWallpaperPlugin = wallpaperPlugin;
0123         break;
0124     }
0125 
0126     // saveHomescreenSettings();
0127     Q_EMIT homescreenWallpaperPluginChanged();
0128 }
0129 
0130 QString WallpaperPlugin::lockscreenWallpaperPlugin() const
0131 {
0132     return m_lockscreenWallpaperPlugin;
0133 }
0134 
0135 QString WallpaperPlugin::lockscreenWallpaperPluginSource()
0136 {
0137     if (m_lockscreenWallpaperPlugin.isEmpty()) {
0138         return QString();
0139     }
0140 
0141     const auto model = wallpaperPluginModel();
0142     const auto wallpaperPluginCount = model->count();
0143     for (int i = 0; i < wallpaperPluginCount; ++i) {
0144         if (model->data(model->index(i), PlasmaQuick::ConfigModel::PluginNameRole) == m_lockscreenWallpaperPlugin) {
0145             return model->data(model->index(i), PlasmaQuick::ConfigModel::SourceRole).toString();
0146         }
0147     }
0148 
0149     return QString();
0150 }
0151 
0152 void WallpaperPlugin::setLockscreenWallpaperPlugin(const QString &wallpaperPlugin)
0153 {
0154     KConfigGroup greeterGroup = m_lockscreenConfigFile->group(QStringLiteral("Greeter")).group(QStringLiteral("Wallpaper")).group(wallpaperPlugin);
0155 
0156     m_homescreenConfig = loadConfiguration(greeterGroup, wallpaperPlugin, true);
0157     m_lockscreenWallpaperPlugin = wallpaperPlugin;
0158     saveLockscreenSettings();
0159 
0160     Q_EMIT lockscreenWallpaperPluginChanged();
0161 }
0162 
0163 QCoro::Task<void> WallpaperPlugin::setHomescreenWallpaper(const QString &path)
0164 {
0165     auto message = QDBusMessage::createMethodCall(QLatin1String("org.kde.plasmashell"),
0166                                                   QLatin1String("/PlasmaShell"),
0167                                                   QLatin1String("org.kde.PlasmaShell"),
0168                                                   QLatin1String("setWallpaper"));
0169 
0170     for (uint screen = 0; screen < qApp->screens().size(); screen++) {
0171         message.setArguments({"org.kde.image", QVariantMap{{"Image", path}}, screen});
0172 
0173         const QDBusReply<void> reply = co_await QDBusConnection::sessionBus().asyncCall(message);
0174         if (!reply.isValid()) {
0175             qWarning() << "Failed to set wallpaper for screen" << screen << ":" << reply.error();
0176         }
0177     }
0178 }
0179 
0180 void WallpaperPlugin::setLockscreenWallpaper(const QString &path)
0181 {
0182     auto greeterGroup = m_lockscreenConfigFile->group(QStringLiteral("Greeter"))
0183                             .group(QStringLiteral("Wallpaper"))
0184                             .group(QStringLiteral("org.kde.image"))
0185                             .group(QStringLiteral("General"));
0186     greeterGroup.writeEntry("Image", path, KConfigGroup::Notify);
0187 
0188     greeterGroup = m_lockscreenConfigFile->group(QStringLiteral("Greeter"));
0189     greeterGroup.writeEntry("WallpaperPlugin", "org.kde.image", KConfigGroup::Notify);
0190 
0191     m_lockscreenConfigFile->sync();
0192 }
0193 
0194 QString WallpaperPlugin::homescreenWallpaperPath()
0195 {
0196     return m_homescreenWallpaperPath;
0197 }
0198 
0199 QString WallpaperPlugin::lockscreenWallpaperPath()
0200 {
0201     return m_lockscreenWallpaperPath;
0202 }
0203 
0204 QCoro::Task<void> WallpaperPlugin::loadHomescreenSettings()
0205 {
0206     auto message = QDBusMessage::createMethodCall(QLatin1String("org.kde.plasmashell"),
0207                                                   QLatin1String("/PlasmaShell"),
0208                                                   QLatin1String("org.kde.PlasmaShell"),
0209                                                   QLatin1String("wallpaper"));
0210     message.setArguments({(uint)0}); // assume wallpaper on first screen
0211 
0212     QDBusReply<QVariantMap> reply = co_await QDBusConnection::sessionBus().asyncCall(message);
0213     if (!reply.isValid()) {
0214         qWarning() << "unable to load homescreen wallpaper settings:" << reply.error();
0215         co_return;
0216     }
0217 
0218     QVariantMap map = reply.value();
0219     m_homescreenWallpaperPath = QString{};
0220 
0221     if (!map.contains("wallpaperPlugin")) {
0222         qWarning() << "wallpaperPlugin not found in response from org.kde.PlasmaShell wallpaper(), could not retrieve wallpaper";
0223         Q_EMIT homescreenWallpaperPathChanged();
0224         co_return;
0225     }
0226 
0227     // load wallpaper plugin config
0228     if (m_homescreenConfig) {
0229         m_homescreenConfig->deleteLater();
0230     }
0231     m_homescreenConfig = new QQmlPropertyMap{this};
0232 
0233     for (const auto &key : map.keys()) {
0234         if (key != QStringLiteral("wallpaperPlugin")) {
0235             m_homescreenConfig->insert(key, map[key]);
0236         }
0237     }
0238 
0239     // get wallpaper plugin
0240     m_homescreenWallpaperPlugin = map["wallpaperPlugin"].toString();
0241 
0242     // parse image configuration
0243     if (m_homescreenWallpaperPlugin == QStringLiteral("org.kde.image")) {
0244         m_homescreenWallpaperPath = map["Image"].toString();
0245     }
0246 
0247     Q_EMIT homescreenConfigurationChanged();
0248     Q_EMIT homescreenWallpaperPluginChanged();
0249     Q_EMIT homescreenWallpaperPathChanged();
0250 }
0251 
0252 void WallpaperPlugin::loadLockscreenSettings()
0253 {
0254     auto greeterGroup = m_lockscreenConfigFile->group(QStringLiteral("Greeter"));
0255     m_lockscreenWallpaperPlugin = greeterGroup.readEntry(QStringLiteral("WallpaperPlugin"), QString());
0256     m_lockscreenWallpaperPath = QString{};
0257 
0258     greeterGroup = m_lockscreenConfigFile->group(QStringLiteral("Greeter")).group(QStringLiteral("Wallpaper")).group(m_lockscreenWallpaperPlugin);
0259 
0260     m_lockscreenConfig = static_cast<QQmlPropertyMap *>(loadConfiguration(greeterGroup, m_lockscreenWallpaperPlugin, true));
0261 
0262     if (m_lockscreenWallpaperPlugin == QStringLiteral("org.kde.image")) {
0263         m_lockscreenWallpaperPath = greeterGroup.group(QStringLiteral("General")).readEntry(QStringLiteral("Image"), QString());
0264     }
0265 
0266     Q_EMIT lockscreenWallpaperPluginChanged();
0267     Q_EMIT lockscreenConfigurationChanged();
0268     Q_EMIT lockscreenWallpaperPathChanged();
0269 }
0270 
0271 QQmlPropertyMap *WallpaperPlugin::loadConfiguration(KConfigGroup group, QString wallpaperPlugin, bool loadDefaults)
0272 {
0273     auto packages = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/Wallpaper"), "plasma/wallpapers");
0274     KPackage::Package pkg;
0275     bool found = false;
0276 
0277     for (auto &metaData : packages) {
0278         if (metaData.pluginId() == wallpaperPlugin) {
0279             found = true;
0280             pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Wallpaper"), QFileInfo(metaData.fileName()).path());
0281             break;
0282         }
0283     }
0284 
0285     if (!found || !pkg.isValid()) {
0286         qWarning() << "Could not find wallpaper plugin" << wallpaperPlugin;
0287         return nullptr;
0288     }
0289 
0290     QFile file(pkg.fileUrl("config", "main.xml").toLocalFile());
0291 
0292     auto *configLoader = new KConfigLoader(group, &file, this);
0293     if (loadDefaults) {
0294         configLoader->setDefaults();
0295     }
0296     auto config = new KConfigPropertyMap(configLoader, this);
0297     return config;
0298 }
0299 
0300 QCoro::Task<void> WallpaperPlugin::saveHomescreenSettings()
0301 {
0302     auto iface = new QDBusInterface("org.kde.plasmashell", "/PlasmaShell", "org.kde.PlasmaShell", QDBusConnection::sessionBus(), this);
0303     if (!iface->isValid()) {
0304         qWarning() << "Failed to connect to wallpaper dbus:" << qPrintable(QDBusConnection::sessionBus().lastError().message());
0305         co_return;
0306     }
0307 
0308     QVariantMap params;
0309     for (const auto &key : m_homescreenConfig->keys()) {
0310         params.insert(key, m_homescreenConfig->value(key).toString());
0311     }
0312 
0313     if (m_homescreenWallpaperPlugin == "org.kde.image") {
0314         params.remove("PreviewImage");
0315     }
0316 
0317     for (uint screen = 0; screen < qApp->screens().size(); screen++) {
0318         QList<QVariant> args = {m_homescreenWallpaperPlugin, params, screen};
0319         const QDBusReply<void> response = co_await iface->asyncCallWithArgumentList(QStringLiteral("setWallpaper"), args);
0320         if (!response.isValid()) {
0321             qWarning() << "Failed to set wallpaper:" << response.error();
0322         }
0323     }
0324 }
0325 
0326 void WallpaperPlugin::saveLockscreenSettings()
0327 {
0328     auto greeterGroup = m_lockscreenConfigFile->group(QStringLiteral("Greeter"))
0329                             .group(QStringLiteral("Wallpaper"))
0330                             .group(m_lockscreenWallpaperPlugin)
0331                             .group(QStringLiteral("General"));
0332     for (const auto &key : m_lockscreenConfig->keys()) {
0333         greeterGroup.writeEntry(key, m_lockscreenConfig->value(key), KConfigGroup::Notify);
0334     }
0335 
0336     greeterGroup = m_lockscreenConfigFile->group(QStringLiteral("Greeter"));
0337     greeterGroup.writeEntry("WallpaperPlugin", m_lockscreenWallpaperPlugin, KConfigGroup::Notify);
0338 
0339     m_lockscreenConfigFile->sync();
0340 }
0341 
0342 WallpaperConfigModel::WallpaperConfigModel(QObject *parent)
0343     : PlasmaQuick::ConfigModel(parent)
0344 {
0345     repopulate();
0346 }
0347 
0348 void WallpaperConfigModel::repopulate()
0349 {
0350     clear();
0351     for (const KPluginMetaData &m : KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/Wallpaper"))) {
0352         KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Wallpaper"), m.pluginId());
0353         if (!pkg.isValid()) {
0354             continue;
0355         }
0356         appendCategory(pkg.metadata().iconName(), pkg.metadata().name(), pkg.fileUrl("ui", QStringLiteral("config.qml")).toString(), m.pluginId());
0357     }
0358 }
0359 
0360 #include "wallpaperplugin.moc"