File indexing completed on 2024-05-19 04:26:52
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 1998, 1999 Torben Weis <weis@kde.org> 0003 SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "KoJsonTrader.h" 0009 0010 #include "kis_debug.h" 0011 0012 #include <QCoreApplication> 0013 #include <QPluginLoader> 0014 #include <QJsonObject> 0015 #include <QJsonArray> 0016 #include <QDirIterator> 0017 #include <QDir> 0018 #include <QProcessEnvironment> 0019 #include <QGlobalStatic> 0020 #include <QMutexLocker> 0021 0022 struct KoJsonTrader::PluginCacheEntry 0023 { 0024 QString filePath; 0025 QJsonArray serviceTypes; 0026 QStringList mimeTypes; 0027 QSharedPointer<QPluginLoader> loader; 0028 }; 0029 0030 0031 KoJsonTrader::KoJsonTrader() 0032 { 0033 // Allow a command line variable KRITA_PLUGIN_PATH to override the automatic search 0034 m_pluginPath = QProcessEnvironment::systemEnvironment().value("KRITA_PLUGIN_PATH"); 0035 0036 if (m_pluginPath.isEmpty() || 0037 !(QFileInfo(m_pluginPath).exists() && QFileInfo(m_pluginPath).isDir())) { 0038 0039 QList<QDir> searchDirs; 0040 0041 QDir appDir(qApp->applicationDirPath()); 0042 appDir.cdUp(); 0043 #ifdef Q_OS_MACOS 0044 // Help Krita run without deployment 0045 QDir d(appDir); 0046 d.cd("../../../"); 0047 searchDirs << d; 0048 #endif 0049 0050 #ifdef Q_OS_ANDROID 0051 appDir.cdUp(); 0052 #endif 0053 searchDirs << appDir; 0054 0055 Q_FOREACH (const QDir& dir, searchDirs) { 0056 const QStringList nameFilters = { 0057 #ifdef Q_OS_MACOS 0058 "*PlugIns*", 0059 #endif 0060 "lib*", 0061 }; 0062 Q_FOREACH (const QFileInfo &info, dir.entryInfoList(nameFilters, QDir::Dirs | QDir::NoDotAndDotDot)) { 0063 #ifdef Q_OS_MACOS 0064 if (info.fileName().contains("PlugIns")) { 0065 m_pluginPath = info.absoluteFilePath(); 0066 break; 0067 } 0068 else if (info.fileName().contains("lib")) { 0069 #else 0070 if (info.fileName().contains("lib")) { 0071 #endif 0072 QDir libDir(info.absoluteFilePath()); 0073 0074 #ifdef Q_OS_ANDROID 0075 #if defined(Q_PROCESSOR_ARM_64) 0076 libDir.cd("arm64-v8a"); 0077 #elif defined(Q_PROCESSOR_ARM) 0078 libDir.cd("armeabi-v7a"); 0079 #elif defined(Q_PROCESSOR_X86_64) 0080 libDir.cd("x86_64"); 0081 #elif defined(Q_PROCESSOR_x86) 0082 libDir.cd("x86"); 0083 #endif 0084 m_pluginPath = libDir.absolutePath(); 0085 break; 0086 #else 0087 // on many systems this will be the actual lib dir (and krita subdir contains plugins) 0088 if (libDir.cd("kritaplugins")) { 0089 m_pluginPath = libDir.absolutePath(); 0090 break; 0091 } 0092 0093 // on debian at least the actual libdir is a subdir named like "lib/x86_64-linux-gnu" 0094 // so search there for the Krita subdir which will contain our plugins 0095 // FIXME: what are the chances of there being more than one Krita install with different arch and compiler ABI? 0096 Q_FOREACH (const QFileInfo &subInfo, libDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { 0097 QDir subDir(subInfo.absoluteFilePath()); 0098 if (subDir.cd("kritaplugins")) { 0099 m_pluginPath = subDir.absolutePath(); 0100 break; // will only break inner loop so we need the extra check below 0101 } 0102 } 0103 0104 if (!m_pluginPath.isEmpty()) { 0105 break; 0106 } 0107 #endif 0108 } 0109 } 0110 0111 if (!m_pluginPath.isEmpty()) { 0112 break; 0113 } 0114 } 0115 dbgPlugins << "KoJsonTrader will load its plugins from" << m_pluginPath; 0116 } 0117 0118 initializePluginLoaderCache(); 0119 } 0120 0121 KoJsonTrader::~KoJsonTrader() 0122 { 0123 } 0124 0125 0126 Q_GLOBAL_STATIC(KoJsonTrader, s_instance) 0127 0128 KoJsonTrader* KoJsonTrader::instance() 0129 { 0130 return s_instance; 0131 } 0132 0133 void KoJsonTrader::initializePluginLoaderCache() 0134 { 0135 QMutexLocker l(&m_mutex); 0136 0137 KIS_SAFE_ASSERT_RECOVER_RETURN(m_pluginLoaderCache.isEmpty()); 0138 0139 QList<QPluginLoader *>list; 0140 QDirIterator dirIter(m_pluginPath, QDirIterator::Subdirectories); 0141 while (dirIter.hasNext()) { 0142 dirIter.next(); 0143 #ifdef Q_OS_ANDROID 0144 // files starting with lib_krita are plugins, it is needed because of the loading rules in NDK 0145 if (dirIter.fileInfo().isFile() && dirIter.fileName().startsWith("lib_krita")) { 0146 #elif defined(_MSC_VER) 0147 if (dirIter.fileInfo().isFile() && dirIter.fileName().startsWith("krita") && !dirIter.fileName().endsWith(".pdb") && !dirIter.fileName().endsWith(".lib")) { 0148 #else 0149 if (dirIter.fileInfo().isFile() && dirIter.fileName().startsWith("krita") && !dirIter.fileName().endsWith(".debug")) { 0150 #endif 0151 0152 dbgPlugins << dirIter.fileName(); 0153 QScopedPointer<QPluginLoader> loader(new QPluginLoader(dirIter.filePath())); 0154 QJsonObject json = loader->metaData().value("MetaData").toObject(); 0155 0156 dbgPlugins << json << json.value("X-KDE-ServiceTypes"); 0157 0158 if (json.isEmpty()) { 0159 qWarning() << dirIter.filePath() << "has no json!"; 0160 continue; 0161 } else { 0162 QJsonArray serviceTypes = json.value("X-KDE-ServiceTypes").toArray(); 0163 if (serviceTypes.isEmpty()) { 0164 qWarning() << dirIter.fileName() << "has no X-KDE-ServiceTypes"; 0165 continue; 0166 } 0167 0168 QStringList mimeTypes = json.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); 0169 mimeTypes += json.value("MimeType").toString().split(';'); 0170 mimeTypes += json.value("X-KDE-NativeMimeType").toString(); 0171 0172 PluginCacheEntry cacheEntry; 0173 cacheEntry.filePath = dirIter.filePath(); 0174 cacheEntry.serviceTypes = serviceTypes; 0175 cacheEntry.mimeTypes = mimeTypes; 0176 cacheEntry.loader = toQShared(loader.take()); 0177 m_pluginLoaderCache << cacheEntry; 0178 } 0179 } 0180 } 0181 } 0182 0183 QList<KoJsonTrader::Plugin> KoJsonTrader::query(const QString &servicetype, const QString &mimetype) 0184 { 0185 QMutexLocker l(&m_mutex); 0186 0187 QList<Plugin> list; 0188 Q_FOREACH(const PluginCacheEntry &plugin, m_pluginLoaderCache) { 0189 if (!plugin.serviceTypes.contains(QJsonValue(servicetype))) { 0190 continue; 0191 } 0192 0193 if (!mimetype.isEmpty() && !plugin.mimeTypes.contains(mimetype)) { 0194 continue; 0195 } 0196 0197 list << Plugin(plugin.loader, &m_mutex); 0198 } 0199 0200 return list; 0201 } 0202 0203 KoJsonTrader::Plugin::Plugin(QSharedPointer<QPluginLoader> loader, QMutex *mutex) 0204 : m_loader(loader), 0205 m_mutex(mutex) 0206 { 0207 } 0208 0209 KoJsonTrader::Plugin::~Plugin() 0210 { 0211 } 0212 0213 QObject *KoJsonTrader::Plugin::instance() const 0214 { 0215 QMutexLocker l(m_mutex); 0216 return m_loader->instance(); 0217 } 0218 0219 QJsonObject KoJsonTrader::Plugin::metaData() const 0220 { 0221 return m_loader->metaData(); 0222 } 0223 0224 QString KoJsonTrader::Plugin::fileName() const 0225 { 0226 return m_loader->fileName(); 0227 } 0228 0229 QString KoJsonTrader::Plugin::errorString() const 0230 { 0231 return m_loader->errorString(); 0232 }