diff --git a/shell/shellmanager.cpp b/shell/shellmanager.cpp index 6f43fc60f..b6ace081d 100644 --- a/shell/shellmanager.cpp +++ b/shell/shellmanager.cpp @@ -1,250 +1,252 @@ /* * Copyright (C) 2013 Ivan Cukic * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * or (at your option) any later version, as published by the Free * Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "shellmanager.h" #include #include #include #include #include #include #include #include #include #include //#include #include "shellcorona.h" #include "config-workspace.h" #include #include #include static const QStringList s_shellsDirs(QStandardPaths::locateAll(QStandardPaths::QStandardPaths::GenericDataLocation, PLASMA_RELATIVE_DATA_INSTALL_DIR "/shells/", QStandardPaths::LocateDirectory)); static const QString s_shellLoaderPath = QStringLiteral("/contents/loader.qml"); bool ShellManager::s_standaloneOption = false; QString ShellManager::s_fixedShell; QString ShellManager::s_testModeLayout; // // ShellManager // class ShellManager::Private { public: Private() : currentHandler(nullptr), corona(nullptr) { shellUpdateDelay.setInterval(100); shellUpdateDelay.setSingleShot(true); } QList handlers; QObject * currentHandler; QTimer shellUpdateDelay; ShellCorona * corona; }; ShellManager::ShellManager() : d(new Private()) { //we have to ensure this is executed after QCoreApplication::exec() QMetaObject::invokeMethod(this, "loadHandlers", Qt::QueuedConnection); + connect(&d->shellUpdateDelay, &QTimer::timeout, + this, &ShellManager::updateShell); } ShellManager::~ShellManager() { // if (d->currentHandler) // d->currentHandler->unload(); } void ShellManager::loadHandlers() { //this should be executed one single time in the app life cycle Q_ASSERT(!d->corona); d->corona = new ShellCorona(this); connect( this, &ShellManager::shellChanged, d->corona, &ShellCorona::setShell ); for (const QString &shellsDir : qAsConst(s_shellsDirs)) { const auto dirs = QDir(shellsDir).entryList(QDir::Dirs | QDir::NoDotAndDotDot); for (const auto &dir : dirs) { const QString qmlFile = shellsDir + dir + s_shellLoaderPath; // qDebug() << "Making a new instance of " << qmlFile; //this shell is not valid, ignore it if (!QFileInfo::exists(qmlFile)) { continue; } auto *handlerContainer = new KDeclarative::QmlObjectSharedEngine(this); handlerContainer->setSource(QUrl::fromLocalFile(qmlFile)); QObject *handler = handlerContainer->rootObject(); if (handler) { handler->setProperty("pluginName", dir); // This property is useful for shells to launch themselves in some specific sessions // For example mediacenter shell can be launched when in plasma-mediacenter session handler->setProperty("currentSession", QString::fromUtf8(qgetenv("DESKTOP_SESSION"))); registerHandler(handler); } } } updateShell(); } void ShellManager::registerHandler(QObject * handler) { // qDebug() << "We got the handler: " << handler->property("shell").toString(); connect( handler, &QObject::destroyed, this, &ShellManager::deregisterHandler ); connect( handler, SIGNAL(willingChanged()), this, SLOT(requestShellUpdate()) ); connect( handler, SIGNAL(priorityChanged()), this, SLOT(requestShellUpdate()) ); d->handlers.push_back(handler); } void ShellManager::deregisterHandler(QObject * handler) { const int removed = d->handlers.removeAll(handler); if (removed > 0) { handler->disconnect(this); } if (d->currentHandler == handler) { d->currentHandler = nullptr; updateShell(); } handler->deleteLater(); } void ShellManager::requestShellUpdate() { d->shellUpdateDelay.start(); } void ShellManager::updateShell() { d->shellUpdateDelay.stop(); if (d->handlers.isEmpty()) { KMessageBox::error(nullptr, //wID, but we don't have a window yet i18nc("Fatal error message body","All shell packages missing.\nThis is an installation issue, please contact your distribution"), i18nc("Fatal error message title", "Plasma Cannot Start")); qCritical("We have no shell handlers installed"); QCoreApplication::exit(-1); } QObject *handler = nullptr; if (!s_fixedShell.isEmpty()) { QList::const_iterator it = std::find_if (d->handlers.cbegin(), d->handlers.cend(), [=] (QObject *handler) { return handler->property("pluginName").toString() == s_fixedShell; }); if (it != d->handlers.cend()) { handler = *it; } else { KMessageBox::error(nullptr, i18nc("Fatal error message body", "Shell package %1 cannot be found", s_fixedShell), i18nc("Fatal error message title", "Plasma Cannot Start")); qCritical("Unable to find the shell plugin '%s'", qPrintable(s_fixedShell)); QCoreApplication::exit(-1); } } else { // Finding the handler that has the priority closest to zero. // We will return a handler even if there are no willing ones. handler =* std::min_element(d->handlers.cbegin(), d->handlers.cend(), [] (QObject * left, QObject * right) { auto willing = [] (QObject * handler) { return handler->property("willing").toBool(); }; auto priority = [] (QObject * handler) { return handler->property("priority").toInt(); }; return // If one is willing and the other is not, // return it - it has the priority willing(left) && !willing(right) ? true : !willing(left) && willing(right) ? false : // otherwise just compare the priorities priority(left) < priority(right); } ); } if (handler == d->currentHandler) return; // Activating the new handler and killing the old one if (d->currentHandler) { d->currentHandler->setProperty("loaded", false); } // handler will never be null, unless there is no shells // available on the system, which is definitely not something // we want to support :) d->currentHandler = handler; d->currentHandler->setProperty("loaded", true); emit shellChanged(d->currentHandler->property("shell").toString()); } ShellManager * ShellManager::instance() { static ShellManager* manager = nullptr; if (!manager) { manager = new ShellManager; } return manager; } Plasma::Corona* ShellManager::corona() const { return d->corona; }