D-Bus fixes (#1048)

According to LorenDB's First Law of Software Development, once a developer has committed or merged a new feature, he will find at least one problem with the implementation.

I realized that I was constructing the room info items with some parameters out of order, which required a rather urgent fix. Furthermore, I fixed the image decoding algorithms in the QDBusArgument operator. Finally, I switched the API version parameter back to QString, since passing it as a QVersionNumber would create a problem for non-Qt API users.

On the general improvements side of things, I added some handy wrappers for D-Bus calls so that other devs that copy the NhekoDBusApi files to use for their own applications won't have to go to the effort of making the D-Bus calls themselves.
This commit is contained in:
Loren Burkholder 2022-04-18 12:50:15 -04:00 committed by GitHub
parent 8cf3f1c077
commit da6b3eb8f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 39 deletions

View file

@ -5,7 +5,9 @@
#include "NhekoDBusApi.h" #include "NhekoDBusApi.h"
#include <QDBusInterface>
#include <QDBusMetaType> #include <QDBusMetaType>
#include <QDBusReply>
namespace nheko::dbus { namespace nheko::dbus {
void void
@ -14,18 +16,17 @@ init()
qDBusRegisterMetaType<RoomInfoItem>(); qDBusRegisterMetaType<RoomInfoItem>();
qDBusRegisterMetaType<QVector<RoomInfoItem>>(); qDBusRegisterMetaType<QVector<RoomInfoItem>>();
qDBusRegisterMetaType<QImage>(); qDBusRegisterMetaType<QImage>();
qDBusRegisterMetaType<QVersionNumber>();
} }
bool bool
apiVersionIsCompatible(const QVersionNumber &clientAppVersion) apiVersionIsCompatible(const QVersionNumber &clientAppVersion)
{ {
if (clientAppVersion.majorVersion() != nheko::dbus::apiVersion.majorVersion()) if (clientAppVersion.majorVersion() != nheko::dbus::dbusApiVersion.majorVersion())
return false; return false;
if (clientAppVersion.minorVersion() > nheko::dbus::apiVersion.minorVersion()) if (clientAppVersion.minorVersion() > nheko::dbus::dbusApiVersion.minorVersion())
return false; return false;
if (clientAppVersion.minorVersion() == nheko::dbus::apiVersion.minorVersion() && if (clientAppVersion.minorVersion() == nheko::dbus::dbusApiVersion.minorVersion() &&
clientAppVersion.microVersion() < nheko::dbus::apiVersion.microVersion()) clientAppVersion.microVersion() < nheko::dbus::dbusApiVersion.microVersion())
return false; return false;
return true; return true;
@ -87,6 +88,61 @@ operator>>(const QDBusArgument &arg, RoomInfoItem &item)
arg.endStructure(); arg.endStructure();
return arg; return arg;
} }
QString
apiVersion()
{
if (QDBusInterface interface{QStringLiteral(NHEKO_DBUS_SERVICE_NAME), QStringLiteral("/")};
interface.isValid())
return QDBusReply<QString>{interface.call(QStringLiteral("apiVersion"))}.value();
else
return {};
}
QString
nhekoVersion()
{
if (QDBusInterface interface{QStringLiteral(NHEKO_DBUS_SERVICE_NAME), QStringLiteral("/")};
interface.isValid())
return QDBusReply<QString>{interface.call(QStringLiteral("nhekoVersion"))}.value();
else
return {};
}
QVector<RoomInfoItem>
rooms()
{
if (QDBusInterface interface{QStringLiteral(NHEKO_DBUS_SERVICE_NAME), QStringLiteral("/")};
interface.isValid())
return QDBusReply<QVector<RoomInfoItem>>{interface.call(QStringLiteral("rooms"))}
.value();
else
return {};
}
void
activateRoom(const QString &alias)
{
if (QDBusInterface interface{QStringLiteral(NHEKO_DBUS_SERVICE_NAME), QStringLiteral("/")};
interface.isValid())
interface.call(QDBus::NoBlock, QStringLiteral("activateRoom"), alias);
}
void
joinRoom(const QString &alias)
{
if (QDBusInterface interface{QStringLiteral(NHEKO_DBUS_SERVICE_NAME), QStringLiteral("/")};
interface.isValid())
interface.call(QDBus::NoBlock, QStringLiteral("joinRoom"), alias);
}
void
directChat(const QString &userId)
{
if (QDBusInterface interface{QStringLiteral(NHEKO_DBUS_SERVICE_NAME), QStringLiteral("/")};
interface.isValid())
interface.call(QDBus::NoBlock, QStringLiteral("directChat"), userId);
}
} // nheko::dbus } // nheko::dbus
/** /**
@ -140,27 +196,12 @@ operator>>(const QDBusArgument &arg, QImage &image)
arg >> width >> height >> garbage >> garbage >> garbage >> garbage >> bits; arg >> width >> height >> garbage >> garbage >> garbage >> garbage >> bits;
arg.endStructure(); arg.endStructure();
image = QImage(reinterpret_cast<uchar *>(bits.data()), width, height, QImage::Format_RGBA8888); // Unfortunately, this copy-and-detach is necessary to ensure that the source buffer
// is copied properly. If anybody finds a better solution, please implement it.
auto temp =
QImage(reinterpret_cast<uchar *>(bits.data()), width, height, QImage::Format_RGBA8888);
image = temp;
image.detach();
return arg; return arg;
} }
QDBusArgument &
operator<<(QDBusArgument &arg, const QVersionNumber &v)
{
arg.beginStructure();
arg << v.toString();
arg.endStructure();
return arg;
}
const QDBusArgument &
operator>>(const QDBusArgument &arg, QVersionNumber &v)
{
arg.beginStructure();
QString temp;
arg >> temp;
v = QVersionNumber::fromString(temp);
arg.endStructure();
return arg;
}

View file

@ -18,7 +18,7 @@ init();
//! The nheko D-Bus API version provided by this file. The API version number follows semantic //! The nheko D-Bus API version provided by this file. The API version number follows semantic
//! versioning as defined by https://semver.org. //! versioning as defined by https://semver.org.
const QVersionNumber apiVersion{0, 0, 1}; const QVersionNumber dbusApiVersion{0, 0, 1};
//! Compare the installed Nheko API to the version that your client app targets to see if they //! Compare the installed Nheko API to the version that your client app targets to see if they
//! are compatible. //! are compatible.
@ -58,6 +58,26 @@ private:
int unreadNotifications_; int unreadNotifications_;
}; };
//! Get the nheko D-Bus API version.
QString
apiVersion();
//! Get the nheko version.
QString
nhekoVersion();
//! Call this function to get a list of all joined rooms.
QVector<RoomInfoItem>
rooms();
//! Activates a currently joined room.
void
activateRoom(const QString &alias);
//! Joins a room. It is your responsibility to ask for confirmation (if desired).
void
joinRoom(const QString &alias);
//! Starts or activates a direct chat. It is your responsibility to ask for confirmation (if
//! desired).
void
directChat(const QString &userId);
QDBusArgument & QDBusArgument &
operator<<(QDBusArgument &arg, const RoomInfoItem &item); operator<<(QDBusArgument &arg, const RoomInfoItem &item);
const QDBusArgument & const QDBusArgument &
@ -70,11 +90,6 @@ operator<<(QDBusArgument &arg, const QImage &image);
const QDBusArgument & const QDBusArgument &
operator>>(const QDBusArgument &arg, QImage &); operator>>(const QDBusArgument &arg, QImage &);
QDBusArgument &
operator<<(QDBusArgument &arg, const QVersionNumber &v);
const QDBusArgument &
operator>>(const QDBusArgument &arg, QVersionNumber &v);
#define NHEKO_DBUS_SERVICE_NAME "im.nheko.Nheko" #define NHEKO_DBUS_SERVICE_NAME "im.nheko.Nheko"
#endif // NHEKODBUSAPI_H #endif // NHEKODBUSAPI_H

View file

@ -19,7 +19,7 @@ NhekoDBusBackend::NhekoDBusBackend(RoomlistModel *parent)
{} {}
QVector<nheko::dbus::RoomInfoItem> QVector<nheko::dbus::RoomInfoItem>
NhekoDBusBackend::getRooms(const QDBusMessage &message) NhekoDBusBackend::rooms(const QDBusMessage &message)
{ {
const auto roomListModel = m_parent->models; const auto roomListModel = m_parent->models;
QSharedPointer<QVector<nheko::dbus::RoomInfoItem>> model{ QSharedPointer<QVector<nheko::dbus::RoomInfoItem>> model{
@ -39,7 +39,7 @@ NhekoDBusBackend::getRooms(const QDBusMessage &message)
} }
model->push_back(nheko::dbus::RoomInfoItem{ model->push_back(nheko::dbus::RoomInfoItem{
room->roomId(), room->roomName(), alias, image, room->notificationCount()}); room->roomId(), alias, room->roomName(), image, room->notificationCount()});
if (model->length() == roomListModelSize) { if (model->length() == roomListModelSize) {
auto reply = message.createReply(); auto reply = message.createReply();
@ -81,7 +81,7 @@ NhekoDBusBackend::joinRoom(const QString &alias) const
} }
void void
NhekoDBusBackend::startDirectChat(const QString &userId) const NhekoDBusBackend::directChat(const QString &userId) const
{ {
bringWindowToTop(); bringWindowToTop();
ChatPage::instance()->startChat(userId); ChatPage::instance()->startChat(userId);

View file

@ -23,18 +23,18 @@ public:
public slots: public slots:
//! Get the nheko D-Bus API version. //! Get the nheko D-Bus API version.
Q_SCRIPTABLE QVersionNumber apiVersion() const { return nheko::dbus::apiVersion; } Q_SCRIPTABLE QString apiVersion() const { return nheko::dbus::dbusApiVersion.toString(); }
//! Get the nheko version. //! Get the nheko version.
Q_SCRIPTABLE QString nhekoVersionString() const { return nheko::version; } Q_SCRIPTABLE QString nhekoVersion() const { return nheko::version; }
//! Call this function to get a list of all joined rooms. //! Call this function to get a list of all joined rooms.
Q_SCRIPTABLE QVector<nheko::dbus::RoomInfoItem> getRooms(const QDBusMessage &message); Q_SCRIPTABLE QVector<nheko::dbus::RoomInfoItem> rooms(const QDBusMessage &message);
//! Activates a currently joined room. //! Activates a currently joined room.
Q_SCRIPTABLE void activateRoom(const QString &alias) const; Q_SCRIPTABLE void activateRoom(const QString &alias) const;
//! Joins a room. It is your responsibility to ask for confirmation (if desired). //! Joins a room. It is your responsibility to ask for confirmation (if desired).
Q_SCRIPTABLE void joinRoom(const QString &alias) const; Q_SCRIPTABLE void joinRoom(const QString &alias) const;
//! Starts or activates a direct chat. It is your responsibility to ask for confirmation (if //! Starts or activates a direct chat. It is your responsibility to ask for confirmation (if
//! desired). //! desired).
Q_SCRIPTABLE void startDirectChat(const QString &userId) const; Q_SCRIPTABLE void directChat(const QString &userId) const;
private: private:
void bringWindowToTop() const; void bringWindowToTop() const;