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