/* * 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: notify.c 13107 2011-12-10 19:00:50Z jordan $ */ #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 ) { GVariant *result; char **caps; int i; 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 ) { TrNotification * n; guint id; 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 ) { TrNotification * n = user_data; 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( "(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 ); 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 ); }