Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
main.c
Go to the documentation of this file.
00001 /*  Audacious - Cross-platform multimedia player
00002  *  Copyright (C) 2005-2007  Audacious development team.
00003  *
00004  *  Based on BMP:
00005  *  Copyright (C) 2003-2004  BMP development team.
00006  *
00007  *  Based on XMMS:
00008  *  Copyright (C) 1998-2003  XMMS development team.
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; under version 3 of the License.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program.  If not, see <http://www.gnu.org/licenses>.
00021  *
00022  *  The Audacious team does not consider modular code linking to
00023  *  Audacious or using our public API to be a derived work.
00024  */
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #  include "config.h"
00028 #endif
00029 
00030 #include <gtk/gtk.h>
00031 
00032 #include "main.h"
00033 
00034 #include <glib/gprintf.h>
00035 
00036 #include <libaudcore/audstrings.h>
00037 #include <libaudcore/hook.h>
00038 #include <libaudtag/audtag.h>
00039 
00040 #ifdef USE_DBUS
00041 #  include "dbus-service.h"
00042 #  include "audctrl.h"
00043 #endif
00044 
00045 #ifdef USE_EGGSM
00046 #include "eggsmclient.h"
00047 #include "eggdesktopfile.h"
00048 #endif
00049 
00050 #include "audconfig.h"
00051 #include "chardet.h"
00052 #include "compatibility.h"
00053 #include "configdb.h"
00054 #include "debug.h"
00055 #include "drct.h"
00056 #include "equalizer.h"
00057 #include "i18n.h"
00058 #include "interface.h"
00059 #include "output.h"
00060 #include "playback.h"
00061 #include "playlist.h"
00062 #include "pluginenum.h"
00063 #include "signals.h"
00064 #include "util.h"
00065 #include "visualization.h"
00066 
00067 #define AUTOSAVE_INTERVAL 300 /* seconds */
00068 
00069 static const gchar *application_name = N_("Audacious");
00070 
00071 struct _AudCmdLineOpt
00072 {
00073     gchar **filenames;
00074     gint session;
00075     gboolean play, stop, pause, fwd, rew, play_pause, show_jump_box;
00076     gboolean enqueue, mainwin, remote, activate;
00077     gboolean enqueue_to_temp;
00078     gboolean version;
00079     gchar *previous_session_id;
00080 };
00081 typedef struct _AudCmdLineOpt AudCmdLineOpt;
00082 
00083 static AudCmdLineOpt options;
00084 
00085 gchar * aud_paths[BMP_PATH_COUNT];
00086 
00087 #ifdef USE_DBUS
00088 MprisPlayer *mpris;
00089 MprisTrackList *mpris_tracklist;
00090 #endif
00091 
00092 static void print_version(void)
00093 {
00094     g_printf("%s %s (%s)\n", _(application_name), VERSION, BUILDSTAMP);
00095 }
00096 
00097 static void aud_make_user_dir(void)
00098 {
00099     const mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
00100 
00101     make_directory(aud_paths[BMP_PATH_USER_DIR], mode755);
00102     make_directory(aud_paths[BMP_PATH_USER_PLUGIN_DIR], mode755);
00103     make_directory(aud_paths[BMP_PATH_USER_SKIN_DIR], mode755);
00104     make_directory(aud_paths[BMP_PATH_SKIN_THUMB_DIR], mode755);
00105     make_directory(aud_paths[BMP_PATH_PLAYLISTS_DIR], mode755);
00106 }
00107 
00108 static void aud_free_paths(void)
00109 {
00110     gint i;
00111 
00112     for (i = 0; i < BMP_PATH_COUNT; i++)
00113     {
00114         g_free(aud_paths[i]);
00115         aud_paths[i] = 0;
00116     }
00117 }
00118 
00119 static void aud_init_paths()
00120 {
00121     gchar *xdg_config_home;
00122     gchar *xdg_data_home;
00123     gchar *xdg_cache_home;
00124 
00125     xdg_config_home = (getenv ("XDG_CONFIG_HOME") == NULL) ? g_build_filename
00126      (getenv ("HOME"), ".config", NULL) : g_strdup (getenv ("XDG_CONFIG_HOME"));
00127     xdg_data_home = (getenv ("XDG_DATA_HOME") == NULL) ? g_build_filename
00128      (getenv ("HOME"), ".local", "share", NULL) : g_strdup (getenv
00129      ("XDG_DATA_HOME"));
00130     xdg_cache_home = (getenv ("XDG_CACHE_HOME") == NULL) ? g_build_filename
00131      (getenv ("HOME"), ".cache", NULL) : g_strdup (getenv ("XDG_CACHE_HOME"));
00132 
00133     aud_paths[BMP_PATH_USER_DIR] = g_build_filename(xdg_config_home, "audacious", NULL);
00134     aud_paths[BMP_PATH_USER_SKIN_DIR] = g_build_filename(xdg_data_home, "audacious", "Skins", NULL);
00135     aud_paths[BMP_PATH_USER_PLUGIN_DIR] = g_build_filename(xdg_data_home, "audacious", "Plugins", NULL);
00136 
00137     aud_paths[BMP_PATH_SKIN_THUMB_DIR] = g_build_filename(xdg_cache_home, "audacious", "thumbs", NULL);
00138 
00139     aud_paths[BMP_PATH_PLAYLISTS_DIR] = g_build_filename(aud_paths[BMP_PATH_USER_DIR], "playlists", NULL);
00140 
00141     aud_paths[BMP_PATH_CONFIG_FILE] = g_build_filename(aud_paths[BMP_PATH_USER_DIR], "config", NULL);
00142     aud_paths[BMP_PATH_PLAYLIST_FILE] = g_build_filename(aud_paths[BMP_PATH_USER_DIR], "playlist.xspf", NULL);
00143     aud_paths[BMP_PATH_ACCEL_FILE] = g_build_filename(aud_paths[BMP_PATH_USER_DIR], "accels", NULL);
00144 
00145     aud_paths[BMP_PATH_GTKRC_FILE] = g_build_filename(aud_paths[BMP_PATH_USER_DIR], "gtkrc", NULL);
00146 
00147     g_free(xdg_config_home);
00148     g_free(xdg_data_home);
00149     g_free(xdg_cache_home);
00150 
00151     g_atexit(aud_free_paths);
00152 }
00153 
00154 static GOptionEntry cmd_entries[] = {
00155     {"rew", 'r', 0, G_OPTION_ARG_NONE, &options.rew, N_("Skip backwards in playlist"), NULL},
00156     {"play", 'p', 0, G_OPTION_ARG_NONE, &options.play, N_("Start playing current playlist"), NULL},
00157     {"pause", 'u', 0, G_OPTION_ARG_NONE, &options.pause, N_("Pause current song"), NULL},
00158     {"stop", 's', 0, G_OPTION_ARG_NONE, &options.stop, N_("Stop current song"), NULL},
00159     {"play-pause", 't', 0, G_OPTION_ARG_NONE, &options.play_pause, N_("Pause if playing, play otherwise"), NULL},
00160     {"fwd", 'f', 0, G_OPTION_ARG_NONE, &options.fwd, N_("Skip forward in playlist"), NULL},
00161     {"show-jump-box", 'j', 0, G_OPTION_ARG_NONE, &options.show_jump_box, N_("Display Jump to File dialog"), NULL},
00162     {"enqueue", 'e', 0, G_OPTION_ARG_NONE, &options.enqueue, N_("Add files to the playlist"), NULL},
00163     {"enqueue-to-temp", 'E', 0, G_OPTION_ARG_NONE, &options.enqueue_to_temp, N_("Add new files to a temporary playlist"), NULL},
00164     {"show-main-window", 'm', 0, G_OPTION_ARG_NONE, &options.mainwin, N_("Display the main window"), NULL},
00165     {"activate", 'a', 0, G_OPTION_ARG_NONE, &options.activate, N_("Display all open Audacious windows"), NULL},
00166     {"version", 'v', 0, G_OPTION_ARG_NONE, &options.version, N_("Show version"), NULL},
00167     {"verbose", 'V', 0, G_OPTION_ARG_NONE, &cfg.verbose, N_("Print debugging messages"), NULL},
00168     {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &options.filenames, N_("FILE..."), NULL},
00169     {NULL},
00170 };
00171 
00172 static void parse_cmd_line_options(gint * argc, gchar *** argv)
00173 {
00174     GOptionContext *context;
00175     GError *error = NULL;
00176 
00177     memset(&options, '\0', sizeof(AudCmdLineOpt));
00178     options.session = -1;
00179 
00180     context = g_option_context_new(_("- play multimedia files"));
00181     g_option_context_add_main_entries(context, cmd_entries, PACKAGE_NAME);
00182     g_option_context_add_group(context, gtk_get_option_group(FALSE));
00183 #ifdef USE_EGGSM
00184     g_option_context_add_group(context, egg_sm_client_get_option_group());
00185 #endif
00186 
00187     if (!g_option_context_parse(context, argc, argv, &error))
00188         /* checking for MacOS X -psn_0_* errors */
00189         if (error->message && !g_strrstr(error->message, "-psn_0_"))
00190         {
00191             g_printerr(_("%s: %s\nTry `%s --help' for more information.\n"), (*argv)[0], error->message, (*argv)[0]);
00192             exit(EXIT_FAILURE);
00193         }
00194 
00195     g_option_context_free (context);
00196 }
00197 
00198 static void handle_cmd_line_filenames(gboolean is_running)
00199 {
00200     gint i;
00201     gchar *working, **filenames = options.filenames;
00202     GList * fns = NULL;
00203 #ifdef USE_DBUS
00204     DBusGProxy *session = audacious_get_dbus_proxy();
00205 #endif
00206 
00207     if (filenames == NULL)
00208         return;
00209 
00210     working = g_get_current_dir();
00211     for (i = 0; filenames[i] != NULL; i++)
00212     {
00213         gchar * uri;
00214 
00215         if (strstr (filenames[i], "://"))
00216             uri = g_strdup (filenames[i]);
00217         else if (g_path_is_absolute (filenames[i]))
00218             uri = filename_to_uri (filenames[i]);
00219         else
00220         {
00221             gchar * absolute = g_build_filename (working, filenames[i], NULL);
00222             uri = filename_to_uri (absolute);
00223             g_free (absolute);
00224         }
00225 
00226         fns = g_list_prepend(fns, uri);
00227     }
00228     fns = g_list_reverse(fns);
00229     g_free(working);
00230 
00231 #ifdef USE_DBUS
00232     if (is_running)
00233     {
00234         if (options.enqueue_to_temp)
00235             audacious_remote_playlist_open_list_to_temp (session, fns);
00236         else if (options.enqueue)
00237             audacious_remote_playlist_add (session, fns);
00238         else
00239             audacious_remote_playlist_open_list (session, fns);
00240     }
00241     else                        /* !is_running */
00242 #endif
00243     {
00244         if (options.enqueue_to_temp)
00245         {
00246             drct_pl_open_temp_list (fns);
00247             cfg.resume_state = 0;
00248         }
00249         else if (options.enqueue)
00250             drct_pl_add_list (fns, -1);
00251         else
00252         {
00253             drct_pl_open_list (fns);
00254             cfg.resume_state = 0;
00255         }
00256     }                           /* !is_running */
00257 
00258     g_list_foreach(fns, (GFunc) g_free, NULL);
00259     g_list_free(fns);
00260 }
00261 
00262 static void handle_cmd_line_options_first(void)
00263 {
00264 #ifdef USE_DBUS
00265     DBusGProxy *session;
00266 #endif
00267 
00268     if (options.version)
00269     {
00270         print_version();
00271         exit(EXIT_SUCCESS);
00272     }
00273 
00274 #ifdef USE_DBUS
00275     session = audacious_get_dbus_proxy();
00276     if (audacious_remote_is_running(session))
00277     {
00278         handle_cmd_line_filenames(TRUE);
00279         if (options.rew)
00280             audacious_remote_playlist_prev(session);
00281         if (options.play)
00282             audacious_remote_play(session);
00283         if (options.pause)
00284             audacious_remote_pause(session);
00285         if (options.stop)
00286             audacious_remote_stop(session);
00287         if (options.fwd)
00288             audacious_remote_playlist_next(session);
00289         if (options.play_pause)
00290             audacious_remote_play_pause(session);
00291         if (options.show_jump_box)
00292             audacious_remote_show_jtf_box(session);
00293         if (options.mainwin)
00294             audacious_remote_main_win_toggle(session, 1);
00295         if (options.activate)
00296             audacious_remote_activate(session);
00297         exit(EXIT_SUCCESS);
00298     }
00299 #endif
00300 }
00301 
00302 static void handle_cmd_line_options(void)
00303 {
00304     handle_cmd_line_filenames(FALSE);
00305 
00306     if (cfg.resume_playback_on_startup && cfg.resume_state > 0)
00307         playback_play (cfg.resume_playback_on_startup_time, cfg.resume_state ==
00308          2);
00309 
00310     if (options.play || options.play_pause)
00311     {
00312         if (! playback_get_playing ())
00313             playback_play (0, FALSE);
00314         else if (playback_get_paused ())
00315             playback_pause ();
00316     }
00317 
00318     if (options.show_jump_box)
00319         interface_show_jump_to_track ();
00320     if (options.mainwin)
00321         interface_toggle_visibility ();
00322 }
00323 
00324 void aud_quit (void)
00325 {
00326     AUDDBG ("Ending main loop.\n");
00327     gtk_main_quit ();
00328 }
00329 
00330 static void shut_down (void)
00331 {
00332     AUDDBG ("Saving configuration.\n");
00333     aud_config_save();
00334     save_playlists ();
00335 
00336     if (playback_get_playing ())
00337         playback_stop ();
00338 
00339     AUDDBG ("Shutting down user interface subsystem.\n");
00340     interface_unload ();
00341 
00342     output_cleanup ();
00343 
00344     AUDDBG ("Plugin subsystem shutdown.\n");
00345     plugin_system_cleanup();
00346 
00347     cfg_db_flush (); /* must be after plugin cleanup */
00348 
00349     AUDDBG ("Playlist cleanup.\n");
00350     playlist_end();
00351 }
00352 
00353 #ifdef USE_DBUS
00354 static void mpris_status_cb1(gpointer hook_data, gpointer user_data)
00355 {
00356     mpris_emit_status_change(mpris, GPOINTER_TO_INT(user_data));
00357 }
00358 
00359 static void mpris_status_cb2(gpointer hook_data, gpointer user_data)
00360 {
00361     mpris_emit_status_change(mpris, -1);
00362 }
00363 
00364 void init_playback_hooks(void)
00365 {
00366     hook_associate("playback begin", mpris_status_cb1, GINT_TO_POINTER(MPRIS_STATUS_PLAY));
00367     hook_associate("playback pause", mpris_status_cb1, GINT_TO_POINTER(MPRIS_STATUS_PAUSE));
00368     hook_associate("playback unpause", mpris_status_cb1, GINT_TO_POINTER(MPRIS_STATUS_PLAY));
00369     hook_associate("playback stop", mpris_status_cb1, GINT_TO_POINTER(MPRIS_STATUS_STOP));
00370 
00371     hook_associate("playback shuffle", mpris_status_cb2, NULL);
00372     hook_associate("playback repeat", mpris_status_cb2, NULL);
00373     hook_associate("playback no playlist advance", mpris_status_cb2, NULL);
00374 }
00375 #endif
00376 
00377 static gboolean autosave_cb (void * unused)
00378 {
00379     AUDDBG ("Saving configuration.\n");
00380     aud_config_save ();
00381     cfg_db_flush ();
00382     save_playlists ();
00383     return TRUE;
00384 }
00385 
00386 static PluginHandle * current_iface = NULL;
00387 
00388 PluginHandle * iface_plugin_get_active (void)
00389 {
00390     return current_iface;
00391 }
00392 
00393 void iface_plugin_set_active (PluginHandle * plugin)
00394 {
00395     AUDDBG ("Unloading visualizers.\n");
00396     vis_cleanup ();
00397 
00398     AUDDBG ("Unloading %s.\n", plugin_get_name (current_iface));
00399     interface_unload ();
00400 
00401     current_iface = plugin;
00402     interface_set_default (plugin);
00403 
00404     AUDDBG ("Starting %s.\n", plugin_get_name (plugin));
00405     if (! interface_load (plugin))
00406     {
00407         fprintf (stderr, "%s failed to start.\n", plugin_get_name (plugin));
00408         exit (EXIT_FAILURE);
00409     }
00410 
00411     AUDDBG ("Loading visualizers.\n");
00412     vis_init ();
00413 }
00414 
00415 gint main(gint argc, gchar ** argv)
00416 {
00417     /* glib-2.13.0 requires g_thread_init() to be called before all
00418        other GLib functions */
00419     g_thread_init(NULL);
00420     if (!g_thread_supported())
00421     {
00422         g_printerr(_("Sorry, threads aren't supported on your platform.\n"));
00423         exit(EXIT_FAILURE);
00424     }
00425 
00426     gdk_threads_init();
00427     mowgli_init();
00428     chardet_init();
00429     tag_init();
00430 
00431     hook_init();
00432     hook_associate ("quit", (HookFunction) gtk_main_quit, NULL);
00433 
00434     /* Setup l10n early so we can print localized error messages */
00435     gtk_set_locale();
00436     bindtextdomain(PACKAGE_NAME, LOCALEDIR);
00437     bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
00438     bindtextdomain(PACKAGE_NAME "-plugins", LOCALEDIR);
00439     bind_textdomain_codeset(PACKAGE_NAME "-plugins", "UTF-8");
00440     textdomain(PACKAGE_NAME);
00441 
00442 #if !defined(_WIN32) && defined(USE_EGGSM)
00443     egg_set_desktop_file(AUDACIOUS_DESKTOP_FILE);
00444 #endif
00445     aud_init_paths();
00446     aud_make_user_dir();
00447 
00448     gtk_rc_add_default_file(aud_paths[BMP_PATH_GTKRC_FILE]);
00449 
00450     parse_cmd_line_options(&argc, &argv);
00451 
00452     if (!gtk_init_check(&argc, &argv))
00453     {                           /* XXX */
00454         /* GTK check failed, and no arguments passed to indicate
00455            that user is intending to only remote control a running
00456            session */
00457         g_printerr(_("%s: Unable to open display, exiting.\n"), argv[0]);
00458         exit(EXIT_FAILURE);
00459     }
00460 
00461     AUDDBG ("Loading configuration.\n");
00462     aud_config_load();
00463     atexit (aud_config_free);
00464 
00465     AUDDBG ("Initializing signal handlers.\n");
00466     signal_handlers_init();
00467 
00468     AUDDBG ("Handling commandline options, part #1.\n");
00469     handle_cmd_line_options_first();
00470 
00471     output_init ();
00472 
00473 #ifdef USE_DBUS
00474     AUDDBG ("Initializing D-Bus.\n");
00475     init_dbus();
00476     init_playback_hooks();
00477 #endif
00478 
00479     AUDDBG ("Initializing plugin subsystems.\n");
00480     plugin_system_init();
00481 
00482     playlist_init ();
00483     load_playlists ();
00484     eq_init ();
00485 
00486     AUDDBG ("Handling commandline options, part #2.\n");
00487     handle_cmd_line_options();
00488 
00489     AUDDBG ("Registering interface hooks.\n");
00490     register_interface_hooks();
00491 
00492     g_timeout_add_seconds (AUTOSAVE_INTERVAL, autosave_cb, NULL);
00493 
00494     if ((current_iface = interface_get_default ()) == NULL)
00495     {
00496         fprintf (stderr, "No interface plugin found.\n");
00497         return EXIT_FAILURE;
00498     }
00499 
00500     AUDDBG ("Starting %s.\n", plugin_get_name (current_iface));
00501     if (! interface_load (current_iface))
00502     {
00503         fprintf (stderr, "%s failed to start.\n", plugin_get_name (current_iface));
00504         return EXIT_FAILURE;
00505     }
00506 
00507     AUDDBG ("Loading visualizers.\n");
00508     vis_init ();
00509 
00510     AUDDBG ("Starting main loop.\n");
00511     gtk_main ();
00512 
00513     AUDDBG ("Unloading visualizers.\n");
00514     vis_cleanup ();
00515 
00516     AUDDBG ("Shutting down.\n");
00517     shut_down ();
00518     return EXIT_SUCCESS;
00519 }