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_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
)

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 <QtDBus/QDBusConnection>
#include <QtDBus/QDBusConnectionInterface>
#include <QtDBus/QDBusMessage>
#include <QtDBus/QDBusReply>
#include <QtDBus/QDBusError>
@ -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<QString> 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<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;
return interface
? interface->isServiceRegistered(kService.utf16()) || activatable
: activatable;
}
std::vector<QString> GetServerInformation() {
static const auto Result = ComputeServerInformation();
return Result;
std::vector<QString> 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<QStringList> reply = QDBusConnection::sessionBus().call(
message);
// We may be launched earlier than notification daemon
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()));
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<Window::Notifications::Manager> 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*> 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")

View File

@ -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<internal::NotificationServiceWatcher> 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

View File

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