You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

notificator.cpp 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #include "notificator.h"
  2. #include <QMetaType>
  3. #include <QVariant>
  4. #include <QIcon>
  5. #include <QApplication>
  6. #include <QStyle>
  7. #include <QByteArray>
  8. #include <QSystemTrayIcon>
  9. #include <QMessageBox>
  10. #ifdef QT_DBUS
  11. #include <QtDBus/QtDBus>
  12. #include <stdint.h>
  13. #endif
  14. // https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128
  15. const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
  16. Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent):
  17. QObject(parent),
  18. parent(parent),
  19. programName(programName),
  20. mode(None),
  21. trayIcon(trayicon)
  22. #ifdef QT_DBUS
  23. ,interface(0)
  24. #endif
  25. {
  26. if(trayicon && trayicon->supportsMessages())
  27. {
  28. mode = QSystemTray;
  29. }
  30. #ifdef QT_DBUS
  31. interface = new QDBusInterface("org.freedesktop.Notifications",
  32. "/org/freedesktop/Notifications", "org.freedesktop.Notifications");
  33. if(interface->isValid())
  34. {
  35. mode = Freedesktop;
  36. }
  37. #endif
  38. }
  39. Notificator::~Notificator()
  40. {
  41. #ifdef QT_DBUS
  42. delete interface;
  43. #endif
  44. }
  45. #ifdef QT_DBUS
  46. // Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html
  47. class FreedesktopImage
  48. {
  49. public:
  50. FreedesktopImage() {}
  51. FreedesktopImage(const QImage &img);
  52. static int metaType();
  53. // Image to variant that can be marshaled over DBus
  54. static QVariant toVariant(const QImage &img);
  55. private:
  56. int width, height, stride;
  57. bool hasAlpha;
  58. int channels;
  59. int bitsPerSample;
  60. QByteArray image;
  61. friend QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i);
  62. friend const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i);
  63. };
  64. Q_DECLARE_METATYPE(FreedesktopImage);
  65. // Image configuration settings
  66. const int CHANNELS = 4;
  67. const int BYTES_PER_PIXEL = 4;
  68. const int BITS_PER_SAMPLE = 8;
  69. FreedesktopImage::FreedesktopImage(const QImage &img):
  70. width(img.width()),
  71. height(img.height()),
  72. stride(img.width() * BYTES_PER_PIXEL),
  73. hasAlpha(true),
  74. channels(CHANNELS),
  75. bitsPerSample(BITS_PER_SAMPLE)
  76. {
  77. // Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format
  78. QImage tmp = img.convertToFormat(QImage::Format_ARGB32);
  79. const uint32_t *data = reinterpret_cast<const uint32_t*>(tmp.constBits());
  80. unsigned int num_pixels = width * height;
  81. image.resize(num_pixels * BYTES_PER_PIXEL);
  82. for(unsigned int ptr = 0; ptr < num_pixels; ++ptr)
  83. {
  84. image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R
  85. image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8; // G
  86. image[ptr*BYTES_PER_PIXEL+2] = data[ptr]; // B
  87. image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A
  88. }
  89. }
  90. QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i)
  91. {
  92. a.beginStructure();
  93. a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image;
  94. a.endStructure();
  95. return a;
  96. }
  97. const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i)
  98. {
  99. a.beginStructure();
  100. a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image;
  101. a.endStructure();
  102. return a;
  103. }
  104. int FreedesktopImage::metaType()
  105. {
  106. return qDBusRegisterMetaType<FreedesktopImage>();
  107. }
  108. QVariant FreedesktopImage::toVariant(const QImage &img)
  109. {
  110. FreedesktopImage fimg(img);
  111. return QVariant(FreedesktopImage::metaType(), &fimg);
  112. }
  113. void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
  114. {
  115. Q_UNUSED(cls);
  116. // Arguments for DBus call:
  117. QList<QVariant> args;
  118. // Program Name:
  119. args.append(programName);
  120. // Unique ID of this notification type:
  121. args.append(0U);
  122. // Application Icon, empty string
  123. args.append(QString());
  124. // Summary
  125. args.append(title);
  126. // Body
  127. args.append(text);
  128. // Actions (none, actions are deprecated)
  129. QStringList actions;
  130. args.append(actions);
  131. // Hints
  132. QVariantMap hints;
  133. // If no icon specified, set icon based on class
  134. QIcon tmpicon;
  135. if(icon.isNull())
  136. {
  137. QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
  138. switch(cls)
  139. {
  140. case Information: sicon = QStyle::SP_MessageBoxInformation; break;
  141. case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
  142. case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
  143. default: break;
  144. }
  145. tmpicon = QApplication::style()->standardIcon(sicon);
  146. }
  147. else
  148. {
  149. tmpicon = icon;
  150. }
  151. hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage());
  152. args.append(hints);
  153. // Timeout (in msec)
  154. args.append(millisTimeout);
  155. // "Fire and forget"
  156. interface->callWithArgumentList(QDBus::NoBlock, "Notify", args);
  157. }
  158. #endif
  159. void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
  160. {
  161. Q_UNUSED(icon);
  162. QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon;
  163. switch(cls) // Set icon based on class
  164. {
  165. case Information: sicon = QSystemTrayIcon::Information; break;
  166. case Warning: sicon = QSystemTrayIcon::Warning; break;
  167. case Critical: sicon = QSystemTrayIcon::Critical; break;
  168. }
  169. trayIcon->showMessage(title, text, sicon, millisTimeout);
  170. }
  171. void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
  172. {
  173. switch(mode)
  174. {
  175. #ifdef QT_DBUS
  176. case Freedesktop:
  177. notifyDBus(cls, title, text, icon, millisTimeout);
  178. break;
  179. #endif
  180. case QSystemTray:
  181. notifySystray(cls, title, text, icon, millisTimeout);
  182. break;
  183. default:
  184. if(cls == Critical)
  185. {
  186. // Fall back to old fashioned popup dialog if critical and no other notification available
  187. QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok);
  188. }
  189. break;
  190. }
  191. }