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_message.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
);
116 catch_sigint(G_GNUC_UNUSED
int sig
)
118 g_main_loop_quit(main_loop
);
123 catch_sigcont(G_GNUC_UNUSED
int sig
)
125 char irrelevant
= 'a';
126 if (1 != write(sigwinch_pipes
[1], &irrelevant
, 1))
133 def_prog_mode(); /* save the tty modes */
134 endwin(); /* end curses mode temporarily */
135 kill(0, SIGSTOP
); /* issue SIGSTOP */
139 sigwinch_event(G_GNUC_UNUSED GIOChannel
*source
,
140 G_GNUC_UNUSED GIOCondition condition
, G_GNUC_UNUSED gpointer data
)
143 if (1 > read(sigwinch_pipes
[0], ignoreme
, 64))
154 catch_sigwinch(G_GNUC_UNUSED
int sig
)
156 char irrelevant
= 'a';
157 if (1 != write(sigwinch_pipes
[1], &irrelevant
, 1))
162 idle_callback(enum mpd_error error
,
163 G_GNUC_UNUSED
enum mpd_server_error server_error
,
164 const char *message
, enum mpd_idle events
,
165 G_GNUC_UNUSED
void *ctx
);
168 timer_mpd_update(gpointer data
);
171 enable_update_timer(void)
173 if (update_source_id
!= 0)
176 update_source_id
= g_timeout_add(update_interval
,
177 timer_mpd_update
, NULL
);
181 disable_update_timer(void)
183 if (update_source_id
== 0)
186 g_source_remove(update_source_id
);
187 update_source_id
= 0;
191 should_enable_update_timer(void)
193 return (mpdclient_is_connected(mpd
) &&
194 (mpd
->source
== NULL
|| /* "idle" not supported */
195 (mpd
->status
!= NULL
&&
196 mpd_status_get_state(mpd
->status
) == MPD_STATE_PLAY
)))
198 || options
.display_time
204 auto_update_timer(void)
206 if (should_enable_update_timer())
207 enable_update_timer();
209 disable_update_timer();
213 check_reconnect(void);
218 if (mpdclient_is_connected(mpd
) &&
219 (mpd
->source
== NULL
|| mpd
->events
!= 0 ||
220 (mpd
->status
!= NULL
&&
221 mpd_status_get_state(mpd
->status
) == MPD_STATE_PLAY
)))
222 mpdclient_update(mpd
);
225 if (options
.enable_xterm_title
)
226 update_xterm_title();
232 mpdclient_put_connection(mpd
);
236 #if LIBMPDCLIENT_CHECK_VERSION(2,4,0)
239 settings_name(const struct mpd_settings
*settings
)
241 const char *host
= mpd_settings_get_host(settings
);
246 return g_strdup(host
);
248 unsigned port
= mpd_settings_get_port(settings
);
249 if (port
== 0 || port
== 6600)
250 return g_strdup(host
);
252 return g_strdup_printf("%s:%u", host
, port
);
258 default_settings_name(void)
260 #if LIBMPDCLIENT_CHECK_VERSION(2,4,0)
261 struct mpd_settings
*settings
=
262 mpd_settings_new(options
.host
, options
.port
, 0,
263 NULL
, options
.password
);
264 if (settings
== NULL
)
265 return g_strdup(_("unknown"));
267 char *name
= settings_name(settings
);
268 mpd_settings_free(settings
);
272 return g_strdup(options
.host
);
277 connection_settings_name(const struct mpd_connection
*connection
)
279 #if LIBMPDCLIENT_CHECK_VERSION(2,4,0)
280 const struct mpd_settings
*settings
=
281 mpd_connection_get_settings(connection
);
282 if (settings
== NULL
)
283 return g_strdup(_("unknown"));
285 return settings_name(settings
);
290 * localhost is actually not correct, we only know that
291 * mpd_connection_new() has connected to the "default host".
293 const char *name
= options
.host
?: "localhost";
294 return g_strdup(name
);
299 * This timer is installed when the connection to the MPD server is
300 * broken. It tries to recover by reconnecting periodically.
303 timer_reconnect(G_GNUC_UNUSED gpointer data
)
306 struct mpd_connection
*connection
;
308 assert(!mpdclient_is_connected(mpd
));
310 reconnect_source_id
= 0;
312 char *name
= default_settings_name();
313 screen_status_printf(_("Connecting to %s... [Press %s to abort]"),
314 name
, get_key_names(CMD_QUIT
,0) );
318 mpdclient_disconnect(mpd
);
319 success
= mpdclient_connect(mpd
,
320 options
.host
, options
.port
,
324 /* try again in 5 seconds */
325 reconnect_source_id
= g_timeout_add(5000,
326 timer_reconnect
, NULL
);
330 connection
= mpdclient_get_connection(mpd
);
333 /* quit if mpd is pre 0.11.0 - song id not supported by mpd */
334 if (mpd_connection_cmp_server_version(connection
, 0, 12, 0) < 0) {
335 const unsigned *version
=
336 mpd_connection_get_server_version(connection
);
337 screen_status_printf(_("Error: MPD version %d.%d.%d is to old (%s needed)"),
338 version
[0], version
[1], version
[2],
340 mpdclient_disconnect(mpd
);
343 /* try again after 30 seconds */
344 reconnect_source_id
= g_timeout_add(30000,
345 timer_reconnect
, NULL
);
350 if (mpd_connection_cmp_server_version(connection
,
352 mpd
->source
= mpd_glib_new(connection
,
355 name
= connection_settings_name(connection
);
356 screen_status_printf(_("Connected to %s"), name
);
360 /* update immediately */
361 mpd
->events
= MPD_IDLE_DATABASE
|MPD_IDLE_STORED_PLAYLIST
|
362 MPD_IDLE_QUEUE
|MPD_IDLE_PLAYER
|MPD_IDLE_MIXER
|MPD_IDLE_OUTPUT
|
363 MPD_IDLE_OPTIONS
|MPD_IDLE_UPDATE
;
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_get_connection(c
) != NULL
);
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
, 0));
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
[])
545 struct sigaction act
;
550 const char *charset
= NULL
;
552 GIOChannel
*keyboard_channel
;
555 GIOChannel
*lirc_channel
= NULL
;
557 GIOChannel
*sigwinch_channel
= NULL
;
560 /* time and date formatting */
561 setlocale(LC_TIME
,"");
562 /* care about sorting order etc */
563 setlocale(LC_COLLATE
,"");
565 setlocale(LC_CTYPE
,"");
566 /* initialize charset conversions */
567 charset
= charset_init();
569 /* initialize i18n support */
573 setlocale(LC_MESSAGES
, "");
574 bindtextdomain(GETTEXT_PACKAGE
, LOCALE_DIR
);
576 bind_textdomain_codeset(GETTEXT_PACKAGE
, charset
);
578 textdomain(GETTEXT_PACKAGE
);
581 /* initialize options */
584 /* parse command line options - 1 pass get configuration files */
585 options_parse(argc
, argv
);
588 /* read configuration */
589 read_configuration();
591 /* check key bindings */
592 check_key_bindings(NULL
, NULL
, 0);
595 /* parse command line options - 2 pass */
596 options_parse(argc
, argv
);
598 /* setup signal behavior - SIGINT */
599 sigemptyset(&act
.sa_mask
);
601 act
.sa_handler
= catch_sigint
;
602 if (sigaction(SIGINT
, &act
, NULL
) < 0) {
607 /* setup signal behavior - SIGTERM */
609 act
.sa_handler
= catch_sigint
;
610 if (sigaction(SIGTERM
, &act
, NULL
) < 0) {
611 perror("sigaction()");
615 /* setup signal behavior - SIGCONT */
617 act
.sa_handler
= catch_sigcont
;
618 if (sigaction(SIGCONT
, &act
, NULL
) < 0) {
619 perror("sigaction(SIGCONT)");
623 /* setup signal behaviour - SIGHUP*/
625 act
.sa_handler
= catch_sigint
;
626 if (sigaction(SIGHUP
, &act
, NULL
) < 0) {
627 perror("sigaction(SIGHUP)");
633 act
.sa_flags
= SA_RESTART
;
634 act
.sa_handler
= catch_sigwinch
;
635 if (sigaction(SIGWINCH
, &act
, NULL
) < 0) {
636 perror("sigaction(SIGWINCH)");
642 act
.sa_handler
= SIG_IGN
;
643 if (sigaction(SIGPIPE
, &act
, NULL
) < 0) {
644 perror("sigaction(SIGPIPE)");
650 #ifdef ENABLE_LYRICS_SCREEN
654 /* create mpdclient instance */
655 mpd
= mpdclient_new();
657 /* initialize curses */
661 main_loop
= g_main_loop_new(NULL
, FALSE
);
663 /* watch out for keyboard input */
664 keyboard_channel
= g_io_channel_unix_new(STDIN_FILENO
);
665 g_io_add_watch(keyboard_channel
, G_IO_IN
, keyboard_event
, NULL
);
668 /* watch out for lirc input */
669 lirc_socket
= ncmpc_lirc_open();
670 if (lirc_socket
>= 0) {
671 lirc_channel
= g_io_channel_unix_new(lirc_socket
);
672 g_io_add_watch(lirc_channel
, G_IO_IN
, lirc_event
, NULL
);
676 if (!pipe(sigwinch_pipes
) &&
677 !fcntl(sigwinch_pipes
[1], F_SETFL
, O_NONBLOCK
)) {
678 sigwinch_channel
= g_io_channel_unix_new(sigwinch_pipes
[0]);
679 g_io_add_watch(sigwinch_channel
, G_IO_IN
, sigwinch_event
, NULL
);
682 perror("sigwinch pipe creation failed");
686 /* attempt to connect */
687 reconnect_source_id
= g_timeout_add(1, timer_reconnect
, NULL
);
692 check_key_bindings_source_id
= g_timeout_add(10000, timer_check_key_bindings
, NULL
);
697 g_main_loop_run(main_loop
);
703 disable_update_timer();
705 if (reconnect_source_id
!= 0)
706 g_source_remove(reconnect_source_id
);
709 if (check_key_bindings_source_id
!= 0)
710 g_source_remove(check_key_bindings_source_id
);
713 g_main_loop_unref(main_loop
);
714 g_io_channel_unref(keyboard_channel
);
715 g_io_channel_unref(sigwinch_channel
);
716 close(sigwinch_pipes
[0]);
717 close(sigwinch_pipes
[1]);
720 if (lirc_socket
>= 0)
721 g_io_channel_unref(lirc_channel
);
727 #ifdef ENABLE_LYRICS_SCREEN