LibNotify Linux notifications support added, testing.

This commit is contained in:
John Preston 2016-10-03 11:56:03 +03:00
parent 2d1d62a953
commit 6db52f7fa9
13 changed files with 664 additions and 118 deletions

View File

@ -292,6 +292,10 @@ static const int32 ApiId = 17349;
static const char *ApiHash = "344583e45741c457fe1862106095a5eb";
#endif
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
#error "Only little endian is supported!"
#endif // Q_BYTE_ORDER == Q_BIG_ENDIAN
#ifndef BETA_VERSION_MACRO
#error "Beta version macro is not defined."
#endif

View File

@ -0,0 +1,118 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#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
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)) {
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);
}
} // namespace Libs
} // namespace Platform

View File

@ -0,0 +1,131 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
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

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "platform/linux/linux_libs.h"
#include "platform/linux/linux_gdk_helper.h"
#include "platform/linux/linux_libnotify.h"
namespace Platform {
namespace Libs {
@ -175,6 +176,7 @@ f_app_indicator_set_menu app_indicator_set_menu = nullptr;
f_app_indicator_set_icon_full app_indicator_set_icon_full = nullptr;
f_gdk_init_check gdk_init_check = nullptr;
f_gdk_pixbuf_new_from_data gdk_pixbuf_new_from_data = nullptr;
f_gdk_pixbuf_new_from_file gdk_pixbuf_new_from_file = nullptr;
f_gtk_status_icon_new_from_pixbuf gtk_status_icon_new_from_pixbuf = nullptr;
f_gtk_status_icon_set_from_pixbuf gtk_status_icon_set_from_pixbuf = nullptr;
f_gtk_status_icon_new_from_file gtk_status_icon_new_from_file = nullptr;
@ -233,6 +235,7 @@ void start() {
if (gtkLoaded) {
load(lib_gtk, "gdk_init_check", gdk_init_check);
load(lib_gtk, "gdk_pixbuf_new_from_data", gdk_pixbuf_new_from_data);
load(lib_gtk, "gdk_pixbuf_new_from_file", gdk_pixbuf_new_from_file);
load(lib_gtk, "gtk_status_icon_new_from_pixbuf", gtk_status_icon_new_from_pixbuf);
load(lib_gtk, "gtk_status_icon_set_from_pixbuf", gtk_status_icon_set_from_pixbuf);
load(lib_gtk, "gtk_status_icon_new_from_file", gtk_status_icon_new_from_file);
@ -266,6 +269,10 @@ void start() {
load(lib_unity, "unity_launcher_entry_set_count_visible", unity_launcher_entry_set_count_visible);
}
#endif // TDESKTOP_DISABLE_UNITY_INTEGRATION
if (gtkLoaded) {
startLibNotify();
}
}
} // namespace Libs

View File

@ -273,6 +273,9 @@ extern f_gdk_init_check gdk_init_check;
typedef GdkPixbuf* (*f_gdk_pixbuf_new_from_data)(const guchar *data, GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height, int rowstride, GdkPixbufDestroyNotify destroy_fn, gpointer destroy_fn_data);
extern f_gdk_pixbuf_new_from_data gdk_pixbuf_new_from_data;
typedef GdkPixbuf* (*f_gdk_pixbuf_new_from_file)(const gchar *filename, GError **error);
extern f_gdk_pixbuf_new_from_file gdk_pixbuf_new_from_file;
typedef GtkStatusIcon* (*f_gtk_status_icon_new_from_pixbuf)(GdkPixbuf *pixbuf);
extern f_gtk_status_icon_new_from_pixbuf gtk_status_icon_new_from_pixbuf;

View File

@ -63,22 +63,11 @@ gboolean _trayIconResized(GtkStatusIcon *status_icon, gint size, gpointer popup_
return FALSE;
}
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
#define QT_RED 3
#define QT_GREEN 2
#define QT_BLUE 1
#define QT_ALPHA 0
#else
#define QT_RED 0
#define QT_GREEN 1
#define QT_BLUE 2
#define QT_ALPHA 3
#endif
#define GTK_RED 2
#define GTK_GREEN 1
#define GTK_BLUE 0

View File

@ -21,17 +21,210 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "platform/linux/notifications_manager_linux.h"
#include "window/notifications_utilities.h"
#include "platform/linux/linux_libnotify.h"
#include "platform/linux/linux_libs.h"
namespace Platform {
namespace Notifications {
namespace {
void start() {
NeverFreedPointer<Manager> ManagerInstance;
bool supported() {
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_unref != nullptr)
// && (Libs::gdk_pixbuf_new_from_data != nullptr)
&& (Libs::gdk_pixbuf_new_from_file != nullptr);
}
Window::Notifications::Manager *manager() {
return nullptr;
} // namespace
void start() {
if (supported()) {
if (Libs::notify_is_initted() || Libs::notify_init("Telegram Desktop")) {
ManagerInstance.makeIfNull();
}
}
}
Manager *manager() {
return ManagerInstance.data();
}
void finish() {
if (manager()) {
ManagerInstance.reset();
Libs::notify_uninit();
}
}
class Manager::Impl {
public:
void showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, bool showUserpic, const QString &msg, bool showReplyButton);
void clearAll();
void clearFromHistory(History *history);
private:
static void notificationClosed(Libs::NotifyNotification *notification, gpointer data);
void clearNotification(Libs::NotifyNotification *notification);
using Notifications = QMap<PeerId, QMap<MsgId, Libs::NotifyNotification*>>;
Notifications _notifications;
using NotificationsData = QMap<Libs::NotifyNotification*, QPair<PeerId, MsgId>>;
NotificationsData _notificationsData;
Window::Notifications::CachedUserpics _cachedUserpics;
};
void Manager::Impl::notificationClosed(Libs::NotifyNotification *notification, gpointer data) {
if (auto manager = ManagerInstance.data()) {
manager->_impl->clearNotification(notification);
}
}
void Manager::Impl::clearNotification(Libs::NotifyNotification *notification) {
auto dataIt = _notificationsData.find(notification);
if (dataIt == _notificationsData.cend()) {
return;
}
auto peerId = dataIt->first;
auto msgId = dataIt->second;
_notificationsData.erase(dataIt);
auto i = _notifications.find(peerId);
if (i != _notifications.cend()) {
auto it = i.value().find(msgId);
if (it != i.value().cend()) {
Libs::g_object_unref(Libs::g_object_cast(it.value()));
i.value().erase(it);
if (i.value().isEmpty()) {
_notifications.erase(i);
}
}
}
}
void Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, bool showUserpic, const QString &msg, bool showReplyButton) {
auto titleUtf8 = title.toUtf8();
auto bodyUtf8 = (subtitle.isEmpty() ? msg : ("<b>" + subtitle + "</b>\n" + msg)).toUtf8();
auto notification = Libs::notify_notification_new(titleUtf8.constData(), bodyUtf8.constData(), nullptr);
if (!notification) {
return;
}
Libs::g_signal_connect_helper(Libs::g_object_cast(notification), "closed", G_CALLBACK(Manager::Impl::notificationClosed), peer);
StorageKey key;
if (showUserpic) {
key = peer->userpicUniqueKey();
} else {
key = StorageKey(0, 0);
}
auto userpicPath = _cachedUserpics.get(key, peer);
auto userpicPathNative = QFile::encodeName(userpicPath);
if (auto pixbuf = Libs::gdk_pixbuf_new_from_file(userpicPathNative.constData(), nullptr)) {
Libs::notify_notification_set_image_from_pixbuf(notification, pixbuf);
Libs::g_object_unref(Libs::g_object_cast(pixbuf));
}
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);
_notificationsData.remove(oldNotification);
Libs::notify_notification_close(oldNotification, nullptr);
Libs::g_object_unref(Libs::g_object_cast(oldNotification));
i = _notifications.find(peer->id);
}
}
if (i == _notifications.cend()) {
i = _notifications.insert(peer->id, QMap<MsgId, Libs::NotifyNotification*>());
}
auto result = Libs::notify_notification_show(notification, nullptr);
if (!result) {
Libs::g_object_unref(Libs::g_object_cast(notification));
i = _notifications.find(peer->id);
if (i != _notifications.cend() && i->isEmpty()) _notifications.erase(i);
return;
}
_notifications[peer->id].insert(msgId, notification);
_notificationsData.insert(notification, qMakePair(peer->id, msgId));
}
void Manager::Impl::clearAll() {
_notificationsData.clear();
auto temp = createAndSwap(_notifications);
for_const (auto &notifications, temp) {
for_const (auto notification, notifications) {
Libs::notify_notification_close(notification, nullptr);
Libs::g_object_unref(Libs::g_object_cast(notification));
}
}
}
void Manager::Impl::clearFromHistory(History *history) {
auto i = _notifications.find(history->peer->id);
if (i != _notifications.cend()) {
auto temp = createAndSwap(i.value());
_notifications.erase(i);
for_const (auto notification, temp) {
_notificationsData.remove(notification);
Libs::notify_notification_close(notification, nullptr);
Libs::g_object_unref(Libs::g_object_cast(notification));
}
}
}
Manager::Manager() : _impl(std_::make_unique<Impl>()) {
}
Manager::~Manager() = default;
void Manager::doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, bool showUserpic, const QString &msg, bool showReplyButton) {
_impl->showNotification(peer, msgId, title, subtitle, showUserpic, msg, showReplyButton);
}
void Manager::doClearAllFast() {
_impl->clearAll();
}
void Manager::doClearFromHistory(History *history) {
_impl->clearFromHistory(history);
}
} // namespace Notifications

View File

@ -25,12 +25,31 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Platform {
namespace Notifications {
class Manager;
void start();
Window::Notifications::Manager *manager();
Manager *manager();
void finish();
inline void defaultNotificationShown(QWidget *widget) {
}
class Manager : public Window::Notifications::NativeManager {
public:
Manager();
~Manager();
protected:
void doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, bool showUserpic, const QString &msg, bool showReplyButton) override;
void doClearAllFast() override;
void doClearFromHistory(History *history) override;
private:
class Impl;
friend class Impl;
std_::unique_ptr<Impl> _impl;
};
} // namespace Notifications
} // namespace Platform

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "platform/win/notifications_manager_win.h"
#include "window/notifications_utilities.h"
#include "platform/win/windows_app_user_model_id.h"
#include "platform/win/windows_dlls.h"
#include "mainwindow.h"
@ -47,9 +48,6 @@ namespace Platform {
namespace Notifications {
namespace {
// Delete notify photo file after 1 minute of not using.
constexpr int kNotifyDeletePhotoAfterMs = 60000;
NeverFreedPointer<Manager> ManagerInstance;
ComPtr<IToastNotificationManagerStatics> _notificationManager;
@ -65,13 +63,6 @@ struct NotificationPtr {
};
using Notifications = QMap<PeerId, QMap<MsgId, NotificationPtr>>;
Notifications _notifications;
struct Image {
uint64 until;
QString path;
};
using Images = QMap<StorageKey, Image>;
Images _images;
bool _imageSavedFlag = false;
class StringReferenceWrapper {
public:
@ -155,7 +146,6 @@ bool init() {
if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &_notificationFactory))) {
return false;
}
QDir().mkpath(cWorkingDir() + qsl("tdata/temp"));
return true;
}
@ -322,38 +312,6 @@ private:
};
QString getImage(const StorageKey &key, PeerData *peer) {
uint64 ms = getms(true);
auto i = _images.find(key);
if (i != _images.cend()) {
if (i->until) {
i->until = ms + kNotifyDeletePhotoAfterMs;
if (auto manager = ManagerInstance.data()) {
manager->clearPhotosInMs(-kNotifyDeletePhotoAfterMs);
}
}
} else {
Image v;
if (key.first) {
v.until = ms + kNotifyDeletePhotoAfterMs;
if (auto manager = ManagerInstance.data()) {
manager->clearPhotosInMs(-kNotifyDeletePhotoAfterMs);
}
} else {
v.until = 0;
}
v.path = cWorkingDir() + qsl("tdata/temp/") + QString::number(rand_value<uint64>(), 16) + qsl(".png");
if (key.first || key.second) {
peer->saveUserpic(v.path, st::notifyMacPhotoSize);
} else {
App::wnd()->iconLarge().save(v.path, "PNG");
}
i = _images.insert(key, v);
_imageSavedFlag = true;
}
return i->path;
}
} // namespace
void start() {
@ -377,28 +335,6 @@ void finish() {
ManagerInstance.clear();
}
uint64 clearImages(uint64 ms) {
uint64 result = 0;
for (auto i = _images.begin(); i != _images.end();) {
if (!i->until) {
++i;
continue;
}
if (i->until <= ms) {
QFile(i->path).remove();
i = _images.erase(i);
} else {
if (!result) {
result = i->until;
} else {
accumulate_min(result, i->until);
}
++i;
}
}
return result;
}
class Manager::Impl {
public:
bool showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, bool showUserpic, const QString &msg, bool showReplyButton);
@ -411,8 +347,7 @@ public:
~Impl();
private:
QTimer _clearPhotosTimer;
friend class Manager;
Window::Notifications::CachedUserpics _cachedUserpics;
};
@ -421,10 +356,6 @@ Manager::Impl::~Impl() {
if (_notificationManager) _notificationManager.Reset();
if (_notifier) _notifier.Reset();
if (_notificationFactory) _notificationFactory.Reset();
if (_imageSavedFlag) {
psDeleteDir(cWorkingDir() + qsl("tdata/temp"));
}
}
void Manager::Impl::clearAll() {
@ -446,8 +377,8 @@ void Manager::Impl::clearFromHistory(History *history) {
auto temp = createAndSwap(i.value());
_notifications.erase(i);
for (auto j = temp.cbegin(), e = temp.cend(); j != e; ++j) {
_notifier->Hide(j->p.Get());
for_const (auto &notification, temp) {
_notifier->Hide(notification.p.Get());
}
}
}
@ -485,16 +416,15 @@ bool Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString
if (!SUCCEEDED(hr)) return false;
StorageKey key;
QString imagePath;
if (showUserpic) {
key = peer->userpicUniqueKey();
} else {
key = StorageKey(0, 0);
}
QString image = getImage(key, peer);
std::wstring wimage = QDir::toNativeSeparators(image).toStdWString();
auto userpicPath = _cachedUserpics.get(key, peer);
auto userpicPathWide = QDir::toNativeSeparators(userpicPath).toStdWString();
hr = SetImageSrc(wimage.c_str(), toastXml.Get());
hr = SetImageSrc(userpicPathWide.c_str(), toastXml.Get());
if (!SUCCEEDED(hr)) return false;
ComPtr<IXmlNodeList> nodeList;
@ -576,31 +506,12 @@ bool Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString
}
Manager::Manager() : _impl(std_::make_unique<Impl>()) {
connect(&_impl->_clearPhotosTimer, SIGNAL(timeout()), this, SLOT(onClearPhotos()));
}
void Manager::clearPhotosInMs(int ms) {
if (ms < 0) {
ms = -ms;
if (_impl->_clearPhotosTimer.isActive() && _impl->_clearPhotosTimer.remainingTime() <= ms) {
return;
}
}
_impl->_clearPhotosTimer.start(ms);
}
void Manager::clearNotification(PeerId peerId, MsgId msgId) {
_impl->clearNotification(peerId, msgId);
}
void Manager::onClearPhotos() {
auto ms = getms(true);
auto minuntil = clearImages(ms);
if (minuntil) {
clearPhotosInMs(int32(minuntil - ms));
}
}
Manager::~Manager() = default;
void Manager::doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, bool showUserpic, const QString &msg, bool showReplyButton) {

View File

@ -35,20 +35,14 @@ void finish();
inline void defaultNotificationShown(QWidget *widget) {
}
class Manager : public QObject, public Window::Notifications::NativeManager {
Q_OBJECT
class Manager : public Window::Notifications::NativeManager {
public:
Manager();
void clearPhotosInMs(int ms);
void clearNotification(PeerId peerId, MsgId msgId);
~Manager();
private slots:
void onClearPhotos();
protected:
void doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, bool showUserpic, const QString &msg, bool showReplyButton) override;
void doClearAllFast() override;

View File

@ -0,0 +1,116 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "window/notifications_utilities.h"
#include "pspecific.h"
#include "mainwindow.h"
namespace Window {
namespace Notifications {
namespace {
// Delete notify photo file after 1 minute of not using.
constexpr int kNotifyDeletePhotoAfterMs = 60000;
} // namespace
CachedUserpics::CachedUserpics() {
connect(&_clearTimer, SIGNAL(timeout()), this, SLOT(onClear()));
QDir().mkpath(cWorkingDir() + qsl("tdata/temp"));
}
QString CachedUserpics::get(const StorageKey &key, PeerData *peer) {
uint64 ms = getms(true);
auto i = _images.find(key);
if (i != _images.cend()) {
if (i->until) {
i->until = ms + kNotifyDeletePhotoAfterMs;
clearInMs(-kNotifyDeletePhotoAfterMs);
}
} else {
Image v;
if (key.first) {
v.until = ms + kNotifyDeletePhotoAfterMs;
clearInMs(-kNotifyDeletePhotoAfterMs);
} else {
v.until = 0;
}
v.path = cWorkingDir() + qsl("tdata/temp/") + QString::number(rand_value<uint64>(), 16) + qsl(".png");
if (key.first || key.second) {
peer->saveUserpic(v.path, st::notifyMacPhotoSize);
} else {
App::wnd()->iconLarge().save(v.path, "PNG");
}
i = _images.insert(key, v);
_someSavedFlag = true;
}
return i->path;
}
uint64 CachedUserpics::clear(uint64 ms) {
uint64 result = 0;
for (auto i = _images.begin(); i != _images.end();) {
if (!i->until) {
++i;
continue;
}
if (i->until <= ms) {
QFile(i->path).remove();
i = _images.erase(i);
} else {
if (!result) {
result = i->until;
} else {
accumulate_min(result, i->until);
}
++i;
}
}
return result;
}
void CachedUserpics::clearInMs(int ms) {
if (ms < 0) {
ms = -ms;
if (_clearTimer.isActive() && _clearTimer.remainingTime() <= ms) {
return;
}
}
_clearTimer.start(ms);
}
void CachedUserpics::onClear() {
auto ms = getms(true);
auto minuntil = clear(ms);
if (minuntil) {
clearInMs(int(minuntil - ms));
}
}
CachedUserpics::~CachedUserpics() {
if (_someSavedFlag) {
psDeleteDir(cWorkingDir() + qsl("tdata/temp"));
}
}
} // namespace Notifications
} // namespace Window

View File

@ -0,0 +1,57 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "window/notifications_manager.h"
namespace Window {
namespace Notifications {
class CachedUserpics : public QObject {
Q_OBJECT
public:
CachedUserpics();
QString get(const StorageKey &key, PeerData *peer);
~CachedUserpics();
private slots:
void onClear();
private:
void clearInMs(int ms);
uint64 clear(uint64 ms);
struct Image {
uint64 until;
QString path;
};
using Images = QMap<StorageKey, Image>;
Images _images;
bool _someSavedFlag = false;
SingleTimer _clearTimer;
};
} // namesapce Notifications
} // namespace Window

View File

@ -318,6 +318,8 @@
'<(src_loc)/pspecific_linux.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_dialog_linux.cpp',
@ -486,6 +488,8 @@
'<(src_loc)/window/notifications_manager.h',
'<(src_loc)/window/notifications_manager_default.cpp',
'<(src_loc)/window/notifications_manager_default.h',
'<(src_loc)/window/notifications_utilities.cpp',
'<(src_loc)/window/notifications_utilities.h',
'<(src_loc)/window/section_widget.cpp',
'<(src_loc)/window/section_widget.h',
'<(src_loc)/window/slide_animation.cpp',