From 722d0666d3890eeee94b7f7e93033a0d7cfd08da Mon Sep 17 00:00:00 2001 From: ComixHe Date: Thu, 10 Aug 2023 14:32:09 +0800 Subject: [PATCH] feat: add impl of updateApplicationInfo 1. change the way to traverse files 2. refact some code Signed-off-by: ComixHe Signed-off-by: black-desk --- .../org.desktopspec.ApplicationManager1.xml | 2 +- apps/app-launch-helper/src/main.cpp | 32 +++-- apps/dde-application-manager/src/main.cpp | 38 +++--- src/cgroupsidentifier.cpp | 3 +- src/cgroupsidentifier.h | 2 +- src/dbus/applicationmanager1service.cpp | 114 ++++++++++++++---- src/dbus/applicationmanager1service.h | 10 +- src/dbus/applicationservice.cpp | 4 + src/dbus/applicationservice.h | 15 ++- src/dbus/instanceservice.h | 10 +- src/dbus/jobmanager1service.h | 6 +- src/desktopentry.cpp | 104 ++++++++++------ src/desktopentry.h | 22 +++- src/global.h | 86 ++++++++++--- src/systemdsignaldispatcher.h | 2 +- 15 files changed, 307 insertions(+), 143 deletions(-) diff --git a/api/dbus/org.desktopspec.ApplicationManager1.xml b/api/dbus/org.desktopspec.ApplicationManager1.xml index e3fd60b..082f181 100644 --- a/api/dbus/org.desktopspec.ApplicationManager1.xml +++ b/api/dbus/org.desktopspec.ApplicationManager1.xml @@ -3,7 +3,7 @@ - + &ex if (ret = sd_bus_message_open_container(msg, SD_BUS_TYPE_VARIANT, "a(sasb)"); ret < 0) { sd_journal_perror("open variant of execStart failed."); - if (auto tmp = sd_bus_message_close_container(msg)) + if (auto tmp = sd_bus_message_close_container(msg)) { return ret; + } } if (ret = sd_bus_message_open_container(msg, SD_BUS_TYPE_ARRAY, "(sasb)"); ret < 0) { @@ -94,8 +102,9 @@ static int processExecStart(msg_ptr &msg, const std::deque &ex sd_journal_perror("open array of execStart variant failed."); return ret; } - for (std::size_t i = 0; i < execArgs.size(); ++i) { - if (ret = sd_bus_message_append(msg, "s", execArgs[i].data()); ret < 0) { + + for (auto execArg : execArgs) { + if (ret = sd_bus_message_append(msg, "s", execArg.data()); ret < 0) { sd_journal_perror("append args of execStart failed."); return ret; } @@ -211,11 +220,11 @@ static std::string cmdParse(msg_ptr &msg, std::deque &&cmdLine if (ret = sd_bus_message_append(msg, "s", props["unitName"].data()); ret < 0) { // unitName sd_journal_perror("append unitName failed."); return serviceName; - } else { - serviceName = props["unitName"]; - props.erase("unitName"); } + serviceName = props["unitName"]; + props.erase("unitName"); + if (ret = sd_bus_message_append(msg, "s", "replace"); ret < 0) { // start mode sd_journal_perror("append startMode failed."); return serviceName; @@ -271,11 +280,12 @@ int jobRemovedReceiver(sd_bus_message *m, void *userdata, sd_bus_error *ret_erro if (ret = sd_bus_error_is_set(ret_error); ret != 0) { sd_journal_print(LOG_ERR, "JobRemoved error: [%s,%s]", ret_error->name, ret_error->message); } else { - const char *serviceId{nullptr}, *jobResult{nullptr}; + const char *serviceId{nullptr}; + const char *jobResult{nullptr}; if (ret = sd_bus_message_read(m, "uoss", nullptr, nullptr, &serviceId, &jobResult); ret < 0) { sd_journal_perror("read from JobRemoved failed."); } else { - auto ptr = reinterpret_cast(userdata); + auto *ptr = reinterpret_cast(userdata); if (ptr->id == serviceId) { ptr->removedFlag = 1; ptr->result = fromString(jobResult); diff --git a/apps/dde-application-manager/src/main.cpp b/apps/dde-application-manager/src/main.cpp index ef7a864..d34b85c 100644 --- a/apps/dde-application-manager/src/main.cpp +++ b/apps/dde-application-manager/src/main.cpp @@ -8,7 +8,6 @@ #include #include "dbus/applicationmanager1service.h" #include "cgroupsidentifier.h" -#include "global.h" static void registerComplexDbusType() { @@ -20,6 +19,7 @@ static void registerComplexDbusType() int main(int argc, char *argv[]) { QCoreApplication app{argc, argv}; + auto &bus = ApplicationManager1DBus::instance(); bus.initGlobalServerBus(DBusType::Session); bus.setDestBus(""); @@ -32,30 +32,24 @@ int main(int argc, char *argv[]) XDGDataDirs = qgetenv("XDG_DATA_DIRS"); if (XDGDataDirs.isEmpty()) { XDGDataDirs.append("/usr/local/share/:/usr/share/"); + qputenv("XDG_DATA_DIRS", XDGDataDirs); } - auto desktopFileDirs = XDGDataDirs.split(':'); + auto desktopFileDirs = QString::fromLocal8Bit(XDGDataDirs).split(':', Qt::SkipEmptyParts); - for (const auto &dir : desktopFileDirs) { - auto dirPath = QDir{QDir::cleanPath(dir) + "/applications"}; - if (!dirPath.exists()) { - continue; + std::for_each(desktopFileDirs.begin(), desktopFileDirs.end(), [](QString &str) { + str = QDir::cleanPath(str) + QDir::separator() + "applications"; + }); + + applyIteratively(QList(desktopFileDirs.begin(), desktopFileDirs.end()), [&AMService](const QFileInfo &info) -> bool { + ParseError err{ParseError::NoError}; + auto ret = DesktopFile::searchDesktopFile(info.absoluteFilePath(), err); + if (!ret.has_value()) { + qWarning() << "failed to search File:" << err; + return false; } - QDirIterator it{dirPath.absolutePath(), - {"*.desktop"}, - QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Readable, - QDirIterator::Subdirectories}; - while (it.hasNext()) { - auto file = it.next(); - ParseError err; - auto ret = DesktopFile::searchDesktopFile(file, err); - if (!ret.has_value()) { - continue; - } - if (!AMService.addApplication(std::move(ret).value())) { - break; - } - } - } + AMService.addApplication(std::move(ret).value()); + return false; // means to apply this function to the rest of the files + }); return app.exec(); } diff --git a/src/cgroupsidentifier.cpp b/src/cgroupsidentifier.cpp index c860a70..d6372ac 100644 --- a/src/cgroupsidentifier.cpp +++ b/src/cgroupsidentifier.cpp @@ -26,7 +26,7 @@ IdentifyRet CGroupsIdentifier::Identify(pid_t pid) return {appInstanceComponent.first(), appInstanceComponent.last()}; } -QString CGroupsIdentifier::parseCGroupsPath(QString CGP) const noexcept +QString CGroupsIdentifier::parseCGroupsPath(const QString &CGP) noexcept { if (CGP.isEmpty()) { qWarning() << "CGroupPath is empty."; @@ -34,7 +34,6 @@ QString CGroupsIdentifier::parseCGroupsPath(QString CGP) const noexcept } auto unescapedCGP = unescapeString(CGP); auto CGPSlices = unescapedCGP.split('/'); - auto curUID = getCurrentUID(); if (CGPSlices.first() != "user.slice") { qWarning() << "unrecognized process."; diff --git a/src/cgroupsidentifier.h b/src/cgroupsidentifier.h index 0e5b8fa..6db7ecf 100644 --- a/src/cgroupsidentifier.h +++ b/src/cgroupsidentifier.h @@ -13,7 +13,7 @@ public: IdentifyRet Identify(pid_t pid) override; private: - [[nodiscard]] QString parseCGroupsPath(QString path) const noexcept; + [[nodiscard]] static QString parseCGroupsPath(const QString &CGP) noexcept; }; #endif diff --git a/src/dbus/applicationmanager1service.cpp b/src/dbus/applicationmanager1service.cpp index ce397e7..2fc1ac2 100644 --- a/src/dbus/applicationmanager1service.cpp +++ b/src/dbus/applicationmanager1service.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "dbus/applicationmanager1service.h" #include "dbus/applicationmanager1adaptor.h" +#include "systemdsignaldispatcher.h" #include #include @@ -28,7 +29,7 @@ ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr &app) { - if (app->id() == appId) { - return true; - } - return false; - }); + [&appId](const QSharedPointer &app) { return app->id() == appId; }); if (appIt == m_applicationList.cend()) [[unlikely]] { qWarning() << "couldn't find app" << appId << "in application manager."; @@ -76,10 +73,7 @@ ApplicationManager1Service::ApplicationManager1Service(std::unique_ptrm_Instances.cbegin(), appRef->m_Instances.cend(), [&systemdUnitPath](const QSharedPointer &value) { - if (value->systemdUnitPath() == systemdUnitPath) { - return true; - } - return false; + return value->systemdUnitPath() == systemdUnitPath; }); if (instanceIt != appRef->m_Instances.cend()) [[likely]] { @@ -88,7 +82,7 @@ ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr ApplicationManager1Service::processServiceName(const QString &serviceName) +QPair ApplicationManager1Service::processServiceName(const QString &serviceName) noexcept { QString instanceId; QString applicationId; @@ -128,23 +122,23 @@ QList ApplicationManager1Service::list() const return m_applicationList.keys(); } -bool ApplicationManager1Service::removeOneApplication(const QDBusObjectPath &application) +void ApplicationManager1Service::removeOneApplication(const QDBusObjectPath &application) { - return m_applicationList.remove(application) != 0; + unregisterObjectFromDBus(application.path()); + m_applicationList.remove(application); } void ApplicationManager1Service::removeAllApplication() { - m_applicationList.clear(); + for (const auto &app : m_applicationList.keys()) { + removeOneApplication(app); + } } QDBusObjectPath ApplicationManager1Service::Application(const QString &id) const { auto ret = std::find_if(m_applicationList.cbegin(), m_applicationList.cend(), [&id](const auto &app) { - if (app->id() == id) { - return true; - } - return false; + return static_cast(app->id() == id); }); if (ret != m_applicationList.cend()) { @@ -219,4 +213,80 @@ QDBusObjectPath ApplicationManager1Service::Launch(const QString &id, return value->Launch(actions, fields, options); } -void ApplicationManager1Service::UpdateApplicationInfo(const QStringList &update_path) {} +void ApplicationManager1Service::UpdateApplicationInfo(const QStringList &app_id) +{ + auto XDGDataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':', Qt::SkipEmptyParts); + std::for_each(XDGDataDirs.begin(), XDGDataDirs.end(), [](QString &str) { + if (!str.endsWith(QDir::separator())) { + str.append(QDir::separator()); + } + str.append("applications"); + }); + + for (auto id : app_id) { + auto destApp = std::find_if(m_applicationList.begin(), + m_applicationList.end(), + [&id](const QSharedPointer &value) { return value->id() == id; }); + + if (destApp == m_applicationList.end()) { // new app + qInfo() << "add a new application:" << id; + do { + for (const auto &suffix : XDGDataDirs) { + QFileInfo info{suffix + id}; + if (info.exists()) { + ParseError err; + auto file = DesktopFile::searchDesktopFile(info.absoluteFilePath(), err); + + if (!file.has_value()) { + continue; + } + + if (!addApplication(std::move(file).value())) { + id.clear(); + break; + } + } + } + + if (id.isEmpty()) { + break; + } + + auto hyphenIndex = id.indexOf('-'); + if (hyphenIndex == -1) { + break; + } + + id[hyphenIndex] = QDir::separator(); + } while (true); + } else { // remove or update + if (!(*destApp)->m_isPersistence) [[unlikely]] { + continue; + } + + auto filePath = (*destApp)->m_desktopSource.m_file.filePath(); + if (QFileInfo::exists(filePath)) { // update + qInfo() << "update application:" << id; + struct stat buf; + if (auto ret = stat(filePath.toLatin1().data(), &buf); ret == -1) { + qWarning() << "get file" << filePath << "state failed:" << std::strerror(errno) << ", skip..."; + continue; + } + + if ((*destApp)->m_desktopSource.m_file.modified( + static_cast(buf.st_mtim.tv_sec * 1e9 + buf.st_mtim.tv_nsec))) { + auto newEntry = new DesktopEntry{}; + auto err = newEntry->parse((*destApp)->m_desktopSource.m_file); + if (err != ParseError::NoError and err != ParseError::EntryKeyInvalid) { + qWarning() << "update desktop file failed:" << err << ", content wouldn't change."; + continue; + } + (*destApp)->m_entry.reset(newEntry); + } + } else { // remove + qInfo() << "remove application:" << id; + removeOneApplication((*destApp)->m_applicationPath); + } + } + } +} diff --git a/src/dbus/applicationmanager1service.h b/src/dbus/applicationmanager1service.h index 8d4a410..b77e082 100644 --- a/src/dbus/applicationmanager1service.h +++ b/src/dbus/applicationmanager1service.h @@ -29,7 +29,7 @@ public: ApplicationManager1Service &operator=(ApplicationManager1Service &&) = delete; Q_PROPERTY(QList List READ list) - QList list() const; + [[nodiscard]] QList list() const; template bool addApplication(T &&desktopFileSource) { @@ -45,23 +45,23 @@ public: m_applicationList.insert(application->m_applicationPath, application); return true; } - bool removeOneApplication(const QDBusObjectPath &application); + void removeOneApplication(const QDBusObjectPath &application); void removeAllApplication(); JobManager1Service &jobManager() noexcept { return *m_jobManager; } public Q_SLOTS: - QDBusObjectPath Application(const QString &id) const; + [[nodiscard]] QDBusObjectPath Application(const QString &id) const; QString Identify(const QDBusUnixFileDescriptor &pidfd, QDBusObjectPath &application, QDBusObjectPath &application_instance); QDBusObjectPath Launch(const QString &id, const QString &action, const QStringList &fields, const QVariantMap &options); - void UpdateApplicationInfo(const QStringList &update_path); + void UpdateApplicationInfo(const QStringList &app_id); private: std::unique_ptr m_identifier; QScopedPointer m_jobManager{nullptr}; QMap> m_applicationList; - QPair processServiceName(const QString &serviceName); + static QPair processServiceName(const QString &serviceName) noexcept; }; #endif diff --git a/src/dbus/applicationservice.cpp b/src/dbus/applicationservice.cpp index f9b31e4..686e2a0 100644 --- a/src/dbus/applicationservice.cpp +++ b/src/dbus/applicationservice.cpp @@ -17,6 +17,10 @@ ApplicationService::~ApplicationService() { m_desktopSource.destruct(m_isPersistence); + for (auto &instance : m_Instances.values()) { + instance->m_Application = QDBusObjectPath{"/"}; + instance->setParent(qApp); // detach all instances to qApp + } } qsizetype ApplicationService::applicationCheck(const QString &serviceName) diff --git a/src/dbus/applicationservice.h b/src/dbus/applicationservice.h index c59fd36..70d090a 100644 --- a/src/dbus/applicationservice.h +++ b/src/dbus/applicationservice.h @@ -18,7 +18,6 @@ #include "global.h" #include "desktopentry.h" #include "desktopicons.h" -#include "systemdsignaldispatcher.h" #include "dbus/jobmanager1service.h" class ApplicationService : public QObject @@ -32,25 +31,25 @@ public: ApplicationService &operator=(ApplicationService &&) = delete; Q_PROPERTY(QStringList Actions READ actions CONSTANT) - QStringList actions() const noexcept; + [[nodiscard]] QStringList actions() const noexcept; Q_PROPERTY(QString ID READ id CONSTANT) - QString id() const noexcept; + [[nodiscard]] QString id() const noexcept; Q_PROPERTY(IconMap Icons READ icons) - IconMap icons() const; + [[nodiscard]] IconMap icons() const; IconMap &iconsRef(); Q_PROPERTY(bool AutoStart READ isAutoStart WRITE setAutoStart) - bool isAutoStart() const noexcept; + [[nodiscard]] bool isAutoStart() const noexcept; void setAutoStart(bool autostart) noexcept; Q_PROPERTY(QList Instances READ instances) - QList instances() const noexcept; + [[nodiscard]] QList instances() const noexcept; - QDBusObjectPath findInstance(const QString &instanceId) const; + [[nodiscard]] QDBusObjectPath findInstance(const QString &instanceId) const; - const QString &getLauncher() const noexcept { return m_launcher; } + [[nodiscard]] const QString &getLauncher() const noexcept { return m_launcher; } void setLauncher(const QString &launcher) noexcept { m_launcher = launcher; } bool addOneInstance(const QString &instanceId, const QString &application, const QString &systemdUnitPath); diff --git a/src/dbus/instanceservice.h b/src/dbus/instanceservice.h index 7ebfed6..bd9aa39 100644 --- a/src/dbus/instanceservice.h +++ b/src/dbus/instanceservice.h @@ -19,19 +19,19 @@ public: InstanceService &operator=(InstanceService &&) = delete; Q_PROPERTY(QDBusObjectPath Application READ application) - QDBusObjectPath application() const; + [[nodiscard]] QDBusObjectPath application() const; Q_PROPERTY(QDBusObjectPath SystemdUnitPath READ systemdUnitPath) - QDBusObjectPath systemdUnitPath() const; + [[nodiscard]] QDBusObjectPath systemdUnitPath() const; - const QString &instanceId() const noexcept { return m_instanceId; } + [[nodiscard]] const QString &instanceId() const noexcept { return m_instanceId; } private: friend class ApplicationService; InstanceService(QString instanceId, QString application, QString systemdUnitPath); QString m_instanceId; - const QDBusObjectPath m_Application; - const QDBusObjectPath m_SystemdUnitPath; + QDBusObjectPath m_Application; + QDBusObjectPath m_SystemdUnitPath; }; #endif diff --git a/src/dbus/jobmanager1service.h b/src/dbus/jobmanager1service.h index d01f883..9a31480 100644 --- a/src/dbus/jobmanager1service.h +++ b/src/dbus/jobmanager1service.h @@ -46,7 +46,7 @@ public: ~JobManager1Service() override; template - QDBusObjectPath addJob(QString source, F func, QVariantList args) + QDBusObjectPath addJob(const QString &source, F func, QVariantList args) { static_assert(std::is_invocable_v, "param type must be QVariant."); @@ -66,7 +66,7 @@ public: auto path = QDBusObjectPath{objectPath}; { QMutexLocker locker{&m_mutex}; - m_jobs.insert(path, std::move(job)); // Insertion is always successful + m_jobs.insert(path, job); // Insertion is always successful } emit JobNew(path, QDBusObjectPath{source}); @@ -97,7 +97,7 @@ Q_SIGNALS: private: bool removeOneJob(const QDBusObjectPath &path); friend class ApplicationManager1Service; - JobManager1Service(ApplicationManager1Service *parent); + explicit JobManager1Service(ApplicationManager1Service *parent); QMutex m_mutex; QMap> m_jobs; ApplicationManager1Service *m_parent{nullptr}; diff --git a/src/desktopentry.cpp b/src/desktopentry.cpp index 5e0094d..0763d2b 100644 --- a/src/desktopentry.cpp +++ b/src/desktopentry.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "desktopentry.h" +#include "global.h" #include #include #include @@ -15,11 +16,11 @@ auto DesktopEntry::parserGroupHeader(const QString &str) noexcept { auto groupHeader = str.sliced(1, str.size() - 2); - if (auto it = m_entryMap.find(groupHeader); it == m_entryMap.cend()) { + auto it = m_entryMap.find(groupHeader); + if (it == m_entryMap.cend()) { return m_entryMap.insert(groupHeader, {}); - } else { - return it; } + return it; } ParseError DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept @@ -31,15 +32,17 @@ ParseError DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::it auto splitCharIndex = str.indexOf(']'); if (splitCharIndex != -1) { for (; splitCharIndex < str.size(); ++splitCharIndex) { - if (str.at(splitCharIndex) == '=') + if (str.at(splitCharIndex) == '=') { break; + } } } else { splitCharIndex = str.indexOf('='); } auto keyStr = str.first(splitCharIndex).trimmed(); auto valueStr = str.sliced(splitCharIndex + 1).trimmed(); - QString key, valueKey{defaultKeyStr}; + QString key; + QString valueKey{defaultKeyStr}; constexpr auto MainKey = R"re((?[0-9a-zA-Z-]+))re"; // main key. eg.(Name, X-CUSTOM-KEY). constexpr auto Language = R"re((?:[a-z]+))re"; // language of locale postfix. eg.(en, zh) @@ -62,19 +65,23 @@ ParseError DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::it if (auto locale = matcher.captured("LOCALE"); !locale.isEmpty()) { valueKey = locale; } - if (auto cur = currentGroup->find(key); cur == currentGroup->end()) { + + auto cur = currentGroup->find(key); + if (cur == currentGroup->end()) { currentGroup->insert(keyStr, {{valueKey, valueStr}}); return ParseError::NoError; - } else { - if (auto v = cur->find(valueKey); v == cur->end()) { - cur->insert(valueKey, valueStr); - return ParseError::NoError; - } else { - qWarning() << "duplicated postfix and this line will be aborted, maybe format is invalid.\n" - << "exist: " << v.key() << "[" << v.value() << "]" - << "current: " << str; - } } + + auto value = cur->find(valueKey); + if (value == cur->end()) { + cur->insert(valueKey, valueStr); + return ParseError::NoError; + } + + qWarning() << "duplicated postfix and this line will be aborted, maybe format is invalid.\n" + << "exist: " << value.key() << "[" << value.value() << "]" + << "current: " << str; + return ParseError::NoError; } @@ -85,24 +92,27 @@ std::optional DesktopFile::searchDesktopFile(const QString &desktop err = ParseError::MismatchedFile; return std::nullopt; } - QString path, id; + + QString path; + QString id; + QFileInfo Fileinfo{desktopFile}; if (Fileinfo.isAbsolute() and Fileinfo.exists()) { path = desktopFile; } else { - auto XDGDataDirs = qgetenv("XDG_DATA_DIRS").split(':'); - qDebug() << "Current Application Dirs:" << XDGDataDirs; - for (const auto &d : XDGDataDirs) { - auto dirPath = QDir::cleanPath(d); - QDirIterator it{dirPath, - {desktopFile}, - QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Readable, - QDirIterator::Subdirectories}; - if (it.hasNext()) { - path = it.next(); - break; + auto XDGDataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':', Qt::SkipEmptyParts); + std::for_each(XDGDataDirs.begin(), XDGDataDirs.end(), [](QString &str) { + str = QDir::cleanPath(str) + QDir::separator() + "applications"; + }); + auto fileName = Fileinfo.fileName(); + + applyIteratively(QList{XDGDataDirs.begin(), XDGDataDirs.end()}, [&fileName, &path](const QFileInfo &file) -> bool { + if (file.fileName() == fileName) { + path = file.absoluteFilePath(); + return true; } - } + return false; + }); } if (path.isEmpty()) { @@ -122,8 +132,23 @@ std::optional DesktopFile::searchDesktopFile(const QString &desktop FileId += (*(it++) + "-"); id = FileId.chopped(1); // remove extra "-"" } + + struct stat buf; + if (auto ret = stat(path.toLatin1().data(), &buf); ret == -1) { + err = ParseError::OpenFailed; + qWarning() << "get file" << path << "state failed:" << std::strerror(errno); + return std::nullopt; + } + err = ParseError::NoError; - return DesktopFile{std::move(path), std::move(id)}; + constexpr std::size_t nanoToSec = 1e9; + + return DesktopFile{std::move(path), std::move(id), buf.st_mtim.tv_sec * nanoToSec + buf.st_mtim.tv_nsec}; +} + +bool DesktopFile::modified(std::size_t time) const noexcept +{ + return time != m_mtime; } ParseError DesktopEntry::parse(const DesktopFile &desktopFile) noexcept @@ -140,8 +165,9 @@ ParseError DesktopEntry::parse(const DesktopFile &desktopFile) noexcept ParseError DesktopEntry::parse(QTextStream &stream) noexcept { - if (stream.atEnd()) + if (stream.atEnd()) { return ParseError::OpenFailed; + } stream.setEncoding(QStringConverter::Utf8); decltype(m_entryMap)::iterator currentGroup; @@ -155,14 +181,15 @@ ParseError DesktopEntry::parse(QTextStream &stream) noexcept } if (line.startsWith("[")) { - if (!line.endsWith("]")) + if (!line.endsWith("]")) { return ParseError::GroupHeaderInvalid; + } currentGroup = parserGroupHeader(line); continue; } - if (auto e = parseEntry(line, currentGroup); e != ParseError::NoError) { - err = e; + if (auto error = parseEntry(line, currentGroup); error != ParseError::NoError) { + err = error; qWarning() << "an error occurred,this line will be skipped:" << line; } } @@ -171,8 +198,9 @@ ParseError DesktopEntry::parse(QTextStream &stream) noexcept std::optional> DesktopEntry::group(const QString &key) const noexcept { - if (auto group = m_entryMap.find(key); group != m_entryMap.cend()) + if (auto group = m_entryMap.find(key); group != m_entryMap.cend()) { return *group; + } return std::nullopt; } @@ -192,7 +220,7 @@ std::optional DesktopEntry::value(const QString &groupKey, return *it; } -QString DesktopEntry::Value::unescape(const QString &str) const noexcept +QString DesktopEntry::Value::unescape(const QString &str) noexcept { QString unescapedStr; for (qsizetype i = 0; i < str.size(); ++i) { @@ -240,13 +268,15 @@ QString DesktopEntry::Value::toString(bool &ok) const noexcept { ok = false; auto str = this->find(defaultKeyStr); - if (str == this->end()) + if (str == this->end()) { return {}; + } auto unescapedStr = unescape(*str); constexpr auto controlChars = "\\p{Cc}"; constexpr auto asciiChars = "[^\x00-\x7f]"; - if (unescapedStr.contains(QRegularExpression{controlChars}) and unescapedStr.contains(QRegularExpression{asciiChars})) + if (unescapedStr.contains(QRegularExpression{controlChars}) and unescapedStr.contains(QRegularExpression{asciiChars})) { return {}; + } ok = true; return unescapedStr; diff --git a/src/desktopentry.h b/src/desktopentry.h index f74b306..b065fb7 100644 --- a/src/desktopentry.h +++ b/src/desktopentry.h @@ -8,6 +8,7 @@ #include #include #include +#include constexpr static auto defaultKeyStr = "default"; @@ -21,17 +22,21 @@ struct DesktopFile DesktopFile &operator=(DesktopFile &&) = default; ~DesktopFile() = default; - const QString &filePath() const { return m_filePath; } - const QString &desktopId() const { return m_desktopId; } + [[nodiscard]] const QString &filePath() const { return m_filePath; } + [[nodiscard]] const QString &desktopId() const { return m_desktopId; } static std::optional searchDesktopFile(const QString &desktopFilePath, ParseError &err) noexcept; + [[nodiscard]] bool modified(std::size_t time) const noexcept; private: - DesktopFile(QString &&path, QString &&fileId) - : m_filePath(std::move(path)) + DesktopFile(QString &&path, QString &&fileId, std::size_t mtime) + : m_mtime(mtime) + , m_filePath(std::move(path)) , m_desktopId(std::move(fileId)) { } + + std::size_t m_mtime; QString m_filePath{""}; QString m_desktopId{""}; }; @@ -51,10 +56,15 @@ public: friend QDebug operator<<(QDebug debug, const DesktopEntry::Value &v); private: - [[nodiscard]] QString unescape(const QString &str) const noexcept; + [[nodiscard]] static QString unescape(const QString &str) noexcept; }; DesktopEntry() = default; + DesktopEntry(const DesktopEntry &) = default; + DesktopEntry(DesktopEntry &&) = default; + DesktopEntry &operator=(const DesktopEntry &) = default; + DesktopEntry &operator=(DesktopEntry &&) = default; + ~DesktopEntry() = default; [[nodiscard]] ParseError parse(const DesktopFile &file) noexcept; [[nodiscard]] ParseError parse(QTextStream &stream) noexcept; @@ -64,7 +74,7 @@ public: private: QMap> m_entryMap; auto parserGroupHeader(const QString &str) noexcept; - ParseError parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept; + static ParseError parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept; }; QDebug operator<<(QDebug debug, const DesktopEntry::Value &v); diff --git a/src/global.h b/src/global.h index 65b4fdf..e2543c9 100644 --- a/src/global.h +++ b/src/global.h @@ -14,10 +14,10 @@ #include #include #include +#include #include #include #include -#include #include "constant.h" #include "config.h" @@ -25,17 +25,57 @@ using IconMap = QMap> inline QString getApplicationLauncherBinary() { + static bool logFlag{true}; auto value = qgetenv("DEEPIN_APPLICATION_MANAGER_APP_LAUNCH_HELPER_BIN"); if (value.isEmpty()) { return ApplicationLaunchHelperBinary; - } else { - qWarning() << "Using app launch helper defined in environment variable DEEPIN_APPLICATION_MANAGER_APP_LAUNCH_HELPER_BIN."; - return value; } + + if (logFlag) { + qWarning() << "Using app launch helper defined in environment variable DEEPIN_APPLICATION_MANAGER_APP_LAUNCH_HELPER_BIN."; + logFlag = false; + } + return value; } enum class DBusType { Session = QDBusConnection::SessionBus, System = QDBusConnection::SystemBus, Custom }; +template +using remove_cvr_t = std::remove_reference_t>; + +template +void applyIteratively(QList &&dirs, T &&func) +{ + static_assert(std::is_invocable_v, "apply function should only accept one QFileInfo"); + static_assert(std::is_same_v, + "apply function should return a boolean to indicate when should return"); + QList dirList{std::move(dirs)}; + + while (!dirList.isEmpty()) { + const auto dir = dirList.takeFirst(); + + if (!dir.exists()) { + qWarning() << "apply function to an non-existent directory:" << dir.absolutePath() << ", skip."; + continue; + } + + const auto &infoList = + dir.entryInfoList({"*.desktop"}, + QDir::Readable | QDir::AllDirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot, + QDir::Name | QDir::DirsLast); + + for (const auto &info : infoList) { + if (info.isFile() and func(info)) { + return; + } + + if (info.isDir()) { + dirList.append(info.absoluteFilePath()); + } + } + } +} + class ApplicationManager1DBus { public: @@ -43,8 +83,8 @@ public: ApplicationManager1DBus(ApplicationManager1DBus &&) = delete; ApplicationManager1DBus &operator=(const ApplicationManager1DBus &) = delete; ApplicationManager1DBus &operator=(ApplicationManager1DBus &&) = delete; - const QString &globalDestBusAddress() const { return m_destBusAddress; } - const QString &globalServerBusAddress() const { return m_serverBusAddress; } + [[nodiscard]] const QString &globalDestBusAddress() const { return m_destBusAddress; } + [[nodiscard]] const QString &globalServerBusAddress() const { return m_serverBusAddress; } void initGlobalServerBus(DBusType type, const QString &busAddress = "") { if (m_initFlag) { @@ -54,7 +94,6 @@ public: m_serverBusAddress = busAddress; m_serverType = type; m_initFlag = true; - return; } QDBusConnection &globalServerBus() @@ -121,25 +160,23 @@ public: qFatal() << m_destConnection->lastError(); } return; - } else { - if (m_destBusAddress.isEmpty()) { - qFatal() << "connect to custom dbus must init this object by custom dbus address"; - } - m_destConnection.emplace(QDBusConnection::connectToBus(m_destBusAddress, ApplicationManagerDestDBusName)); - if (!m_destConnection->isConnected()) { - qFatal() << m_destConnection->lastError(); - } - return; } - Q_UNREACHABLE(); + if (m_destBusAddress.isEmpty()) { + qFatal() << "connect to custom dbus must init this object by custom dbus address"; + } + + m_destConnection.emplace(QDBusConnection::connectToBus(m_destBusAddress, ApplicationManagerDestDBusName)); + if (!m_destConnection->isConnected()) { + qFatal() << m_destConnection->lastError(); + } } private: ApplicationManager1DBus() = default; ~ApplicationManager1DBus() = default; - bool m_initFlag; - DBusType m_serverType; + bool m_initFlag{false}; + DBusType m_serverType{}; QString m_serverBusAddress; QString m_destBusAddress; std::optional m_destConnection{std::nullopt}; @@ -224,4 +261,15 @@ inline QString unescapeFromObjectPath(const QString &str) return ret; } +inline QString getRelativePathFromAppId(const QString &id) +{ + QString path; + auto components = id.split('-', Qt::SkipEmptyParts); + for (qsizetype i = 0; i < components.size() - 1; ++i) { + path += QString{"/%1"}.arg(components[i]); + } + path += QString{R"(-%1.desktop)"}.arg(components.last()); + return path; +} + #endif diff --git a/src/systemdsignaldispatcher.h b/src/systemdsignaldispatcher.h index 06c097e..4818d59 100644 --- a/src/systemdsignaldispatcher.h +++ b/src/systemdsignaldispatcher.h @@ -11,7 +11,7 @@ class SystemdSignalDispatcher : public QObject { Q_OBJECT public: - ~SystemdSignalDispatcher() = default; + ~SystemdSignalDispatcher() override = default; static SystemdSignalDispatcher &instance() { static SystemdSignalDispatcher dispatcher;