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 #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
00062 void chardet_init (void);
00063
00064
00065 void mpris_signals_init (void);
00066 void mpris_signals_cleanup (void);
00067
00068
00069 void signals_init (void);
00070
00071
00072 void smclient_init (void);
00073
00074 #define AUTOSAVE_INTERVAL 300
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] == '\\')
00109 #else
00110 if (len > 1 && path[len - 1] == '/')
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;
00129 else
00130 elem[0] = 0;
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
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
00176
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
00189 gchar * base = last_path_element (new);
00190 if (! base)
00191 goto ERR;
00192 strip_path_element (new, base);
00193
00194
00195
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
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
00229
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 ();
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 }