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