Allow qualified notification daemons by default on Linux

This commit is contained in:
Ilya Fedin 2021-01-13 13:56:49 +04:00 committed by John Preston
parent 49736cd879
commit 64b12bde55
6 changed files with 86 additions and 24 deletions

View file

@ -43,7 +43,7 @@ constexpr auto kImageDataType = "(iiibii@ay)"_cs;
constexpr auto kNotifyArgsType = "(susssasa{sv}i)"_cs; constexpr auto kNotifyArgsType = "(susssasa{sv}i)"_cs;
bool ServiceRegistered = false; bool ServiceRegistered = false;
bool InhibitedNotSupported = false; bool InhibitionSupported = false;
std::vector<QString> CurrentServerInformation; std::vector<QString> CurrentServerInformation;
QStringList CurrentCapabilities; QStringList CurrentCapabilities;
@ -118,10 +118,42 @@ QStringList GetCapabilities() {
return {}; return {};
} }
bool GetInhibitionSupported() {
auto message = QDBusMessage::createMethodCall(
kService.utf16(),
kObjectPath.utf16(),
kPropertiesInterface.utf16(),
qsl("Get"));
message.setArguments({
qsl("org.freedesktop.Notifications"),
qsl("Inhibited")
});
// We may be launched earlier than notification daemon
while (true) {
const QDBusError error = QDBusConnection::sessionBus().call(message);
if (!error.isValid()) {
return true;
} else if (error.type() == QDBusError::InvalidArgs) {
break;
}
LOG(("Native notification error: %1").arg(error.message()));
if (error.type() != QDBusError::NoReply) {
break;
}
}
return false;
}
bool Inhibited() { bool Inhibited() {
if (!Supported() if (!Supported()
|| !CurrentCapabilities.contains(qsl("inhibitions")) || !CurrentCapabilities.contains(qsl("inhibitions"))
|| InhibitedNotSupported) { || !InhibitionSupported) {
return false; return false;
} }
@ -139,26 +171,36 @@ bool Inhibited() {
const QDBusReply<QVariant> reply = QDBusConnection::sessionBus().call( const QDBusReply<QVariant> reply = QDBusConnection::sessionBus().call(
message); message);
static const auto NotSupportedErrors = {
QDBusError::ServiceUnknown,
QDBusError::InvalidArgs,
};
if (reply.isValid()) { if (reply.isValid()) {
return reply.value().toBool(); return reply.value().toBool();
} else if (ranges::contains(NotSupportedErrors, reply.error().type())) {
InhibitedNotSupported = true;
} else {
if (reply.error().type() == QDBusError::AccessDenied) {
InhibitedNotSupported = true;
}
LOG(("Native notification error: %1").arg(reply.error().message()));
} }
LOG(("Native notification error: %1").arg(reply.error().message()));
return false; return false;
} }
bool IsQualifiedDaemon() {
// A list of capabilities that offer feature parity
// with custom notifications
static const auto NeededCapabilities = {
// To show message content
qsl("body"),
// To make the sender name bold
qsl("body-markup"),
// To have buttons on notifications
qsl("actions"),
// To have quick reply
qsl("inline-reply"),
// To not to play sound with Don't Disturb activated
// (no, using sound capability is not a way)
qsl("inhibitions"),
};
return ranges::all_of(NeededCapabilities, [&](const auto &capability) {
return CurrentCapabilities.contains(capability);
}) && InhibitionSupported;
}
QVersionNumber ParseSpecificationVersion( QVersionNumber ParseSpecificationVersion(
const std::vector<QString> &serverInformation) { const std::vector<QString> &serverInformation) {
if (serverInformation.size() >= 4) { if (serverInformation.size() >= 4) {
@ -605,21 +647,28 @@ bool Supported() {
return ServiceRegistered; return ServiceRegistered;
} }
bool Enforced() {
// Wayland doesn't support positioning
// and custom notifications don't work here
return IsQualifiedDaemon() || IsWayland();
}
std::unique_ptr<Window::Notifications::Manager> Create( std::unique_ptr<Window::Notifications::Manager> Create(
Window::Notifications::System *system) { Window::Notifications::System *system) {
ServiceRegistered = GetServiceRegistered(); ServiceRegistered = GetServiceRegistered();
InhibitedNotSupported = false;
if (Supported()) { if (Supported()) {
CurrentServerInformation = GetServerInformation(); CurrentServerInformation = GetServerInformation();
CurrentCapabilities = GetCapabilities(); CurrentCapabilities = GetCapabilities();
InhibitionSupported = GetInhibitionSupported();
} else { } else {
CurrentServerInformation = {}; CurrentServerInformation = {};
CurrentCapabilities = QStringList{}; CurrentCapabilities = QStringList{};
InhibitionSupported = false;
} }
if ((Core::App().settings().nativeNotifications() && Supported()) if ((Core::App().settings().nativeNotifications() && Supported())
|| IsWayland()) { || Enforced()) {
return std::make_unique<Manager>(system); return std::make_unique<Manager>(system);
} }

View file

@ -27,9 +27,15 @@ bool Supported() {
return false; return false;
} }
bool Enforced() {
// Wayland doesn't support positioning
// and custom notifications don't work here
return IsWayland();
}
std::unique_ptr<Window::Notifications::Manager> Create( std::unique_ptr<Window::Notifications::Manager> Create(
Window::Notifications::System *system) { Window::Notifications::System *system) {
if (IsWayland()) { if (Enforced()) {
return std::make_unique<Window::Notifications::DummyManager>(system); return std::make_unique<Window::Notifications::DummyManager>(system);
} }

View file

@ -162,6 +162,10 @@ bool Supported() {
return Platform::IsMac10_8OrGreater(); return Platform::IsMac10_8OrGreater();
} }
bool Enforced() {
return Supported();
}
std::unique_ptr<Window::Notifications::Manager> Create(Window::Notifications::System *system) { std::unique_ptr<Window::Notifications::Manager> Create(Window::Notifications::System *system) {
if (Supported()) { if (Supported()) {
return std::make_unique<Manager>(system); return std::make_unique<Manager>(system);

View file

@ -17,6 +17,7 @@ namespace Notifications {
[[nodiscard]] bool SkipFlashBounce(); [[nodiscard]] bool SkipFlashBounce();
[[nodiscard]] bool Supported(); [[nodiscard]] bool Supported();
[[nodiscard]] bool Enforced();
[[nodiscard]] std::unique_ptr<Window::Notifications::Manager> Create( [[nodiscard]] std::unique_ptr<Window::Notifications::Manager> Create(
Window::Notifications::System *system); Window::Notifications::System *system);

View file

@ -325,6 +325,10 @@ bool Supported() {
return false; return false;
} }
bool Enforced() {
return false;
}
std::unique_ptr<Window::Notifications::Manager> Create(Window::Notifications::System *system) { std::unique_ptr<Window::Notifications::Manager> Create(Window::Notifications::System *system) {
#ifndef __MINGW32__ #ifndef __MINGW32__
if (Core::App().settings().nativeNotifications() && Supported()) { if (Core::App().settings().nativeNotifications() && Supported()) {

View file

@ -676,14 +676,13 @@ void SetupNotificationsContent(
}, joined->lifetime()); }, joined->lifetime());
const auto nativeText = [&] { const auto nativeText = [&] {
if (!Platform::Notifications::Supported()) { if (!Platform::Notifications::Supported()
|| Platform::Notifications::Enforced()) {
return QString(); return QString();
} else if (Platform::IsWindows()) { } else if (Platform::IsWindows()) {
return tr::lng_settings_use_windows(tr::now); return tr::lng_settings_use_windows(tr::now);
} else if (Platform::IsLinux() && !Platform::IsWayland()) {
return tr::lng_settings_use_native_notifications(tr::now);
} }
return QString(); return tr::lng_settings_use_native_notifications(tr::now);
}(); }();
const auto native = [&]() -> Ui::Checkbox* { const auto native = [&]() -> Ui::Checkbox* {
if (nativeText.isEmpty()) { if (nativeText.isEmpty()) {
@ -697,8 +696,7 @@ void SetupNotificationsContent(
return addCheckbox(nativeText, settings.nativeNotifications()); return addCheckbox(nativeText, settings.nativeNotifications());
}(); }();
const auto advancedSlide = !Platform::IsMac10_8OrGreater() const auto advancedSlide = !Platform::Notifications::Enforced()
&& !Platform::IsWayland()
? container->add( ? container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container, container,