Пример #1
0
static void
mainloop_thread (void *ctx) {
    // this runs until DB_EV_TERMINATE is sent (blocks right here)
    player_mainloop ();

    // tell the gui thread to finish
    DB_plugin_t *gui = plug_get_gui ();
    if (gui) {
        gui->stop ();
    }
    return;
}
Пример #2
0
static DB_plugin_action_t *
get_action (const char* command)
{
    DB_plugin_t **plugins = deadbeef->plug_get_list ();
    for (int i = 0; plugins[i]; i++) {
        DB_plugin_t *p = plugins[i];
        if (p->get_actions) {
            DB_plugin_action_t *actions = p->get_actions (NULL);
            while (actions) {
                if (actions->name && !strcasecmp (command, actions->name)) {
                    return actions;
                }
                actions = actions->next;
            }
        }
    }

    return NULL;
}
Пример #3
0
static DB_plugin_action_t *
find_action_by_name (const char *command) {
    // find action with this name, and add to list
    DB_plugin_action_t *actions = NULL;
    DB_plugin_t **plugins = deadbeef->plug_get_list ();
    for (int i = 0; plugins[i]; i++) {
        DB_plugin_t *p = plugins[i];
        if (p->get_actions) {
            actions = p->get_actions (NULL);
            while (actions) {
                if (actions->name && actions->title && !strcasecmp (actions->name, command)) {
                    break; // found
                }
                actions = actions->next;
            }
            if (actions) {
                break;
            }
        }
    }
    return actions;
}
Пример #4
0
void
init_action_tree (GtkWidget *actions, const char *act, int ctx) {
    GtkTreeViewColumn *hk_act_col1 = gtk_tree_view_column_new_with_attributes (_("Action"), gtk_cell_renderer_text_new (), "text", 0, NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (actions), hk_act_col1);

    // traverse all plugins and collect all exported actions to treeview
    // column0: title
    // column1: ID (invisible)
    // column2: ctx (invisible
    GtkTreeStore *actions_store = gtk_tree_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
    GtkTreeIter action_main_iter;
    gtk_tree_store_append (actions_store, &action_main_iter, NULL);
    gtk_tree_store_set (actions_store, &action_main_iter, 0, _("Main"), -1);

    GtkTreeIter action_selection_iter;
    gtk_tree_store_append (actions_store, &action_selection_iter, NULL);
    gtk_tree_store_set (actions_store, &action_selection_iter, 0, _("Selected track(s)"), -1);
    GtkTreeIter action_playlist_iter;
    gtk_tree_store_append (actions_store, &action_playlist_iter, NULL);
    gtk_tree_store_set (actions_store, &action_playlist_iter, 0, _("Current playlist"), -1);
    GtkTreeIter action_nowplaying_iter;
    gtk_tree_store_append (actions_store, &action_nowplaying_iter, NULL);
    gtk_tree_store_set (actions_store, &action_nowplaying_iter, 0, _("Now playing"), -1);

    DB_plugin_t **plugins = deadbeef->plug_get_list ();
    for (int i = 0; plugins[i]; i++) {
        DB_plugin_t *p = plugins[i];
        if (p->get_actions) {
            DB_plugin_action_t *actions = p->get_actions (NULL);
            while (actions) {
                if (actions->name && actions->title) { // only add actions with both the name and the title
                    char title[100];

                    GtkTreeIter iter;
                    const char *t;
                    if (actions->flags & DB_ACTION_COMMON) {
                        t = action_tree_append (actions->title, actions_store, &action_main_iter, &iter);
                        unescape_forward_slash (t, title, sizeof (title));
                        gtk_tree_store_set (actions_store, &iter, 0, title, 1, actions->name, 2, DDB_ACTION_CTX_MAIN, -1);
                    }
                    if (actions->flags & (DB_ACTION_SINGLE_TRACK | DB_ACTION_MULTIPLE_TRACKS | DB_ACTION_CAN_MULTIPLE_TRACKS)) {
                        t = action_tree_append (actions->title, actions_store, &action_selection_iter, &iter);
                        unescape_forward_slash (t, title, sizeof (title));
                        gtk_tree_store_set (actions_store, &iter, 0, title, 1, actions->name, 2, DDB_ACTION_CTX_SELECTION, -1);
                        t = action_tree_append (actions->title, actions_store, &action_playlist_iter, &iter);
                        unescape_forward_slash (t, title, sizeof (title));
                        gtk_tree_store_set (actions_store, &iter, 0, title, 1, actions->name, 2, DDB_ACTION_CTX_PLAYLIST, -1);
                        t = action_tree_append (actions->title, actions_store, &action_nowplaying_iter, &iter);
                        unescape_forward_slash (t, title, sizeof (title));
                        gtk_tree_store_set (actions_store, &iter, 0, title, 1, actions->name, 2, DDB_ACTION_CTX_NOWPLAYING, -1);
                    }
                }
                else {
//                    fprintf (stderr, "WARNING: action %s/%s from plugin %s is missing name and/or title\n", actions->name, actions->title, p->name);
                }
                actions = actions->next;
            }
        }
    }

    gtk_tree_view_set_model (GTK_TREE_VIEW (actions), GTK_TREE_MODEL (actions_store));

    if (act && ctx != -1) {
        actionbinding_t binding = {
            .name = act,
            .ctx = ctx,
            .treeview = actions
        };
        gtk_tree_model_foreach (GTK_TREE_MODEL (actions_store), set_current_action, (void*)&binding);
    }
Пример #5
0
int
main (int argc, char *argv[]) {
    int portable = 0;
#if STATICLINK
    int staticlink = 1;
#else
    int staticlink = 0;
#endif
#if PORTABLE
    portable = 1;
    if (!realpath (argv[0], dbinstalldir)) {
        strcpy (dbinstalldir, argv[0]);
    }
    char *e = strrchr (dbinstalldir, '/');
    if (e) {
        *e = 0;
    }
    else {
        fprintf (stderr, "couldn't determine install folder from path %s\n", argv[0]);
        exit (-1);
    }
#else
    if (!realpath (argv[0], dbinstalldir)) {
        strcpy (dbinstalldir, argv[0]);
    }
    char *e = strrchr (dbinstalldir, '/');
    if (e) {
        *e = 0;
        struct stat st;
        char checkpath[PATH_MAX];
        snprintf (checkpath, sizeof (checkpath), "%s/.ddb_portable", dbinstalldir);
        if (!stat (checkpath, &st)) {
            if (S_ISREG (st.st_mode)) {
                portable = 1;
            }
        }
    }
    if (!portable) {
        strcpy (dbinstalldir, PREFIX);
    }
#endif

#ifdef __GLIBC__
    signal (SIGSEGV, sigsegv_handler);
#endif
    setlocale (LC_ALL, "");
    setlocale (LC_NUMERIC, "C");
#ifdef ENABLE_NLS
//    fprintf (stderr, "enabling gettext support: package=" PACKAGE ", dir=" LOCALEDIR "...\n");
    if (portable) {
        char localedir[PATH_MAX];
        snprintf (localedir, sizeof (localedir), "%s/locale", dbinstalldir);
        bindtextdomain (PACKAGE, localedir);
    }
    else {
        bindtextdomain (PACKAGE, LOCALEDIR);
    }
	bind_textdomain_codeset (PACKAGE, "UTF-8");
	textdomain (PACKAGE);
#endif

    fprintf (stderr, "starting deadbeef " VERSION "%s%s\n", staticlink ? " [static]" : "", portable ? " [portable]" : "");
    srand (time (NULL));
#ifdef __linux__
    prctl (PR_SET_NAME, "deadbeef-main", 0, 0, 0, 0);
#endif

#if PORTABLE_FULL
    if (snprintf (confdir, sizeof (confdir), "%s/config", dbinstalldir) > sizeof (confdir)) {
        fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir);
        return -1;
    }

    strcpy (dbconfdir, confdir);
#else
    char *homedir = getenv ("HOME");
    if (!homedir) {
        fprintf (stderr, "unable to find home directory. stopping.\n");
        return -1;
    }

    char *xdg_conf_dir = getenv ("XDG_CONFIG_HOME");
    if (xdg_conf_dir) {
        if (snprintf (confdir, sizeof (confdir), "%s", xdg_conf_dir) > sizeof (confdir)) {
            fprintf (stderr, "fatal: XDG_CONFIG_HOME value is too long: %s\n", xdg_conf_dir);
            return -1;
        }
    }
    else {
        if (snprintf (confdir, sizeof (confdir), "%s/.config", homedir) > sizeof (confdir)) {
            fprintf (stderr, "fatal: HOME value is too long: %s\n", homedir);
            return -1;
        }
    }
    if (snprintf (dbconfdir, sizeof (dbconfdir), "%s/deadbeef", confdir) > sizeof (dbconfdir)) {
        fprintf (stderr, "fatal: out of memory while configuring\n");
        return -1;
    }
    mkdir (confdir, 0755);
#endif


    if (portable) {
        if (snprintf (dbdocdir, sizeof (dbdocdir), "%s/doc", dbinstalldir) > sizeof (dbdocdir)) {
            fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir);
            return -1;
        }
#ifdef HAVE_COCOAUI
        char respath[PATH_MAX];
        cocoautil_get_resources_path (respath, sizeof (respath));
        if (snprintf (dbplugindir, sizeof (dbplugindir), "%s", respath) > sizeof (dbplugindir)) {
            fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir);
            return -1;
        }
#else
        if (snprintf (dbplugindir, sizeof (dbplugindir), "%s/plugins", dbinstalldir) > sizeof (dbplugindir)) {
            fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir);
            return -1;
        }
#endif
        if (snprintf (dbpixmapdir, sizeof (dbpixmapdir), "%s/pixmaps", dbinstalldir) > sizeof (dbpixmapdir)) {
            fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir);
            return -1;
        }
        mkdir (dbplugindir, 0755);
    }
    else {
        if (snprintf (dbdocdir, sizeof (dbdocdir), "%s", DOCDIR) > sizeof (dbdocdir)) {
            fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir);
            return -1;
        }
        if (snprintf (dbplugindir, sizeof (dbplugindir), "%s/deadbeef", LIBDIR) > sizeof (dbplugindir)) {
            fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir);
            return -1;
        }
        if (snprintf (dbpixmapdir, sizeof (dbpixmapdir), "%s/share/deadbeef/pixmaps", PREFIX) > sizeof (dbpixmapdir)) {
            fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir);
            return -1;
        }
    }

    for (int i = 1; i < argc; i++) {
        // help, version and nowplaying are executed with any filter
        if (!strcmp (argv[i], "--help") || !strcmp (argv[i], "-h")) {
            print_help ();
            return 0;
        }
        else if (!strcmp (argv[i], "--version")) {
            fprintf (stderr, "DeaDBeeF " VERSION " Copyright © 2009-2013 Alexey Yakovenko\n");
            return 0;
        }
        else if (!strcmp (argv[i], "--gui")) {
            if (i == argc-1) {
                break;
            }
            i++;
            strncpy (use_gui_plugin, argv[i], sizeof(use_gui_plugin) - 1);
            use_gui_plugin[sizeof(use_gui_plugin) - 1] = 0;
        }
    }

    trace ("installdir: %s\n", dbinstalldir);
    trace ("confdir: %s\n", confdir);
    trace ("docdir: %s\n", dbdocdir);
    trace ("plugindir: %s\n", dbplugindir);
    trace ("pixmapdir: %s\n", dbpixmapdir);

    mkdir (dbconfdir, 0755);

    int size = 0;
    char *cmdline = prepare_command_line (argc, argv, &size);

    // try to connect to remote player
    int s, len;
    struct sockaddr_un remote;

    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    memset (&remote, 0, sizeof (remote));
    remote.sun_family = AF_UNIX;
#if USE_ABSTRACT_SOCKET_NAME
    memcpy (remote.sun_path, server_id, sizeof (server_id));
    len = offsetof(struct sockaddr_un, sun_path) + sizeof (server_id)-1;
#else
    char *socketdirenv = getenv ("DDB_SOCKET_DIR");
    snprintf (remote.sun_path, sizeof (remote.sun_path), "%s/socket", socketdirenv ? socketdirenv : dbconfdir);
    len = offsetof(struct sockaddr_un, sun_path) + strlen (remote.sun_path);
#endif
    if (connect(s, (struct sockaddr *)&remote, len) == 0) {
        // pass args to remote and exit
        if (send(s, cmdline, size, 0) == -1) {
            perror ("send");
            exit (-1);
        }
        // end of message
        shutdown(s, SHUT_WR);

        int sz = -1;
        char *out = read_entire_message(s, &sz);
        if (sz == -1) {
            fprintf (stderr, "failed to pass args to remote!\n");
            exit (-1);
        }
        else {
            // check if that's nowplaying response
            const char np[] = "nowplaying ";
            const char err[] = "error ";
            if (!strncmp (out, np, sizeof (np)-1)) {
                const char *prn = &out[sizeof (np)-1];
                fwrite (prn, 1, strlen (prn), stdout);
            }
            else if (!strncmp (out, err, sizeof (err)-1)) {
                const char *prn = &out[sizeof (err)-1];
                fwrite (prn, 1, strlen (prn), stderr);
            }
            else if (sz > 0 && out[0]) {
                fprintf (stderr, "%s\n", out);
            }
        }
        if (out) {
            free (out);
        }
        close (s);
        exit (0);
    }
//    else {
//        perror ("INFO: failed to connect to existing session:");
//    }
    close(s);

    // become a server
    if (server_start () < 0) {
        exit (-1);
    }

    // hack: report nowplaying
    if (!strcmp (cmdline, "--nowplaying")) {
        char nothing[] = "nothing";
        fwrite (nothing, 1, sizeof (nothing)-1, stdout);
        return 0;
    }

    pl_init ();
    conf_init ();
    conf_load (); // required by some plugins at startup

    if (use_gui_plugin[0]) {
        conf_set_str ("gui_plugin", use_gui_plugin);
    }

    conf_set_str ("deadbeef_version", VERSION);

    volume_set_db (conf_get_float ("playback.volume", 0)); // volume need to be initialized before plugins start

    messagepump_init (); // required to push messages while handling commandline
    if (plug_load_all ()) { // required to add files to playlist from commandline
        exit (-1);
    }
    pl_load_all ();
    plt_set_curr_idx (conf_get_int ("playlist.current", 0));

    // execute server commands in local context
    int noloadpl = 0;
    if (argc > 1) {
        int res = server_exec_command_line (cmdline, size, NULL, 0);
        // some of the server commands ran on 1st instance should terminate it
        if (res == 2) {
            noloadpl = 1;
        }
        else if (res > 0) {
            exit (0);
        }
        else if (res < 0) {
            exit (-1);
        }
    }

    free (cmdline);

#if 0
    signal (SIGTERM, sigterm_handler);
    atexit (atexit_handler); // helps to save in simple cases
#endif

    streamer_init ();

    plug_connect_all ();
    messagepump_push (DB_EV_PLUGINSLOADED, 0, 0, 0);

    if (!noloadpl) {
        restore_resume_state ();
    }

    server_tid = thread_start (server_loop, NULL);

    mainloop_tid = thread_start (mainloop_thread, NULL);

    DB_plugin_t *gui = plug_get_gui ();
    if (gui) {
        gui->start ();
    }

    fprintf (stderr, "gui plugin has quit; waiting for mainloop thread to finish\n");
    thread_join (mainloop_tid);

    // terminate server and wait for completion
    if (server_tid) {
        server_terminate = 1;
        thread_join (server_tid);
        server_tid = 0;
    }

    // save config
    pl_save_all ();
    conf_save ();

    // delete legacy session file
    {
        char sessfile[1024]; // $HOME/.config/deadbeef/session
        if (snprintf (sessfile, sizeof (sessfile), "%s/deadbeef/session", confdir) < sizeof (sessfile)) {
            unlink (sessfile);
        }
    }

    // stop receiving messages from outside
    server_close ();

    // plugins might still hold references to playitems,
    // and query configuration in background
    // so unload everything 1st before final cleanup
    plug_disconnect_all ();
    plug_unload_all ();

    // at this point we can simply do exit(0), but let's clean up for debugging
    pl_free (); // may access conf_*
    conf_free ();

    fprintf (stderr, "messagepump_free\n");
    messagepump_free ();
    fprintf (stderr, "plug_cleanup\n");
    plug_cleanup ();

    fprintf (stderr, "hej-hej!\n");

    return 0;
}