2006-08-04 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / shell / main.c
blobd3bf50c9ffafbc18bf9bf798ea9bee6d45722956
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: The Rhythmbox main entrypoint
5 * Copyright (C) 2002 Jorn Baayen
6 * Copyright (C) 2003,2004 Colin Walters <walters@gnome.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <config.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <time.h>
29 #include <string.h>
30 #include <libintl.h>
31 #include <locale.h>
33 #include <glib/gi18n.h>
34 #include <gdk/gdkx.h> /* For _get_user_time... */
35 #include <gtk/gtk.h>
36 #include <glade/glade-init.h>
37 #include <libgnome/gnome-program.h>
38 #include <libgnomeui/gnome-ui-init.h>
39 #include <libgnomeui/gnome-authentication-manager.h>
41 #ifdef HAVE_GSTREAMER
42 #include <gst/gst.h>
43 #ifdef HAVE_GSTREAMER_0_8
44 #include <gst/gconf/gconf.h>
45 #include <gst/control/control.h>
46 #endif
47 #endif
48 #ifdef WITH_RHYTHMDB_GDA
49 #include <libgda/libgda.h>
50 #endif
52 #include "rb-refstring.h"
53 #include "rb-shell.h"
54 #include "rb-shell-player.h"
55 #include "rb-debug.h"
56 #include "rb-dialog.h"
57 #include "rb-file-helpers.h"
58 #include "rb-stock-icons.h"
59 #include "rb-util.h"
60 #include "eel-gconf-extensions.h"
61 #include "rb-util.h"
63 #if WITH_DBUS
64 #include <dbus/dbus-glib.h>
65 #include "rb-shell-glue.h"
66 #include "rb-shell-player-glue.h"
67 #include "rb-playlist-manager.h"
68 #include "rb-playlist-manager-glue.h"
69 #elif WITH_OLD_DBUS
70 #include <dbus/dbus.h>
71 #include <dbus/dbus-glib.h>
72 #include <dbus/dbus-glib-lowlevel.h>
73 #endif
75 #include <nautilus-burn-drive.h>
76 #ifndef NAUTILUS_BURN_CHECK_VERSION
77 #define NAUTILUS_BURN_CHECK_VERSION(a,b,c) FALSE
78 #endif
80 #if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
81 #include <nautilus-burn.h>
82 #endif
84 #define HAVE_LIBGNOME_GOPTION defined(GNOME_PARAM_GOPTION_CONTEXT)
86 static gboolean debug = FALSE;
87 static char *debug_match = NULL;
88 static gboolean quit = FALSE;
89 static gboolean no_registration = FALSE;
90 static gboolean no_update = FALSE;
91 static gboolean dry_run = FALSE;
92 static char *rhythmdb_file = NULL;
93 static char *playlists_file = NULL;
94 static char **remaining_args = NULL;
96 #if WITH_DBUS
97 static gboolean load_uri_args (const char **args, GFunc handler, gpointer user_data);
98 static void dbus_load_uri (const char *filename, DBusGProxy *proxy);
99 #endif
101 static void main_shell_weak_ref_cb (gpointer data, GObject *objptr);
103 #if WITH_OLD_DBUS
104 static void register_dbus_handler (DBusConnection *connection, RBShell *shell);
105 static gboolean send_present_message (DBusConnection *connection, guint32 current_time);
107 #endif
110 main (int argc, char **argv)
112 GnomeProgram *program = NULL;
113 #if WITH_DBUS || WITH_OLD_DBUS
114 DBusGConnection *session_bus;
115 GError *error = NULL;
116 #endif
117 RBShell *rb_shell;
118 char **new_argv;
119 gboolean activated;
122 GOptionContext *context;
123 static const GOptionEntry options [] = {
124 { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, N_("Enable debug output"), NULL },
125 { "debug-match", 'D', 0, G_OPTION_ARG_STRING, &debug_match, N_("Enable debug output matching a specified string"), NULL },
126 { "no-update", 0, 0, G_OPTION_ARG_NONE, &no_update, N_("Do not update the library with file changes"), NULL },
127 { "no-registration", 'n', 0, G_OPTION_ARG_NONE, &no_registration, N_("Do not register the shell"), NULL },
128 { "dry-run", 0, 0, G_OPTION_ARG_NONE, &dry_run, N_("Don't save any data permanently (implies --no-registration)"), NULL },
129 { "rhythmdb-file", 0, 0, G_OPTION_ARG_STRING, &rhythmdb_file, N_("Path for database file to use"), NULL },
130 { "playlists-file", 0, 0, G_OPTION_ARG_STRING, &playlists_file, N_("Path for playlists file to use"), NULL },
131 { "quit", 'q', 0, G_OPTION_ARG_NONE, &quit, N_("Quit Rhythmbox"), NULL },
132 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining_args, NULL, N_("[URI...]") },
133 { NULL }
136 rb_profile_start ("starting rhythmbox");
138 context = g_option_context_new (NULL);
139 g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
141 /* Disable event sounds for now by passing "--disable-sound" to libgnomeui.
142 * See: http://bugzilla.gnome.org/show_bug.cgi?id=119222 */
143 new_argv = g_strdupv (argv);
144 new_argv = g_realloc (new_argv, (argc+2)*sizeof(char*));
145 new_argv[argc] = g_strdup ("--disable-sound");
146 new_argv[argc+1] = NULL;
148 #ifdef HAVE_GSTREAMER_0_8
149 /* To pass options to GStreamer in 0.8, RB needs to use popt, for
150 * now, GStreamer can live without them (people can still use env
151 * vars, after all) */
152 gst_init (NULL, NULL);
153 #elif HAVE_GSTREAMER_0_10
154 rb_profile_start ("initializing gstreamer");
155 g_option_context_add_group (context, gst_init_get_option_group ());
156 rb_profile_end ("initializing gstreamer");
157 #endif
159 gtk_set_locale ();
160 rb_profile_start ("initializing gnome program");
162 #if HAVE_LIBGNOME_GOPTION
163 program = gnome_program_init (PACKAGE, VERSION,
164 LIBGNOMEUI_MODULE,
165 argc+1, new_argv,
166 GNOME_PARAM_GOPTION_CONTEXT, context,
167 GNOME_PARAM_HUMAN_READABLE_NAME, _("Rhythmbox"),
168 GNOME_PARAM_APP_DATADIR, DATADIR,
169 NULL);
170 #else
171 g_option_context_parse (context, &argc, &argv, NULL);
172 g_option_context_free (context);
173 program = gnome_program_init (PACKAGE, VERSION,
174 LIBGNOMEUI_MODULE,
175 argc+1, new_argv,
176 GNOME_PARAM_HUMAN_READABLE_NAME, _("Rhythmbox"),
177 GNOME_PARAM_APP_DATADIR, DATADIR,
178 NULL);
179 #endif
181 rb_profile_end ("initializing gnome program");
183 rb_profile_start ("initializing gnome auth manager");
184 gnome_authentication_manager_init ();
185 rb_profile_end ("initializing gnome auth manager");
187 g_random_set_seed (time (0));
189 #ifdef ENABLE_NLS
190 /* initialize i18n */
191 bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
192 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
193 textdomain (GETTEXT_PACKAGE);
194 #endif
196 #ifdef HAVE_GSTREAMER_0_8
197 gst_control_init (&argc, &argv);
198 #endif
200 if (!debug && debug_match)
201 rb_debug_init_match (debug_match);
202 else
203 rb_debug_init (debug);
204 rb_debug ("initializing Rhythmbox %s", VERSION);
206 /* TODO: kill this function */
207 rb_threads_init ();
209 activated = FALSE;
211 #if WITH_DBUS || WITH_OLD_DBUS
212 rb_debug ("going to create DBus object");
214 dbus_g_thread_init ();
216 session_bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
217 if (session_bus == NULL) {
218 g_warning ("couldn't connect to session bus: %s", (error) ? error->message : "(null)");
219 g_clear_error (&error);
220 } else if (!no_registration) {
221 guint request_name_reply;
222 int flags;
223 #ifndef DBUS_NAME_FLAG_DO_NOT_QUEUE
224 flags = DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT;
225 #else
226 flags = DBUS_NAME_FLAG_DO_NOT_QUEUE;
227 #endif
229 #ifndef WITH_OLD_DBUS
230 DBusGProxy *bus_proxy;
232 bus_proxy = dbus_g_proxy_new_for_name (session_bus,
233 "org.freedesktop.DBus",
234 "/org/freedesktop/DBus",
235 "org.freedesktop.DBus");
237 if (!dbus_g_proxy_call (bus_proxy,
238 "RequestName",
239 &error,
240 G_TYPE_STRING,
241 "org.gnome.Rhythmbox",
242 G_TYPE_UINT,
243 flags,
244 G_TYPE_INVALID,
245 G_TYPE_UINT,
246 &request_name_reply,
247 G_TYPE_INVALID)) {
248 g_warning ("Failed to invoke RequestName: %s",
249 error->message);
251 g_object_unref (bus_proxy);
253 #else
254 DBusError dbus_error = {0,};
255 DBusConnection *connection;
257 connection = dbus_g_connection_get_connection (session_bus);
258 request_name_reply = dbus_bus_request_name (connection,
259 "org.gnome.Rhythmbox",
260 flags,
261 &dbus_error);
262 if (dbus_error_is_set (&dbus_error)) {
263 g_warning ("Failed to invoke RequestName: %s",
264 dbus_error.message);
266 dbus_error_free (&dbus_error);
267 #endif
268 if (request_name_reply == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER
269 || request_name_reply == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER)
270 activated = FALSE;
271 else if (request_name_reply == DBUS_REQUEST_NAME_REPLY_EXISTS
272 || request_name_reply == DBUS_REQUEST_NAME_REPLY_IN_QUEUE)
273 activated = TRUE;
274 else {
275 g_warning ("Got unhandled reply %u from RequestName",
276 request_name_reply);
277 activated = FALSE;
280 #endif
282 if (!activated) {
283 if (quit) {
284 rb_debug ("was asked to quit, but no instance was running");
285 gdk_notify_startup_complete ();
286 exit (0);
288 #ifdef WITH_RHYTHMDB_GDA
289 gda_init (PACKAGE, VERSION, argc, argv);
290 #endif
292 rb_refstring_system_init ();
294 rb_file_helpers_init ();
296 rb_debug ("Going to create a new shell");
298 glade_gnome_init ();
300 rb_stock_icons_init ();
302 #if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
303 nautilus_burn_init ();
304 #endif
306 gtk_window_set_default_icon_name ("rhythmbox");
308 rb_shell = rb_shell_new (argc, argv, no_registration, no_update, dry_run, rhythmdb_file, playlists_file);
309 g_object_weak_ref (G_OBJECT (rb_shell), main_shell_weak_ref_cb, NULL);
310 if (!no_registration && session_bus != NULL) {
311 #if WITH_DBUS
312 GObject *obj;
313 const char *path;
315 dbus_g_object_type_install_info (RB_TYPE_SHELL, &dbus_glib_rb_shell_object_info);
316 dbus_g_connection_register_g_object (session_bus, "/org/gnome/Rhythmbox/Shell", G_OBJECT (rb_shell));
318 /* register player object */
319 dbus_g_object_type_install_info (RB_TYPE_SHELL_PLAYER, &dbus_glib_rb_shell_player_object_info);
320 obj = rb_shell_get_player (rb_shell);
321 path = rb_shell_get_player_path (rb_shell);
322 dbus_g_connection_register_g_object (session_bus, path, obj);
324 /* register playlist manager object */
325 dbus_g_object_type_install_info (RB_TYPE_PLAYLIST_MANAGER, &dbus_glib_rb_playlist_manager_object_info);
326 obj = rb_shell_get_playlist_manager (rb_shell);
327 path = rb_shell_get_playlist_manager_path (rb_shell);
328 dbus_g_connection_register_g_object (session_bus, path, obj);
329 #elif WITH_OLD_DBUS
330 register_dbus_handler (dbus_g_connection_get_connection (session_bus),
331 rb_shell);
332 #endif /* WITH_DBUS */
334 } else if (!no_registration && session_bus != NULL) {
335 #if WITH_DBUS
336 DBusGProxy *shell_proxy;
337 #endif
338 guint32 current_time;
339 #if GTK_MINOR_VERSION >= 8
340 current_time = gdk_x11_display_get_user_time (gdk_display_get_default ());
341 #else
342 /* FIXME - this does not work; it will return 0 since
343 * we're not in an event. When we pass this to
344 * gtk_window_present_with_time, it ignores the value
345 * since it's 0. The only alternative is to parse the
346 * startup-notification junk from the environment
347 * ourself...
349 current_time = GDK_CURRENT_TIME;
350 #endif /* GTK_MINOR_VERSION */
352 #if WITH_DBUS
353 shell_proxy = dbus_g_proxy_new_for_name_owner (session_bus,
354 "org.gnome.Rhythmbox",
355 "/org/gnome/Rhythmbox/Shell",
356 "org.gnome.Rhythmbox.Shell",
357 &error);
358 if (!shell_proxy) {
359 g_warning ("Couldn't create proxy for Rhythmbox shell: %s",
360 error->message);
361 } else {
362 if (quit) {
363 dbus_g_proxy_call_no_reply (shell_proxy, "quit",
364 G_TYPE_INVALID);
365 } else {
366 load_uri_args ((const char **) remaining_args, (GFunc) dbus_load_uri, shell_proxy);
367 dbus_g_proxy_call_no_reply (shell_proxy, "present",
368 G_TYPE_UINT, current_time,
369 G_TYPE_INVALID);
371 g_object_unref (G_OBJECT (shell_proxy));
373 #elif WITH_OLD_DBUS
374 if (!send_present_message (dbus_g_connection_get_connection (session_bus),
375 current_time))
376 g_warning ("Unable to send dbus message to existing rhythmbox instance");
377 #endif /* WITH_DBUS */
380 if (activated) {
381 gdk_notify_startup_complete ();
382 } else {
384 rb_profile_start ("mainloop");
385 gtk_main ();
386 rb_profile_end ("mainloop");
388 rb_debug ("out of toplevel loop");
390 rb_file_helpers_shutdown ();
391 rb_stock_icons_shutdown ();
392 rb_refstring_system_shutdown ();
394 gnome_vfs_shutdown ();
396 #if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
397 nautilus_burn_shutdown ();
398 #endif
401 #ifdef HAVE_GSTREAMER_0_10
402 gst_deinit ();
403 #endif
405 g_strfreev (new_argv);
407 rb_debug ("THE END");
408 rb_profile_end ("starting rhythmbox");
409 g_object_unref (program);
411 exit (0);
414 #if WITH_DBUS
415 static gboolean
416 load_uri_args (const char **args, GFunc handler, gpointer user_data)
418 gboolean handled;
419 guint i;
421 handled = FALSE;
422 for (i = 0; args && args[i]; i++) {
423 char *uri;
425 rb_debug ("examining argument %s", args[i]);
427 uri = gnome_vfs_make_uri_from_shell_arg (args[i]);
429 if (rb_uri_is_local (uri) == FALSE || rb_uri_exists (uri)) {
430 handler (uri, user_data);
432 g_free (uri);
434 handled = TRUE;
436 return handled;
439 static void
440 dbus_load_uri (const char *filename, DBusGProxy *proxy)
442 GError *error = NULL;
443 rb_debug ("Sending loadURI for %s", filename);
444 if (!dbus_g_proxy_call (proxy, "loadURI", &error,
445 G_TYPE_STRING, filename,
446 G_TYPE_BOOLEAN, TRUE,
447 G_TYPE_INVALID,
448 G_TYPE_INVALID))
449 g_printerr ("Failed to load %s: %s",
450 filename, error->message);
452 #endif
454 static void
455 main_shell_weak_ref_cb (gpointer data, GObject *objptr)
457 rb_debug ("caught shell finalization");
458 gtk_main_quit ();
461 #ifdef WITH_OLD_DBUS
462 /* old dbus support (0.31-0.34) */
464 static gboolean
465 send_present_message (DBusConnection *connection, guint32 current_time)
467 DBusMessage *message;
468 gboolean result;
470 message = dbus_message_new_method_call ("org.gnome.Rhythmbox",
471 "/org/gnome/Rhythmbox/Shell",
472 "org.gnome.Rhythmbox.Shell",
473 "present");
474 if (!message)
475 return FALSE;
477 if (!dbus_message_append_args (message,
478 DBUS_TYPE_UINT32, &current_time,
479 DBUS_TYPE_INVALID)) {
480 dbus_message_unref (message);
481 return FALSE;
484 result = dbus_connection_send (connection, message, NULL);
485 dbus_message_unref (message);
487 return result;
490 static void
491 unregister_dbus_handler (DBusConnection *connection, void *data)
493 /* nothing */
496 static DBusHandlerResult
497 handle_dbus_message (DBusConnection *connection, DBusMessage *message, void *data)
499 RBShell *shell = (RBShell *)data;
501 rb_debug ("Handling dbus message");
502 if (dbus_message_is_method_call (message, "org.gnome.Rhythmbox.Shell", "present")) {
503 DBusMessageIter iter;
504 guint32 current_time;
506 if (!dbus_message_iter_init (message, &iter))
507 return DBUS_HANDLER_RESULT_NEED_MEMORY;
509 if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_UINT32)
510 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
512 dbus_message_iter_get_basic (&iter, &current_time);
514 rb_shell_present (shell, current_time, NULL);
515 return DBUS_HANDLER_RESULT_HANDLED;
516 } else {
517 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
521 static void
522 register_dbus_handler (DBusConnection *connection, RBShell *shell)
524 DBusObjectPathVTable vt = {
525 unregister_dbus_handler,
526 handle_dbus_message,
527 NULL, NULL, NULL, NULL
530 dbus_connection_register_object_path (connection,
531 "/org/gnome/Rhythmbox/Shell",
532 &vt,
533 shell);
534 dbus_connection_ref (connection);
535 dbus_connection_setup_with_g_main (connection, NULL);
538 #endif