Qt Utilities: misc/dbusnotification.cpp Source File

Qt Utilities 6.20.0 Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models

Loading...
Searching...
No Matches
dbusnotification.cpp
Go to the documentation of this file.
1#include "./dbusnotification.h"
2#include "notificationsinterface.h"
3
4#include <QCoreApplication>
5#include <QDBusConnection>
6#include <QDBusPendingReply>
7#include <QImage>
8#include <QMutex>
9#include <QMutexLocker>
10
11#include <limits>
12#include <map>
13
14using namespace std;
15
16namespace QtUtilities {
17
39
41using IDType = uint;
42static QMutex pendingNotificationsMutex;
43static std::map<IDType, DBusNotification *> pendingNotifications;
44OrgFreedesktopNotificationsInterface *DBusNotification::s_dbusInterface = nullptr;
45constexpr auto initialId = std::numeric_limits<IDType>::min();
46constexpr auto pendingId = std::numeric_limits<IDType>::max();
47constexpr auto pendingId2 = pendingId - 1;
49
53 struct SwappedImage : public QImage {
54 SwappedImage(const QImage &image);
55};
56
57 inline SwappedImage::SwappedImage(const QImage &image)
58 : QImage(image.rgbSwapped())
59{
60}
61
67 struct NotificationImage : public QDBusArgument {
68 NotificationImage();
69 NotificationImage(SwappedImage image);
70 QImage toQImage() const;
71 QVariant toDBusArgument() const;
72 static NotificationImage fromDBusArgument(const QVariant &variant);
73
74 qint32 width;
75 qint32 height;
76 qint32 rowstride;
77 bool hasAlpha;
78 qint32 channels;
79 qint32 bitsPerSample;
80 QByteArray data;
81 bool isValid;
82
83private:
84 NotificationImage(const QImage &image);
85};
86
87QDBusArgument &operator<<(QDBusArgument &argument, const NotificationImage &img)
88{
89 argument.beginStructure();
90 argument << img.width << img.height << img.rowstride << img.hasAlpha << img.bitsPerSample << img.channels << img.data;
91 argument.endStructure();
92 return argument;
93}
94
95 const QDBusArgument &operator>>(const QDBusArgument &argument, NotificationImage &img)
96{
97 argument.beginStructure();
98 argument >> img.width >> img.height >> img.rowstride >> img.hasAlpha >> img.bitsPerSample >> img.channels >> img.data;
99 argument.endStructure();
100 return argument;
101}
102
103 inline NotificationImage::NotificationImage()
104 : isValid(false)
105{
106}
107
108 inline NotificationImage::NotificationImage(SwappedImage image)
109 : NotificationImage(static_cast<const QImage &>(image))
110{
111}
112
113inline NotificationImage::NotificationImage(const QImage &image)
114 : width(image.width())
115 , height(image.height())
116 , rowstride(static_cast<qint32>(image.bytesPerLine()))
117 , hasAlpha(image.hasAlphaChannel())
118 , channels(image.isGrayscale() ? 1
119 : hasAlpha ? 4
120 : 3)
121 , bitsPerSample(image.depth() / channels)
122#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
123 , data(reinterpret_cast<const char *>(image.bits()), static_cast<int>(image.sizeInBytes()))
124#else
125 , data(reinterpret_cast<const char *>(image.bits()), image.byteCount())
126#endif
127 , isValid(!image.isNull())
128{
129 if (isValid) {
130 // populate QDBusArgument structure
131 // note: Just use the operator overload which is required for qDBusMarshallHelper().
132 *this << *this;
133 }
134}
135
136 inline QImage NotificationImage::toQImage() const
137{
138 return isValid ? QImage(reinterpret_cast<const uchar *>(data.constData()), width, height, hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32)
139 .rgbSwapped()
140 : QImage();
141}
142
143 inline QVariant NotificationImage::toDBusArgument() const
144{
145 return QVariant::fromValue(*this);
146}
147
148 inline NotificationImage NotificationImage::fromDBusArgument(const QVariant &variant)
149{
150 return variant.canConvert<NotificationImage>() ? variant.value<NotificationImage>() : NotificationImage();
151}
152
153} // namespace QtUtilities
154
155Q_DECLARE_METATYPE(QtUtilities::NotificationImage);
156
157namespace QtUtilities {
158
162DBusNotification::DBusNotification(const QString &title, NotificationIcon icon, int timeout, QObject *parent)
163 : QObject(parent)
164 , m_id(initialId)
165 , m_watcher(nullptr)
166 , m_title(title)
167 , m_timeout(timeout)
168{
169 initInterface();
170 setIcon(icon);
171}
172
176 DBusNotification::DBusNotification(const QString &title, const QString &icon, int timeout, QObject *parent)
177 : QObject(parent)
178 , m_id(initialId)
179 , m_watcher(nullptr)
180 , m_title(title)
181 , m_icon(icon)
182 , m_timeout(timeout)
183{
184 initInterface();
185}
186
190void DBusNotification::initInterface()
191{
192 if (!s_dbusInterface) {
193 qDBusRegisterMetaType<NotificationImage>();
194 s_dbusInterface = new OrgFreedesktopNotificationsInterface(
195 QStringLiteral("org.freedesktop.Notifications"), QStringLiteral("/org/freedesktop/Notifications"), QDBusConnection::sessionBus());
196 connect(s_dbusInterface, &OrgFreedesktopNotificationsInterface::ActionInvoked, &DBusNotification::handleActionInvoked);
197 connect(s_dbusInterface, &OrgFreedesktopNotificationsInterface::NotificationClosed, &DBusNotification::handleNotificationClosed);
198 }
199}
200
204DBusNotification::~DBusNotification()
205{
206 {
207 QMutexLocker lock(&pendingNotificationsMutex);
208 auto i = pendingNotifications.find(m_id);
209 if (i != pendingNotifications.end()) {
210 pendingNotifications.erase(i);
211 }
212 }
213 hide();
214}
215
219 bool DBusNotification::isAvailable()
220{
221 initInterface();
222 return s_dbusInterface->isValid();
223}
224
228 void DBusNotification::setIcon(NotificationIcon icon)
229{
230 switch (icon) {
231 case NotificationIcon::Information:
232 m_icon = QStringLiteral("dialog-information");
233 break;
234 case NotificationIcon::Warning:
235 m_icon = QStringLiteral("dialog-warning");
236 break;
237 case NotificationIcon::Critical:
238 m_icon = QStringLiteral("dialog-critical");
239 break;
240 default:;
241 }
242}
243
248 const QImage DBusNotification::image() const
249{
250 return NotificationImage::fromDBusArgument(hint(QStringLiteral("image-data"), QStringLiteral("image_data"))).toQImage();
251}
252
259 void DBusNotification::setImage(const QImage &image)
260{
261 m_hints[QStringLiteral("image-data")] = NotificationImage(SwappedImage(image)).toDBusArgument();
262}
263
272 bool DBusNotification::isPending() const
273{
274 return m_id == pendingId || m_id == pendingId2;
275}
276
281 void DBusNotification::deleteOnCloseOrError()
282{
283 connect(this, &DBusNotification::closed, this, &DBusNotification::deleteLater);
284 connect(this, &DBusNotification::error, this, &DBusNotification::deleteLater);
285}
286
296 bool DBusNotification::show()
297{
298 if (isPending()) {
299 m_id = pendingId2;
300 return true;
301 }
302 if (!s_dbusInterface->isValid()) {
303 emit error();
304 return false;
305 }
306
307 delete m_watcher;
308 m_watcher
309 = new QDBusPendingCallWatcher(s_dbusInterface->Notify(m_applicationName.isEmpty() ? QCoreApplication::applicationName() : m_applicationName,
310 m_id, m_icon, m_title, m_msg, m_actions, m_hints, m_timeout),
311 this);
312 connect(m_watcher, &QDBusPendingCallWatcher::finished, this, &DBusNotification::handleNotifyResult);
313 m_id = pendingId;
314 return true;
315}
316
324 bool DBusNotification::show(const QString &message)
325{
326 m_msg = message;
327 return show();
328}
329
342 bool DBusNotification::update(const QString &line)
343{
344 if ((!isPending() && !isVisible()) || m_msg.isEmpty()) {
345 m_msg = line;
346 } else {
347 if (!m_msg.startsWith(QStringLiteral("•"))) {
348 m_msg.insert(0, QStringLiteral("• "));
349 }
350 m_msg.append(QStringLiteral("\n• "));
351 m_msg.append(line);
352 }
353 return show();
354}
355
356 bool DBusNotification::queryCapabilities(const std::function<void(Capabilities &&capabilities)> &callback)
357{
358 // ensure DBus-interface is initialized and valid
359 initInterface();
360 if (!s_dbusInterface->isValid()) {
361 return false;
362 }
363
364 // invoke GetCapabilities() and pass the return value to the callback when available
365 const auto *const newWatcher = new QDBusPendingCallWatcher(s_dbusInterface->GetCapabilities());
366 connect(newWatcher, &QDBusPendingCallWatcher::finished, [&callback](QDBusPendingCallWatcher *watcher) {
367 watcher->deleteLater();
368 const QDBusPendingReply<QStringList> returnValue(*watcher);
369 if (returnValue.isError()) {
370 callback(Capabilities());
371 } else {
372 callback(Capabilities(std::move(returnValue.value())));
373 }
374 });
375 return true;
376}
377
383 bool DBusNotification::hide()
384{
385 if (m_id) {
386 s_dbusInterface->CloseNotification(m_id);
387 return true;
388 }
389 return false;
390}
391
395void DBusNotification::handleNotifyResult(QDBusPendingCallWatcher *watcher)
396{
397 if (watcher != m_watcher) {
398 return;
399 }
400
401 watcher->deleteLater();
402 m_watcher = nullptr;
403
404 QDBusPendingReply<uint> returnValue = *watcher;
405 if (returnValue.isError()) {
406 m_id = initialId;
407 emit error();
408 return;
409 }
410
411 const auto needsUpdate = m_id == pendingId2;
412 {
413 QMutexLocker lock(&pendingNotificationsMutex);
414 pendingNotifications[m_id = returnValue.argumentAt<0>()] = this;
415 }
416 emit shown();
417
418 // update the notification again if show() was called before we've got the ID
419 if (needsUpdate) {
420 show();
421 }
422}
423
427void DBusNotification::handleNotificationClosed(IDType id, uint reason)
428{
429 QMutexLocker lock(&pendingNotificationsMutex);
430 auto i = pendingNotifications.find(id);
431 if (i != pendingNotifications.end()) {
432 DBusNotification *notification = i->second;
433 notification->m_id = initialId;
434 emit notification->closed(reason >= 1 && reason <= 3 ? static_cast<NotificationCloseReason>(reason) : NotificationCloseReason::Undefined);
435 pendingNotifications.erase(i);
436 }
437}
438
442void DBusNotification::handleActionInvoked(IDType id, const QString &action)
443{
444 QMutexLocker lock(&pendingNotificationsMutex);
445 auto i = pendingNotifications.find(id);
446 if (i != pendingNotifications.end()) {
447 DBusNotification *notification = i->second;
448 emit notification->actionInvoked(action);
449 // Plasma 5 also closes the notification but doesn't emit the
450 // NotificationClose signal
451 // -> just consider the notification closed
452 emit notification->closed(NotificationCloseReason::ActionInvoked);
453 notification->m_id = initialId;
454 pendingNotifications.erase(i);
455 // however, lxqt-notificationd does not close the notification
456 // -> close manually for consistent behaviour
457 s_dbusInterface->CloseNotification(i->first);
458 }
459}
460
466
475
482
491
497
507
512} // namespace QtUtilities
QtUtilities::DBusNotification::Capabilities
Definition dbusnotification.h:35
QtUtilities::DBusNotification::deleteOnCloseOrError
void deleteOnCloseOrError()
Makes the notification object delete itself when the notification has been closed or an error occurre...
Definition dbusnotification.cpp:281
QtUtilities::DBusNotification::message
QString message
Returns the assigned message.
Definition dbusnotification.h:26
QtUtilities::DBusNotification::update
bool update(const QString &line)
Updates the message and shows/updates the notification.
Definition dbusnotification.cpp:342
QtUtilities::DBusNotification::shown
void shown()
Emitted when the notification could be shown successful.
QtUtilities::DBusNotification::isVisible
bool isVisible() const
Returns whether the notification is (still) visible.
Definition dbusnotification.h:387
QtUtilities::DBusNotification::isPending
bool isPending() const
Returns whether the notification is about to be shown after calling show() or update() but has not be...
Definition dbusnotification.cpp:272
QtUtilities::DBusNotification::image
const QImage image() const
Returns the image.
Definition dbusnotification.cpp:248
QtUtilities::DBusNotification::setIcon
void setIcon(const QString &icon)
Sets the icon name.
Definition dbusnotification.h:252
QtUtilities::DBusNotification::queryCapabilities
static bool queryCapabilities(const std::function< void(Capabilities &&capabilities)> &callback)
Definition dbusnotification.cpp:356
QtUtilities::DBusNotification::icon
QString icon
Definition dbusnotification.h:27
QtUtilities::DBusNotification::DBusNotification
DBusNotification(const QString &title, NotificationIcon icon=NotificationIcon::Information, int timeout=10000, QObject *parent=nullptr)
Creates a new notification (which is not shown instantly).
Definition dbusnotification.cpp:162
QtUtilities::DBusNotification::isAvailable
static bool isAvailable()
Returns whether the notification D-Bus daemon is running.
Definition dbusnotification.cpp:219
QtUtilities::DBusNotification::closed
void closed(NotificationCloseReason reason)
Emitted when the notification has been closed.
QtUtilities::DBusNotification::error
void error()
Emitted when the notification couldn't be shown.
QtUtilities::DBusNotification::timeout
int timeout
Returns the number of milliseconds the notification will be visible after calling show().
Definition dbusnotification.h:28
QtUtilities::DBusNotification::hide
bool hide()
Hides the notification (if still visible).
Definition dbusnotification.cpp:383
QtUtilities::DBusNotification::hint
QVariant hint(const QString &name) const
Returns the hint with the specified name.
Definition dbusnotification.h:260
QtUtilities::DBusNotification::setImage
void setImage(const QImage &image)
Sets the image.
Definition dbusnotification.cpp:259
QtUtilities::DBusNotification::~DBusNotification
~DBusNotification() override
Closes the notification if still shown and delete the object.
Definition dbusnotification.cpp:204
QtUtilities::DBusNotification::title
QString title
Definition dbusnotification.h:25
QtUtilities::DBusNotification::show
bool show()
Shows the notification.
Definition dbusnotification.cpp:296
Q_DECLARE_METATYPE
Q_DECLARE_METATYPE(QtUtilities::NotificationImage)
dbusnotification.h
QtUtilities
!
Definition trylocker.h:8
QtUtilities::NotificationIcon
NotificationIcon
Definition dbusnotification.h:18
QtUtilities::NotificationIcon::Warning
@ Warning
Definition dbusnotification.h:18
QtUtilities::NotificationIcon::Critical
@ Critical
Definition dbusnotification.h:18
QtUtilities::NotificationIcon::Information
@ Information
Definition dbusnotification.h:18
QtUtilities::operator>>
const QDBusArgument & operator>>(const QDBusArgument &argument, NotificationImage &img)
Definition dbusnotification.cpp:95
QtUtilities::NotificationCloseReason
NotificationCloseReason
Definition dbusnotification.h:20
QtUtilities::NotificationCloseReason::ActionInvoked
@ ActionInvoked
Definition dbusnotification.h:20
QtUtilities::NotificationCloseReason::Undefined
@ Undefined
Definition dbusnotification.h:20
QtUtilities::operator<<
QDBusArgument & operator<<(QDBusArgument &argument, const NotificationImage &img)
Definition dbusnotification.cpp:87
QtUtilities::NotificationImage
The NotificationImage struct is a raw data image format.
Definition dbusnotification.cpp:67
QtUtilities::NotificationImage::channels
qint32 channels
Definition dbusnotification.cpp:78
QtUtilities::NotificationImage::isValid
bool isValid
Definition dbusnotification.cpp:81
QtUtilities::NotificationImage::hasAlpha
bool hasAlpha
Definition dbusnotification.cpp:77
QtUtilities::NotificationImage::height
qint32 height
Definition dbusnotification.cpp:75
QtUtilities::NotificationImage::toDBusArgument
QVariant toDBusArgument() const
Definition dbusnotification.cpp:143
QtUtilities::NotificationImage::rowstride
qint32 rowstride
Definition dbusnotification.cpp:76
QtUtilities::NotificationImage::bitsPerSample
qint32 bitsPerSample
Definition dbusnotification.cpp:79
QtUtilities::NotificationImage::data
QByteArray data
Definition dbusnotification.cpp:80
QtUtilities::NotificationImage::NotificationImage
NotificationImage()
Definition dbusnotification.cpp:103
QtUtilities::NotificationImage::width
qint32 width
Definition dbusnotification.cpp:74
QtUtilities::NotificationImage::toQImage
QImage toQImage() const
Definition dbusnotification.cpp:136
QtUtilities::NotificationImage::fromDBusArgument
static NotificationImage fromDBusArgument(const QVariant &variant)
Definition dbusnotification.cpp:148
QtUtilities::SwappedImage
The SwappedImage struct represents RGB-interved version of the image specified on construction.
Definition dbusnotification.cpp:53
QtUtilities::SwappedImage::SwappedImage
SwappedImage(const QImage &image)
Definition dbusnotification.cpp:57