/* * This file Copyright (C) Mnemosyne LLC * * This file is licensed by the GPL version 2. Works owned by the * Transmission project are granted a special exemption to clause 2 (b) * so that the bulk of its code can remain under the MIT license. * This exemption does not extend to derived works not owned by * the Transmission project. * * $Id$ */ #include /* strcmp () */ #include #include #include "conf.h" #include "notify.h" #include "tr-prefs.h" #include "util.h" #define NOTIFICATIONS_DBUS_NAME "org.freedesktop.Notifications" #define NOTIFICATIONS_DBUS_CORE_OBJECT "/org/freedesktop/Notifications" #define NOTIFICATIONS_DBUS_CORE_INTERFACE "org.freedesktop.Notifications" static GDBusProxy *proxy = NULL; static GHashTable *active_notifications = NULL; static gboolean server_supports_actions = FALSE; typedef struct _TrNotification { guint id; TrCore * core; int torrent_id; } TrNotification; static void tr_notification_free (gpointer data) { TrNotification * n = data; if (n->core) g_object_unref (G_OBJECT (n->core)); g_free (n); } static void get_capabilities_callback (GObject * source, GAsyncResult * res, gpointer user_data UNUSED) { int i; char ** caps; GVariant * result; result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, NULL); if (!result || !g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)"))) { if (result) g_variant_unref (result); return; } g_variant_get (result, "(^a&s)", &caps); for (i=0; caps[i]; i++) { if (strcmp (caps[i], "actions") == 0) { server_supports_actions = TRUE; break; } } g_free (caps); g_variant_unref (result); } static void g_signal_callback (GDBusProxy * proxy UNUSED, char * sender_name UNUSED, char * signal_name, GVariant * params, gpointer user_data UNUSED) { guint id; TrNotification * n; g_return_if_fail (g_variant_is_of_type (params, G_VARIANT_TYPE ("(u*)"))); g_variant_get (params, "(u*)", &id, NULL); n = g_hash_table_lookup (active_notifications, GINT_TO_POINTER ((int *) &id)); if (n == NULL) return; if (strcmp (signal_name, "NotificationClosed") == 0) { g_hash_table_remove (active_notifications, GINT_TO_POINTER ((int *) &n->id)); } else if (strcmp (signal_name, "ActionInvoked") == 0 && g_variant_is_of_type (params, G_VARIANT_TYPE ("(us)"))) { char * action; tr_torrent * tor; tor = gtr_core_find_torrent (n->core, n->torrent_id); if (tor == NULL) return; g_variant_get (params, "(u&s)", NULL, &action); if (strcmp (action, "folder") == 0) { gtr_core_open_folder (n->core, n->torrent_id); } else if (strcmp (action, "file") == 0) { const tr_info * inf = tr_torrentInfo (tor); const char * dir = tr_torrentGetDownloadDir (tor); char * path = g_build_filename (dir, inf->files[0].name, NULL); gtr_open_file (path); g_free (path); } } } static void dbus_proxy_ready_callback (GObject * source UNUSED, GAsyncResult * res, gpointer user_data UNUSED) { proxy = g_dbus_proxy_new_for_bus_finish (res, NULL); if (proxy == NULL) { g_warning ("Failed to create proxy for %s", NOTIFICATIONS_DBUS_NAME); return; } g_signal_connect (proxy, "g-signal", G_CALLBACK (g_signal_callback), NULL); g_dbus_proxy_call (proxy, "GetCapabilities", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, get_capabilities_callback, NULL); } void gtr_notify_init (void) { active_notifications = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, tr_notification_free); 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); } static void notify_callback (GObject * source, GAsyncResult * res, gpointer user_data) { GVariant * result; TrNotification * n = user_data; result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, NULL); if (!result || !g_variant_is_of_type (result, G_VARIANT_TYPE ("(u)"))) { if (result) g_variant_unref (result); tr_notification_free (n); return; } g_variant_get (result, "(u)", &n->id); g_hash_table_insert (active_notifications, GINT_TO_POINTER ((int *)&n->id), n); g_variant_unref (result); } void gtr_notify_torrent_completed (TrCore * core, int torrent_id) { GVariantBuilder actions_builder; TrNotification * n; tr_torrent * tor; const char * cmd = gtr_pref_string_get (PREF_KEY_TORRENT_COMPLETE_SOUND_COMMAND); if (gtr_pref_flag_get (PREF_KEY_TORRENT_COMPLETE_SOUND_ENABLED)) g_spawn_command_line_async (cmd, NULL); if (!gtr_pref_flag_get (PREF_KEY_TORRENT_COMPLETE_NOTIFICATION_ENABLED)) return; g_return_if_fail (G_IS_DBUS_PROXY (proxy)); tor = gtr_core_find_torrent (core, torrent_id); n = g_new0 (TrNotification, 1); n->core = g_object_ref (G_OBJECT (core)); n->torrent_id = torrent_id; g_variant_builder_init (&actions_builder, G_VARIANT_TYPE ("as")); if (server_supports_actions) { const tr_info * inf = tr_torrentInfo (tor); if (inf->fileCount == 1) { g_variant_builder_add (&actions_builder, "s", "file"); g_variant_builder_add (&actions_builder, "s", _("Open File")); } else { g_variant_builder_add (&actions_builder, "s", "folder"); g_variant_builder_add (&actions_builder, "s", _("Open Folder")); } } g_dbus_proxy_call (proxy, "Notify", g_variant_new ("(susssasa{sv}i)", "Transmission", n->id, "transmission", _("Torrent Complete"), tr_torrentName (tor), &actions_builder, NULL, -1), G_DBUS_CALL_FLAGS_NONE, -1, NULL, notify_callback, n); } void gtr_notify_torrent_added (const char * name) { TrNotification * n; g_return_if_fail (G_IS_DBUS_PROXY (proxy)); if (!gtr_pref_flag_get (PREF_KEY_TORRENT_ADDED_NOTIFICATION_ENABLED)) return; n = g_new0 (TrNotification, 1); 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); }