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 struct mpd_status
*status
= NULL
;
78 const struct mpd_song
*song
= NULL
;
85 if (options
.xterm_title_format
&& status
&& song
&&
86 mpd_status_get_state(status
) == MPD_STATE_PLAY
)
87 strfsong(tmp
, BUFSIZE
, options
.xterm_title_format
, song
);
89 g_strlcpy(tmp
, PACKAGE
" version " VERSION
, BUFSIZE
);
91 static char title
[BUFSIZE
];
92 if (strncmp(title
, tmp
, BUFSIZE
)) {
93 g_strlcpy(title
, tmp
, BUFSIZE
);
94 set_xterm_title("%s", title
);
100 exit_and_cleanup(void)
109 mpdclient_disconnect(mpd
);
116 catch_sigint(gcc_unused
int sig
)
118 g_main_loop_quit(main_loop
);
123 catch_sigcont(gcc_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(gcc_unused GIOChannel
*source
,
140 gcc_unused GIOCondition condition
, gcc_unused gpointer data
)
143 if (1 > read(sigwinch_pipes
[0], ignoreme
, 64))
154 catch_sigwinch(gcc_unused
int sig
)
156 if (1 != write(sigwinch_pipes
[1], "", 1))
162 idle_callback(enum mpd_error error
,
163 gcc_unused
enum mpd_server_error server_error
,
164 const char *message
, enum mpd_idle events
,
165 gcc_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
);
237 settings_name(const struct mpd_settings
*settings
)
239 const char *host
= mpd_settings_get_host(settings
);
244 return g_strdup(host
);
246 unsigned port
= mpd_settings_get_port(settings
);
247 if (port
== 0 || port
== 6600)
248 return g_strdup(host
);
250 return g_strdup_printf("%s:%u", host
, port
);
254 default_settings_name(void)
256 struct mpd_settings
*settings
=
257 mpd_settings_new(options
.host
, options
.port
, 0,
258 NULL
, options
.password
);
259 if (settings
== NULL
)
260 return g_strdup(_("unknown"));
262 char *name
= settings_name(settings
);
263 mpd_settings_free(settings
);
269 * This timer is installed when the connection to the MPD server is
270 * broken. It tries to recover by reconnecting periodically.
273 timer_reconnect(gcc_unused gpointer data
)
275 assert(!mpdclient_is_connected(mpd
));
277 reconnect_source_id
= 0;
279 char *name
= default_settings_name();
280 screen_status_printf(_("Connecting to %s... [Press %s to abort]"),
281 name
, get_key_names(CMD_QUIT
, false));
285 mpdclient_disconnect(mpd
);
286 if (!mpdclient_connect(mpd
, options
.host
, options
.port
,
289 /* try again in 5 seconds */
290 reconnect_source_id
= g_timeout_add(5000,
291 timer_reconnect
, NULL
);
295 struct mpd_connection
*connection
= mpdclient_get_connection(mpd
);
298 /* quit if mpd is pre 0.14 - song id not supported by mpd */
299 if (mpd_connection_cmp_server_version(connection
, 0, 16, 0) < 0) {
300 const unsigned *version
=
301 mpd_connection_get_server_version(connection
);
302 screen_status_printf(_("Error: MPD version %d.%d.%d is too old (%s needed)"),
303 version
[0], version
[1], version
[2],
305 mpdclient_disconnect(mpd
);
308 /* try again after 30 seconds */
309 reconnect_source_id
= g_timeout_add(30000,
310 timer_reconnect
, NULL
);
315 mpd
->source
= mpd_glib_new(connection
,
318 screen_status_clear_message();
321 /* update immediately */
322 mpd
->events
= MPD_IDLE_ALL
;
332 check_reconnect(void)
334 if (!mpdclient_is_connected(mpd
) && reconnect_source_id
== 0)
335 /* reconnect when the connection is lost */
336 reconnect_source_id
= g_timeout_add(1000, timer_reconnect
,
341 * This function is called by the gidle.c library when MPD sends us an
342 * idle event (or when the connection dies).
345 idle_callback(enum mpd_error error
, enum mpd_server_error server_error
,
346 const char *message
, enum mpd_idle events
,
349 struct mpdclient
*c
= ctx
;
353 assert(mpdclient_is_connected(c
));
355 if (error
!= MPD_ERROR_SUCCESS
) {
356 if (error
== MPD_ERROR_SERVER
&&
357 server_error
== MPD_SERVER_ERROR_UNKNOWN_CMD
) {
358 /* the "idle" command is not supported - fall
359 back to timer based polling */
360 mpd_glib_free(c
->source
);
367 if (error
== MPD_ERROR_SERVER
)
368 message
= allocated
= utf8_to_locale(message
);
371 screen_status_message(message
);
376 mpdclient_disconnect(c
);
378 reconnect_source_id
= g_timeout_add(1000, timer_reconnect
,
387 if (options
.enable_xterm_title
)
388 update_xterm_title();
394 mpdclient_put_connection(c
);
400 timer_mpd_update(gcc_unused gpointer data
)
404 if (should_enable_update_timer())
407 update_source_id
= 0;
412 void begin_input_event(void)
416 void end_input_event(void)
421 mpdclient_put_connection(mpd
);
426 int do_input_event(command_t cmd
)
428 if (cmd
== CMD_QUIT
) {
429 g_main_loop_quit(main_loop
);
433 screen_cmd(mpd
, cmd
);
435 if (cmd
== CMD_VOLUME_UP
|| cmd
== CMD_VOLUME_DOWN
)
436 /* make sure we don't update the volume yet */
437 disable_update_timer();
443 keyboard_event(gcc_unused GIOChannel
*source
,
444 gcc_unused GIOCondition condition
,
445 gcc_unused gpointer data
)
449 command_t cmd
= get_keyboard_command();
451 if (do_input_event(cmd
) != 0)
460 * Check the configured key bindings for errors, and display a status
461 * message every 10 seconds.
464 timer_check_key_bindings(gcc_unused gpointer data
)
468 if (check_key_bindings(NULL
, buf
, sizeof(buf
))) {
469 /* no error: disable this timer for the rest of this
471 check_key_bindings_source_id
= 0;
475 #ifdef ENABLE_KEYDEF_SCREEN
477 g_strlcat(buf
, " (", sizeof(buf
));
478 /* to translators: a key was bound twice in the key editor,
479 and this is a hint for the user what to press to correct
482 g_snprintf(comment
, sizeof(comment
), _("press %s for the key editor"),
483 get_key_names(CMD_SCREEN_KEYDEF
, false));
484 g_strlcat(buf
, comment
, sizeof(buf
));
485 g_strlcat(buf
, ")", sizeof(buf
));
488 screen_status_printf("%s", buf
);
496 main(int argc
, const char *argv
[])
502 const char *charset
= NULL
;
503 /* time and date formatting */
504 setlocale(LC_TIME
,"");
505 /* care about sorting order etc */
506 setlocale(LC_COLLATE
,"");
508 setlocale(LC_CTYPE
,"");
509 /* initialize charset conversions */
510 charset
= charset_init();
512 /* initialize i18n support */
516 setlocale(LC_MESSAGES
, "");
517 bindtextdomain(GETTEXT_PACKAGE
, LOCALE_DIR
);
519 bind_textdomain_codeset(GETTEXT_PACKAGE
, charset
);
521 textdomain(GETTEXT_PACKAGE
);
524 /* initialize options */
527 /* parse command line options - 1 pass get configuration files */
528 options_parse(argc
, argv
);
531 /* read configuration */
532 read_configuration();
534 /* check key bindings */
535 check_key_bindings(NULL
, NULL
, 0);
538 /* parse command line options - 2 pass */
539 options_parse(argc
, argv
);
542 /* setup signal behavior - SIGINT */
543 struct sigaction act
;
544 sigemptyset(&act
.sa_mask
);
546 act
.sa_handler
= catch_sigint
;
547 if (sigaction(SIGINT
, &act
, NULL
) < 0) {
552 /* setup signal behavior - SIGTERM */
554 act
.sa_handler
= catch_sigint
;
555 if (sigaction(SIGTERM
, &act
, NULL
) < 0) {
556 perror("sigaction()");
560 /* setup signal behavior - SIGCONT */
562 act
.sa_handler
= catch_sigcont
;
563 if (sigaction(SIGCONT
, &act
, NULL
) < 0) {
564 perror("sigaction(SIGCONT)");
568 /* setup signal behaviour - SIGHUP*/
570 act
.sa_handler
= catch_sigint
;
571 if (sigaction(SIGHUP
, &act
, NULL
) < 0) {
572 perror("sigaction(SIGHUP)");
578 act
.sa_flags
= SA_RESTART
;
579 act
.sa_handler
= catch_sigwinch
;
580 if (sigaction(SIGWINCH
, &act
, NULL
) < 0) {
581 perror("sigaction(SIGWINCH)");
587 act
.sa_handler
= SIG_IGN
;
588 if (sigaction(SIGPIPE
, &act
, NULL
) < 0) {
589 perror("sigaction(SIGPIPE)");
596 #ifdef ENABLE_LYRICS_SCREEN
600 /* create mpdclient instance */
601 mpd
= mpdclient_new();
603 /* initialize curses */
607 main_loop
= g_main_loop_new(NULL
, FALSE
);
609 /* watch out for keyboard input */
610 GIOChannel
*keyboard_channel
= g_io_channel_unix_new(STDIN_FILENO
);
611 g_io_add_watch(keyboard_channel
, G_IO_IN
, keyboard_event
, NULL
);
614 /* watch out for lirc input */
615 int lirc_socket
= ncmpc_lirc_open();
616 GIOChannel
*lirc_channel
= NULL
;
617 if (lirc_socket
>= 0) {
618 lirc_channel
= g_io_channel_unix_new(lirc_socket
);
619 g_io_add_watch(lirc_channel
, G_IO_IN
, lirc_event
, NULL
);
624 GIOChannel
*sigwinch_channel
= NULL
;
625 if (!pipe(sigwinch_pipes
) &&
626 !fcntl(sigwinch_pipes
[1], F_SETFL
, O_NONBLOCK
)) {
627 sigwinch_channel
= g_io_channel_unix_new(sigwinch_pipes
[0]);
628 g_io_add_watch(sigwinch_channel
, G_IO_IN
, sigwinch_event
, NULL
);
631 perror("sigwinch pipe creation failed");
636 /* attempt to connect */
637 reconnect_source_id
= g_timeout_add(1, timer_reconnect
, NULL
);
642 check_key_bindings_source_id
= g_timeout_add(10000, timer_check_key_bindings
, NULL
);
647 g_main_loop_run(main_loop
);
653 disable_update_timer();
655 if (reconnect_source_id
!= 0)
656 g_source_remove(reconnect_source_id
);
659 if (check_key_bindings_source_id
!= 0)
660 g_source_remove(check_key_bindings_source_id
);
663 g_main_loop_unref(main_loop
);
664 g_io_channel_unref(keyboard_channel
);
665 g_io_channel_unref(sigwinch_channel
);
666 close(sigwinch_pipes
[0]);
667 close(sigwinch_pipes
[1]);
670 if (lirc_socket
>= 0)
671 g_io_channel_unref(lirc_channel
);
677 #ifdef ENABLE_LYRICS_SCREEN