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"