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 <heyuming@deepin.org>
This commit is contained in:
ComixHe 2023-08-23 17:20:47 +08:00 committed by Comix
parent 2bdb9e99ee
commit 9f2a8b6798
8 changed files with 108 additions and 18 deletions

View File

@ -16,6 +16,7 @@
</property> </property>
<property name="AutoStart" type="b" access="readwrite"/> <property name="AutoStart" type="b" access="readwrite"/>
<property name="LastLaunchedTime" type="t" access="read"/>
<property name="Instances" type="ao" access="read"> <property name="Instances" type="ao" access="read">
<annotation <annotation

View File

@ -25,7 +25,9 @@ void registerComplexDbusType()
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
#ifdef PROFILING_MODE
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
#endif
QCoreApplication app{argc, argv}; QCoreApplication app{argc, argv};
auto &bus = ApplicationManager1DBus::instance(); auto &bus = ApplicationManager1DBus::instance();
@ -36,24 +38,25 @@ int main(int argc, char *argv[])
registerComplexDbusType(); registerComplexDbusType();
ApplicationManager1Service AMService{std::make_unique<CGroupsIdentifier>(), AMBus}; ApplicationManager1Service AMService{std::make_unique<CGroupsIdentifier>(), AMBus};
QList<DesktopFile> fileList{}; QList<DesktopFile> fileList{};
auto desktopFileDirs = getXDGDataDirs(); const auto &desktopFileDirs = getDesktopFileDirs();
applyIteratively(QList<QDir>(desktopFileDirs.begin(), desktopFileDirs.end()), [&AMService](const QFileInfo &info) -> bool { applyIteratively(QList<QDir>(desktopFileDirs.cbegin(), desktopFileDirs.cend()), [&AMService](const QFileInfo &info) -> bool {
DesktopErrorCode err{DesktopErrorCode::NoError}; DesktopErrorCode err{DesktopErrorCode::NoError};
auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err); auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err);
if (!ret.has_value()) { if (!ret.has_value()) {
qWarning() << "failed to search File:" << err; qWarning() << "failed to search File:" << err;
return false; 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 return false; // means to apply this function to the rest of the files
}); });
#ifdef PROFILING_MODE
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
qCInfo(DDEAMProf) << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms"; qCInfo(DDEAMProf) << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms";
return return 0;
#ifdef PROFILING_MODE
QCoreApplication::exec();
#else #else
0; return QCoreApplication::exec();
#endif #endif
} }

View File

@ -52,12 +52,19 @@ QSharedPointer<ApplicationService> ApplicationService::createApplicationService(
objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + QUuid::createUuid().toString(QUuid::Id128); objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + QUuid::createUuid().toString(QUuid::Id128);
} }
DesktopFileGuard guard{app->desktopFileSource()};
if (!guard.try_open()) {
return nullptr;
}
sourceStream.setDevice(app->desktopFileSource().sourceFile()); sourceStream.setDevice(app->desktopFileSource().sourceFile());
std::unique_ptr<DesktopEntry> entry{std::make_unique<DesktopEntry>()}; std::unique_ptr<DesktopEntry> entry{std::make_unique<DesktopEntry>()};
auto error = entry->parse(sourceStream); auto error = entry->parse(sourceStream);
if (error != DesktopErrorCode::NoError) { if (error != DesktopErrorCode::NoError) {
if (error != DesktopErrorCode::EntryKeyInvalid) { if (error != DesktopErrorCode::EntryKeyInvalid) {
qWarning() << "parse failed:" << error;
return nullptr; return nullptr;
} }
} }
@ -65,6 +72,7 @@ QSharedPointer<ApplicationService> ApplicationService::createApplicationService(
if (auto val = entry->value(DesktopFileEntryKey, "Hidden"); val.has_value()) { if (auto val = entry->value(DesktopFileEntryKey, "Hidden"); val.has_value()) {
bool ok{false}; bool ok{false};
if (auto hidden = val.value().toBoolean(ok); ok and hidden) { if (auto hidden = val.value().toBoolean(ok); ok and hidden) {
qWarning() << "invalid hidden value:" << *val.value().find(defaultKeyStr);
return nullptr; return nullptr;
} }
} }
@ -333,6 +341,11 @@ QString ApplicationService::id() const noexcept
return m_desktopSource.desktopId(); return m_desktopSource.desktopId();
} }
qulonglong ApplicationService::lastLaunchedTime() const noexcept
{
return m_lastLaunch;
}
IconMap ApplicationService::icons() const IconMap ApplicationService::icons() const
{ {
if (m_Icons) { if (m_Icons) {

View File

@ -13,6 +13,7 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QUuid> #include <QUuid>
#include <QTextStream> #include <QTextStream>
#include <QDBusContext>
#include <QFile> #include <QFile>
#include <memory> #include <memory>
#include <utility> #include <utility>
@ -22,7 +23,7 @@
#include "desktopicons.h" #include "desktopicons.h"
#include "dbus/jobmanager1service.h" #include "dbus/jobmanager1service.h"
class ApplicationService : public QObject class ApplicationService : public QObject, public QDBusContext
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -38,6 +39,9 @@ public:
Q_PROPERTY(QString ID READ id CONSTANT) Q_PROPERTY(QString ID READ id CONSTANT)
[[nodiscard]] QString id() const noexcept; [[nodiscard]] QString id() const noexcept;
Q_PROPERTY(qulonglong LastLaunchedTime READ lastLaunchedTime)
[[nodiscard]] qulonglong lastLaunchedTime() const noexcept;
Q_PROPERTY(IconMap Icons READ icons) Q_PROPERTY(IconMap Icons READ icons)
[[nodiscard]] IconMap icons() const; [[nodiscard]] IconMap icons() const;
IconMap &iconsRef(); IconMap &iconsRef();
@ -83,6 +87,7 @@ private:
static QSharedPointer<ApplicationService> createApplicationService(DesktopFile source, static QSharedPointer<ApplicationService> createApplicationService(DesktopFile source,
ApplicationManager1Service *parent) noexcept; ApplicationManager1Service *parent) noexcept;
bool m_AutoStart{false}; bool m_AutoStart{false};
qlonglong m_lastLaunch{0};
QDBusObjectPath m_applicationPath; QDBusObjectPath m_applicationPath;
QString m_launcher{getApplicationLauncherBinary()}; QString m_launcher{getApplicationLauncherBinary()};
DesktopFile m_desktopSource; DesktopFile m_desktopSource;

View File

@ -13,6 +13,7 @@
#include <QVariant> #include <QVariant>
#include <iostream> #include <iostream>
#include <chrono> #include <chrono>
#include <cstdio>
auto DesktopEntry::parserGroupHeader(const QString &str) noexcept auto DesktopEntry::parserGroupHeader(const QString &str) noexcept
{ {
@ -108,9 +109,33 @@ std::optional<DesktopFile> DesktopFile::createTemporaryDesktopFile(std::unique_p
return DesktopFile{std::move(temporaryFile), "", mtime}; return DesktopFile{std::move(temporaryFile), "", mtime};
} }
std::optional<DesktopFile> DesktopFile::createTemporaryDesktopFile(const QString &temporaryFile) noexcept
{
const static QString userTmp = QString{"/run/user/%1/"}.arg(getCurrentUID());
auto tempFile = std::make_unique<QFile>(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> DesktopFile::searchDesktopFileByPath(const QString &desktopFile, DesktopErrorCode &err) noexcept std::optional<DesktopFile> DesktopFile::searchDesktopFileByPath(const QString &desktopFile, DesktopErrorCode &err) noexcept
{ {
constexpr decltype(auto) desktopSuffix = ".desktop"; decltype(auto) desktopSuffix = ".desktop";
if (!desktopFile.endsWith(desktopSuffix)) { if (!desktopFile.endsWith(desktopSuffix)) {
qWarning() << "file isn't a desktop file:" << desktopFile; qWarning() << "file isn't a desktop file:" << desktopFile;
@ -128,7 +153,7 @@ std::optional<DesktopFile> DesktopFile::searchDesktopFileByPath(const QString &d
QString path{desktopFile}; QString path{desktopFile};
QString id; QString id;
const auto &XDGDataDirs = getXDGDataDirs(); const auto &XDGDataDirs = getDesktopFileDirs();
auto idGen = std::any_of(XDGDataDirs.cbegin(), XDGDataDirs.cend(), [&desktopFile](const QString &suffixPath) { auto idGen = std::any_of(XDGDataDirs.cbegin(), XDGDataDirs.cend(), [&desktopFile](const QString &suffixPath) {
return desktopFile.startsWith(suffixPath); return desktopFile.startsWith(suffixPath);
}); });
@ -160,7 +185,7 @@ std::optional<DesktopFile> DesktopFile::searchDesktopFileByPath(const QString &d
std::optional<DesktopFile> DesktopFile::searchDesktopFileById(const QString &appId, DesktopErrorCode &err) noexcept std::optional<DesktopFile> DesktopFile::searchDesktopFileById(const QString &appId, DesktopErrorCode &err) noexcept
{ {
auto XDGDataDirs = getXDGDataDirs(); auto XDGDataDirs = getDesktopFileDirs();
constexpr auto desktopSuffix = u8".desktop"; constexpr auto desktopSuffix = u8".desktop";
for (const auto &dir : XDGDataDirs) { for (const auto &dir : XDGDataDirs) {

View File

@ -45,6 +45,7 @@ struct DesktopFile
static std::optional<DesktopFile> searchDesktopFileById(const QString &appId, DesktopErrorCode &err) noexcept; static std::optional<DesktopFile> searchDesktopFileById(const QString &appId, DesktopErrorCode &err) noexcept;
static std::optional<DesktopFile> searchDesktopFileByPath(const QString &desktopFilePath, DesktopErrorCode &err) noexcept; static std::optional<DesktopFile> searchDesktopFileByPath(const QString &desktopFilePath, DesktopErrorCode &err) noexcept;
static std::optional<DesktopFile> createTemporaryDesktopFile(const QString &temporaryFile) noexcept;
static std::optional<DesktopFile> createTemporaryDesktopFile(std::unique_ptr<QFile> temporaryFile) noexcept; static std::optional<DesktopFile> createTemporaryDesktopFile(std::unique_ptr<QFile> temporaryFile) noexcept;
private: 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() ~DesktopFileGuard()
{ {
if (fileRef.m_fileSource->isOpen()) { if (fileRef.m_fileSource->isOpen()) {
@ -100,6 +114,7 @@ public:
using QMap<QString, QString>::cend; using QMap<QString, QString>::cend;
using QMap<QString, QString>::begin; using QMap<QString, QString>::begin;
using QMap<QString, QString>::end; using QMap<QString, QString>::end;
using QMap<QString, QString>::asKeyValueRange;
QString toString(bool &ok) const noexcept; QString toString(bool &ok) const noexcept;
bool toBoolean(bool &ok) const noexcept; bool toBoolean(bool &ok) const noexcept;

View File

@ -48,7 +48,7 @@ template <typename T>
using remove_cvr_t = std::remove_reference_t<std::remove_cv_t<T>>; using remove_cvr_t = std::remove_reference_t<std::remove_cv_t<T>>;
template <typename T> template <typename T>
void applyIteratively(QList<QDir> &&dirs, T &&func) void applyIteratively(QList<QDir> dirs, T &&func)
{ {
static_assert(std::is_invocable_v<T, const QFileInfo &>, "apply function should only accept one QFileInfo"); static_assert(std::is_invocable_v<T, const QFileInfo &>, "apply function should only accept one QFileInfo");
static_assert(std::is_same_v<decltype(func(QFileInfo{})), bool>, static_assert(std::is_same_v<decltype(func(QFileInfo{})), bool>,
@ -300,7 +300,7 @@ inline QString getRelativePathFromAppId(const QString &id)
return path; return path;
} }
inline QStringList getXDGDataDirs() inline QStringList getDesktopFileDirs()
{ {
const static auto XDGDataDirs = []() { const static auto XDGDataDirs = []() {
auto XDGDataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':', Qt::SkipEmptyParts); 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")); auto XDGDataHome = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME"));
if (XDGDataHome.isEmpty()) { 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) { std::for_each(XDGDataDirs.begin(), XDGDataDirs.end(), [](QString &str) {
if (!str.endsWith(QDir::separator())) { if (!str.endsWith(QDir::separator())) {
@ -330,6 +330,34 @@ inline QStringList getXDGDataDirs()
return XDGDataDirs; 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<QString, QString> processUnitName(const QString &unitName) inline QPair<QString, QString> processUnitName(const QString &unitName)
{ {
QString instanceId; QString instanceId;

View File

@ -33,7 +33,7 @@ bool SystemdSignalDispatcher::connectToSignals() noexcept
void SystemdSignalDispatcher::onUnitNew(QString unitName, QDBusObjectPath systemdUnitPath) void SystemdSignalDispatcher::onUnitNew(QString unitName, QDBusObjectPath systemdUnitPath)
{ {
constexpr decltype(auto) appPrefix = u8"app-"; decltype(auto) appPrefix = u8"app-";
if (!unitName.startsWith(appPrefix)) { if (!unitName.startsWith(appPrefix)) {
return; return;
} }
@ -43,7 +43,7 @@ void SystemdSignalDispatcher::onUnitNew(QString unitName, QDBusObjectPath system
void SystemdSignalDispatcher::onUnitRemoved(QString unitName, QDBusObjectPath systemdUnitPath) void SystemdSignalDispatcher::onUnitRemoved(QString unitName, QDBusObjectPath systemdUnitPath)
{ {
constexpr decltype(auto) appPrefix = u8"app-"; decltype(auto) appPrefix = u8"app-";
if (!unitName.startsWith(appPrefix)) { if (!unitName.startsWith(appPrefix)) {
return; return;
} }