Code

a0ff344610af15b40d0407515004cae737eae60a
[ncmpc.git] / src / main.c
1 /* 
2  * $Id$
3  *
4  * (c) 2004 by Kalle Wallin <kaw@linux.se>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
21 #include "config.h"
22 #include "ncmpc.h"
23 #include "mpdclient.h"
24 #include "support.h"
25 #include "options.h"
26 #include "conf.h"
27 #include "command.h"
28 #include "lyrics.h"
29 #include "screen.h"
30 #include "screen_utils.h"
31 #include "strfsong.h"
32 #include "gcc.h"
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <signal.h>
37 #include <string.h>
39 #define BUFSIZE 1024
41 static mpdclient_t *mpd = NULL;
42 static gboolean connected = FALSE;
43 static GTimer *timer = NULL;
45 static const gchar *
46 error_msg(const gchar *msg)
47 {
48         gchar *p;
50         if ((p = strchr(msg, '}')) == NULL)
51                 return msg;
53         while (p && *p && (*p=='}' || *p==' '))
54                 p++;
56         return p;
57 }
59 static void
60 error_callback(mpd_unused mpdclient_t *c, gint error, const gchar *msg)
61 {
62         error = error & 0xFF;
63         D("Error [%d:%d]> \"%s\"\n", error, GET_ACK_ERROR_CODE(error), msg);
64         switch (error) {
65         case MPD_ERROR_CONNPORT:
66         case MPD_ERROR_NORESPONSE:
67                 break;
68         case MPD_ERROR_ACK:
69                 screen_status_printf("%s", error_msg(msg));
70                 screen_bell();
71                 break;
72         default:
73                 screen_status_printf("%s", msg);
74                 screen_bell();
75                 doupdate();
76                 connected = FALSE;
77         }
78 }
80 static void
81 update_xterm_title(void)
82 {
83         static char title[BUFSIZE];
84         char tmp[BUFSIZE];
85         mpd_Status *status = NULL;
86         mpd_Song *song = NULL;
88         if (mpd) {
89                 status = mpd->status;
90                 song = mpd->song;
91         }
93         if (options.xterm_title_format && status && song &&
94             IS_PLAYING(status->state))
95                 strfsong(tmp, BUFSIZE, options.xterm_title_format, song);
96         else
97                 g_strlcpy(tmp, PACKAGE " version " VERSION, BUFSIZE);
99         if (strncmp(title, tmp, BUFSIZE)) {
100                 g_strlcpy(title, tmp, BUFSIZE);
101                 set_xterm_title("%s", title);
102         }
105 static void
106 exit_and_cleanup(void)
108         screen_exit();
109         set_xterm_title("");
110         printf("\n");
112         if (mpd) {
113                 mpdclient_disconnect(mpd);
114                 mpdclient_free(mpd);
115         }
117         g_free(options.host);
118         g_free(options.password);
119         g_free(options.list_format);
120         g_free(options.status_format);
121         g_free(options.scroll_sep);
123         if (timer)
124                 g_timer_destroy(timer);
127 static void
128 catch_sigint(mpd_unused int sig)
130         printf("\n%s\n", _("Exiting..."));
131         exit(EXIT_SUCCESS);
135 static void
136 catch_sigcont(mpd_unused int sig)
138         D("catch_sigcont()\n");
139 #ifdef ENABLE_RAW_MODE
140         reset_prog_mode(); /* restore tty modes */
141         refresh();
142 #endif
143         screen_resize();
146 void
147 sigstop(void)
149   def_prog_mode();  /* save the tty modes */
150   endwin();         /* end curses mode temporarily */
151   kill(0, SIGSTOP); /* issue SIGSTOP */
154 #ifndef NDEBUG
155 void 
156 D(const char *format, ...)
158   if( options.debug )
159     {
160       gchar *msg;
161       va_list ap;
162   
163       va_start(ap,format);
164       msg = g_strdup_vprintf(format,ap);
165       va_end(ap);
166       fprintf(stderr, "%s", msg);
167       g_free(msg);
168     }
170 #endif
172 int
173 main(int argc, const char *argv[])
175         struct sigaction act;
176         const char *charset = NULL;
177         gboolean key_error;
179 #ifdef HAVE_LOCALE_H
180         /* time and date formatting */
181         setlocale(LC_TIME,"");
182         /* care about sorting order etc */
183         setlocale(LC_COLLATE,"");
184         /* charset */
185         setlocale(LC_CTYPE,"");
186         /* initialize charset conversions */
187         charset_init(g_get_charset(&charset));
188         D("charset: %s\n", charset);
189 #endif
191         /* initialize i18n support */
192 #ifdef ENABLE_NLS
193         setlocale(LC_MESSAGES, "");
194         bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
195         bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
196         textdomain(GETTEXT_PACKAGE);
197 #endif
199         /* initialize options */
200         options_init();
202         /* parse command line options - 1 pass get configuration files */
203         options_parse(argc, argv);
205         /* read configuration */
206         read_configuration(&options);
208         /* check key bindings */
209         key_error = check_key_bindings(NULL, NULL, 0);
211         /* parse command line options - 2 pass */
212         options_parse(argc, argv);
214         /* setup signal behavior - SIGINT */
215         sigemptyset(&act.sa_mask);
216         act.sa_flags = 0;
217         act.sa_handler = catch_sigint;
218         if (sigaction(SIGINT, &act, NULL) < 0) {
219                 perror("signal");
220                 exit(EXIT_FAILURE);
221         }
223         /* setup signal behavior - SIGTERM */
224         sigemptyset(&act.sa_mask);
225         act.sa_flags = 0;
226         act.sa_handler = catch_sigint;
227         if (sigaction(SIGTERM, &act, NULL) < 0) {
228                 perror("sigaction()");
229                 exit(EXIT_FAILURE);
230         }
232         /* setup signal behavior - SIGCONT */
233         sigemptyset(&act.sa_mask);
234         act.sa_flags = 0;
235         act.sa_handler = catch_sigcont;
236         if (sigaction(SIGCONT, &act, NULL) < 0) {
237                 perror("sigaction(SIGCONT)");
238                 exit(EXIT_FAILURE);
239         }
241         /* setup signal behaviour - SIGHUP*/
242         sigemptyset(&act.sa_mask);
243         act.sa_flags = 0;
244         act.sa_handler = catch_sigint;
245         if (sigaction(SIGHUP, &act, NULL) < 0) {
246                 perror("sigaction(SIGHUP)");
247                 exit(EXIT_FAILURE);
248         }
250         /* install exit function */
251         atexit(exit_and_cleanup);
253         ncurses_init();
255         lyrics_init();
257         /* connect to our music player daemon */
258         mpd = mpdclient_new();
260         if (mpdclient_connect(mpd,
261                               options.host,
262                               options.port,
263                               10.0,
264                               options.password))
265                 exit(EXIT_FAILURE);
267         /* if no password is used, but the mpd wants one, the connection
268            might be established but no status information is avaiable */
269         mpdclient_update(mpd);
270         if (!mpd->status)
271                 screen_auth(mpd);
273         if (!mpd->status)
274                 exit(EXIT_FAILURE);
276         connected = TRUE;
277         D("Connected to MPD version %d.%d.%d\n",
278           mpd->connection->version[0],
279           mpd->connection->version[1],
280           mpd->connection->version[2]);
282         /* quit if mpd is pre 0.11.0 - song id not supported by mpd */
283         if( MPD_VERSION_LT(mpd, 0,11,0) ) {
284                 fprintf(stderr,
285                         _("Error: MPD version %d.%d.%d is to old (0.11.0 needed).\n"),
286                         mpd->connection->version[0],
287                         mpd->connection->version[1],
288                         mpd->connection->version[2]);
289                 exit(EXIT_FAILURE);
290         }
292         /* initialize curses */
293         screen_init(mpd);
294         /* install error callback function */
295         mpdclient_install_error_callback(mpd, error_callback);
297         /* initialize timer */
298         timer = g_timer_new();
300         connected = TRUE;
301         while (connected || options.reconnect) {
302                 static gdouble t = G_MAXDOUBLE;
304                 if (key_error) {
305                         char buf[BUFSIZE];
307                         key_error=check_key_bindings(NULL, buf, BUFSIZE);
308                         screen_status_printf("%s", buf);
309                 }
311                 if (connected && (t >= MPD_UPDATE_TIME || mpd->need_update)) {
312                         mpdclient_update(mpd);
313                         g_timer_start(timer);
314                 }
316                 if (connected) {
317                         command_t cmd;
319                         screen_update(mpd);
320                         if ((cmd=get_keyboard_command()) != CMD_NONE) {
321                                 screen_cmd(mpd, cmd);
322                                 if (cmd == CMD_VOLUME_UP || cmd == CMD_VOLUME_DOWN)
323                                         /* make shure we dont update the volume yet */
324                                         g_timer_start(timer);
325                         } else
326                                 screen_idle(mpd);
327                 } else if (options.reconnect) {
328                         screen_status_printf(_("Connecting to %s...  [Press %s to abort]"),
329                                              options.host, get_key_names(CMD_QUIT,0) );
331                         if (get_keyboard_command_with_timeout(MPD_RECONNECT_TIME) == CMD_QUIT)
332                                 exit(EXIT_SUCCESS);
334                         if (mpdclient_connect(mpd,
335                                               options.host, options.port,
336                                               1.5,
337                                               options.password) == 0) {
338                                 screen_status_printf(_("Connected to %s!"), options.host);
339                                 connected = TRUE;
340                         }
342                         doupdate();
343                 }
345                 if (options.enable_xterm_title)
346                         update_xterm_title();
348                 t = g_timer_elapsed(timer, NULL);
349         }
350         exit(EXIT_FAILURE);