mirror of https://github.com/Nheko-Reborn/nheko
Add D-Bus API (#916)
This adds functionality for viewing joined rooms and activating rooms.pull/1046/head
parent
3329b7aab2
commit
686ebfdbec
@ -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; |
||||
} |
@ -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
|
@ -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(); |
||||
} |
@ -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
|
Loading…
Reference in new issue