view src/audacious/main.c @ 4661:e4e7999a21bc

Move D-Bus initialization before plugin system and playlist initialization. (The other changes in this changeset are to remedy the problem arising from the move, which resulted in annoying pause during startup due to D-Bus being initialized when assumed not to be.)
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 27 Jun 2008 17:57:39 +0300
parents 23e712435e3e
children 8a783f826316
line wrap: on
line source

/*  Audacious - Cross-platform multimedia player
 *  Copyright (C) 2005-2007  Audacious development team.
 *
 *  Based on BMP:
 *  Copyright (C) 2003-2004  BMP development team.
 *
 *  Based on XMMS:
 *  Copyright (C) 1998-2003  XMMS development team.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; under version 3 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses>.
 *
 *  The Audacious team does not consider modular code linking to
 *  Audacious or using our public API to be a derived work.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include "main.h"

#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gprintf.h>
#include <gdk/gdk.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <ctype.h>
#include <time.h>

#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>

#ifdef USE_SAMPLERATE
#  include <samplerate.h>
#endif

#include "platform/smartinclude.h"

#include "configdb.h"
#include "vfs.h"

#ifdef USE_DBUS
#  include "dbus-service.h"
#  include "audctrl.h"
#endif

#include "auddrct.h"
#include "build_stamp.h"
#include "dnd.h"
#include "input.h"
#include "logger.h"
#include "output.h"
#include "playback.h"
#include "playlist.h"
#include "pluginenum.h"
#include "signals.h"
#include "ui_skin.h"
#include "ui_equalizer.h"
#include "ui_fileinfo.h"
#include "ui_hints.h"
#include "ui_main.h"
#include "ui_manager.h"
#include "ui_playlist.h"
#include "ui_preferences.h"
#include "ui_skinselector.h"
#include "util.h"

#include "libSAD.h"
#ifdef USE_EGGSM
#include "eggsmclient.h"
#include "eggdesktopfile.h"
#endif

#include "icons-stock.h"
#include "images/audacious_player.xpm"

static const gchar *application_name = N_("Audacious");

struct _AudCmdLineOpt {
    gchar **filenames;
    gint session;
    gboolean play, stop, pause, fwd, rew, play_pause, show_jump_box;
    gboolean enqueue, mainwin, remote, activate;
    gboolean load_skins;
    gboolean headless;
    gboolean no_log;
    gboolean enqueue_to_temp;
    gboolean version;
    gchar *previous_session_id;
    gboolean macpack;
};
typedef struct _AudCmdLineOpt AudCmdLineOpt;

static AudCmdLineOpt options;

gchar *aud_paths[BMP_PATH_COUNT] = {};

GCond *cond_scan;
GMutex *mutex_scan;
#ifdef USE_DBUS
MprisPlayer *mpris;
#endif

static void
print_version(void)
{
    g_printf("%s %s [%s]\n", _(application_name), VERSION, svn_stamp);
}

static void
aud_make_user_dir(void)
{
    const mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;

    make_directory(aud_paths[BMP_PATH_USER_DIR], mode755);
    make_directory(aud_paths[BMP_PATH_USER_PLUGIN_DIR], mode755);
    make_directory(aud_paths[BMP_PATH_USER_SKIN_DIR], mode755);
    make_directory(aud_paths[BMP_PATH_SKIN_THUMB_DIR], mode755);
    make_directory(aud_paths[BMP_PATH_PLAYLISTS_DIR], mode755);
}

static void
aud_free_paths(void)
{
    int i;

    for (i = 0; i < BMP_PATH_COUNT; i++)
    {
        g_free(aud_paths[i]);
        aud_paths[i] = 0;
    }
}

static void
aud_init_paths()
{
    char *xdg_config_home;
    char *xdg_data_home;
    char *xdg_cache_home;

    xdg_config_home = (getenv("XDG_CONFIG_HOME") == NULL
        ? g_build_filename(g_get_home_dir(), ".config", NULL)
        : g_strdup(getenv("XDG_CONFIG_HOME")));
    xdg_data_home = (getenv("XDG_DATA_HOME") == NULL
        ? g_build_filename(g_get_home_dir(), ".local", "share", NULL)
        : g_strdup(getenv("XDG_DATA_HOME")));
    xdg_cache_home = (getenv("XDG_CACHE_HOME") == NULL
        ? g_build_filename(g_get_home_dir(), ".cache", NULL)
        : g_strdup(getenv("XDG_CACHE_HOME")));

    aud_paths[BMP_PATH_USER_DIR] =
        g_build_filename(xdg_config_home, "audacious", NULL);
    aud_paths[BMP_PATH_USER_SKIN_DIR] =
        g_build_filename(xdg_data_home, "audacious", "Skins", NULL);
    aud_paths[BMP_PATH_USER_PLUGIN_DIR] =
        g_build_filename(xdg_data_home, "audacious", "Plugins", NULL);

    aud_paths[BMP_PATH_SKIN_THUMB_DIR] =
        g_build_filename(xdg_cache_home, "audacious", "thumbs", NULL);

    aud_paths[BMP_PATH_PLAYLISTS_DIR] =
        g_build_filename(aud_paths[BMP_PATH_USER_DIR], "playlists", NULL);

    aud_paths[BMP_PATH_CONFIG_FILE] =
        g_build_filename(aud_paths[BMP_PATH_USER_DIR], "config", NULL);
#ifdef HAVE_XSPF_PLAYLIST
    aud_paths[BMP_PATH_PLAYLIST_FILE] =
        g_build_filename(aud_paths[BMP_PATH_USER_DIR],
        "playlist.xspf", NULL);
#else
    aud_paths[BMP_PATH_PLAYLIST_FILE] =
        g_build_filename(aud_paths[BMP_PATH_USER_DIR],
        "playlist.m3u", NULL);
#endif
    aud_paths[BMP_PATH_ACCEL_FILE] =
        g_build_filename(aud_paths[BMP_PATH_USER_DIR], "accels", NULL);
    aud_paths[BMP_PATH_LOG_FILE] =
        g_build_filename(aud_paths[BMP_PATH_USER_DIR], "log", NULL);

    aud_paths[BMP_PATH_GTKRC_FILE] =
        g_build_filename(aud_paths[BMP_PATH_USER_DIR], "gtkrc", NULL);

    g_free(xdg_config_home);
    g_free(xdg_data_home);
    g_free(xdg_cache_home);

    g_atexit(aud_free_paths);
}

static void
aud_set_default_icon(void)
{
    GdkPixbuf *icon;

    icon = gdk_pixbuf_new_from_xpm_data((const gchar **) audacious_player_xpm);
    gtk_window_set_default_icon(icon);
    g_object_unref(icon);
}

#ifdef GDK_WINDOWING_QUARTZ
static void
set_dock_icon(void)
{
    GdkPixbuf *icon, *pixbuf;
    CGColorSpaceRef colorspace;
    CGDataProviderRef data_provider;
    CGImageRef image;
    gpointer data;
    gint rowstride, pixbuf_width, pixbuf_height;
    gboolean has_alpha;

    icon = gdk_pixbuf_new_from_xpm_data((const gchar **) audacious_player_xpm);
    pixbuf = gdk_pixbuf_scale_simple(icon, 128, 128, GDK_INTERP_BILINEAR);

    data = gdk_pixbuf_get_pixels(pixbuf);
    pixbuf_width = gdk_pixbuf_get_width(pixbuf);
    pixbuf_height = gdk_pixbuf_get_height(pixbuf);
    rowstride = gdk_pixbuf_get_rowstride(pixbuf);
    has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);

    /* create the colourspace for the CGImage. */
    colorspace = CGColorSpaceCreateDeviceRGB();
    data_provider = CGDataProviderCreateWithData(NULL, data,
                                                 pixbuf_height * rowstride,
                                                 NULL);
    image = CGImageCreate(pixbuf_width, pixbuf_height, 8,
                          has_alpha ? 32 : 24, rowstride, colorspace,
                          has_alpha ? kCGImageAlphaLast : 0,
                          data_provider, NULL, FALSE,
                          kCGRenderingIntentDefault);

    /* release the colourspace and data provider, we have what we want. */
    CGDataProviderRelease(data_provider);
    CGColorSpaceRelease(colorspace);

    /* set the dock tile images */
    SetApplicationDockTileImage(image);

#if 0
    /* and release */
    CGImageRelease(image);
    g_object_unref(icon);
    g_object_unref(pixbuf);
#endif
}
#endif

static GOptionEntry cmd_entries[] = {
    {"rew", 'r', 0, G_OPTION_ARG_NONE, &options.rew, N_("Skip backwards in playlist"), NULL},
    {"play", 'p', 0, G_OPTION_ARG_NONE, &options.play, N_("Start playing current playlist"), NULL},
    {"pause", 'u', 0, G_OPTION_ARG_NONE, &options.pause, N_("Pause current song"), NULL},
    {"stop", 's', 0, G_OPTION_ARG_NONE, &options.stop, N_("Stop current song"), NULL},
    {"play-pause", 't', 0, G_OPTION_ARG_NONE, &options.play_pause, N_("Pause if playing, play otherwise"), NULL},
    {"fwd", 'f', 0, G_OPTION_ARG_NONE, &options.fwd, N_("Skip forward in playlist"), NULL},
    {"show-jump-box", 'j', 0, G_OPTION_ARG_NONE, &options.show_jump_box, N_("Display Jump to File dialog"), NULL},
    {"enqueue", 'e', 0, G_OPTION_ARG_NONE, &options.enqueue, N_("Don't clear the playlist"), NULL},
    {"enqueue-to-temp", 'E', 0, G_OPTION_ARG_NONE, &options.enqueue_to_temp, N_("Add new files to a temporary playlist"), NULL},
    {"show-main-window", 'm', 0, G_OPTION_ARG_NONE, &options.mainwin, N_("Display the main window"), NULL},
    {"activate", 'a', 0, G_OPTION_ARG_NONE, &options.activate, N_("Display all open Audacious windows"), NULL},
    {"headless", 'H', 0, G_OPTION_ARG_NONE, &options.headless, N_("Enable headless operation"), NULL},
    {"no-log", 'N', 0, G_OPTION_ARG_NONE, &options.no_log, N_("Print all errors and warnings to stdout"), NULL},
    {"version", 'v', 0, G_OPTION_ARG_NONE, &options.version, N_("Show version"), NULL},
#ifdef GDK_WINDOWING_QUARTZ
    {"macpack", 'n', 0, G_OPTION_ARG_NONE, &options.macpack, N_("Used in macpacking"), NULL}, /* Make this hidden */
#endif
    {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &options.filenames, N_("FILE..."), NULL},
    {NULL},
};

static gboolean
aud_start_playback(gpointer unused)
{
    drct_play();
    return FALSE;
}

static void
parse_cmd_line_options(gint *argc, gchar ***argv)
{
    GOptionContext *context;
    GError *error = NULL;

    memset(&options, '\0', sizeof(AudCmdLineOpt));
    options.session = -1;

    context = g_option_context_new(_("- play multimedia files"));
    g_option_context_add_main_entries(context, cmd_entries, PACKAGE_NAME);
    g_option_context_add_group(context, gtk_get_option_group(FALSE));
#ifdef USE_EGGSM
    g_option_context_add_group(context, egg_sm_client_get_option_group());
#endif
    if (!g_option_context_parse(context, argc, argv, &error))
        /* checking for MacOS X -psn_0_* errors*/
        if (error->message && !g_strrstr(error->message,"-psn_0_"))
        {
            g_printerr(_("%s: %s\nTry `%s --help' for more information.\n"),
                       (*argv)[0], error->message, (*argv)[0]);
            exit(EXIT_FAILURE);
        }
}

static void
handle_cmd_line_options(gboolean skip)
{
    gchar **filenames = options.filenames;
#ifdef USE_DBUS
    DBusGProxy *session = audacious_get_dbus_proxy();
    gboolean is_running;

    if (skip)
      is_running = audacious_remote_is_running(session);
    else
      is_running = FALSE;
#endif

    if (options.version)
    {
        print_version();
        exit(EXIT_SUCCESS);
    }

#ifdef USE_DBUS
    if (is_running)
    {
        if (filenames != NULL)
        {
            gint pos = 0;
            gint i = 0;
            GList *fns = NULL;

            for (i = 0; filenames[i] != NULL; i++)
            {
                gchar *filename;
                gchar *current_dir = g_get_current_dir();

                if (!strstr(filenames[i], "://"))
                {
                    if (filenames[i][0] == '/')
                        filename = g_strdup_printf("file:///%s", filenames[i]);
                    else
                        filename = g_strdup_printf("file:///%s/%s", current_dir,
                                                   filenames[i]);
                }
                else
                    filename = g_strdup(filenames[i]);

                fns = g_list_prepend(fns, filename);

                g_free(current_dir);
            }

            fns = g_list_reverse(fns);

            if (options.load_skins)
            {
                audacious_remote_set_skin(session, filenames[0]);
                skin_install_skin(filenames[0]);
            }
            else
            {
                GList *i;

                if (options.enqueue_to_temp)
                    audacious_remote_playlist_enqueue_to_temp(session, filenames[0]);

                if (options.enqueue && options.play)
                    pos = audacious_remote_get_playlist_length(session);

                if (!options.enqueue)
                {
                    audacious_remote_playlist_clear(session);
                    audacious_remote_stop(session);
                }

                for (i = fns; i != NULL; i = i->next)
                    audacious_remote_playlist_add_url_string(session, i->data);

                if (options.enqueue && options.play &&
                    audacious_remote_get_playlist_length(session) > pos)
                    audacious_remote_set_playlist_pos(session, pos);

                if (!options.enqueue)
                    audacious_remote_play(session);
            }

            g_list_foreach(fns, (GFunc) g_free, NULL);
            g_list_free(fns);

            g_strfreev(filenames);
        } /* filename */

        if (options.rew)
            audacious_remote_playlist_prev(session);

        if (options.play)
            audacious_remote_play(session);

        if (options.pause)
            audacious_remote_pause(session);

        if (options.stop)
            audacious_remote_stop(session);

        if (options.fwd)
            audacious_remote_playlist_next(session);

        if (options.play_pause)
            audacious_remote_play_pause(session);

        if (options.show_jump_box)
            audacious_remote_show_jtf_box(session);

        if (options.mainwin)
            audacious_remote_main_win_toggle(session, TRUE);

        if (options.activate)
            audacious_remote_activate(session);

        exit(EXIT_SUCCESS);
    } /* is_running */
    else
#endif
    if (!skip) { /* !is_running */
        if (filenames != NULL)
        {
            gint pos = 0;
            gint i = 0;
            GList *fns = NULL;

            for (i = 0; filenames[i] != NULL; i++)
            {
                gchar *filename;
                gchar *current_dir = g_get_current_dir();

                if (!strstr(filenames[i], "://"))
                {
                    if (filenames[i][0] == '/')
                        filename = g_strdup_printf("file:///%s", filenames[i]);
                    else
                        filename = g_strdup_printf("file:///%s/%s", current_dir,
                                                   filenames[i]);
                }
                else
                    filename = g_strdup(filenames[i]);

                fns = g_list_prepend(fns, filename);

                g_free(current_dir);
            }

            fns = g_list_reverse(fns);

            {
                if (options.enqueue_to_temp)
                    drct_pl_enqueue_to_temp(filenames[0]);

                if (options.enqueue && options.play)
                    pos = drct_pl_get_length();

                if (!options.enqueue)
                {
                    drct_pl_clear();
                    drct_stop();
                }

                drct_pl_add(fns);

                if (options.enqueue && options.play &&
                    drct_pl_get_length() > pos)
                    drct_pl_set_pos(pos);

                if (!options.enqueue)
                    g_idle_add(aud_start_playback, NULL);
            }

            g_list_foreach(fns, (GFunc) g_free, NULL);
            g_list_free(fns);

            g_strfreev(filenames);
        } /* filename */

        if (options.rew)
            drct_pl_prev();

        if (options.play)
            drct_play();

        if (options.pause)
            drct_pause();

        if (options.stop)
            drct_stop();

        if (options.fwd)
            drct_pl_next();

        if (options.play_pause) {
            if (drct_get_paused())
                drct_play();
            else
                drct_pause();
        }

        if (options.show_jump_box)
            drct_jtf_show();

        if (options.mainwin)
            drct_main_win_toggle(TRUE);

        if (options.activate)
            drct_activate();
    } /* !is_running */
}

static void
aud_setup_logger(void)
{
    if (!aud_logger_start(aud_paths[BMP_PATH_LOG_FILE]))
        return;

    g_atexit(aud_logger_stop);
}

static void
run_load_skin_error_dialog(const gchar * skin_path)
{
    const gchar *markup =
        N_("<b><big>Unable to load skin.</big></b>\n"
           "\n"
           "Check that skin at '%s' is usable and default skin is properly "
           "installed at '%s'\n");

    GtkWidget *dialog =
        gtk_message_dialog_new_with_markup(NULL,
                                           GTK_DIALOG_MODAL,
                                           GTK_MESSAGE_ERROR,
                                           GTK_BUTTONS_CLOSE,
                                           _(markup),
                                           skin_path,
                                           BMP_DEFAULT_SKIN_PATH);
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
}

static gboolean
aud_headless_iteration(gpointer unused)
{
    free_vis_data();
    return TRUE;
}

static gboolean
load_extra_playlist(const gchar * path, const gchar * basename,
        gpointer def)
{
    Playlist *playlist = playlist_new();
    
    if (!playlist) {
        g_warning("Couldn't create new playlist for %s / %s\n", path, basename);
        return FALSE;
    }

    playlist_add_playlist(playlist);
    playlist_load(playlist, path);

    return FALSE; /* keep loading other playlists */
}

static void
resume_playback_on_startup()
{
    gint i;
    
    if (!cfg.resume_playback_on_startup ||
        cfg.resume_playback_on_startup_time == -1 ||
        playlist_get_length(playlist_get_active()) <= 0)
        return;

    while (gtk_events_pending()) gtk_main_iteration();

    playback_initiate();

    /* Busy wait; loop is fairly tight to minimize duration of
     * "frozen" GUI. Feel free to tune. --chainsaw */
    for (i = 0; i < 20; i++)
    { 
        g_usleep(1000);
        if (!ip_data.playing)
            break;
    }
    playback_seek(cfg.resume_playback_on_startup_time / 1000);
}

static void
playlist_system_init()
{
    Playlist *playlist;

    playlist_init();
    playlist = playlist_get_active();
    playlist_load(playlist, aud_paths[BMP_PATH_PLAYLIST_FILE]);
    playlist_set_position(playlist, cfg.playlist_position);

    /* Load extra playlists */
    if (!dir_foreach(aud_paths[BMP_PATH_PLAYLISTS_DIR], load_extra_playlist,
                     playlist, NULL))
        g_warning("Could not load extra playlists\n");
}

void
aud_quit(void)
{
    GList *playlists = NULL, *playlists_top = NULL;

    playlist_stop_get_info_thread();

    aud_config_save();

    if (options.headless == FALSE)
    {
        gtk_widget_hide(equalizerwin);
        gtk_widget_hide(playlistwin);
        gtk_widget_hide(mainwin);

        gtk_accel_map_save(aud_paths[BMP_PATH_ACCEL_FILE]);
        gtk_main_quit();

        cleanup_skins();
    }

    plugin_system_cleanup();

    /* free and clear each playlist */
    playlists = playlist_get_playlists();
    playlists_top = playlists;
    while ( playlists != NULL )
    {
        playlist_clear((Playlist*)playlists->data);
        playlist_free((Playlist*)playlists->data);
        playlists = g_list_next(playlists);
    }
    g_list_free( playlists_top );

    g_cond_free(cond_scan);
    g_mutex_free(mutex_scan);

    exit(EXIT_SUCCESS);
}

gint
main(gint argc, gchar ** argv)
{
    /* glib-2.13.0 requires g_thread_init() to be called before all
       other GLib functions */
    g_thread_init(NULL);
    if (!g_thread_supported()) {
        g_printerr(_("Sorry, threads aren't supported on your platform.\n"));
        exit(EXIT_FAILURE);
    }

    gdk_threads_init();
    mowgli_init();

    /* Setup l10n early so we can print localized error messages */
    gtk_set_locale();
    bindtextdomain(PACKAGE_NAME, LOCALEDIR);
    bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
    bindtextdomain(PACKAGE_NAME "-plugins", LOCALEDIR);
    bind_textdomain_codeset(PACKAGE_NAME "-plugins", "UTF-8");
    textdomain(PACKAGE_NAME);

#if !defined(_WIN32) && defined(USE_EGGSM)
    egg_set_desktop_file(AUDACIOUS_DESKTOP_FILE);
#endif
    aud_init_paths();
    aud_make_user_dir();

    cond_scan = g_cond_new();
    mutex_scan = g_mutex_new();
    gtk_rc_add_default_file(aud_paths[BMP_PATH_GTKRC_FILE]);

    parse_cmd_line_options(&argc, &argv);

    if (options.no_log == FALSE)
        aud_setup_logger();

    if (!gtk_init_check(&argc, &argv) && options.headless == FALSE) {
        /* GTK check failed, and no arguments passed to indicate
           that user is intending to only remote control a running
           session */
        g_printerr(_("%s: Unable to open display, exiting.\n"), argv[0]);
        exit(EXIT_FAILURE);
    }

    g_random_set_seed(time(NULL));
    SAD_dither_init_rand((gint32)time(NULL));

    aud_config_load();

    signal_handlers_init();

    handle_cmd_line_options(TRUE);

    if (options.headless == FALSE)
    {
        ui_main_check_theme_engine();

        /* register icons in stock
           NOTE: should be called before UIManager */
        register_aud_stock_icons();

        /* UIManager
           NOTE: this needs to be called before plugin init, cause
           plugin init functions may want to add custom menu entries */
        ui_manager_init();
        ui_manager_create_menus();
    }

#ifdef USE_DBUS
    init_dbus();
#endif

    plugin_system_init();
    playlist_system_init();

    handle_cmd_line_options(FALSE);


    playlist_start_get_info_thread();

    output_set_volume((cfg.saved_volume & 0xff00) >> 8,
                      (cfg.saved_volume & 0x00ff));

    if (options.headless == FALSE)
    {
        aud_set_default_icon();
#ifdef GDK_WINDOWING_QUARTZ
        set_dock_icon();
#endif

        gtk_accel_map_load(aud_paths[BMP_PATH_ACCEL_FILE]);

        if (!init_skins(cfg.skin)) {
            run_load_skin_error_dialog(cfg.skin);
            exit(EXIT_FAILURE);
        }

        GDK_THREADS_ENTER();

        /* this needs to be called after all 3 windows are created and
         * input plugins are setup'ed 
         * but not if we're running headless --nenolod
         */
        mainwin_setup_menus();

        gint h_vol[2];
        input_get_volume(&h_vol[0], &h_vol[1]);
        hook_call("volume set", h_vol);

        /* FIXME: delayed, because it deals directly with the plugin
         * interface to set menu items */
        create_prefs_window();

        create_fileinfo_window();


        if (cfg.player_visible)
            mainwin_show(TRUE);
        else if (!cfg.playlist_visible && !cfg.equalizer_visible)
        {
          /* all of the windows are hidden... warn user about this */
          mainwin_show_visibility_warning();
        }

        if (cfg.equalizer_visible)
            equalizerwin_show(TRUE);

        if (cfg.playlist_visible)
            playlistwin_show();

        hint_set_always(cfg.always_on_top);

        resume_playback_on_startup();
        
        gtk_main();

        GDK_THREADS_LEAVE();
    }
    // if we are running headless
    else
    {
        g_print(_("Headless operation enabled\n"));
        resume_playback_on_startup();

        g_timeout_add(10, aud_headless_iteration, NULL);
        g_main_loop_run(g_main_loop_new(NULL, TRUE));
    }

    aud_quit();
    return EXIT_SUCCESS;
}