From 49736cd879db4283043e901b89ba132af385b296 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 9 Jan 2021 06:10:06 +0400 Subject: [PATCH] Recreate notification manager on notification service owner change --- Telegram/CMakeLists.txt | 4 + .../linux_notification_service_watcher.cpp | 37 ++++ .../linux_notification_service_watcher.h | 24 +++ .../linux/notifications_manager_linux.cpp | 180 ++++++++---------- .../platform/linux/specific_linux.cpp | 26 +++ .../platform/linux/specific_linux.h | 1 + 6 files changed, 172 insertions(+), 100 deletions(-) create mode 100644 Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp create mode 100644 Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 9d8dcff78d..f5f02c7b91 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -823,6 +823,8 @@ PRIVATE platform/linux/linux_gsd_media_keys.h platform/linux/linux_libs.cpp platform/linux/linux_libs.h + platform/linux/linux_notification_service_watcher.cpp + platform/linux/linux_notification_service_watcher.h platform/linux/linux_wayland_integration.cpp platform/linux/linux_wayland_integration.h platform/linux/linux_xlib_helper.cpp @@ -1116,6 +1118,8 @@ if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION) remove_target_sources(Telegram ${src_loc} platform/linux/linux_gsd_media_keys.cpp platform/linux/linux_gsd_media_keys.h + platform/linux/linux_notification_service_watcher.cpp + platform/linux/linux_notification_service_watcher.h platform/linux/notifications_manager_linux.cpp ) diff --git a/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp b/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp new file mode 100644 index 0000000000..1aa0fb9698 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.cpp @@ -0,0 +1,37 @@ +/* +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_notification_service_watcher.h" + +#include "core/application.h" +#include "main/main_domain.h" +#include "window/notifications_manager.h" + +#include + +namespace Platform { +namespace internal { + +NotificationServiceWatcher::NotificationServiceWatcher() +: _dbusWatcher( + qsl("org.freedesktop.Notifications"), + QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForOwnerChange) { + const auto signal = &QDBusServiceWatcher::serviceOwnerChanged; + QObject::connect(&_dbusWatcher, signal, [=] { + crl::on_main([=] { + if (!Core::App().domain().started()) { + return; + } + + Core::App().notifications().createManager(); + }); + }); +} + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.h b/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.h new file mode 100644 index 0000000000..50295d0892 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_notification_service_watcher.h @@ -0,0 +1,24 @@ +/* +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 + +#include + +namespace Platform { +namespace internal { + +class NotificationServiceWatcher { +public: + NotificationServiceWatcher(); + +private: + QDBusServiceWatcher _dbusWatcher; +}; + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 771db12f6f..931cdafd81 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include +#include #include #include #include @@ -41,112 +42,89 @@ constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs; constexpr auto kImageDataType = "(iiibii@ay)"_cs; constexpr auto kNotifyArgsType = "(susssasa{sv}i)"_cs; -bool NotificationsSupported = false; +bool ServiceRegistered = false; bool InhibitedNotSupported = false; +std::vector CurrentServerInformation; +QStringList CurrentCapabilities; -void ComputeSupported(bool wait = false) { - const auto message = QDBusMessage::createMethodCall( - kService.utf16(), - kObjectPath.utf16(), - kInterface.utf16(), - qsl("GetServerInformation")); +bool GetServiceRegistered() { + const auto interface = QDBusConnection::sessionBus().interface(); + const auto activatable = IsNotificationServiceActivatable(); - auto async = QDBusConnection::sessionBus().asyncCall(message); - auto watcher = new QDBusPendingCallWatcher(async); - - QObject::connect( - watcher, - &QDBusPendingCallWatcher::finished, - [=](QDBusPendingCallWatcher *call) { - QDBusPendingReply< - QString, - QString, - QString, - QString> reply = *call; - - if (reply.isValid()) { - NotificationsSupported = true; - } - - call->deleteLater(); - }); - - if (wait) { - watcher->waitForFinished(); - } -} - -void GetSupported() { - static auto Checked = false; - if (Checked) { - return; - } - Checked = true; - - if (Core::App().settings().nativeNotifications() && !IsWayland()) { - ComputeSupported(true); - } else { - ComputeSupported(); - } -} - -std::vector ComputeServerInformation() { - std::vector serverInformation; - - const auto message = QDBusMessage::createMethodCall( - kService.utf16(), - kObjectPath.utf16(), - kInterface.utf16(), - qsl("GetServerInformation")); - - const auto reply = QDBusConnection::sessionBus().call(message); - - if (reply.type() == QDBusMessage::ReplyMessage) { - ranges::transform( - reply.arguments(), - ranges::back_inserter(serverInformation), - &QVariant::toString - ); - } else if (reply.type() == QDBusMessage::ErrorMessage) { - LOG(("Native notification error: %1").arg(reply.errorMessage())); - } else { - LOG(("Native notification error: " - "invalid reply from GetServerInformation")); - } - - return serverInformation; + return interface + ? interface->isServiceRegistered(kService.utf16()) || activatable + : activatable; } std::vector GetServerInformation() { - static const auto Result = ComputeServerInformation(); - return Result; + std::vector result; + + const auto message = QDBusMessage::createMethodCall( + kService.utf16(), + kObjectPath.utf16(), + kInterface.utf16(), + qsl("GetServerInformation")); + + // We may be launched earlier than notification daemon + while (true) { + const auto reply = QDBusConnection::sessionBus().call(message); + + if (reply.type() == QDBusMessage::ReplyMessage) { + ranges::transform( + reply.arguments(), + ranges::back_inserter(result), + &QVariant::toString); + } else if (reply.type() == QDBusMessage::ErrorMessage) { + LOG(("Native notification error: %1").arg(reply.errorMessage())); + + if (reply.errorName() + == qsl("org.freedesktop.DBus.Error.NoReply")) { + continue; + } + } else { + LOG(("Native notification error: " + "invalid reply from GetServerInformation")); + } + + break; + } + + return result; } -QStringList ComputeCapabilities() { +QStringList GetCapabilities() { const auto message = QDBusMessage::createMethodCall( kService.utf16(), kObjectPath.utf16(), kInterface.utf16(), qsl("GetCapabilities")); - const QDBusReply reply = QDBusConnection::sessionBus().call( - message); + // We may be launched earlier than notification daemon + while (true) { + const QDBusReply reply = QDBusConnection::sessionBus() + .call(message); + + if (reply.isValid()) { + return reply.value(); + } - if (reply.isValid()) { - return reply.value(); - } else { LOG(("Native notification error: %1").arg(reply.error().message())); + + if (reply.error().type() != QDBusError::NoReply) { + break; + } } return {}; } -QStringList GetCapabilities() { - static const auto Result = ComputeCapabilities(); - return Result; -} - bool Inhibited() { + if (!Supported() + || !CurrentCapabilities.contains(qsl("inhibitions")) + || InhibitedNotSupported) { + return false; + } + auto message = QDBusMessage::createMethodCall( kService.utf16(), kObjectPath.utf16(), @@ -277,8 +255,7 @@ NotificationData::NotificationData( bool hideReplyButton) : _manager(manager) , _title(title) -, _imageKey(GetImageKey(ParseSpecificationVersion( - GetServerInformation()))) +, _imageKey(GetImageKey(ParseSpecificationVersion(CurrentServerInformation))) , _id(id) { GError *error = nullptr; @@ -293,7 +270,7 @@ NotificationData::NotificationData( return; } - const auto capabilities = GetCapabilities(); + const auto capabilities = CurrentCapabilities; if (capabilities.contains(qsl("body-markup"))) { _body = subtitle.isEmpty() @@ -613,30 +590,33 @@ void NotificationData::notificationReplied(uint id, const QString &text) { } // namespace bool SkipAudio() { - if (Supported() - && GetCapabilities().contains(qsl("inhibitions")) - && !InhibitedNotSupported) { - return Inhibited(); - } - - return false; + return Inhibited(); } bool SkipToast() { - return SkipAudio(); + return Inhibited(); } bool SkipFlashBounce() { - return SkipAudio(); + return Inhibited(); } bool Supported() { - return NotificationsSupported; + return ServiceRegistered; } std::unique_ptr Create( Window::Notifications::System *system) { - GetSupported(); + ServiceRegistered = GetServiceRegistered(); + InhibitedNotSupported = false; + + if (Supported()) { + CurrentServerInformation = GetServerInformation(); + CurrentCapabilities = GetCapabilities(); + } else { + CurrentServerInformation = {}; + CurrentCapabilities = QStringList{}; + } if ((Core::App().settings().nativeNotifications() && Supported()) || IsWayland()) { @@ -683,8 +663,8 @@ Manager::Private::Private(not_null manager, Type type) return; } - const auto serverInformation = GetServerInformation(); - const auto capabilities = GetCapabilities(); + const auto serverInformation = CurrentServerInformation; + const auto capabilities = CurrentCapabilities; if (!serverInformation.empty()) { LOG(("Notification daemon product name: %1") diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 5eac9bd28d..251d5db7b4 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +#include "platform/linux/linux_notification_service_watcher.h" #include "platform/linux/linux_gsd_media_keys.h" #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION @@ -87,6 +88,8 @@ constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs; QStringList PlatformThemes; #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +std::unique_ptr NSWInstance; + QStringList ListDBusActivatableNames() { static const auto Result = [&] { const auto message = QDBusMessage::createMethodCall( @@ -632,6 +635,17 @@ bool CanOpenDirectoryWithPortal() { return false; } +bool IsNotificationServiceActivatable() { +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + static const auto Result = ListDBusActivatableNames().contains( + qsl("org.freedesktop.Notifications")); + + return Result; +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION + + return false; +} + QString AppRuntimeDirectory() { static const auto Result = [&] { auto runtimeDir = QStandardPaths::writableLocation( @@ -1231,9 +1245,21 @@ void start() { if (const auto waylandIntegration = WaylandIntegration::Instance()) { waylandIntegration->waitForInterfaceAnnounce(); } + +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + if (!IsNotificationServiceActivatable()) { + NSWInstance = std::make_unique< + internal::NotificationServiceWatcher>(); + } +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION } void finish() { +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + if (NSWInstance) { + NSWInstance = nullptr; + } +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION } } // namespace ThirdParty diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h index 80584a23de..2f279c5d4e 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.h +++ b/Telegram/SourceFiles/platform/linux/specific_linux.h @@ -25,6 +25,7 @@ bool UseGtkIntegration(); bool IsGtkIntegrationForced(); bool UseXDGDesktopPortal(); bool CanOpenDirectoryWithPortal(); +bool IsNotificationServiceActivatable(); QString AppRuntimeDirectory(); QString GetLauncherBasename();