From 9f2a8b6798756138976764f51587aca2d8d002d5 Mon Sep 17 00:00:00 2001 From: ComixHe Date: Wed, 23 Aug 2023 17:20:47 +0800 Subject: [PATCH] feat: add property "LastLaunchedTime" 1. reactor some utils implementation. 2. remove constexpr before `decltype(auto)` due to GCC bug. refer: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102229 Signed-off-by: ComixHe --- ...opspec.ApplicationManager1.Application.xml | 1 + apps/dde-application-manager/src/main.cpp | 17 +++++---- src/dbus/applicationservice.cpp | 13 +++++++ src/dbus/applicationservice.h | 7 +++- src/desktopentry.cpp | 31 ++++++++++++++-- src/desktopentry.h | 17 ++++++++- src/global.h | 36 ++++++++++++++++--- src/systemdsignaldispatcher.cpp | 4 +-- 8 files changed, 108 insertions(+), 18 deletions(-) diff --git a/api/dbus/org.desktopspec.ApplicationManager1.Application.xml b/api/dbus/org.desktopspec.ApplicationManager1.Application.xml index 57a09aa..af2b1e7 100644 --- a/api/dbus/org.desktopspec.ApplicationManager1.Application.xml +++ b/api/dbus/org.desktopspec.ApplicationManager1.Application.xml @@ -16,6 +16,7 @@ + (), AMBus}; QList fileList{}; - auto desktopFileDirs = getXDGDataDirs(); + const auto &desktopFileDirs = getDesktopFileDirs(); - applyIteratively(QList(desktopFileDirs.begin(), desktopFileDirs.end()), [&AMService](const QFileInfo &info) -> bool { + applyIteratively(QList(desktopFileDirs.cbegin(), desktopFileDirs.cend()), [&AMService](const QFileInfo &info) -> bool { DesktopErrorCode err{DesktopErrorCode::NoError}; auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err); if (!ret.has_value()) { qWarning() << "failed to search File:" << err; return false; } - AMService.addApplication(std::move(ret).value()); + if (!AMService.addApplication(std::move(ret).value())) { + qWarning() << "add Application failed:" << ret->sourcePath() << "skip..."; + } return false; // means to apply this function to the rest of the files }); +#ifdef PROFILING_MODE auto end = std::chrono::high_resolution_clock::now(); qCInfo(DDEAMProf) << std::chrono::duration_cast(end - start).count() << "ms"; - return -#ifdef PROFILING_MODE - QCoreApplication::exec(); + return 0; #else - 0; + return QCoreApplication::exec(); #endif } diff --git a/src/dbus/applicationservice.cpp b/src/dbus/applicationservice.cpp index 66b9dc2..1ea2dfb 100644 --- a/src/dbus/applicationservice.cpp +++ b/src/dbus/applicationservice.cpp @@ -52,12 +52,19 @@ QSharedPointer ApplicationService::createApplicationService( objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + QUuid::createUuid().toString(QUuid::Id128); } + DesktopFileGuard guard{app->desktopFileSource()}; + + if (!guard.try_open()) { + return nullptr; + } + sourceStream.setDevice(app->desktopFileSource().sourceFile()); std::unique_ptr entry{std::make_unique()}; auto error = entry->parse(sourceStream); if (error != DesktopErrorCode::NoError) { if (error != DesktopErrorCode::EntryKeyInvalid) { + qWarning() << "parse failed:" << error; return nullptr; } } @@ -65,6 +72,7 @@ QSharedPointer ApplicationService::createApplicationService( if (auto val = entry->value(DesktopFileEntryKey, "Hidden"); val.has_value()) { bool ok{false}; if (auto hidden = val.value().toBoolean(ok); ok and hidden) { + qWarning() << "invalid hidden value:" << *val.value().find(defaultKeyStr); return nullptr; } } @@ -333,6 +341,11 @@ QString ApplicationService::id() const noexcept return m_desktopSource.desktopId(); } +qulonglong ApplicationService::lastLaunchedTime() const noexcept +{ + return m_lastLaunch; +} + IconMap ApplicationService::icons() const { if (m_Icons) { diff --git a/src/dbus/applicationservice.h b/src/dbus/applicationservice.h index b6562de..f49352a 100644 --- a/src/dbus/applicationservice.h +++ b/src/dbus/applicationservice.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,7 @@ #include "desktopicons.h" #include "dbus/jobmanager1service.h" -class ApplicationService : public QObject +class ApplicationService : public QObject, public QDBusContext { Q_OBJECT public: @@ -38,6 +39,9 @@ public: Q_PROPERTY(QString ID READ id CONSTANT) [[nodiscard]] QString id() const noexcept; + Q_PROPERTY(qulonglong LastLaunchedTime READ lastLaunchedTime) + [[nodiscard]] qulonglong lastLaunchedTime() const noexcept; + Q_PROPERTY(IconMap Icons READ icons) [[nodiscard]] IconMap icons() const; IconMap &iconsRef(); @@ -83,6 +87,7 @@ private: static QSharedPointer createApplicationService(DesktopFile source, ApplicationManager1Service *parent) noexcept; bool m_AutoStart{false}; + qlonglong m_lastLaunch{0}; QDBusObjectPath m_applicationPath; QString m_launcher{getApplicationLauncherBinary()}; DesktopFile m_desktopSource; diff --git a/src/desktopentry.cpp b/src/desktopentry.cpp index 8c9c4a2..9a41816 100644 --- a/src/desktopentry.cpp +++ b/src/desktopentry.cpp @@ -13,6 +13,7 @@ #include #include #include +#include auto DesktopEntry::parserGroupHeader(const QString &str) noexcept { @@ -108,9 +109,33 @@ std::optional DesktopFile::createTemporaryDesktopFile(std::unique_p return DesktopFile{std::move(temporaryFile), "", mtime}; } +std::optional DesktopFile::createTemporaryDesktopFile(const QString &temporaryFile) noexcept +{ + const static QString userTmp = QString{"/run/user/%1/"}.arg(getCurrentUID()); + auto tempFile = std::make_unique(QString{userTmp + QUuid::createUuid().toString(QUuid::Id128) + ".desktop"}); + + if (!tempFile->open(QFile::NewOnly | QFile::WriteOnly | QFile::Text)) { + qWarning() << "failed to create temporary desktop file:" << QFileInfo{*tempFile}.absoluteFilePath() + << tempFile->errorString(); + return std::nullopt; + } + + auto content = temporaryFile.toLocal8Bit(); + auto writeByte = tempFile->write(content); + + if (writeByte == -1 || writeByte != content.length()) { + qWarning() << "write to temporary file failed:" << tempFile->errorString(); + return std::nullopt; + } + + tempFile->close(); + + return createTemporaryDesktopFile(std::move(tempFile)); +} + std::optional DesktopFile::searchDesktopFileByPath(const QString &desktopFile, DesktopErrorCode &err) noexcept { - constexpr decltype(auto) desktopSuffix = ".desktop"; + decltype(auto) desktopSuffix = ".desktop"; if (!desktopFile.endsWith(desktopSuffix)) { qWarning() << "file isn't a desktop file:" << desktopFile; @@ -128,7 +153,7 @@ std::optional DesktopFile::searchDesktopFileByPath(const QString &d QString path{desktopFile}; QString id; - const auto &XDGDataDirs = getXDGDataDirs(); + const auto &XDGDataDirs = getDesktopFileDirs(); auto idGen = std::any_of(XDGDataDirs.cbegin(), XDGDataDirs.cend(), [&desktopFile](const QString &suffixPath) { return desktopFile.startsWith(suffixPath); }); @@ -160,7 +185,7 @@ std::optional DesktopFile::searchDesktopFileByPath(const QString &d std::optional DesktopFile::searchDesktopFileById(const QString &appId, DesktopErrorCode &err) noexcept { - auto XDGDataDirs = getXDGDataDirs(); + auto XDGDataDirs = getDesktopFileDirs(); constexpr auto desktopSuffix = u8".desktop"; for (const auto &dir : XDGDataDirs) { diff --git a/src/desktopentry.h b/src/desktopentry.h index b163498..b86f8e6 100644 --- a/src/desktopentry.h +++ b/src/desktopentry.h @@ -45,6 +45,7 @@ struct DesktopFile static std::optional searchDesktopFileById(const QString &appId, DesktopErrorCode &err) noexcept; static std::optional searchDesktopFileByPath(const QString &desktopFilePath, DesktopErrorCode &err) noexcept; + static std::optional createTemporaryDesktopFile(const QString &temporaryFile) noexcept; static std::optional createTemporaryDesktopFile(std::unique_ptr temporaryFile) noexcept; private: @@ -75,7 +76,20 @@ struct DesktopFileGuard { } - bool try_open() { return fileRef.m_fileSource->open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text); } + bool try_open() + { + if (fileRef.m_fileSource->isOpen()) { + return true; + } + + auto ret = fileRef.m_fileSource->open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text); + if (!ret) { + qWarning() << "open desktop file failed:" << fileRef.m_fileSource->errorString(); + } + + return ret; + } + ~DesktopFileGuard() { if (fileRef.m_fileSource->isOpen()) { @@ -100,6 +114,7 @@ public: using QMap::cend; using QMap::begin; using QMap::end; + using QMap::asKeyValueRange; QString toString(bool &ok) const noexcept; bool toBoolean(bool &ok) const noexcept; diff --git a/src/global.h b/src/global.h index 12f7bdd..f118f9a 100644 --- a/src/global.h +++ b/src/global.h @@ -48,7 +48,7 @@ template using remove_cvr_t = std::remove_reference_t>; template -void applyIteratively(QList &&dirs, T &&func) +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, @@ -300,7 +300,7 @@ inline QString getRelativePathFromAppId(const QString &id) return path; } -inline QStringList getXDGDataDirs() +inline QStringList getDesktopFileDirs() { const static auto XDGDataDirs = []() { auto XDGDataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':', Qt::SkipEmptyParts); @@ -312,10 +312,10 @@ inline QStringList getXDGDataDirs() auto XDGDataHome = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME")); if (XDGDataHome.isEmpty()) { - XDGDataHome = QString{qgetenv("HOME")} + QDir::separator() + ".local" + QDir::separator() + "share"; + XDGDataHome = QString::fromLocal8Bit(qgetenv("HOME")) + QDir::separator() + ".local" + QDir::separator() + "share"; } - XDGDataDirs.push_front(XDGDataHome); + XDGDataDirs.push_front(std::move(XDGDataHome)); std::for_each(XDGDataDirs.begin(), XDGDataDirs.end(), [](QString &str) { if (!str.endsWith(QDir::separator())) { @@ -330,6 +330,34 @@ inline QStringList getXDGDataDirs() return XDGDataDirs; } +inline QStringList getAutoStartDirs() +{ + const static auto XDGConfigDirs = []() { + auto XDGConfigDirs = QString::fromLocal8Bit(qgetenv("XDG_CONFIG_DIRS")).split(':', Qt::SkipEmptyParts); + if (XDGConfigDirs.isEmpty()) { + XDGConfigDirs.append("/etc/xdg"); + } + + auto XDGConfigHome = QString::fromLocal8Bit(qgetenv("XDG_CONFIG_HOME")); + if (XDGConfigHome.isEmpty()) { + XDGConfigHome = QString::fromLocal8Bit(qgetenv("HOME")) + QDir::separator() + ".config"; + } + + XDGConfigDirs.push_front(std::move(XDGConfigHome)); + + std::for_each(XDGConfigDirs.begin(), XDGConfigDirs.end(), [](QString &str) { + if (!str.endsWith(QDir::separator())) { + str.append(QDir::separator()); + } + str.append("autostart"); + }); + + return XDGConfigDirs; + }(); + + return XDGConfigDirs; +} + inline QPair processUnitName(const QString &unitName) { QString instanceId; diff --git a/src/systemdsignaldispatcher.cpp b/src/systemdsignaldispatcher.cpp index db77e67..4c806cc 100644 --- a/src/systemdsignaldispatcher.cpp +++ b/src/systemdsignaldispatcher.cpp @@ -33,7 +33,7 @@ bool SystemdSignalDispatcher::connectToSignals() noexcept void SystemdSignalDispatcher::onUnitNew(QString unitName, QDBusObjectPath systemdUnitPath) { - constexpr decltype(auto) appPrefix = u8"app-"; + decltype(auto) appPrefix = u8"app-"; if (!unitName.startsWith(appPrefix)) { return; } @@ -43,7 +43,7 @@ void SystemdSignalDispatcher::onUnitNew(QString unitName, QDBusObjectPath system void SystemdSignalDispatcher::onUnitRemoved(QString unitName, QDBusObjectPath systemdUnitPath) { - constexpr decltype(auto) appPrefix = u8"app-"; + decltype(auto) appPrefix = u8"app-"; if (!unitName.startsWith(appPrefix)) { return; }