Code

7849f8c1305d6ec98832c1defc5e075c2a283d55
[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 "src_lyrics.h"
37 #include "screen.h"
38 #include "screen_utils.h"
39 #include "strfsong.h"
40 #include "splash.h"
42 #define BUFSIZE 1024
44 static mpdclient_t   *mpd = NULL;
45 static gboolean connected = FALSE;
46 static GTimer      *timer = NULL;
48 static gchar *
49 error_msg(gchar *msg)
50 {
51   gchar *p;
53   if( (p=strchr(msg, '}' )) == NULL )
54     return msg;
55   while( p && *p && (*p=='}' || *p==' ') )
56     p++;
58   return p;
59 }
61 static void
62 error_callback(mpdclient_t *c, gint error, gchar *msg)
63 {
64   gint code = GET_ACK_ERROR_CODE(error);
66   error = error & 0xFF;
67   D("Error [%d:%d]> \"%s\"\n", error, code, msg);
68   switch(error)
69     {
70     case MPD_ERROR_CONNPORT:
71     case MPD_ERROR_NORESPONSE:
72       break;
73     case MPD_ERROR_ACK:
74       screen_status_printf("%s", error_msg(msg));
75       screen_bell();
76       break;
77     default:
78       screen_status_printf("%s", msg);
79       screen_bell();
80       doupdate();
81       connected = FALSE;
82     }
83 }
85 static void
86 update_xterm_title(void)
87 {
88   static char title[BUFSIZE];
89   char tmp[BUFSIZE];
90   mpd_Status *status = NULL;
91   mpd_Song *song = NULL;
93   if( mpd )
94     {
95       status = mpd->status;
96       song = mpd->song;
97     }
99   if(options.xterm_title_format && status && song && IS_PLAYING(status->state))
100     {
101       strfsong(tmp, BUFSIZE, options.xterm_title_format, song);
102     }
103   else
104     g_strlcpy(tmp, PACKAGE " version " VERSION, BUFSIZE);
106   if( strncmp(title,tmp,BUFSIZE) )
107     {
108       g_strlcpy(title, tmp, BUFSIZE);
109       set_xterm_title("%s", title);
110     }
113 void
114 exit_and_cleanup(void)
116   screen_exit();
117   set_xterm_title("");
118   printf("\n");
119   if( mpd )
120     {
121       mpdclient_disconnect(mpd);
122       mpd = mpdclient_free(mpd);
123     }
124   g_free(options.host);
125   g_free(options.password);
126   g_free(options.list_format);
127   g_free(options.status_format);
128   if( timer )
129     g_timer_destroy(timer);
132 void
133 catch_sigint( int sig )
135   printf("\n%s\n", _("Exiting..."));
136   exit(EXIT_SUCCESS);
139 void
140 catch_sighup( int sig)
142   printf("\n%s\n", _("Exiting..."));
143   exit(EXIT_SUCCESS);
146 void
147 catch_sigcont( int sig )
149   D("catch_sigcont()\n");
150 #ifdef ENABLE_RAW_MODE
151   reset_prog_mode(); /* restore tty modes */
152   refresh();
153 #endif
154   screen_resize(); 
157 void
158 sigstop(void)
160   def_prog_mode();  /* save the tty modes */
161   endwin();         /* end curses mode temporarily */
162   kill(0, SIGSTOP); /* issue SIGSTOP */
165 #ifdef DEBUG
166 void 
167 D(char *format, ...)
169   if( options.debug )
170     {
171       gchar *msg;
172       va_list ap;
173   
174       va_start(ap,format);
175       msg = g_strdup_vprintf(format,ap);
176       va_end(ap);
177       fprintf(stderr, "%s", msg);
178       g_free(msg);
179     }
181 #endif
183 int 
184 main(int argc, const char *argv[])
186   options_t *options;
187   struct sigaction act;
188   const char *charset = NULL;
189   gboolean key_error;
191 #ifdef HAVE_LOCALE_H
192   /* time and date formatting */
193   setlocale(LC_TIME,"");
194   /* care about sorting order etc */
195   setlocale(LC_COLLATE,"");
196   /* charset */
197   setlocale(LC_CTYPE,"");
198   /* initialize charset conversions */
199   charset_init(g_get_charset(&charset));
200   D("charset: %s\n", charset);
201 #endif
203   /* initialize i18n support */
204 #ifdef ENABLE_NLS
205   setlocale(LC_MESSAGES, "");
206   bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
207   bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
208   textdomain(GETTEXT_PACKAGE);
209 #endif
211   /* initialize options */
212   options = options_init();
214   /* parse command line options - 1 pass get configuration files */
215   options_parse(argc, argv);
216   
217   /* read configuration */
218   read_configuration(options);
220   /* check key bindings */
221   key_error = check_key_bindings(NULL, NULL, 0);
222   
223   /* parse command line options - 2 pass */
224   options_parse(argc, argv);
226   /* setup signal behavior - SIGINT */
227   sigemptyset( &act.sa_mask );
228   act.sa_flags    = 0;
229   act.sa_handler = catch_sigint;
230   if( sigaction(SIGINT, &act, NULL)<0 )
231     {
232       perror("signal");
233       exit(EXIT_FAILURE);
234     }
235   /* setup signal behavior - SIGTERM */
236   sigemptyset( &act.sa_mask );
237   act.sa_flags    = 0;
238   act.sa_handler = catch_sigint;
239   if( sigaction(SIGTERM, &act, NULL)<0 )
240     {
241       perror("sigaction()");
242       exit(EXIT_FAILURE);
243     }
244   /* setup signal behavior - SIGCONT */
245   sigemptyset( &act.sa_mask );
246   act.sa_flags    = 0;
247   act.sa_handler = catch_sigcont;
248   if( sigaction(SIGCONT, &act, NULL)<0 )
249     {
250       perror("sigaction(SIGCONT)");
251       exit(EXIT_FAILURE);
252     }
253  
254   /* setup signal behaviour - SIGHUP*/ 
255   sigemptyset( &act.sa_mask );
256   act.sa_flags    = 0;
257   act.sa_handler = catch_sigint;
258   if( sigaction(SIGHUP, &act, NULL)<0 )
259         {
260         perror("sigaction(SIGHUP)");
261         exit(EXIT_FAILURE);
262         }
263         
264   /* install exit function */
265   atexit(exit_and_cleanup);
266   
267   ncurses_init();
268   if(options->show_splash == TRUE) draw_splash();
270   src_lyr_init ();
272   /* connect to our music player daemon */
273   mpd = mpdclient_new();
274  
275   if( mpdclient_connect(mpd, 
276                         options->host, 
277                         options->port, 
278                         10.0, 
279                         options->password) )
280     {
281       exit(EXIT_FAILURE);
282     }
284   /* if no password is used, but the mpd wants one, the connection
285      might be established but no status information is avaiable */
286   mpdclient_update(mpd);
287   if(!mpd->status)
288     {
289       screen_auth(mpd);
290     }
291   if(!mpd->status) exit(EXIT_FAILURE);
292   
293   connected = TRUE;
294   D("Connected to MPD version %d.%d.%d\n",
295     mpd->connection->version[0],
296     mpd->connection->version[1],
297     mpd->connection->version[2]);
299   /* quit if mpd is pre 0.11.0 - song id not supported by mpd */
300   if( MPD_VERSION_LT(mpd, 0,11,0) )
301     {
302       fprintf(stderr,
303               _("Error: MPD version %d.%d.%d is to old (0.11.0 needed).\n"),
304               mpd->connection->version[0],
305               mpd->connection->version[1],
306               mpd->connection->version[2]);
307       exit(EXIT_FAILURE);
308     }
310   /* initialize curses */
311   screen_init(mpd);
312   /* install error callback function */
313   mpdclient_install_error_callback(mpd, error_callback);
315   /* initialize timer */
316   timer = g_timer_new();
318   connected = TRUE;
319   while( connected || options->reconnect )
320     {
321       static gdouble t = G_MAXDOUBLE;
323       if( key_error )
324         {
325           char buf[BUFSIZE];
327           key_error=check_key_bindings(NULL, buf, BUFSIZE);
328           screen_status_printf("%s", buf);
329         }
331       if( connected && (t>=MPD_UPDATE_TIME || mpd->need_update) )
332         {
333           mpdclient_update(mpd);
334           g_timer_start(timer);
335         }
337       if( connected )
338         {
339           command_t cmd;
341           screen_update(mpd);
342           if( (cmd=get_keyboard_command()) != CMD_NONE )
343             {
344               screen_cmd(mpd, cmd);
345               if( cmd==CMD_VOLUME_UP || cmd==CMD_VOLUME_DOWN)
346                 /* make shure we dont update the volume yet */
347                 g_timer_start(timer);
348             }
349           else
350             screen_idle(mpd);
351         }
352       else if( options->reconnect )
353         {
354           screen_status_printf(_("Connecting to %s...  [Press %s to abort]"), 
355                                options->host, get_key_names(CMD_QUIT,0) );
357           if( get_keyboard_command_with_timeout(MPD_RECONNECT_TIME)==CMD_QUIT)
358             exit(EXIT_SUCCESS);
359           
360           if( mpdclient_connect(mpd,
361                                 options->host,
362                                 options->port, 
363                                 1.5,
364                                 options->password) == 0 )
365             {
366               screen_status_printf(_("Connected to %s!"), options->host);
367               connected = TRUE;
368             }     
369           doupdate();
370         }
371       if( options->enable_xterm_title )
372         update_xterm_title();
373       t = g_timer_elapsed(timer, NULL);
374     }
375   exit(EXIT_FAILURE);