Code

screen: eliminated state macros
[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 #ifndef NCMPC_MINI
70 static void
71 update_xterm_title(void)
72 {
73         static char title[BUFSIZE];
74         char tmp[BUFSIZE];
75         struct mpd_status *status = NULL;
76         const struct mpd_song *song = NULL;
78         if (mpd) {
79                 status = mpd->status;
80                 song = mpd->song;
81         }
83         if (options.xterm_title_format && status && song &&
84             mpd_status_get_state(status) == MPD_STATE_PLAY)
85                 strfsong(tmp, BUFSIZE, options.xterm_title_format, song);
86         else
87                 g_strlcpy(tmp, PACKAGE " version " VERSION, BUFSIZE);
89         if (strncmp(title, tmp, BUFSIZE)) {
90                 g_strlcpy(title, tmp, BUFSIZE);
91                 set_xterm_title("%s", title);
92         }
93 }
94 #endif
96 static void
97 exit_and_cleanup(void)
98 {
99         screen_exit();
100 #ifndef NCMPC_MINI
101         set_xterm_title("");
102 #endif
103         printf("\n");
105         if (mpd) {
106                 mpdclient_disconnect(mpd);
107                 mpdclient_free(mpd);
108         }
111 static void
112 catch_sigint(G_GNUC_UNUSED int sig)
114         g_main_loop_quit(main_loop);
118 static void
119 catch_sigcont(G_GNUC_UNUSED int sig)
121         screen_resize(mpd);
124 void
125 sigstop(void)
127   def_prog_mode();  /* save the tty modes */
128   endwin();         /* end curses mode temporarily */
129   kill(0, SIGSTOP); /* issue SIGSTOP */
132 static guint timer_sigwinch_id;
134 static gboolean
135 timer_sigwinch(G_GNUC_UNUSED gpointer data)
137         /* the following causes the screen to flicker.  There might be
138            better solutions, but I believe it isn't all that
139            important. */
141         endwin();
142         refresh();
143         screen_resize(mpd);
145         return FALSE;
148 static void
149 catch_sigwinch(G_GNUC_UNUSED int sig)
151         if (timer_sigwinch_id != 0)
152                 g_source_remove(timer_sigwinch_id);
154         timer_sigwinch_id = g_timeout_add(100, timer_sigwinch, NULL);
157 static gboolean
158 timer_mpd_update(gpointer data);
160 /**
161  * This timer is installed when the connection to the MPD server is
162  * broken.  It tries to recover by reconnecting periodically.
163  */
164 static gboolean
165 timer_reconnect(G_GNUC_UNUSED gpointer data)
167         bool success;
169         assert(!mpdclient_is_connected(mpd));
171         screen_status_printf(_("Connecting to %s...  [Press %s to abort]"),
172                              options.host, get_key_names(CMD_QUIT,0) );
173         doupdate();
175         mpdclient_disconnect(mpd);
176         success = mpdclient_connect(mpd,
177                                     options.host, options.port,
178                                     1.5,
179                                     options.password);
180         if (!success) {
181                 /* try again in 5 seconds */
182                 g_timeout_add(5000, timer_reconnect, NULL);
183                 return FALSE;
184         }
186 #ifndef NCMPC_MINI
187         /* quit if mpd is pre 0.11.0 - song id not supported by mpd */
188         if (mpd_connection_cmp_server_version(mpd->connection, 0, 12, 0) < 0) {
189                 const unsigned *version =
190                         mpd_connection_get_server_version(mpd->connection);
191                 screen_status_printf(_("Error: MPD version %d.%d.%d is to old (%s needed)"),
192                                      version[0], version[1], version[2],
193                                      "0.12.0");
194                 mpdclient_disconnect(mpd);
195                 doupdate();
197                 /* try again after 30 seconds */
198                 g_timeout_add(30000, timer_reconnect, NULL);
199                 return FALSE;
200         }
201 #endif
203         screen_status_printf(_("Connected to %s"),
204                              options.host != NULL
205                              ? options.host : "localhost");
206         doupdate();
208         /* update immediately */
209         g_timeout_add(1, timer_mpd_update, GINT_TO_POINTER(FALSE));
211         reconnect_source_id = 0;
212         return FALSE;
216 static gboolean
217 timer_mpd_update(gpointer data)
219         if (mpdclient_is_connected(mpd))
220                 mpdclient_update(mpd);
221         else if (reconnect_source_id == 0)
222                 reconnect_source_id = g_timeout_add(1000, timer_reconnect,
223                                                     NULL);
225 #ifndef NCMPC_MINI
226         if (options.enable_xterm_title)
227                 update_xterm_title();
228 #endif
230         screen_update(mpd);
232         mpd->events = 0;
234         return GPOINTER_TO_INT(data);
237 void begin_input_event(void)
241 void end_input_event(void)
243         screen_update(mpd);
246 int do_input_event(command_t cmd)
248         if (cmd == CMD_QUIT) {
249                 g_main_loop_quit(main_loop);
250                 return -1;
251         }
253         screen_cmd(mpd, cmd);
255         if (cmd == CMD_VOLUME_UP || cmd == CMD_VOLUME_DOWN) {
256                 /* make sure we don't update the volume yet */
257                 g_source_remove(update_source_id);
258                 update_source_id = g_timeout_add(update_interval,
259                                                  timer_mpd_update,
260                                                  GINT_TO_POINTER(TRUE));
261         }
263         return 0;
266 static gboolean
267 keyboard_event(G_GNUC_UNUSED GIOChannel *source,
268                G_GNUC_UNUSED GIOCondition condition,
269                G_GNUC_UNUSED gpointer data)
271         command_t cmd;
273         begin_input_event();
275         if ((cmd=get_keyboard_command()) != CMD_NONE)
276                 if (do_input_event(cmd) != 0)
277                         return FALSE;
279         end_input_event();
280         return TRUE;
283 #ifndef NCMPC_MINI
284 /**
285  * Check the configured key bindings for errors, and display a status
286  * message every 10 seconds.
287  */
288 static gboolean
289 timer_check_key_bindings(G_GNUC_UNUSED gpointer data)
291         char buf[256];
292 #ifdef ENABLE_KEYDEF_SCREEN
293         char comment[64];
294 #endif
295         gboolean key_error;
297         key_error = check_key_bindings(NULL, buf, sizeof(buf));
298         if (!key_error) {
299                 /* no error: disable this timer for the rest of this
300                    process */
301                 check_key_bindings_source_id = 0;
302                 return FALSE;
303         }
305 #ifdef ENABLE_KEYDEF_SCREEN
306         g_strchomp(buf);
307         g_strlcat(buf, " (", sizeof(buf));
308         /* to translators: a key was bound twice in the key editor,
309            and this is a hint for the user what to press to correct
310            that */
311         g_snprintf(comment, sizeof(comment), _("press %s for the key editor"),
312                    get_key_names(CMD_SCREEN_KEYDEF, 0));
313         g_strlcat(buf, comment, sizeof(buf));
314         g_strlcat(buf, ")", sizeof(buf));
315 #endif
317         screen_status_printf("%s", buf);
319         doupdate();
320         return TRUE;
322 #endif
324 int
325 main(int argc, const char *argv[])
327         struct sigaction act;
328 #ifdef ENABLE_LOCALE
329         const char *charset = NULL;
330 #endif
331         GIOChannel *keyboard_channel;
332 #ifdef ENABLE_LIRC
333         int lirc_socket;
334         GIOChannel *lirc_channel = NULL;
335 #endif
337 #ifdef ENABLE_LOCALE
338         /* time and date formatting */
339         setlocale(LC_TIME,"");
340         /* care about sorting order etc */
341         setlocale(LC_COLLATE,"");
342         /* charset */
343         setlocale(LC_CTYPE,"");
344         /* initialize charset conversions */
345         charset = charset_init();
347         /* initialize i18n support */
348 #endif
350 #ifdef ENABLE_NLS
351         setlocale(LC_MESSAGES, "");
352         bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
353 #ifdef ENABLE_LOCALE
354         bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
355 #endif
356         textdomain(GETTEXT_PACKAGE);
357 #endif
359         /* initialize options */
360         options_init();
362         /* parse command line options - 1 pass get configuration files */
363         options_parse(argc, argv);
365 #ifndef NCMPC_MINI
366         /* read configuration */
367         read_configuration();
369         /* check key bindings */
370         check_key_bindings(NULL, NULL, 0);
371 #endif
373         /* parse command line options - 2 pass */
374         options_parse(argc, argv);
376         /* setup signal behavior - SIGINT */
377         sigemptyset(&act.sa_mask);
378         act.sa_flags = 0;
379         act.sa_handler = catch_sigint;
380         if (sigaction(SIGINT, &act, NULL) < 0) {
381                 perror("signal");
382                 exit(EXIT_FAILURE);
383         }
385         /* setup signal behavior - SIGTERM */
387         act.sa_handler = catch_sigint;
388         if (sigaction(SIGTERM, &act, NULL) < 0) {
389                 perror("sigaction()");
390                 exit(EXIT_FAILURE);
391         }
393         /* setup signal behavior - SIGCONT */
395         act.sa_handler = catch_sigcont;
396         if (sigaction(SIGCONT, &act, NULL) < 0) {
397                 perror("sigaction(SIGCONT)");
398                 exit(EXIT_FAILURE);
399         }
401         /* setup signal behaviour - SIGHUP*/
403         act.sa_handler = catch_sigint;
404         if (sigaction(SIGHUP, &act, NULL) < 0) {
405                 perror("sigaction(SIGHUP)");
406                 exit(EXIT_FAILURE);
407         }
409         /* setup SIGWINCH */
411         act.sa_flags = SA_RESTART;
412         act.sa_handler = catch_sigwinch;
413         if (sigaction(SIGWINCH, &act, NULL) < 0) {
414                 perror("sigaction(SIGWINCH)");
415                 exit(EXIT_FAILURE);
416         }
418         /* ignore SIGPIPE */
420         act.sa_handler = SIG_IGN;
421         if (sigaction(SIGPIPE, &act, NULL) < 0) {
422                 perror("sigaction(SIGPIPE)");
423                 exit(EXIT_FAILURE);
424         }
426         ncu_init();
428 #ifdef ENABLE_LYRICS_SCREEN
429         lyrics_init();
430 #endif
432         /* create mpdclient instance */
433         mpd = mpdclient_new();
435         /* initialize curses */
436         screen_init(mpd);
438         /* the main loop */
439         main_loop = g_main_loop_new(NULL, FALSE);
441         /* watch out for keyboard input */
442         keyboard_channel = g_io_channel_unix_new(STDIN_FILENO);
443         g_io_add_watch(keyboard_channel, G_IO_IN, keyboard_event, NULL);
445 #ifdef ENABLE_LIRC
446         /* watch out for lirc input */
447         lirc_socket = ncmpc_lirc_open();
448         if (lirc_socket >= 0) {
449                 lirc_channel = g_io_channel_unix_new(lirc_socket);
450                 g_io_add_watch(lirc_channel, G_IO_IN, lirc_event, NULL);
451         }
452 #endif
454         /* attempt to connect */
455         reconnect_source_id = g_timeout_add(1, timer_reconnect, NULL);
457         update_source_id = g_timeout_add(update_interval,
458                                          timer_mpd_update,
459                                          GINT_TO_POINTER(TRUE));
460 #ifndef NCMPC_MINI
461         check_key_bindings_source_id = g_timeout_add(10000, timer_check_key_bindings, NULL);
462 #endif
464         screen_paint(mpd);
466         g_main_loop_run(main_loop);
468         /* cleanup */
470         cancel_seek_timer();
472         g_source_remove(update_source_id);
474 #ifndef NCMPC_MINI
475         if (check_key_bindings_source_id != 0)
476                 g_source_remove(check_key_bindings_source_id);
477 #endif
479         g_main_loop_unref(main_loop);
480         g_io_channel_unref(keyboard_channel);
482 #ifdef ENABLE_LIRC
483         if (lirc_socket >= 0)
484                 g_io_channel_unref(lirc_channel);
485         ncmpc_lirc_close();
486 #endif
488         exit_and_cleanup();
490 #ifdef ENABLE_LYRICS_SCREEN
491         lyrics_deinit();
492 #endif
494         ncu_deinit();
495         options_deinit();
497         return 0;