mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-21 18:50:47 +03:00
Add D-Bus API (#916)
This adds functionality for viewing joined rooms and activating rooms.
This commit is contained in:
parent
3329b7aab2
commit
686ebfdbec
15 changed files with 508 additions and 54 deletions
|
@ -303,7 +303,6 @@ check_symbol_exists(backtrace_symbols_fd "execinfo.h" HAVE_BACKTRACE_SYMBOLS_FD)
|
|||
|
||||
configure_file(cmake/nheko.h config/nheko.h)
|
||||
|
||||
|
||||
#
|
||||
# Declare source and header files.
|
||||
#
|
||||
|
@ -501,6 +500,11 @@ add_subdirectory(third_party/SingleApplication-3.3.2/)
|
|||
|
||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
# this must be defined here to make the moc work properly
|
||||
if (NOT APPLE AND NOT WIN32)
|
||||
add_compile_definitions(NHEKO_DBUS_SYS)
|
||||
endif()
|
||||
|
||||
qt5_wrap_cpp(MOC_HEADERS
|
||||
# Dialogs
|
||||
src/dialogs/FallbackAuth.h
|
||||
|
@ -599,7 +603,15 @@ elseif (WIN32)
|
|||
|
||||
set(SRC_FILES ${SRC_FILES} src/notifications/ManagerWin.cpp src/wintoastlib.cpp)
|
||||
else ()
|
||||
set(SRC_FILES ${SRC_FILES} src/notifications/ManagerLinux.cpp)
|
||||
set(SRC_FILES ${SRC_FILES}
|
||||
src/dbus/NhekoDBusApi.cpp
|
||||
src/dbus/NhekoDBusBackend.cpp
|
||||
src/notifications/ManagerLinux.cpp
|
||||
)
|
||||
qt5_wrap_cpp(MOC_HEADERS
|
||||
src/dbus/NhekoDBusApi.h
|
||||
src/dbus/NhekoDBusBackend.h
|
||||
)
|
||||
endif ()
|
||||
|
||||
set(NHEKO_DEPS
|
||||
|
|
14
README.md
14
README.md
|
@ -43,6 +43,7 @@ Specifically there is support for:
|
|||
- Room switcher (ctrl-K).
|
||||
- Light, Dark & System themes.
|
||||
- Creating separate profiles (command line only, use `-p name`).
|
||||
- D-Bus API to allow integration with third-party plugins (does not support Windows or macOS).
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -156,6 +157,12 @@ with [Chocolatey](https://chocolatey.org/):
|
|||
choco install nheko-reborn
|
||||
```
|
||||
|
||||
#### D-Bus plugins
|
||||
|
||||
nheko does not provide binaries for any D-Bus plugins. However, we do provide the following list of known plugins:
|
||||
|
||||
- [nheko-krunner](https://github.com/LorenDB/nheko-krunner)
|
||||
|
||||
### FAQ
|
||||
|
||||
---
|
||||
|
@ -409,7 +416,12 @@ Also copy the respective cmark.dll to the binary dir from `build/cmark-build/src
|
|||
|
||||
### Contributing
|
||||
|
||||
See [CONTRIBUTING](.github/CONTRIBUTING.md)
|
||||
See [CONTRIBUTING](.github/CONTRIBUTING.md).
|
||||
|
||||
### Using the D-Bus API
|
||||
|
||||
Currently, there is no documentation for the D-Bus API, so if you'd like to make use of it, come ask
|
||||
for support in [#nheko:nheko.im](https://matrix.to/#/#nheko:nheko.im).
|
||||
|
||||
### Screens
|
||||
|
||||
|
|
|
@ -54,6 +54,10 @@
|
|||
#include "ui/UIA.h"
|
||||
#include "voip/WebRTCSession.h"
|
||||
|
||||
#ifdef NHEKO_DBUS_SYS
|
||||
#include "dbus/NhekoDBusApi.h"
|
||||
#endif
|
||||
|
||||
Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents)
|
||||
Q_DECLARE_METATYPE(std::vector<DeviceInfo>)
|
||||
Q_DECLARE_METATYPE(std::vector<mtx::responses::PublicRoomsChunk>)
|
||||
|
@ -282,6 +286,18 @@ MainWindow::registerQmlTypes()
|
|||
engine()->addImageProvider(QStringLiteral("jdenticon"), new JdenticonProvider());
|
||||
|
||||
QObject::connect(engine(), &QQmlEngine::quit, &QGuiApplication::quit);
|
||||
|
||||
#ifdef NHEKO_DBUS_SYS
|
||||
if (UserSettings::instance()->exposeDBusApi()) {
|
||||
if (QDBusConnection::sessionBus().isConnected() &&
|
||||
QDBusConnection::sessionBus().registerService(NHEKO_DBUS_SERVICE_NAME)) {
|
||||
nheko::dbus::init();
|
||||
nhlog::ui()->info("Initialized D-Bus");
|
||||
dbusAvailable_ = true;
|
||||
} else
|
||||
nhlog::ui()->warn("Could not connect to D-Bus!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -54,6 +54,10 @@ public:
|
|||
//! Show the chat page and start communicating with the given access token.
|
||||
void showChatPage();
|
||||
|
||||
#ifdef NHEKO_DBUS_SYS
|
||||
bool dbusAvailable() const { return dbusAvailable_; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event);
|
||||
bool event(QEvent *event) override;
|
||||
|
@ -96,4 +100,8 @@ private:
|
|||
TrayIcon *trayIcon_;
|
||||
|
||||
MxcImageProvider *imgProvider = nullptr;
|
||||
|
||||
#ifdef NHEKO_DBUS_SYS
|
||||
bool dbusAvailable_{false};
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <mtxclient/crypto/client.hpp>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QCache>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QPainter>
|
||||
|
@ -103,6 +104,12 @@ MxcImageProvider::download(const QString &id,
|
|||
bool crop,
|
||||
double radius)
|
||||
{
|
||||
if (id.isEmpty()) {
|
||||
nhlog::net()->warn("Attempted to download image with empty ID");
|
||||
then(id, QSize{}, QImage{}, QString{});
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<mtx::crypto::EncryptedFile> encryptionInfo;
|
||||
auto temp = infos.find("mxc://" + id);
|
||||
if (temp != infos.end())
|
||||
|
@ -264,6 +271,7 @@ MxcImageProvider::download(const QString &id,
|
|||
image.setText(QStringLiteral("original filename"),
|
||||
QString::fromStdString(originalFilename));
|
||||
image.setText(QStringLiteral("mxc url"), "mxc://" + id);
|
||||
|
||||
then(id, requestedSize, image, fileInfo.absoluteFilePath());
|
||||
return;
|
||||
}
|
||||
|
@ -276,6 +284,7 @@ MxcImageProvider::download(const QString &id,
|
|||
image.setText(QStringLiteral("original filename"),
|
||||
QString::fromStdString(originalFilename));
|
||||
image.setText(QStringLiteral("mxc url"), "mxc://" + id);
|
||||
|
||||
then(id, requestedSize, image, fileInfo.absoluteFilePath());
|
||||
});
|
||||
} catch (std::exception &e) {
|
||||
|
|
|
@ -90,6 +90,8 @@ UserSettings::load(std::optional<QString> profile)
|
|||
privacyScreen_ = settings.value(QStringLiteral("user/privacy_screen"), false).toBool();
|
||||
privacyScreenTimeout_ =
|
||||
settings.value(QStringLiteral("user/privacy_screen_timeout"), 0).toInt();
|
||||
exposeDBusApi_ = settings.value(QStringLiteral("user/expose_dbus_api"), false).toBool();
|
||||
|
||||
mobileMode_ = settings.value(QStringLiteral("user/mobile_mode"), false).toBool();
|
||||
emojiFont_ = settings.value(QStringLiteral("user/emoji_font_family"), "emoji").toString();
|
||||
baseFontSize_ =
|
||||
|
@ -247,6 +249,17 @@ UserSettings::setCollapsedSpaces(QList<QStringList> spaces)
|
|||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setExposeDBusApi(bool state)
|
||||
{
|
||||
if (exposeDBusApi_ == state)
|
||||
return;
|
||||
|
||||
exposeDBusApi_ = state;
|
||||
emit exposeDBusApiChanged(state);
|
||||
save();
|
||||
}
|
||||
|
||||
void
|
||||
UserSettings::setMarkdown(bool state)
|
||||
{
|
||||
|
@ -788,6 +801,7 @@ UserSettings::save()
|
|||
settings.setValue(QStringLiteral("use_identicon"), useIdenticon_);
|
||||
settings.setValue(QStringLiteral("open_image_external"), openImageExternal_);
|
||||
settings.setValue(QStringLiteral("open_video_external"), openVideoExternal_);
|
||||
settings.setValue(QStringLiteral("expose_dbus_api"), exposeDBusApi_);
|
||||
|
||||
settings.endGroup(); // user
|
||||
|
||||
|
@ -972,6 +986,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
|
|||
return tr("User signing key");
|
||||
case MasterKey:
|
||||
return tr("Master signing key");
|
||||
case ExposeDBusApi:
|
||||
return tr("Expose room information via D-Bus");
|
||||
}
|
||||
} else if (role == Value) {
|
||||
switch (index.row()) {
|
||||
|
@ -1091,6 +1107,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
|
|||
.has_value();
|
||||
case MasterKey:
|
||||
return cache::secret(mtx::secret_storage::secrets::cross_signing_master).has_value();
|
||||
case ExposeDBusApi:
|
||||
return i->exposeDBusApi();
|
||||
}
|
||||
} else if (role == Description) {
|
||||
switch (index.row()) {
|
||||
|
@ -1235,6 +1253,12 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
|
|||
"Your most important key. You don't need to have it cached, since not caching "
|
||||
"it makes it less likely it can be stolen and it is only needed to rotate your "
|
||||
"other signing keys.");
|
||||
case ExposeDBusApi:
|
||||
return tr("Allow third-party plugins and applications to load information about rooms "
|
||||
"you are in via D-Bus. "
|
||||
"This can have useful applications, but it also could be used for nefarious "
|
||||
"purposes. Enable at your own risk.\n\n"
|
||||
"This setting will take effect upon restart.");
|
||||
}
|
||||
} else if (role == Type) {
|
||||
switch (index.row()) {
|
||||
|
@ -1279,6 +1303,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
|
|||
case OnlyShareKeysWithVerifiedUsers:
|
||||
case ShareKeysWithTrustedUsers:
|
||||
case UseOnlineKeyBackup:
|
||||
case ExposeDBusApi:
|
||||
return Toggle;
|
||||
case Profile:
|
||||
case UserId:
|
||||
|
@ -1711,6 +1736,13 @@ UserSettingsModel::setData(const QModelIndex &index, const QVariant &value, int
|
|||
} else
|
||||
return false;
|
||||
}
|
||||
case ExposeDBusApi: {
|
||||
if (value.userType() == QMetaType::Bool) {
|
||||
i->setExposeDBusApi(value.toBool());
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -1940,4 +1972,7 @@ UserSettingsModel::UserSettingsModel(QObject *p)
|
|||
connect(MainWindow::instance(), &MainWindow::secretsChanged, this, [this]() {
|
||||
emit dataChanged(index(OnlineBackupKey), index(MasterKey), {Value, Good});
|
||||
});
|
||||
connect(s.get(), &UserSettings::exposeDBusApiChanged, this, [this] {
|
||||
emit dataChanged(index(ExposeDBusApi), index(ExposeDBusApi), {Value});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -115,6 +115,8 @@ class UserSettings : public QObject
|
|||
recentReactionsChanged)
|
||||
Q_PROPERTY(QStringList hiddenWidgets READ hiddenWidgets WRITE setHiddenWidgets NOTIFY
|
||||
hiddenWidgetsChanged)
|
||||
Q_PROPERTY(
|
||||
bool exposeDBusApi READ exposeDBusApi WRITE setExposeDBusApi NOTIFY exposeDBusApiChanged)
|
||||
|
||||
UserSettings();
|
||||
|
||||
|
@ -191,6 +193,7 @@ public:
|
|||
void setOpenImageExternal(bool state);
|
||||
void setOpenVideoExternal(bool state);
|
||||
void setCollapsedSpaces(QList<QStringList> spaces);
|
||||
void setExposeDBusApi(bool state);
|
||||
|
||||
QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; }
|
||||
bool messageHoverHighlight() const { return messageHoverHighlight_; }
|
||||
|
@ -255,6 +258,7 @@ public:
|
|||
bool openImageExternal() const { return openImageExternal_; }
|
||||
bool openVideoExternal() const { return openVideoExternal_; }
|
||||
QList<QStringList> collapsedSpaces() const { return collapsedSpaces_; }
|
||||
bool exposeDBusApi() const { return exposeDBusApi_; }
|
||||
|
||||
signals:
|
||||
void groupViewStateChanged(bool state);
|
||||
|
@ -310,6 +314,7 @@ signals:
|
|||
void hiddenPinsChanged();
|
||||
void hiddenWidgetsChanged();
|
||||
void recentReactionsChanged();
|
||||
void exposeDBusApiChanged(bool state);
|
||||
|
||||
private:
|
||||
// Default to system theme if QT_QPA_PLATFORMTHEME var is set.
|
||||
|
@ -373,6 +378,7 @@ private:
|
|||
bool useIdenticon_;
|
||||
bool openImageExternal_;
|
||||
bool openVideoExternal_;
|
||||
bool exposeDBusApi_;
|
||||
|
||||
QSettings settings;
|
||||
|
||||
|
@ -398,6 +404,9 @@ class UserSettingsModel : public QAbstractListModel
|
|||
UseIdenticon,
|
||||
PrivacyScreen,
|
||||
PrivacyScreenTimeout,
|
||||
#ifdef NHEKO_DBUS_SYS
|
||||
ExposeDBusApi,
|
||||
#endif
|
||||
|
||||
TimelineSection,
|
||||
TimelineMaxWidth,
|
||||
|
@ -457,6 +466,9 @@ class UserSettingsModel : public QAbstractListModel
|
|||
AccessToken,
|
||||
#ifdef Q_OS_MAC
|
||||
ScaleFactor,
|
||||
#endif
|
||||
#ifndef NHEKO_DBUS_SYS
|
||||
ExposeDBusApi,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
166
src/dbus/NhekoDBusApi.cpp
Normal file
166
src/dbus/NhekoDBusApi.cpp
Normal file
|
@ -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;
|
||||
}
|
80
src/dbus/NhekoDBusApi.h
Normal file
80
src/dbus/NhekoDBusApi.h
Normal file
|
@ -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
|
87
src/dbus/NhekoDBusBackend.cpp
Normal file
87
src/dbus/NhekoDBusBackend.cpp
Normal file
|
@ -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();
|
||||
}
|
45
src/dbus/NhekoDBusBackend.h
Normal file
45
src/dbus/NhekoDBusBackend.h
Normal file
|
@ -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
|
|
@ -11,11 +11,6 @@
|
|||
|
||||
#include <mtx/responses/notifications.hpp>
|
||||
|
||||
// convenience definition
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_HAIKU)
|
||||
#define NHEKO_DBUS_SYS
|
||||
#endif
|
||||
|
||||
#if defined(NHEKO_DBUS_SYS)
|
||||
#include <QtDBus/QDBusArgument>
|
||||
#include <QtDBus/QDBusInterface>
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "MxcImageProvider.h"
|
||||
#include "UserSettingsPage.h"
|
||||
#include "Utils.h"
|
||||
#include "dbus/NhekoDBusApi.h"
|
||||
|
||||
NotificationsManager::NotificationsManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
|
@ -269,49 +270,3 @@ NotificationsManager::notificationClosed(uint id, uint reason)
|
|||
Q_UNUSED(reason);
|
||||
notificationIds.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatic marshaling of a QImage for org.freedesktop.Notifications.Notify
|
||||
*
|
||||
* This function is 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.hasAlphaChannel() ? 4 : 3;
|
||||
arg << i.depth() / channels;
|
||||
arg << channels;
|
||||
arg << QByteArray(reinterpret_cast<const char *>(i.bits()), i.sizeInBytes());
|
||||
arg.endStructure();
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
const QDBusArgument &
|
||||
operator>>(const QDBusArgument &arg, QImage &)
|
||||
{
|
||||
// This is needed to link but shouldn't be called.
|
||||
Q_ASSERT(0);
|
||||
return arg;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
#include "TimelineViewManager.h"
|
||||
#include "UserSettingsPage.h"
|
||||
|
||||
#ifdef NHEKO_DBUS_SYS
|
||||
#include <QDBusConnection>
|
||||
#endif
|
||||
|
||||
RoomlistModel::RoomlistModel(TimelineViewManager *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, manager(parent)
|
||||
|
@ -604,6 +608,15 @@ RoomlistModel::initializeRooms()
|
|||
nhlog::db()->info("Restored {} rooms from cache", rowCount());
|
||||
|
||||
endResetModel();
|
||||
|
||||
#ifdef NHEKO_DBUS_SYS
|
||||
if (MainWindow::instance()->dbusAvailable()) {
|
||||
dbusInterface_ = new NhekoDBusBackend{this};
|
||||
if (!QDBusConnection::sessionBus().registerObject(
|
||||
"/", dbusInterface_, QDBusConnection::ExportScriptableSlots))
|
||||
nhlog::ui()->warn("Failed to register rooms with D-Bus");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
#include "TimelineModel.h"
|
||||
|
||||
#ifdef NHEKO_DBUS_SYS
|
||||
#include "dbus/NhekoDBusBackend.h"
|
||||
#endif
|
||||
|
||||
class TimelineViewManager;
|
||||
|
||||
class RoomPreview
|
||||
|
@ -138,6 +142,11 @@ private:
|
|||
|
||||
std::map<QString, std::vector<QString>> directChatToUser;
|
||||
|
||||
#ifdef NHEKO_DBUS_SYS
|
||||
NhekoDBusBackend *dbusInterface_;
|
||||
friend class NhekoDBusBackend;
|
||||
#endif
|
||||
|
||||
friend class FilteredRoomlistModel;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue