summary refs log tree commit diff
path: root/src/dbus
diff options
context:
space:
mode:
authorLoren Burkholder <55629213+LorenDB@users.noreply.github.com>2022-04-14 11:02:55 -0400
committerGitHub <noreply@github.com>2022-04-14 15:02:55 +0000
commit686ebfdbecbcacdbdba6c6289f22479c2fe5133e (patch)
treeef4124f0ad74d78612555160bfd556a8e98d068b /src/dbus
parentTranslated using Weblate (Finnish) (diff)
downloadnheko-686ebfdbecbcacdbdba6c6289f22479c2fe5133e.tar.xz
Add D-Bus API (#916)
This adds functionality for viewing joined rooms and activating rooms.
Diffstat (limited to 'src/dbus')
-rw-r--r--src/dbus/NhekoDBusApi.cpp166
-rw-r--r--src/dbus/NhekoDBusApi.h80
-rw-r--r--src/dbus/NhekoDBusBackend.cpp87
-rw-r--r--src/dbus/NhekoDBusBackend.h45
4 files changed, 378 insertions, 0 deletions
diff --git a/src/dbus/NhekoDBusApi.cpp b/src/dbus/NhekoDBusApi.cpp
new file mode 100644

index 00000000..edc3fa8a --- /dev/null +++ b/src/dbus/NhekoDBusApi.cpp
@@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: 2010 David Sansome <me@davidsansome.com> +// SPDX-FileCopyrightText: 2022 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "NhekoDBusApi.h" + +#include <QDBusMetaType> + +namespace nheko::dbus { +void +init() +{ + qDBusRegisterMetaType<RoomInfoItem>(); + qDBusRegisterMetaType<QVector<RoomInfoItem>>(); + qDBusRegisterMetaType<QImage>(); + qDBusRegisterMetaType<QVersionNumber>(); +} + +bool +apiVersionIsCompatible(const QVersionNumber &clientAppVersion) +{ + if (clientAppVersion.majorVersion() != nheko::dbus::apiVersion.majorVersion()) + return false; + if (clientAppVersion.minorVersion() > nheko::dbus::apiVersion.minorVersion()) + return false; + if (clientAppVersion.minorVersion() == nheko::dbus::apiVersion.minorVersion() && + clientAppVersion.microVersion() < nheko::dbus::apiVersion.microVersion()) + return false; + + return true; +} + +RoomInfoItem::RoomInfoItem(const QString &roomId, + const QString &alias, + const QString &title, + const QImage &image, + const int unreadNotifications, + QObject *parent) + : QObject{parent} + , roomId_{roomId} + , alias_{alias} + , roomName_{title} + , image_{image} + , unreadNotifications_{unreadNotifications} +{} + +RoomInfoItem::RoomInfoItem(const RoomInfoItem &other) + : QObject{other.parent()} + , roomId_{other.roomId_} + , alias_{other.alias_} + , roomName_{other.roomName_} + , image_{other.image_} + , unreadNotifications_{other.unreadNotifications_} +{} + +RoomInfoItem & +RoomInfoItem::operator=(const RoomInfoItem &other) +{ + roomId_ = other.roomId_; + alias_ = other.alias_; + roomName_ = other.roomName_; + image_ = other.image_; + unreadNotifications_ = other.unreadNotifications_; + return *this; +} + +QDBusArgument & +operator<<(QDBusArgument &arg, const RoomInfoItem &item) +{ + arg.beginStructure(); + arg << item.roomId_ << item.alias_ << item.roomName_ << item.image_ + << item.unreadNotifications_; + arg.endStructure(); + return arg; +} + +const QDBusArgument & +operator>>(const QDBusArgument &arg, RoomInfoItem &item) +{ + arg.beginStructure(); + arg >> item.roomId_ >> item.alias_ >> item.roomName_ >> item.image_ >> + item.unreadNotifications_; + if (item.image_.isNull()) + item.image_ = QImage{QStringLiteral(":/icons/ui/speech-bubbles.svg")}; + + arg.endStructure(); + return arg; +} +} // nheko::dbus + +/** + * Automatic marshaling of a QImage for org.freedesktop.Notifications.Notify + * + * This function is heavily based on a function from the Clementine project (see + * http://www.clementine-player.org) and licensed under the GNU General Public + * License, version 3 or later. + * + * SPDX-FileCopyrightText: 2010 David Sansome <me@davidsansome.com> + */ +QDBusArgument & +operator<<(QDBusArgument &arg, const QImage &image) +{ + if (image.isNull()) { + arg.beginStructure(); + arg << 0 << 0 << 0 << false << 0 << 0 << QByteArray(); + arg.endStructure(); + return arg; + } + + QImage i = image.height() > 100 || image.width() > 100 + ? image.scaledToHeight(100, Qt::SmoothTransformation) + : image; + i = std::move(i).convertToFormat(QImage::Format_RGBA8888); + + arg.beginStructure(); + arg << i.width(); + arg << i.height(); + arg << i.bytesPerLine(); + arg << i.hasAlphaChannel(); + int channels = i.isGrayscale() ? 1 : (i.hasAlphaChannel() ? 4 : 3); + arg << i.depth() / channels; + arg << channels; + arg << QByteArray(reinterpret_cast<const char *>(i.bits()), i.sizeInBytes()); + arg.endStructure(); + + return arg; +} + +// This function, however, was merely reverse-engineered from the above function +// and is not from the Clementine project. +const QDBusArgument & +operator>>(const QDBusArgument &arg, QImage &image) +{ + // garbage is used as a sort of /dev/null + int width, height, garbage; + QByteArray bits; + + arg.beginStructure(); + arg >> width >> height >> garbage >> garbage >> garbage >> garbage >> bits; + arg.endStructure(); + + image = QImage(reinterpret_cast<uchar *>(bits.data()), width, height, QImage::Format_RGBA8888); + + 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; +} diff --git a/src/dbus/NhekoDBusApi.h b/src/dbus/NhekoDBusApi.h new file mode 100644
index 00000000..47cc108a --- /dev/null +++ b/src/dbus/NhekoDBusApi.h
@@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: 2022 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NHEKODBUSAPI_H +#define NHEKODBUSAPI_H + +#include <QDBusArgument> +#include <QIcon> +#include <QObject> +#include <QVersionNumber> + +namespace nheko::dbus { + +//! Registers all necessary classes with D-Bus. Call this before using any nheko D-Bus classes. +void +init(); + +//! The nheko D-Bus API version provided by this file. The API version number follows semantic +//! versioning as defined by https://semver.org. +const QVersionNumber apiVersion{0, 0, 1}; + +//! Compare the installed Nheko API to the version that your client app targets to see if they +//! are compatible. +bool +apiVersionIsCompatible(const QVersionNumber &clientAppVersion); + +class RoomInfoItem : public QObject +{ + Q_OBJECT + +public: + RoomInfoItem(const QString &roomId = QString{}, + const QString &alias = QString{}, + const QString &title = QString{}, + const QImage &image = QImage{}, + const int unreadNotifications = 0, + QObject *parent = nullptr); + + RoomInfoItem(const RoomInfoItem &other); + + const QString &roomId() const { return roomId_; } + const QString &alias() const { return alias_; } + const QString &roomName() const { return roomName_; } + const QImage &image() const { return image_; } + int unreadNotifications() const { return unreadNotifications_; } + + RoomInfoItem &operator=(const RoomInfoItem &other); + friend QDBusArgument &operator<<(QDBusArgument &arg, const nheko::dbus::RoomInfoItem &item); + friend const QDBusArgument & + operator>>(const QDBusArgument &arg, nheko::dbus::RoomInfoItem &item); + +private: + QString roomId_; + QString alias_; + QString roomName_; + QImage image_; + int unreadNotifications_; +}; + +QDBusArgument & +operator<<(QDBusArgument &arg, const RoomInfoItem &item); +const QDBusArgument & +operator>>(const QDBusArgument &arg, RoomInfoItem &item); +} // nheko::dbus +Q_DECLARE_METATYPE(nheko::dbus::RoomInfoItem) + +QDBusArgument & +operator<<(QDBusArgument &arg, const QImage &image); +const QDBusArgument & +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 "io.github.Nheko-Reborn.nheko" + +#endif // NHEKODBUSAPI_H diff --git a/src/dbus/NhekoDBusBackend.cpp b/src/dbus/NhekoDBusBackend.cpp new file mode 100644
index 00000000..3645aea6 --- /dev/null +++ b/src/dbus/NhekoDBusBackend.cpp
@@ -0,0 +1,87 @@ +// SPDX-FileCopyrightText: 2022 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "NhekoDBusBackend.h" + +#include "Cache_p.h" +#include "ChatPage.h" +#include "Logging.h" +#include "MainWindow.h" +#include "MxcImageProvider.h" +#include "timeline/RoomlistModel.h" + +#include <QDBusConnection> + +NhekoDBusBackend::NhekoDBusBackend(RoomlistModel *parent) + : QObject{parent} + , m_parent{parent} +{} + +QVector<nheko::dbus::RoomInfoItem> +NhekoDBusBackend::getRooms(const QDBusMessage &message) +{ + const auto roomListModel = m_parent->models; + QSharedPointer<QVector<nheko::dbus::RoomInfoItem>> model{ + new QVector<nheko::dbus::RoomInfoItem>}; + + for (const auto &room : roomListModel) { + MainWindow::instance()->imageProvider()->download( + room->roomAvatarUrl().remove("mxc://"), + {96, 96}, + [message, room, model, roomListModel]( + const QString &, const QSize &, const QImage &image, const QString &) { + const auto aliases = cache::client()->getRoomAliases(room->roomId().toStdString()); + QString alias; + if (aliases.has_value()) { + const auto &val = aliases.value(); + if (!val.alias.empty()) + alias = QString::fromStdString(val.alias); + else if (val.alt_aliases.size() > 0) + alias = QString::fromStdString(val.alt_aliases.front()); + } + + model->push_back(nheko::dbus::RoomInfoItem{ + room->roomId(), room->roomName(), alias, image, room->notificationCount()}); + + if (model->length() == roomListModel.size()) { + auto reply = message.createReply(); + nhlog::ui()->debug("Sending {} rooms over D-Bus...", model->size()); + reply << QVariant::fromValue(*model); + QDBusConnection::sessionBus().send(reply); + nhlog::ui()->debug("Rooms successfully sent to D-Bus."); + } + }, + true); + } + + return {}; +} + +void +NhekoDBusBackend::activateRoom(const QString &alias) const +{ + bringWindowToTop(); + m_parent->setCurrentRoom(alias); +} + +void +NhekoDBusBackend::joinRoom(const QString &alias) const +{ + bringWindowToTop(); + ChatPage::instance()->joinRoom(alias); +} + +void +NhekoDBusBackend::startDirectChat(const QString &userId) const +{ + bringWindowToTop(); + ChatPage::instance()->startChat(userId); +} + +void +NhekoDBusBackend::bringWindowToTop() const +{ + MainWindow::instance()->show(); + MainWindow::instance()->raise(); +} diff --git a/src/dbus/NhekoDBusBackend.h b/src/dbus/NhekoDBusBackend.h new file mode 100644
index 00000000..02fd87d5 --- /dev/null +++ b/src/dbus/NhekoDBusBackend.h
@@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2022 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NHEKODBUSBACKEND_H +#define NHEKODBUSBACKEND_H + +#include <QDBusMessage> +#include <QObject> + +#include "NhekoDBusApi.h" +#include "config/nheko.h" + +class RoomlistModel; + +class NhekoDBusBackend : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "im.nheko.Nheko") + +public: + NhekoDBusBackend(RoomlistModel *parent); + +public slots: + //! Get the nheko D-Bus API version. + Q_SCRIPTABLE QVersionNumber apiVersion() const { return nheko::dbus::apiVersion; } + //! Get the nheko version. + Q_SCRIPTABLE QString nhekoVersionString() const { return nheko::version; } + //! Call this function to get a list of all joined rooms. + Q_SCRIPTABLE QVector<nheko::dbus::RoomInfoItem> getRooms(const QDBusMessage &message); + //! Activates a currently joined room. + Q_SCRIPTABLE void activateRoom(const QString &alias) const; + //! Joins a room. It is your responsibility to ask for confirmation (if desired). + Q_SCRIPTABLE void joinRoom(const QString &alias) const; + //! Starts or activates a direct chat. It is your responsibility to ask for confirmation (if + //! desired). + Q_SCRIPTABLE void startDirectChat(const QString &userId) const; + +private: + void bringWindowToTop() const; + + RoomlistModel *m_parent; +}; + +#endif // NHEKODBUSBACKEND_H