X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fmain.c;h=a5a803f04104163562c514ffa88e9ab7281e2d44;hb=2b171daac0ab6ac161514aeaa521fdd51e3f09c6;hp=6a6ba34f36bf4ff5c3b9ca8adfe3e8c414a10cfc;hpb=76a8fab4adc293982f355609b89dde88d5f355e0;p=ncmpc.git diff --git a/src/main.c b/src/main.c index 6a6ba34..a5a803f 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,6 @@ -/* - * $Id$ - * - * (c) 2004 by Kalle Wallin +/* ncmpc (Ncurses MPD Client) + * (c) 2004-2017 The Music Player Daemon Project + * Project homepage: http://musicpd.org * * 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 @@ -12,176 +11,352 @@ * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include -#include -#include -#include -#include -#include -#include - #include "config.h" #include "ncmpc.h" #include "mpdclient.h" -#include "support.h" +#include "callbacks.h" +#include "charset.h" #include "options.h" -#include "conf.h" #include "command.h" -#include "src_lyrics.h" +#include "ncu.h" #include "screen.h" -#include "screen_utils.h" +#include "screen_status.h" +#include "xterm_title.h" #include "strfsong.h" +#include "i18n.h" +#include "player_command.h" +#include "keyboard.h" +#include "lirc.h" +#include "signals.h" + +#ifndef NCMPC_MINI +#include "conf.h" +#endif + +#ifdef ENABLE_LYRICS_SCREEN +#include "lyrics.h" +#endif + +#include + +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALE +#include +#endif + +/* time between mpd updates [ms] */ +static const guint update_interval = 500; #define BUFSIZE 1024 -static mpdclient_t *mpd = NULL; -static gboolean connected = FALSE; -static GTimer *timer = NULL; +static struct mpdclient *mpd = NULL; +static GMainLoop *main_loop; +static guint reconnect_source_id, update_source_id; -static const gchar * -error_msg(const gchar *msg) +#ifndef NCMPC_MINI +static guint check_key_bindings_source_id; +#endif + +#ifndef NCMPC_MINI +static void +update_xterm_title(void) { - gchar *p; + const struct mpd_song *song = mpd->song; + + char tmp[BUFSIZE]; + const char *new_title = NULL; + if (options.xterm_title_format && mpd->playing && song) + new_title = strfsong(tmp, BUFSIZE, options.xterm_title_format, song) > 0 + ? tmp + : NULL; + + if (new_title == NULL) + new_title = PACKAGE " version " VERSION; + + static char title[BUFSIZE]; + if (strncmp(title, new_title, BUFSIZE)) { + g_strlcpy(title, new_title, BUFSIZE); + set_xterm_title(title); + } +} +#endif - if( (p=strchr(msg, '}' )) == NULL ) - return msg; - while( p && *p && (*p=='}' || *p==' ') ) - p++; +static gboolean +timer_mpd_update(gpointer data); - return p; +static void +enable_update_timer(void) +{ + if (update_source_id != 0) + return; + + update_source_id = g_timeout_add(update_interval, + timer_mpd_update, NULL); } static void -error_callback(mpdclient_t *c, gint error, const gchar *msg) +disable_update_timer(void) { - gint code = GET_ACK_ERROR_CODE(error); - - error = error & 0xFF; - D("Error [%d:%d]> \"%s\"\n", error, code, msg); - switch (error) { - case MPD_ERROR_CONNPORT: - case MPD_ERROR_NORESPONSE: - break; - case MPD_ERROR_ACK: - screen_status_printf("%s", error_msg(msg)); - screen_bell(); - break; - default: - screen_status_printf("%s", msg); - screen_bell(); - doupdate(); - connected = FALSE; - } + if (update_source_id == 0) + return; + + g_source_remove(update_source_id); + update_source_id = 0; } -static void -update_xterm_title(void) +static bool +should_enable_update_timer(void) { - static char title[BUFSIZE]; - char tmp[BUFSIZE]; - mpd_Status *status = NULL; - mpd_Song *song = NULL; - - if( mpd ) - { - status = mpd->status; - song = mpd->song; - } - - if(options.xterm_title_format && status && song && IS_PLAYING(status->state)) - { - strfsong(tmp, BUFSIZE, options.xterm_title_format, song); - } - else - g_strlcpy(tmp, PACKAGE " version " VERSION, BUFSIZE); - - if( strncmp(title,tmp,BUFSIZE) ) - { - g_strlcpy(title, tmp, BUFSIZE); - set_xterm_title("%s", title); - } + return mpd->playing; } static void -exit_and_cleanup(void) +auto_update_timer(void) { - screen_exit(); - set_xterm_title(""); - printf("\n"); - if( mpd ) - { - mpdclient_disconnect(mpd); - mpd = mpdclient_free(mpd); - } - g_free(options.host); - g_free(options.password); - g_free(options.list_format); - g_free(options.status_format); - g_free(options.scroll_sep); - if( timer ) - g_timer_destroy(timer); + if (should_enable_update_timer()) + enable_update_timer(); + else + disable_update_timer(); } static void -catch_sigint( int sig ) +do_mpd_update(void) +{ + if (mpdclient_is_connected(mpd) && + (mpd->events != 0 || mpd->playing)) + mpdclient_update(mpd); + +#ifndef NCMPC_MINI + if (options.enable_xterm_title) + update_xterm_title(); +#endif + + screen_update(mpd); + mpd->events = 0; +} + +static char * +settings_name(const struct mpd_settings *settings) { - printf("\n%s\n", _("Exiting...")); - exit(EXIT_SUCCESS); + const char *host = mpd_settings_get_host(settings); + if (host == NULL) + host = _("unknown"); + + if (host[0] == '/') + return g_strdup(host); + + unsigned port = mpd_settings_get_port(settings); + if (port == 0 || port == 6600) + return g_strdup(host); + + return g_strdup_printf("%s:%u", host, port); } +static char * +default_settings_name(void) +{ + struct mpd_settings *settings = + mpd_settings_new(options.host, options.port, 0, + NULL, options.password); + if (settings == NULL) + return g_strdup(_("unknown")); -static void -catch_sigcont( int sig ) + char *name = settings_name(settings); + mpd_settings_free(settings); + + return name; +} + +/** + * This timer is installed when the connection to the MPD server is + * broken. It tries to recover by reconnecting periodically. + */ +static gboolean +timer_reconnect(gcc_unused gpointer data) { - D("catch_sigcont()\n"); -#ifdef ENABLE_RAW_MODE - reset_prog_mode(); /* restore tty modes */ - refresh(); + assert(mpdclient_is_dead(mpd)); + + reconnect_source_id = 0; + + char *name = default_settings_name(); + screen_status_printf(_("Connecting to %s... [Press %s to abort]"), + name, get_key_names(CMD_QUIT, false)); + g_free(name); + doupdate(); + + mpdclient_connect(mpd); + + return FALSE; +} + +void +mpdclient_connected_callback(void) +{ + assert(reconnect_source_id == 0); + +#ifndef NCMPC_MINI + /* quit if mpd is pre 0.14 - song id not supported by mpd */ + struct mpd_connection *connection = mpdclient_get_connection(mpd); + if (mpd_connection_cmp_server_version(connection, 0, 16, 0) < 0) { + const unsigned *version = + mpd_connection_get_server_version(connection); + screen_status_printf(_("Error: MPD version %d.%d.%d is too old (%s needed)"), + version[0], version[1], version[2], + "0.16.0"); + mpdclient_disconnect(mpd); + doupdate(); + + /* try again after 30 seconds */ + reconnect_source_id = + g_timeout_add_seconds(30, timer_reconnect, NULL); + return; + } #endif - screen_resize(); + + screen_status_clear_message(); + doupdate(); + + /* update immediately */ + mpd->events = MPD_IDLE_ALL; + + do_mpd_update(); + + auto_update_timer(); } void -sigstop(void) +mpdclient_failed_callback(void) { - def_prog_mode(); /* save the tty modes */ - endwin(); /* end curses mode temporarily */ - kill(0, SIGSTOP); /* issue SIGSTOP */ + assert(reconnect_source_id == 0); + + /* try again in 5 seconds */ + reconnect_source_id = g_timeout_add_seconds(5, timer_reconnect, NULL); } -#ifndef NDEBUG -void -D(const char *format, ...) +void +mpdclient_lost_callback(void) { - if( options.debug ) - { - gchar *msg; - va_list ap; - - va_start(ap,format); - msg = g_strdup_vprintf(format,ap); - va_end(ap); - fprintf(stderr, "%s", msg); - g_free(msg); - } + assert(reconnect_source_id == 0); + + screen_update(mpd); + + reconnect_source_id = g_timeout_add_seconds(1, timer_reconnect, NULL); +} + +/** + * This function is called by the gidle.c library when MPD sends us an + * idle event (or when the connection dies). + */ +void +mpdclient_idle_callback(gcc_unused enum mpd_idle events) +{ +#ifndef NCMPC_MINI + if (options.enable_xterm_title) + update_xterm_title(); +#endif + + screen_update(mpd); + auto_update_timer(); +} + +static gboolean +timer_mpd_update(gcc_unused gpointer data) +{ + do_mpd_update(); + + if (should_enable_update_timer()) + return true; + else { + update_source_id = 0; + return false; + } +} + +void begin_input_event(void) +{ +} + +void end_input_event(void) +{ + screen_update(mpd); + mpd->events = 0; + + auto_update_timer(); +} + +bool +do_input_event(command_t cmd) +{ + if (cmd == CMD_QUIT) { + g_main_loop_quit(main_loop); + return false; + } + + screen_cmd(mpd, cmd); + + if (cmd == CMD_VOLUME_UP || cmd == CMD_VOLUME_DOWN) + /* make sure we don't update the volume yet */ + disable_update_timer(); + + return true; +} + +#ifndef NCMPC_MINI +/** + * Check the configured key bindings for errors, and display a status + * message every 10 seconds. + */ +static gboolean +timer_check_key_bindings(gcc_unused gpointer data) +{ + char buf[256]; + + if (check_key_bindings(NULL, buf, sizeof(buf))) { + /* no error: disable this timer for the rest of this + process */ + check_key_bindings_source_id = 0; + return FALSE; + } + +#ifdef ENABLE_KEYDEF_SCREEN + g_strchomp(buf); + g_strlcat(buf, " (", sizeof(buf)); + /* to translators: a key was bound twice in the key editor, + and this is a hint for the user what to press to correct + that */ + char comment[64]; + g_snprintf(comment, sizeof(comment), _("press %s for the key editor"), + get_key_names(CMD_SCREEN_KEYDEF, false)); + g_strlcat(buf, comment, sizeof(buf)); + g_strlcat(buf, ")", sizeof(buf)); +#endif + + screen_status_printf("%s", buf); + + doupdate(); + return TRUE; } #endif int main(int argc, const char *argv[]) { - options_t *options; - struct sigaction act; +#ifdef ENABLE_LOCALE +#ifndef ENABLE_NLS + gcc_unused +#endif const char *charset = NULL; - gboolean key_error; - -#ifdef HAVE_LOCALE_H /* time and date formatting */ setlocale(LC_TIME,""); /* care about sorting order etc */ @@ -189,171 +364,108 @@ main(int argc, const char *argv[]) /* charset */ setlocale(LC_CTYPE,""); /* initialize charset conversions */ - charset_init(g_get_charset(&charset)); - D("charset: %s\n", charset); -#endif + charset = charset_init(); /* initialize i18n support */ +#endif + #ifdef ENABLE_NLS setlocale(LC_MESSAGES, ""); bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR); +#ifdef ENABLE_LOCALE bind_textdomain_codeset(GETTEXT_PACKAGE, charset); +#endif textdomain(GETTEXT_PACKAGE); #endif /* initialize options */ - options = options_init(); + options_init(); /* parse command line options - 1 pass get configuration files */ options_parse(argc, argv); +#ifndef NCMPC_MINI /* read configuration */ - read_configuration(options); + read_configuration(); /* check key bindings */ - key_error = check_key_bindings(NULL, NULL, 0); + check_key_bindings(NULL, NULL, 0); +#endif /* parse command line options - 2 pass */ options_parse(argc, argv); - /* setup signal behavior - SIGINT */ - sigemptyset( &act.sa_mask ); - act.sa_flags = 0; - act.sa_handler = catch_sigint; - if( sigaction(SIGINT, &act, NULL)<0 ) { - perror("signal"); - exit(EXIT_FAILURE); - } - - /* setup signal behavior - SIGTERM */ - sigemptyset( &act.sa_mask ); - act.sa_flags = 0; - act.sa_handler = catch_sigint; - if( sigaction(SIGTERM, &act, NULL)<0 ) { - perror("sigaction()"); - exit(EXIT_FAILURE); - } + ncu_init(); - /* setup signal behavior - SIGCONT */ - sigemptyset( &act.sa_mask ); - act.sa_flags = 0; - act.sa_handler = catch_sigcont; - if( sigaction(SIGCONT, &act, NULL)<0 ) { - perror("sigaction(SIGCONT)"); - exit(EXIT_FAILURE); - } - - /* setup signal behaviour - SIGHUP*/ - sigemptyset( &act.sa_mask ); - act.sa_flags = 0; - act.sa_handler = catch_sigint; - if( sigaction(SIGHUP, &act, NULL)<0 ) { - perror("sigaction(SIGHUP)"); - exit(EXIT_FAILURE); - } +#ifdef ENABLE_LYRICS_SCREEN + lyrics_init(); +#endif - /* install exit function */ - atexit(exit_and_cleanup); - - ncurses_init(); - - src_lyr_init (); - - /* connect to our music player daemon */ - mpd = mpdclient_new(); - - if (mpdclient_connect(mpd, - options->host, - options->port, - 10.0, - options->password)) - exit(EXIT_FAILURE); - - /* if no password is used, but the mpd wants one, the connection - might be established but no status information is avaiable */ - mpdclient_update(mpd); - if (!mpd->status) - screen_auth(mpd); - - if (!mpd->status) - exit(EXIT_FAILURE); - - connected = TRUE; - D("Connected to MPD version %d.%d.%d\n", - mpd->connection->version[0], - mpd->connection->version[1], - mpd->connection->version[2]); - - /* quit if mpd is pre 0.11.0 - song id not supported by mpd */ - if( MPD_VERSION_LT(mpd, 0,11,0) ) { - fprintf(stderr, - _("Error: MPD version %d.%d.%d is to old (0.11.0 needed).\n"), - mpd->connection->version[0], - mpd->connection->version[1], - mpd->connection->version[2]); - exit(EXIT_FAILURE); - } + /* create mpdclient instance */ + mpd = mpdclient_new(options.host, options.port, + options.timeout_ms, + options.password); /* initialize curses */ screen_init(mpd); - /* install error callback function */ - mpdclient_install_error_callback(mpd, error_callback); - - /* initialize timer */ - timer = g_timer_new(); - - connected = TRUE; - while (connected || options->reconnect) { - static gdouble t = G_MAXDOUBLE; - - if( key_error ) { - char buf[BUFSIZE]; - - key_error=check_key_bindings(NULL, buf, BUFSIZE); - screen_status_printf("%s", buf); - } - - if( connected && (t>=MPD_UPDATE_TIME || mpd->need_update) ) { - mpdclient_update(mpd); - g_timer_start(timer); - } - - if( connected ) { - command_t cmd; - - screen_update(mpd); - if( (cmd=get_keyboard_command()) != CMD_NONE ) - { - screen_cmd(mpd, cmd); - if( cmd==CMD_VOLUME_UP || cmd==CMD_VOLUME_DOWN) - /* make shure we dont update the volume yet */ - g_timer_start(timer); - } - else - screen_idle(mpd); - } else if (options->reconnect) { - screen_status_printf(_("Connecting to %s... [Press %s to abort]"), - options->host, get_key_names(CMD_QUIT,0) ); - - if( get_keyboard_command_with_timeout(MPD_RECONNECT_TIME)==CMD_QUIT) - exit(EXIT_SUCCESS); - - if (mpdclient_connect(mpd, - options->host, options->port, - 1.5, - options->password) == 0) { - screen_status_printf(_("Connected to %s!"), options->host); - connected = TRUE; - } - - doupdate(); - } - - if (options->enable_xterm_title) - update_xterm_title(); - - t = g_timer_elapsed(timer, NULL); - } - exit(EXIT_FAILURE); -} + /* the main loop */ + main_loop = g_main_loop_new(NULL, FALSE); + + /* watch out for keyboard input */ + keyboard_init(); + + /* watch out for lirc input */ + ncmpc_lirc_init(); + + signals_init(main_loop, mpd); + + /* attempt to connect */ + reconnect_source_id = g_idle_add(timer_reconnect, NULL); + + auto_update_timer(); + +#ifndef NCMPC_MINI + check_key_bindings_source_id = + g_timeout_add_seconds(10, timer_check_key_bindings, NULL); +#endif + + screen_paint(mpd, true); + + g_main_loop_run(main_loop); + g_main_loop_unref(main_loop); + + /* cleanup */ + + cancel_seek_timer(); + + disable_update_timer(); + + if (reconnect_source_id != 0) + g_source_remove(reconnect_source_id); + +#ifndef NCMPC_MINI + if (check_key_bindings_source_id != 0) + g_source_remove(check_key_bindings_source_id); +#endif + + signals_deinit(); + ncmpc_lirc_deinit(); + + screen_exit(); +#ifndef NCMPC_MINI + set_xterm_title(""); +#endif + printf("\n"); + + mpdclient_free(mpd); + +#ifdef ENABLE_LYRICS_SCREEN + lyrics_deinit(); +#endif + + ncu_deinit(); + options_deinit(); + + return 0; +}