diff --git a/gtk/conf.c b/gtk/conf.c index f46a74356..a3f8860a7 100644 --- a/gtk/conf.c +++ b/gtk/conf.c @@ -93,9 +93,7 @@ tr_prefs_init_defaults( tr_benc * d ) tr_bencDictAddBool( d, PREF_KEY_SHOW_BACKUP_TRACKERS, FALSE ); tr_bencDictAddStr ( d, PREF_KEY_STATUSBAR_STATS, "total-ratio" ); - tr_bencDictAddStr ( d, PREF_KEY_TORRENT_ADDED_NOTIFICATION_COMMAND, "notify-send -c transfer -i transmission '%s' '%s'" ); tr_bencDictAddBool( d, PREF_KEY_TORRENT_ADDED_NOTIFICATION_ENABLED, true ); - tr_bencDictAddStr ( d, PREF_KEY_TORRENT_COMPLETE_NOTIFICATION_COMMAND, "notify-send -c transfer.complete -i transmission '%s' '%s'" ); tr_bencDictAddBool( d, PREF_KEY_TORRENT_COMPLETE_NOTIFICATION_ENABLED, true ); tr_bencDictAddStr ( d, PREF_KEY_TORRENT_COMPLETE_SOUND_COMMAND, "canberra-gtk-play -i complete-download -d 'transmission torrent downloaded'" ); tr_bencDictAddBool( d, PREF_KEY_TORRENT_COMPLETE_SOUND_ENABLED, true ); diff --git a/gtk/main.c b/gtk/main.c index d88b565a8..9c92a0e4c 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -46,6 +46,7 @@ #include "hig.h" #include "makemeta-ui.h" #include "msgwin.h" +#include "notify.h" #include "open-dialog.h" #include "relocate.h" #include "stats.h" @@ -643,6 +644,9 @@ main( int argc, char ** argv ) gtr_pref_init( cbdata.config_dir ); g_mkdir_with_parents( cbdata.config_dir, 0755 ); + /* init notifications */ + gtr_notify_init( ); + /* init the application for the specified config dir */ stat( cbdata.config_dir, &sb ); application_id = g_strdup_printf( "com.transmissionbt.transmission_%lu_%lu", (unsigned long)sb.st_dev, (unsigned long)sb.st_ino ); diff --git a/gtk/notify.c b/gtk/notify.c index f163133bb..5b816be20 100644 --- a/gtk/notify.c +++ b/gtk/notify.c @@ -10,38 +10,243 @@ * $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 ) +{ + 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 ) { - if( gtr_pref_flag_get( PREF_KEY_TORRENT_COMPLETE_SOUND_ENABLED ) ) + 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 char * cmd = gtr_pref_string_get( PREF_KEY_TORRENT_COMPLETE_SOUND_COMMAND ); - g_spawn_command_line_async( cmd, NULL ); + 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" ) ); + } } - if( gtr_pref_flag_get( PREF_KEY_TORRENT_COMPLETE_NOTIFICATION_ENABLED ) ) - { - const tr_torrent * tor = gtr_core_find_torrent( core, torrent_id ); - const char * fmt = gtr_pref_string_get( PREF_KEY_TORRENT_COMPLETE_NOTIFICATION_COMMAND ); - char * cmd = g_strdup_printf( fmt, _( "Torrent Complete" ), ( tor ? tr_torrentName( tor ) : "" ) ); - g_spawn_command_line_async( cmd, NULL ); - g_free( cmd ); - } + 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 ) { - if( gtr_pref_flag_get( PREF_KEY_TORRENT_ADDED_NOTIFICATION_ENABLED ) ) - { - const char * fmt = gtr_pref_string_get( PREF_KEY_TORRENT_ADDED_NOTIFICATION_COMMAND ); - char * cmd = g_strdup_printf( fmt, _( "Torrent Added" ), name ); - g_spawn_command_line_async( cmd, NULL ); - g_free( cmd ); - } + 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 ); } diff --git a/gtk/notify.h b/gtk/notify.h index a3ff9795c..c462a7b82 100644 --- a/gtk/notify.h +++ b/gtk/notify.h @@ -15,6 +15,8 @@ #include "tr-core.h" +void gtr_notify_init( void ); + void gtr_notify_torrent_added ( const char * name ); void gtr_notify_torrent_completed ( TrCore * core, int torrent_id ); diff --git a/gtk/tr-prefs.h b/gtk/tr-prefs.h index f1c315acc..65c6a3de8 100644 --- a/gtk/tr-prefs.h +++ b/gtk/tr-prefs.h @@ -42,9 +42,7 @@ GtkWidget * gtr_prefs_dialog_new( GtkWindow * parent, GObject * core ); #define PREF_KEY_STATUSBAR "show-statusbar" #define PREF_KEY_STATUSBAR_STATS "statusbar-stats" #define PREF_KEY_TOOLBAR "show-toolbar" -#define PREF_KEY_TORRENT_ADDED_NOTIFICATION_COMMAND "torrent-added-notification-command" #define PREF_KEY_TORRENT_ADDED_NOTIFICATION_ENABLED "torrent-added-notification-enabled" -#define PREF_KEY_TORRENT_COMPLETE_NOTIFICATION_COMMAND "torrent-complete-notification-command" #define PREF_KEY_TORRENT_COMPLETE_NOTIFICATION_ENABLED "torrent-complete-notification-enabled" #define PREF_KEY_TORRENT_COMPLETE_SOUND_COMMAND "torrent-complete-sound-command" #define PREF_KEY_TORRENT_COMPLETE_SOUND_ENABLED "torrent-complete-sound-enabled"