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 }
110 }
112 void
113 exit_and_cleanup(void)
114 {
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);
129 }
131 void
132 catch_sigint( int sig )
133 {
134 printf("\n%s\n", _("Exiting..."));
135 exit(EXIT_SUCCESS);
136 }
138 void
139 catch_sighup( int sig)
140 {
141 printf("\n%s\n", _("Exiting..."));
142 exit(EXIT_SUCCESS);
143 }
145 void
146 catch_sigcont( int sig )
147 {
148 D("catch_sigcont()\n");
149 #ifdef ENABLE_RAW_MODE
150 reset_prog_mode(); /* restore tty modes */
151 refresh();
152 #endif
153 screen_resize();
154 }
156 void
157 sigstop(void)
158 {
159 def_prog_mode(); /* save the tty modes */
160 endwin(); /* end curses mode temporarily */
161 kill(0, SIGSTOP); /* issue SIGSTOP */
162 }
164 #ifdef DEBUG
165 void
166 D(char *format, ...)
167 {
168 if( options.debug )
169 {
170 gchar *msg;
171 va_list ap;
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 }
179 }
180 #endif
182 int
183 main(int argc, const char *argv[])
184 {
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);
216 /* read configuration */
217 read_configuration(options);
219 /* check key bindings */
220 key_error = check_key_bindings(NULL, NULL, 0);
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 }
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 }
263 /* install exit function */
264 atexit(exit_and_cleanup);
266 ncurses_init();
267 if(options->show_splash == TRUE) draw_splash();
269 /* connect to our music player daemon */
270 mpd = mpdclient_new();
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);
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);
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);
373 }