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; }
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; }
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; }
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); }
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; }