1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2010 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
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.
22 #include "mpdclient.h"
29 #include "screen_utils.h"
30 #include "screen_status.h"
33 #include "player_command.h"
39 #ifdef ENABLE_LYRICS_SCREEN
47 #include <mpd/client.h>
59 /* time between mpd updates [ms] */
60 static const guint update_interval
= 500;
64 static struct mpdclient
*mpd
= NULL
;
65 static GMainLoop
*main_loop
;
66 static guint reconnect_source_id
, update_source_id
;
67 static int sigwinch_pipes
[2];
70 static guint check_key_bindings_source_id
;
75 update_xterm_title(void)
77 static char title
[BUFSIZE
];
79 struct mpd_status
*status
= NULL
;
80 const struct mpd_song
*song
= NULL
;
87 if (options
.xterm_title_format
&& status
&& song
&&
88 mpd_status_get_state(status
) == MPD_STATE_PLAY
)
89 strfsong(tmp
, BUFSIZE
, options
.xterm_title_format
, song
);
91 g_strlcpy(tmp
, PACKAGE
" version " VERSION
, BUFSIZE
);
93 if (strncmp(title
, tmp
, BUFSIZE
)) {
94 g_strlcpy(title
, tmp
, BUFSIZE
);
95 set_xterm_title("%s", title
);
101 exit_and_cleanup(void)
110 mpdclient_disconnect(mpd
);
117 catch_sigint(G_GNUC_UNUSED
int sig
)
119 g_main_loop_quit(main_loop
);
124 catch_sigcont(G_GNUC_UNUSED
int sig
)
126 char irrelevant
= 'a';
127 if (1 != write(sigwinch_pipes
[1], &irrelevant
, 1))
134 def_prog_mode(); /* save the tty modes */
135 endwin(); /* end curses mode temporarily */
136 kill(0, SIGSTOP
); /* issue SIGSTOP */
140 sigwinch_event(G_GNUC_UNUSED GIOChannel
*source
,
141 G_GNUC_UNUSED GIOCondition condition
, G_GNUC_UNUSED gpointer data
)
144 if (1 > read(sigwinch_pipes
[0], ignoreme
, 64))
155 catch_sigwinch(G_GNUC_UNUSED
int sig
)
157 char irrelevant
= 'a';
158 if (1 != write(sigwinch_pipes
[1], &irrelevant
, 1))
164 idle_callback(enum mpd_error error
,
165 G_GNUC_UNUSED
enum mpd_server_error server_error
,
166 const char *message
, enum mpd_idle events
,
167 G_GNUC_UNUSED
void *ctx
);
170 timer_mpd_update(gpointer data
);
173 enable_update_timer(void)
175 if (update_source_id
!= 0)
178 update_source_id
= g_timeout_add(update_interval
,
179 timer_mpd_update
, NULL
);
183 disable_update_timer(void)
185 if (update_source_id
== 0)
188 g_source_remove(update_source_id
);
189 update_source_id
= 0;
193 should_enable_update_timer(void)
195 return (mpdclient_is_connected(mpd
) &&
196 (mpd
->source
== NULL
|| /* "idle" not supported */
197 (mpd
->status
!= NULL
&&
198 mpd_status_get_state(mpd
->status
) == MPD_STATE_PLAY
)))
200 || options
.display_time
206 auto_update_timer(void)
208 if (should_enable_update_timer())
209 enable_update_timer();
211 disable_update_timer();
215 check_reconnect(void);
220 if (mpdclient_is_connected(mpd
) &&
221 (mpd
->source
== NULL
|| mpd
->events
!= 0 ||
222 (mpd
->status
!= NULL
&&
223 mpd_status_get_state(mpd
->status
) == MPD_STATE_PLAY
)))
224 mpdclient_update(mpd
);
227 if (options
.enable_xterm_title
)
228 update_xterm_title();
234 mpdclient_put_connection(mpd
);
238 #if LIBMPDCLIENT_CHECK_VERSION(2,4,0)
241 settings_name(const struct mpd_settings
*settings
)
243 const char *host
= mpd_settings_get_host(settings
);
248 return g_strdup(host
);
250 unsigned port
= mpd_settings_get_port(settings
);
251 if (port
== 0 || port
== 6600)
252 return g_strdup(host
);
254 return g_strdup_printf("%s:%u", host
, port
);
260 default_settings_name(void)
262 #if LIBMPDCLIENT_CHECK_VERSION(2,4,0)
263 struct mpd_settings
*settings
=
264 mpd_settings_new(options
.host
, options
.port
, 0,
265 NULL
, options
.password
);
266 if (settings
== NULL
)
267 return g_strdup(_("unknown"));
269 char *name
= settings_name(settings
);
270 mpd_settings_free(settings
);
275 * localhost is actually not correct, we only know that
276 * mpd_connection_new() has connected to the "default host".
278 const char *name
= options
.host
?: "localhost";
279 return g_strdup(name
);
284 connection_settings_name(const struct mpd_connection
*connection
)
286 #if LIBMPDCLIENT_CHECK_VERSION(2,4,0)
287 const struct mpd_settings
*settings
=
288 mpd_connection_get_settings(connection
);
289 if (settings
== NULL
)
290 return g_strdup(_("unknown"));
292 return settings_name(settings
);
296 return default_settings_name();
301 * This timer is installed when the connection to the MPD server is
302 * broken. It tries to recover by reconnecting periodically.
305 timer_reconnect(G_GNUC_UNUSED gpointer data
)
308 struct mpd_connection
*connection
;
310 assert(!mpdclient_is_connected(mpd
));
312 reconnect_source_id
= 0;
314 char *name
= default_settings_name();
315 screen_status_printf(_("Connecting to %s... [Press %s to abort]"),
316 name
, get_key_names(CMD_QUIT
, false));
320 mpdclient_disconnect(mpd
);
321 success
= mpdclient_connect(mpd
,
322 options
.host
, options
.port
,
326 /* try again in 5 seconds */
327 reconnect_source_id
= g_timeout_add(5000,
328 timer_reconnect
, NULL
);
332 connection
= mpdclient_get_connection(mpd
);
335 /* quit if mpd is pre 0.11.0 - song id not supported by mpd */
336 if (mpd_connection_cmp_server_version(connection
, 0, 12, 0) < 0) {
337 const unsigned *version
=
338 mpd_connection_get_server_version(connection
);
339 screen_status_printf(_("Error: MPD version %d.%d.%d is to old (%s needed)"),
340 version
[0], version
[1], version
[2],
342 mpdclient_disconnect(mpd
);
345 /* try again after 30 seconds */
346 reconnect_source_id
= g_timeout_add(30000,
347 timer_reconnect
, NULL
);
352 if (mpd_connection_cmp_server_version(connection
,
354 mpd
->source
= mpd_glib_new(connection
,
357 name
= connection_settings_name(connection
);
358 screen_status_printf(_("Connected to %s"), name
);
362 /* update immediately */
363 mpd
->events
= MPD_IDLE_ALL
;
373 check_reconnect(void)
375 if (!mpdclient_is_connected(mpd
) && reconnect_source_id
== 0)
376 /* reconnect when the connection is lost */
377 reconnect_source_id
= g_timeout_add(1000, timer_reconnect
,
382 * This function is called by the gidle.c library when MPD sends us an
383 * idle event (or when the connection dies).
386 idle_callback(enum mpd_error error
, enum mpd_server_error server_error
,
387 const char *message
, enum mpd_idle events
,
390 struct mpdclient
*c
= ctx
;
394 assert(mpdclient_is_connected(c
));
396 if (error
!= MPD_ERROR_SUCCESS
) {
399 if (error
== MPD_ERROR_SERVER
&&
400 server_error
== MPD_SERVER_ERROR_UNKNOWN_CMD
) {
401 /* the "idle" command is not supported - fall
402 back to timer based polling */
403 mpd_glib_free(c
->source
);
409 if (error
== MPD_ERROR_SERVER
)
410 message
= allocated
= utf8_to_locale(message
);
413 screen_status_message(message
);
418 mpdclient_disconnect(c
);
420 reconnect_source_id
= g_timeout_add(1000, timer_reconnect
,
429 if (options
.enable_xterm_title
)
430 update_xterm_title();
436 mpdclient_put_connection(c
);
442 timer_mpd_update(G_GNUC_UNUSED gpointer data
)
446 if (should_enable_update_timer())
449 update_source_id
= 0;
454 void begin_input_event(void)
458 void end_input_event(void)
463 mpdclient_put_connection(mpd
);
468 int do_input_event(command_t cmd
)
470 if (cmd
== CMD_QUIT
) {
471 g_main_loop_quit(main_loop
);
475 screen_cmd(mpd
, cmd
);
477 if (cmd
== CMD_VOLUME_UP
|| cmd
== CMD_VOLUME_DOWN
)
478 /* make sure we don't update the volume yet */
479 disable_update_timer();
485 keyboard_event(G_GNUC_UNUSED GIOChannel
*source
,
486 G_GNUC_UNUSED GIOCondition condition
,
487 G_GNUC_UNUSED gpointer data
)
493 if ((cmd
=get_keyboard_command()) != CMD_NONE
)
494 if (do_input_event(cmd
) != 0)
503 * Check the configured key bindings for errors, and display a status
504 * message every 10 seconds.
507 timer_check_key_bindings(G_GNUC_UNUSED gpointer data
)
510 #ifdef ENABLE_KEYDEF_SCREEN
515 key_error
= check_key_bindings(NULL
, buf
, sizeof(buf
));
517 /* no error: disable this timer for the rest of this
519 check_key_bindings_source_id
= 0;
523 #ifdef ENABLE_KEYDEF_SCREEN
525 g_strlcat(buf
, " (", sizeof(buf
));
526 /* to translators: a key was bound twice in the key editor,
527 and this is a hint for the user what to press to correct
529 g_snprintf(comment
, sizeof(comment
), _("press %s for the key editor"),
530 get_key_names(CMD_SCREEN_KEYDEF
, false));
531 g_strlcat(buf
, comment
, sizeof(buf
));
532 g_strlcat(buf
, ")", sizeof(buf
));
535 screen_status_printf("%s", buf
);
543 main(int argc
, const char *argv
[])
546 struct sigaction act
;
552 const char *charset
= NULL
;
554 GIOChannel
*keyboard_channel
;
557 GIOChannel
*lirc_channel
= NULL
;
559 GIOChannel
*sigwinch_channel
= NULL
;
562 /* time and date formatting */
563 setlocale(LC_TIME
,"");
564 /* care about sorting order etc */
565 setlocale(LC_COLLATE
,"");
567 setlocale(LC_CTYPE
,"");
568 /* initialize charset conversions */
569 charset
= charset_init();
571 /* initialize i18n support */
575 setlocale(LC_MESSAGES
, "");
576 bindtextdomain(GETTEXT_PACKAGE
, LOCALE_DIR
);
578 bind_textdomain_codeset(GETTEXT_PACKAGE
, charset
);
580 textdomain(GETTEXT_PACKAGE
);
583 /* initialize options */
586 /* parse command line options - 1 pass get configuration files */
587 options_parse(argc
, argv
);
590 /* read configuration */
591 read_configuration();
593 /* check key bindings */
594 check_key_bindings(NULL
, NULL
, 0);
597 /* parse command line options - 2 pass */
598 options_parse(argc
, argv
);
601 /* setup signal behavior - SIGINT */
602 sigemptyset(&act
.sa_mask
);
604 act
.sa_handler
= catch_sigint
;
605 if (sigaction(SIGINT
, &act
, NULL
) < 0) {
610 /* setup signal behavior - SIGTERM */
612 act
.sa_handler
= catch_sigint
;
613 if (sigaction(SIGTERM
, &act
, NULL
) < 0) {
614 perror("sigaction()");
618 /* setup signal behavior - SIGCONT */
620 act
.sa_handler
= catch_sigcont
;
621 if (sigaction(SIGCONT
, &act
, NULL
) < 0) {
622 perror("sigaction(SIGCONT)");
626 /* setup signal behaviour - SIGHUP*/
628 act
.sa_handler
= catch_sigint
;
629 if (sigaction(SIGHUP
, &act
, NULL
) < 0) {
630 perror("sigaction(SIGHUP)");
636 act
.sa_flags
= SA_RESTART
;
637 act
.sa_handler
= catch_sigwinch
;
638 if (sigaction(SIGWINCH
, &act
, NULL
) < 0) {
639 perror("sigaction(SIGWINCH)");
645 act
.sa_handler
= SIG_IGN
;
646 if (sigaction(SIGPIPE
, &act
, NULL
) < 0) {
647 perror("sigaction(SIGPIPE)");
654 #ifdef ENABLE_LYRICS_SCREEN
658 /* create mpdclient instance */
659 mpd
= mpdclient_new();
661 /* initialize curses */
665 main_loop
= g_main_loop_new(NULL
, FALSE
);
667 /* watch out for keyboard input */
668 keyboard_channel
= g_io_channel_unix_new(STDIN_FILENO
);
669 g_io_add_watch(keyboard_channel
, G_IO_IN
, keyboard_event
, NULL
);
672 /* watch out for lirc input */
673 lirc_socket
= ncmpc_lirc_open();
674 if (lirc_socket
>= 0) {
675 lirc_channel
= g_io_channel_unix_new(lirc_socket
);
676 g_io_add_watch(lirc_channel
, G_IO_IN
, lirc_event
, NULL
);
681 if (!pipe(sigwinch_pipes
) &&
682 !fcntl(sigwinch_pipes
[1], F_SETFL
, O_NONBLOCK
)) {
683 sigwinch_channel
= g_io_channel_unix_new(sigwinch_pipes
[0]);
684 g_io_add_watch(sigwinch_channel
, G_IO_IN
, sigwinch_event
, NULL
);
687 perror("sigwinch pipe creation failed");
692 /* attempt to connect */
693 reconnect_source_id
= g_timeout_add(1, timer_reconnect
, NULL
);
698 check_key_bindings_source_id
= g_timeout_add(10000, timer_check_key_bindings
, NULL
);
703 g_main_loop_run(main_loop
);
709 disable_update_timer();
711 if (reconnect_source_id
!= 0)
712 g_source_remove(reconnect_source_id
);
715 if (check_key_bindings_source_id
!= 0)
716 g_source_remove(check_key_bindings_source_id
);
719 g_main_loop_unref(main_loop
);
720 g_io_channel_unref(keyboard_channel
);
721 g_io_channel_unref(sigwinch_channel
);
722 close(sigwinch_pipes
[0]);
723 close(sigwinch_pipes
[1]);
726 if (lirc_socket
>= 0)
727 g_io_channel_unref(lirc_channel
);
733 #ifdef ENABLE_LYRICS_SCREEN