2008-02-24 15:42:31 +00:00
|
|
|
/*
|
2014-01-19 01:09:44 +00:00
|
|
|
* This file Copyright (C) 2008-2014 Mnemosyne LLC
|
2008-02-24 15:42:31 +00:00
|
|
|
*
|
2014-01-21 03:10:30 +00:00
|
|
|
* It may be used under the GNU GPL versions 2 or 3
|
2014-01-19 01:09:44 +00:00
|
|
|
* or any future license endorsed by Mnemosyne LLC.
|
2008-09-23 19:11:04 +00:00
|
|
|
*
|
2008-02-24 15:42:31 +00:00
|
|
|
*/
|
|
|
|
|
2011-12-10 19:00:50 +00:00
|
|
|
#include <gio/gio.h>
|
|
|
|
|
2008-03-03 04:44:27 +00:00
|
|
|
#include <glib/gi18n.h>
|
2009-01-13 16:39:19 +00:00
|
|
|
#include "conf.h"
|
2008-02-24 15:42:31 +00:00
|
|
|
#include "notify.h"
|
2009-01-12 21:15:14 +00:00
|
|
|
#include "tr-prefs.h"
|
2011-12-10 19:00:50 +00:00
|
|
|
#include "util.h"
|
2008-02-24 15:42:31 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
#define NOTIFICATIONS_DBUS_NAME "org.freedesktop.Notifications"
|
|
|
|
#define NOTIFICATIONS_DBUS_CORE_OBJECT "/org/freedesktop/Notifications"
|
2011-12-10 19:00:50 +00:00
|
|
|
#define NOTIFICATIONS_DBUS_CORE_INTERFACE "org.freedesktop.Notifications"
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static GDBusProxy* proxy = NULL;
|
|
|
|
static GHashTable* active_notifications = NULL;
|
2011-12-10 19:00:50 +00:00
|
|
|
static gboolean server_supports_actions = FALSE;
|
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
typedef struct TrNotification
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
TrCore* core;
|
2017-04-21 07:40:57 +00:00
|
|
|
int torrent_id;
|
2021-08-15 09:41:48 +00:00
|
|
|
} TrNotification;
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static void tr_notification_free(gpointer data)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-09-25 10:47:09 +00:00
|
|
|
auto* n = static_cast<TrNotification*>(data);
|
2012-12-05 21:26:40 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (n->core != NULL)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
g_object_unref(G_OBJECT(n->core));
|
|
|
|
}
|
2012-12-05 21:26:40 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
g_free(n);
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 10:36:10 +00:00
|
|
|
static void get_capabilities_callback(GObject* source, GAsyncResult* res, gpointer user_data)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2020-08-18 10:36:10 +00:00
|
|
|
TR_UNUSED(user_data);
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
char** caps;
|
|
|
|
GVariant* result;
|
|
|
|
|
|
|
|
result = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, NULL);
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (result == NULL || !g_variant_is_of_type(result, G_VARIANT_TYPE("(as)")))
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-30 16:25:26 +00:00
|
|
|
if (result != NULL)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
g_variant_unref(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
g_variant_get(result, "(^a&s)", &caps);
|
|
|
|
|
2017-05-13 22:38:31 +00:00
|
|
|
for (int i = 0; caps[i] != NULL; i++)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
if (g_strcmp0(caps[i], "actions") == 0)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
server_supports_actions = TRUE;
|
|
|
|
break;
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
}
|
2012-12-05 21:26:40 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
g_free(caps);
|
|
|
|
g_variant_unref(result);
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
static void g_signal_callback(
|
|
|
|
GDBusProxy const* dbus_proxy,
|
|
|
|
char const* sender_name,
|
|
|
|
char const* signal_name,
|
|
|
|
GVariant* params,
|
2020-11-05 22:46:21 +00:00
|
|
|
gconstpointer user_data)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2020-08-18 10:36:10 +00:00
|
|
|
TR_UNUSED(dbus_proxy);
|
|
|
|
TR_UNUSED(sender_name);
|
|
|
|
TR_UNUSED(user_data);
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
g_return_if_fail(g_variant_is_of_type(params, G_VARIANT_TYPE("(u*)")));
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-09-25 10:47:09 +00:00
|
|
|
guint id;
|
2017-04-19 12:04:45 +00:00
|
|
|
g_variant_get(params, "(u*)", &id, NULL);
|
2021-09-25 10:47:09 +00:00
|
|
|
auto* n = static_cast<TrNotification*>(g_hash_table_lookup(active_notifications, GUINT_TO_POINTER(id)));
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (n == NULL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (g_strcmp0(signal_name, "NotificationClosed") == 0)
|
2009-11-22 18:55:24 +00:00
|
|
|
{
|
2021-08-07 21:24:11 +00:00
|
|
|
g_hash_table_remove(active_notifications, GUINT_TO_POINTER(id));
|
2009-11-22 18:55:24 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else if (g_strcmp0(signal_name, "ActionInvoked") == 0 && g_variant_is_of_type(params, G_VARIANT_TYPE("(us)")))
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2020-11-09 03:31:02 +00:00
|
|
|
tr_torrent const* tor = gtr_core_find_torrent(n->core, n->torrent_id);
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tor == NULL)
|
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
|
|
|
|
2020-11-09 03:31:02 +00:00
|
|
|
char* action = NULL;
|
2017-04-19 12:04:45 +00:00
|
|
|
g_variant_get(params, "(u&s)", NULL, &action);
|
|
|
|
|
|
|
|
if (g_strcmp0(action, "folder") == 0)
|
|
|
|
{
|
|
|
|
gtr_core_open_folder(n->core, n->torrent_id);
|
|
|
|
}
|
|
|
|
else if (g_strcmp0(action, "file") == 0)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
tr_info const* inf = tr_torrentInfo(tor);
|
|
|
|
char const* dir = tr_torrentGetDownloadDir(tor);
|
2017-04-19 12:04:45 +00:00
|
|
|
char* path = g_build_filename(dir, inf->files[0].name, NULL);
|
|
|
|
gtr_open_file(path);
|
|
|
|
g_free(path);
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-18 10:36:10 +00:00
|
|
|
static void dbus_proxy_ready_callback(GObject* source, GAsyncResult* res, gpointer user_data)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2020-08-18 10:36:10 +00:00
|
|
|
TR_UNUSED(source);
|
|
|
|
TR_UNUSED(user_data);
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
proxy = g_dbus_proxy_new_for_bus_finish(res, NULL);
|
|
|
|
|
|
|
|
if (proxy == NULL)
|
2009-01-12 18:48:20 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
g_warning("Failed to create proxy for %s", NOTIFICATIONS_DBUS_NAME);
|
|
|
|
return;
|
2009-01-12 18:48:20 +00:00
|
|
|
}
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
g_signal_connect(proxy, "g-signal", G_CALLBACK(g_signal_callback), NULL);
|
2021-08-15 09:41:48 +00:00
|
|
|
g_dbus_proxy_call(
|
|
|
|
proxy,
|
|
|
|
"GetCapabilities",
|
|
|
|
g_variant_new("()"),
|
|
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
|
|
-1,
|
|
|
|
NULL,
|
|
|
|
get_capabilities_callback,
|
|
|
|
NULL);
|
2008-02-24 15:42:31 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
void gtr_notify_init(void)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2021-08-07 21:24:11 +00:00
|
|
|
active_notifications = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, tr_notification_free);
|
2021-08-15 09:41:48 +00:00
|
|
|
g_dbus_proxy_new_for_bus(
|
|
|
|
G_BUS_TYPE_SESSION,
|
|
|
|
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
|
|
|
|
NULL,
|
|
|
|
NOTIFICATIONS_DBUS_NAME,
|
|
|
|
NOTIFICATIONS_DBUS_CORE_OBJECT,
|
|
|
|
NOTIFICATIONS_DBUS_CORE_INTERFACE,
|
|
|
|
NULL,
|
|
|
|
dbus_proxy_ready_callback,
|
|
|
|
NULL);
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static void notify_callback(GObject* source, GAsyncResult* res, gpointer user_data)
|
2009-06-11 16:17:48 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
GVariant* result;
|
2021-09-25 10:47:09 +00:00
|
|
|
auto* n = static_cast<TrNotification*>(user_data);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
result = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, NULL);
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (result == NULL || !g_variant_is_of_type(result, G_VARIANT_TYPE("(u)")))
|
2009-06-11 16:17:48 +00:00
|
|
|
{
|
2017-04-30 16:25:26 +00:00
|
|
|
if (result != NULL)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
g_variant_unref(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
tr_notification_free(n);
|
|
|
|
return;
|
2009-06-11 16:17:48 +00:00
|
|
|
}
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2021-08-07 21:24:11 +00:00
|
|
|
guint id;
|
|
|
|
g_variant_get(result, "(u)", &id);
|
|
|
|
g_hash_table_insert(active_notifications, GUINT_TO_POINTER(id), n);
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
g_variant_unref(result);
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
void gtr_notify_torrent_completed(TrCore* core, int torrent_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-05-25 16:21:41 +00:00
|
|
|
char** argv = gtr_pref_strv_get(TR_KEY_torrent_complete_sound_command);
|
2021-08-15 09:41:48 +00:00
|
|
|
g_spawn_async(
|
|
|
|
NULL /*cwd*/,
|
2021-05-25 16:21:41 +00:00
|
|
|
argv,
|
|
|
|
NULL /*envp*/,
|
|
|
|
G_SPAWN_SEARCH_PATH,
|
|
|
|
NULL /*GSpawnChildSetupFunc*/,
|
|
|
|
NULL /*user_data*/,
|
|
|
|
NULL /*child_pid*/,
|
|
|
|
NULL);
|
|
|
|
g_strfreev(argv);
|
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
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
g_return_if_fail(G_IS_DBUS_PROXY(proxy));
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2020-11-09 03:31:02 +00:00
|
|
|
tr_torrent const* const tor = gtr_core_find_torrent(core, torrent_id);
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2020-11-09 03:31:02 +00:00
|
|
|
TrNotification* const n = g_new0(TrNotification, 1);
|
fix: gcc warnings in libtransmission/ and utils/ (#843)
* fix: __attribute__(__printf__) warnings
* fix: implicit fallthrough warning
* fixup! fix: implicit fallthrough warning
* fix: disable warnings for 3rd party code
Since we want to leave upstream code as-is
* fixup! fix: disable warnings for 3rd party code
* fixup! fix: disable warnings for 3rd party code
* silence spurious alignment warning
Xrefs
Discussion: https://stackoverflow.com/a/35554349
Macro inspiration: https://pagure.io/SSSD/sssd/blob/90ac46f71068d131391492360a8553bdd005b5a7/f/src/util/util_safealign.h#_35
* fixup! fix: disable warnings for 3rd party code
* fixup! fix: implicit fallthrough warning
* make uncrustify happy
* remove uncrustify-test.sh
that's probably off-topic for this PR
* fixup! fix: __attribute__(__printf__) warnings
* Update libtransmission/CMakeLists.txt
Co-Authored-By: ckerr <ckerr@github.com>
* fixup! silence spurious alignment warning
* use -w for DISABLE_WARNINGS in Clang
* refactor: fix libtransmission deprecation warnings
* fix: pthread_create's start_routine's return value
This was defined as `void` on non-Windows but should have been `void*`
* chore: uncrustify
* fix: add DISABLE_WARNINGS option for SunPro Studio
* fix "unused in lambda capture" warnings by clang++
* fix 'increases required alignment' warning
Caused from storing int16_t's in a char array.
* fix net.c 'increases required alignment' warning
The code passes in a `struct sockaddr_storage*` which is a padded struct
large enough for the necessary alignment. Unfortunately it was recast as
a `struct sockaddr*` which has less padding and a smaller alignment. The
warning occrred because of these differing alignments.
* make building quieter so warnings are more visible
* fixup! fix 'increases required alignment' warning
* Fix -Wcast-function-type warnings in GTK+ app code
https://gitlab.gnome.org/GNOME/gnome-terminal/issues/96 talks about both
the issue and its solution.
GCC 8's -Wcast-function-type, enabled by -Wextra, is problematic in glib
applications because it's idiomatic there to recast function signatures,
e.g. `g_slist_free(list, (GFunc)g_free, NULL);`.
Disabling the warning with pragmas causes "unrecognized pragma" warnings
on clang and older versions of gcc, and disabling the warning could miss
actual bugs. GCC defines `void (*)(void)` as a special case that matches
anything so we can silence warnings by double-casting through GCallback.
In the previous example, the warning is silenced by changing the code to
read `g_slist_free(list, (GFunc)(GCallback)g_free, NULL);`).
* fixup! fix "unused in lambda capture" warnings by clang++
* fixup! fix "unused in lambda capture" warnings by clang++
* fix two more libtransmission compiler warnings
* fix: in watchdir, use TR_ENABLE_ASSERTS not NDEBUG
2019-11-06 17:27:03 +00:00
|
|
|
g_object_ref(G_OBJECT(core));
|
|
|
|
n->core = core;
|
2017-04-19 12:04:45 +00:00
|
|
|
n->torrent_id = torrent_id;
|
2011-12-10 19:00:50 +00:00
|
|
|
|
2020-07-28 21:51:01 +00:00
|
|
|
GVariantBuilder actions_builder;
|
2017-04-19 12:04:45 +00:00
|
|
|
g_variant_builder_init(&actions_builder, G_VARIANT_TYPE("as"));
|
|
|
|
if (server_supports_actions)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
tr_info const* inf = tr_torrentInfo(tor);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (inf->fileCount == 1)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
g_variant_builder_add(&actions_builder, "s", "file");
|
|
|
|
g_variant_builder_add(&actions_builder, "s", _("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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
g_variant_builder_add(&actions_builder, "s", "folder");
|
|
|
|
g_variant_builder_add(&actions_builder, "s", _("Open Folder"));
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 21:51:01 +00:00
|
|
|
GVariantBuilder hints_builder;
|
|
|
|
g_variant_builder_init(&hints_builder, G_VARIANT_TYPE("a{sv}"));
|
|
|
|
g_variant_builder_add(&hints_builder, "{sv}", "category", g_variant_new_string("transfer.complete"));
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
g_dbus_proxy_call(
|
|
|
|
proxy,
|
|
|
|
"Notify",
|
|
|
|
g_variant_new(
|
|
|
|
"(susssasa{sv}i)",
|
|
|
|
"Transmission",
|
|
|
|
0,
|
|
|
|
"transmission",
|
|
|
|
_("Torrent Complete"),
|
|
|
|
tr_torrentName(tor),
|
|
|
|
&actions_builder,
|
|
|
|
&hints_builder,
|
|
|
|
-1),
|
|
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
|
|
-1,
|
|
|
|
NULL,
|
|
|
|
notify_callback,
|
|
|
|
n);
|
2011-12-10 19:00:50 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
void gtr_notify_torrent_added(char const* name)
|
2011-12-10 19:00:50 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
TrNotification* n;
|
|
|
|
|
|
|
|
g_return_if_fail(G_IS_DBUS_PROXY(proxy));
|
|
|
|
|
|
|
|
if (!gtr_pref_flag_get(TR_KEY_torrent_added_notification_enabled))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = g_new0(TrNotification, 1);
|
2021-08-15 09:41:48 +00:00
|
|
|
g_dbus_proxy_call(
|
|
|
|
proxy,
|
|
|
|
"Notify",
|
|
|
|
g_variant_new("(susssasa{sv}i)", "Transmission", 0, "transmission", _("Torrent Added"), name, NULL, NULL, -1),
|
|
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
|
|
-1,
|
|
|
|
NULL,
|
|
|
|
notify_callback,
|
|
|
|
n);
|
2009-06-11 16:17:48 +00:00
|
|
|
}
|