feat: add impl of updateApplicationInfo
1. change the way to traverse files 2. refact some code Signed-off-by: ComixHe <heyuming@deepin.org> Signed-off-by: black-desk <me@black-desk.cn>
This commit is contained in:
parent
799100436c
commit
722d0666d3
@ -3,7 +3,7 @@
|
||||
<interface name="org.desktopspec.ApplicationManager1">
|
||||
<property type="ao" access="read" name="List" />
|
||||
<method name="UpdateApplicationInfo">
|
||||
<arg type="as" name="update_path" direction="in" />
|
||||
<arg type="as" name="app_id" direction="in" />
|
||||
<annotation
|
||||
name="org.freedesktop.DBus.Description"
|
||||
value="This method is used to update the desktop file cache when needed.
|
||||
|
@ -33,13 +33,20 @@ static ExitCode fromString(const std::string &str)
|
||||
{
|
||||
if (str == "done") {
|
||||
return ExitCode::Done;
|
||||
} else if (str == "canceled" or str == "timeout" or str == "failed" or str == "dependency" or str == "skipped") {
|
||||
}
|
||||
|
||||
if (str == "canceled" or str == "timeout" or str == "failed" or str == "dependency" or str == "skipped") {
|
||||
return ExitCode::SystemdError;
|
||||
} else if (str == "internalError") {
|
||||
}
|
||||
|
||||
if (str == "internalError") {
|
||||
return ExitCode::InternalError;
|
||||
} else if (str == "invalidInput") {
|
||||
}
|
||||
|
||||
if (str == "invalidInput") {
|
||||
return ExitCode::InvalidInput;
|
||||
}
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
@ -71,8 +78,9 @@ static int processExecStart(msg_ptr &msg, const std::deque<std::string_view> &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<std::string_view> &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<std::string_view> &&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<JobRemoveResult *>(userdata);
|
||||
auto *ptr = reinterpret_cast<JobRemoveResult *>(userdata);
|
||||
if (ptr->id == serviceId) {
|
||||
ptr->removedFlag = 1;
|
||||
ptr->result = fromString(jobResult);
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <QDir>
|
||||
#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<QDir>(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();
|
||||
}
|
||||
|
@ -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.";
|
||||
|
@ -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
|
||||
|
@ -3,6 +3,7 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#include "dbus/applicationmanager1service.h"
|
||||
#include "dbus/applicationmanager1adaptor.h"
|
||||
#include "systemdsignaldispatcher.h"
|
||||
#include <QFile>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -28,7 +29,7 @@ ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr<Identifie
|
||||
connect(&dispatcher,
|
||||
&SystemdSignalDispatcher::SystemdUnitNew,
|
||||
this,
|
||||
[this](QString serviceName, QDBusObjectPath systemdUnitPath) {
|
||||
[this](const QString &serviceName, const QDBusObjectPath &systemdUnitPath) {
|
||||
auto [appId, instanceId] = processServiceName(serviceName);
|
||||
if (appId.isEmpty()) {
|
||||
return;
|
||||
@ -50,21 +51,17 @@ ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr<Identifie
|
||||
connect(&dispatcher,
|
||||
&SystemdSignalDispatcher::SystemdUnitRemoved,
|
||||
this,
|
||||
[this](QString serviceName, QDBusObjectPath systemdUnitPath) {
|
||||
[this](const QString &serviceName, QDBusObjectPath systemdUnitPath) {
|
||||
auto pair = processServiceName(serviceName);
|
||||
auto appId = pair.first, instanceId = pair.second;
|
||||
auto appId = pair.first;
|
||||
auto instanceId = pair.second;
|
||||
if (appId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto appIt = std::find_if(m_applicationList.cbegin(),
|
||||
m_applicationList.cend(),
|
||||
[&appId](const QSharedPointer<ApplicationService> &app) {
|
||||
if (app->id() == appId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
[&appId](const QSharedPointer<ApplicationService> &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_ptr<Identifie
|
||||
auto instanceIt = std::find_if(appRef->m_Instances.cbegin(),
|
||||
appRef->m_Instances.cend(),
|
||||
[&systemdUnitPath](const QSharedPointer<InstanceService> &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<Identifie
|
||||
});
|
||||
}
|
||||
|
||||
QPair<QString, QString> ApplicationManager1Service::processServiceName(const QString &serviceName)
|
||||
QPair<QString, QString> ApplicationManager1Service::processServiceName(const QString &serviceName) noexcept
|
||||
{
|
||||
QString instanceId;
|
||||
QString applicationId;
|
||||
@ -128,23 +122,23 @@ QList<QDBusObjectPath> 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<bool>(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<ApplicationService> &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<std::size_t>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
ApplicationManager1Service &operator=(ApplicationManager1Service &&) = delete;
|
||||
|
||||
Q_PROPERTY(QList<QDBusObjectPath> List READ list)
|
||||
QList<QDBusObjectPath> list() const;
|
||||
[[nodiscard]] QList<QDBusObjectPath> list() const;
|
||||
template <typename T>
|
||||
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<Identifier> m_identifier;
|
||||
QScopedPointer<JobManager1Service> m_jobManager{nullptr};
|
||||
QMap<QDBusObjectPath, QSharedPointer<ApplicationService>> m_applicationList;
|
||||
|
||||
QPair<QString, QString> processServiceName(const QString &serviceName);
|
||||
static QPair<QString, QString> processServiceName(const QString &serviceName) noexcept;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -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)
|
||||
|
@ -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<QDBusObjectPath> Instances READ instances)
|
||||
QList<QDBusObjectPath> instances() const noexcept;
|
||||
[[nodiscard]] QList<QDBusObjectPath> 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);
|
||||
|
@ -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
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
|
||||
~JobManager1Service() override;
|
||||
template <typename F>
|
||||
QDBusObjectPath addJob(QString source, F func, QVariantList args)
|
||||
QDBusObjectPath addJob(const QString &source, F func, QVariantList args)
|
||||
{
|
||||
static_assert(std::is_invocable_v<F, QVariant>, "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<QDBusObjectPath, QSharedPointer<JobService>> m_jobs;
|
||||
ApplicationManager1Service *m_parent{nullptr};
|
||||
|
@ -3,6 +3,7 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#include "desktopentry.h"
|
||||
#include "global.h"
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <algorithm>
|
||||
@ -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((?<MainKey>[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> 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<QDir>{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> 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<QMap<QString, DesktopEntry::Value>> 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> 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;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <QLocale>
|
||||
#include <QTextStream>
|
||||
#include <optional>
|
||||
#include <sys/stat.h>
|
||||
|
||||
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<DesktopFile> 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<QString, QMap<QString, Value>> 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);
|
||||
|
86
src/global.h
86
src/global.h
@ -14,10 +14,10 @@
|
||||
#include <QMetaType>
|
||||
#include <QMetaClassInfo>
|
||||
#include <QLocale>
|
||||
#include <QDir>
|
||||
#include <QRegularExpression>
|
||||
#include <QDBusObjectPath>
|
||||
#include <unistd.h>
|
||||
#include <optional>
|
||||
#include "constant.h"
|
||||
#include "config.h"
|
||||
|
||||
@ -25,17 +25,57 @@ using IconMap = QMap<QString, QMap<uint, QMap<QString, QDBusUnixFileDescriptor>>
|
||||
|
||||
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 <typename T>
|
||||
using remove_cvr_t = std::remove_reference_t<std::remove_cv_t<T>>;
|
||||
|
||||
template <typename T>
|
||||
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_same_v<decltype(func(QFileInfo{})), bool>,
|
||||
"apply function should return a boolean to indicate when should return");
|
||||
QList<QDir> 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<QDBusConnection> 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
|
||||
|
@ -11,7 +11,7 @@ class SystemdSignalDispatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~SystemdSignalDispatcher() = default;
|
||||
~SystemdSignalDispatcher() override = default;
|
||||
static SystemdSignalDispatcher &instance()
|
||||
{
|
||||
static SystemdSignalDispatcher dispatcher;
|
||||
|
Loading…
Reference in New Issue
Block a user