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