Linux multi-monitor fullscreen support
[ryzomcore.git] / studio / src / extension_system / plugin_manager.cpp
blob2a32b551696e8f7da2049b09a32fe0e6763c2ed8
1 // Object Viewer Qt - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2011 Dzmitry KAMIAHIN (dnk-88) <dnk-88@tut.by>
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Winch Gate Property Limited
6 // Copyright (C) 2014 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "plugin_manager.h"
22 #include "plugin_spec.h"
24 #include <QtCore/QDir>
26 #include <nel/misc/debug.h>
28 namespace ExtensionSystem
31 PluginManager::PluginManager(QObject *parent)
32 :IPluginManager(parent),
33 m_settings(0),
34 m_extension("xml")
38 PluginManager::~PluginManager()
40 writeSettings();
41 stopAll();
42 deleteAll();
43 qDeleteAll(m_pluginSpecs);
46 void PluginManager::addObject(QObject *obj)
48 QWriteLocker lock(&m_lock);
49 if (obj == 0)
51 nlwarning("trying to add null object");
52 return;
54 if (m_allObjects.contains(obj))
56 nlwarning("trying to add duplicate object");
57 return;
59 nlinfo("addObject: %s", obj->objectName().toUtf8().constData());
61 m_allObjects.append(obj);
63 Q_EMIT objectAdded(obj);
66 void PluginManager::removeObject(QObject *obj)
68 if (obj == 0)
70 nlwarning("trying to remove null object");
71 return;
74 if (!m_allObjects.contains(obj))
76 nlinfo("object not in list: %s", obj->objectName().toUtf8().constData());
77 return;
79 nlinfo("removeObject: %s", obj->objectName().toUtf8().constData());
81 Q_EMIT aboutToRemoveObject(obj);
82 QWriteLocker lock(&m_lock);
83 m_allObjects.removeAll(obj);
86 QList<QObject *> PluginManager::allObjects() const
88 return m_allObjects;
91 void PluginManager::loadPlugins()
93 Q_FOREACH (PluginSpec *spec, m_pluginSpecs)
94 setPluginState(spec, State::Resolved);
96 QList<PluginSpec *> queue = loadQueue();
97 Q_EMIT pluginCount( queue.count() );
99 Q_FOREACH (PluginSpec *spec, queue)
100 setPluginState(spec, State::Loaded);
102 Q_EMIT pluginsLoaded();
104 Q_FOREACH (PluginSpec *spec, queue)
105 setPluginState(spec, State::Initialized);
107 Q_EMIT pluginsInitialized();
109 QListIterator<PluginSpec *> it(queue);
110 it.toBack();
111 while (it.hasPrevious())
112 setPluginState(it.previous(), State::Running);
114 Q_EMIT pluginsStarted();
116 Q_EMIT pluginsChanged();
119 bool PluginManager::loadPluginSpec( const QString &plugin )
121 nlinfo( "Loading plugin spec %s", plugin.toUtf8().data() );
123 PluginSpec *spec = new PluginSpec;
124 spec->m_pluginManager = this;
125 if( !spec->setSpecFileName( plugin ) )
127 nlinfo( "Error loading plugin spec %s", plugin.toUtf8().data() );
128 return false;
131 m_pluginSpecs.append( spec );
132 m_ipluginSpecs.append( spec );
134 return true;
137 bool PluginManager::loadPlugin( const QString &plugin )
139 if( !loadPluginSpec( plugin ) )
140 return false;
142 ExtensionSystem::PluginSpec *spec = m_pluginSpecs.last();
144 if( !spec->resolveDependencies( m_pluginSpecs ) )
146 nlinfo( "Error resolving dependencies for plugin spec %s", plugin.toUtf8().data() );
147 return false;
150 if( !spec->loadLibrary() )
152 nlinfo( "Error loading plugin %s", spec->fileName().toUtf8().data() );
153 return false;
156 if( !spec->initializePlugin() )
158 nlinfo( "Error initializing plugin %s", spec->fileName().toUtf8().data() );
159 spec->kill();
160 return false;
163 if( !spec->initializeExtensions() )
165 nlinfo( "Error starting plugin %s", spec->fileName().toUtf8().data() );
166 spec->stop();
167 spec->kill();
168 return false;
171 nlinfo( "Loaded plugin %s ( %s )", spec->name().data(), spec->fileName().toUtf8().data() );
173 Q_EMIT pluginsChanged();
175 return true;
178 bool PluginManager::unloadPlugin( ExtensionSystem::IPluginSpec *plugin )
180 ExtensionSystem::PluginSpec *spec = static_cast< ExtensionSystem::PluginSpec* >( plugin );
182 // Let's see if anything depends on this one
183 QListIterator< ExtensionSystem::PluginSpec* > itr( m_pluginSpecs );
184 while( itr.hasNext() )
186 ExtensionSystem::PluginSpec *sp = itr.next();
187 QList< ExtensionSystem::PluginSpec* > l = sp->dependencySpecs();
188 if( l.contains( spec ) )
189 return false;
192 // Stop, delete then remove plugin
193 spec->stop();
194 spec->kill();
195 removePlugin( plugin );
197 return true;
200 void PluginManager::removePlugin( ExtensionSystem::IPluginSpec *plugin )
202 QList< ExtensionSystem::IPluginSpec* >::iterator itr1;
203 QList< ExtensionSystem::PluginSpec* >::iterator itr2;
205 QList< ExtensionSystem::IPluginSpec* >::iterator i1 = m_ipluginSpecs.begin();
206 while( i1 != m_ipluginSpecs.end() )
208 if( *i1 == plugin )
210 itr1 = i1;
211 break;
213 i1++;
216 QList< ExtensionSystem::PluginSpec* >::iterator i2 = m_pluginSpecs.begin();
217 while( i2 != m_pluginSpecs.end() )
219 if( *i2 == static_cast< ExtensionSystem::PluginSpec* >( plugin ) )
221 itr2 = i2;
222 break;
224 i2++;
227 m_ipluginSpecs.erase( itr1 );
228 m_pluginSpecs.erase( itr2 );
229 delete plugin;
231 Q_EMIT pluginsChanged();
235 QStringList PluginManager::getPluginPaths() const
237 return m_pluginPaths;
240 void PluginManager::setPluginPaths(const QStringList &paths)
242 m_pluginPaths = paths;
243 readPluginPaths();
244 readSettings();
247 QList<IPluginSpec *> PluginManager::plugins() const
249 return m_ipluginSpecs;
252 void PluginManager::setSettings(QSettings *settings)
254 m_settings = settings;
257 QSettings *PluginManager::settings() const
259 return m_settings;
262 void PluginManager::readSettings()
264 if (m_settings)
266 QStringList blackList;
267 m_settings->beginGroup("PluginManager");
268 blackList = m_settings->value("BlackList").toStringList();
269 m_settings->endGroup();
270 Q_FOREACH (PluginSpec *spec, m_pluginSpecs)
272 QString pluginName = spec->fileName();
274 if (blackList.contains(pluginName))
276 spec->setEnabled(false);
277 spec->setEnabledStartup(false);
283 void PluginManager::writeSettings()
285 if (m_settings)
287 QStringList blackList;
288 Q_FOREACH(PluginSpec *spec, m_pluginSpecs)
290 if (!spec->isEnabled())
291 blackList.push_back(spec->fileName());
293 m_settings->beginGroup("PluginManager");
294 m_settings->setValue("BlackList", blackList);
295 m_settings->endGroup();
296 m_settings->sync();
300 void PluginManager::readPluginPaths()
302 qDeleteAll(m_pluginSpecs);
303 m_pluginSpecs.clear();
304 m_ipluginSpecs.clear();
306 QStringList pluginsList;
307 QStringList searchPaths = m_pluginPaths;
308 while (!searchPaths.isEmpty())
310 const QDir dir(searchPaths.takeFirst());
311 const QFileInfoList files = dir.entryInfoList(QStringList() << QString("studio_plugin_*.%1").arg(m_extension), QDir::Files);
312 Q_FOREACH (const QFileInfo &file, files)
313 pluginsList << file.absoluteFilePath();
314 const QFileInfoList dirs = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot);
315 Q_FOREACH (const QFileInfo &subdir, dirs)
316 searchPaths << subdir.absoluteFilePath();
319 Q_FOREACH (const QString &pluginFile, pluginsList)
321 loadPluginSpec( pluginFile.toUtf8().data() );
324 Q_EMIT pluginsChanged();
327 void PluginManager::setPluginState(PluginSpec *spec, int destState)
329 if (spec->hasError() || spec->state() != destState-1)
330 return;
332 // plugin in black list
333 if (!spec->isEnabledStartup())
334 return;
336 switch (destState)
338 case State::Resolved:
339 spec->resolveDependencies(m_pluginSpecs);
340 return;
341 case State::Running:
342 Q_EMIT pluginStarting( spec->name().toUtf8().data() );
343 spec->initializeExtensions();
344 return;
345 case State::Deleted:
346 spec->kill();
347 return;
348 default:
349 break;
351 Q_FOREACH (const PluginSpec *depSpec, spec->dependencySpecs())
353 if (depSpec->state() != destState)
355 spec->m_hasError = true;
356 spec->m_errorString = tr("Cannot load plugin because dependency failed to load: %1")
357 .arg(depSpec->name());
358 return;
361 switch (destState)
363 case State::Loaded:
364 Q_EMIT pluginLoading( spec->name().toUtf8().data() );
365 spec->loadLibrary();
366 return;
367 case State::Initialized:
368 Q_EMIT pluginInitializing( spec->name().toUtf8().data() );
369 spec->initializePlugin();
370 break;
371 case State::Stopped:
372 spec->stop();
373 break;
374 default:
375 break;
379 QList<PluginSpec *> PluginManager::loadQueue()
381 QList<PluginSpec *> queue;
382 Q_FOREACH(PluginSpec *spec, m_pluginSpecs)
384 QList<PluginSpec *> circularityCheckQueue;
385 loadQueue(spec, queue, circularityCheckQueue);
387 return queue;
390 bool PluginManager::loadQueue(PluginSpec *spec, QList<PluginSpec *> &queue,
391 QList<PluginSpec *> &circularityCheckQueue)
393 if (queue.contains(spec))
394 return true;
395 // check for circular dependencies
396 if (circularityCheckQueue.contains(spec))
398 spec->m_hasError = true;
399 spec->m_errorString = tr("Circular dependency detected:\n");
400 int index = circularityCheckQueue.indexOf(spec);
401 for (int i = index; i < circularityCheckQueue.size(); ++i)
403 spec->m_errorString.append(tr("%1(%2) depends on\n")
404 .arg(circularityCheckQueue.at(i)->name()).arg(circularityCheckQueue.at(i)->version()));
406 spec->m_errorString.append(tr("%1(%2)").arg(spec->name()).arg(spec->version()));
407 return false;
409 circularityCheckQueue.append(spec);
410 // check if we have the dependencies
411 if (spec->state() == State::Invalid || spec->state() == State::Read)
413 queue.append(spec);
414 return false;
417 // add dependencies
418 Q_FOREACH (PluginSpec *depSpec, spec->dependencySpecs())
420 if (!loadQueue(depSpec, queue, circularityCheckQueue))
422 spec->m_hasError = true;
423 spec->m_errorString =
424 tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
425 .arg(depSpec->name()).arg(depSpec->version()).arg(depSpec->errorString());
426 return false;
429 // add self
430 queue.append(spec);
431 return true;
434 void PluginManager::stopAll()
436 QList<PluginSpec *> queue = loadQueue();
437 Q_FOREACH (PluginSpec *spec, queue)
438 setPluginState(spec, State::Stopped);
441 void PluginManager::deleteAll()
443 QList<PluginSpec *> queue = loadQueue();
444 QListIterator<PluginSpec *> it(queue);
445 it.toBack();
446 while (it.hasPrevious())
448 setPluginState(it.previous(), State::Deleted);
452 }; // namespace ExtensionSystem