Qt Utilities  6.0.6
Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models
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 <map>
12 
13 using namespace std;
14 
15 namespace QtUtilities {
16 
39 static QMutex pendingNotificationsMutex;
41 static std::map<uint, DBusNotification *> pendingNotifications;
42 OrgFreedesktopNotificationsInterface *DBusNotification::s_dbusInterface = nullptr;
44 
48 struct SwappedImage : public QImage {
49  SwappedImage(const QImage &image);
50 };
51 
52 inline SwappedImage::SwappedImage(const QImage &image)
53  : QImage(image.rgbSwapped())
54 {
55 }
56 
62 struct NotificationImage : public QDBusArgument {
65  QImage toQImage() const;
66  QVariant toDBusArgument() const;
67  static NotificationImage fromDBusArgument(const QVariant &variant);
68 
69  qint32 width;
70  qint32 height;
71  qint32 rowstride;
72  bool hasAlpha;
73  qint32 channels;
74  qint32 bitsPerSample;
75  QByteArray data;
76  bool isValid;
77 
78 private:
79  NotificationImage(const QImage &image);
80 };
81 
82 QDBusArgument &operator<<(QDBusArgument &argument, const NotificationImage &img)
83 {
84  argument.beginStructure();
85  argument << img.width << img.height << img.rowstride << img.hasAlpha << img.bitsPerSample << img.channels << img.data;
86  argument.endStructure();
87  return argument;
88 }
89 
90 const QDBusArgument &operator>>(const QDBusArgument &argument, NotificationImage &img)
91 {
92  argument.beginStructure();
93  argument >> img.width >> img.height >> img.rowstride >> img.hasAlpha >> img.bitsPerSample >> img.channels >> img.data;
94  argument.endStructure();
95  return argument;
96 }
97 
99  : isValid(false)
100 {
101 }
102 
104  : NotificationImage(static_cast<const QImage &>(image))
105 {
106 }
107 
108 inline NotificationImage::NotificationImage(const QImage &image)
109  : width(image.width())
110  , height(image.height())
111  , rowstride(image.bytesPerLine())
112  , hasAlpha(image.hasAlphaChannel())
113  , channels(image.isGrayscale() ? 1 : hasAlpha ? 4 : 3)
114  , bitsPerSample(image.depth() / channels)
115 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
116  , data(reinterpret_cast<const char *>(image.bits()), static_cast<int>(image.sizeInBytes()))
117 #else
118  , data(reinterpret_cast<const char *>(image.bits()), image.byteCount())
119 #endif
120  , isValid(!image.isNull())
121 {
122  if (isValid) {
123  // populate QDBusArgument structure
124  // note: Just use the operator overload which is required for qDBusMarshallHelper().
125  *this << *this;
126  }
127 }
128 
129 inline QImage NotificationImage::toQImage() const
130 {
131  return isValid ? QImage(reinterpret_cast<const uchar *>(data.constData()), width, height, hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32)
132  .rgbSwapped()
133  : QImage();
134 }
135 
136 inline QVariant NotificationImage::toDBusArgument() const
137 {
138  return QVariant::fromValue(*this);
139 }
140 
142 {
143  return variant.canConvert<NotificationImage>() ? variant.value<NotificationImage>() : NotificationImage();
144 }
145 
146 } // namespace QtUtilities
147 
149 
150 namespace QtUtilities {
151 
155 DBusNotification::DBusNotification(const QString &title, NotificationIcon icon, int timeout, QObject *parent)
156  : QObject(parent)
157  , m_id(0)
158  , m_watcher(nullptr)
159  , m_title(title)
160  , m_timeout(timeout)
161 {
162  initInterface();
163  setIcon(icon);
164 }
165 
169 DBusNotification::DBusNotification(const QString &title, const QString &icon, int timeout, QObject *parent)
170  : QObject(parent)
171  , m_id(0)
172  , m_watcher(nullptr)
173  , m_title(title)
174  , m_icon(icon)
175  , m_timeout(timeout)
176 {
177  initInterface();
178 }
179 
183 void DBusNotification::initInterface()
184 {
185  if (!s_dbusInterface) {
186  qDBusRegisterMetaType<NotificationImage>();
187  s_dbusInterface = new OrgFreedesktopNotificationsInterface(
188  QStringLiteral("org.freedesktop.Notifications"), QStringLiteral("/org/freedesktop/Notifications"), QDBusConnection::sessionBus());
189  connect(s_dbusInterface, &OrgFreedesktopNotificationsInterface::ActionInvoked, &DBusNotification::handleActionInvoked);
190  connect(s_dbusInterface, &OrgFreedesktopNotificationsInterface::NotificationClosed, &DBusNotification::handleNotificationClosed);
191  }
192 }
193 
198 {
199  {
200  QMutexLocker lock(&pendingNotificationsMutex);
201  auto i = pendingNotifications.find(m_id);
202  if (i != pendingNotifications.end()) {
203  pendingNotifications.erase(i);
204  }
205  }
206  hide();
207 }
208 
213 {
214  initInterface();
215  return s_dbusInterface->isValid();
216 }
217 
222 {
223  switch (icon) {
225  m_icon = QStringLiteral("dialog-information");
226  break;
228  m_icon = QStringLiteral("dialog-warning");
229  break;
231  m_icon = QStringLiteral("dialog-critical");
232  break;
233  default:;
234  }
235 }
236 
241 const QImage DBusNotification::image() const
242 {
243  return NotificationImage::fromDBusArgument(hint(QStringLiteral("image-data"), QStringLiteral("image_data"))).toQImage();
244 }
245 
252 void DBusNotification::setImage(const QImage &image)
253 {
254  m_hints[QStringLiteral("image-data")] = NotificationImage(SwappedImage(image)).toDBusArgument();
255 }
256 
262 {
263  connect(this, &DBusNotification::closed, this, &DBusNotification::deleteLater);
264  connect(this, &DBusNotification::error, this, &DBusNotification::deleteLater);
265 }
266 
275 {
276  if (!s_dbusInterface->isValid()) {
277  emit error();
278  return false;
279  }
280 
281  delete m_watcher;
282  m_watcher
283  = new QDBusPendingCallWatcher(s_dbusInterface->Notify(m_applicationName.isEmpty() ? QCoreApplication::applicationName() : m_applicationName,
284  m_id, m_icon, m_title, m_msg, m_actions, m_hints, m_timeout),
285  this);
286  connect(m_watcher, &QDBusPendingCallWatcher::finished, this, &DBusNotification::handleNotifyResult);
287  return true;
288 }
289 
297 bool DBusNotification::show(const QString &message)
298 {
299  m_msg = message;
300  return show();
301 }
302 
315 bool DBusNotification::update(const QString &line)
316 {
317  if (!isVisible() || m_msg.isEmpty()) {
318  m_msg = line;
319  } else {
320  if (!m_msg.startsWith(QStringLiteral("•"))) {
321  m_msg.insert(0, QStringLiteral("• "));
322  }
323  m_msg.append(QStringLiteral("\n• "));
324  m_msg.append(line);
325  }
326  return show();
327 }
328 
329 bool DBusNotification::queryCapabilities(const std::function<void(Capabilities &&capabilities)> &callback)
330 {
331  // ensure DBus-interface is initialized and valid
332  initInterface();
333  if (!s_dbusInterface->isValid()) {
334  return false;
335  }
336 
337  // invoke GetCapabilities() and pass the return value to the callback when available
338  const auto *const watcher = new QDBusPendingCallWatcher(s_dbusInterface->GetCapabilities());
339  connect(watcher, &QDBusPendingCallWatcher::finished, [&callback](QDBusPendingCallWatcher *watcher) {
340  watcher->deleteLater();
341  const QDBusPendingReply<QStringList> returnValue(*watcher);
342  if (returnValue.isError()) {
343  callback(Capabilities());
344  } else {
345  callback(Capabilities(move(returnValue.value())));
346  }
347  });
348  return true;
349 }
350 
357 {
358  if (m_id) {
359  s_dbusInterface->CloseNotification(m_id);
360  return true;
361  }
362  return false;
363 }
364 
368 void DBusNotification::handleNotifyResult(QDBusPendingCallWatcher *watcher)
369 {
370  if (watcher != m_watcher) {
371  return;
372  }
373 
374  watcher->deleteLater();
375  m_watcher = nullptr;
376 
377  QDBusPendingReply<uint> returnValue = *watcher;
378  if (returnValue.isError()) {
379  emit error();
380  return;
381  }
382 
383  {
384  QMutexLocker lock(&pendingNotificationsMutex);
385  pendingNotifications[m_id = returnValue.argumentAt<0>()] = this;
386  }
387  emit shown();
388 }
389 
393 void DBusNotification::handleNotificationClosed(uint id, uint reason)
394 {
395  QMutexLocker lock(&pendingNotificationsMutex);
396  auto i = pendingNotifications.find(id);
397  if (i != pendingNotifications.end()) {
398  DBusNotification *notification = i->second;
399  notification->m_id = 0;
400  emit notification->closed(reason >= 1 && reason <= 3 ? static_cast<NotificationCloseReason>(reason) : NotificationCloseReason::Undefined);
401  pendingNotifications.erase(i);
402  }
403 }
404 
408 void DBusNotification::handleActionInvoked(uint id, const QString &action)
409 {
410  QMutexLocker lock(&pendingNotificationsMutex);
411  auto i = pendingNotifications.find(id);
412  if (i != pendingNotifications.end()) {
413  DBusNotification *notification = i->second;
414  emit notification->actionInvoked(action);
415  // Plasma 5 also closes the notification but doesn't emit the
416  // NotificationClose signal
417  // -> just consider the notification closed
418  emit notification->closed(NotificationCloseReason::ActionInvoked);
419  notification->m_id = 0;
420  pendingNotifications.erase(i);
421  // however, lxqt-notificationd does not close the notification
422  // -> close manually for consistent behaviour
423  s_dbusInterface->CloseNotification(i->first);
424  }
425 }
426 
478 } // namespace QtUtilities
QtUtilities::SwappedImage
The SwappedImage struct represents RGB-interved version of the image specified on construction.
Definition: dbusnotification.cpp:48
QtUtilities::NotificationIcon::Information
@ Information
QtUtilities::NotificationImage::data
QByteArray data
Definition: dbusnotification.cpp:75
QtUtilities::NotificationImage::fromDBusArgument
static NotificationImage fromDBusArgument(const QVariant &variant)
Definition: dbusnotification.cpp:141
QtUtilities::DBusNotification::isAvailable
static bool isAvailable()
Returns whether the notification D-Bus daemon is running.
Definition: dbusnotification.cpp:212
QtUtilities::DBusNotification::deleteOnCloseOrError
void deleteOnCloseOrError()
Makes the notification object delete itself when the notification has been closed or an error occurre...
Definition: dbusnotification.cpp:261
QtUtilities::DBusNotification::isVisible
bool isVisible() const
Returns whether the notification is (still) visible.
Definition: dbusnotification.h:384
QtUtilities::DBusNotification::queryCapabilities
static bool queryCapabilities(const std::function< void(Capabilities &&capabilities)> &callback)
Definition: dbusnotification.cpp:329
QtUtilities::NotificationImage::bitsPerSample
qint32 bitsPerSample
Definition: dbusnotification.cpp:74
QtUtilities::NotificationImage::channels
qint32 channels
Definition: dbusnotification.cpp:73
QtUtilities::DBusNotification::show
bool show()
Shows the notification.
Definition: dbusnotification.cpp:274
QtUtilities::DBusNotification::shown
void shown()
Emitted when the notification could be shown successful.
QtUtilities::DBusNotification::Capabilities
Definition: dbusnotification.h:33
QtUtilities::NotificationImage::width
qint32 width
Definition: dbusnotification.cpp:69
QtUtilities::NotificationCloseReason::ActionInvoked
@ ActionInvoked
QtUtilities::NotificationImage
The NotificationImage struct is a raw data image format.
Definition: dbusnotification.cpp:62
QtUtilities::DBusNotification::message
QString message
Returns the assigned message.
Definition: dbusnotification.h:26
QtUtilities::DBusNotification::~DBusNotification
~DBusNotification() override
Closes the notification if still shown and delete the object.
Definition: dbusnotification.cpp:197
QtUtilities::NotificationImage::toQImage
QImage toQImage() const
Definition: dbusnotification.cpp:129
dbusnotification.h
QtUtilities::NotificationCloseReason::Undefined
@ Undefined
QtUtilities::operator<<
QDBusArgument & operator<<(QDBusArgument &argument, const NotificationImage &img)
Definition: dbusnotification.cpp:82
QtUtilities::NotificationImage::rowstride
qint32 rowstride
Definition: dbusnotification.cpp:71
QtUtilities::DBusNotification::update
bool update(const QString &line)
Updates the message and shows/updates the notification.
Definition: dbusnotification.cpp:315
QtUtilities::DBusNotification::hide
bool hide()
Hides the notification (if still visible).
Definition: dbusnotification.cpp:356
QtUtilities::DBusNotification::error
void error()
Emitted when the notification couldn't be shown.
QtUtilities::NotificationIcon::Critical
@ Critical
QtUtilities
!
Definition: trylocker.h:8
QtUtilities::NotificationImage::isValid
bool isValid
Definition: dbusnotification.cpp:76
QtUtilities::DBusNotification::closed
void closed(NotificationCloseReason reason)
Emitted when the notification has been closed.
QtUtilities::NotificationIcon
NotificationIcon
Definition: dbusnotification.h:18
QtUtilities::DBusNotification::setIcon
void setIcon(const QString &icon)
Sets the icon name.
Definition: dbusnotification.h:249
QtUtilities::NotificationCloseReason
NotificationCloseReason
Definition: dbusnotification.h:20
QtUtilities::NotificationImage::toDBusArgument
QVariant toDBusArgument() const
Definition: dbusnotification.cpp:136
QtUtilities::DBusNotification::setImage
void setImage(const QImage &image)
Sets the image.
Definition: dbusnotification.cpp:252
QtUtilities::DBusNotification::hint
QVariant hint(const QString &name) const
Returns the hint with the specified name.
Definition: dbusnotification.h:257
QtUtilities::DBusNotification::image
const QImage image() const
Returns the image.
Definition: dbusnotification.cpp:241
QtUtilities::NotificationIcon::Warning
@ Warning
QtUtilities::operator>>
const QDBusArgument & operator>>(const QDBusArgument &argument, NotificationImage &img)
Definition: dbusnotification.cpp:90
Q_DECLARE_METATYPE
Q_DECLARE_METATYPE(QtUtilities::NotificationImage)
QtUtilities::NotificationImage::NotificationImage
NotificationImage()
Definition: dbusnotification.cpp:98
QtUtilities::NotificationImage::height
qint32 height
Definition: dbusnotification.cpp:70
QtUtilities::NotificationImage::hasAlpha
bool hasAlpha
Definition: dbusnotification.cpp:72
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:155