2
0
mirror of https://github.com/kotatogram/kotatogram-desktop synced 2025-08-31 14:45:14 +00:00

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

@@ -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