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