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 }
111 }
113 void
114 exit_and_cleanup(void)
115 {
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 g_free(options.scroll_sep);
129 if( timer )
130 g_timer_destroy(timer);
131 }
133 void
134 catch_sigint( int sig )
135 {
136 printf("\n%s\n", _("Exiting..."));
137 exit(EXIT_SUCCESS);
138 }
140 void
141 catch_sighup( int sig)
142 {
143 printf("\n%s\n", _("Exiting..."));
144 exit(EXIT_SUCCESS);
145 }
147 void
148 catch_sigcont( int sig )
149 {
150 D("catch_sigcont()\n");
151 #ifdef ENABLE_RAW_MODE
152 reset_prog_mode(); /* restore tty modes */
153 refresh();
154 #endif
155 screen_resize();
156 }
158 void
159 sigstop(void)
160 {
161 def_prog_mode(); /* save the tty modes */
162 endwin(); /* end curses mode temporarily */
163 kill(0, SIGSTOP); /* issue SIGSTOP */
164 }
166 #ifdef DEBUG
167 void
168 D(char *format, ...)
169 {
170 if( options.debug )
171 {
172 gchar *msg;
173 va_list ap;
175 va_start(ap,format);
176 msg = g_strdup_vprintf(format,ap);
177 va_end(ap);
178 fprintf(stderr, "%s", msg);
179 g_free(msg);
180 }
181 }
182 #endif
184 int
185 main(int argc, const char *argv[])
186 {
187 options_t *options;
188 struct sigaction act;
189 const char *charset = NULL;
190 gboolean key_error;
192 #ifdef HAVE_LOCALE_H
193 /* time and date formatting */
194 setlocale(LC_TIME,"");
195 /* care about sorting order etc */
196 setlocale(LC_COLLATE,"");
197 /* charset */
198 setlocale(LC_CTYPE,"");
199 /* initialize charset conversions */
200 charset_init(g_get_charset(&charset));
201 D("charset: %s\n", charset);
202 #endif
204 /* initialize i18n support */
205 #ifdef ENABLE_NLS
206 setlocale(LC_MESSAGES, "");
207 bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
208 bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
209 textdomain(GETTEXT_PACKAGE);
210 #endif
212 /* initialize options */
213 options = options_init();
215 /* parse command line options - 1 pass get configuration files */
216 options_parse(argc, argv);
218 /* read configuration */
219 read_configuration(options);
221 /* check key bindings */
222 key_error = check_key_bindings(NULL, NULL, 0);
224 /* parse command line options - 2 pass */
225 options_parse(argc, argv);
227 /* setup signal behavior - SIGINT */
228 sigemptyset( &act.sa_mask );
229 act.sa_flags = 0;
230 act.sa_handler = catch_sigint;
231 if( sigaction(SIGINT, &act, NULL)<0 )
232 {
233 perror("signal");
234 exit(EXIT_FAILURE);
235 }
236 /* setup signal behavior - SIGTERM */
237 sigemptyset( &act.sa_mask );
238 act.sa_flags = 0;
239 act.sa_handler = catch_sigint;
240 if( sigaction(SIGTERM, &act, NULL)<0 )
241 {
242 perror("sigaction()");
243 exit(EXIT_FAILURE);
244 }
245 /* setup signal behavior - SIGCONT */
246 sigemptyset( &act.sa_mask );
247 act.sa_flags = 0;
248 act.sa_handler = catch_sigcont;
249 if( sigaction(SIGCONT, &act, NULL)<0 )
250 {
251 perror("sigaction(SIGCONT)");
252 exit(EXIT_FAILURE);
253 }
255 /* setup signal behaviour - SIGHUP*/
256 sigemptyset( &act.sa_mask );
257 act.sa_flags = 0;
258 act.sa_handler = catch_sigint;
259 if( sigaction(SIGHUP, &act, NULL)<0 )
260 {
261 perror("sigaction(SIGHUP)");
262 exit(EXIT_FAILURE);
263 }
265 /* install exit function */
266 atexit(exit_and_cleanup);
268 ncurses_init();
269 if(options->show_splash == TRUE) draw_splash();
271 src_lyr_init ();
273 /* connect to our music player daemon */
274 mpd = mpdclient_new();
276 if( mpdclient_connect(mpd,
277 options->host,
278 options->port,
279 10.0,
280 options->password) )
281 {
282 exit(EXIT_FAILURE);
283 }
285 /* if no password is used, but the mpd wants one, the connection
286 might be established but no status information is avaiable */
287 mpdclient_update(mpd);
288 if(!mpd->status)
289 {
290 screen_auth(mpd);
291 }
292 if(!mpd->status) exit(EXIT_FAILURE);
294 connected = TRUE;
295 D("Connected to MPD version %d.%d.%d\n",
296 mpd->connection->version[0],
297 mpd->connection->version[1],
298 mpd->connection->version[2]);
300 /* quit if mpd is pre 0.11.0 - song id not supported by mpd */
301 if( MPD_VERSION_LT(mpd, 0,11,0) )
302 {
303 fprintf(stderr,
304 _("Error: MPD version %d.%d.%d is to old (0.11.0 needed).\n"),
305 mpd->connection->version[0],
306 mpd->connection->version[1],
307 mpd->connection->version[2]);
308 exit(EXIT_FAILURE);
309 }
311 /* initialize curses */
312 screen_init(mpd);
313 /* install error callback function */
314 mpdclient_install_error_callback(mpd, error_callback);
316 /* initialize timer */
317 timer = g_timer_new();
319 connected = TRUE;
320 while( connected || options->reconnect )
321 {
322 static gdouble t = G_MAXDOUBLE;
324 if( key_error )
325 {
326 char buf[BUFSIZE];
328 key_error=check_key_bindings(NULL, buf, BUFSIZE);
329 screen_status_printf("%s", buf);
330 }
332 if( connected && (t>=MPD_UPDATE_TIME || mpd->need_update) )
333 {
334 mpdclient_update(mpd);
335 g_timer_start(timer);
336 }
338 if( connected )
339 {
340 command_t cmd;
342 screen_update(mpd);
343 if( (cmd=get_keyboard_command()) != CMD_NONE )
344 {
345 screen_cmd(mpd, cmd);
346 if( cmd==CMD_VOLUME_UP || cmd==CMD_VOLUME_DOWN)
347 /* make shure we dont update the volume yet */
348 g_timer_start(timer);
349 }
350 else
351 screen_idle(mpd);
352 }
353 else if( options->reconnect )
354 {
355 screen_status_printf(_("Connecting to %s... [Press %s to abort]"),
356 options->host, get_key_names(CMD_QUIT,0) );
358 if( get_keyboard_command_with_timeout(MPD_RECONNECT_TIME)==CMD_QUIT)
359 exit(EXIT_SUCCESS);
361 if( mpdclient_connect(mpd,
362 options->host,
363 options->port,
364 1.5,
365 options->password) == 0 )
366 {
367 screen_status_printf(_("Connected to %s!"), options->host);
368 connected = TRUE;
369 }
370 doupdate();
371 }
372 if( options->enable_xterm_title )
373 update_xterm_title();
374 t = g_timer_elapsed(timer, NULL);
375 }
376 exit(EXIT_FAILURE);
377 }