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