feat: implementation of the major feature of dbus service
Log: Signed-off-by: ComixHe <heyuming@deepin.org>
This commit is contained in:
@ -2,12 +2,27 @@
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#include "applicationmanager1service.h"
|
||||
#include "applicationadaptor.h"
|
||||
#include "global.h"
|
||||
#include "applicationmanager1adaptor.h"
|
||||
#include <QFile>
|
||||
#include <unistd.h>
|
||||
|
||||
ApplicationManager1Service::~ApplicationManager1Service() = default;
|
||||
|
||||
ApplicationManager1Service::ApplicationManager1Service() = default;
|
||||
ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr<Identifier> ptr, QDBusConnection &connection)
|
||||
: m_identifier(std::move(ptr))
|
||||
{
|
||||
if (!connection.registerService(DDEApplicationManager1ServiceName)) {
|
||||
qFatal() << connection.lastError();
|
||||
}
|
||||
|
||||
if (!registerObjectToDBus(new ApplicationManager1Adaptor{this},
|
||||
DDEApplicationManager1ObjectPath,
|
||||
getDBusInterface<ApplicationManager1Adaptor>())) {
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
m_jobManager.reset(new JobManager1Service(this));
|
||||
}
|
||||
|
||||
QList<QDBusObjectPath> ApplicationManager1Service::list() const
|
||||
{
|
||||
@ -24,9 +39,18 @@ void ApplicationManager1Service::removeAllApplication()
|
||||
m_applicationList.clear();
|
||||
}
|
||||
|
||||
QDBusObjectPath ApplicationManager1Service::Application(const QString &id)
|
||||
QDBusObjectPath ApplicationManager1Service::Application(const QString &id) const
|
||||
{
|
||||
// TODO: impl
|
||||
auto ret = std::find_if(m_applicationList.cbegin(), m_applicationList.cend(), [&id](const auto &app) {
|
||||
if (app->id() == id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (ret != m_applicationList.cend()) {
|
||||
return ret.key();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -34,8 +58,52 @@ QString ApplicationManager1Service::Identify(const QDBusUnixFileDescriptor &pidf
|
||||
QDBusObjectPath &application,
|
||||
QDBusObjectPath &application_instance)
|
||||
{
|
||||
// TODO: impl
|
||||
return {};
|
||||
if (!pidfd.isValid()) {
|
||||
qWarning() << "pidfd isn't a valid unix file descriptor";
|
||||
return {};
|
||||
}
|
||||
|
||||
Q_ASSERT_X(static_cast<bool>(m_identifier), "Identify", "Broken Identifier.");
|
||||
|
||||
QString fdFilePath = QString{"/proc/self/fdinfo/%1"}.arg(pidfd.fileDescriptor());
|
||||
QFile fdFile{fdFilePath};
|
||||
if (!fdFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) {
|
||||
qWarning() << "open " << fdFilePath << "failed: " << fdFile.errorString();
|
||||
return {};
|
||||
}
|
||||
auto content = fdFile.readAll();
|
||||
QTextStream stream{content};
|
||||
QString appPid;
|
||||
while (!stream.atEnd()) {
|
||||
auto line = stream.readLine();
|
||||
if (line.startsWith("Pid")) {
|
||||
appPid = line.split(":").last().trimmed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (appPid.isEmpty()) {
|
||||
qWarning() << "can't find pid which corresponding with the instance of this application.";
|
||||
return {};
|
||||
}
|
||||
bool ok;
|
||||
auto pid = appPid.toUInt(&ok);
|
||||
if (!ok) {
|
||||
qWarning() << "AppId is failed to convert to uint.";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto ret = m_identifier->Identify(pid);
|
||||
|
||||
auto app = std::find_if(m_applicationList.cbegin(), m_applicationList.cend(), [&ret](const auto &appPtr) {
|
||||
return appPtr->id() == ret.ApplicationId;
|
||||
});
|
||||
if (app == m_applicationList.cend()) {
|
||||
qWarning() << "can't find application:" << ret.ApplicationId;
|
||||
return {};
|
||||
}
|
||||
application = m_applicationList.key(*app);
|
||||
application_instance = (*app)->findInstance(ret.InstanceId);
|
||||
return ret.ApplicationId;
|
||||
}
|
||||
|
||||
QDBusObjectPath ApplicationManager1Service::Launch(const QString &id,
|
||||
@ -43,18 +111,13 @@ QDBusObjectPath ApplicationManager1Service::Launch(const QString &id,
|
||||
const QStringList &fields,
|
||||
const QVariantMap &options)
|
||||
{
|
||||
// TODO: impl reset of Launch
|
||||
QString objectPath;
|
||||
if (id.contains('.')) {
|
||||
objectPath = id.split('.').first();
|
||||
auto app = Application(id);
|
||||
if (app.path().isEmpty()) {
|
||||
qWarning() << "No such application.";
|
||||
return {};
|
||||
}
|
||||
objectPath.prepend(DDEApplicationManager1ApplicationObjectPath);
|
||||
QSharedPointer<ApplicationService> app{new ApplicationService{id}};
|
||||
auto *ptr = app.data();
|
||||
if (registerObjectToDbus(new ApplicationAdaptor(ptr), objectPath, getDBusInterface<ApplicationAdaptor>())) {
|
||||
QDBusObjectPath path{objectPath};
|
||||
m_applicationList.insert(path, app);
|
||||
return path;
|
||||
}
|
||||
return {};
|
||||
const auto &value = m_applicationList.value(app);
|
||||
return value->Launch(actions, fields, options);
|
||||
}
|
||||
|
||||
void ApplicationManager1Service::UpdateApplicationInfo(const QStringList &update_path) {}
|
||||
|
@ -9,14 +9,19 @@
|
||||
#include <QDBusObjectPath>
|
||||
#include <QDBusUnixFileDescriptor>
|
||||
#include <QSharedPointer>
|
||||
#include <QScopedPointer>
|
||||
#include <memory>
|
||||
#include <QMap>
|
||||
#include "jobmanager1service.h"
|
||||
#include "applicationservice.h"
|
||||
#include "applicationadaptor.h"
|
||||
#include "identifier.h"
|
||||
|
||||
class ApplicationManager1Service final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ApplicationManager1Service();
|
||||
explicit ApplicationManager1Service(std::unique_ptr<Identifier> ptr, QDBusConnection &connection);
|
||||
~ApplicationManager1Service() override;
|
||||
ApplicationManager1Service(const ApplicationManager1Service &) = delete;
|
||||
ApplicationManager1Service(ApplicationManager1Service &&) = delete;
|
||||
@ -25,16 +30,35 @@ public:
|
||||
|
||||
Q_PROPERTY(QList<QDBusObjectPath> List READ list)
|
||||
QList<QDBusObjectPath> list() const;
|
||||
void addApplication(const QString &ID, const QStringList &actions, bool AutoStart = false);
|
||||
template <typename T>
|
||||
bool addApplication(T &&desktopFileSource)
|
||||
{
|
||||
QSharedPointer<ApplicationService> application{new ApplicationService{std::forward<T>(desktopFileSource), this}};
|
||||
if (!application) {
|
||||
return false;
|
||||
}
|
||||
if (!registerObjectToDBus(new ApplicationAdaptor{application.data()},
|
||||
application->m_applicationPath.path(),
|
||||
getDBusInterface<ApplicationAdaptor>())) {
|
||||
return false;
|
||||
}
|
||||
m_applicationList.insert(application->m_applicationPath, application);
|
||||
return true;
|
||||
}
|
||||
bool removeOneApplication(const QDBusObjectPath &application);
|
||||
void removeAllApplication();
|
||||
|
||||
JobManager1Service &jobManager() noexcept { return *m_jobManager; }
|
||||
|
||||
public Q_SLOTS:
|
||||
QDBusObjectPath Application(const QString &id);
|
||||
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);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Identifier> m_identifier;
|
||||
QScopedPointer<JobManager1Service> m_jobManager{nullptr};
|
||||
QMap<QDBusObjectPath, QSharedPointer<ApplicationService>> m_applicationList;
|
||||
};
|
||||
|
||||
|
@ -3,61 +3,220 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#include "applicationservice.h"
|
||||
#include "applicationadaptor.h"
|
||||
#include "applicationmanager1service.h"
|
||||
#include "instanceadaptor.h"
|
||||
#include "pwd.h"
|
||||
#include <QUuid>
|
||||
#include <QStringList>
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
#include <QRegularExpression>
|
||||
#include <QProcess>
|
||||
#include <algorithm>
|
||||
|
||||
ApplicationService::ApplicationService(QString ID)
|
||||
: m_AutoStart(false)
|
||||
, m_ID(std::move(ID))
|
||||
, m_applicationPath(DDEApplicationManager1ApplicationObjectPath + m_ID)
|
||||
ApplicationService::~ApplicationService()
|
||||
{
|
||||
m_desktopSource.destruct(m_isPersistence);
|
||||
}
|
||||
|
||||
ApplicationService::~ApplicationService() = default;
|
||||
|
||||
QString ApplicationService::GetActionName(const QString &identifier, const QStringList &env)
|
||||
{
|
||||
// TODO: impl
|
||||
return {};
|
||||
const auto &supportedActions = actions();
|
||||
if (supportedActions.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
if (auto index = supportedActions.indexOf(identifier); index == -1) {
|
||||
qWarning() << "can't find " << identifier << " in supported actions List.";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &actionHeader = QString{"%1%2"}.arg(DesktopFileActionKey, identifier);
|
||||
const auto &actionName = m_entry->value(actionHeader, "Name");
|
||||
if (!actionName) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QString locale{""};
|
||||
bool ok;
|
||||
if (!env.isEmpty()) {
|
||||
QString lcStr;
|
||||
for (const auto &lc : env) {
|
||||
if (lc.startsWith("LANG")) {
|
||||
locale = lc.split('=').last();
|
||||
}
|
||||
|
||||
if (lc.startsWith("LC_ALL")) {
|
||||
locale = lc.split('=').last();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QLocale lc = locale.isEmpty() ? getUserLocale() : QLocale{locale};
|
||||
|
||||
const auto &name = actionName->toLocaleString(lc, ok);
|
||||
if (!ok) {
|
||||
qWarning() << "convert to locale string failed, dest locale:" << lc;
|
||||
return {};
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
QDBusObjectPath ApplicationService::Launch(const QString &action, const QStringList &fields, const QVariantMap &options)
|
||||
QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, QVariantMap options)
|
||||
{
|
||||
// TODO: impl launch app from systemd.
|
||||
QString objectPath{m_applicationPath.path() + "/" + QUuid::createUuid().toString()};
|
||||
QSharedPointer<InstanceService> ins{new InstanceService{objectPath, ""}};
|
||||
auto *ptr = ins.data();
|
||||
if (registerObjectToDbus(new InstanceAdaptor(ptr), objectPath, getDBusInterface<InstanceAdaptor>())) {
|
||||
m_Instances.insert(QDBusObjectPath{objectPath}, ins);
|
||||
return QDBusObjectPath{objectPath};
|
||||
QString execStr;
|
||||
bool ok;
|
||||
const auto &supportedActions = actions();
|
||||
|
||||
while (!action.isEmpty() and !supportedActions.isEmpty()) { // break trick
|
||||
if (auto index = supportedActions.indexOf(action); index == -1) {
|
||||
qWarning() << "can't find " << action << " in supported actions List. application will use default action to launch.";
|
||||
break;
|
||||
}
|
||||
|
||||
const auto &actionHeader = QString{"%1%2"}.arg(DesktopFileEntryKey, action);
|
||||
const auto &actionExec = m_entry->value(actionHeader, "Exec");
|
||||
if (!actionExec) {
|
||||
break;
|
||||
}
|
||||
execStr = actionExec->toString(ok);
|
||||
if (!ok) {
|
||||
qWarning() << "exec value to string failed, try default action.";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
|
||||
if (execStr.isEmpty()) {
|
||||
auto Actions = m_entry->value(DesktopFileEntryKey, "Exec");
|
||||
if (!Actions) {
|
||||
qWarning() << "application has isn't executable.";
|
||||
return {};
|
||||
}
|
||||
execStr = Actions->toString(ok);
|
||||
if (!ok) {
|
||||
qWarning() << "default action value to string failed. abort...";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
auto [bin, cmds, res] = unescapeExec(execStr, fields);
|
||||
|
||||
uid_t uid;
|
||||
auto it = options.find("uid");
|
||||
if (it != options.cend()) {
|
||||
uid = it->toUInt(&ok);
|
||||
if (!ok) {
|
||||
qWarning() << "convert uid string to uint failed: " << *it;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (uid != getCurrentUID()) {
|
||||
cmds.prepend(userNameLookup(uid));
|
||||
cmds.prepend("--user");
|
||||
cmds.prepend("pkexec");
|
||||
}
|
||||
} else {
|
||||
uid = getCurrentUID();
|
||||
}
|
||||
|
||||
cmds.prepend("--");
|
||||
|
||||
auto &jobManager = m_parent->jobManager();
|
||||
|
||||
return jobManager.addJob(
|
||||
m_applicationPath.path(),
|
||||
[this, binary = std::move(bin), commands = std::move(cmds)](QVariant variantValue) mutable -> QVariant {
|
||||
auto resourceFile = variantValue.toString();
|
||||
if (resourceFile.isEmpty()) {
|
||||
auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128);
|
||||
commands.push_front(QString{R"(--unitName=DDE-%1@%2.service)"}.arg(
|
||||
this->id(), instanceRandomUUID)); // launcher should use this instanceId
|
||||
QProcess process;
|
||||
process.start(m_launcher, commands);
|
||||
process.waitForFinished();
|
||||
if (auto code = process.exitCode(); code != 0) {
|
||||
qWarning() << "Launch Application Failed. exitCode:" << code;
|
||||
return false;
|
||||
}
|
||||
return addOneInstance(instanceRandomUUID, m_applicationPath.path()); // TODO: pass correct Systemd Unit Path
|
||||
}
|
||||
|
||||
int location{0};
|
||||
location = commands.indexOf(R"(%f)");
|
||||
if (location != -1) { // due to std::move, there only remove once
|
||||
commands.remove(location);
|
||||
}
|
||||
|
||||
auto url = QUrl::fromUserInput(resourceFile);
|
||||
if (!url.isValid()) { // if url is invalid, passing to launcher directly
|
||||
auto scheme = url.scheme();
|
||||
if (!scheme.isEmpty()) {
|
||||
// TODO: resourceFile = processRemoteFile(resourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
// resourceFile must be available in the following contexts
|
||||
auto tmp = commands;
|
||||
tmp.insert(location, resourceFile);
|
||||
auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128);
|
||||
tmp.push_front(QString{R"(--unitName=DDE-%1@%2.service)"}.arg(this->id(), instanceRandomUUID));
|
||||
QProcess process;
|
||||
process.start(ApplicationLauncherBinary, tmp);
|
||||
process.waitForFinished();
|
||||
auto exitCode = process.exitCode();
|
||||
if (exitCode != 0) {
|
||||
qWarning() << "Launch Application Failed:" << binary << tmp;
|
||||
}
|
||||
return addOneInstance(instanceRandomUUID, m_applicationPath.path());
|
||||
},
|
||||
std::move(res));
|
||||
}
|
||||
|
||||
QStringList ApplicationService::actions() const noexcept
|
||||
{
|
||||
return m_actions;
|
||||
if (m_entry.isNull()) {
|
||||
qWarning() << "desktop entry is empty, isPersistence:" << m_isPersistence;
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &actions = m_entry->value(DesktopFileEntryKey, "Actions");
|
||||
if (!actions) {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ok{false};
|
||||
const auto &str = actions->toString(ok);
|
||||
if (!ok) {
|
||||
qWarning() << "Actions convert to String failed.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto actionList = str.split(";");
|
||||
actionList.removeAll("");
|
||||
|
||||
return actionList;
|
||||
}
|
||||
|
||||
QStringList &ApplicationService::actionsRef() noexcept
|
||||
QString ApplicationService::id() const noexcept
|
||||
{
|
||||
return m_actions;
|
||||
}
|
||||
|
||||
QString ApplicationService::iD() const noexcept
|
||||
{
|
||||
return m_ID;
|
||||
if (m_isPersistence) {
|
||||
return m_desktopSource.m_file.desktopId();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
IconMap ApplicationService::icons() const
|
||||
{
|
||||
return m_Icons;
|
||||
if (m_Icons) {
|
||||
return m_Icons->icons();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
IconMap &ApplicationService::iconsRef()
|
||||
{
|
||||
return m_Icons;
|
||||
return m_Icons->iconsRef();
|
||||
}
|
||||
|
||||
bool ApplicationService::isAutoStart() const noexcept
|
||||
@ -75,12 +234,168 @@ QList<QDBusObjectPath> ApplicationService::instances() const noexcept
|
||||
return m_Instances.keys();
|
||||
}
|
||||
|
||||
bool ApplicationService::removeOneInstance(const QDBusObjectPath &instance)
|
||||
bool ApplicationService::addOneInstance(const QString &instanceId, const QString &application, const QString &systemdUnitPath)
|
||||
{
|
||||
return m_Instances.remove(instance) != 0;
|
||||
auto service = new InstanceService{instanceId, application, systemdUnitPath};
|
||||
auto adaptor = new InstanceAdaptor(service);
|
||||
QString objectPath{DDEApplicationManager1InstanceObjectPath + instanceId};
|
||||
|
||||
if (registerObjectToDBus(adaptor, objectPath, getDBusInterface<InstanceAdaptor>())) {
|
||||
m_Instances.insert(QDBusObjectPath{objectPath}, QSharedPointer<InstanceService>{service});
|
||||
service->moveToThread(this->thread());
|
||||
adaptor->moveToThread(this->thread());
|
||||
return true;
|
||||
}
|
||||
|
||||
adaptor->deleteLater();
|
||||
service->deleteLater();
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApplicationService::removeOneInstance(const QDBusObjectPath &instance)
|
||||
{
|
||||
m_Instances.remove(instance);
|
||||
unregisterObjectFromDBus(instance.path());
|
||||
}
|
||||
|
||||
void ApplicationService::removeAllInstance()
|
||||
{
|
||||
m_Instances.clear();
|
||||
for (const auto &instance : m_Instances.keys()) {
|
||||
removeOneInstance(instance);
|
||||
}
|
||||
}
|
||||
|
||||
QDBusObjectPath ApplicationService::findInstance(const QString &instanceId) const
|
||||
{
|
||||
for (const auto &[path, ptr] : m_Instances.asKeyValueRange()) {
|
||||
if (ptr->instanceId() == instanceId) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString ApplicationService::userNameLookup(uid_t uid)
|
||||
{
|
||||
auto pws = getpwuid(uid);
|
||||
return pws->pw_name;
|
||||
}
|
||||
|
||||
LaunchTask ApplicationService::unescapeExec(const QString &str, const QStringList &fields)
|
||||
{
|
||||
LaunchTask task;
|
||||
QStringList execList = str.split(' ');
|
||||
task.LaunchBin = execList.first();
|
||||
|
||||
QRegularExpression re{"%[fFuUickdDnNvm]"};
|
||||
auto matcher = re.match(str);
|
||||
if (!matcher.hasMatch()) {
|
||||
task.command.append(std::move(execList));
|
||||
return task;
|
||||
}
|
||||
|
||||
auto list = matcher.capturedTexts();
|
||||
if (list.count() > 1) {
|
||||
qWarning() << "invalid exec format, all filed code will be ignored.";
|
||||
for (const auto &code : list) {
|
||||
execList.removeOne(code);
|
||||
}
|
||||
task.command.append(std::move(execList));
|
||||
return task;
|
||||
}
|
||||
|
||||
auto filesCode = list.first().back().toLatin1();
|
||||
auto codeStr = QString(R"(%%1)").arg(filesCode);
|
||||
auto location = execList.indexOf(codeStr);
|
||||
|
||||
switch (filesCode) {
|
||||
case 'f': { // Defer to async job
|
||||
task.command.append(std::move(execList));
|
||||
for (auto &field : fields) {
|
||||
task.Resources.emplace_back(std::move(field));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
execList.removeAt(location);
|
||||
if (fields.count() > 1) {
|
||||
qDebug() << R"(fields count is greater than one, %u will only take first element.)";
|
||||
}
|
||||
execList.insert(location, fields.first());
|
||||
task.command.append(execList);
|
||||
break;
|
||||
}
|
||||
case 'F':
|
||||
[[fallthrough]];
|
||||
case 'U': {
|
||||
execList.removeAt(location);
|
||||
auto it = execList.begin() + location;
|
||||
for (const auto &field : fields) {
|
||||
it = execList.insert(it, field);
|
||||
}
|
||||
task.command.append(std::move(execList));
|
||||
break;
|
||||
}
|
||||
case 'i': {
|
||||
execList.removeAt(location);
|
||||
auto val = m_entry->value(DesktopFileEntryKey, "Icon");
|
||||
if (!val) {
|
||||
qDebug() << R"(Application Icons can't be found. %i will be ignored.)";
|
||||
task.command.append(std::move(execList));
|
||||
return task;
|
||||
}
|
||||
bool ok;
|
||||
auto iconStr = val->toIconString(ok);
|
||||
if (!ok) {
|
||||
qDebug() << R"(Icons Convert to string failed. %i will be ignored.)";
|
||||
task.command.append(std::move(execList));
|
||||
return task;
|
||||
}
|
||||
auto it = execList.insert(location, iconStr);
|
||||
execList.insert(it, "--icon");
|
||||
task.command.append(std::move(execList));
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
execList.removeAt(location);
|
||||
auto val = m_entry->value(DesktopFileEntryKey, "Name");
|
||||
if (!val) {
|
||||
qDebug() << R"(Application Name can't be found. %c will be ignored.)";
|
||||
task.command.append(std::move(execList));
|
||||
return task;
|
||||
}
|
||||
bool ok;
|
||||
auto NameStr = val->toLocaleString(getUserLocale(), ok);
|
||||
if (!ok) {
|
||||
qDebug() << R"(Name Convert to locale string failed. %c will be ignored.)";
|
||||
task.command.append(std::move(execList));
|
||||
return task;
|
||||
}
|
||||
execList.insert(location, NameStr);
|
||||
task.command.append(std::move(execList));
|
||||
break;
|
||||
}
|
||||
case 'k': { // ignore all desktop file location for now.
|
||||
execList.removeAt(location);
|
||||
task.command.append(std::move(execList));
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
case 'D':
|
||||
case 'n':
|
||||
case 'N':
|
||||
case 'v':
|
||||
[[fallthrough]]; // Deprecated field codes should be removed from the command line and ignored.
|
||||
case 'm': {
|
||||
execList.removeAt(location);
|
||||
task.command.append(std::move(execList));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (task.Resources.isEmpty()) {
|
||||
task.Resources.emplace_back(QString{""}); // mapReduce should run once at least
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
@ -11,8 +11,14 @@
|
||||
#include <QString>
|
||||
#include <QDBusUnixFileDescriptor>
|
||||
#include <QSharedPointer>
|
||||
#include <QUuid>
|
||||
#include <QTextStream>
|
||||
#include <QFile>
|
||||
#include "instanceservice.h"
|
||||
#include "global.h"
|
||||
#include "desktopentry.h"
|
||||
#include "desktopicons.h"
|
||||
#include "jobmanager1service.h"
|
||||
|
||||
class ApplicationService : public QObject
|
||||
{
|
||||
@ -26,10 +32,9 @@ public:
|
||||
|
||||
Q_PROPERTY(QStringList Actions READ actions CONSTANT)
|
||||
QStringList actions() const noexcept;
|
||||
QStringList &actionsRef() noexcept;
|
||||
|
||||
Q_PROPERTY(QString ID READ iD CONSTANT)
|
||||
QString iD() const noexcept;
|
||||
Q_PROPERTY(QString ID READ id CONSTANT)
|
||||
QString id() const noexcept;
|
||||
|
||||
Q_PROPERTY(IconMap Icons READ icons)
|
||||
IconMap icons() const;
|
||||
@ -42,23 +47,93 @@ public:
|
||||
Q_PROPERTY(QList<QDBusObjectPath> Instances READ instances)
|
||||
QList<QDBusObjectPath> instances() const noexcept;
|
||||
|
||||
QDBusObjectPath findInstance(const QString &instanceId) const;
|
||||
|
||||
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 = "/");
|
||||
void recoverInstances(const QList<QDBusObjectPath>) noexcept;
|
||||
bool removeOneInstance(const QDBusObjectPath &instance);
|
||||
void removeOneInstance(const QDBusObjectPath &instance); // TODO: remove instance when app closed
|
||||
void removeAllInstance();
|
||||
|
||||
public Q_SLOTS:
|
||||
QString GetActionName(const QString &identifier, const QStringList &env);
|
||||
QDBusObjectPath Launch(const QString &action, const QStringList &fields, const QVariantMap &options);
|
||||
QDBusObjectPath Launch(QString action, QStringList fields, QVariantMap options);
|
||||
|
||||
private:
|
||||
friend class ApplicationManager1Service;
|
||||
explicit ApplicationService(QString ID);
|
||||
bool m_AutoStart;
|
||||
QString m_ID;
|
||||
template <typename T>
|
||||
ApplicationService(T &&source, ApplicationManager1Service *parent)
|
||||
: m_parent(parent)
|
||||
, m_desktopSource(std::forward<T>(source))
|
||||
{
|
||||
static_assert(std::is_same_v<T, DesktopFile> or std::is_same_v<T, QString>, "param type must be QString or DesktopFile.");
|
||||
QString objectPath{DDEApplicationManager1ApplicationObjectPath};
|
||||
QTextStream sourceStream;
|
||||
QFile sourceFile;
|
||||
auto dbusAppid = m_desktopSource.m_file.desktopId();
|
||||
|
||||
if constexpr (std::is_same_v<T, DesktopFile>) {
|
||||
m_applicationPath = QDBusObjectPath{objectPath + escapeToObjectPath(dbusAppid)};
|
||||
m_isPersistence = true;
|
||||
sourceFile.setFileName(m_desktopSource.m_file.filePath());
|
||||
if (!sourceFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) {
|
||||
qCritical() << "desktop file can't open:" << m_desktopSource.m_file.filePath() << sourceFile.errorString();
|
||||
return;
|
||||
}
|
||||
sourceStream.setDevice(&sourceFile);
|
||||
} else if (std::is_same_v<T, QString>) {
|
||||
m_applicationPath = QDBusObjectPath{objectPath + QUuid::createUuid().toString(QUuid::Id128)};
|
||||
m_isPersistence = false;
|
||||
sourceStream.setString(&m_desktopSource.m_temp, QTextStream::ReadOnly | QTextStream::Text);
|
||||
}
|
||||
m_entry.reset(new DesktopEntry());
|
||||
if (auto error = m_entry->parse(sourceStream); error != ParseError::NoError) {
|
||||
if (error != ParseError::EntryKeyInvalid) {
|
||||
m_entry.reset(nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// TODO: icon lookup
|
||||
}
|
||||
|
||||
bool m_AutoStart{false};
|
||||
bool m_isPersistence;
|
||||
ApplicationManager1Service *m_parent{nullptr};
|
||||
QDBusObjectPath m_applicationPath;
|
||||
QStringList m_actions;
|
||||
QString m_launcher{ApplicationLauncherBinary};
|
||||
union DesktopSource
|
||||
{
|
||||
template <typename T, std::enable_if_t<std::is_same_v<T, DesktopFile>, bool> = true>
|
||||
DesktopSource(T &&source)
|
||||
: m_file(std::forward<T>(source))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_same_v<T, QString>, bool> = true>
|
||||
DesktopSource(T &&source)
|
||||
: m_temp(std::forward<T>(source))
|
||||
{
|
||||
}
|
||||
|
||||
void destruct(bool isPersistence)
|
||||
{
|
||||
if (isPersistence) {
|
||||
m_file.~DesktopFile();
|
||||
} else {
|
||||
m_temp.~QString();
|
||||
}
|
||||
}
|
||||
~DesktopSource(){};
|
||||
QString m_temp;
|
||||
DesktopFile m_file;
|
||||
} m_desktopSource;
|
||||
QSharedPointer<DesktopEntry> m_entry{nullptr};
|
||||
QSharedPointer<DesktopIcons> m_Icons{nullptr};
|
||||
QMap<QDBusObjectPath, QSharedPointer<InstanceService>> m_Instances;
|
||||
IconMap m_Icons;
|
||||
QString userNameLookup(uid_t uid);
|
||||
[[nodiscard]] LaunchTask unescapeExec(const QString &str, const QStringList &fields);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -4,8 +4,9 @@
|
||||
|
||||
#include "instanceservice.h"
|
||||
|
||||
InstanceService::InstanceService(QString application, QString systemdUnitPath)
|
||||
: m_Application(std::move(application))
|
||||
InstanceService::InstanceService(QString instanceId, QString application, QString systemdUnitPath)
|
||||
: m_instanceId(std::move(instanceId))
|
||||
, m_Application(std::move(application))
|
||||
, m_SystemdUnitPath(std::move(systemdUnitPath))
|
||||
{
|
||||
}
|
||||
|
@ -18,15 +18,18 @@ public:
|
||||
InstanceService &operator=(const InstanceService &) = delete;
|
||||
InstanceService &operator=(InstanceService &&) = delete;
|
||||
|
||||
Q_PROPERTY(QDBusObjectPath Application READ application CONSTANT)
|
||||
Q_PROPERTY(QDBusObjectPath Application READ application)
|
||||
QDBusObjectPath application() const;
|
||||
|
||||
Q_PROPERTY(QDBusObjectPath SystemdUnitPath READ systemdUnitPath CONSTANT)
|
||||
Q_PROPERTY(QDBusObjectPath SystemdUnitPath READ systemdUnitPath)
|
||||
QDBusObjectPath systemdUnitPath() const;
|
||||
|
||||
const QString &instanceId() const noexcept { return m_instanceId; }
|
||||
|
||||
private:
|
||||
friend class ApplicationService;
|
||||
InstanceService(QString application, QString systemdUnitPath);
|
||||
InstanceService(QString instanceId, QString application, QString systemdUnitPath);
|
||||
QString m_instanceId;
|
||||
const QDBusObjectPath m_Application;
|
||||
const QDBusObjectPath m_SystemdUnitPath;
|
||||
};
|
||||
|
@ -3,7 +3,38 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#include "jobmanager1service.h"
|
||||
#include "jobmanager1adaptor.h"
|
||||
|
||||
JobManager1Service::JobManager1Service() = default;
|
||||
LaunchTask::LaunchTask()
|
||||
{
|
||||
qRegisterMetaType<LaunchTask>();
|
||||
}
|
||||
|
||||
JobManager1Service::JobManager1Service(ApplicationManager1Service *parent)
|
||||
: m_parent(parent)
|
||||
{
|
||||
if (!registerObjectToDBus(
|
||||
new JobManager1Adaptor{this}, DDEApplicationManager1JobManagerObjectPath, getDBusInterface<JobManager1Adaptor>())) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
JobManager1Service::~JobManager1Service() = default;
|
||||
|
||||
bool JobManager1Service::removeOneJob(const QDBusObjectPath &path)
|
||||
{
|
||||
decltype(m_jobs)::size_type removeCount{0};
|
||||
{
|
||||
QMutexLocker locker{&m_mutex};
|
||||
removeCount = m_jobs.remove(path);
|
||||
}
|
||||
// removeCount means m_jobs can't find value which corresponding with path
|
||||
// and we shouldn't emit jobRemoved signal because this signal may already has been emit
|
||||
if (removeCount == 0) {
|
||||
qWarning() << "Job already has been removed: " << path.path();
|
||||
return false;
|
||||
}
|
||||
|
||||
unregisterObjectFromDBus(path.path());
|
||||
return true;
|
||||
}
|
||||
|
@ -14,14 +14,30 @@
|
||||
#include <QtConcurrent>
|
||||
#include <QFuture>
|
||||
#include <QUuid>
|
||||
#include "jobadaptor.h"
|
||||
#include "global.h"
|
||||
#include "jobadaptor.h"
|
||||
|
||||
class ApplicationManager1Service;
|
||||
|
||||
struct LaunchTask
|
||||
{
|
||||
LaunchTask();
|
||||
~LaunchTask() = default;
|
||||
LaunchTask(const LaunchTask &) = default;
|
||||
LaunchTask(LaunchTask &&) = default;
|
||||
LaunchTask &operator=(const LaunchTask &) = default;
|
||||
LaunchTask &operator=(LaunchTask &&) = default;
|
||||
QString LaunchBin;
|
||||
QStringList command;
|
||||
QVariantList Resources;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(LaunchTask)
|
||||
|
||||
class JobManager1Service final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
JobManager1Service();
|
||||
JobManager1Service(const JobManager1Service &) = delete;
|
||||
JobManager1Service(JobManager1Service &&) = delete;
|
||||
JobManager1Service &operator=(const JobManager1Service &) = delete;
|
||||
@ -29,48 +45,48 @@ public:
|
||||
|
||||
~JobManager1Service() override;
|
||||
template <typename F>
|
||||
void addJob(const QDBusObjectPath &source, F func, QVariantList args)
|
||||
QDBusObjectPath addJob(QString source, F func, QVariantList args)
|
||||
{
|
||||
static_assert(std::is_invocable_v<F, QVariant>, "param type must be QVariant.");
|
||||
|
||||
QString objectPath{DDEApplicationManager1JobObjectPath + QUuid::createUuid().toString(QUuid::Id128)};
|
||||
auto future = QtConcurrent::mappedReduced(args.begin(),
|
||||
args.end(),
|
||||
func,
|
||||
qOverload<QVariantList::parameter_type>(&QVariantList::append),
|
||||
QVariantList{},
|
||||
QtConcurrent::ReduceOption::OrderedReduce);
|
||||
QFuture<QVariantList> future = QtConcurrent::mappedReduced(std::move(args),
|
||||
func,
|
||||
qOverload<QVariantList::parameter_type>(&QVariantList::append),
|
||||
QVariantList{},
|
||||
QtConcurrent::ReduceOption::OrderedReduce);
|
||||
QSharedPointer<JobService> job{new JobService{future}};
|
||||
if (!registerObjectToDBus(new JobAdaptor(job.data()), objectPath, getDBusInterface<JobAdaptor>())) {
|
||||
qCritical() << "can't register job to dbus.";
|
||||
future.cancel();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto path = QDBusObjectPath{objectPath};
|
||||
{
|
||||
QMutexLocker locker{&m_mutex};
|
||||
m_jobs.insert(path, job); // Insertion is always successful
|
||||
m_jobs.insert(path, std::move(job)); // Insertion is always successful
|
||||
}
|
||||
emit JobNew(path, source);
|
||||
registerObjectToDbus(new JobAdaptor(job.data()), objectPath, getDBusInterface<JobAdaptor>());
|
||||
emit JobNew(path, QDBusObjectPath{source});
|
||||
|
||||
auto emitRemove = [this, job, path, future](QVariantList value) {
|
||||
decltype(m_jobs)::size_type removeCount{0};
|
||||
{
|
||||
QMutexLocker locker{&m_mutex};
|
||||
removeCount = m_jobs.remove(path);
|
||||
}
|
||||
// removeCount means m_jobs can't find value which corresponding with path
|
||||
// and we shouldn't emit jobRemoved signal because this signal may already has been emit
|
||||
if (removeCount == 0) {
|
||||
qCritical() << "Job already has been removed: " << path.path();
|
||||
if (!removeOneJob(path)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
QString result{job->status()};
|
||||
for (const auto &val : future.result()) {
|
||||
if (val.template canConvert<QDBusError>()) {
|
||||
if (val.canConvert<QDBusError>()) {
|
||||
result = "failed";
|
||||
}
|
||||
break;
|
||||
}
|
||||
emit this->JobRemoved(path, result, future.result());
|
||||
emit JobRemoved(path, result, future.result());
|
||||
return value;
|
||||
};
|
||||
future.then(emitRemove);
|
||||
|
||||
future.then(this, emitRemove);
|
||||
return path;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
@ -78,8 +94,12 @@ Q_SIGNALS:
|
||||
void JobRemoved(const QDBusObjectPath &job, const QString &status, const QVariantList &result);
|
||||
|
||||
private:
|
||||
bool removeOneJob(const QDBusObjectPath &path);
|
||||
friend class ApplicationManager1Service;
|
||||
JobManager1Service(ApplicationManager1Service *parent);
|
||||
QMutex m_mutex;
|
||||
QMap<QDBusObjectPath, QSharedPointer<JobService>> m_jobs;
|
||||
ApplicationManager1Service *m_parent{nullptr};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user