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