Code

the updated sources
[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 <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <ncurses.h>
27 #include <glib.h>
29 #include "config.h"
30 #include "ncmpc.h"
31 #include "mpdclient.h"
32 #include "support.h"
33 #include "options.h"
34 #include "conf.h"
35 #include "command.h"
36 #include "screen.h"
37 #include "screen_utils.h"
38 #include "strfsong.h"
40 #define BUFSIZE 1024
42 static mpdclient_t   *mpd = NULL;
43 static gboolean connected = FALSE;
44 static GTimer      *timer = NULL;
46 static gchar *
47 error_msg(gchar *msg)
48 {
49   gchar *p;
51   if( (p=strchr(msg, '}' )) == NULL )
52     return msg;
53   while( p && *p && (*p=='}' || *p==' ') )
54     p++;
56   return p;
57 }
59 static void
60 error_callback(mpdclient_t *c, gint error, gchar *msg)
61 {
62   gint code = GET_ACK_ERROR_CODE(error);
64   error = error & 0xFF;
65   D("Error [%d:%d]> \"%s\"\n", error, code, msg);
66   switch(error)
67     {
68     case MPD_ERROR_CONNPORT:
69     case MPD_ERROR_NORESPONSE:
70       break;
71     case MPD_ERROR_ACK:
72       screen_status_printf("%s", error_msg(msg));
73       screen_bell();
74       break;
75     default:
76       screen_status_printf("%s", msg);
77       screen_bell();
78       doupdate();
79       connected = FALSE;
80     }
81 }
83 static void
84 update_xterm_title(void)
85 {
86   static char title[BUFSIZE];
87   char tmp[BUFSIZE];
88   mpd_Status *status = NULL;
89   mpd_Song *song = NULL;
91   if( mpd )
92     {
93       status = mpd->status;
94       song = mpd->song;
95     }
97   if(options.xterm_title_format && status && song && IS_PLAYING(status->state))
98     {
99       strfsong(tmp, BUFSIZE, options.xterm_title_format, song);
100     }
101   else
102     g_strlcpy(tmp, PACKAGE " version " VERSION, BUFSIZE);
104   if( strncmp(title,tmp,BUFSIZE) )
105     {
106       g_strlcpy(title, tmp, BUFSIZE);
107       set_xterm_title("%s", title);
108     }
111 void
112 exit_and_cleanup(void)
114   screen_exit();
115   set_xterm_title("");
116   printf("\n");
117   if( mpd )
118     {
119       mpdclient_disconnect(mpd);
120       mpd = mpdclient_free(mpd);
121     }
122   g_free(options.host);
123   g_free(options.password);
124   g_free(options.list_format);
125   g_free(options.status_format);
126   if( timer )
127     g_timer_destroy(timer);
130 void
131 catch_sigint( int sig )
133   printf("\n%s\n", _("Exiting..."));
134   exit(EXIT_SUCCESS);
137 void
138 catch_sighup( int sig)
140   printf("\n%s\n", _("Exiting..."));
141   exit(EXIT_SUCCESS);
144 void
145 catch_sigcont( int sig )
147   D("catch_sigcont()\n");
148 #ifdef ENABLE_RAW_MODE
149   reset_prog_mode(); /* restore tty modes */
150   refresh();
151 #endif
152   screen_resize(); 
155 void
156 sigstop(void)
158   def_prog_mode();  /* save the tty modes */
159   endwin();         /* end curses mode temporarily */
160   kill(0, SIGSTOP); /* issue SIGSTOP */
163 #ifdef DEBUG
164 void 
165 D(char *format, ...)
167   if( options.debug )
168     {
169       gchar *msg;
170       va_list ap;
171   
172       va_start(ap,format);
173       msg = g_strdup_vprintf(format,ap);
174       va_end(ap);
175       fprintf(stderr, "%s", msg);
176       g_free(msg);
177     }
179 #endif
181 int 
182 main(int argc, const char *argv[])
184   options_t *options;
185   struct sigaction act;
186   const char *charset = NULL;
187   gboolean key_error;
189 #ifdef HAVE_LOCALE_H
190   /* time and date formatting */
191   setlocale(LC_TIME,"");
192   /* charset */
193   setlocale(LC_CTYPE,"");
194   /* initialize charset conversions */
195   charset_init(g_get_charset(&charset));
196   D("charset: %s\n", charset);
197 #endif
199   /* initialize i18n support */
200 #ifdef ENABLE_NLS
201   setlocale(LC_MESSAGES, "");
202   bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
203   bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
204   textdomain(GETTEXT_PACKAGE);
205 #endif
207   /* initialize options */
208   options = options_init();
210   /* parse command line options - 1 pass get configuration files */
211   options_parse(argc, argv);
212   
213   /* read configuration */
214   read_configuration(options);
216   /* check key bindings */
217   key_error = check_key_bindings(NULL, NULL, 0);
218   
219   /* parse command line options - 2 pass */
220   options_parse(argc, argv);
222   /* setup signal behavior - SIGINT */
223   sigemptyset( &act.sa_mask );
224   act.sa_flags    = 0;
225   act.sa_handler = catch_sigint;
226   if( sigaction(SIGINT, &act, NULL)<0 )
227     {
228       perror("signal");
229       exit(EXIT_FAILURE);
230     }
231   /* setup signal behavior - SIGTERM */
232   sigemptyset( &act.sa_mask );
233   act.sa_flags    = 0;
234   act.sa_handler = catch_sigint;
235   if( sigaction(SIGTERM, &act, NULL)<0 )
236     {
237       perror("sigaction()");
238       exit(EXIT_FAILURE);
239     }
240   /* setup signal behavior - SIGCONT */
241   sigemptyset( &act.sa_mask );
242   act.sa_flags    = 0;
243   act.sa_handler = catch_sigcont;
244   if( sigaction(SIGCONT, &act, NULL)<0 )
245     {
246       perror("sigaction(SIGCONT)");
247       exit(EXIT_FAILURE);
248     }
249  
250   /* setup signal behaviour - SIGHUP*/ 
251   sigemptyset( &act.sa_mask );
252   act.sa_flags    = 0;
253   act.sa_handler = catch_sigint;
254   if( sigaction(SIGHUP, &act, NULL)<0 )
255         {
256         perror("sigaction(SIGHUP)");
257         exit(EXIT_FAILURE);
258         }
259         
260   /* install exit function */
261   atexit(exit_and_cleanup);
263   /* connect to our music player daemon */
264   mpd = mpdclient_new();
265   if( mpdclient_connect(mpd, 
266                         options->host, 
267                         options->port, 
268                         10.0, 
269                         options->password) )
270     {
271       exit(EXIT_FAILURE);
272     }
274   /* if no password is used, but the mpd wants one, the connection
275      might be established but no status information is avaiable */
276   mpdclient_update(mpd);
277   if(!mpd->status)  
278     exit(EXIT_FAILURE);
279   
280   connected = TRUE;
281   D("Connected to MPD version %d.%d.%d\n",
282     mpd->connection->version[0],
283     mpd->connection->version[1],
284     mpd->connection->version[2]);
286   /* quit if mpd is pre 0.11.0 - song id not supported by mpd */
287   if( MPD_VERSION_LT(mpd, 0,11,0) )
288     {
289       fprintf(stderr,
290               _("Error: MPD version %d.%d.%d is to old (0.11.0 needed).\n"),
291               mpd->connection->version[0],
292               mpd->connection->version[1],
293               mpd->connection->version[2]);
294       exit(EXIT_FAILURE);
295     }
297   /* initialize curses */
298   screen_init(mpd);
300   /* install error callback function */
301   mpdclient_install_error_callback(mpd, error_callback);
303   /* initialize timer */
304   timer = g_timer_new();
306   connected = TRUE;
307   while( connected || options->reconnect )
308     {
309       static gdouble t = G_MAXDOUBLE;
311       if( key_error )
312         {
313           char buf[BUFSIZE];
315           key_error=check_key_bindings(NULL, buf, BUFSIZE);
316           screen_status_printf("%s", buf);
317         }
319       if( connected && (t>=MPD_UPDATE_TIME || mpd->need_update) )
320         {
321           mpdclient_update(mpd);
322           g_timer_start(timer);
323         }
325       if( connected )
326         {
327           command_t cmd;
329           screen_update(mpd);
330           if( (cmd=get_keyboard_command()) != CMD_NONE )
331             {
332               screen_cmd(mpd, cmd);
333               if( cmd==CMD_VOLUME_UP || cmd==CMD_VOLUME_DOWN)
334                 /* make shure we dont update the volume yet */
335                 g_timer_start(timer);
336             }
337           else
338             screen_idle(mpd);
339         }
340       else if( options->reconnect )
341         {
342           screen_status_printf(_("Connecting to %s...  [Press %s to abort]"), 
343                                options->host, get_key_names(CMD_QUIT,0) );
345           if( get_keyboard_command_with_timeout(MPD_RECONNECT_TIME)==CMD_QUIT)
346             exit(EXIT_SUCCESS);
347           
348           if( mpdclient_connect(mpd,
349                                 options->host,
350                                 options->port, 
351                                 1.5,
352                                 options->password) == 0 )
353             {
354               screen_status_printf(_("Connected to %s!"), options->host);
355               connected = TRUE;
356             }     
357           doupdate();
358         }
359       if( options->enable_xterm_title )
360         update_xterm_title();
361       t = g_timer_elapsed(timer, NULL);
362     }
363   exit(EXIT_FAILURE);