Bitcoin Core 22.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
27const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
28#endif
29
30Notificator::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 {
43 }
44#ifdef USE_DBUS
45 interface = new QDBusInterface("org.freedesktop.Notifications",
46 "/org/freedesktop/Notifications", "org.freedesktop.Notifications");
47 if(interface->isValid())
48 {
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
70class FreedesktopImage
71{
72public:
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
81private:
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
92Q_DECLARE_METATYPE(FreedesktopImage);
93
94// Image configuration settings
95const int CHANNELS = 4;
96const int BYTES_PER_PIXEL = 4;
97const int BITS_PER_SAMPLE = 8;
98
99FreedesktopImage::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
123QDBusArgument &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
131const 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
139int FreedesktopImage::metaType()
140{
141 return qDBusRegisterMetaType<FreedesktopImage>();
142}
143
144QVariant FreedesktopImage::toVariant(const QImage &img)
145{
146 FreedesktopImage fimg(img);
147 return QVariant(FreedesktopImage::metaType(), &fimg);
148}
149
150void 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
207void 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
220void 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
227void 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}
#define USE_DBUS
static MacNotificationHandler * instance()
void showNotification(const QString &title, const QString &text)
shows a macOS 10.8+ UserNotification in the UserNotificationCenter
QString programName
Definition: notificator.h:63
QWidget * parent
Definition: notificator.h:56
@ Information
Informational message.
Definition: notificator.h:38
@ Critical
An error occurred.
Definition: notificator.h:40
@ Warning
Notify user of potential problem.
Definition: notificator.h:39
@ UserNotificationCenter
Use the 10.8+ User Notification Center (Mac only)
Definition: notificator.h:61
@ QSystemTray
Use QSystemTrayIcon::showMessage()
Definition: notificator.h:60
@ Freedesktop
Use DBus org.freedesktop.Notifications.
Definition: notificator.h:59
void notifySystray(Class cls, const QString &title, const QString &text, int millisTimeout)
Notificator(const QString &programName, QSystemTrayIcon *trayIcon, QWidget *parent)
Create a new notificator.
Definition: notificator.cpp:30
QSystemTrayIcon * trayIcon
Definition: notificator.h:65
void notify(Class cls, const QString &title, const QString &text, const QIcon &icon=QIcon(), int millisTimeout=10000)
Show notification message.
std::ostream & operator<<(std::ostream &os, BigO const &bigO)