Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
main.c
Go to the documentation of this file.
1 /*
2  * main.c
3  * Copyright 2007-2011 William Pitcock and John Lindgren
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions, and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions, and the following disclaimer in the documentation
13  * provided with the distribution.
14  *
15  * This software is provided "as is" and without any warranty, express or
16  * implied. In no event shall the authors be liable for any damages arising from
17  * the use of this software.
18  */
19 
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <locale.h>
27 
28 #include <gtk/gtk.h>
29 
30 #include <libaudcore/audstrings.h>
31 #include <libaudcore/hook.h>
32 #include <libaudtag/audtag.h>
33 
34 #include "config.h"
35 
36 #ifdef USE_DBUS
37 #include "../libaudclient/audctrl.h"
38 #include "dbus.h"
39 #endif
40 
41 #include "debug.h"
42 #include "drct.h"
43 #include "equalizer.h"
44 #include "i18n.h"
45 #include "interface.h"
46 #include "main.h"
47 #include "misc.h"
48 #include "playback.h"
49 #include "playlist.h"
50 #include "plugins.h"
51 #include "util.h"
52 
53 #define AUTOSAVE_INTERVAL 300 /* seconds */
54 
56 
57 static struct {
58  char **filenames;
59  int session;
66 } options;
67 
68 static char * aud_paths[AUD_PATH_COUNT];
69 
70 static void make_dirs(void)
71 {
72 #ifdef S_IRGRP
73  const mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
74 #else
75  const mode_t mode755 = S_IRWXU;
76 #endif
77 
80 }
81 
82 static void normalize_path (char * path)
83 {
84 #ifdef _WIN32
85  string_replace_char (path, '/', '\\');
86 #endif
87  int len = strlen (path);
88 #ifdef _WIN32
89  if (len > 3 && path[len - 1] == '\\') /* leave "C:\" */
90 #else
91  if (len > 1 && path[len - 1] == '/') /* leave leading "/" */
92 #endif
93  path[len - 1] = 0;
94 }
95 
96 static char * last_path_element (char * path)
97 {
98  char * slash = strrchr (path, G_DIR_SEPARATOR);
99  return (slash && slash[1]) ? slash + 1 : NULL;
100 }
101 
102 static void strip_path_element (char * path, char * elem)
103 {
104 #ifdef _WIN32
105  if (elem > path + 3)
106 #else
107  if (elem > path + 1)
108 #endif
109  elem[-1] = 0; /* overwrite slash */
110  else
111  elem[0] = 0; /* leave [drive letter and] leading slash */
112 }
113 
114 static void relocate_path (char * * pathp, const char * old, const char * new)
115 {
116  char * path = * pathp;
117  int oldlen = strlen (old);
118  int newlen = strlen (new);
119 
120  if (oldlen && old[oldlen - 1] == G_DIR_SEPARATOR)
121  oldlen --;
122  if (newlen && new[newlen - 1] == G_DIR_SEPARATOR)
123  newlen --;
124 
125 #ifdef _WIN32
126  if (strncasecmp (path, old, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR))
127 #else
128  if (strncmp (path, old, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR))
129 #endif
130  {
131  fprintf (stderr, "Failed to relocate a data path. Falling back to "
132  "compile-time path: %s\n", path);
133  return;
134  }
135 
136  * pathp = g_strdup_printf ("%.*s%s", newlen, new, path + oldlen);
137  g_free (path);
138 }
139 
140 static void relocate_paths (void)
141 {
142  /* Start with the paths hard coded at compile time. */
143  aud_paths[AUD_PATH_BIN_DIR] = g_strdup (HARDCODE_BINDIR);
144  aud_paths[AUD_PATH_DATA_DIR] = g_strdup (HARDCODE_DATADIR);
145  aud_paths[AUD_PATH_PLUGIN_DIR] = g_strdup (HARDCODE_PLUGINDIR);
146  aud_paths[AUD_PATH_LOCALE_DIR] = g_strdup (HARDCODE_LOCALEDIR);
147  aud_paths[AUD_PATH_DESKTOP_FILE] = g_strdup (HARDCODE_DESKTOPFILE);
148  aud_paths[AUD_PATH_ICON_FILE] = g_strdup (HARDCODE_ICONFILE);
155 
156  /* Compare the compile-time path to the executable and the actual path to
157  * see if we have been moved. */
158  char * old = g_strdup (aud_paths[AUD_PATH_BIN_DIR]);
159  char * new = get_path_to_self ();
160  if (! new)
161  {
162 ERR:
163  g_free (old);
164  g_free (new);
165  return;
166  }
167  normalize_path (new);
168 
169  /* Strip the name of the executable file, leaving the path. */
170  char * base = last_path_element (new);
171  if (! base)
172  goto ERR;
173  strip_path_element (new, base);
174 
175  /* Strip innermost folder names from both paths as long as they match. This
176  * leaves a compile-time prefix and a run-time one to replace it with. */
177  char * a, * b;
178  while ((a = last_path_element (old)) && (b = last_path_element (new)) &&
179 #ifdef _WIN32
180  ! strcasecmp (a, b))
181 #else
182  ! strcmp (a, b))
183 #endif
184  {
185  strip_path_element (old, a);
186  strip_path_element (new, b);
187  }
188 
189  /* Do the replacements. */
190  relocate_path (& aud_paths[AUD_PATH_BIN_DIR], old, new);
191  relocate_path (& aud_paths[AUD_PATH_DATA_DIR], old, new);
192  relocate_path (& aud_paths[AUD_PATH_PLUGIN_DIR], old, new);
193  relocate_path (& aud_paths[AUD_PATH_LOCALE_DIR], old, new);
194  relocate_path (& aud_paths[AUD_PATH_DESKTOP_FILE], old, new);
195  relocate_path (& aud_paths[AUD_PATH_ICON_FILE], old, new);
196 
197  g_free (old);
198  g_free (new);
199 }
200 
201 static void init_paths (void)
202 {
203  relocate_paths ();
204 
205  const char * xdg_config_home = g_get_user_config_dir ();
206  const char * xdg_data_home = g_get_user_data_dir ();
207 
208 #ifdef _WIN32
209  /* Some libraries (libmcs) and plugins (filewriter) use these variables,
210  * which are generally not set on Windows. */
211  g_setenv ("HOME", g_get_home_dir (), TRUE);
212  g_setenv ("XDG_CONFIG_HOME", xdg_config_home, TRUE);
213  g_setenv ("XDG_DATA_HOME", xdg_data_home, TRUE);
214  g_setenv ("XDG_CACHE_HOME", g_get_user_cache_dir (), TRUE);
215 #endif
216 
217  aud_paths[AUD_PATH_USER_DIR] = g_build_filename(xdg_config_home, "audacious", NULL);
218  aud_paths[AUD_PATH_USER_PLUGIN_DIR] = g_build_filename(xdg_data_home, "audacious", "Plugins", NULL);
219  aud_paths[AUD_PATH_PLAYLISTS_DIR] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "playlists", NULL);
220  aud_paths[AUD_PATH_GTKRC_FILE] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "gtkrc", NULL);
221 
222  for (int i = 0; i < AUD_PATH_COUNT; i ++)
223  AUDDBG ("Data path: %s\n", aud_paths[i]);
224 }
225 
226 const char * get_path (int id)
227 {
228  g_return_val_if_fail (id >= 0 && id < AUD_PATH_COUNT, NULL);
229  return aud_paths[id];
230 }
231 
232 static GOptionEntry cmd_entries[] = {
233  {"rew", 'r', 0, G_OPTION_ARG_NONE, &options.rew, N_("Skip backwards in playlist"), NULL},
234  {"play", 'p', 0, G_OPTION_ARG_NONE, &options.play, N_("Start playing current playlist"), NULL},
235  {"pause", 'u', 0, G_OPTION_ARG_NONE, &options.pause, N_("Pause current song"), NULL},
236  {"stop", 's', 0, G_OPTION_ARG_NONE, &options.stop, N_("Stop current song"), NULL},
237  {"play-pause", 't', 0, G_OPTION_ARG_NONE, &options.play_pause, N_("Pause if playing, play otherwise"), NULL},
238  {"fwd", 'f', 0, G_OPTION_ARG_NONE, &options.fwd, N_("Skip forward in playlist"), NULL},
239  {"show-jump-box", 'j', 0, G_OPTION_ARG_NONE, &options.show_jump_box, N_("Display Jump to File dialog"), NULL},
240  {"enqueue", 'e', 0, G_OPTION_ARG_NONE, &options.enqueue, N_("Add files to the playlist"), NULL},
241  {"enqueue-to-temp", 'E', 0, G_OPTION_ARG_NONE, &options.enqueue_to_temp, N_("Add new files to a temporary playlist"), NULL},
242  {"show-main-window", 'm', 0, G_OPTION_ARG_NONE, &options.mainwin, N_("Display the main window"), NULL},
243  {"version", 'v', 0, G_OPTION_ARG_NONE, &options.version, N_("Show version"), NULL},
244  {"verbose", 'V', 0, G_OPTION_ARG_NONE, &options.verbose, N_("Print debugging messages"), NULL},
245  {"headless", 'h', 0, G_OPTION_ARG_NONE, & headless, N_("Headless mode (beta)"), NULL},
246  {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &options.filenames, N_("FILE..."), NULL},
247  {NULL},
248 };
249 
250 static void parse_options (int * argc, char *** argv)
251 {
252  GOptionContext *context;
253  GError *error = NULL;
254 
255  memset (& options, 0, sizeof options);
256  options.session = -1;
257 
258  context = g_option_context_new(_("- play multimedia files"));
259  g_option_context_add_main_entries(context, cmd_entries, PACKAGE);
260  g_option_context_add_group(context, gtk_get_option_group(FALSE));
261 
262  if (!g_option_context_parse(context, argc, argv, &error))
263  {
264  fprintf (stderr,
265  _("%s: %s\nTry `%s --help' for more information.\n"), (* argv)[0],
266  error->message, (* argv)[0]);
267  exit (EXIT_FAILURE);
268  }
269 
270  g_option_context_free (context);
271 
272  verbose = options.verbose;
273 }
274 
275 static bool_t get_lock (void)
276 {
277  char * path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "lock", aud_paths[AUD_PATH_USER_DIR]);
278  int handle = open (path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
279 
280  if (handle < 0)
281  {
282  if (errno != EEXIST)
283  fprintf (stderr, "Cannot create %s: %s.\n", path, strerror (errno));
284 
285  g_free (path);
286  return FALSE;
287  }
288 
289  close (handle);
290  g_free (path);
291  return TRUE;
292 }
293 
294 static void release_lock (void)
295 {
296  char * path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "lock", aud_paths[AUD_PATH_USER_DIR]);
297  unlink (path);
298  g_free (path);
299 }
300 
301 static Index * convert_filenames (void)
302 {
303  if (! options.filenames)
304  return NULL;
305 
306  Index * filenames = index_new ();
307  char * * f = options.filenames;
308  char * cur = g_get_current_dir ();
309 
310  for (int i = 0; f[i]; i ++)
311  {
312  char * uri = NULL;
313 
314  if (strstr (f[i], "://"))
315  uri = str_get (f[i]);
316  else if (g_path_is_absolute (f[i]))
317  {
318  char * tmp = filename_to_uri (f[i]);
319  uri = str_get (tmp);
320  free (tmp);
321  }
322  else
323  {
324  char * tmp = g_build_filename (cur, f[i], NULL);
325  char * tmp2 = filename_to_uri (tmp);
326  uri = str_get (tmp2);
327  free (tmp);
328  free (tmp2);
329  }
330 
331  if (uri)
332  index_append (filenames, uri);
333  }
334 
335  g_free (cur);
336  return filenames;
337 }
338 
339 static void do_remote (void)
340 {
341 #ifdef USE_DBUS
342  DBusGProxy * session = audacious_get_dbus_proxy ();
343 
344  if (session && audacious_remote_is_running (session))
345  {
346  Index * filenames = convert_filenames ();
347 
348  /* if no command line options, then present running instance */
349  if (! (filenames || options.play || options.pause || options.play_pause ||
350  options.stop || options.rew || options.fwd || options.show_jump_box ||
351  options.mainwin))
352  options.mainwin = TRUE;
353 
354  if (filenames)
355  {
356  GList * list = NULL;
357 
358  for (int f = index_count (filenames); f --; )
359  list = g_list_prepend (list, index_get (filenames, f));
360 
361  if (options.enqueue_to_temp)
363  else if (options.enqueue)
364  audacious_remote_playlist_add (session, list);
365  else
366  audacious_remote_playlist_open_list (session, list);
367 
368  g_list_free (list);
369 
370  for (int f = 0; f < index_count (filenames); f ++)
371  str_unref (index_get (filenames, f));
372 
373  index_free (filenames);
374  }
375 
376  if (options.play)
377  audacious_remote_play (session);
378  if (options.pause)
379  audacious_remote_pause (session);
380  if (options.play_pause)
381  audacious_remote_play_pause (session);
382  if (options.stop)
383  audacious_remote_stop (session);
384  if (options.rew)
386  if (options.fwd)
388  if (options.show_jump_box)
390  if (options.mainwin)
392 
393  exit (EXIT_SUCCESS);
394  }
395 #endif
396 
397  fprintf (stderr, "WARNING: Audacious seems to be already running but is not responding.\n");
398 }
399 
400 static void do_commands (void)
401 {
402  bool_t resume = TRUE;
403 
404  Index * filenames = convert_filenames ();
405  if (filenames)
406  {
407  if (options.enqueue_to_temp)
408  {
409  drct_pl_open_temp_list (filenames);
410  resume = FALSE;
411  }
412  else if (options.enqueue)
413  drct_pl_add_list (filenames, -1);
414  else
415  {
416  drct_pl_open_list (filenames);
417  resume = FALSE;
418  }
419  }
420 
421  if (resume)
422  playlist_resume ();
423 
424  if (options.play || options.play_pause)
425  {
426  if (! playback_get_playing ())
427  playback_play (0, FALSE);
428  else if (playback_get_paused ())
429  playback_pause ();
430  }
431 
432  if (options.show_jump_box)
434  if (options.mainwin)
436 }
437 
438 static void init_one (void)
439 {
440  init_paths ();
441  make_dirs ();
442 
443  setlocale (LC_ALL, "");
444  bindtextdomain (PACKAGE, aud_paths[AUD_PATH_LOCALE_DIR]);
445  bind_textdomain_codeset (PACKAGE, "UTF-8");
446  bindtextdomain (PACKAGE "-plugins", aud_paths[AUD_PATH_LOCALE_DIR]);
447  bind_textdomain_codeset (PACKAGE "-plugins", "UTF-8");
448  textdomain (PACKAGE);
449 }
450 
451 static void init_two (int * p_argc, char * * * p_argv)
452 {
453  if (! headless)
454  {
455 #if ! GLIB_CHECK_VERSION (2, 32, 0)
456  g_thread_init (NULL);
457 #endif
458  gtk_init (p_argc, p_argv);
459  }
460 
461  config_load ();
462  chardet_init ();
463 
464  tag_set_verbose (verbose);
466 
467  eq_init ();
468 
469 #ifdef HAVE_SIGWAIT
470  signals_init ();
471 #endif
472 
473  AUDDBG ("Loading lowlevel plugins.\n");
475 
476  playlist_init ();
477  adder_init ();
478  art_init ();
479  load_playlists ();
480 
481 #ifdef USE_DBUS
482  init_dbus ();
483 #endif
484 
485  do_commands ();
486 
487  AUDDBG ("Loading highlevel plugins.\n");
489 
491 }
492 
493 static void shut_down (void)
494 {
496 
497  AUDDBG ("Capturing state.\n");
498  hook_call ("config save", NULL);
500 
501  AUDDBG ("Unloading highlevel plugins.\n");
502  stop_plugins_two ();
503 
504  AUDDBG ("Stopping playback.\n");
505  if (playback_get_playing ())
506  {
507  bool_t stop_after_song = get_bool (NULL, "stop_after_current_song");
508  playback_stop ();
509  set_bool (NULL, "stop_after_current_song", stop_after_song);
510  }
511 
512 #ifdef USE_DBUS
513  cleanup_dbus ();
514 #endif
515 
516  adder_cleanup ();
517  art_cleanup ();
518  history_cleanup ();
519  playlist_end ();
520 
521  AUDDBG ("Unloading lowlevel plugins.\n");
522  stop_plugins_one ();
523 
524  AUDDBG ("Saving configuration.\n");
525  config_save ();
526  config_cleanup ();
527 
528  eq_cleanup ();
529 
530  strpool_shutdown ();
531 }
532 
534 {
535  AUDDBG ("Saving configuration.\n");
536  hook_call ("config save", NULL);
538  config_save ();
539  return TRUE;
540 }
541 
542 int main(int argc, char ** argv)
543 {
544  init_one ();
545  parse_options (& argc, & argv);
546 
547  if (options.version)
548  {
549  printf ("%s %s (%s)\n", _("Audacious"), VERSION, BUILDSTAMP);
550  return EXIT_SUCCESS;
551  }
552 
553  if (! get_lock ())
554  do_remote (); /* may exit */
555 
556  AUDDBG ("No remote session; starting up.\n");
557  init_two (& argc, & argv);
558 
559  AUDDBG ("Startup complete.\n");
560  g_timeout_add_seconds (AUTOSAVE_INTERVAL, (GSourceFunc) do_autosave, NULL);
561 
562  hook_associate ("quit", (HookFunction) gtk_main_quit, NULL);
563  gtk_main ();
564  hook_dissociate ("quit", (HookFunction) gtk_main_quit);
565 
566  shut_down ();
567  release_lock ();
568  return EXIT_SUCCESS;
569 }