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