summarylogtreecommitdiffstats
path: root/QtDBus-Notifications-Implementation.patch
diff options
context:
space:
mode:
Diffstat (limited to 'QtDBus-Notifications-Implementation.patch')
-rw-r--r--QtDBus-Notifications-Implementation.patch1172
1 files changed, 1172 insertions, 0 deletions
diff --git a/QtDBus-Notifications-Implementation.patch b/QtDBus-Notifications-Implementation.patch
new file mode 100644
index 000000000000..559adc3d49ea
--- /dev/null
+++ b/QtDBus-Notifications-Implementation.patch
@@ -0,0 +1,1172 @@
+diff --git a/Telegram/SourceFiles/platform/linux/linux_libnotify.cpp b/Telegram/SourceFiles/platform/linux/linux_libnotify.cpp
+deleted file mode 100644
+index 657c46075..000000000
+--- a/Telegram/SourceFiles/platform/linux/linux_libnotify.cpp
++++ /dev/null
+@@ -1,110 +0,0 @@
+-/*
+-This file is part of Telegram Desktop,
+-the official desktop application for the Telegram messaging service.
+-
+-For license and copyright information please follow this link:
+-https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+-*/
+-#include "platform/linux/linux_libnotify.h"
+-
+-#include "platform/linux/linux_libs.h"
+-
+-namespace Platform {
+-namespace Libs {
+-namespace {
+-
+-bool loadLibrary(QLibrary &lib, const char *name, int version) {
+- DEBUG_LOG(("Loading '%1' with version %2...").arg(QLatin1String(name)).arg(version));
+- lib.setFileNameAndVersion(QLatin1String(name), version);
+- if (lib.load()) {
+- DEBUG_LOG(("Loaded '%1' with version %2!").arg(QLatin1String(name)).arg(version));
+- return true;
+- }
+- lib.setFileNameAndVersion(QLatin1String(name), QString());
+- if (lib.load()) {
+- DEBUG_LOG(("Loaded '%1' without version!").arg(QLatin1String(name)));
+- return true;
+- }
+- LOG(("Could not load '%1' with version %2 :(").arg(QLatin1String(name)).arg(version));
+- return false;
+-}
+-
+-} // namespace
+-
+-#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
+-f_notify_init notify_init = nullptr;
+-f_notify_uninit notify_uninit = nullptr;
+-f_notify_is_initted notify_is_initted = nullptr;
+-//f_notify_get_app_name notify_get_app_name = nullptr;
+-//f_notify_set_app_name notify_set_app_name = nullptr;
+-f_notify_get_server_caps notify_get_server_caps = nullptr;
+-f_notify_get_server_info notify_get_server_info = nullptr;
+-
+-f_notify_notification_new notify_notification_new = nullptr;
+-//f_notify_notification_update notify_notification_update = nullptr;
+-f_notify_notification_show notify_notification_show = nullptr;
+-//f_notify_notification_set_app_name notify_notification_set_app_name = nullptr;
+-f_notify_notification_set_timeout notify_notification_set_timeout = nullptr;
+-//f_notify_notification_set_category notify_notification_set_category = nullptr;
+-//f_notify_notification_set_urgency notify_notification_set_urgency = nullptr;
+-//f_notify_notification_set_icon_from_pixbuf notify_notification_set_icon_from_pixbuf = nullptr;
+-f_notify_notification_set_image_from_pixbuf notify_notification_set_image_from_pixbuf = nullptr;
+-//f_notify_notification_set_hint notify_notification_set_hint = nullptr;
+-//f_notify_notification_set_hint_int32 notify_notification_set_hint_int32 = nullptr;
+-//f_notify_notification_set_hint_uint32 notify_notification_set_hint_uint32 = nullptr;
+-//f_notify_notification_set_hint_double notify_notification_set_hint_double = nullptr;
+-f_notify_notification_set_hint_string notify_notification_set_hint_string = nullptr;
+-//f_notify_notification_set_hint_byte notify_notification_set_hint_byte = nullptr;
+-//f_notify_notification_set_hint_byte_array notify_notification_set_hint_byte_array = nullptr;
+-//f_notify_notification_clear_hints notify_notification_clear_hints = nullptr;
+-f_notify_notification_add_action notify_notification_add_action = nullptr;
+-f_notify_notification_clear_actions notify_notification_clear_actions = nullptr;
+-f_notify_notification_close notify_notification_close = nullptr;
+-f_notify_notification_get_closed_reason notify_notification_get_closed_reason = nullptr;
+-
+-void startLibNotify() {
+- DEBUG_LOG(("Loading libnotify"));
+-
+- QLibrary lib_notify;
+- if (!loadLibrary(lib_notify, "notify", 4)) {
+- if (!loadLibrary(lib_notify, "notify", 5)) {
+- if (!loadLibrary(lib_notify, "notify", 1)) {
+- return;
+- }
+- }
+- }
+-
+- load(lib_notify, "notify_init", notify_init);
+- load(lib_notify, "notify_uninit", notify_uninit);
+- load(lib_notify, "notify_is_initted", notify_is_initted);
+-// load(lib_notify, "notify_get_app_name", notify_get_app_name);
+-// load(lib_notify, "notify_set_app_name", notify_set_app_name);
+- load(lib_notify, "notify_get_server_caps", notify_get_server_caps);
+- load(lib_notify, "notify_get_server_info", notify_get_server_info);
+-
+- load(lib_notify, "notify_notification_new", notify_notification_new);
+-// load(lib_notify, "notify_notification_update", notify_notification_update);
+- load(lib_notify, "notify_notification_show", notify_notification_show);
+-// load(lib_notify, "notify_notification_set_app_name", notify_notification_set_app_name);
+- load(lib_notify, "notify_notification_set_timeout", notify_notification_set_timeout);
+-// load(lib_notify, "notify_notification_set_category", notify_notification_set_category);
+-// load(lib_notify, "notify_notification_set_urgency", notify_notification_set_urgency);
+-// load(lib_notify, "notify_notification_set_icon_from_pixbuf", notify_notification_set_icon_from_pixbuf);
+- load(lib_notify, "notify_notification_set_image_from_pixbuf", notify_notification_set_image_from_pixbuf);
+-// load(lib_notify, "notify_notification_set_hint", notify_notification_set_hint);
+-// load(lib_notify, "notify_notification_set_hint_int32", notify_notification_set_hint_int32);
+-// load(lib_notify, "notify_notification_set_hint_uint32", notify_notification_set_hint_uint32);
+-// load(lib_notify, "notify_notification_set_hint_double", notify_notification_set_hint_double);
+- load(lib_notify, "notify_notification_set_hint_string", notify_notification_set_hint_string);
+-// load(lib_notify, "notify_notification_set_hint_byte", notify_notification_set_hint_byte);
+-// load(lib_notify, "notify_notification_set_hint_byte_array", notify_notification_set_hint_byte_array);
+-// load(lib_notify, "notify_notification_clear_hints", notify_notification_clear_hints);
+- load(lib_notify, "notify_notification_add_action", notify_notification_add_action);
+- load(lib_notify, "notify_notification_clear_actions", notify_notification_clear_actions);
+- load(lib_notify, "notify_notification_close", notify_notification_close);
+- load(lib_notify, "notify_notification_get_closed_reason", notify_notification_get_closed_reason);
+-}
+-#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
+-
+-} // namespace Libs
+-} // namespace Platform
+diff --git a/Telegram/SourceFiles/platform/linux/linux_libnotify.h b/Telegram/SourceFiles/platform/linux/linux_libnotify.h
+deleted file mode 100644
+index 6dcfd676a..000000000
+--- a/Telegram/SourceFiles/platform/linux/linux_libnotify.h
++++ /dev/null
+@@ -1,120 +0,0 @@
+-/*
+-This file is part of Telegram Desktop,
+-the official desktop application for the Telegram messaging service.
+-
+-For license and copyright information please follow this link:
+-https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+-*/
+-#pragma once
+-
+-#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
+-extern "C" {
+-#undef signals
+-#include <gtk/gtk.h>
+-#define signals public
+-} // extern "C"
+-
+-namespace Platform {
+-namespace Libs {
+-
+-void startLibNotify();
+-
+-constexpr gint NOTIFY_EXPIRES_DEFAULT = -1;
+-constexpr gint NOTIFY_EXPIRES_NEVER = 0;
+-
+-struct NotifyNotification;
+-typedef enum {
+- NOTIFY_URGENCY_LOW,
+- NOTIFY_URGENCY_NORMAL,
+- NOTIFY_URGENCY_CRITICAL,
+-} NotifyUrgency;
+-
+-using NotifyActionCallback = void (*)(NotifyNotification *notification, char *action, gpointer user_data);
+-
+-using f_notify_init = gboolean (*)(const char *app_name);
+-extern f_notify_init notify_init;
+-
+-using f_notify_uninit = void (*)(void);
+-extern f_notify_uninit notify_uninit;
+-
+-using f_notify_is_initted = gboolean (*)(void);
+-extern f_notify_is_initted notify_is_initted;
+-
+-//using f_notify_get_app_name = const char* (*)(void);
+-//extern f_notify_get_app_name notify_get_app_name;
+-
+-//using f_notify_set_app_name = void (*)(const char *app_name);
+-//extern f_notify_set_app_name notify_set_app_name;
+-
+-using f_notify_get_server_caps = GList* (*)(void);
+-extern f_notify_get_server_caps notify_get_server_caps;
+-
+-using f_notify_get_server_info = gboolean (*)(char **ret_name, char **ret_vendor, char **ret_version, char **ret_spec_version);
+-extern f_notify_get_server_info notify_get_server_info;
+-
+-using f_notify_notification_new = NotifyNotification* (*)(const char *summary, const char *body, const char *icon);
+-extern f_notify_notification_new notify_notification_new;
+-
+-//using f_notify_notification_update = gboolean (*)(NotifyNotification *notification, const char *summary, const char *body, const char *icon);
+-//extern f_notify_notification_update notify_notification_update;
+-
+-using f_notify_notification_show = gboolean (*)(NotifyNotification *notification, GError **error);
+-extern f_notify_notification_show notify_notification_show;
+-
+-//using f_notify_notification_set_app_name = void (*)(NotifyNotification *notification, const char *app_name);
+-//extern f_notify_notification_set_app_name notify_notification_set_app_name;
+-
+-using f_notify_notification_set_timeout = void (*)(NotifyNotification *notification, gint timeout);
+-extern f_notify_notification_set_timeout notify_notification_set_timeout;
+-
+-//using f_notify_notification_set_category = void (*)(NotifyNotification *notification, const char *category);
+-//extern f_notify_notification_set_category notify_notification_set_category;
+-
+-//using f_notify_notification_set_urgency = void (*)(NotifyNotification *notification, NotifyUrgency urgency);
+-//extern f_notify_notification_set_urgency notify_notification_set_urgency;
+-
+-//using f_notify_notification_set_icon_from_pixbuf = void (*)(NotifyNotification *notification, GdkPixbuf *icon);
+-//extern f_notify_notification_set_icon_from_pixbuf notify_notification_set_icon_from_pixbuf;
+-
+-using f_notify_notification_set_image_from_pixbuf = void (*)(NotifyNotification *notification, GdkPixbuf *pixbuf);
+-extern f_notify_notification_set_image_from_pixbuf notify_notification_set_image_from_pixbuf;
+-
+-//using f_notify_notification_set_hint = void (*)(NotifyNotification *notification, const char *key, GVariant *value);
+-//extern f_notify_notification_set_hint notify_notification_set_hint;
+-
+-//using f_notify_notification_set_hint_int32 = void (*)(NotifyNotification *notification, const char *key, gint value);
+-//extern f_notify_notification_set_hint_int32 notify_notification_set_hint_int32;
+-
+-//using f_notify_notification_set_hint_uint32 = void (*)(NotifyNotification *notification, const char *key, guint value);
+-//extern f_notify_notification_set_hint_uint32 notify_notification_set_hint_uint32;
+-
+-//using f_notify_notification_set_hint_double = void (*)(NotifyNotification *notification, const char *key, gdouble value);
+-//extern f_notify_notification_set_hint_double notify_notification_set_hint_double;
+-
+-using f_notify_notification_set_hint_string = void (*)(NotifyNotification *notification, const char *key, const char *value);
+-extern f_notify_notification_set_hint_string notify_notification_set_hint_string;
+-
+-//using f_notify_notification_set_hint_byte = void (*)(NotifyNotification *notification, const char *key, guchar value);
+-//extern f_notify_notification_set_hint_byte notify_notification_set_hint_byte;
+-
+-//using f_notify_notification_set_hint_byte_array = void (*)(NotifyNotification *notification, const char *key, const guchar *value, gsize len);
+-//extern f_notify_notification_set_hint_byte_array notify_notification_set_hint_byte_array;
+-
+-//using f_notify_notification_clear_hints = void (*)(NotifyNotification *notification);
+-//extern f_notify_notification_clear_hints notify_notification_clear_hints;
+-
+-using f_notify_notification_add_action = void (*)(NotifyNotification *notification, const char *action, const char *label, NotifyActionCallback callback, gpointer user_data, GFreeFunc free_func);
+-extern f_notify_notification_add_action notify_notification_add_action;
+-
+-using f_notify_notification_clear_actions = void (*)(NotifyNotification *notification);
+-extern f_notify_notification_clear_actions notify_notification_clear_actions;
+-
+-using f_notify_notification_close = gboolean (*)(NotifyNotification *notification, GError **error);
+-extern f_notify_notification_close notify_notification_close;
+-
+-using f_notify_notification_get_closed_reason = gint (*)(const NotifyNotification *notification);
+-extern f_notify_notification_get_closed_reason notify_notification_get_closed_reason;
+-
+-} // namespace Libs
+-} // namespace Platform
+-#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
+diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.cpp b/Telegram/SourceFiles/platform/linux/linux_libs.cpp
+index 5071d63d1..d60734e7c 100644
+--- a/Telegram/SourceFiles/platform/linux/linux_libs.cpp
++++ b/Telegram/SourceFiles/platform/linux/linux_libs.cpp
+@@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+ #include "platform/linux/linux_libs.h"
+
+ #include "platform/linux/linux_gdk_helper.h"
+-#include "platform/linux/linux_libnotify.h"
+ #include "platform/linux/linux_desktop_environment.h"
+
+ #include <QtGui/QGuiApplication>
+@@ -290,10 +289,6 @@ void start() {
+ } else {
+ LOG(("Could not load gtk-x11-2.0!"));
+ }
+-
+- if (gtkLoaded) {
+- startLibNotify();
+- }
+ #endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
+ }
+
+diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
+index d076b90f3..13c9d20aa 100644
+--- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
++++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
+@@ -264,13 +264,6 @@ void MainWindow::psSetupTrayIcon() {
+ }
+ trayIcon->setIcon(icon);
+
+- // This is very important for native notifications via libnotify!
+- // Some notification servers compose several notifications with a "Reply"
+- // action into one and after that a click on "Reply" button does not call
+- // the specified callback from any of the sent notification - libnotify
+- // just ignores ibus messages, but Qt tray icon at least emits this signal.
+- connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showFromTray()));
+-
+ attachToTrayIcon(trayIcon);
+ }
+ updateIconCounters();
+diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
+index 9b0dc1d65..211abf9fc 100644
+--- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
++++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp
+@@ -7,389 +7,244 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+ */
+ #include "platform/linux/notifications_manager_linux.h"
+
+-#include "window/notifications_utilities.h"
+-#include "platform/linux/linux_libnotify.h"
+-#include "platform/linux/linux_libs.h"
+ #include "history/history.h"
+ #include "lang/lang_keys.h"
+ #include "facades.h"
+
++#include <QtCore/QBuffer>
++#include <QtDBus/QDBusConnection>
++#include <QtDBus/QDBusReply>
++
+ namespace Platform {
+ namespace Notifications {
+-#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
+-namespace {
+-
+-bool LibNotifyLoaded() {
+- return (Libs::notify_init != nullptr)
+- && (Libs::notify_uninit != nullptr)
+- && (Libs::notify_is_initted != nullptr)
+-// && (Libs::notify_get_app_name != nullptr)
+-// && (Libs::notify_set_app_name != nullptr)
+- && (Libs::notify_get_server_caps != nullptr)
+- && (Libs::notify_get_server_info != nullptr)
+- && (Libs::notify_notification_new != nullptr)
+-// && (Libs::notify_notification_update != nullptr)
+- && (Libs::notify_notification_show != nullptr)
+-// && (Libs::notify_notification_set_app_name != nullptr)
+- && (Libs::notify_notification_set_timeout != nullptr)
+-// && (Libs::notify_notification_set_category != nullptr)
+-// && (Libs::notify_notification_set_urgency != nullptr)
+-// && (Libs::notify_notification_set_icon_from_pixbuf != nullptr)
+- && (Libs::notify_notification_set_image_from_pixbuf != nullptr)
+-// && (Libs::notify_notification_set_hint != nullptr)
+-// && (Libs::notify_notification_set_hint_int32 != nullptr)
+-// && (Libs::notify_notification_set_hint_uint32 != nullptr)
+-// && (Libs::notify_notification_set_hint_double != nullptr)
+- && (Libs::notify_notification_set_hint_string != nullptr)
+-// && (Libs::notify_notification_set_hint_byte != nullptr)
+-// && (Libs::notify_notification_set_hint_byte_array != nullptr)
+-// && (Libs::notify_notification_clear_hints != nullptr)
+- && (Libs::notify_notification_add_action != nullptr)
+- && (Libs::notify_notification_clear_actions != nullptr)
+- && (Libs::notify_notification_close != nullptr)
+- && (Libs::notify_notification_get_closed_reason != nullptr)
+- && (Libs::g_object_ref_sink != nullptr)
+- && (Libs::g_object_unref != nullptr)
+- && (Libs::g_list_free_full != nullptr)
+- && (Libs::g_error_free != nullptr)
+- && (Libs::g_signal_connect_data != nullptr)
+- && (Libs::g_signal_handler_disconnect != nullptr)
+-// && (Libs::gdk_pixbuf_new_from_data != nullptr)
+- && (Libs::gdk_pixbuf_new_from_file != nullptr);
+-}
+-
+-QString escapeHtml(const QString &text) {
+- auto result = QString();
+- auto copyFrom = 0, textSize = text.size();
+- auto data = text.constData();
+- for (auto i = 0; i != textSize; ++i) {
+- auto ch = data[i];
+- if (ch == '<' || ch == '>' || ch == '&') {
+- if (!copyFrom) {
+- result.reserve(textSize * 5);
+- }
+- if (i > copyFrom) {
+- result.append(data + copyFrom, i - copyFrom);
+- }
+- switch (ch.unicode()) {
+- case '<': result.append(qstr("&lt;")); break;
+- case '>': result.append(qstr("&gt;")); break;
+- case '&': result.append(qstr("&amp;")); break;
+- }
+- copyFrom = i + 1;
+- }
+- }
+- if (copyFrom > 0) {
+- result.append(data + copyFrom, textSize - copyFrom);
+- return result;
+- }
+- return text;
+-}
+
+-class NotificationData {
+-public:
+- NotificationData(const std::shared_ptr<Manager*> &guarded, const QString &title, const QString &body, const QStringList &capabilities, PeerId peerId, MsgId msgId)
+- : _data(Libs::notify_notification_new(title.toUtf8().constData(), body.toUtf8().constData(), nullptr)) {
+- if (valid()) {
+- init(guarded, capabilities, peerId, msgId);
+- }
+- }
+- bool valid() const {
+- return (_data != nullptr);
+- }
+- NotificationData(const NotificationData &other) = delete;
+- NotificationData &operator=(const NotificationData &other) = delete;
+- NotificationData(NotificationData &&other) = delete;
+- NotificationData &operator=(NotificationData &&other) = delete;
+-
+- void setImage(const QString &imagePath) {
+- auto imagePathNative = QFile::encodeName(imagePath);
+- if (auto pixbuf = Libs::gdk_pixbuf_new_from_file(imagePathNative.constData(), nullptr)) {
+- Libs::notify_notification_set_image_from_pixbuf(_data, pixbuf);
+- Libs::g_object_unref(Libs::g_object_cast(pixbuf));
+- }
+- }
+- bool show() {
+- if (valid()) {
+- GError *error = nullptr;
++constexpr auto kService = str_const("org.freedesktop.Notifications");
++constexpr auto kObjectPath = str_const("/org/freedesktop/Notifications");
++constexpr auto kInterface = kService;
+
+- Libs::notify_notification_show(_data, &error);
+- if (!error) {
+- return true;
+- }
++NotificationData::NotificationData(const std::shared_ptr<Manager*> &guarded, const QString &title, const QString &subtitle, const QString &msg, PeerId peerId, MsgId msgId)
++: _notificationInterface(str_const_toString(kService), str_const_toString(kObjectPath), str_const_toString(kInterface))
++, _specificationVersion(parseSpecificationVersion(getServerInformation()))
++, _weak(guarded)
++, _title(title)
++, _peerId(peerId)
++, _msgId(msgId) {
++ auto capabilities = getCapabilities();
++ auto capabilitiesEnd = capabilities.end();
+
+- logError(error);
+- }
+- return false;
++ if (!_specificationVersion.isNull()) {
++ DEBUG_LOG(("Notification daemon specification version: %1").arg(_specificationVersion.toString()));
+ }
+
+- bool close() {
+- if (valid()) {
+- GError *error = nullptr;
+- Libs::notify_notification_close(_data, &error);
+- if (!error) {
+- return true;
++ if (!capabilities.empty()) {
++ QString capabilitiesString;
++
++ for (const auto &capability : capabilities) {
++ if (!capabilitiesString.isEmpty()) {
++ capabilitiesString += qstr(", ");
+ }
+
+- logError(error);
++ capabilitiesString += capability;
+ }
+- return false;
+- }
+
+- ~NotificationData() {
+- if (valid()) {
+-// if (_handlerId > 0) {
+-// Libs::g_signal_handler_disconnect(Libs::g_object_cast(_data), _handlerId);
+-// }
+-// Libs::notify_notification_clear_actions(_data);
+- Libs::g_object_unref(Libs::g_object_cast(_data));
+- }
++ DEBUG_LOG(("Notification daemon capabilities: %1").arg(capabilitiesString));
+ }
+
+-private:
+- void init(const std::shared_ptr<Manager*> &guarded, const QStringList &capabilities, PeerId peerId, MsgId msgId) {
+- if (capabilities.contains(qsl("append"))) {
+- Libs::notify_notification_set_hint_string(_data, "append", "true");
+- } else if (capabilities.contains(qsl("x-canonical-append"))) {
+- Libs::notify_notification_set_hint_string(_data, "x-canonical-append", "true");
+- }
++ if (ranges::find(capabilities, qsl("body-markup")) != capabilitiesEnd) {
++ _body = subtitle.isEmpty()
++ ? msg.toHtmlEscaped()
++ : qsl("<b>%1</b>\n%2").arg(subtitle.toHtmlEscaped()).arg(msg.toHtmlEscaped());
++ } else {
++ _body = subtitle.isEmpty()
++ ? msg
++ : qsl("%1\n%2").arg(subtitle).arg(msg);
++ }
+
+- Libs::notify_notification_set_hint_string(_data, "desktop-entry", "kotatogramdesktop");
+-
+- auto signalReceiver = Libs::g_object_cast(_data);
+- auto signalHandler = G_CALLBACK(NotificationData::notificationClosed);
+- auto signalName = "closed";
+- auto signalDataFreeMethod = &NotificationData::notificationDataFreeClosure;
+- auto signalData = new NotificationDataStruct(guarded, peerId, msgId);
+- _handlerId = Libs::g_signal_connect_helper(signalReceiver, signalName, signalHandler, signalData, signalDataFreeMethod);
+-
+- Libs::notify_notification_set_timeout(_data, Libs::NOTIFY_EXPIRES_DEFAULT);
+-
+- if ((*guarded)->hasActionsSupport()) {
+- auto label = tr::lng_notification_reply(tr::now).toUtf8();
+- auto actionReceiver = _data;
+- auto actionHandler = &NotificationData::notificationClicked;
+- auto actionLabel = label.constData();
+- auto actionName = "default";
+- auto actionDataFreeMethod = &NotificationData::notificationDataFree;
+- auto actionData = new NotificationDataStruct(guarded, peerId, msgId);
+- Libs::notify_notification_add_action(actionReceiver, actionName, actionLabel, actionHandler, actionData, actionDataFreeMethod);
+- }
++ if (ranges::find(capabilities, qsl("actions")) != capabilitiesEnd) {
++ // icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
++ _actions << "mail-reply-sender" << tr::lng_notification_reply(tr::now);
++ connect(&_notificationInterface, SIGNAL(ActionInvoked(uint, QString)), this, SLOT(notificationClicked(uint)));
+ }
+
+- void logError(GError *error) {
+- LOG(("LibNotify Error: domain %1, code %2, message '%3'").arg(error->domain).arg(error->code).arg(QString::fromUtf8(error->message)));
+- Libs::g_error_free(error);
++ if (ranges::find(capabilities, qsl("action-icons")) != capabilitiesEnd) {
++ _hints["action-icons"] = true;
+ }
+
+- struct NotificationDataStruct {
+- NotificationDataStruct(const std::shared_ptr<Manager*> &guarded, PeerId peerId, MsgId msgId)
+- : weak(guarded)
+- , peerId(peerId)
+- , msgId(msgId) {
++ // suppress system sound if telegram sound activated, otherwise use system sound
++ if (ranges::find(capabilities, qsl("sound")) != capabilitiesEnd) {
++ if (Global::SoundNotify()) {
++ _hints["suppress-sound"] = true;
++ } else {
++ // sound name according to http://0pointer.de/public/sound-naming-spec.html
++ _hints["sound-name"] = "message-new-instant";
+ }
+-
+- std::weak_ptr<Manager*> weak;
+- PeerId peerId = 0;
+- MsgId msgId = 0;
+- };
+- static void performOnMainQueue(NotificationDataStruct *data, FnMut<void(Manager *manager)> task) {
+- const auto weak = data->weak;
+- crl::on_main(weak, [=, task = std::move(task)]() mutable {
+- task(*weak.lock());
+- });
+- }
+- static void notificationDataFree(gpointer data) {
+- auto notificationData = static_cast<NotificationDataStruct*>(data);
+- delete notificationData;
+- }
+- static void notificationDataFreeClosure(gpointer data, GClosure *closure) {
+- auto notificationData = static_cast<NotificationDataStruct*>(data);
+- delete notificationData;
+- }
+- static void notificationClosed(Libs::NotifyNotification *notification, gpointer data) {
+- auto closedReason = Libs::notify_notification_get_closed_reason(notification);
+- auto notificationData = static_cast<NotificationDataStruct*>(data);
+- performOnMainQueue(notificationData, [peerId = notificationData->peerId, msgId = notificationData->msgId](Manager *manager) {
+- manager->clearNotification(peerId, msgId);
+- });
+- }
+- static void notificationClicked(Libs::NotifyNotification *notification, char *action, gpointer data) {
+- auto notificationData = static_cast<NotificationDataStruct*>(data);
+- performOnMainQueue(notificationData, [peerId = notificationData->peerId, msgId = notificationData->msgId](Manager *manager) {
+- manager->notificationActivated(peerId, msgId);
+- });
+ }
+
+- Libs::NotifyNotification *_data = nullptr;
+- gulong _handlerId = 0;
++ if (ranges::find(capabilities, qsl("x-canonical-append")) != capabilitiesEnd) {
++ _hints["x-canonical-append"] = "true";
++ }
+
+-};
++ _hints["category"] = "im.received";
++ _hints["desktop-entry"] = "kotatogramdesktop";
+
+-using Notification = std::shared_ptr<NotificationData>;
++ connect(&_notificationInterface, SIGNAL(NotificationClosed(uint, uint)), this, SLOT(notificationClosed(uint)));
++}
+
+-QString GetServerName() {
+- if (!LibNotifyLoaded()) {
+- return QString();
+- }
+- if (!Libs::notify_is_initted() && !Libs::notify_init("Kotatogram Desktop")) {
+- LOG(("LibNotify Error: failed to init!"));
+- return QString();
+- }
++bool NotificationData::show() {
++ QDBusReply<uint> notifyReply = _notificationInterface.call("Notify", str_const_toString(AppName), uint(0), "kotatogram", _title, _body, _actions, _hints, -1);
+
+- gchar *name = nullptr;
+- auto guard = gsl::finally([&name] {
+- if (name) Libs::g_free(name);
+- });
+-
+- if (!Libs::notify_get_server_info(&name, nullptr, nullptr, nullptr)) {
+- LOG(("LibNotify Error: could not get server name!"));
+- return QString();
+- }
+- if (!name) {
+- LOG(("LibNotify Error: successfully got empty server name!"));
+- return QString();
++ if (notifyReply.isValid()) {
++ _notificationId = notifyReply.value();
++ } else {
++ LOG(("Native notification error: %1").arg(notifyReply.error().message()));
+ }
+
+- auto result = QString::fromUtf8(static_cast<const char*>(name));
+- LOG(("Notifications Server: %1").arg(result));
+-
+- return result;
++ return notifyReply.isValid();
+ }
+
+-auto LibNotifyServerName = QString();
++bool NotificationData::close() {
++ QDBusReply<void> closeReply = _notificationInterface.call("CloseNotification", _notificationId);
+
+-} // namespace
+-#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
+-
+-bool Supported() {
+-#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
+- static auto Checked = false;
+- if (!Checked) {
+- Checked = true;
+- LibNotifyServerName = GetServerName();
++ if (!closeReply.isValid()) {
++ LOG(("Native notification error: %1").arg(closeReply.error().message()));
+ }
+
+- return !LibNotifyServerName.isEmpty();
+-#else
+- return false;
+-#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
++ return closeReply.isValid();
+ }
+
+-std::unique_ptr<Window::Notifications::Manager> Create(Window::Notifications::System *system) {
+-#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
+- if (Global::NativeNotifications() && Supported()) {
+- return std::make_unique<Manager>(system);
+- }
+-#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
+- return nullptr;
+-}
++void NotificationData::setImage(const QString &imagePath) {
++ QString imageKey;
+
+-void Finish() {
+-#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
+- if (Libs::notify_is_initted && Libs::notify_uninit) {
+- if (Libs::notify_is_initted()) {
+- Libs::notify_uninit();
++ if (!_specificationVersion.isNull()) {
++ const auto majorVersion = _specificationVersion.majorVersion();
++ const auto minorVersion = _specificationVersion.majorVersion();
++
++ if ((majorVersion == 1 && minorVersion >= 2) || majorVersion > 1) {
++ imageKey = "image-data";
++ } else if (majorVersion == 1 && minorVersion) {
++ imageKey = "image_data";
++ } else if ((majorVersion == 1 && minorVersion < 1) || majorVersion < 1) {
++ imageKey = "icon_data";
++ } else {
++ LOG(("Native notification error: unknown specification version"));
++ return;
+ }
++ } else {
++ LOG(("Native notification error: specification version is null"));
++ return;
+ }
+-#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
+-}
+
+-#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
+-class Manager::Private {
+-public:
+- using Type = Window::Notifications::CachedUserpics::Type;
+- explicit Private(Type type)
+- : _cachedUserpics(type) {
+- }
++ auto image = QImage(imagePath).convertToFormat(QImage::Format_RGBA8888);
++ QByteArray imageBytes((const char*)image.constBits(), image.sizeInBytes());
+
+- void init(Manager *manager);
++ ImageData imageData;
++ imageData.width = image.width();
++ imageData.height = image.height();
++ imageData.rowStride = image.bytesPerLine();
++ imageData.hasAlpha = true;
++ imageData.bitsPerSample = 8;
++ imageData.channels = 4;
++ imageData.data = imageBytes;
+
+- void showNotification(
+- not_null<PeerData*> peer,
+- MsgId msgId,
+- const QString &title,
+- const QString &subtitle,
+- const QString &msg,
+- bool hideNameAndPhoto,
+- bool hideReplyButton);
+- void clearAll();
+- void clearFromHistory(not_null<History*> history);
+- void clearNotification(PeerId peerId, MsgId msgId);
++ _hints[imageKey] = QVariant::fromValue(imageData);
++}
+
+- bool hasPoorSupport() const {
+- return _poorSupported;
+- }
+- bool hasActionsSupport() const {
+- return _actionsSupported;
++std::vector<QString> NotificationData::getServerInformation() {
++ std::vector<QString> serverInformation;
++ auto serverInformationReply = _notificationInterface.call("GetServerInformation");
++
++ if (serverInformationReply.type() == QDBusMessage::ReplyMessage) {
++ for (const auto &arg : serverInformationReply.arguments()) {
++ if (static_cast<QMetaType::Type>(arg.type()) == QMetaType::QString) {
++ serverInformation.push_back(arg.toString());
++ } else {
++ LOG(("Native notification error: all elements in GetServerInformation should be strings"));
++ }
++ }
++ } else if (serverInformationReply.type() == QDBusMessage::ErrorMessage) {
++ LOG(("Native notification error: %1").arg(QDBusError(serverInformationReply).message()));
++ } else {
++ LOG(("Native notification error: error while getting information about notification daemon"));
+ }
+
+- ~Private();
++ return serverInformation;
++}
+
+-private:
+- QString escapeNotificationText(const QString &text) const;
+- void showNextNotification();
++std::vector<QString> NotificationData::getCapabilities() {
++ QDBusReply<QStringList> capabilitiesReply = _notificationInterface.call("GetCapabilities");
+
+- struct QueuedNotification {
+- PeerData *peer = nullptr;
+- MsgId msgId = 0;
+- QString title;
+- QString body;
+- bool hideNameAndPhoto = false;
+- };
++ if (capabilitiesReply.isValid()) {
++ return capabilitiesReply.value().toVector().toStdVector();
++ } else {
++ LOG(("Native notification error: %1").arg(capabilitiesReply.error().message()));
++ }
+
+- QString _serverName;
+- QStringList _capabilities;
++ return std::vector<QString>();
++}
+
+- using QueuedNotifications = QList<QueuedNotification>;
+- QueuedNotifications _queuedNotifications;
++QVersionNumber NotificationData::parseSpecificationVersion(const std::vector<QString> &serverInformation) {
++ if (serverInformation.size() >= 4) {
++ return QVersionNumber::fromString(serverInformation[3]);
++ } else {
++ LOG(("Native notification error: server information should have 4 elements"));
++ }
+
+- using Notifications = QMap<PeerId, QMap<MsgId, Notification>>;
+- Notifications _notifications;
++ return QVersionNumber();
++}
+
+- Window::Notifications::CachedUserpics _cachedUserpics;
+- bool _actionsSupported = false;
+- bool _markupSupported = false;
+- bool _poorSupported = false;
++void NotificationData::performOnMainQueue(FnMut<void(Manager *manager)> task) {
++ const auto weak = _weak;
++ crl::on_main(weak, [=, task = std::move(task)]() mutable {
++ task(*weak.lock());
++ });
++}
+
+- std::shared_ptr<Manager*> _guarded;
++void NotificationData::notificationClosed(uint id) {
++ if (id == _notificationId) {
++ performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) {
++ manager->clearNotification(peerId, msgId);
++ });
++ }
++}
+
+-};
+-#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
++void NotificationData::notificationClicked(uint id) {
++ if (id == _notificationId) {
++ performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) {
++ manager->notificationActivated(peerId, msgId);
++ });
++ }
++}
+
+-#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
+-void Manager::Private::init(Manager *manager) {
+- _guarded = std::make_shared<Manager*>(manager);
++QDBusArgument &operator<<(QDBusArgument &argument, const NotificationData::ImageData &imageData) {
++ argument.beginStructure();
++ argument << imageData.width << imageData.height << imageData.rowStride << imageData.hasAlpha << imageData.bitsPerSample << imageData.channels << imageData.data;
++ argument.endStructure();
++ return argument;
++}
+
+- if (auto capabilities = Libs::notify_get_server_caps()) {
+- for (auto capability = capabilities; capability; capability = capability->next) {
+- auto capabilityText = QString::fromUtf8(static_cast<const char*>(capability->data));
+- _capabilities.push_back(capabilityText);
+- }
+- Libs::g_list_free_full(capabilities, g_free);
++const QDBusArgument &operator>>(const QDBusArgument &argument, NotificationData::ImageData &imageData) {
++ argument.beginStructure();
++ argument >> imageData.width >> imageData.height >> imageData.rowStride >> imageData.hasAlpha >> imageData.bitsPerSample >> imageData.channels >> imageData.data;;
++ argument.endStructure();
++ return argument;
++}
+
+- LOG(("LibNotify capabilities: %1").arg(_capabilities.join(qstr(", "))));
+- if (_capabilities.contains(qsl("actions"))) {
+- _actionsSupported = true;
+- } else if (_capabilities.contains(qsl("body-markup"))) {
+- _markupSupported = true;
+- }
+- } else {
+- LOG(("LibNotify Error: could not get capabilities!"));
++auto NotificationDaemonRunning = false;
++bool Supported() {
++ static auto Checked = false;
++ if (!Checked) {
++ Checked = true;
++ NotificationDaemonRunning = QDBusInterface(str_const_toString(kService), str_const_toString(kObjectPath), str_const_toString(kInterface)).isValid();
+ }
+
+- // Unity and other Notify OSD users handle desktop notifications
+- // extremely poor, even without the ability to close() them.
+- _serverName = LibNotifyServerName;
+- Assert(!_serverName.isEmpty());
+- if (_serverName == qstr("notify-osd")) {
+-// _poorSupported = true;
+- _actionsSupported = false;
+- }
++ return NotificationDaemonRunning;
+ }
+
+-QString Manager::Private::escapeNotificationText(const QString &text) const {
+- return _markupSupported ? escapeHtml(text) : text;
++std::unique_ptr<Window::Notifications::Manager> Create(Window::Notifications::System *system) {
++ if (Global::NativeNotifications() && Supported()) {
++ return std::make_unique<Manager>(system);
++ }
++ return nullptr;
+ }
+
+ void Manager::Private::showNotification(
+@@ -400,93 +255,43 @@ void Manager::Private::showNotification(
+ const QString &msg,
+ bool hideNameAndPhoto,
+ bool hideReplyButton) {
+- auto titleText = escapeNotificationText(title);
+- auto subtitleText = escapeNotificationText(subtitle);
+- auto msgText = escapeNotificationText(msg);
+- if (_markupSupported && !subtitleText.isEmpty()) {
+- subtitleText = qstr("<b>") + subtitleText + qstr("</b>");
+- }
+- auto bodyText = subtitleText.isEmpty() ? msgText : (subtitleText + '\n' + msgText);
+-
+- QueuedNotification notification;
+- notification.peer = peer;
+- notification.msgId = msgId;
+- notification.title = titleText;
+- notification.body = bodyText;
+- notification.hideNameAndPhoto = hideNameAndPhoto;
+- _queuedNotifications.push_back(notification);
+-
+- showNextNotification();
+-}
+-
+-void Manager::Private::showNextNotification() {
+- // Show only one notification at a time in Unity / Notify OSD.
+- if (_poorSupported) {
+- for (auto b = _notifications.begin(); !_notifications.isEmpty() && b->isEmpty();) {
+- _notifications.erase(b);
+- }
+- if (!_notifications.isEmpty()) {
+- return;
+- }
+- }
+-
+- QueuedNotification data;
+- while (!_queuedNotifications.isEmpty()) {
+- data = _queuedNotifications.front();
+- _queuedNotifications.pop_front();
+- if (data.peer) {
+- break;
+- }
+- }
+- if (!data.peer) {
+- return;
+- }
+-
+- auto peerId = data.peer->id;
+- auto msgId = data.msgId;
+ auto notification = std::make_shared<NotificationData>(
+ _guarded,
+- data.title,
+- data.body,
+- _capabilities,
+- peerId,
++ title,
++ subtitle,
++ msg,
++ peer->id,
+ msgId);
+- if (!notification->valid()) {
+- return;
+- }
+
+- const auto key = data.hideNameAndPhoto
++ const auto key = hideNameAndPhoto
+ ? InMemoryKey()
+- : data.peer->userpicUniqueKey();
+- notification->setImage(_cachedUserpics.get(key, data.peer));
++ :peer->userpicUniqueKey();
++ notification->setImage(_cachedUserpics.get(key, peer));
+
+- auto i = _notifications.find(peerId);
++ auto i = _notifications.find(peer->id);
+ if (i != _notifications.cend()) {
+ auto j = i->find(msgId);
+ if (j != i->cend()) {
+ auto oldNotification = j.value();
+ i->erase(j);
+ oldNotification->close();
+- i = _notifications.find(peerId);
++ i = _notifications.find(peer->id);
+ }
+ }
+ if (i == _notifications.cend()) {
+- i = _notifications.insert(peerId, QMap<MsgId, Notification>());
++ i = _notifications.insert(peer->id, QMap<MsgId, Notification>());
+ }
+- _notifications[peerId].insert(msgId, notification);
++ _notifications[peer->id].insert(msgId, notification);
+ if (!notification->show()) {
+- i = _notifications.find(peerId);
++ i = _notifications.find(peer->id);
+ if (i != _notifications.cend()) {
+ i->remove(msgId);
+ if (i->isEmpty()) _notifications.erase(i);
+ }
+- showNextNotification();
+ }
+ }
+
+ void Manager::Private::clearAll() {
+- _queuedNotifications.clear();
+-
+ auto temp = base::take(_notifications);
+ for_const (auto &notifications, temp) {
+ for_const (auto notification, notifications) {
+@@ -496,14 +301,6 @@ void Manager::Private::clearAll() {
+ }
+
+ void Manager::Private::clearFromHistory(not_null<History*> history) {
+- for (auto i = _queuedNotifications.begin(); i != _queuedNotifications.end();) {
+- if (i->peer == history->peer) {
+- i = _queuedNotifications.erase(i);
+- } else {
+- ++i;
+- }
+- }
+-
+ auto i = _notifications.find(history->peer->id);
+ if (i != _notifications.cend()) {
+ auto temp = base::take(i.value());
+@@ -513,8 +310,6 @@ void Manager::Private::clearFromHistory(not_null<History*> history) {
+ notification->close();
+ }
+ }
+-
+- showNextNotification();
+ }
+
+ void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) {
+@@ -525,8 +320,6 @@ void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) {
+ _notifications.erase(i);
+ }
+ }
+-
+- showNextNotification();
+ }
+
+ Manager::Private::~Private() {
+@@ -534,22 +327,13 @@ Manager::Private::~Private() {
+ }
+
+ Manager::Manager(Window::Notifications::System *system) : NativeManager(system)
+-, _private(std::make_unique<Private>(Private::Type::Rounded)) {
+- _private->init(this);
++, _private(std::make_unique<Private>(this, Private::Type::Rounded)) {
+ }
+
+ void Manager::clearNotification(PeerId peerId, MsgId msgId) {
+ _private->clearNotification(peerId, msgId);
+ }
+
+-bool Manager::hasPoorSupport() const {
+- return _private->hasPoorSupport();
+-}
+-
+-bool Manager::hasActionsSupport() const {
+- return _private->hasActionsSupport();
+-}
+-
+ Manager::~Manager() = default;
+
+ void Manager::doShowNativeNotification(
+@@ -577,7 +361,6 @@ void Manager::doClearAllFast() {
+ void Manager::doClearFromHistory(not_null<History*> history) {
+ _private->clearFromHistory(history);
+ }
+-#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
+
+ } // namespace Notifications
+ } // namespace Platform
+diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h
+index f40204a56..1f87dbe28 100644
+--- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h
++++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h
+@@ -8,6 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+ #pragma once
+
+ #include "platform/platform_notifications_manager.h"
++#include "window/notifications_utilities.h"
++
++#include <QtDBus/QDBusInterface>
++#include <QtDBus/QDBusArgument>
++#include <QtDBus/QDBusMetaType>
+
+ namespace Platform {
+ namespace Notifications {
+@@ -23,16 +28,62 @@ inline bool SkipToast() {
+ inline void FlashBounce() {
+ }
+
+-void Finish();
++class NotificationData : public QObject {
++ Q_OBJECT
++
++public:
++ NotificationData(const std::shared_ptr<Manager*> &guarded, const QString &title, const QString &subtitle, const QString &msg, PeerId peerId, MsgId msgId);
++
++ NotificationData(const NotificationData &other) = delete;
++ NotificationData &operator=(const NotificationData &other) = delete;
++ NotificationData(NotificationData &&other) = delete;
++ NotificationData &operator=(NotificationData &&other) = delete;
++
++ bool show();
++ bool close();
++ void setImage(const QString &imagePath);
++
++ struct ImageData {
++ int width, height, rowStride;
++ bool hasAlpha;
++ int bitsPerSample, channels;
++ QByteArray data;
++ };
++
++private:
++ QDBusInterface _notificationInterface;
++ QVersionNumber _specificationVersion;
++ std::weak_ptr<Manager*> _weak;
++
++ QString _title;
++ QString _body;
++ QStringList _actions;
++ QVariantMap _hints;
++
++ uint _notificationId;
++ PeerId _peerId;
++ MsgId _msgId;
++
++ std::vector<QString> getServerInformation();
++ std::vector<QString> getCapabilities();
++ QVersionNumber parseSpecificationVersion(const std::vector<QString> &serverInformation);
++
++ void performOnMainQueue(FnMut<void(Manager *manager)> task);
++
++private slots:
++ void notificationClosed(uint id);
++ void notificationClicked(uint id);
++};
++
++using Notification = std::shared_ptr<NotificationData>;
++
++QDBusArgument &operator<<(QDBusArgument &argument, const NotificationData::ImageData &imageData);
++const QDBusArgument &operator>>(const QDBusArgument &argument, NotificationData::ImageData &imageData);
+
+ class Manager : public Window::Notifications::NativeManager {
+ public:
+ Manager(Window::Notifications::System *system);
+-
+ void clearNotification(PeerId peerId, MsgId msgId);
+- bool hasPoorSupport() const;
+- bool hasActionsSupport() const;
+-
+ ~Manager();
+
+ protected:
+@@ -53,5 +104,38 @@ private:
+
+ };
+
++class Manager::Private {
++public:
++ using Type = Window::Notifications::CachedUserpics::Type;
++ explicit Private(Manager *manager, Type type)
++ : _cachedUserpics(type)
++ , _guarded(std::make_shared<Manager*>(manager)) {
++ qDBusRegisterMetaType<NotificationData::ImageData>();
++ }
++
++ void showNotification(
++ not_null<PeerData*> peer,
++ MsgId msgId,
++ const QString &title,
++ const QString &subtitle,
++ const QString &msg,
++ bool hideNameAndPhoto,
++ bool hideReplyButton);
++ void clearAll();
++ void clearFromHistory(not_null<History*> history);
++ void clearNotification(PeerId peerId, MsgId msgId);
++
++ ~Private();
++
++private:
++ using Notifications = QMap<PeerId, QMap<MsgId, Notification>>;
++ Notifications _notifications;
++
++ Window::Notifications::CachedUserpics _cachedUserpics;
++ std::shared_ptr<Manager*> _guarded;
++};
++
+ } // namespace Notifications
+ } // namespace Platform
++
++Q_DECLARE_METATYPE(Platform::Notifications::NotificationData::ImageData)
+diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
+index 68d07a55b..cc70426a1 100644
+--- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp
++++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
+@@ -229,7 +229,6 @@ void start() {
+ }
+
+ void finish() {
+- Notifications::Finish();
+ }
+
+ void RegisterCustomScheme() {
+diff --git a/Telegram/gyp/telegram/sources.txt b/Telegram/gyp/telegram/sources.txt
+index e3ccb66db..1977e6f2f 100644
+--- a/Telegram/gyp/telegram/sources.txt
++++ b/Telegram/gyp/telegram/sources.txt
+@@ -605,8 +605,6 @@
+ <(src_loc)/platform/linux/linux_desktop_environment.h
+ <(src_loc)/platform/linux/linux_gdk_helper.cpp
+ <(src_loc)/platform/linux/linux_gdk_helper.h
+-<(src_loc)/platform/linux/linux_libnotify.cpp
+-<(src_loc)/platform/linux/linux_libnotify.h
+ <(src_loc)/platform/linux/linux_libs.cpp
+ <(src_loc)/platform/linux/linux_libs.h
+ <(src_loc)/platform/linux/file_utilities_linux.cpp