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