Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
00001 /* Audacious - Cross-platform multimedia player 00002 * Copyright (C) 2005-2011 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 #include <errno.h> 00027 #include <limits.h> 00028 00029 #include <gtk/gtk.h> 00030 00031 #include <libaudcore/audstrings.h> 00032 #include <libaudcore/hook.h> 00033 #include <libaudtag/audtag.h> 00034 00035 #include "config.h" 00036 00037 #ifdef USE_DBUS 00038 #include "audctrl.h" 00039 #include "dbus-service.h" 00040 #endif 00041 00042 #ifdef USE_EGGSM 00043 #include "eggdesktopfile.h" 00044 #include "eggsmclient.h" 00045 #endif 00046 00047 #include "audconfig.h" 00048 #include "configdb.h" 00049 #include "debug.h" 00050 #include "drct.h" 00051 #include "equalizer.h" 00052 #include "i18n.h" 00053 #include "interface.h" 00054 #include "misc.h" 00055 #include "playback.h" 00056 #include "playlist.h" 00057 #include "plugins.h" 00058 #include "util.h" 00059 00060 /* adder.c */ 00061 void adder_init (void); 00062 void adder_cleanup (void); 00063 00064 /* chardet.c */ 00065 void chardet_init (void); 00066 00067 /* mpris-signals.c */ 00068 void mpris_signals_init (void); 00069 void mpris_signals_cleanup (void); 00070 00071 /* signals.c */ 00072 void signals_init (void); 00073 00074 /* smclient.c */ 00075 void smclient_init (void); 00076 00077 #define AUTOSAVE_INTERVAL 300 /* seconds */ 00078 00079 static struct { 00080 gchar **filenames; 00081 gint session; 00082 gboolean play, stop, pause, fwd, rew, play_pause, show_jump_box; 00083 gboolean enqueue, mainwin, remote; 00084 gboolean enqueue_to_temp; 00085 gboolean version; 00086 gchar *previous_session_id; 00087 } options; 00088 00089 static gchar * aud_paths[AUD_PATH_COUNT]; 00090 00091 static void make_dirs(void) 00092 { 00093 #ifdef S_IRGRP 00094 const mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 00095 #else 00096 const mode_t mode755 = S_IRWXU; 00097 #endif 00098 00099 make_directory(aud_paths[AUD_PATH_USER_DIR], mode755); 00100 make_directory(aud_paths[AUD_PATH_USER_PLUGIN_DIR], mode755); 00101 make_directory(aud_paths[AUD_PATH_PLAYLISTS_DIR], mode755); 00102 } 00103 00104 static void normalize_path (gchar * path) 00105 { 00106 #ifdef _WIN32 00107 string_replace_char (path, '/', '\\'); 00108 #endif 00109 gint len = strlen (path); 00110 #ifdef _WIN32 00111 if (len > 3 && path[len - 1] == '\\') /* leave "C:\" */ 00112 #else 00113 if (len > 1 && path[len - 1] == '/') /* leave leading "/" */ 00114 #endif 00115 path[len - 1] = 0; 00116 } 00117 00118 static gchar * last_path_element (gchar * path) 00119 { 00120 gchar * slash = strrchr (path, G_DIR_SEPARATOR); 00121 return (slash && slash[1]) ? slash + 1 : NULL; 00122 } 00123 00124 static void strip_path_element (gchar * path, gchar * elem) 00125 { 00126 #ifdef _WIN32 00127 if (elem > path + 3) 00128 #else 00129 if (elem > path + 1) 00130 #endif 00131 elem[-1] = 0; /* overwrite slash */ 00132 else 00133 elem[0] = 0; /* leave [drive letter and] leading slash */ 00134 } 00135 00136 static void relocate_path (gchar * * pathp, const gchar * old, const gchar * new) 00137 { 00138 gchar * path = * pathp; 00139 gint oldlen = strlen (old); 00140 gint newlen = strlen (new); 00141 00142 if (oldlen && old[oldlen - 1] == G_DIR_SEPARATOR) 00143 oldlen --; 00144 if (newlen && new[newlen - 1] == G_DIR_SEPARATOR) 00145 newlen --; 00146 00147 #ifdef _WIN32 00148 if (strncasecmp (path, old, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR)) 00149 #else 00150 if (strncmp (path, old, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR)) 00151 #endif 00152 { 00153 fprintf (stderr, "Failed to relocate a data path. Falling back to " 00154 "compile-time path: %s\n", path); 00155 return; 00156 } 00157 00158 * pathp = g_strdup_printf ("%.*s%s", newlen, new, path + oldlen); 00159 g_free (path); 00160 } 00161 00162 static void relocate_paths (void) 00163 { 00164 /* Start with the paths hard coded at compile time. */ 00165 aud_paths[AUD_PATH_BIN_DIR] = g_strdup (HARDCODE_BINDIR); 00166 aud_paths[AUD_PATH_DATA_DIR] = g_strdup (HARDCODE_DATADIR); 00167 aud_paths[AUD_PATH_PLUGIN_DIR] = g_strdup (HARDCODE_PLUGINDIR); 00168 aud_paths[AUD_PATH_LOCALE_DIR] = g_strdup (HARDCODE_LOCALEDIR); 00169 aud_paths[AUD_PATH_DESKTOP_FILE] = g_strdup (HARDCODE_DESKTOPFILE); 00170 aud_paths[AUD_PATH_ICON_FILE] = g_strdup (HARDCODE_ICONFILE); 00171 normalize_path (aud_paths[AUD_PATH_BIN_DIR]); 00172 normalize_path (aud_paths[AUD_PATH_DATA_DIR]); 00173 normalize_path (aud_paths[AUD_PATH_PLUGIN_DIR]); 00174 normalize_path (aud_paths[AUD_PATH_LOCALE_DIR]); 00175 normalize_path (aud_paths[AUD_PATH_DESKTOP_FILE]); 00176 normalize_path (aud_paths[AUD_PATH_ICON_FILE]); 00177 00178 /* Compare the compile-time path to the executable and the actual path to 00179 * see if we have been moved. */ 00180 gchar * old = g_strdup (aud_paths[AUD_PATH_BIN_DIR]); 00181 gchar * new = get_path_to_self (); 00182 if (! new) 00183 { 00184 ERR: 00185 g_free (old); 00186 g_free (new); 00187 return; 00188 } 00189 normalize_path (new); 00190 00191 /* Strip the name of the executable file, leaving the path. */ 00192 gchar * base = last_path_element (new); 00193 if (! base) 00194 goto ERR; 00195 strip_path_element (new, base); 00196 00197 /* Strip innermost folder names from both paths as long as they match. This 00198 * leaves a compile-time prefix and a run-time one to replace it with. */ 00199 gchar * a, * b; 00200 while ((a = last_path_element (old)) && (b = last_path_element (new)) && 00201 #ifdef _WIN32 00202 ! strcasecmp (a, b)) 00203 #else 00204 ! strcmp (a, b)) 00205 #endif 00206 { 00207 strip_path_element (old, a); 00208 strip_path_element (new, b); 00209 } 00210 00211 /* Do the replacements. */ 00212 relocate_path (& aud_paths[AUD_PATH_BIN_DIR], old, new); 00213 relocate_path (& aud_paths[AUD_PATH_DATA_DIR], old, new); 00214 relocate_path (& aud_paths[AUD_PATH_PLUGIN_DIR], old, new); 00215 relocate_path (& aud_paths[AUD_PATH_LOCALE_DIR], old, new); 00216 relocate_path (& aud_paths[AUD_PATH_DESKTOP_FILE], old, new); 00217 relocate_path (& aud_paths[AUD_PATH_ICON_FILE], old, new); 00218 00219 g_free (old); 00220 g_free (new); 00221 } 00222 00223 static void init_paths (void) 00224 { 00225 relocate_paths (); 00226 00227 const gchar * xdg_config_home = g_get_user_config_dir (); 00228 const gchar * xdg_data_home = g_get_user_data_dir (); 00229 00230 #ifdef _WIN32 00231 /* Some libraries (libmcs) and plugins (filewriter) use these variables, 00232 * which are generally not set on Windows. */ 00233 g_setenv ("HOME", g_get_home_dir (), TRUE); 00234 g_setenv ("XDG_CONFIG_HOME", xdg_config_home, TRUE); 00235 g_setenv ("XDG_DATA_HOME", xdg_data_home, TRUE); 00236 g_setenv ("XDG_CACHE_HOME", g_get_user_cache_dir (), TRUE); 00237 #endif 00238 00239 aud_paths[AUD_PATH_USER_DIR] = g_build_filename(xdg_config_home, "audacious", NULL); 00240 aud_paths[AUD_PATH_USER_PLUGIN_DIR] = g_build_filename(xdg_data_home, "audacious", "Plugins", NULL); 00241 aud_paths[AUD_PATH_PLAYLISTS_DIR] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "playlists", NULL); 00242 aud_paths[AUD_PATH_PLAYLIST_FILE] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "playlist.xspf", NULL); 00243 aud_paths[AUD_PATH_GTKRC_FILE] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "gtkrc", NULL); 00244 00245 for (gint i = 0; i < AUD_PATH_COUNT; i ++) 00246 AUDDBG ("Data path: %s\n", aud_paths[i]); 00247 } 00248 00249 const gchar * get_path (gint id) 00250 { 00251 g_return_val_if_fail (id >= 0 && id < AUD_PATH_COUNT, NULL); 00252 return aud_paths[id]; 00253 } 00254 00255 static GOptionEntry cmd_entries[] = { 00256 {"rew", 'r', 0, G_OPTION_ARG_NONE, &options.rew, N_("Skip backwards in playlist"), NULL}, 00257 {"play", 'p', 0, G_OPTION_ARG_NONE, &options.play, N_("Start playing current playlist"), NULL}, 00258 {"pause", 'u', 0, G_OPTION_ARG_NONE, &options.pause, N_("Pause current song"), NULL}, 00259 {"stop", 's', 0, G_OPTION_ARG_NONE, &options.stop, N_("Stop current song"), NULL}, 00260 {"play-pause", 't', 0, G_OPTION_ARG_NONE, &options.play_pause, N_("Pause if playing, play otherwise"), NULL}, 00261 {"fwd", 'f', 0, G_OPTION_ARG_NONE, &options.fwd, N_("Skip forward in playlist"), NULL}, 00262 {"show-jump-box", 'j', 0, G_OPTION_ARG_NONE, &options.show_jump_box, N_("Display Jump to File dialog"), NULL}, 00263 {"enqueue", 'e', 0, G_OPTION_ARG_NONE, &options.enqueue, N_("Add files to the playlist"), NULL}, 00264 {"enqueue-to-temp", 'E', 0, G_OPTION_ARG_NONE, &options.enqueue_to_temp, N_("Add new files to a temporary playlist"), NULL}, 00265 {"show-main-window", 'm', 0, G_OPTION_ARG_NONE, &options.mainwin, N_("Display the main window"), NULL}, 00266 {"version", 'v', 0, G_OPTION_ARG_NONE, &options.version, N_("Show version"), NULL}, 00267 {"verbose", 'V', 0, G_OPTION_ARG_NONE, &cfg.verbose, N_("Print debugging messages"), NULL}, 00268 {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &options.filenames, N_("FILE..."), NULL}, 00269 {NULL}, 00270 }; 00271 00272 static void parse_options (gint * argc, gchar *** argv) 00273 { 00274 GOptionContext *context; 00275 GError *error = NULL; 00276 00277 memset (& options, 0, sizeof options); 00278 options.session = -1; 00279 00280 context = g_option_context_new(_("- play multimedia files")); 00281 g_option_context_add_main_entries(context, cmd_entries, PACKAGE_NAME); 00282 g_option_context_add_group(context, gtk_get_option_group(FALSE)); 00283 #ifdef USE_EGGSM 00284 g_option_context_add_group(context, egg_sm_client_get_option_group()); 00285 #endif 00286 00287 if (!g_option_context_parse(context, argc, argv, &error)) 00288 { 00289 fprintf (stderr, 00290 _("%s: %s\nTry `%s --help' for more information.\n"), (* argv)[0], 00291 error->message, (* argv)[0]); 00292 exit (EXIT_FAILURE); 00293 } 00294 00295 g_option_context_free (context); 00296 } 00297 00298 static gboolean get_lock (void) 00299 { 00300 gchar path[PATH_MAX]; 00301 snprintf (path, sizeof path, "%s" G_DIR_SEPARATOR_S "lock", 00302 aud_paths[AUD_PATH_USER_DIR]); 00303 00304 int handle = open (path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); 00305 00306 if (handle < 0) 00307 { 00308 if (errno != EEXIST) 00309 fprintf (stderr, "Cannot create %s: %s.\n", path, strerror (errno)); 00310 return FALSE; 00311 } 00312 00313 close (handle); 00314 return TRUE; 00315 } 00316 00317 static void release_lock (void) 00318 { 00319 gchar path[PATH_MAX]; 00320 snprintf (path, sizeof path, "%s" G_DIR_SEPARATOR_S "lock", 00321 aud_paths[AUD_PATH_USER_DIR]); 00322 00323 unlink (path); 00324 } 00325 00326 static GList * convert_filenames (void) 00327 { 00328 if (! options.filenames) 00329 return NULL; 00330 00331 gchar * * f = options.filenames; 00332 GList * list = NULL; 00333 gchar * cur = g_get_current_dir (); 00334 00335 for (gint i = 0; f[i]; i ++) 00336 { 00337 gchar * uri; 00338 00339 if (strstr (f[i], "://")) 00340 uri = g_strdup (f[i]); 00341 else if (g_path_is_absolute (f[i])) 00342 uri = filename_to_uri (f[i]); 00343 else 00344 { 00345 gchar * tmp = g_build_filename (cur, f[i], NULL); 00346 uri = filename_to_uri (tmp); 00347 g_free (tmp); 00348 } 00349 00350 list = g_list_prepend (list, uri); 00351 } 00352 00353 g_free (cur); 00354 return g_list_reverse (list); 00355 } 00356 00357 static void do_remote (void) 00358 { 00359 #ifdef USE_DBUS 00360 DBusGProxy * session = audacious_get_dbus_proxy (); 00361 00362 if (session && audacious_remote_is_running (session)) 00363 { 00364 GList * list = convert_filenames (); 00365 00366 /* if no command line options, then present running instance */ 00367 if (! (list || options.play || options.pause || options.play_pause || 00368 options.stop || options.rew || options.fwd || options.show_jump_box || 00369 options.mainwin)) 00370 options.mainwin = TRUE; 00371 00372 if (list) 00373 { 00374 if (options.enqueue_to_temp) 00375 audacious_remote_playlist_open_list_to_temp (session, list); 00376 else if (options.enqueue) 00377 audacious_remote_playlist_add (session, list); 00378 else 00379 audacious_remote_playlist_open_list (session, list); 00380 00381 g_list_foreach (list, (GFunc) g_free, NULL); 00382 g_list_free (list); 00383 } 00384 00385 if (options.play) 00386 audacious_remote_play (session); 00387 if (options.pause) 00388 audacious_remote_pause (session); 00389 if (options.play_pause) 00390 audacious_remote_play_pause (session); 00391 if (options.stop) 00392 audacious_remote_stop (session); 00393 if (options.rew) 00394 audacious_remote_playlist_prev (session); 00395 if (options.fwd) 00396 audacious_remote_playlist_next (session); 00397 if (options.show_jump_box) 00398 audacious_remote_show_jtf_box (session); 00399 if (options.mainwin) 00400 audacious_remote_main_win_toggle (session, TRUE); 00401 00402 exit (EXIT_SUCCESS); 00403 } 00404 #endif 00405 00406 GtkWidget * dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING, 00407 GTK_BUTTONS_OK_CANCEL, _("Audacious seems to be already running but is " 00408 "not responding. You can start another instance of the program, but " 00409 "please be warned that this can cause data loss. If Audacious is not " 00410 "running, you can safely ignore this message. Press OK to start " 00411 "Audacious or Cancel to quit.")); 00412 00413 g_signal_connect (dialog, "destroy", (GCallback) gtk_widget_destroyed, 00414 & dialog); 00415 00416 if (gtk_dialog_run ((GtkDialog *) dialog) != GTK_RESPONSE_OK) 00417 exit (EXIT_FAILURE); 00418 00419 if (dialog) 00420 gtk_widget_destroy (dialog); 00421 } 00422 00423 static void do_commands (void) 00424 { 00425 GList * list = convert_filenames (); 00426 00427 if (list) 00428 { 00429 if (options.enqueue_to_temp) 00430 { 00431 drct_pl_open_temp_list (list); 00432 cfg.resume_state = 0; 00433 } 00434 else if (options.enqueue) 00435 drct_pl_add_list (list, -1); 00436 else 00437 { 00438 drct_pl_open_list (list); 00439 cfg.resume_state = 0; 00440 } 00441 00442 g_list_foreach (list, (GFunc) g_free, NULL); 00443 g_list_free (list); 00444 } 00445 00446 if (cfg.resume_playback_on_startup && cfg.resume_state > 0) 00447 playback_play (cfg.resume_playback_on_startup_time, cfg.resume_state == 00448 2); 00449 00450 if (options.play || options.play_pause) 00451 { 00452 if (! playback_get_playing ()) 00453 playback_play (0, FALSE); 00454 else if (playback_get_paused ()) 00455 playback_pause (); 00456 } 00457 00458 if (options.show_jump_box) 00459 interface_show_jump_to_track (); 00460 if (options.mainwin) 00461 interface_show (TRUE); 00462 } 00463 00464 static void init_one (gint * p_argc, gchar * * * p_argv) 00465 { 00466 init_paths (); 00467 make_dirs (); 00468 00469 bindtextdomain (PACKAGE_NAME, aud_paths[AUD_PATH_LOCALE_DIR]); 00470 bind_textdomain_codeset (PACKAGE_NAME, "UTF-8"); 00471 bindtextdomain (PACKAGE_NAME "-plugins", aud_paths[AUD_PATH_LOCALE_DIR]); 00472 bind_textdomain_codeset (PACKAGE_NAME "-plugins", "UTF-8"); 00473 textdomain (PACKAGE_NAME); 00474 00475 mowgli_init (); 00476 chardet_init (); 00477 00478 g_thread_init (NULL); 00479 gdk_threads_init (); 00480 gdk_threads_enter (); 00481 00482 gtk_rc_add_default_file (aud_paths[AUD_PATH_GTKRC_FILE]); 00483 gtk_init (p_argc, p_argv); 00484 00485 #ifdef USE_EGGSM 00486 egg_sm_client_set_mode (EGG_SM_CLIENT_MODE_NORMAL); 00487 egg_set_desktop_file (aud_paths[AUD_PATH_DESKTOP_FILE]); 00488 #endif 00489 } 00490 00491 static void init_two (void) 00492 { 00493 hook_init (); 00494 tag_init (); 00495 00496 aud_config_load (); 00497 tag_set_verbose (cfg.verbose); 00498 vfs_set_verbose (cfg.verbose); 00499 00500 eq_init (); 00501 register_interface_hooks (); 00502 00503 #ifdef HAVE_SIGWAIT 00504 signals_init (); 00505 #endif 00506 #ifdef USE_EGGSM 00507 smclient_init (); 00508 #endif 00509 00510 AUDDBG ("Loading lowlevel plugins.\n"); 00511 start_plugins_one (); 00512 00513 playlist_init (); 00514 adder_init (); 00515 load_playlists (); 00516 00517 #ifdef USE_DBUS 00518 init_dbus (); 00519 #endif 00520 00521 do_commands (); 00522 00523 AUDDBG ("Loading highlevel plugins.\n"); 00524 start_plugins_two (); 00525 00526 mpris_signals_init (); 00527 } 00528 00529 static void shut_down (void) 00530 { 00531 mpris_signals_cleanup (); 00532 00533 AUDDBG ("Capturing state.\n"); 00534 aud_config_save (); 00535 save_playlists (); 00536 00537 AUDDBG ("Unloading highlevel plugins.\n"); 00538 stop_plugins_two (); 00539 00540 AUDDBG ("Stopping playback.\n"); 00541 if (playback_get_playing ()) 00542 playback_stop (); 00543 00544 adder_cleanup (); 00545 playlist_end (); 00546 00547 AUDDBG ("Unloading lowlevel plugins.\n"); 00548 stop_plugins_one (); 00549 00550 AUDDBG ("Saving configuration.\n"); 00551 cfg_db_flush (); 00552 00553 gdk_threads_leave (); 00554 } 00555 00556 static gboolean autosave_cb (void * unused) 00557 { 00558 AUDDBG ("Saving configuration.\n"); 00559 aud_config_save (); 00560 cfg_db_flush (); 00561 save_playlists (); 00562 return TRUE; 00563 } 00564 00565 gint main(gint argc, gchar ** argv) 00566 { 00567 init_one (& argc, & argv); 00568 parse_options (& argc, & argv); 00569 00570 if (options.version) 00571 { 00572 printf ("%s %s (%s)\n", _("Audacious"), VERSION, BUILDSTAMP); 00573 return EXIT_SUCCESS; 00574 } 00575 00576 if (! get_lock ()) 00577 do_remote (); /* may exit */ 00578 00579 AUDDBG ("No remote session; starting up.\n"); 00580 init_two (); 00581 00582 AUDDBG ("Startup complete.\n"); 00583 g_timeout_add_seconds (AUTOSAVE_INTERVAL, autosave_cb, NULL); 00584 hook_associate ("quit", (HookFunction) gtk_main_quit, NULL); 00585 00586 gtk_main (); 00587 00588 shut_down (); 00589 release_lock (); 00590 return EXIT_SUCCESS; 00591 }