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