Recreate notification manager on notification service owner change

This commit is contained in:
Ilya Fedin 2021-01-09 06:10:06 +04:00 committed by John Preston
parent e55581e0c9
commit 49736cd879
6 changed files with 172 additions and 100 deletions

View File

@ -823,6 +823,8 @@ PRIVATE
platform/linux/linux_gsd_media_keys.h platform/linux/linux_gsd_media_keys.h
platform/linux/linux_libs.cpp platform/linux/linux_libs.cpp
platform/linux/linux_libs.h 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.cpp
platform/linux/linux_wayland_integration.h platform/linux/linux_wayland_integration.h
platform/linux/linux_xlib_helper.cpp platform/linux/linux_xlib_helper.cpp
@ -1116,6 +1118,8 @@ if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
remove_target_sources(Telegram ${src_loc} remove_target_sources(Telegram ${src_loc}
platform/linux/linux_gsd_media_keys.cpp platform/linux/linux_gsd_media_keys.cpp
platform/linux/linux_gsd_media_keys.h 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 platform/linux/notifications_manager_linux.cpp
) )

View File

@ -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 <QtDBus/QDBusConnection>
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

View File

@ -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 <QtDBus/QDBusServiceWatcher>
namespace Platform {
namespace internal {
class NotificationServiceWatcher {
public:
NotificationServiceWatcher();
private:
QDBusServiceWatcher _dbusWatcher;
};
} // namespace internal
} // namespace Platform

View File

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QVersionNumber> #include <QtCore/QVersionNumber>
#include <QtDBus/QDBusConnection> #include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusConnectionInterface>
#include <QtDBus/QDBusMessage> #include <QtDBus/QDBusMessage>
#include <QtDBus/QDBusReply> #include <QtDBus/QDBusReply>
#include <QtDBus/QDBusError> #include <QtDBus/QDBusError>
@ -41,112 +42,89 @@ constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
constexpr auto kImageDataType = "(iiibii@ay)"_cs; constexpr auto kImageDataType = "(iiibii@ay)"_cs;
constexpr auto kNotifyArgsType = "(susssasa{sv}i)"_cs; constexpr auto kNotifyArgsType = "(susssasa{sv}i)"_cs;
bool NotificationsSupported = false; bool ServiceRegistered = false;
bool InhibitedNotSupported = false; bool InhibitedNotSupported = false;
std::vector<QString> CurrentServerInformation;
QStringList CurrentCapabilities;
void ComputeSupported(bool wait = false) { bool GetServiceRegistered() {
const auto message = QDBusMessage::createMethodCall( const auto interface = QDBusConnection::sessionBus().interface();
kService.utf16(), const auto activatable = IsNotificationServiceActivatable();
kObjectPath.utf16(),
kInterface.utf16(),
qsl("GetServerInformation"));
auto async = QDBusConnection::sessionBus().asyncCall(message); return interface
auto watcher = new QDBusPendingCallWatcher(async); ? interface->isServiceRegistered(kService.utf16()) || activatable
: activatable;
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<QString> ComputeServerInformation() {
std::vector<QString> 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;
} }
std::vector<QString> GetServerInformation() { std::vector<QString> GetServerInformation() {
static const auto Result = ComputeServerInformation(); std::vector<QString> result;
return 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( const auto message = QDBusMessage::createMethodCall(
kService.utf16(), kService.utf16(),
kObjectPath.utf16(), kObjectPath.utf16(),
kInterface.utf16(), kInterface.utf16(),
qsl("GetCapabilities")); qsl("GetCapabilities"));
const QDBusReply<QStringList> reply = QDBusConnection::sessionBus().call( // We may be launched earlier than notification daemon
message); while (true) {
const QDBusReply<QStringList> 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())); LOG(("Native notification error: %1").arg(reply.error().message()));
if (reply.error().type() != QDBusError::NoReply) {
break;
}
} }
return {}; return {};
} }
QStringList GetCapabilities() {
static const auto Result = ComputeCapabilities();
return Result;
}
bool Inhibited() { bool Inhibited() {
if (!Supported()
|| !CurrentCapabilities.contains(qsl("inhibitions"))
|| InhibitedNotSupported) {
return false;
}
auto message = QDBusMessage::createMethodCall( auto message = QDBusMessage::createMethodCall(
kService.utf16(), kService.utf16(),
kObjectPath.utf16(), kObjectPath.utf16(),
@ -277,8 +255,7 @@ NotificationData::NotificationData(
bool hideReplyButton) bool hideReplyButton)
: _manager(manager) : _manager(manager)
, _title(title) , _title(title)
, _imageKey(GetImageKey(ParseSpecificationVersion( , _imageKey(GetImageKey(ParseSpecificationVersion(CurrentServerInformation)))
GetServerInformation())))
, _id(id) { , _id(id) {
GError *error = nullptr; GError *error = nullptr;
@ -293,7 +270,7 @@ NotificationData::NotificationData(
return; return;
} }
const auto capabilities = GetCapabilities(); const auto capabilities = CurrentCapabilities;
if (capabilities.contains(qsl("body-markup"))) { if (capabilities.contains(qsl("body-markup"))) {
_body = subtitle.isEmpty() _body = subtitle.isEmpty()
@ -613,30 +590,33 @@ void NotificationData::notificationReplied(uint id, const QString &text) {
} // namespace } // namespace
bool SkipAudio() { bool SkipAudio() {
if (Supported() return Inhibited();
&& GetCapabilities().contains(qsl("inhibitions"))
&& !InhibitedNotSupported) {
return Inhibited();
}
return false;
} }
bool SkipToast() { bool SkipToast() {
return SkipAudio(); return Inhibited();
} }
bool SkipFlashBounce() { bool SkipFlashBounce() {
return SkipAudio(); return Inhibited();
} }
bool Supported() { bool Supported() {
return NotificationsSupported; return ServiceRegistered;
} }
std::unique_ptr<Window::Notifications::Manager> Create( std::unique_ptr<Window::Notifications::Manager> Create(
Window::Notifications::System *system) { 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()) if ((Core::App().settings().nativeNotifications() && Supported())
|| IsWayland()) { || IsWayland()) {
@ -683,8 +663,8 @@ Manager::Private::Private(not_null<Manager*> manager, Type type)
return; return;
} }
const auto serverInformation = GetServerInformation(); const auto serverInformation = CurrentServerInformation;
const auto capabilities = GetCapabilities(); const auto capabilities = CurrentCapabilities;
if (!serverInformation.empty()) { if (!serverInformation.empty()) {
LOG(("Notification daemon product name: %1") LOG(("Notification daemon product name: %1")

View File

@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h" #include "core/application.h"
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#include "platform/linux/linux_notification_service_watcher.h"
#include "platform/linux/linux_gsd_media_keys.h" #include "platform/linux/linux_gsd_media_keys.h"
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
@ -87,6 +88,8 @@ constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs;
QStringList PlatformThemes; QStringList PlatformThemes;
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
std::unique_ptr<internal::NotificationServiceWatcher> NSWInstance;
QStringList ListDBusActivatableNames() { QStringList ListDBusActivatableNames() {
static const auto Result = [&] { static const auto Result = [&] {
const auto message = QDBusMessage::createMethodCall( const auto message = QDBusMessage::createMethodCall(
@ -632,6 +635,17 @@ bool CanOpenDirectoryWithPortal() {
return false; 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() { QString AppRuntimeDirectory() {
static const auto Result = [&] { static const auto Result = [&] {
auto runtimeDir = QStandardPaths::writableLocation( auto runtimeDir = QStandardPaths::writableLocation(
@ -1231,9 +1245,21 @@ void start() {
if (const auto waylandIntegration = WaylandIntegration::Instance()) { if (const auto waylandIntegration = WaylandIntegration::Instance()) {
waylandIntegration->waitForInterfaceAnnounce(); waylandIntegration->waitForInterfaceAnnounce();
} }
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (!IsNotificationServiceActivatable()) {
NSWInstance = std::make_unique<
internal::NotificationServiceWatcher>();
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
} }
void finish() { void finish() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (NSWInstance) {
NSWInstance = nullptr;
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
} }
} // namespace ThirdParty } // namespace ThirdParty

View File

@ -25,6 +25,7 @@ bool UseGtkIntegration();
bool IsGtkIntegrationForced(); bool IsGtkIntegrationForced();
bool UseXDGDesktopPortal(); bool UseXDGDesktopPortal();
bool CanOpenDirectoryWithPortal(); bool CanOpenDirectoryWithPortal();
bool IsNotificationServiceActivatable();
QString AppRuntimeDirectory(); QString AppRuntimeDirectory();
QString GetLauncherBasename(); QString GetLauncherBasename();