Code

mpdclient: mpdclient_connect() returns bool
[ncmpc.git] / src / main.c
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2009 The Music Player Daemon Project
3  * Project homepage: http://musicpd.org
4  
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
20 #include "config.h"
21 #include "ncmpc.h"
22 #include "mpdclient.h"
23 #include "charset.h"
24 #include "options.h"
25 #include "command.h"
26 #include "ncu.h"
27 #include "screen.h"
28 #include "screen_utils.h"
29 #include "strfsong.h"
30 #include "i18n.h"
31 #include "player_command.h"
33 #ifndef NCMPC_MINI
34 #include "conf.h"
35 #endif
37 #ifdef ENABLE_LYRICS_SCREEN
38 #include "lyrics.h"
39 #endif
41 #ifdef ENABLE_LIRC
42 #include "lirc.h"
43 #endif
45 #include <mpd/client.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <signal.h>
50 #include <string.h>
52 #ifdef ENABLE_LOCALE
53 #include <locale.h>
54 #endif
56 /* time between mpd updates [s] */
57 static const guint update_interval = 500;
59 #define BUFSIZE 1024
61 static struct mpdclient *mpd = NULL;
62 static GMainLoop *main_loop;
63 static guint reconnect_source_id, update_source_id;
65 #ifndef NCMPC_MINI
66 static guint check_key_bindings_source_id;
67 #endif
69 static void
70 error_callback(G_GNUC_UNUSED struct mpdclient *c, G_GNUC_UNUSED gint error,
71                const gchar *_msg)
72 {
73         char *msg = utf8_to_locale(_msg);
74         screen_status_printf("%s", msg);
75         g_free(msg);
77         screen_bell();
78         doupdate();
79 }
81 #ifndef NCMPC_MINI
82 static void
83 update_xterm_title(void)
84 {
85         static char title[BUFSIZE];
86         char tmp[BUFSIZE];
87         struct mpd_status *status = NULL;
88         const struct mpd_song *song = NULL;
90         if (mpd) {
91                 status = mpd->status;
92                 song = mpd->song;
93         }
95         if (options.xterm_title_format && status && song &&
96             IS_PLAYING(mpd_status_get_state(status)))
97                 strfsong(tmp, BUFSIZE, options.xterm_title_format, song);
98         else
99                 g_strlcpy(tmp, PACKAGE " version " VERSION, BUFSIZE);
101         if (strncmp(title, tmp, BUFSIZE)) {
102                 g_strlcpy(title, tmp, BUFSIZE);
103                 set_xterm_title("%s", title);
104         }
106 #endif
108 static void
109 exit_and_cleanup(void)
111         screen_exit();
112 #ifndef NCMPC_MINI
113         set_xterm_title("");
114 #endif
115         printf("\n");
117         if (mpd) {
118                 mpdclient_disconnect(mpd);
119                 mpdclient_free(mpd);
120         }
123 static void
124 catch_sigint(G_GNUC_UNUSED int sig)
126         g_main_loop_quit(main_loop);
130 static void
131 catch_sigcont(G_GNUC_UNUSED int sig)
133         screen_resize(mpd);
136 void
137 sigstop(void)
139   def_prog_mode();  /* save the tty modes */
140   endwin();         /* end curses mode temporarily */
141   kill(0, SIGSTOP); /* issue SIGSTOP */
144 static guint timer_sigwinch_id;
146 static gboolean
147 timer_sigwinch(G_GNUC_UNUSED gpointer data)
149         /* the following causes the screen to flicker.  There might be
150            better solutions, but I believe it isn't all that
151            important. */
153         endwin();
154         refresh();
155         screen_resize(mpd);
157         return FALSE;
160 static void
161 catch_sigwinch(G_GNUC_UNUSED int sig)
163         if (timer_sigwinch_id != 0)
164                 g_source_remove(timer_sigwinch_id);
166         timer_sigwinch_id = g_timeout_add(100, timer_sigwinch, NULL);
169 static gboolean
170 timer_mpd_update(gpointer data);
172 /**
173  * This timer is installed when the connection to the MPD server is
174  * broken.  It tries to recover by reconnecting periodically.
175  */
176 static gboolean
177 timer_reconnect(G_GNUC_UNUSED gpointer data)
179         bool success;
181         assert(!mpdclient_is_connected(mpd));
183         screen_status_printf(_("Connecting to %s...  [Press %s to abort]"),
184                              options.host, get_key_names(CMD_QUIT,0) );
185         doupdate();
187         mpdclient_disconnect(mpd);
188         success = mpdclient_connect(mpd,
189                                     options.host, options.port,
190                                     1.5,
191                                     options.password);
192         if (!success) {
193                 /* try again in 5 seconds */
194                 g_timeout_add(5000, timer_reconnect, NULL);
195                 return FALSE;
196         }
198 #ifndef NCMPC_MINI
199         /* quit if mpd is pre 0.11.0 - song id not supported by mpd */
200         if (mpd_connection_cmp_server_version(mpd->connection, 0, 12, 0) < 0) {
201                 const unsigned *version =
202                         mpd_connection_get_server_version(mpd->connection);
203                 screen_status_printf(_("Error: MPD version %d.%d.%d is to old (%s needed)"),
204                                      version[0], version[1], version[2],
205                                      "0.12.0");
206                 mpdclient_disconnect(mpd);
207                 doupdate();
209                 /* try again after 30 seconds */
210                 g_timeout_add(30000, timer_reconnect, NULL);
211                 return FALSE;
212         }
213 #endif
215         screen_status_printf(_("Connected to %s"),
216                              options.host != NULL
217                              ? options.host : "localhost");
218         doupdate();
220         /* update immediately */
221         g_timeout_add(1, timer_mpd_update, GINT_TO_POINTER(FALSE));
223         reconnect_source_id = 0;
224         return FALSE;
228 static gboolean
229 timer_mpd_update(gpointer data)
231         if (mpdclient_is_connected(mpd))
232                 mpdclient_update(mpd);
233         else if (reconnect_source_id == 0)
234                 reconnect_source_id = g_timeout_add(1000, timer_reconnect,
235                                                     NULL);
237 #ifndef NCMPC_MINI
238         if (options.enable_xterm_title)
239                 update_xterm_title();
240 #endif
242         screen_update(mpd);
244         return GPOINTER_TO_INT(data);
247 void begin_input_event(void)
251 void end_input_event(void)
253         screen_update(mpd);
256 int do_input_event(command_t cmd)
258         if (cmd == CMD_QUIT) {
259                 g_main_loop_quit(main_loop);
260                 return -1;
261         }
263         screen_cmd(mpd, cmd);
265         if (cmd == CMD_VOLUME_UP || cmd == CMD_VOLUME_DOWN) {
266                 /* make sure we don't update the volume yet */
267                 g_source_remove(update_source_id);
268                 update_source_id = g_timeout_add(update_interval,
269                                                  timer_mpd_update,
270                                                  GINT_TO_POINTER(TRUE));
271         }
273         return 0;
276 static gboolean
277 keyboard_event(G_GNUC_UNUSED GIOChannel *source,
278                G_GNUC_UNUSED GIOCondition condition,
279                G_GNUC_UNUSED gpointer data)
281         command_t cmd;
283         begin_input_event();
285         if ((cmd=get_keyboard_command()) != CMD_NONE)
286                 if (do_input_event(cmd) != 0)
287                         return FALSE;
289         end_input_event();
290         return TRUE;
293 #ifndef NCMPC_MINI
294 /**
295  * Check the configured key bindings for errors, and display a status
296  * message every 10 seconds.
297  */
298 static gboolean
299 timer_check_key_bindings(G_GNUC_UNUSED gpointer data)
301         char buf[256];
302 #ifdef ENABLE_KEYDEF_SCREEN
303         char comment[64];
304 #endif
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                 check_key_bindings_source_id = 0;
312                 return FALSE;
313         }
315 #ifdef ENABLE_KEYDEF_SCREEN
316         g_strchomp(buf);
317         g_strlcat(buf, " (", sizeof(buf));
318         /* to translators: a key was bound twice in the key editor,
319            and this is a hint for the user what to press to correct
320            that */
321         g_snprintf(comment, sizeof(comment), _("press %s for the key editor"),
322                    get_key_names(CMD_SCREEN_KEYDEF, 0));
323         g_strlcat(buf, comment, sizeof(buf));
324         g_strlcat(buf, ")", sizeof(buf));
325 #endif
327         screen_status_printf("%s", buf);
329         doupdate();
330         return TRUE;
332 #endif
334 int
335 main(int argc, const char *argv[])
337         struct sigaction act;
338 #ifdef ENABLE_LOCALE
339         const char *charset = NULL;
340 #endif
341         GIOChannel *keyboard_channel;
342 #ifdef ENABLE_LIRC
343         int lirc_socket;
344         GIOChannel *lirc_channel = NULL;
345 #endif
347 #ifdef ENABLE_LOCALE
348         /* time and date formatting */
349         setlocale(LC_TIME,"");
350         /* care about sorting order etc */
351         setlocale(LC_COLLATE,"");
352         /* charset */
353         setlocale(LC_CTYPE,"");
354         /* initialize charset conversions */
355         charset = charset_init();
357         /* initialize i18n support */
358 #endif
360 #ifdef ENABLE_NLS
361         setlocale(LC_MESSAGES, "");
362         bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
363 #ifdef ENABLE_LOCALE
364         bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
365 #endif
366         textdomain(GETTEXT_PACKAGE);
367 #endif
369         /* initialize options */
370         options_init();
372         /* parse command line options - 1 pass get configuration files */
373         options_parse(argc, argv);
375 #ifndef NCMPC_MINI
376         /* read configuration */
377         read_configuration();
379         /* check key bindings */
380         check_key_bindings(NULL, NULL, 0);
381 #endif
383         /* parse command line options - 2 pass */
384         options_parse(argc, argv);
386         /* setup signal behavior - SIGINT */
387         sigemptyset(&act.sa_mask);
388         act.sa_flags = 0;
389         act.sa_handler = catch_sigint;
390         if (sigaction(SIGINT, &act, NULL) < 0) {
391                 perror("signal");
392                 exit(EXIT_FAILURE);
393         }
395         /* setup signal behavior - SIGTERM */
397         act.sa_handler = catch_sigint;
398         if (sigaction(SIGTERM, &act, NULL) < 0) {
399                 perror("sigaction()");
400                 exit(EXIT_FAILURE);
401         }
403         /* setup signal behavior - SIGCONT */
405         act.sa_handler = catch_sigcont;
406         if (sigaction(SIGCONT, &act, NULL) < 0) {
407                 perror("sigaction(SIGCONT)");
408                 exit(EXIT_FAILURE);
409         }
411         /* setup signal behaviour - SIGHUP*/
413         act.sa_handler = catch_sigint;
414         if (sigaction(SIGHUP, &act, NULL) < 0) {
415                 perror("sigaction(SIGHUP)");
416                 exit(EXIT_FAILURE);
417         }
419         /* setup SIGWINCH */
421         act.sa_flags = SA_RESTART;
422         act.sa_handler = catch_sigwinch;
423         if (sigaction(SIGWINCH, &act, NULL) < 0) {
424                 perror("sigaction(SIGWINCH)");
425                 exit(EXIT_FAILURE);
426         }
428         /* ignore SIGPIPE */
430         act.sa_handler = SIG_IGN;
431         if (sigaction(SIGPIPE, &act, NULL) < 0) {
432                 perror("sigaction(SIGPIPE)");
433                 exit(EXIT_FAILURE);
434         }
436         ncu_init();
438 #ifdef ENABLE_LYRICS_SCREEN
439         lyrics_init();
440 #endif
442         /* create mpdclient instance */
443         mpd = mpdclient_new();
444         mpdclient_install_error_callback(mpd, error_callback);
446         /* initialize curses */
447         screen_init(mpd);
449         /* the main loop */
450         main_loop = g_main_loop_new(NULL, FALSE);
452         /* watch out for keyboard input */
453         keyboard_channel = g_io_channel_unix_new(STDIN_FILENO);
454         g_io_add_watch(keyboard_channel, G_IO_IN, keyboard_event, NULL);
456 #ifdef ENABLE_LIRC
457         /* watch out for lirc input */
458         lirc_socket = ncmpc_lirc_open();
459         if (lirc_socket >= 0) {
460                 lirc_channel = g_io_channel_unix_new(lirc_socket);
461                 g_io_add_watch(lirc_channel, G_IO_IN, lirc_event, NULL);
462         }
463 #endif
465         /* attempt to connect */
466         reconnect_source_id = g_timeout_add(1, timer_reconnect, NULL);
468         update_source_id = g_timeout_add(update_interval,
469                                          timer_mpd_update,
470                                          GINT_TO_POINTER(TRUE));
471 #ifndef NCMPC_MINI
472         check_key_bindings_source_id = g_timeout_add(10000, timer_check_key_bindings, NULL);
473 #endif
475         screen_paint(mpd);
477         g_main_loop_run(main_loop);
479         /* cleanup */
481         cancel_seek_timer();
483         g_source_remove(update_source_id);
485 #ifndef NCMPC_MINI
486         if (check_key_bindings_source_id != 0)
487                 g_source_remove(check_key_bindings_source_id);
488 #endif
490         g_main_loop_unref(main_loop);
491         g_io_channel_unref(keyboard_channel);
493 #ifdef ENABLE_LIRC
494         if (lirc_socket >= 0)
495                 g_io_channel_unref(lirc_channel);
496         ncmpc_lirc_close();
497 #endif
499         exit_and_cleanup();
501 #ifdef ENABLE_LYRICS_SCREEN
502         lyrics_deinit();
503 #endif
505         ncu_deinit();
506         options_deinit();
508         return 0;