Code

removed the debugging function D()
[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 "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 "ncu.h"
29 #include "screen.h"
30 #include "screen_utils.h"
31 #include "strfsong.h"
32 #include "gcc.h"
34 #ifdef ENABLE_LYRICS_SCREEN
35 #include "lyrics.h"
36 #endif
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <signal.h>
41 #include <string.h>
43 #define BUFSIZE 1024
45 static const guint idle_interval = 500;
47 static mpdclient_t *mpd = NULL;
48 static gboolean connected = FALSE;
49 static GMainLoop *main_loop;
50 static guint reconnect_source_id, idle_source_id, update_source_id;
52 static const gchar *
53 error_msg(const gchar *msg)
54 {
55         gchar *p;
57         if ((p = strchr(msg, '}')) == NULL)
58                 return msg;
60         while (p && *p && (*p=='}' || *p==' '))
61                 p++;
63         return p;
64 }
66 static void
67 error_callback(mpd_unused mpdclient_t *c, gint error, const gchar *msg)
68 {
69         error = error & 0xFF;
70         switch (error) {
71         case MPD_ERROR_CONNPORT:
72         case MPD_ERROR_NORESPONSE:
73                 break;
74         case MPD_ERROR_ACK:
75                 screen_status_printf("%s", error_msg(msg));
76                 screen_bell();
77                 break;
78         default:
79                 screen_status_printf("%s", msg);
80                 screen_bell();
81                 doupdate();
82                 connected = FALSE;
83         }
84 }
86 static void
87 update_xterm_title(void)
88 {
89         static char title[BUFSIZE];
90         char tmp[BUFSIZE];
91         mpd_Status *status = NULL;
92         mpd_Song *song = NULL;
94         if (mpd) {
95                 status = mpd->status;
96                 song = mpd->song;
97         }
99         if (options.xterm_title_format && status && song &&
100             IS_PLAYING(status->state))
101                 strfsong(tmp, BUFSIZE, options.xterm_title_format, song);
102         else
103                 g_strlcpy(tmp, PACKAGE " version " VERSION, BUFSIZE);
105         if (strncmp(title, tmp, BUFSIZE)) {
106                 g_strlcpy(title, tmp, BUFSIZE);
107                 set_xterm_title("%s", title);
108         }
111 static void
112 exit_and_cleanup(void)
114         screen_exit();
115         set_xterm_title("");
116         printf("\n");
118         if (mpd) {
119                 mpdclient_disconnect(mpd);
120                 mpdclient_free(mpd);
121         }
123         g_free(options.host);
124         g_free(options.password);
125         g_free(options.list_format);
126         g_free(options.status_format);
127         g_free(options.scroll_sep);
130 static void
131 catch_sigint(mpd_unused int sig)
133         g_main_loop_quit(main_loop);
137 static void
138 catch_sigcont(mpd_unused int sig)
140 #ifdef ENABLE_RAW_MODE
141         reset_prog_mode(); /* restore tty modes */
142         refresh();
143 #endif
144         screen_resize();
147 void
148 sigstop(void)
150   def_prog_mode();  /* save the tty modes */
151   endwin();         /* end curses mode temporarily */
152   kill(0, SIGSTOP); /* issue SIGSTOP */
155 static guint timer_sigwinch_id;
157 static gboolean
158 timer_sigwinch(mpd_unused gpointer data)
160         /* the following causes the screen to flicker.  There might be
161            better solutions, but I believe it isn't all that
162            important. */
164         endwin();
165         refresh();
166         screen_resize();
168         return FALSE;
171 static void
172 catch_sigwinch(mpd_unused int sig)
174         if (timer_sigwinch_id != 0)
175                 g_source_remove(timer_sigwinch_id);
177         timer_sigwinch_id = g_timeout_add(100, timer_sigwinch, NULL);
180 static gboolean
181 timer_mpd_update(gpointer data);
183 /**
184  * This timer is installed when the connection to the MPD server is
185  * broken.  It tries to recover by reconnecting periodically.
186  */
187 static gboolean
188 timer_reconnect(mpd_unused gpointer data)
190         int ret;
192         if (connected)
193                 return FALSE;
195         screen_status_printf(_("Connecting to %s...  [Press %s to abort]"),
196                              options.host, get_key_names(CMD_QUIT,0) );
197         doupdate();
199         mpdclient_disconnect(mpd);
200         ret = mpdclient_connect(mpd,
201                                 options.host, options.port,
202                                 1.5,
203                                 options.password);
204         if (ret != 0) {
205                 /* try again in 5 seconds */
206                 g_timeout_add(5000, timer_reconnect, NULL);
207                 return FALSE;
208         }
210         /* quit if mpd is pre 0.11.0 - song id not supported by mpd */
211         if (MPD_VERSION_LT(mpd, 0, 11, 0)) {
212                 screen_status_printf(_("Error: MPD version %d.%d.%d is to old (0.11.0 needed).\n"),
213                                      mpd->connection->version[0],
214                                      mpd->connection->version[1],
215                                      mpd->connection->version[2]);
216                 mpdclient_disconnect(mpd);
217                 doupdate();
219                 /* try again after 30 seconds */
220                 g_timeout_add(30000, timer_reconnect, NULL);
221                 return FALSE;
222         }
224         screen_status_printf(_("Connected to %s!"), options.host);
225         doupdate();
227         connected = TRUE;
229         /* update immediately */
230         g_timeout_add(1, timer_mpd_update, GINT_TO_POINTER(FALSE));
232         reconnect_source_id = 0;
233         return FALSE;
237 static gboolean
238 timer_mpd_update(gpointer data)
240         if (connected)
241                 mpdclient_update(mpd);
242         else if (reconnect_source_id == 0)
243                 reconnect_source_id = g_timeout_add(1000, timer_reconnect,
244                                                     NULL);
246         if (options.enable_xterm_title)
247                 update_xterm_title();
249         screen_update(mpd);
251         return GPOINTER_TO_INT(data);
254 /**
255  * This idle timer is invoked when the user hasn't typed a key for
256  * 500ms.  It is used for delayed seeking.
257  */
258 static gboolean
259 timer_idle(mpd_unused gpointer data)
261         screen_idle(mpd);
262         return TRUE;
265 static gboolean
266 keyboard_event(mpd_unused GIOChannel *source,
267                mpd_unused GIOCondition condition, mpd_unused gpointer data)
269         command_t cmd;
271         /* remove the idle timeout; add it later with fresh interval */
272         g_source_remove(idle_source_id);
274         if ((cmd=get_keyboard_command()) != CMD_NONE) {
275                 if (cmd == CMD_QUIT) {
276                         g_main_loop_quit(main_loop);
277                         return FALSE;
278                 }
280                 screen_cmd(mpd, cmd);
282                 if (cmd == CMD_VOLUME_UP || cmd == CMD_VOLUME_DOWN) {
283                         /* make sure we dont update the volume yet */
284                         g_source_remove(update_source_id);
285                         update_source_id = g_timeout_add((guint)(MPD_UPDATE_TIME * 1000),
286                                                          timer_mpd_update,
287                                                          GINT_TO_POINTER(TRUE));
288                 }
289         }
291         screen_update(mpd);
293         idle_source_id = g_timeout_add(idle_interval, timer_idle, NULL);
294         return TRUE;
297 /**
298  * Check the configured key bindings for errors, and display a status
299  * message every 10 seconds.
300  */
301 static gboolean
302 timer_check_key_bindings(mpd_unused gpointer data)
304         char buf[256];
305         gboolean key_error;
307         key_error = check_key_bindings(NULL, buf, sizeof(buf));
308         if (!key_error)
309                 /* no error: disable this timer for the rest of this
310                    process */
311                 return FALSE;
313         screen_status_printf("%s", buf);
314         doupdate();
315         return TRUE;
318 int
319 main(int argc, const char *argv[])
321         struct sigaction act;
322         const char *charset = NULL;
323         GIOChannel *keyboard_channel;
325 #ifdef HAVE_LOCALE_H
326         /* time and date formatting */
327         setlocale(LC_TIME,"");
328         /* care about sorting order etc */
329         setlocale(LC_COLLATE,"");
330         /* charset */
331         setlocale(LC_CTYPE,"");
332         /* initialize charset conversions */
333         charset_init(g_get_charset(&charset));
334 #endif
336         /* initialize i18n support */
337 #ifdef ENABLE_NLS
338         setlocale(LC_MESSAGES, "");
339         bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
340         bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
341         textdomain(GETTEXT_PACKAGE);
342 #endif
344         /* initialize options */
345         options_init();
347         /* parse command line options - 1 pass get configuration files */
348         options_parse(argc, argv);
350         /* read configuration */
351         read_configuration(&options);
353         /* check key bindings */
354         check_key_bindings(NULL, NULL, 0);
356         /* parse command line options - 2 pass */
357         options_parse(argc, argv);
359         /* setup signal behavior - SIGINT */
360         sigemptyset(&act.sa_mask);
361         act.sa_flags = 0;
362         act.sa_handler = catch_sigint;
363         if (sigaction(SIGINT, &act, NULL) < 0) {
364                 perror("signal");
365                 exit(EXIT_FAILURE);
366         }
368         /* setup signal behavior - SIGTERM */
369         sigemptyset(&act.sa_mask);
370         act.sa_flags = 0;
371         act.sa_handler = catch_sigint;
372         if (sigaction(SIGTERM, &act, NULL) < 0) {
373                 perror("sigaction()");
374                 exit(EXIT_FAILURE);
375         }
377         /* setup signal behavior - SIGCONT */
378         sigemptyset(&act.sa_mask);
379         act.sa_flags = 0;
380         act.sa_handler = catch_sigcont;
381         if (sigaction(SIGCONT, &act, NULL) < 0) {
382                 perror("sigaction(SIGCONT)");
383                 exit(EXIT_FAILURE);
384         }
386         /* setup signal behaviour - SIGHUP*/
387         sigemptyset(&act.sa_mask);
388         act.sa_flags = 0;
389         act.sa_handler = catch_sigint;
390         if (sigaction(SIGHUP, &act, NULL) < 0) {
391                 perror("sigaction(SIGHUP)");
392                 exit(EXIT_FAILURE);
393         }
395         /* setup SIGWINCH */
397         act.sa_handler = catch_sigwinch;
398         if (sigaction(SIGWINCH, &act, NULL) < 0) {
399                 perror("sigaction(SIGWINCH)");
400                 exit(EXIT_FAILURE);
401         }
403         /* ignore SIGPIPE */
405         act.sa_flags = SA_RESTART;
406         act.sa_handler = SIG_IGN;
407         if (sigaction(SIGWINCH, &act, NULL) < 0) {
408                 perror("sigaction(SIGWINCH)");
409                 exit(EXIT_FAILURE);
410         }
412         ncu_init();
414 #ifdef ENABLE_LYRICS_SCREEN
415         lyrics_init();
416 #endif
418         /* create mpdclient instance */
419         mpd = mpdclient_new();
420         mpdclient_install_error_callback(mpd, error_callback);
422         /* initialize curses */
423         screen_init(mpd);
425         /* the main loop */
426         main_loop = g_main_loop_new(NULL, FALSE);
428         /* watch out for keyboard input */
429         keyboard_channel = g_io_channel_unix_new(STDIN_FILENO);
430         g_io_add_watch(keyboard_channel, G_IO_IN, keyboard_event, NULL);
432         /* attempt to connect */
433         reconnect_source_id = g_timeout_add(1, timer_reconnect, NULL);
435         update_source_id = g_timeout_add((guint)(MPD_UPDATE_TIME * 1000),
436                                          timer_mpd_update,
437                                          GINT_TO_POINTER(TRUE));
438         g_timeout_add(10000, timer_check_key_bindings, NULL);
439         idle_source_id = g_timeout_add(idle_interval, timer_idle, NULL);
441         g_main_loop_run(main_loop);
443         /* cleanup */
445         g_main_loop_unref(main_loop);
446         g_io_channel_unref(keyboard_channel);
448         exit_and_cleanup();
449         ncu_deinit();
451         return 0;