2023-11-01 21:11:11 +00:00
|
|
|
// This file Copyright © Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2008-02-24 15:42:31 +00:00
|
|
|
|
2022-12-26 21:13:21 +00:00
|
|
|
#include "Notify.h"
|
|
|
|
|
2022-12-29 02:42:20 +00:00
|
|
|
#include "GtkCompat.h"
|
2022-12-26 21:13:21 +00:00
|
|
|
#include "Prefs.h"
|
|
|
|
#include "PrefsDialog.h"
|
|
|
|
#include "Session.h"
|
|
|
|
#include "Utils.h"
|
2021-10-18 20:22:31 +00:00
|
|
|
|
2022-12-27 01:43:20 +00:00
|
|
|
#include <giomm/asyncresult.h>
|
|
|
|
#include <giomm/dbusproxy.h>
|
|
|
|
#include <glibmm/error.h>
|
2021-10-18 20:22:31 +00:00
|
|
|
#include <glibmm/i18n.h>
|
2022-12-27 01:43:20 +00:00
|
|
|
#include <glibmm/miscutils.h>
|
|
|
|
#include <glibmm/spawn.h>
|
|
|
|
#include <glibmm/ustring.h>
|
|
|
|
#include <glibmm/variant.h>
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2022-10-15 16:13:50 +00:00
|
|
|
#include <fmt/core.h>
|
|
|
|
|
2022-12-26 21:13:21 +00:00
|
|
|
#include <map>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
2008-02-24 15:42:31 +00:00
|
|
|
|
2021-12-14 08:43:27 +00:00
|
|
|
using namespace std::literals;
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
using StringVariantType = Glib::Variant<Glib::ustring>;
|
|
|
|
using StringListVariantType = Glib::Variant<std::vector<Glib::ustring>>;
|
|
|
|
using UInt32VariantType = Glib::Variant<guint32>;
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
namespace
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
|
|
|
|
2022-11-15 00:53:12 +00:00
|
|
|
auto const NotificationsDbusName = "org.freedesktop.Notifications"sv; // TODO(C++20): Use ""s
|
|
|
|
auto const NotificationsDbusCoreObject = "/org/freedesktop/Notifications"sv; // TODO(C++20): Use ""s
|
|
|
|
auto const NotificationsDbusCoreInterface = "org.freedesktop.Notifications"sv; // TODO(C++20): Use ""s
|
2021-12-14 08:43:27 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
struct TrNotification
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-11-01 00:11:23 +00:00
|
|
|
Glib::RefPtr<Session> core;
|
2022-06-17 15:43:04 +00:00
|
|
|
tr_torrent_id_t torrent_id = {};
|
2021-10-18 20:22:31 +00:00
|
|
|
};
|
2012-12-05 21:26:40 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
Glib::RefPtr<Gio::DBus::Proxy> proxy;
|
|
|
|
std::map<guint32, TrNotification> active_notifications;
|
|
|
|
bool server_supports_actions = false;
|
2012-12-05 21:26:40 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
template<typename... Ts>
|
|
|
|
Glib::VariantContainerBase make_variant_tuple(Ts&&... args)
|
|
|
|
{
|
|
|
|
return Glib::VariantContainerBase::create_tuple(
|
|
|
|
{ Glib::Variant<std::remove_cv_t<std::remove_reference_t<Ts>>>::create(std::forward<Ts>(args))... });
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
void get_capabilities_callback(Glib::RefPtr<Gio::AsyncResult>& res)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2022-10-19 21:27:07 +00:00
|
|
|
auto result = Glib::VariantContainerBase();
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
result = proxy->call_finish(res);
|
|
|
|
}
|
|
|
|
catch (Glib::Error const&)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2020-08-18 10:36:10 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
if (!result || result.get_n_children() != 1 || !result.get_child(0).is_of_type(StringListVariantType::variant_type()))
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
return;
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
auto const caps = Glib::VariantBase::cast_dynamic<StringListVariantType>(result.get_child(0)).get();
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
for (auto const& cap : caps)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-10-18 20:22:31 +00:00
|
|
|
if (cap == "actions")
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-10-18 20:22:31 +00:00
|
|
|
server_supports_actions = true;
|
2017-04-19 12:04:45 +00:00
|
|
|
break;
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
void g_signal_callback(
|
|
|
|
Glib::ustring const& /*sender_name*/,
|
|
|
|
Glib::ustring const& signal_name,
|
|
|
|
Glib::VariantContainerBase params)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2021-10-18 20:22:31 +00:00
|
|
|
g_return_if_fail(params.get_n_children() > 0 && params.get_child(0).is_of_type(UInt32VariantType::variant_type()));
|
2020-08-18 10:36:10 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
auto const id = Glib::VariantBase::cast_dynamic<UInt32VariantType>(params.get_child(0)).get();
|
|
|
|
auto const n_it = active_notifications.find(id);
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
if (n_it == active_notifications.end())
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
auto const& n = n_it->second;
|
|
|
|
|
|
|
|
if (signal_name == "NotificationClosed")
|
2009-11-22 18:55:24 +00:00
|
|
|
{
|
2021-10-18 20:22:31 +00:00
|
|
|
active_notifications.erase(n_it);
|
2009-11-22 18:55:24 +00:00
|
|
|
}
|
2021-10-18 20:22:31 +00:00
|
|
|
else if (
|
|
|
|
signal_name == "ActionInvoked" && params.get_n_children() > 1 &&
|
|
|
|
params.get_child(1).is_of_type(StringVariantType::variant_type()))
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-10-18 20:22:31 +00:00
|
|
|
auto const* tor = n.core->find_torrent(n.torrent_id);
|
2021-10-06 16:32:17 +00:00
|
|
|
if (tor == nullptr)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
return;
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
auto const action = Glib::VariantBase::cast_dynamic<StringVariantType>(params.get_child(1)).get();
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
if (action == "folder")
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-10-18 20:22:31 +00:00
|
|
|
n.core->open_folder(n.torrent_id);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2021-10-18 20:22:31 +00:00
|
|
|
else if (action == "file")
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
char const* dir = tr_torrentGetDownloadDir(tor);
|
2021-11-28 03:17:47 +00:00
|
|
|
auto const path = Glib::build_filename(dir, tr_torrentFile(tor, 0).name);
|
2017-04-19 12:04:45 +00:00
|
|
|
gtr_open_file(path);
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
2021-11-08 17:21:20 +00:00
|
|
|
else if (action == "start-now")
|
|
|
|
{
|
|
|
|
n.core->start_now(n.torrent_id);
|
|
|
|
}
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
void dbus_proxy_ready_callback(Glib::RefPtr<Gio::AsyncResult>& res)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2022-10-19 21:27:07 +00:00
|
|
|
try
|
2009-01-12 18:48:20 +00:00
|
|
|
{
|
2022-10-19 21:27:07 +00:00
|
|
|
proxy = Gio::DBus::Proxy::create_for_bus_finish(res);
|
|
|
|
}
|
|
|
|
catch (Glib::Error const& e)
|
|
|
|
{
|
2022-11-13 17:36:16 +00:00
|
|
|
gtr_warning(fmt::format(
|
|
|
|
_("Couldn't create proxy for '{bus}': {error} ({error_code})"),
|
|
|
|
fmt::arg("bus", NotificationsDbusName),
|
|
|
|
fmt::arg("error", TR_GLIB_EXCEPTION_WHAT(e)),
|
|
|
|
fmt::arg("error_code", e.code())));
|
2017-04-19 12:04:45 +00:00
|
|
|
return;
|
2009-01-12 18:48:20 +00:00
|
|
|
}
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
proxy->signal_signal().connect(&g_signal_callback);
|
|
|
|
proxy->call("GetCapabilities", &get_capabilities_callback);
|
2008-02-24 15:42:31 +00:00
|
|
|
}
|
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
void gtr_notify_init()
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-10-18 20:22:31 +00:00
|
|
|
Gio::DBus::Proxy::create_for_bus(
|
2022-09-10 13:19:54 +00:00
|
|
|
TR_GIO_DBUS_BUS_TYPE(SESSION),
|
2022-11-15 00:53:12 +00:00
|
|
|
std::string(NotificationsDbusName),
|
|
|
|
std::string(NotificationsDbusCoreObject),
|
|
|
|
std::string(NotificationsDbusCoreInterface),
|
2021-10-18 20:22:31 +00:00
|
|
|
&dbus_proxy_ready_callback,
|
|
|
|
{},
|
2022-09-10 13:19:54 +00:00
|
|
|
TR_GIO_DBUS_PROXY_FLAGS(DO_NOT_LOAD_PROPERTIES));
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
namespace
|
2009-06-11 16:17:48 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
void notify_callback(Glib::RefPtr<Gio::AsyncResult>& res, TrNotification const& n)
|
|
|
|
{
|
2022-10-19 21:27:07 +00:00
|
|
|
auto result = Glib::VariantContainerBase();
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
result = proxy->call_finish(res);
|
|
|
|
}
|
|
|
|
catch (Glib::Error const&)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
if (!result || result.get_n_children() != 1 || !result.get_child(0).is_of_type(UInt32VariantType::variant_type()))
|
2009-06-11 16:17:48 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
return;
|
2009-06-11 16:17:48 +00:00
|
|
|
}
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
auto const id = Glib::VariantBase::cast_dynamic<UInt32VariantType>(result.get_child(0)).get();
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2022-01-17 18:39:50 +00:00
|
|
|
active_notifications.try_emplace(id, n);
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
} // namespace
|
|
|
|
|
2022-06-17 15:43:04 +00:00
|
|
|
void gtr_notify_torrent_completed(Glib::RefPtr<Session> const& core, tr_torrent_id_t tor_id)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
if (gtr_pref_flag_get(TR_KEY_torrent_complete_sound_enabled))
|
|
|
|
{
|
2021-10-18 20:22:31 +00:00
|
|
|
auto const argv = gtr_pref_strv_get(TR_KEY_torrent_complete_sound_command);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2022-09-10 13:19:54 +00:00
|
|
|
Glib::spawn_async({}, argv, TR_GLIB_SPAWN_FLAGS(SEARCH_PATH));
|
2021-10-18 20:22:31 +00:00
|
|
|
}
|
|
|
|
catch (Glib::SpawnError const&)
|
|
|
|
{
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!gtr_pref_flag_get(TR_KEY_torrent_complete_notification_enabled))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
g_return_if_fail(proxy != nullptr);
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2022-06-17 15:43:04 +00:00
|
|
|
auto const* const tor = core->find_torrent(tor_id);
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2022-06-17 15:43:04 +00:00
|
|
|
auto const n = TrNotification{ core, tor_id };
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
std::vector<Glib::ustring> actions;
|
2017-04-19 12:04:45 +00:00
|
|
|
if (server_supports_actions)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-11-28 03:17:47 +00:00
|
|
|
if (tr_torrentFileCount(tor) == 1)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-12-01 23:11:57 +00:00
|
|
|
actions.emplace_back("file");
|
|
|
|
actions.emplace_back(_("Open File"));
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-12-01 23:11:57 +00:00
|
|
|
actions.emplace_back("folder");
|
|
|
|
actions.emplace_back(_("Open Folder"));
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
std::map<Glib::ustring, Glib::VariantBase> hints;
|
2022-01-17 18:39:50 +00:00
|
|
|
hints.try_emplace("category", StringVariantType::create("transfer.complete"));
|
2020-07-28 21:51:01 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
proxy->call(
|
2021-08-15 09:41:48 +00:00
|
|
|
"Notify",
|
2021-10-18 20:22:31 +00:00
|
|
|
[n](auto& res) { notify_callback(res, n); },
|
|
|
|
make_variant_tuple(
|
|
|
|
Glib::ustring("Transmission"),
|
2022-11-09 16:58:36 +00:00
|
|
|
0U,
|
2021-10-18 20:22:31 +00:00
|
|
|
Glib::ustring("transmission"),
|
|
|
|
Glib::ustring(_("Torrent Complete")),
|
|
|
|
Glib::ustring(tr_torrentName(tor)),
|
|
|
|
actions,
|
|
|
|
hints,
|
|
|
|
-1));
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2022-06-17 15:43:04 +00:00
|
|
|
void gtr_notify_torrent_added(Glib::RefPtr<Session> const& core, tr_torrent_id_t tor_id)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-10-18 20:22:31 +00:00
|
|
|
g_return_if_fail(proxy != nullptr);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (!gtr_pref_flag_get(TR_KEY_torrent_added_notification_enabled))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-06-17 15:43:04 +00:00
|
|
|
auto const* const tor = core->find_torrent(tor_id);
|
2021-11-08 17:21:20 +00:00
|
|
|
|
|
|
|
std::vector<Glib::ustring> actions;
|
|
|
|
if (server_supports_actions)
|
|
|
|
{
|
2021-12-01 23:11:57 +00:00
|
|
|
actions.emplace_back("start-now");
|
|
|
|
actions.emplace_back(_("Start Now"));
|
2021-11-08 17:21:20 +00:00
|
|
|
}
|
|
|
|
|
2022-06-17 15:43:04 +00:00
|
|
|
auto const n = TrNotification{ core, tor_id };
|
2021-11-08 17:21:20 +00:00
|
|
|
|
2021-10-18 20:22:31 +00:00
|
|
|
proxy->call(
|
2021-08-15 09:41:48 +00:00
|
|
|
"Notify",
|
2021-11-08 17:21:20 +00:00
|
|
|
[n](auto& res) { notify_callback(res, n); },
|
2021-10-18 20:22:31 +00:00
|
|
|
make_variant_tuple(
|
|
|
|
Glib::ustring("Transmission"),
|
2022-11-09 16:58:36 +00:00
|
|
|
0U,
|
2021-10-18 20:22:31 +00:00
|
|
|
Glib::ustring("transmission"),
|
|
|
|
Glib::ustring(_("Torrent Added")),
|
2021-11-08 17:21:20 +00:00
|
|
|
Glib::ustring(tr_torrentName(tor)),
|
|
|
|
actions,
|
2021-10-18 20:22:31 +00:00
|
|
|
std::map<Glib::ustring, Glib::VariantBase>(),
|
|
|
|
-1));
|
2009-06-11 16:17:48 +00:00
|
|
|
}
|