Support GNotification
It's used if there's a gtk notification daemon or application is running sandboxed without access to the freedesktop protocol. GNotification API is poor, but should feel native on environments using GNOME technologies.
This commit is contained in:
parent
afaad155a0
commit
f9dd2b4a0a
|
@ -1125,6 +1125,7 @@ PRIVATE
|
||||||
platform/platform_integration.h
|
platform/platform_integration.h
|
||||||
platform/platform_main_window.h
|
platform/platform_main_window.h
|
||||||
platform/platform_notifications_manager.h
|
platform/platform_notifications_manager.h
|
||||||
|
platform/platform_specific.cpp
|
||||||
platform/platform_specific.h
|
platform/platform_specific.h
|
||||||
platform/platform_tray.h
|
platform/platform_tray.h
|
||||||
platform/platform_window_title.h
|
platform/platform_window_title.h
|
||||||
|
|
|
@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "platform/linux/notifications_manager_linux.h"
|
#include "platform/linux/notifications_manager_linux.h"
|
||||||
|
|
||||||
#include "window/notifications_utilities.h"
|
#include "window/notifications_utilities.h"
|
||||||
|
#include "base/options.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "base/platform/linux/base_linux_glibmm_helper.h"
|
#include "base/platform/linux/base_linux_glibmm_helper.h"
|
||||||
#include "base/platform/linux/base_linux_dbus_utilities.h"
|
#include "base/platform/linux/base_linux_dbus_utilities.h"
|
||||||
|
#include "platform/platform_specific.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
|
#include "core/sandbox.h"
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
|
@ -27,6 +30,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <glibmm.h>
|
#include <glibmm.h>
|
||||||
#include <giomm.h>
|
#include <giomm.h>
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
namespace Notifications {
|
namespace Notifications {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -341,6 +346,9 @@ private:
|
||||||
const not_null<Manager*> _manager;
|
const not_null<Manager*> _manager;
|
||||||
NotificationId _id;
|
NotificationId _id;
|
||||||
|
|
||||||
|
Glib::RefPtr<Gio::Application> _application;
|
||||||
|
Glib::RefPtr<Gio::Notification> _notification;
|
||||||
|
|
||||||
Glib::RefPtr<Gio::DBus::Connection> _dbusConnection;
|
Glib::RefPtr<Gio::DBus::Connection> _dbusConnection;
|
||||||
Glib::ustring _title;
|
Glib::ustring _title;
|
||||||
Glib::ustring _body;
|
Glib::ustring _body;
|
||||||
|
@ -367,7 +375,8 @@ NotificationData::NotificationData(
|
||||||
not_null<Manager*> manager,
|
not_null<Manager*> manager,
|
||||||
NotificationId id)
|
NotificationId id)
|
||||||
: _manager(manager)
|
: _manager(manager)
|
||||||
, _id(id) {
|
, _id(id)
|
||||||
|
, _application(Gio::Application::get_default()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NotificationData::init(
|
bool NotificationData::init(
|
||||||
|
@ -375,6 +384,64 @@ bool NotificationData::init(
|
||||||
const QString &subtitle,
|
const QString &subtitle,
|
||||||
const QString &msg,
|
const QString &msg,
|
||||||
Window::Notifications::Manager::DisplayOptions options) {
|
Window::Notifications::Manager::DisplayOptions options) {
|
||||||
|
if (_application) {
|
||||||
|
_notification = Gio::Notification::create(title.toStdString());
|
||||||
|
|
||||||
|
_notification->set_body(
|
||||||
|
subtitle.isEmpty()
|
||||||
|
? msg.toStdString()
|
||||||
|
: qsl("%1\n%2").arg(subtitle, msg).toStdString());
|
||||||
|
|
||||||
|
_notification->set_icon(
|
||||||
|
Gio::ThemedIcon::create(base::IconName().toStdString()));
|
||||||
|
|
||||||
|
// glib 2.42+, we keep glib 2.40+ compatibility
|
||||||
|
static const auto set_priority = [] {
|
||||||
|
// reset dlerror after dlsym call
|
||||||
|
const auto guard = gsl::finally([] { dlerror(); });
|
||||||
|
return reinterpret_cast<decltype(&g_notification_set_priority)>(
|
||||||
|
dlsym(RTLD_DEFAULT, "g_notification_set_priority"));
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (set_priority) {
|
||||||
|
// for chat messages, according to
|
||||||
|
// https://docs.gtk.org/gio/enum.NotificationPriority.html
|
||||||
|
set_priority(_notification->gobj(), G_NOTIFICATION_PRIORITY_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// glib 2.70+, we keep glib 2.40+ compatibility
|
||||||
|
static const auto set_category = [] {
|
||||||
|
// reset dlerror after dlsym call
|
||||||
|
const auto guard = gsl::finally([] { dlerror(); });
|
||||||
|
return reinterpret_cast<decltype(&g_notification_set_category)>(
|
||||||
|
dlsym(RTLD_DEFAULT, "g_notification_set_category"));
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (set_category) {
|
||||||
|
set_category(_notification->gobj(), "im.received");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto idTuple = _id.toTuple();
|
||||||
|
|
||||||
|
_notification->set_default_action(
|
||||||
|
"app.notification-reply",
|
||||||
|
idTuple);
|
||||||
|
|
||||||
|
if (!options.hideMarkAsRead) {
|
||||||
|
_notification->add_button(
|
||||||
|
tr::lng_context_mark_read(tr::now).toStdString(),
|
||||||
|
"app.notification-mark-as-read",
|
||||||
|
idTuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
_notification->add_button(
|
||||||
|
tr::lng_notification_reply(tr::now).toStdString(),
|
||||||
|
"app.notification-reply",
|
||||||
|
idTuple);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Noexcept([&] {
|
Noexcept([&] {
|
||||||
_dbusConnection = Gio::DBus::Connection::get_sync(
|
_dbusConnection = Gio::DBus::Connection::get_sync(
|
||||||
Gio::DBus::BusType::SESSION);
|
Gio::DBus::BusType::SESSION);
|
||||||
|
@ -545,6 +612,17 @@ NotificationData::~NotificationData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationData::show() {
|
void NotificationData::show() {
|
||||||
|
if (_application && _notification) {
|
||||||
|
_application->send_notification(
|
||||||
|
std::to_string(_id.contextId.sessionId)
|
||||||
|
+ '-'
|
||||||
|
+ std::to_string(_id.contextId.peerId.value)
|
||||||
|
+ '-'
|
||||||
|
+ std::to_string(_id.msgId.bare),
|
||||||
|
_notification);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// a hack for snap's activation restriction
|
// a hack for snap's activation restriction
|
||||||
const auto weak = base::make_weak(this);
|
const auto weak = base::make_weak(this);
|
||||||
StartServiceAsync(crl::guard(weak, [=] {
|
StartServiceAsync(crl::guard(weak, [=] {
|
||||||
|
@ -587,6 +665,17 @@ void NotificationData::show() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationData::close() {
|
void NotificationData::close() {
|
||||||
|
if (_application) {
|
||||||
|
_application->withdraw_notification(
|
||||||
|
std::to_string(_id.contextId.sessionId)
|
||||||
|
+ '-'
|
||||||
|
+ std::to_string(_id.contextId.peerId.value)
|
||||||
|
+ '-'
|
||||||
|
+ std::to_string(_id.msgId.bare));
|
||||||
|
_manager->clearNotification(_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_dbusConnection->call(
|
_dbusConnection->call(
|
||||||
std::string(kObjectPath),
|
std::string(kObjectPath),
|
||||||
std::string(kInterface),
|
std::string(kInterface),
|
||||||
|
@ -602,7 +691,16 @@ void NotificationData::close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationData::setImage(const QString &imagePath) {
|
void NotificationData::setImage(const QString &imagePath) {
|
||||||
if (imagePath.isEmpty() || _imageKey.empty()) {
|
if (imagePath.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_notification) {
|
||||||
|
_notification->set_icon(Gio::Icon::create(imagePath.toStdString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_imageKey.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,13 +794,13 @@ bool SkipFlashBounceForCustom() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Supported() {
|
bool Supported() {
|
||||||
return ServiceRegistered;
|
return ServiceRegistered || Gio::Application::get_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Enforced() {
|
bool Enforced() {
|
||||||
// Wayland doesn't support positioning
|
// Wayland doesn't support positioning
|
||||||
// and custom notifications don't work here
|
// and custom notifications don't work here
|
||||||
return IsWayland();
|
return IsWayland() || OptionGApplication.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ByDefault() {
|
bool ByDefault() {
|
||||||
|
@ -728,25 +826,30 @@ bool ByDefault() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Create(Window::Notifications::System *system) {
|
void Create(Window::Notifications::System *system) {
|
||||||
static const auto ServiceWatcher = CreateServiceWatcher();
|
|
||||||
|
|
||||||
const auto managerSetter = [=] {
|
const auto managerSetter = [=] {
|
||||||
using ManagerType = Window::Notifications::ManagerType;
|
using ManagerType = Window::Notifications::ManagerType;
|
||||||
if ((Core::App().settings().nativeNotifications() || Enforced())
|
if ((Core::App().settings().nativeNotifications() || Enforced())
|
||||||
&& Supported()) {
|
&& Supported()) {
|
||||||
if (system->managerType() != ManagerType::Native) {
|
if (system->manager().type() != ManagerType::Native) {
|
||||||
system->setManager(std::make_unique<Manager>(system));
|
system->setManager(std::make_unique<Manager>(system));
|
||||||
}
|
}
|
||||||
} else if (Enforced()) {
|
} else if (Enforced()) {
|
||||||
if (system->managerType() != ManagerType::Dummy) {
|
if (system->manager().type() != ManagerType::Dummy) {
|
||||||
using DummyManager = Window::Notifications::DummyManager;
|
using DummyManager = Window::Notifications::DummyManager;
|
||||||
system->setManager(std::make_unique<DummyManager>(system));
|
system->setManager(std::make_unique<DummyManager>(system));
|
||||||
}
|
}
|
||||||
} else if (system->managerType() != ManagerType::Default) {
|
} else if (system->manager().type() != ManagerType::Default) {
|
||||||
system->setManager(nullptr);
|
system->setManager(nullptr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (Gio::Application::get_default()) {
|
||||||
|
managerSetter();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const auto ServiceWatcher = CreateServiceWatcher();
|
||||||
|
|
||||||
const auto counter = std::make_shared<int>(2);
|
const auto counter = std::make_shared<int>(2);
|
||||||
const auto oneReady = [=] {
|
const auto oneReady = [=] {
|
||||||
if (!--*counter) {
|
if (!--*counter) {
|
||||||
|
@ -1098,7 +1201,7 @@ void Manager::doClearFromSession(not_null<Main::Session*> session) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::doSkipAudio() const {
|
bool Manager::doSkipAudio() const {
|
||||||
return _private->inhibited();
|
return _private->inhibited() || Gio::Application::get_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::doSkipToast() const {
|
bool Manager::doSkipToast() const {
|
||||||
|
@ -1106,7 +1209,7 @@ bool Manager::doSkipToast() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::doSkipFlashBounce() const {
|
bool Manager::doSkipFlashBounce() const {
|
||||||
return _private->inhibited();
|
return _private->inhibited() || Gio::Application::get_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Notifications
|
} // namespace Notifications
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "platform/linux/specific_linux.h"
|
#include "platform/linux/specific_linux.h"
|
||||||
|
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
|
#include "base/options.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "platform/linux/linux_desktop_environment.h"
|
#include "platform/linux/linux_desktop_environment.h"
|
||||||
#include "platform/linux/linux_wayland_integration.h"
|
#include "platform/linux/linux_wayland_integration.h"
|
||||||
|
@ -16,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
#include "core/sandbox.h"
|
#include "core/sandbox.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
|
#include "core/local_url_handlers.h"
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
#include "core/update_checker.h"
|
#include "core/update_checker.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
|
@ -57,6 +59,58 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
using namespace Platform;
|
using namespace Platform;
|
||||||
using Platform::internal::WaylandIntegration;
|
using Platform::internal::WaylandIntegration;
|
||||||
|
|
||||||
|
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
typedef GApplication TDesktopApplication;
|
||||||
|
typedef GApplicationClass TDesktopApplicationClass;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(
|
||||||
|
TDesktopApplication,
|
||||||
|
t_desktop_application,
|
||||||
|
G_TYPE_APPLICATION)
|
||||||
|
|
||||||
|
static void t_desktop_application_class_init(
|
||||||
|
TDesktopApplicationClass *klass) {
|
||||||
|
const auto application_class = G_APPLICATION_CLASS(klass);
|
||||||
|
|
||||||
|
application_class->before_emit = [](
|
||||||
|
GApplication *application,
|
||||||
|
GVariant *platformData) {
|
||||||
|
if (Platform::IsWayland()) {
|
||||||
|
static const auto keys = {
|
||||||
|
"activation-token",
|
||||||
|
"desktop-startup-id",
|
||||||
|
};
|
||||||
|
for (const auto key : keys) {
|
||||||
|
const char *token = nullptr;
|
||||||
|
g_variant_lookup(platformData, key, "&s", &token);
|
||||||
|
if (token) {
|
||||||
|
qputenv("XDG_ACTIVATION_TOKEN", token);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
application_class->add_platform_data = [](
|
||||||
|
GApplication *application,
|
||||||
|
GVariantBuilder *builder) {
|
||||||
|
if (Platform::IsWayland()) {
|
||||||
|
const auto token = qgetenv("XDG_ACTIVATION_TOKEN");
|
||||||
|
if (!token.isEmpty()) {
|
||||||
|
g_variant_builder_add(
|
||||||
|
builder,
|
||||||
|
"{sv}",
|
||||||
|
"activation-token",
|
||||||
|
g_variant_new_string(token.constData()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void t_desktop_application_init(TDesktopApplication *application) {
|
||||||
|
}
|
||||||
|
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -172,6 +226,197 @@ void PortalAutostart(bool start, bool silent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LaunchGApplication() {
|
||||||
|
const auto connection = [] {
|
||||||
|
try {
|
||||||
|
return Gio::DBus::Connection::get_sync(
|
||||||
|
Gio::DBus::BusType::SESSION);
|
||||||
|
} catch (...) {
|
||||||
|
return Glib::RefPtr<Gio::DBus::Connection>();
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
using namespace base::Platform::DBus;
|
||||||
|
const auto activatableNames = [&] {
|
||||||
|
try {
|
||||||
|
if (connection) {
|
||||||
|
return ListActivatableNames(connection);
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::vector<Glib::ustring>();
|
||||||
|
}();
|
||||||
|
|
||||||
|
const auto freedesktopNotifications = [&] {
|
||||||
|
try {
|
||||||
|
if (connection && NameHasOwner(
|
||||||
|
connection,
|
||||||
|
"org.freedesktop.Notifications")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ranges::contains(
|
||||||
|
activatableNames,
|
||||||
|
"org.freedesktop.Notifications")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto gtkNotifications = [&] {
|
||||||
|
try {
|
||||||
|
if (connection && NameHasOwner(
|
||||||
|
connection,
|
||||||
|
"org.gtk.Notifications")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ranges::contains(activatableNames, "org.gtk.Notifications")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (OptionGApplication.value()
|
||||||
|
|| gtkNotifications()
|
||||||
|
|| (KSandbox::isFlatpak() && !freedesktopNotifications())) {
|
||||||
|
Glib::signal_idle().connect_once([] {
|
||||||
|
const auto appId = QGuiApplication::desktopFileName()
|
||||||
|
.chopped(8)
|
||||||
|
.toStdString();
|
||||||
|
|
||||||
|
const auto app = Glib::wrap(
|
||||||
|
G_APPLICATION(
|
||||||
|
g_object_new(
|
||||||
|
t_desktop_application_get_type(),
|
||||||
|
"application-id",
|
||||||
|
Gio::Application::id_is_valid(appId)
|
||||||
|
? appId.c_str()
|
||||||
|
: nullptr,
|
||||||
|
"flags",
|
||||||
|
G_APPLICATION_HANDLES_OPEN,
|
||||||
|
nullptr)));
|
||||||
|
|
||||||
|
app->signal_startup().connect([=] {
|
||||||
|
QEventLoop loop;
|
||||||
|
loop.exec(QEventLoop::ApplicationExec);
|
||||||
|
app->quit();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
app->signal_activate().connect([] {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
||||||
|
if (const auto w = App::wnd()) {
|
||||||
|
w->activate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
app->signal_open().connect([](
|
||||||
|
const Gio::Application::type_vec_files &files,
|
||||||
|
const Glib::ustring &hint) {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||||
|
for (const auto file : files) {
|
||||||
|
if (file->get_uri_scheme() == "file") {
|
||||||
|
gSendPaths.append(
|
||||||
|
QString::fromStdString(file->get_path()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto url = QString::fromStdString(
|
||||||
|
file->get_uri());
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (url.startsWith(qstr("interpret://"))) {
|
||||||
|
gSendPaths.append(url);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Core::StartUrlRequiresActivate(url)) {
|
||||||
|
if (const auto w = App::wnd()) {
|
||||||
|
w->activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cSetStartUrl(url);
|
||||||
|
Core::App().checkStartUrl();
|
||||||
|
}
|
||||||
|
if (!cSendPaths().isEmpty()) {
|
||||||
|
if (const auto w = App::wnd()) {
|
||||||
|
w->sendPaths();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
app->add_action("Quit", [] {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
||||||
|
Core::Quit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
using Window::Notifications::Manager;
|
||||||
|
using NotificationId = Manager::NotificationId;
|
||||||
|
using NotificationIdTuple = std::result_of<
|
||||||
|
decltype(&NotificationId::toTuple)(NotificationId*)
|
||||||
|
>::type;
|
||||||
|
|
||||||
|
const auto notificationIdVariantType = [] {
|
||||||
|
try {
|
||||||
|
return base::Platform::MakeGlibVariant(
|
||||||
|
NotificationId().toTuple()).get_type();
|
||||||
|
} catch (...) {
|
||||||
|
return Glib::VariantType();
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
app->add_action_with_parameter(
|
||||||
|
"notification-reply",
|
||||||
|
notificationIdVariantType,
|
||||||
|
[](const Glib::VariantBase ¶meter) {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||||
|
try {
|
||||||
|
const auto &app = Core::App();
|
||||||
|
const auto ¬ifications = app.notifications();
|
||||||
|
notifications.manager().notificationActivated(
|
||||||
|
NotificationId::FromTuple(
|
||||||
|
base::Platform::GlibVariantCast<
|
||||||
|
NotificationIdTuple
|
||||||
|
>(parameter)));
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app->add_action_with_parameter(
|
||||||
|
"notification-mark-as-read",
|
||||||
|
notificationIdVariantType,
|
||||||
|
[](const Glib::VariantBase ¶meter) {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||||
|
try {
|
||||||
|
const auto &app = Core::App();
|
||||||
|
const auto ¬ifications = app.notifications();
|
||||||
|
notifications.manager().notificationReplied(
|
||||||
|
NotificationId::FromTuple(
|
||||||
|
base::Platform::GlibVariantCast<
|
||||||
|
NotificationIdTuple
|
||||||
|
>(parameter)),
|
||||||
|
{});
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app->hold();
|
||||||
|
app->run(0, nullptr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
|
||||||
bool GenerateDesktopFile(
|
bool GenerateDesktopFile(
|
||||||
|
@ -587,6 +832,10 @@ namespace ThirdParty {
|
||||||
void start() {
|
void start() {
|
||||||
LOG(("Icon theme: %1").arg(QIcon::themeName()));
|
LOG(("Icon theme: %1").arg(QIcon::themeName()));
|
||||||
LOG(("Fallback icon theme: %1").arg(QIcon::fallbackThemeName()));
|
LOG(("Fallback icon theme: %1").arg(QIcon::fallbackThemeName()));
|
||||||
|
|
||||||
|
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
LaunchGApplication();
|
||||||
|
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
}
|
}
|
||||||
|
|
||||||
void finish() {
|
void finish() {
|
||||||
|
|
26
Telegram/SourceFiles/platform/platform_specific.cpp
Normal file
26
Telegram/SourceFiles/platform/platform_specific.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
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/platform_specific.h"
|
||||||
|
|
||||||
|
#include "base/options.h"
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
const char kOptionGApplication[] = "gapplication";
|
||||||
|
|
||||||
|
base::options::toggle OptionGApplication({
|
||||||
|
.id = kOptionGApplication,
|
||||||
|
.name = "GApplication",
|
||||||
|
.description = "Force enable GNOME's GApplication and GNotification."
|
||||||
|
" This changes notification behavior to be native to GNOME."
|
||||||
|
" When disabled, autodetect is used.",
|
||||||
|
.scope = base::options::linux,
|
||||||
|
.restartRequired = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
} // namespace Platform
|
|
@ -7,8 +7,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
namespace base::options {
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
class option;
|
||||||
|
|
||||||
|
using toggle = option<bool>;
|
||||||
|
|
||||||
|
} // namespace base::options
|
||||||
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
|
|
||||||
|
extern const char kOptionGApplication[];
|
||||||
|
extern base::options::toggle OptionGApplication;
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/chat/chat_style_radius.h"
|
#include "ui/chat/chat_style_radius.h"
|
||||||
#include "base/options.h"
|
#include "base/options.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
|
#include "platform/platform_specific.h"
|
||||||
#include "chat_helpers/tabbed_panel.h"
|
#include "chat_helpers/tabbed_panel.h"
|
||||||
#include "dialogs/dialogs_inner_widget.h"
|
#include "dialogs/dialogs_inner_widget.h"
|
||||||
#include "history/history_widget.h"
|
#include "history/history_widget.h"
|
||||||
|
@ -143,6 +144,7 @@ void SetupExperimental(
|
||||||
addToggle(Settings::kOptionMonoSettingsIcons);
|
addToggle(Settings::kOptionMonoSettingsIcons);
|
||||||
addToggle(Webview::kOptionWebviewDebugEnabled);
|
addToggle(Webview::kOptionWebviewDebugEnabled);
|
||||||
addToggle(kOptionAutoScrollInactiveChat);
|
addToggle(kOptionAutoScrollInactiveChat);
|
||||||
|
addToggle(Platform::kOptionGApplication);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -125,9 +125,9 @@ void System::setManager(std::unique_ptr<Manager> manager) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ManagerType System::managerType() const {
|
Manager &System::manager() const {
|
||||||
Expects(_manager != nullptr);
|
Expects(_manager != nullptr);
|
||||||
return _manager->type();
|
return *_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
Main::Session *System::findSession(uint64 sessionId) const {
|
Main::Session *System::findSession(uint64 sessionId) const {
|
||||||
|
|
|
@ -84,7 +84,7 @@ public:
|
||||||
|
|
||||||
void createManager();
|
void createManager();
|
||||||
void setManager(std::unique_ptr<Manager> manager);
|
void setManager(std::unique_ptr<Manager> manager);
|
||||||
[[nodiscard]] ManagerType managerType() const;
|
[[nodiscard]] Manager &manager() const;
|
||||||
|
|
||||||
void checkDelayed();
|
void checkDelayed();
|
||||||
void schedule(Data::ItemNotification notification);
|
void schedule(Data::ItemNotification notification);
|
||||||
|
@ -217,6 +217,22 @@ public:
|
||||||
friend inline auto operator<=>(
|
friend inline auto operator<=>(
|
||||||
const ContextId&,
|
const ContextId&,
|
||||||
const ContextId&) = default;
|
const ContextId&) = default;
|
||||||
|
|
||||||
|
[[nodiscard]] auto toTuple() const {
|
||||||
|
return std::make_tuple(
|
||||||
|
sessionId,
|
||||||
|
peerId.value,
|
||||||
|
topicRootId.bare);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] static auto FromTuple(const T &tuple) {
|
||||||
|
return ContextId{
|
||||||
|
std::get<0>(tuple),
|
||||||
|
PeerIdHelper(std::get<1>(tuple)),
|
||||||
|
std::get<2>(tuple),
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct NotificationId {
|
struct NotificationId {
|
||||||
ContextId contextId;
|
ContextId contextId;
|
||||||
|
@ -225,6 +241,20 @@ public:
|
||||||
friend inline auto operator<=>(
|
friend inline auto operator<=>(
|
||||||
const NotificationId&,
|
const NotificationId&,
|
||||||
const NotificationId&) = default;
|
const NotificationId&) = default;
|
||||||
|
|
||||||
|
[[nodiscard]] auto toTuple() const {
|
||||||
|
return std::make_tuple(
|
||||||
|
contextId.toTuple(),
|
||||||
|
msgId.bare);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] static auto FromTuple(const T &tuple) {
|
||||||
|
return NotificationId{
|
||||||
|
ContextId::FromTuple(std::get<0>(tuple)),
|
||||||
|
std::get<1>(tuple),
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct NotificationFields {
|
struct NotificationFields {
|
||||||
not_null<HistoryItem*> item;
|
not_null<HistoryItem*> item;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 1cc6aba584869df04341467cf7c5f72c8354086f
|
Subproject commit d3f9e826197c6ed42f8ef2d021d8eb74b2d390cc
|
|
@ -1 +1 @@
|
||||||
Subproject commit b9d81771a0d7533dd07805f0618193277715da80
|
Subproject commit 9623bb460b9be6b0481d775f9e01e5084170024e
|
Loading…
Reference in New Issue
Block a user