From f0aa88e5c18de0b1e4f5d85dd5c3ae5d9be76bc3 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 18 Sep 2008 01:49:53 +0200 Subject: [PATCH] use the glib main event loop This big patch replaces our custom main loop with the event based glib main loop. This has several advantages: we can make all the tiny code bits in the main loop independent from each others, we can add additional file descriptors for polling (e.g. the mpdclient socket). We don't need the ncurses timeout() anymore, because glib will poll stdin for us. --- src/command.c | 16 +--- src/command.h | 1 - src/main.c | 226 ++++++++++++++++++++++++++++++--------------- src/screen.c | 2 - src/screen_utils.c | 2 - 5 files changed, 157 insertions(+), 90 deletions(-) diff --git a/src/command.c b/src/command.c index 881df27..151d465 100644 --- a/src/command.c +++ b/src/command.c @@ -30,6 +30,7 @@ #include #include #include +#include #undef DEBUG_KEYS @@ -420,23 +421,18 @@ my_wgetch(WINDOW *w) sigstop(); /* handle SIGINT (Ctrl-C) */ if (c == 3) - exit(EXIT_SUCCESS); + kill(getpid(), SIGTERM); #endif return c; } command_t -get_keyboard_command_with_timeout(int ms) +get_keyboard_command(void) { int key; - if (ms != SCREEN_TIMEOUT) - timeout(ms); key = my_wgetch(stdscr); - if (ms != SCREEN_TIMEOUT) - timeout(SCREEN_TIMEOUT); - if (key == ERR) return CMD_NONE; @@ -448,12 +444,6 @@ get_keyboard_command_with_timeout(int ms) return get_key_command(key); } -command_t -get_keyboard_command(void) -{ - return get_keyboard_command_with_timeout(SCREEN_TIMEOUT); -} - int assign_keys(command_t command, int keys[MAX_COMMAND_KEYS]) { diff --git a/src/command.h b/src/command.h index 9e3b839..4b20a59 100644 --- a/src/command.h +++ b/src/command.h @@ -100,6 +100,5 @@ int assign_keys(command_t command, int keys[MAX_COMMAND_KEYS]); int my_wgetch(WINDOW *w); command_t get_keyboard_command(void); -command_t get_keyboard_command_with_timeout(int milliseconds); #endif diff --git a/src/main.c b/src/main.c index cbb4735..f29ce67 100644 --- a/src/main.c +++ b/src/main.c @@ -38,9 +38,12 @@ #define BUFSIZE 1024 +static const guint idle_interval = 500; + static mpdclient_t *mpd = NULL; static gboolean connected = FALSE; -static GTimer *timer = NULL; +static GMainLoop *main_loop; +static guint reconnect_source_id, idle_source_id, update_source_id; static const gchar * error_msg(const gchar *msg) @@ -119,16 +122,12 @@ exit_and_cleanup(void) g_free(options.list_format); g_free(options.status_format); g_free(options.scroll_sep); - - if (timer) - g_timer_destroy(timer); } static void catch_sigint(mpd_unused int sig) { - printf("\n%s\n", _("Exiting...")); - exit(EXIT_SUCCESS); + g_main_loop_quit(main_loop); } @@ -169,12 +168,144 @@ D(const char *format, ...) } #endif +static gboolean +timer_mpd_update(gpointer data); + +/** + * This timer is installed when the connection to the MPD server is + * broken. It tries to recover by reconnecting periodically. + */ +static gboolean +timer_reconnect(mpd_unused gpointer data) +{ + int ret; + + if (connected) + return FALSE; + + screen_status_printf(_("Connecting to %s... [Press %s to abort]"), + options.host, get_key_names(CMD_QUIT,0) ); + doupdate(); + + mpdclient_disconnect(mpd); + ret = mpdclient_connect(mpd, + options.host, options.port, + 1.5, + options.password); + if (ret != 0) { + /* try again in 5 seconds */ + g_timeout_add(5000, timer_reconnect, NULL); + return FALSE; + } + + /* quit if mpd is pre 0.11.0 - song id not supported by mpd */ + if (MPD_VERSION_LT(mpd, 0, 11, 0)) { + screen_status_printf(_("Error: MPD version %d.%d.%d is to old (0.11.0 needed).\n"), + mpd->connection->version[0], + mpd->connection->version[1], + mpd->connection->version[2]); + mpdclient_disconnect(mpd); + doupdate(); + + /* try again after 30 seconds */ + g_timeout_add(30000, timer_reconnect, NULL); + return FALSE; + } + + screen_status_printf(_("Connected to %s!"), options.host); + doupdate(); + + connected = TRUE; + + /* update immediately */ + g_timeout_add(1, timer_mpd_update, GINT_TO_POINTER(FALSE)); + + reconnect_source_id = 0; + return FALSE; + +} + +static gboolean +timer_mpd_update(gpointer data) +{ + if (connected) + mpdclient_update(mpd); + else if (reconnect_source_id == 0) + reconnect_source_id = g_timeout_add(1000, timer_reconnect, + NULL); + + if (options.enable_xterm_title) + update_xterm_title(); + + screen_update(mpd); + + return GPOINTER_TO_INT(data); +} + +/** + * This idle timer is invoked when the user hasn't typed a key for + * 500ms. It is used for delayed seeking. + */ +static gboolean +timer_idle(mpd_unused gpointer data) +{ + screen_idle(mpd); + return TRUE; +} + +static gboolean +keyboard_event(mpd_unused GIOChannel *source, + mpd_unused GIOCondition condition, mpd_unused gpointer data) +{ + command_t cmd; + + /* remove the idle timeout; add it later with fresh interval */ + g_source_remove(idle_source_id); + + if ((cmd=get_keyboard_command()) != CMD_NONE) { + screen_cmd(mpd, cmd); + + if (cmd == CMD_VOLUME_UP || cmd == CMD_VOLUME_DOWN) { + /* make sure we dont update the volume yet */ + g_source_remove(update_source_id); + update_source_id = g_timeout_add((guint)(MPD_UPDATE_TIME * 1000), + timer_mpd_update, + GINT_TO_POINTER(TRUE)); + } + } + + screen_update(mpd); + + idle_source_id = g_timeout_add(idle_interval, timer_idle, NULL); + return TRUE; +} + +/** + * Check the configured key bindings for errors, and display a status + * message every 10 seconds. + */ +static gboolean +timer_check_key_bindings(mpd_unused gpointer data) +{ + char buf[256]; + gboolean key_error; + + key_error = check_key_bindings(NULL, buf, sizeof(buf)); + if (!key_error) + /* no error: disable this timer for the rest of this + process */ + return FALSE; + + screen_status_printf("%s", buf); + doupdate(); + return TRUE; +} + int main(int argc, const char *argv[]) { struct sigaction act; const char *charset = NULL; - gboolean key_error; #ifdef HAVE_LOCALE_H /* time and date formatting */ @@ -206,7 +337,7 @@ main(int argc, const char *argv[]) read_configuration(&options); /* check key bindings */ - key_error = check_key_bindings(NULL, NULL, 0); + check_key_bindings(NULL, NULL, 0); /* parse command line options - 2 pass */ options_parse(argc, argv); @@ -261,73 +392,24 @@ main(int argc, const char *argv[]) /* initialize curses */ screen_init(mpd); - /* initialize timer */ - timer = g_timer_new(); + /* the main loop */ + main_loop = g_main_loop_new(NULL, FALSE); - while (1) { - static gdouble t = G_MAXDOUBLE; + /* watch out for keyboard input */ + g_io_add_watch(g_io_channel_unix_new(STDIN_FILENO), G_IO_IN, + keyboard_event, NULL); - if (key_error) { - char buf[BUFSIZE]; + /* attempt to connect */ + reconnect_source_id = g_timeout_add(1, timer_reconnect, NULL); - key_error=check_key_bindings(NULL, buf, BUFSIZE); - screen_status_printf("%s", buf); - } - - if (connected && (t >= MPD_UPDATE_TIME || mpd->need_update)) { - mpdclient_update(mpd); - g_timer_start(timer); - } + update_source_id = g_timeout_add((guint)(MPD_UPDATE_TIME * 1000), + timer_mpd_update, + GINT_TO_POINTER(TRUE)); + g_timeout_add(10000, timer_check_key_bindings, NULL); + idle_source_id = g_timeout_add(idle_interval, timer_idle, NULL); - if (connected) { - command_t cmd; - - screen_update(mpd); - if ((cmd=get_keyboard_command()) != CMD_NONE) { - screen_cmd(mpd, cmd); - if (cmd == CMD_VOLUME_UP || cmd == CMD_VOLUME_DOWN) - /* make shure we dont update the volume yet */ - g_timer_start(timer); - } else - screen_idle(mpd); - } else { - screen_status_printf(_("Connecting to %s... [Press %s to abort]"), - options.host, get_key_names(CMD_QUIT,0) ); - - /* - if (get_keyboard_command_with_timeout(MPD_RECONNECT_TIME) == CMD_QUIT) - exit(EXIT_SUCCESS); - */ - - if (mpdclient_connect(mpd, - options.host, options.port, - 1.5, - options.password) == 0) { - screen_status_printf(_("Connected to %s!"), options.host); - - /* quit if mpd is pre 0.11.0 - song id not supported by mpd */ - if (MPD_VERSION_LT(mpd, 0, 11, 0)) { - screen_status_printf(_("Error: MPD version %d.%d.%d is to old (0.11.0 needed).\n"), - mpd->connection->version[0], - mpd->connection->version[1], - mpd->connection->version[2]); - mpdclient_disconnect(mpd); - } else { - mpdclient_update(mpd); - if (!mpd->status) - screen_auth(mpd); - - connected = TRUE; - } - } - - doupdate(); - } - - if (options.enable_xterm_title) - update_xterm_title(); + g_main_loop_run(main_loop); + g_main_loop_unref(main_loop); - t = g_timer_elapsed(timer, NULL); - } - exit(EXIT_FAILURE); + exit_and_cleanup(); } diff --git a/src/screen.c b/src/screen.c index beddb95..479b1de 100644 --- a/src/screen.c +++ b/src/screen.c @@ -547,8 +547,6 @@ ncurses_init(void) curs_set(0); /* enable extra keys */ keypad(stdscr, TRUE); - /* return from getch() without blocking */ - timeout(SCREEN_TIMEOUT); /* initialize mouse support */ #ifdef HAVE_GETMOUSE if (options.enable_mouse) diff --git a/src/screen_utils.c b/src/screen_utils.c index 311c744..21da92e 100644 --- a/src/screen_utils.c +++ b/src/screen_utils.c @@ -58,7 +58,6 @@ screen_getch(WINDOW *w, const char *prompt) echo(); curs_set(1); - timeout(-1); while( (key=my_wgetch(w)) == ERR ) ; @@ -71,7 +70,6 @@ screen_getch(WINDOW *w, const char *prompt) noecho(); curs_set(0); - timeout(SCREEN_TIMEOUT); return key; } -- 2.30.2