Bitcoin Core  21.99.0
P2P Digital Currency
notificator.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2020 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <qt/notificator.h>
6 
7 #include <QApplication>
8 #include <QByteArray>
9 #include <QImageWriter>
10 #include <QMessageBox>
11 #include <QMetaType>
12 #include <QStyle>
13 #include <QSystemTrayIcon>
14 #include <QTemporaryFile>
15 #include <QVariant>
16 #ifdef USE_DBUS
17 #include <stdint.h>
18 #include <QtDBus>
19 #endif
20 #ifdef Q_OS_MAC
22 #endif
23 
24 
25 #ifdef USE_DBUS
26 // https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128
27 const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
28 #endif
29 
30 Notificator::Notificator(const QString &_programName, QSystemTrayIcon *_trayIcon, QWidget *_parent) :
31  QObject(_parent),
32  parent(_parent),
33  programName(_programName),
34  mode(None),
35  trayIcon(_trayIcon)
36 #ifdef USE_DBUS
37  ,interface(nullptr)
38 #endif
39 {
40  if(_trayIcon && _trayIcon->supportsMessages())
41  {
42  mode = QSystemTray;
43  }
44 #ifdef USE_DBUS
45  interface = new QDBusInterface("org.freedesktop.Notifications",
46  "/org/freedesktop/Notifications", "org.freedesktop.Notifications");
47  if(interface->isValid())
48  {
49  mode = Freedesktop;
50  }
51 #endif
52 #ifdef Q_OS_MAC
53  // check if users OS has support for NSUserNotification
54  if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) {
56  }
57 #endif
58 }
59 
61 {
62 #ifdef USE_DBUS
63  delete interface;
64 #endif
65 }
66 
67 #ifdef USE_DBUS
68 
69 // Loosely based on https://www.qtcentre.org/archive/index.php/t-25879.html
70 class FreedesktopImage
71 {
72 public:
73  FreedesktopImage() {}
74  explicit FreedesktopImage(const QImage &img);
75 
76  static int metaType();
77 
78  // Image to variant that can be marshalled over DBus
79  static QVariant toVariant(const QImage &img);
80 
81 private:
82  int width, height, stride;
83  bool hasAlpha;
84  int channels;
85  int bitsPerSample;
86  QByteArray image;
87 
88  friend QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i);
89  friend const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i);
90 };
91 
92 Q_DECLARE_METATYPE(FreedesktopImage);
93 
94 // Image configuration settings
95 const int CHANNELS = 4;
96 const int BYTES_PER_PIXEL = 4;
97 const int BITS_PER_SAMPLE = 8;
98 
99 FreedesktopImage::FreedesktopImage(const QImage &img):
100  width(img.width()),
101  height(img.height()),
102  stride(img.width() * BYTES_PER_PIXEL),
103  hasAlpha(true),
104  channels(CHANNELS),
105  bitsPerSample(BITS_PER_SAMPLE)
106 {
107  // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format
108  QImage tmp = img.convertToFormat(QImage::Format_ARGB32);
109  const uint32_t *data = reinterpret_cast<const uint32_t*>(tmp.bits());
110 
111  unsigned int num_pixels = width * height;
112  image.resize(num_pixels * BYTES_PER_PIXEL);
113 
114  for(unsigned int ptr = 0; ptr < num_pixels; ++ptr)
115  {
116  image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R
117  image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8; // G
118  image[ptr*BYTES_PER_PIXEL+2] = data[ptr]; // B
119  image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A
120  }
121 }
122 
123 QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i)
124 {
125  a.beginStructure();
126  a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image;
127  a.endStructure();
128  return a;
129 }
130 
131 const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i)
132 {
133  a.beginStructure();
134  a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image;
135  a.endStructure();
136  return a;
137 }
138 
139 int FreedesktopImage::metaType()
140 {
141  return qDBusRegisterMetaType<FreedesktopImage>();
142 }
143 
144 QVariant FreedesktopImage::toVariant(const QImage &img)
145 {
146  FreedesktopImage fimg(img);
147  return QVariant(FreedesktopImage::metaType(), &fimg);
148 }
149 
150 void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
151 {
152  // https://developer.gnome.org/notification-spec/
153  // Arguments for DBus "Notify" call:
154  QList<QVariant> args;
155 
156  // Program Name:
157  args.append(programName);
158 
159  // Replaces ID; A value of 0 means that this notification won't replace any existing notifications:
160  args.append(0U);
161 
162  // Application Icon, empty string
163  args.append(QString());
164 
165  // Summary
166  args.append(title);
167 
168  // Body
169  args.append(text);
170 
171  // Actions (none, actions are deprecated)
172  QStringList actions;
173  args.append(actions);
174 
175  // Hints
176  QVariantMap hints;
177 
178  // If no icon specified, set icon based on class
179  QIcon tmpicon;
180  if(icon.isNull())
181  {
182  QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
183  switch(cls)
184  {
185  case Information: sicon = QStyle::SP_MessageBoxInformation; break;
186  case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
187  case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
188  default: break;
189  }
190  tmpicon = QApplication::style()->standardIcon(sicon);
191  }
192  else
193  {
194  tmpicon = icon;
195  }
196  hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage());
197  args.append(hints);
198 
199  // Timeout (in msec)
200  args.append(millisTimeout);
201 
202  // "Fire and forget"
203  interface->callWithArgumentList(QDBus::NoBlock, "Notify", args);
204 }
205 #endif
206 
207 void Notificator::notifySystray(Class cls, const QString &title, const QString &text, int millisTimeout)
208 {
209  QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon;
210  switch(cls) // Set icon based on class
211  {
212  case Information: sicon = QSystemTrayIcon::Information; break;
213  case Warning: sicon = QSystemTrayIcon::Warning; break;
214  case Critical: sicon = QSystemTrayIcon::Critical; break;
215  }
216  trayIcon->showMessage(title, text, sicon, millisTimeout);
217 }
218 
219 #ifdef Q_OS_MAC
220 void Notificator::notifyMacUserNotificationCenter(const QString &title, const QString &text)
221 {
222  // icon is not supported by the user notification center yet. OSX will use the app icon.
224 }
225 #endif
226 
227 void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
228 {
229  switch(mode)
230  {
231 #ifdef USE_DBUS
232  case Freedesktop:
233  notifyDBus(cls, title, text, icon, millisTimeout);
234  break;
235 #endif
236  case QSystemTray:
237  notifySystray(cls, title, text, millisTimeout);
238  break;
239 #ifdef Q_OS_MAC
241  notifyMacUserNotificationCenter(title, text);
242  break;
243 #endif
244  default:
245  if(cls == Critical)
246  {
247  // Fall back to old fashioned pop-up dialog if critical and no other notification available
248  QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok);
249  }
250  break;
251  }
252 }
Notificator::notifySystray
void notifySystray(Class cls, const QString &title, const QString &text, int millisTimeout)
Definition: notificator.cpp:207
ankerl::nanobench::operator<<
std::ostream & operator<<(std::ostream &os, BigO const &bigO)
notificator.h
Notificator::Critical
@ Critical
An error occurred.
Definition: notificator.h:40
Notificator::QSystemTray
@ QSystemTray
Use QSystemTrayIcon::showMessage()
Definition: notificator.h:60
macnotificationhandler.h
Notificator::notify
void notify(Class cls, const QString &title, const QString &text, const QIcon &icon=QIcon(), int millisTimeout=10000)
Show notification message.
Definition: notificator.cpp:227
Notificator::UserNotificationCenter
@ UserNotificationCenter
Use the 10.8+ User Notification Center (Mac only)
Definition: notificator.h:61
Notificator::trayIcon
QSystemTrayIcon * trayIcon
Definition: notificator.h:65
Notificator::parent
QWidget * parent
Definition: notificator.h:56
Notificator::Freedesktop
@ Freedesktop
Use DBus org.freedesktop.Notifications.
Definition: notificator.h:59
Notificator::mode
Mode mode
Definition: notificator.h:64
Notificator::Information
@ Information
Informational message.
Definition: notificator.h:38
MacNotificationHandler::instance
static MacNotificationHandler * instance()
Definition: macnotificationhandler.mm:47
Notificator::programName
QString programName
Definition: notificator.h:63
Notificator::Notificator
Notificator(const QString &programName, QSystemTrayIcon *trayIcon, QWidget *parent)
Create a new notificator.
Definition: notificator.cpp:30
org
Notificator::~Notificator
~Notificator()
Definition: notificator.cpp:60
MacNotificationHandler::showNotification
void showNotification(const QString &title, const QString &text)
shows a macOS 10.8+ UserNotification in the UserNotificationCenter
Definition: macnotificationhandler.mm:23
ConnectionDirection::None
@ None
Notificator::Class
Class
Definition: notificator.h:36
Notificator::Warning
@ Warning
Notify user of potential problem.
Definition: notificator.h:39