feat: add objectManager interface

refact the way to get DBus interface from Qt Meta System

Signed-off-by: ComixHe <heyuming@deepin.org>
This commit is contained in:
ComixHe 2023-08-18 10:01:52 +08:00 committed by Comix
parent bb83716d27
commit d49a99d252
12 changed files with 165 additions and 71 deletions

View File

@ -4,9 +4,12 @@
<property name="Application" type="o" access="read">
<annotation
name="org.freedesktop.DBus.Description"
value="Object path of the Application.
value="Object path of the Application.
That DBus object will impelement
org.desktopspec.ApplicationManager1.Application."
org.desktopspec.ApplicationManager1.Application.
NOTE:
If the application is uninstalled, this object path
will be set to `/`."
/>
</property>

View File

@ -30,6 +30,5 @@
1. You should use pidfd_open(2) to get a pidfd."
/>
</method>
</interface>
</node>

View File

@ -0,0 +1,19 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.desktopspec.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg name="objectpath_and_interfaces" type="a{oas}" direction="out" />
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ObjectMap"/>
</method>
<signal name="InterfacesAdded">
<arg name="object_path" type="o" />
<arg name="interfaces" type="as" />
</signal>
<signal name="InterfacesRemoved">
<arg name="object_path" type="o" />
<arg name="interfaces" type="as" />
</signal>
</interface>
</node>

View File

@ -15,6 +15,7 @@ void registerComplexDbusType()
qDBusRegisterMetaType<QMap<QString, QDBusUnixFileDescriptor>>();
qDBusRegisterMetaType<QMap<uint, QMap<QString, QDBusUnixFileDescriptor>>>();
qDBusRegisterMetaType<IconMap>();
qDBusRegisterMetaType<ObjectMap>();
}
} // namespace

View File

@ -11,7 +11,8 @@ qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.deskto
qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.ApplicationManager1.Instance.xml dbus/instanceservice.h InstanceService)
qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.JobManager1.xml dbus/jobmanager1service.h JobManager1Service)
qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.JobManager1.Job.xml dbus/jobservice.h JobService)
qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.ObjectManager1.xml dbus/applicationmanager1service.h ApplicationManager1Service AMobjectmanager1adaptor AMObjectManagerAdaptor)
qt_add_dbus_adaptor(dde_am_dbus_SOURCE ${PROJECT_SOURCE_DIR}/api/dbus/org.desktopspec.ObjectManager1.xml dbus/applicationservice.h ApplicationService APPobjectmanager1adaptor APPObjectManagerAdaptor)
target_sources(dde_am_dbus PRIVATE
${dde_am_dbus_SOURCE}

View File

@ -1,8 +1,9 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "dbus/applicationmanager1service.h"
#include "dbus/applicationmanager1adaptor.h"
#include "dbus/AMobjectmanager1adaptor.h"
#include "systemdsignaldispatcher.h"
#include <QFile>
#include <unistd.h>
@ -17,8 +18,14 @@ ApplicationManager1Service::ApplicationManager1Service(std::unique_ptr<Identifie
}
new ApplicationManager1Adaptor{this};
auto *tmp = new AMObjectManagerAdaptor{this};
if (!registerObjectToDBus(this, DDEApplicationManager1ObjectPath, getDBusInterface<ApplicationManager1Adaptor>())) {
if (tmp == nullptr) {
std::terminate();
}
if (!registerObjectToDBus(
this, DDEApplicationManager1ObjectPath, getDBusInterface(QMetaType::fromType<ApplicationManager1Adaptor>()))) {
std::terminate();
}
@ -98,6 +105,7 @@ QList<QDBusObjectPath> ApplicationManager1Service::list() const
void ApplicationManager1Service::removeOneApplication(const QDBusObjectPath &application)
{
if (auto it = m_applicationList.find(application); it != m_applicationList.cend()) {
emit InterfacesRemoved(application, getInterfacesListFromObject(it->data()));
unregisterObjectFromDBus(application.path());
m_applicationList.remove(application);
}
@ -207,3 +215,15 @@ void ApplicationManager1Service::UpdateApplicationInfo(const QStringList &appIdL
addApplication(std::move(file).value());
}
}
ObjectMap ApplicationManager1Service::GetManagedObjects() const
{
ObjectMap objs;
for (const auto &[key, value] : m_applicationList.asKeyValueRange()) {
auto interfaces = getInterfacesListFromObject(value.data());
objs.insert(key, interfaces);
}
return objs;
}

View File

@ -13,7 +13,7 @@
#include <memory>
#include <QMap>
#include "dbus/jobmanager1service.h"
#include "dbus/applicationservice.h"
#include "dbus/APPobjectmanager1adaptor.h"
#include "dbus/applicationadaptor.h"
#include "identifier.h"
@ -39,13 +39,25 @@ public:
return false;
}
if (m_applicationList.constFind(application->m_applicationPath) != m_applicationList.cend()) {
auto info = qInfo();
info << "this application already exists.";
if (application->m_isPersistence) {
info << "desktop source:" << application->m_desktopSource.m_file.filePath();
}
return false;
}
auto *ptr = application.data();
new ApplicationAdaptor{ptr};
if (!registerObjectToDBus(ptr, application->m_applicationPath.path(), getDBusInterface<ApplicationAdaptor>())) {
if (!registerObjectToDBus(
ptr, application->m_applicationPath.path(), getDBusInterface(QMetaType::fromType<ApplicationAdaptor>()))) {
return false;
}
m_applicationList.insert(application->m_applicationPath, application);
emit InterfacesAdded(application->m_applicationPath, getInterfacesListFromObject(ptr));
return true;
}
void removeOneApplication(const QDBusObjectPath &application);
@ -58,6 +70,11 @@ public:
public Q_SLOTS:
QString Identify(const QDBusUnixFileDescriptor &pidfd, QDBusObjectPath &application, QDBusObjectPath &application_instance);
void UpdateApplicationInfo(const QStringList &appIdList);
[[nodiscard]] ObjectMap GetManagedObjects() const;
Q_SIGNALS:
void InterfacesAdded(const QDBusObjectPath &object_path, const QStringList &interfaces);
void InterfacesRemoved(const QDBusObjectPath &object_path, const QStringList &interfaces);
private:
std::unique_ptr<Identifier> m_identifier;
@ -65,4 +82,58 @@ private:
QMap<QDBusObjectPath, QSharedPointer<ApplicationService>> m_applicationList;
};
template <typename T>
QSharedPointer<ApplicationService> makeApplication(T &&source, ApplicationManager1Service *parent)
{
static_assert(std::is_same_v<T, DesktopFile> or std::is_same_v<T, QString>, "param type must be QString or DesktopFile.");
QString objectPath;
QTextStream sourceStream;
QFile sourceFile;
QSharedPointer<ApplicationService> app{nullptr};
if constexpr (std::is_same_v<T, DesktopFile>) {
DesktopFile in{std::forward<T>(source)};
objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + escapeToObjectPath(in.desktopId());
sourceFile.setFileName(in.filePath());
if (!sourceFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) {
qCritical() << "desktop file can't open:" << in.filePath() << sourceFile.errorString();
return nullptr;
}
app.reset(new ApplicationService{std::move(in)});
sourceStream.setDevice(&sourceFile);
} else if (std::is_same_v<T, QString>) {
QString in{std::forward<T>(source)};
objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + QUuid::createUuid().toString(QUuid::Id128);
app.reset(new ApplicationService{std::move(in)});
sourceStream.setString(&in, QTextStream::ReadOnly | QTextStream::Text);
}
std::unique_ptr<DesktopEntry> entry{std::make_unique<DesktopEntry>()};
auto error = entry->parse(sourceStream);
if (error != DesktopErrorCode::NoError) {
if (error != DesktopErrorCode::EntryKeyInvalid) {
return nullptr;
}
}
if (auto val = entry->value(DesktopFileEntryKey, "Hidden"); val.has_value()) {
bool ok{false};
if (auto hidden = val.value().toBoolean(ok); ok and hidden) {
return nullptr;
}
}
app->m_parent = parent;
app->m_entry.reset(entry.release());
app->m_applicationPath = QDBusObjectPath{std::move(objectPath)};
// TODO: icon lookup
new APPObjectManagerAdaptor{app.data()};
return app;
}
#endif

View File

@ -206,6 +206,18 @@ QStringList ApplicationService::actions() const noexcept
return actionList;
}
ObjectMap ApplicationService::GetManagedObjects() const
{
ObjectMap objs;
for (const auto &[key, value] : m_Instances.asKeyValueRange()) {
auto interfaces = getInterfacesListFromObject(value.data());
objs.insert(key, interfaces);
}
return objs;
}
QString ApplicationService::id() const noexcept
{
if (m_isPersistence) {
@ -292,10 +304,11 @@ bool ApplicationService::addOneInstance(const QString &instanceId, const QString
auto adaptor = new InstanceAdaptor(service);
QString objectPath{m_applicationPath.path() + "/" + instanceId};
if (registerObjectToDBus(service, objectPath, getDBusInterface<InstanceAdaptor>())) {
if (registerObjectToDBus(service, objectPath, getDBusInterface(QMetaType::fromType<InstanceAdaptor>()))) {
m_Instances.insert(QDBusObjectPath{objectPath}, QSharedPointer<InstanceService>{service});
service->moveToThread(this->thread());
adaptor->moveToThread(this->thread());
emit InterfacesAdded(QDBusObjectPath{objectPath}, getInterfacesListFromObject(service));
return true;
}
@ -306,8 +319,11 @@ bool ApplicationService::addOneInstance(const QString &instanceId, const QString
void ApplicationService::removeOneInstance(const QDBusObjectPath &instance)
{
unregisterObjectFromDBus(instance.path());
m_Instances.remove(instance);
if (auto it = m_Instances.find(instance); it != m_Instances.cend()) {
emit InterfacesRemoved(instance, getInterfacesListFromObject(it->data()));
unregisterObjectFromDBus(instance.path());
m_Instances.remove(instance);
}
}
void ApplicationService::removeAllInstance()

View File

@ -67,6 +67,11 @@ public:
public Q_SLOTS:
QString GetActionName(const QString &identifier, const QStringList &env);
QDBusObjectPath Launch(QString action, QStringList fields, QVariantMap options);
[[nodiscard]] ObjectMap GetManagedObjects() const;
Q_SIGNALS:
void InterfacesAdded(const QDBusObjectPath &object_path, const QStringList &interfaces);
void InterfacesRemoved(const QDBusObjectPath &object_path, const QStringList &interfaces);
private:
friend class ApplicationManager1Service;
@ -118,58 +123,4 @@ private:
[[nodiscard]] LaunchTask unescapeExec(const QString &str, const QStringList &fields);
};
template <typename T>
QSharedPointer<ApplicationService> makeApplication(T &&source, ApplicationManager1Service *parent)
{
static_assert(std::is_same_v<T, DesktopFile> or std::is_same_v<T, QString>, "param type must be QString or DesktopFile.");
QString objectPath;
QTextStream sourceStream;
QFile sourceFile;
QSharedPointer<ApplicationService> app{nullptr};
if constexpr (std::is_same_v<T, DesktopFile>) {
DesktopFile in{std::forward<T>(source)};
objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + escapeToObjectPath(in.desktopId());
sourceFile.setFileName(in.filePath());
if (!sourceFile.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) {
qCritical() << "desktop file can't open:" << in.filePath() << sourceFile.errorString();
return nullptr;
}
app.reset(new ApplicationService{std::move(in)});
sourceStream.setDevice(&sourceFile);
} else if (std::is_same_v<T, QString>) {
QString in{std::forward<T>(source)};
objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + QUuid::createUuid().toString(QUuid::Id128);
app.reset(new ApplicationService{std::move(in)});
sourceStream.setString(&in, QTextStream::ReadOnly | QTextStream::Text);
}
std::unique_ptr<DesktopEntry> entry{std::make_unique<DesktopEntry>()};
auto error = entry->parse(sourceStream);
if (error != DesktopErrorCode::NoError) {
if (error != DesktopErrorCode::EntryKeyInvalid) {
return nullptr;
}
}
if (auto val = entry->value(DesktopFileEntryKey, "Hidden"); val.has_value()) {
bool ok{false};
if (auto hidden = val.value().toBoolean(ok); ok and hidden) {
return nullptr;
}
}
app->m_parent = parent;
app->m_entry.reset(entry.release());
app->m_applicationPath = QDBusObjectPath{std::move(objectPath)};
// TODO: icon lookup
return app;
}
#endif

View File

@ -9,7 +9,8 @@ JobManager1Service::JobManager1Service(ApplicationManager1Service *parent)
: m_parent(parent)
{
new JobManager1Adaptor{this};
if (!registerObjectToDBus(this, DDEApplicationManager1JobManagerObjectPath, getDBusInterface<JobManager1Adaptor>())) {
if (!registerObjectToDBus(
this, DDEApplicationManager1JobManagerObjectPath, getDBusInterface(QMetaType::fromType<JobManager1Adaptor>()))) {
std::terminate();
}
qRegisterMetaType<LaunchTask>();

View File

@ -61,7 +61,7 @@ public:
auto *ptr = job.data();
new JobAdaptor(ptr);
if (!registerObjectToDBus(ptr, objectPath, getDBusInterface<JobAdaptor>())) {
if (!registerObjectToDBus(ptr, objectPath, getDBusInterface(QMetaType::fromType<JobAdaptor>()))) {
qCritical() << "can't register job to dbus.";
future.cancel();
return {};

View File

@ -23,6 +23,7 @@
#include "config.h"
using IconMap = QMap<QString, QMap<uint, QMap<QString, QDBusUnixFileDescriptor>>>;
using ObjectMap = QMap<QDBusObjectPath, QStringList>;
inline QString getApplicationLauncherBinary()
{
@ -185,11 +186,9 @@ private:
bool registerObjectToDBus(QObject *o, const QString &path, const QString &interface);
void unregisterObjectFromDBus(const QString &path);
template <typename T>
QString getDBusInterface()
inline QString getDBusInterface(const QMetaType &meta)
{
auto meta = QMetaType::fromType<T>();
auto infoObject = meta.metaObject();
const auto *infoObject = meta.metaObject();
if (auto infoIndex = infoObject->indexOfClassInfo("D-Bus Interface"); infoIndex != -1) {
return infoObject->classInfo(infoIndex).value();
}
@ -197,6 +196,19 @@ QString getDBusInterface()
return {};
}
inline QStringList getInterfacesListFromObject(QObject *o)
{
auto childs = o->children();
QStringList interfaces;
std::for_each(childs.cbegin(), childs.cend(), [&interfaces](QObject *app) {
if (app->inherits("QDBusAbstractAdaptor")) {
interfaces.emplace_back(getDBusInterface(app->metaObject()->metaType()));
}
});
return interfaces;
}
inline uid_t getCurrentUID()
{
return getuid(); // current use linux getuid