diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8a31b209..2df35d13 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,104 +1,106 @@ # vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab: # Boosting us a bit if (NOT KACTIVITIES_LIBRARY_ONLY) find_package (Boost 1.49 REQUIRED) string (REGEX MATCH "1053.." BOOST_VERSION_BLACKLISTED ${Boost_VERSION}) if (BOOST_VERSION_BLACKLISTED AND NOT KACTIVITIES_ENABLE_EXCEPTIONS) message ( WARNING "Boost.Container 1.53 has issues when exceptions are disabled. " "We will set the KACTIVITIES_ENABLE_EXCEPTIONS option." ) set (KACTIVITIES_ENABLE_EXCEPTIONS ON) endif () endif () if (KACTIVITIES_ENABLE_EXCEPTIONS) string (REPLACE "-fno-exceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") add_definitions (-fexceptions) endif () # Testing for C++0x/C++11 features include (CheckCxxFeatures) cxx_check_feature ("c++11" "auto" "N2546" HAVE_CXX11_AUTO "${ADDITIONAL_DEFINITIONS}") cxx_check_feature ("c++11" "nullptr" "N2431" HAVE_CXX11_NULLPTR "${ADDITIONAL_DEFINITIONS}") cxx_check_feature ("c++11" "lambda" "N2927" HAVE_CXX11_LAMBDA "${ADDITIONAL_DEFINITIONS}") cxx_check_feature ("c++11" "override" "N3206" HAVE_CXX11_OVERRIDE "${ADDITIONAL_DEFINITIONS}") cxx_check_feature ("c++11" "unique_ptr" "none" HAVE_CXX11_UNIQUE_PTR "${ADDITIONAL_DEFINITIONS}") cxx_check_feature ("c++11" "variadic-templates" "N2242" HAVE_CXX11_VARIADIC_TEMPLATES "${ADDITIONAL_DEFINITIONS}") cxx_check_feature ("c++11" "initializer-lists" "N2672" HAVE_CXX11_INITIALIZER_LISTS "${ADDITIONAL_DEFINITIONS}") # We can now actually require some C++11 features even for the library if (NOT HAVE_CXX11_AUTO OR NOT HAVE_CXX11_LAMBDA OR NOT HAVE_CXX11_UNIQUE_PTR) message ( FATAL_ERROR "You need a C++ compiler that supports the following C++11 features: auto, lambdas and move semantics." ) endif () # ======================================================= # Starting the actual project definition # The libraries do not depend on any compile-time features add_subdirectory (lib) # Config file set (KAMD_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") set (KAMD_DATA_DIR "${KDE_INSTALL_DATADIR_KF5}/kactivitymanagerd/") -set (KAMD_PLUGIN_DIR "${CMAKE_INSTALL_FULL_PLUGINDIR}/kactivitymanagerd/") +set (KAMD_PLUGIN_VERSION 1) +set (KAMD_PLUGIN_DIR "kactivitymanagerd/${KAMD_PLUGIN_VERSION}") +set (KAMD_FULL_PLUGIN_DIR "${CMAKE_INSTALL_FULL_PLUGINDIR}/${KAMD_PLUGIN_DIR}/") configure_file (kactivities-features.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kactivities-features.h) include_directories ( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/lib/core ) # Is the compiler modern enough to build the ActivityManager service # and accompanying workspace addons? string (COMPARE EQUAL "${CXX_FEATURES_UNSUPPORTED}" "" CXX_COMPILER_IS_MODERN) if (NOT KACTIVITIES_LIBRARY_ONLY) if (NOT Boost_FOUND) message (WARNING "WARNING: You need boost libraries (range and containers) in order to build the Activity Manager daemon. We will only build the library, setting the KACTIVITIES_LIBRARY_ONLY option to ON." ) set (KACTIVITIES_LIBRARY_ONLY ON) endif () if (NOT HAVE_CXX11_VARIADIC_TEMPLATES OR NOT HAVE_CXX11_INITIALIZER_LISTS) message (WARNING "WARNING: You need a more modern compiler in order to build the Activity Manager daemon. We will only build the library, setting the KACTIVITIES_LIBRARY_ONLY option to ON." ) set (KACTIVITIES_LIBRARY_ONLY ON) endif () include_directories (${Boost_INCLUDE_DIR}) # The compiler is good enough if (CXX_COMPILER_IS_MODERN) message (STATUS "C++11 enabled compiler: Your compiler is state-of-the-art" ) else () message (STATUS "C++11 enabled compiler:" "Your compiler doesn't support the following features: ${CXX_FEATURES_UNSUPPORTED} but the list of the supported ones is sufficient for the build: ${CXX_FEATURES_SUPPORTED}" ) endif () add_subdirectory (imports) add_subdirectory (service) # No point in having workspace addons without the service add_subdirectory (workspace) endif () diff --git a/src/kactivities-features.h.cmake b/src/kactivities-features.h.cmake index b5a6b99a..30aa01b8 100644 --- a/src/kactivities-features.h.cmake +++ b/src/kactivities-features.h.cmake @@ -1,16 +1,17 @@ #ifndef CONFIG_FEATURES_H_ #define CONFIG_FEATURES_H_ #cmakedefine KAMD_DATA_DIR "@KAMD_DATA_DIR@" #cmakedefine KAMD_PLUGIN_DIR "@KAMD_PLUGIN_DIR@" +#cmakedefine KAMD_FULL_PLUGIN_DIR "@KAMD_FULL_PLUGIN_DIR@" #cmakedefine KAMD_INSTALL_PREFIX "@KAMD_INSTALL_PREFIX@" #cmakedefine01 HAVE_CXX11_AUTO #cmakedefine01 HAVE_CXX11_NULLPTR #cmakedefine01 HAVE_CXX11_LAMBDA #cmakedefine01 HAVE_CXX11_OVERRIDE #cmakedefine01 HAVE_CXX_OVERRIDE_ATTR #endif diff --git a/src/service/Application.cpp b/src/service/Application.cpp index 682606a1..0f3e06a7 100644 --- a/src/service/Application.cpp +++ b/src/service/Application.cpp @@ -1,421 +1,413 @@ /* * Copyright (C) 2010, 2011, 2012, 2013, 2014 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. */ // Self #include #include "Application.h" // Qt #include #include #include #include #include #include // KDE // #include // #include // #include -#include +#include +#include #include #include #include -#include // Boost and utils #include #include #include // System #include #include #include #include // Local #include "Activities.h" #include "Resources.h" #include "Features.h" #include "Config.h" #include "Plugin.h" #include "Debug.h" #include "common/dbus/common.h" namespace { QList s_moduleThreads; } // Runs a QObject inside a QThread template T *runInQThread() { T *object = new T(); class Thread : public QThread { public: Thread(T *ptr = Q_NULLPTR) : QThread() , object(ptr) { } void run() Q_DECL_OVERRIDE { std::unique_ptr o(object); exec(); } private: T *object; } *thread = new Thread(object); s_moduleThreads << thread; object->moveToThread(thread); thread->start(); return object; } class Application::Private { public: Private() { } static inline bool isPluginEnabled(const KConfigGroup &config, - QExplicitlySharedDataPointer plugin) - // const QExplicitlySharedDataPointer &plugin) + const KPluginMetaData& plugin) { - const auto pluginName - = plugin->property("X-KDE-PluginInfo-Name", - QVariant::String).toString(); - // qDebug() << "Plugin Name is " << pluginName; + const auto pluginName = plugin.pluginId(); + qCDebug(KAMD_LOG_APPLICATION) << "Plugin Name is " << pluginName << plugin.fileName(); if (pluginName == "org.kde.ActivityManager.ResourceScoring") { // SQLite plugin is necessary for the proper workspace behaviour return true; - } else { - return config.readEntry( - pluginName + "Enabled", - plugin->property("X-KDE-PluginInfo-EnabledByDefault", - QVariant::Bool).toBool()); + return config.readEntry(pluginName + "Enabled", plugin.isEnabledByDefault()); } } - bool loadPlugin(KService::Ptr offer); + bool loadPlugin(const KPluginMetaData& plugin); Resources *resources; Activities *activities; Features *features; QStringList pluginIds; static Application *s_instance; }; Application *Application::Private::s_instance = Q_NULLPTR; Application::Application(int &argc, char **argv) : QApplication(argc, argv) { } void Application::init() { if (!KDBusConnectionPool::threadConnection().registerService( KAMD_DBUS_SERVICE)) { exit(EXIT_SUCCESS); } // KAMD is a daemon, if it crashes it is not a problem as // long as it restarts properly // TODO: Restart on crash // KCrash::setFlags(KCrash::AutoRestart); d->resources = runInQThread(); d->activities = runInQThread(); d->features = runInQThread(); /* d->config */ new Config(this); // this does not need a separate thread QMetaObject::invokeMethod(this, "loadPlugins", Qt::QueuedConnection); QDBusConnection::sessionBus().registerObject("/ActivityManager", this, QDBusConnection::ExportAllSlots); } -bool Application::Private::loadPlugin(KService::Ptr offer) +bool Application::Private::loadPlugin(const KPluginMetaData& plugin) { - if (!offer) { + if (!plugin.isValid()) { qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] plugin offer not valid"; return false; } - if (pluginIds.contains(offer->storageId())) { - qCDebug(KAMD_LOG_APPLICATION) << "[ OK ] already loaded: " << offer->name(); + if (pluginIds.contains(plugin.pluginId())) { + qCDebug(KAMD_LOG_APPLICATION) << "[ OK ] already loaded: " << plugin.pluginId(); return true; } - QString error; - auto pluginInstance = dynamic_cast( - offer->createInstance(Q_NULLPTR, {}, &error) - ); + KPluginLoader loader(plugin.fileName()); + KPluginFactory* factory = loader.factory(); + if (!factory) { + qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] Could not load KPluginFactory for:" + << plugin.pluginId() << loader.errorString(); + return false; + } + QObject* obj = factory->create(); + auto pluginInstance = factory->create(); auto &modules = Module::get(); if (pluginInstance) { pluginInstance->init(modules); - pluginIds << offer->storageId(); + pluginIds << plugin.pluginId(); - qCDebug(KAMD_LOG_APPLICATION) << "[ OK ] loaded: " << offer->name(); + qCDebug(KAMD_LOG_APPLICATION) << "[ OK ] loaded: " << plugin.pluginId(); return true; } else { - qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] loading: " << offer->name() << error; + qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] loading: " << plugin.pluginId() << loader.errorString(); // TODO: Show a notification for a plugin that failed to load return false; } } void Application::loadPlugins() { - using namespace boost::adaptors; using namespace std::placeholders; - const auto pluginsDir(QLatin1String(KAMD_PLUGIN_DIR)); - QApplication::addLibraryPath(pluginsDir); - const auto config = KSharedConfig::openConfig(QStringLiteral("kactivitymanagerdrc")) ->group("Plugins"); - const auto allOffers - = KServiceTypeTrader::self()->query("ActivityManager/Plugin"); - - const auto filteredOffers = allOffers - | filtered(std::bind(Private::isPluginEnabled, config, _1)); + const auto offers = KPluginLoader::findPlugins(QStringLiteral(KAMD_PLUGIN_DIR), + std::bind(Private::isPluginEnabled, config, _1)); + qCDebug(KAMD_LOG_APPLICATION) << "Found" << offers.size() << "enabled plugins:"; - for (const auto &offer : filteredOffers) { + for (const auto &offer : offers) { d->loadPlugin(offer); } } -bool Application::loadPlugin(const QString &plugin) +bool Application::loadPlugin(const QString &pluginId) { - auto offer = KService::serviceByStorageId(plugin); + auto offers = KPluginLoader::findPluginsById(QStringLiteral(KAMD_PLUGIN_DIR), pluginId); - if (!offer) { - qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] not found: " << plugin; + if (offers.isEmpty()) { + qCWarning(KAMD_LOG_APPLICATION) << "[ FAILED ] not found: " << pluginId; return false; } - return d->loadPlugin(offer); + return d->loadPlugin(offers.first()); } Application::~Application() { for (const auto thread: s_moduleThreads) { thread->quit(); thread->wait(); delete thread; } Private::s_instance = Q_NULLPTR; } int Application::newInstance() { //We don't want to show the mainWindow() return 0; } Activities &Application::activities() const { return *d->activities; } Resources &Application::resources() const { return *d->resources; } // void Application::quit() // { // if (Private::s_instance) { // Private::s_instance->exit(); // delete Private::s_instance; // } // } void Application::quit() { QApplication::quit(); } #include "../lib/core/version.h" QString Application::serviceVersion() const { return KACTIVITIES_VERSION_STRING; } // Leaving object oriented world :) namespace { template Return callOnRunningService(const QString &method) { static QDBusInterface remote(KAMD_DBUS_SERVICE, "/ActivityManager", "org.kde.ActivityManager.Application"); QDBusReply reply = remote.call(method); return (Return)reply; } QString runningServiceVersion() { return callOnRunningService("serviceVersion"); } bool isServiceRunning() { return QDBusConnection::sessionBus().interface()->isServiceRegistered( KAMD_DBUS_SERVICE); } } int main(int argc, char **argv) { Application application(argc, argv); application.setApplicationName(QStringLiteral("ActivityManager")); application.setOrganizationDomain(QStringLiteral("kde.org")); // KAboutData about("kactivitymanagerd", Q_NULLPTR, ki18n("KDE Activity Manager"), "3.0", // ki18n("KDE Activity Management Service"), // KAboutData::License_GPL, // ki18n("(c) 2010, 2011, 2012 Ivan Cukic"), KLocalizedString(), // "http://www.kde.org/"); // KCmdLineArgs::init(argc, argv, &about); const auto arguments = application.arguments(); if (arguments.size() == 0) { exit(EXIT_FAILURE); } else if (arguments.size() != 1 && (arguments.size() != 2 || arguments[1] == "--help")) { QTextStream(stdout) << "start\tStarts the service\n" << "stop\tStops the server\n" << "status\tPrints basic server information\n" << "start-daemon\tStarts the service without forking (use with caution)\n" << "--help\tThis help message\n"; exit(EXIT_SUCCESS); } else if (arguments.size() == 1 || arguments[1] == "start") { // Checking whether the service is already running if (isServiceRunning()) { QTextStream(stdout) << "Already running\n"; exit(EXIT_SUCCESS); } // Creating the watcher, but not on the wall QDBusServiceWatcher watcher(QStringLiteral("org.kde.ActivityManager"), QDBusConnection::sessionBus()); QObject::connect(&watcher, &QDBusServiceWatcher::serviceRegistered, [] (const QString &service) { if (service != KAMD_DBUS_SERVICE) { return; } QTextStream(stdout) << "Service started, version: " << runningServiceVersion() << "\n"; exit(EXIT_SUCCESS); }); // Starting the dameon QProcess::startDetached( KAMD_INSTALL_PREFIX "/bin/kactivitymanagerd", QStringList{"start-daemon"} ); return application.exec(); } else if (arguments[1] == "stop") { if (!isServiceRunning()) { QTextStream(stdout) << "Service not running\n"; exit(EXIT_SUCCESS); } callOnRunningService("quit"); QTextStream(stdout) << "Service stopped\n"; return EXIT_SUCCESS; } else if (arguments[1] == "status") { // Checking whether the service is already running if (isServiceRunning()) { QTextStream(stdout) << "The service is running, version: " << runningServiceVersion() << "\n"; } else { QTextStream(stdout) << "The service is not running\n"; } return EXIT_SUCCESS; } else if (arguments[1] == "start-daemon") { // Really starting the activity manager KDBusService service(KDBusService::Unique); #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0) KAMD_LOG_APPLICATION().setEnabled(QtDebugMsg, true); KAMD_LOG_RESOURCES() .setEnabled(QtDebugMsg, true); KAMD_LOG_ACTIVITIES() .setEnabled(QtDebugMsg, true); KAMD_LOG_APPLICATION().setEnabled(QtWarningMsg, true); KAMD_LOG_RESOURCES() .setEnabled(QtWarningMsg, true); KAMD_LOG_ACTIVITIES() .setEnabled(QtWarningMsg, true); #endif application.init(); return application.exec(); } else { QTextStream(stdout) << "Unrecognized command: " << arguments[1] << '\n'; return EXIT_FAILURE; } } diff --git a/src/service/CMakeLists.txt b/src/service/CMakeLists.txt index 5c8ecc5d..997f7cf6 100644 --- a/src/service/CMakeLists.txt +++ b/src/service/CMakeLists.txt @@ -1,96 +1,94 @@ # vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab: project (ActivityManager) # General find_package (ECM 0.0.8 REQUIRED NO_MODULE) set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) find_package (Qt5 REQUIRED NO_MODULE COMPONENTS Sql Gui Widgets) find_package (KF5Config ${KF5_DEP_VERSION} CONFIG REQUIRED) find_package (KF5CoreAddons ${KF5_DEP_VERSION} CONFIG REQUIRED) find_package (KF5I18n ${KF5_DEP_VERSION} CONFIG REQUIRED) -find_package (KF5Service ${KF5_DEP_VERSION} CONFIG REQUIRED) find_package (KF5WindowSystem ${KF5_DEP_VERSION} CONFIG REQUIRED) find_package (KF5GlobalAccel ${KF5_DEP_VERSION} CONFIG REQUIRED) find_package (KF5XmlGui ${KF5_DEP_VERSION} CONFIG REQUIRED) find_package (KF5KIO ${KF5_DEP_VERSION} CONFIG REQUIRED) # Standard stuff set (CMAKE_INCLUDE_CURRENT_DIR ON) set (plugin_implementation_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/Plugin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Module.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Event.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Debug.cpp ) add_subdirectory (plugins) set (kactivitymanager_SRCS Application.cpp ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/common/dbus/org.kde.ActivityManager.Activities.cpp Activities.cpp Resources.cpp Features.cpp Config.cpp ${plugin_implementation_SRCS} Event.cpp ksmserver/KSMServer.cpp ) qt5_add_dbus_adaptor ( kactivitymanager_SRCS ../common/dbus/org.kde.ActivityManager.Activities.xml Activities.h Activities ) qt5_add_dbus_adaptor ( kactivitymanager_SRCS ../common/dbus/org.kde.ActivityManager.Resources.xml Resources.h Resources ) qt5_add_dbus_adaptor ( kactivitymanager_SRCS ../common/dbus/org.kde.ActivityManager.Features.xml Features.h Features ) add_executable (kactivitymanagerd ${kactivitymanager_SRCS}) target_link_libraries (kactivitymanagerd Qt5::Core Qt5::DBus Qt5::Gui KF5::DBusAddons KF5::CoreAddons KF5::ConfigCore KF5::I18n - KF5::Service KF5::WindowSystem ) ########### install application ############### install (FILES files/kactivitymanagerd.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install (TARGETS kactivitymanagerd ${KF5_INSTALL_TARGETS_DEFAULT_ARGS} ) install (FILES files/kactivitymanagerd-plugin.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR} )