Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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 }