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