Code

main: remove unused function sigstop()
[ncmpc.git] / src / main.c
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2017 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.
9  *
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.
14  *
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 "callbacks.h"
24 #include "charset.h"
25 #include "options.h"
26 #include "command.h"
27 #include "ncu.h"
28 #include "screen.h"
29 #include "screen_utils.h"
30 #include "screen_status.h"
31 #include "strfsong.h"
32 #include "i18n.h"
33 #include "player_command.h"
34 #include "keyboard.h"
35 #include "lirc.h"
37 #ifndef NCMPC_MINI
38 #include "conf.h"
39 #endif
41 #ifdef ENABLE_LYRICS_SCREEN
42 #include "lyrics.h"
43 #endif
45 #include <mpd/client.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50 #include <signal.h>
51 #include <string.h>
53 #ifdef ENABLE_LOCALE
54 #include <locale.h>
55 #endif
57 /* time between mpd updates [ms] */
58 static const guint update_interval = 500;
60 #define BUFSIZE 1024
62 static struct mpdclient *mpd = NULL;
63 static GMainLoop *main_loop;
64 static guint reconnect_source_id, update_source_id;
65 static int sigwinch_pipes[2];
67 #ifndef NCMPC_MINI
68 static guint check_key_bindings_source_id;
69 #endif
71 #ifndef NCMPC_MINI
72 static void
73 update_xterm_title(void)
74 {
75         const struct mpd_song *song = mpd->song;
77         char tmp[BUFSIZE];
78         if (options.xterm_title_format && mpd->playing && song)
79                 strfsong(tmp, BUFSIZE, options.xterm_title_format, song);
80         else
81                 g_strlcpy(tmp, PACKAGE " version " VERSION, BUFSIZE);
83         static char title[BUFSIZE];
84         if (strncmp(title, tmp, BUFSIZE)) {
85                 g_strlcpy(title, tmp, BUFSIZE);
86                 set_xterm_title("%s", title);
87         }
88 }
89 #endif
91 #ifndef WIN32
92 static void
93 catch_sigint(gcc_unused int sig)
94 {
95         g_main_loop_quit(main_loop);
96 }
99 static void
100 catch_sigcont(gcc_unused int sig)
102         if (1 != write(sigwinch_pipes[1], "", 1))
103                 exit(EXIT_FAILURE);
106 static gboolean
107 sigwinch_event(gcc_unused GIOChannel *source,
108                gcc_unused GIOCondition condition, gcc_unused gpointer data)
110         char ignoreme[64];
111         if (1 > read(sigwinch_pipes[0], ignoreme, 64))
112                 exit(EXIT_FAILURE);
114         endwin();
115         refresh();
116         screen_resize(mpd);
118         return TRUE;
121 static void
122 catch_sigwinch(gcc_unused int sig)
124         if (1 != write(sigwinch_pipes[1], "", 1))
125                 exit(EXIT_FAILURE);
127 #endif /* WIN32 */
129 static gboolean
130 timer_mpd_update(gpointer data);
132 static void
133 enable_update_timer(void)
135         if (update_source_id != 0)
136                 return;
138         update_source_id = g_timeout_add(update_interval,
139                                          timer_mpd_update, NULL);
142 static void
143 disable_update_timer(void)
145         if (update_source_id == 0)
146                 return;
148         g_source_remove(update_source_id);
149         update_source_id = 0;
152 static bool
153 should_enable_update_timer(void)
155         return mpd->playing
156 #ifndef NCMPC_MINI
157                 || options.display_time
158 #endif
159                 ;
162 static void
163 auto_update_timer(void)
165         if (should_enable_update_timer())
166                 enable_update_timer();
167         else
168                 disable_update_timer();
171 static void
172 check_reconnect(void);
174 static void
175 do_mpd_update(void)
177         if (mpdclient_is_connected(mpd) &&
178             (mpd->events != 0 || mpd->playing))
179                 mpdclient_update(mpd);
181 #ifndef NCMPC_MINI
182         if (options.enable_xterm_title)
183                 update_xterm_title();
184 #endif
186         screen_update(mpd);
187         mpd->events = 0;
189         mpdclient_put_connection(mpd);
190         check_reconnect();
193 static char *
194 settings_name(const struct mpd_settings *settings)
196         const char *host = mpd_settings_get_host(settings);
197         if (host == NULL)
198                 host = _("unknown");
200         if (host[0] == '/')
201                 return g_strdup(host);
203         unsigned port = mpd_settings_get_port(settings);
204         if (port == 0 || port == 6600)
205                 return g_strdup(host);
207         return g_strdup_printf("%s:%u", host, port);
210 static char *
211 default_settings_name(void)
213         struct mpd_settings *settings =
214                 mpd_settings_new(options.host, options.port, 0,
215                                  NULL, options.password);
216         if (settings == NULL)
217                 return g_strdup(_("unknown"));
219         char *name = settings_name(settings);
220         mpd_settings_free(settings);
222         return name;
225 /**
226  * This timer is installed when the connection to the MPD server is
227  * broken.  It tries to recover by reconnecting periodically.
228  */
229 static gboolean
230 timer_reconnect(gcc_unused gpointer data)
232         assert(mpdclient_is_dead(mpd));
234         reconnect_source_id = 0;
236         char *name = default_settings_name();
237         screen_status_printf(_("Connecting to %s...  [Press %s to abort]"),
238                              name, get_key_names(CMD_QUIT, false));
239         g_free(name);
240         doupdate();
242         mpdclient_disconnect(mpd);
243         mpdclient_connect(mpd, options.host, options.port,
244                           options.timeout_ms,
245                           options.password);
247         return FALSE;
250 static void
251 check_reconnect(void)
253         if (mpdclient_is_dead(mpd) && reconnect_source_id == 0)
254                 /* reconnect when the connection is lost */
255                 reconnect_source_id = g_timeout_add(1000, timer_reconnect,
256                                                     NULL);
259 void
260 mpdclient_connected_callback(void)
262         assert(reconnect_source_id == 0);
264 #ifndef NCMPC_MINI
265         /* quit if mpd is pre 0.14 - song id not supported by mpd */
266         struct mpd_connection *connection = mpdclient_get_connection(mpd);
267         if (mpd_connection_cmp_server_version(connection, 0, 16, 0) < 0) {
268                 const unsigned *version =
269                         mpd_connection_get_server_version(connection);
270                 screen_status_printf(_("Error: MPD version %d.%d.%d is too old (%s needed)"),
271                                      version[0], version[1], version[2],
272                                      "0.16.0");
273                 mpdclient_disconnect(mpd);
274                 doupdate();
276                 /* try again after 30 seconds */
277                 reconnect_source_id = g_timeout_add(30000,
278                                                     timer_reconnect, NULL);
279                 return;
280         }
281 #endif
283         screen_status_clear_message();
284         doupdate();
286         /* update immediately */
287         mpd->events = MPD_IDLE_ALL;
289         do_mpd_update();
291         auto_update_timer();
294 void
295 mpdclient_failed_callback(void)
297         assert(reconnect_source_id == 0);
299         /* try again in 5 seconds */
300         reconnect_source_id = g_timeout_add(5000,
301                                             timer_reconnect, NULL);
304 void
305 mpdclient_lost_callback(void)
307         assert(reconnect_source_id == 0);
309         screen_update(mpd);
311         reconnect_source_id = g_timeout_add(1000, timer_reconnect, NULL);
314 /**
315  * This function is called by the gidle.c library when MPD sends us an
316  * idle event (or when the connection dies).
317  */
318 void
319 mpdclient_idle_callback(gcc_unused enum mpd_idle events)
321 #ifndef NCMPC_MINI
322         if (options.enable_xterm_title)
323                 update_xterm_title();
324 #endif
326         screen_update(mpd);
327         auto_update_timer();
330 static gboolean
331 timer_mpd_update(gcc_unused gpointer data)
333         do_mpd_update();
335         if (should_enable_update_timer())
336                 return true;
337         else {
338                 update_source_id = 0;
339                 return false;
340         }
343 void begin_input_event(void)
347 void end_input_event(void)
349         screen_update(mpd);
350         mpd->events = 0;
352         mpdclient_put_connection(mpd);
353         check_reconnect();
354         auto_update_timer();
357 bool
358 do_input_event(command_t cmd)
360         if (cmd == CMD_QUIT) {
361                 g_main_loop_quit(main_loop);
362                 return false;
363         }
365         screen_cmd(mpd, cmd);
367         if (cmd == CMD_VOLUME_UP || cmd == CMD_VOLUME_DOWN)
368                 /* make sure we don't update the volume yet */
369                 disable_update_timer();
371         return true;
374 #ifndef NCMPC_MINI
375 /**
376  * Check the configured key bindings for errors, and display a status
377  * message every 10 seconds.
378  */
379 static gboolean
380 timer_check_key_bindings(gcc_unused gpointer data)
382         char buf[256];
384         if (check_key_bindings(NULL, buf, sizeof(buf))) {
385                 /* no error: disable this timer for the rest of this
386                    process */
387                 check_key_bindings_source_id = 0;
388                 return FALSE;
389         }
391 #ifdef ENABLE_KEYDEF_SCREEN
392         g_strchomp(buf);
393         g_strlcat(buf, " (", sizeof(buf));
394         /* to translators: a key was bound twice in the key editor,
395            and this is a hint for the user what to press to correct
396            that */
397         char comment[64];
398         g_snprintf(comment, sizeof(comment), _("press %s for the key editor"),
399                    get_key_names(CMD_SCREEN_KEYDEF, false));
400         g_strlcat(buf, comment, sizeof(buf));
401         g_strlcat(buf, ")", sizeof(buf));
402 #endif
404         screen_status_printf("%s", buf);
406         doupdate();
407         return TRUE;
409 #endif
411 int
412 main(int argc, const char *argv[])
414 #ifdef ENABLE_LOCALE
415 #ifndef ENABLE_NLS
416         gcc_unused
417 #endif
418         const char *charset = NULL;
419         /* time and date formatting */
420         setlocale(LC_TIME,"");
421         /* care about sorting order etc */
422         setlocale(LC_COLLATE,"");
423         /* charset */
424         setlocale(LC_CTYPE,"");
425         /* initialize charset conversions */
426         charset = charset_init();
428         /* initialize i18n support */
429 #endif
431 #ifdef ENABLE_NLS
432         setlocale(LC_MESSAGES, "");
433         bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
434 #ifdef ENABLE_LOCALE
435         bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
436 #endif
437         textdomain(GETTEXT_PACKAGE);
438 #endif
440         /* initialize options */
441         options_init();
443         /* parse command line options - 1 pass get configuration files */
444         options_parse(argc, argv);
446 #ifndef NCMPC_MINI
447         /* read configuration */
448         read_configuration();
450         /* check key bindings */
451         check_key_bindings(NULL, NULL, 0);
452 #endif
454         /* parse command line options - 2 pass */
455         options_parse(argc, argv);
457 #ifndef WIN32
458         /* setup signal behavior - SIGINT */
459         struct sigaction act;
460         sigemptyset(&act.sa_mask);
461         act.sa_flags = 0;
462         act.sa_handler = catch_sigint;
463         if (sigaction(SIGINT, &act, NULL) < 0) {
464                 perror("signal");
465                 exit(EXIT_FAILURE);
466         }
468         /* setup signal behavior - SIGTERM */
470         act.sa_handler = catch_sigint;
471         if (sigaction(SIGTERM, &act, NULL) < 0) {
472                 perror("sigaction()");
473                 exit(EXIT_FAILURE);
474         }
476         /* setup signal behavior - SIGCONT */
478         act.sa_handler = catch_sigcont;
479         if (sigaction(SIGCONT, &act, NULL) < 0) {
480                 perror("sigaction(SIGCONT)");
481                 exit(EXIT_FAILURE);
482         }
484         /* setup signal behaviour - SIGHUP*/
486         act.sa_handler = catch_sigint;
487         if (sigaction(SIGHUP, &act, NULL) < 0) {
488                 perror("sigaction(SIGHUP)");
489                 exit(EXIT_FAILURE);
490         }
492         /* setup SIGWINCH */
494         act.sa_flags = SA_RESTART;
495         act.sa_handler = catch_sigwinch;
496         if (sigaction(SIGWINCH, &act, NULL) < 0) {
497                 perror("sigaction(SIGWINCH)");
498                 exit(EXIT_FAILURE);
499         }
501         /* ignore SIGPIPE */
503         act.sa_handler = SIG_IGN;
504         if (sigaction(SIGPIPE, &act, NULL) < 0) {
505                 perror("sigaction(SIGPIPE)");
506                 exit(EXIT_FAILURE);
507         }
508 #endif
510         ncu_init();
512 #ifdef ENABLE_LYRICS_SCREEN
513         lyrics_init();
514 #endif
516         /* create mpdclient instance */
517         mpd = mpdclient_new();
519         /* initialize curses */
520         screen_init(mpd);
522         /* the main loop */
523         main_loop = g_main_loop_new(NULL, FALSE);
525         /* watch out for keyboard input */
526         keyboard_init();
528         /* watch out for lirc input */
529         ncmpc_lirc_init();
531 #ifndef WIN32
532         if (!pipe(sigwinch_pipes) &&
533                 !fcntl(sigwinch_pipes[1], F_SETFL, O_NONBLOCK)) {
534                 GIOChannel *sigwinch_channel = g_io_channel_unix_new(sigwinch_pipes[0]);
535                 g_io_add_watch(sigwinch_channel, G_IO_IN, sigwinch_event, NULL);
536                 g_io_channel_unref(sigwinch_channel);
537         }
538         else {
539                 perror("sigwinch pipe creation failed");
540                 exit(EXIT_FAILURE);
541         }
542 #endif
544         /* attempt to connect */
545         reconnect_source_id = g_timeout_add(1, timer_reconnect, NULL);
547         auto_update_timer();
549 #ifndef NCMPC_MINI
550         check_key_bindings_source_id = g_timeout_add(10000, timer_check_key_bindings, NULL);
551 #endif
553         screen_paint(mpd);
555         g_main_loop_run(main_loop);
556         g_main_loop_unref(main_loop);
558         /* cleanup */
560         cancel_seek_timer();
562         disable_update_timer();
564         if (reconnect_source_id != 0)
565                 g_source_remove(reconnect_source_id);
567 #ifndef NCMPC_MINI
568         if (check_key_bindings_source_id != 0)
569                 g_source_remove(check_key_bindings_source_id);
570 #endif
572         close(sigwinch_pipes[0]);
573         close(sigwinch_pipes[1]);
575         ncmpc_lirc_deinit();
577         screen_exit();
578 #ifndef NCMPC_MINI
579         set_xterm_title("");
580 #endif
581         printf("\n");
583         mpdclient_free(mpd);
585 #ifdef ENABLE_LYRICS_SCREEN
586         lyrics_deinit();
587 #endif
589         ncu_deinit();
590         options_deinit();
592         return 0;