Code

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