/* * Implementation of the proxy around Rhythmbox's Bonobo interface. * * Rhythmbox Applet * Copyright (C) 2004 Paul Kuliniewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "rb-proxy.h" #include #include "Rhythmbox.h" #define POLL_DELAY 5000 #define STATUS_DELAY 500 struct RbProxyPrivate { GNOME_Rhythmbox rb; Bonobo_PropertyBag pb; CORBA_Environment ev; guint poll; guint timer; }; typedef enum { SONG_CHANGED, PLAYING_CHANGED, TIME_UPDATED, LAST_SIGNAL } RbProxySignal; static GObjectClass *parent_class; static guint rb_proxy_signals[LAST_SIGNAL] = { 0 }; /* Function declarations */ static void rb_proxy_class_init (RbProxyClass *klass); static void rb_proxy_init (RbProxy *proxy); static void rb_proxy_finalize (GObject *object); static void rb_proxy_connect (RbProxy *proxy, gboolean launch); static void rb_proxy_disconnect (RbProxy *proxy); static gboolean rb_proxy_is_connected (RbProxy *proxy); static gboolean rb_proxy_poll_cb (RbProxy *proxy); static void report_error (RbProxy *proxy); static void song_change_cb (BonoboListener *listener, const char *event_name, const CORBA_any *any, CORBA_Environment *ev, RbProxy *proxy); static void playing_change_cb (BonoboListener *listener, const char *event_name, const CORBA_any *any, CORBA_Environment *ev, RbProxy *proxy); static gboolean time_update_cb (RbProxy *proxy); /* GType stuff */ GType rb_proxy_get_type (void) { static GType this_type = 0; if (!this_type) { static const GTypeInfo this_info = { sizeof (RbProxyClass), NULL, NULL, (GClassInitFunc) rb_proxy_class_init, NULL, NULL, sizeof (RbProxy), 0, (GInstanceInitFunc) rb_proxy_init, NULL }; this_type = g_type_register_static (G_TYPE_OBJECT, "RbProxy", &this_info, 0); } return this_type; } static void rb_proxy_class_init (RbProxyClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; parent_class = g_type_class_peek_parent (klass); object_class->finalize = rb_proxy_finalize; rb_proxy_signals[SONG_CHANGED] = g_signal_new ("song_changed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RbProxyClass, song_changed), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); rb_proxy_signals[PLAYING_CHANGED] = g_signal_new ("playing_changed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RbProxyClass, playing_changed), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); rb_proxy_signals[TIME_UPDATED] = g_signal_new ("time_updated", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RbProxyClass, time_updated), NULL, NULL, g_cclosure_marshal_VOID__LONG, G_TYPE_NONE, 1, G_TYPE_LONG); } static void rb_proxy_init (RbProxy *proxy) { proxy->priv = g_new0 (RbProxyPrivate, 1); if (!bonobo_is_initialized ()) { gint fake_argc = 0; bonobo_init (&fake_argc, NULL); bonobo_activate (); } proxy->priv->rb = CORBA_OBJECT_NIL; proxy->priv->pb = CORBA_OBJECT_NIL; CORBA_exception_init (&proxy->priv->ev); } static void rb_proxy_finalize (GObject *object) { RbProxy *proxy = RB_PROXY (object); rb_proxy_disconnect (proxy); CORBA_exception_free (&proxy->priv->ev); g_free (proxy->priv); parent_class->finalize (object); } RbProxy * rb_proxy_new (void) { RbProxy *proxy; proxy = g_object_new (RB_TYPE_PROXY, NULL); proxy->priv->poll = 0; proxy->priv->timer = 0; rb_proxy_connect (proxy, FALSE); return proxy; } /* Interface functions */ void rb_proxy_play_pause_stop (RbProxy *proxy) { g_return_if_fail (proxy != NULL); rb_proxy_connect (proxy, TRUE); if (rb_proxy_is_connected (proxy)) { GNOME_Rhythmbox_playPause (proxy->priv->rb, &proxy->priv->ev); if (BONOBO_EX (&proxy->priv->ev)) rb_proxy_disconnect (proxy); } } void rb_proxy_previous (RbProxy *proxy) { g_return_if_fail (proxy != NULL); if (rb_proxy_is_connected (proxy)) { GNOME_Rhythmbox_previous (proxy->priv->rb, &proxy->priv->ev); if (BONOBO_EX (&proxy->priv->ev)) rb_proxy_disconnect (proxy); } } void rb_proxy_next (RbProxy *proxy) { g_return_if_fail (proxy != NULL); if (rb_proxy_is_connected (proxy)) { GNOME_Rhythmbox_next (proxy->priv->rb, &proxy->priv->ev); if (BONOBO_EX (&proxy->priv->ev)) rb_proxy_disconnect (proxy); } } RbProxySongInfo * rb_proxy_get_song_info (RbProxy *proxy) { GNOME_Rhythmbox_SongInfo *song = NULL; g_return_val_if_fail (proxy != NULL, song); if (rb_proxy_is_connected (proxy)) { CORBA_any *any = bonobo_pbclient_get_value ( proxy->priv->pb, "song", TC_GNOME_Rhythmbox_SongInfo, &proxy->priv->ev); if (BONOBO_EX (&proxy->priv->ev)) rb_proxy_disconnect (proxy); if (any != NULL) { song = (GNOME_Rhythmbox_SongInfo *) any->_value; any->_release = FALSE; CORBA_free (any); } } return song; } glong rb_proxy_get_play_time (RbProxy *proxy) { glong play_time = -1; g_return_val_if_fail (proxy != NULL, play_time); if (rb_proxy_is_connected (proxy)) { play_time = GNOME_Rhythmbox_getPlayingTime ( proxy->priv->rb, &proxy->priv->ev); if (BONOBO_EX (&proxy->priv->ev)) { rb_proxy_disconnect (proxy); play_time = -1; } } return play_time; } gboolean rb_proxy_is_playing (RbProxy *proxy) { gboolean playing = FALSE; g_return_val_if_fail (proxy != NULL, playing); if (rb_proxy_is_connected (proxy)) { playing = bonobo_pbclient_get_boolean ( proxy->priv->pb, "playing", &proxy->priv->ev); if (BONOBO_EX (&proxy->priv->ev)) rb_proxy_disconnect (proxy); } return playing; } gboolean rb_proxy_is_pausable (RbProxy *proxy) { gboolean pausable = FALSE; RbProxySongInfo *song; g_return_val_if_fail (proxy != NULL, pausable); song = rb_proxy_get_song_info (proxy); if (song != NULL) { pausable = (song->duration > 0); rb_proxy_song_info_free (song); } return pausable; } void rb_proxy_song_info_free (RbProxySongInfo *song) { CORBA_free (song); } /* Internal functions */ static void rb_proxy_connect (RbProxy *proxy, gboolean launch) { const gchar *query = launch ? "((repo_ids.has('IDL:GNOME/Rhythmbox:1.0')))" : "((repo_ids.has('IDL:GNOME/Rhythmbox:1.0')) AND (_active == TRUE))"; if (rb_proxy_is_connected (proxy)) return; if (proxy->priv->poll != 0) { g_source_remove (proxy->priv->poll); proxy->priv->poll = 0; } proxy->priv->rb = bonobo_activation_activate ( query, NULL, 0, NULL, &proxy->priv->ev); if (proxy->priv->rb == CORBA_OBJECT_NIL) { proxy->priv->poll = g_timeout_add ( POLL_DELAY, (GSourceFunc) rb_proxy_poll_cb, proxy); if (launch) report_error (proxy); return; } proxy->priv->pb = GNOME_Rhythmbox_getPlayerProperties (proxy->priv->rb, &proxy->priv->ev); if (BONOBO_EX (&proxy->priv->ev)) report_error (proxy); bonobo_event_source_client_add_listener ( proxy->priv->pb, (BonoboListenerCallbackFn) song_change_cb, "Bonobo/Property:change:song", &proxy->priv->ev, proxy); if (BONOBO_EX (&proxy->priv->ev)) report_error (proxy); bonobo_event_source_client_add_listener ( proxy->priv->pb, (BonoboListenerCallbackFn) playing_change_cb, "Bonobo/Property:change:playing", &proxy->priv->ev, proxy); if (BONOBO_EX (&proxy->priv->ev)) report_error (proxy); if (rb_proxy_is_connected (proxy)) { proxy->priv->timer = g_timeout_add ( STATUS_DELAY, (GSourceFunc) time_update_cb, proxy); } } static void rb_proxy_disconnect (RbProxy *proxy) { if (proxy->priv->timer != 0) { g_source_remove (proxy->priv->timer); proxy->priv->timer = 0; } if (proxy->priv->pb != CORBA_OBJECT_NIL) { bonobo_object_release_unref ((Bonobo_Unknown) proxy->priv->pb, NULL); proxy->priv->pb = CORBA_OBJECT_NIL; } if (proxy->priv->rb != CORBA_OBJECT_NIL) { /* FIXME: Releasing actually closes Rhythmbox?! */ #if 0 bonobo_object_release_unref ((Bonobo_Unknown) proxy->priv->rb, NULL); #endif proxy->priv->rb = CORBA_OBJECT_NIL; } g_signal_emit_by_name (proxy, "playing_changed", FALSE); g_signal_emit_by_name (proxy, "song_changed", NULL); g_signal_emit_by_name (proxy, "time_updated", -1); proxy->priv->poll = g_timeout_add ( POLL_DELAY, (GSourceFunc) rb_proxy_poll_cb, proxy); } static gboolean rb_proxy_is_connected (RbProxy *proxy) { return (proxy->priv->rb != CORBA_OBJECT_NIL); } static gboolean rb_proxy_poll_cb (RbProxy *proxy) { rb_proxy_connect (proxy, FALSE); return !rb_proxy_is_connected (proxy); } static void report_error (RbProxy *proxy) { gchar *what = bonobo_exception_get_text (&proxy->priv->ev); g_warning ("Bonobo error: %s", what); g_free (what); rb_proxy_disconnect (proxy); } static void song_change_cb (BonoboListener *listener, const char *event_name, const CORBA_any *any, CORBA_Environment *ev, RbProxy *proxy) { const GNOME_Rhythmbox_SongInfo *rb_song_info; g_return_if_fail ( CORBA_TypeCode_equivalent (any->_type, TC_GNOME_Rhythmbox_SongInfo, NULL)); rb_song_info = (const GNOME_Rhythmbox_SongInfo *) any->_value; g_signal_emit_by_name (proxy, "song_changed", rb_song_info); /* TODO: verify we don't need to release rb_song_info */ } static void playing_change_cb (BonoboListener *listener, const char *event_name, const CORBA_any *any, CORBA_Environment *ev, RbProxy *proxy) { gboolean playing; g_return_if_fail ( CORBA_TypeCode_equivalent (any->_type, TC_CORBA_boolean, NULL)); playing = BONOBO_ARG_GET_BOOLEAN (any); g_signal_emit_by_name (proxy, "playing_changed", playing); } static gboolean time_update_cb (RbProxy *proxy) { glong play_time; play_time = rb_proxy_get_play_time (proxy); g_signal_emit_by_name (proxy, "time_updated", play_time); return rb_proxy_is_connected (proxy); }