10 Commits

Author SHA1 Message Date
6c2ae2f6bb Give up and implement elapsed time polling
Playback may sometimes stall but it won't produce any events.

This popular workaround likes to jump around, though.
It might be a good idea to use some kind of hybrid approach.

Therefore this is disabled by default so far.

Updates #1
2020-10-18 07:28:14 +02:00
b3579d1128 Explain the ticking mechanism
Took time to read.  Also fix an invalid comment.
2020-10-18 05:57:44 +02:00
525e952753 Bump liberty and termo 2020-10-10 21:31:31 +02:00
8707b38c48 Make direct SHOUTcast streams work again
Might be an issue specific to my bbc-on-ice, since we're not asking
for SHOUTcast by including "Icy-MetaData: 1" in request headers
but the proxy always outputs an "ICY 200 OK" header.
2020-10-10 14:48:22 +02:00
7af041ac01 Remove unnecessary quotes from macro definitions
The behaviour is defined by the standard.
2020-09-20 13:18:07 +02:00
1f0cab7cdd Bump liberty 2020-09-07 18:15:39 +02:00
e21699ab47 Support iterating tabs with C-PgUp/Down and C-Left/Right 2020-09-07 18:15:39 +02:00
d124f43cf6 Support vi-like scrolling with C-y and C-e 2020-08-01 14:06:17 +02:00
0e2a050c4f Name change 2020-08-01 14:04:10 +02:00
6c1546e919 Workaround cURL bug 2019-02-24 01:41:15 +01:00
6 changed files with 166 additions and 102 deletions

View File

@@ -47,13 +47,13 @@ else (USE_SYSTEM_TERMO)
set (Termo_LIBRARIES termo-static) set (Termo_LIBRARIES termo-static)
endif (USE_SYSTEM_TERMO) endif (USE_SYSTEM_TERMO)
include_directories (${UNISTRING_INCLUDE_DIRS} include_directories (${Unistring_INCLUDE_DIRS}
${NCURSESW_INCLUDE_DIRS} ${Termo_INCLUDE_DIRS} ${curl_INCLUDE_DIRS}) ${Ncursesw_INCLUDE_DIRS} ${Termo_INCLUDE_DIRS} ${curl_INCLUDE_DIRS})
link_directories (${curl_LIBRARY_DIRS}) link_directories (${curl_LIBRARY_DIRS})
# Configuration # Configuration
include (CheckFunctionExists) include (CheckFunctionExists)
set (CMAKE_REQUIRED_LIBRARIES ${NCURSESW_LIBRARIES}) set (CMAKE_REQUIRED_LIBRARIES ${Ncursesw_LIBRARIES})
CHECK_FUNCTION_EXISTS ("resizeterm" HAVE_RESIZETERM) CHECK_FUNCTION_EXISTS ("resizeterm" HAVE_RESIZETERM)
# Generate a configuration file # Generate a configuration file
@@ -63,8 +63,8 @@ include_directories (${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR})
# Build the main executable and link it # Build the main executable and link it
add_executable (${PROJECT_NAME} ${PROJECT_NAME}.c) add_executable (${PROJECT_NAME} ${PROJECT_NAME}.c)
target_link_libraries (${PROJECT_NAME} ${UNISTRING_LIBRARIES} target_link_libraries (${PROJECT_NAME} ${Unistring_LIBRARIES}
${NCURSESW_LIBRARIES} termo-static ${curl_LIBRARIES}) ${Ncursesw_LIBRARIES} termo-static ${curl_LIBRARIES})
add_threads (${PROJECT_NAME}) add_threads (${PROJECT_NAME})
# Installation # Installation
@@ -98,8 +98,8 @@ endforeach (page)
# CPack # CPack
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "MPD client") set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "MPD client")
set (CPACK_PACKAGE_VENDOR "Premysl Janouch") set (CPACK_PACKAGE_VENDOR "Premysl Eric Janouch")
set (CPACK_PACKAGE_CONTACT "Přemysl Janouch <p@janouch.name>") set (CPACK_PACKAGE_CONTACT "Přemysl Eric Janouch <p@janouch.name>")
set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
set (CPACK_PACKAGE_VERSION_MAJOR ${project_VERSION_MAJOR}) set (CPACK_PACKAGE_VERSION_MAJOR ${project_VERSION_MAJOR})
set (CPACK_PACKAGE_VERSION_MINOR ${project_VERSION_MINOR}) set (CPACK_PACKAGE_VERSION_MINOR ${project_VERSION_MINOR})

View File

@@ -1,4 +1,4 @@
Copyright (c) 2016 - 2018, Přemysl Janouch <p@janouch.name> Copyright (c) 2016 - 2020, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted. purpose with or without fee is hereby granted.

Submodule liberty updated: bb30c7d86e...e029aae1d3

View File

@@ -1,7 +1,7 @@
/* /*
* line-editor.c: a line editor component for the TUI part of liberty * line-editor.c: a line editor component for the TUI part of liberty
* *
* Copyright (c) 2017 - 2018, Přemysl Janouch <p@janouch.name> * Copyright (c) 2017 - 2018, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.

246
nncmpp.c
View File

@@ -1,7 +1,7 @@
/* /*
* nncmpp -- the MPD client you never knew you needed * nncmpp -- the MPD client you never knew you needed
* *
* Copyright (c) 2016 - 2018, Přemysl Janouch <p@janouch.name> * Copyright (c) 2016 - 2020, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.
@@ -21,30 +21,30 @@
// We "need" to have an enum for attributes before including liberty. // We "need" to have an enum for attributes before including liberty.
// Avoiding colours in the defaults here in order to support dumb terminals. // Avoiding colours in the defaults here in order to support dumb terminals.
#define ATTRIBUTE_TABLE(XX) \ #define ATTRIBUTE_TABLE(XX) \
XX( NORMAL, "normal", -1, -1, 0 ) \ XX( NORMAL, normal, -1, -1, 0 ) \
XX( HIGHLIGHT, "highlight", -1, -1, A_BOLD ) \ XX( HIGHLIGHT, highlight, -1, -1, A_BOLD ) \
/* Gauge */ \ /* Gauge */ \
XX( ELAPSED, "elapsed", -1, -1, A_REVERSE ) \ XX( ELAPSED, elapsed, -1, -1, A_REVERSE ) \
XX( REMAINS, "remains", -1, -1, A_UNDERLINE ) \ XX( REMAINS, remains, -1, -1, A_UNDERLINE ) \
/* Tab bar */ \ /* Tab bar */ \
XX( TAB_BAR, "tab_bar", -1, -1, A_REVERSE ) \ XX( TAB_BAR, tab_bar, -1, -1, A_REVERSE ) \
XX( TAB_ACTIVE, "tab_active", -1, -1, A_UNDERLINE ) \ XX( TAB_ACTIVE, tab_active, -1, -1, A_UNDERLINE ) \
/* Listview */ \ /* Listview */ \
XX( HEADER, "header", -1, -1, A_UNDERLINE ) \ XX( HEADER, header, -1, -1, A_UNDERLINE ) \
XX( EVEN, "even", -1, -1, 0 ) \ XX( EVEN, even, -1, -1, 0 ) \
XX( ODD, "odd", -1, -1, 0 ) \ XX( ODD, odd, -1, -1, 0 ) \
XX( DIRECTORY, "directory", -1, -1, 0 ) \ XX( DIRECTORY, directory, -1, -1, 0 ) \
XX( SELECTION, "selection", -1, -1, A_REVERSE ) \ XX( SELECTION, selection, -1, -1, A_REVERSE ) \
/* Cyan is good with both black and white. /* Cyan is good with both black and white.
* Can't use A_REVERSE because bold'd be bright. * Can't use A_REVERSE because bold'd be bright.
* Unfortunately ran out of B&W attributes. */ \ * Unfortunately ran out of B&W attributes. */ \
XX( MULTISELECT, "multiselect",-1, 6, 0 ) \ XX( MULTISELECT, multiselect, -1, 6, 0 ) \
XX( SCROLLBAR, "scrollbar", -1, -1, 0 ) \ XX( SCROLLBAR, scrollbar, -1, -1, 0 ) \
/* These are for debugging only */ \ /* These are for debugging only */ \
XX( WARNING, "warning", 3, -1, 0 ) \ XX( WARNING, warning, 3, -1, 0 ) \
XX( ERROR, "error", 1, -1, 0 ) \ XX( ERROR, error, 1, -1, 0 ) \
XX( INCOMING, "incoming", 2, -1, 0 ) \ XX( INCOMING, incoming, 2, -1, 0 ) \
XX( OUTGOING, "outgoing", 4, -1, 0 ) XX( OUTGOING, outgoing, 4, -1, 0 )
enum enum
{ {
@@ -174,7 +174,7 @@ print_curl_debug (CURL *easy, curl_infotype type, char *data, size_t len,
for (size_t i = 0; i < len; i++) for (size_t i = 0; i < len; i++)
{ {
uint8_t c = data[i]; uint8_t c = data[i];
copy[i] = c >= 32 || c == '\n' ? c : '.'; copy[i] = !iscntrl_ascii (c) || c == '\n' ? c : '.';
} }
copy[len] = '\0'; copy[len] = '\0';
@@ -286,6 +286,9 @@ poller_curl_on_socket_action (CURL *easy, curl_socket_t s, int what,
} }
if (what == CURL_POLL_REMOVE) if (what == CURL_POLL_REMOVE)
{ {
// Some annoying cURL bug. Never trust libraries.
fd->fd.closed = fcntl(fd->fd.fd, F_GETFL) < 0 && errno == EBADF;
poller_fd_reset (&fd->fd); poller_fd_reset (&fd->fd);
LIST_UNLINK (self->fds, fd); LIST_UNLINK (self->fds, fd);
free (fd); free (fd);
@@ -606,7 +609,8 @@ static struct app_context
struct str_map playback_info; ///< Current song info struct str_map playback_info; ///< Current song info
struct poller_timer elapsed_event; ///< Seconds elapsed event struct poller_timer elapsed_event; ///< Seconds elapsed event
int64_t elapsed_since; ///< Time of the next tick int64_t elapsed_since; ///< Time of the last tick
bool elapsed_poll; ///< Poll MPD for the elapsed time?
// TODO: initialize these to -1 // TODO: initialize these to -1
int song; ///< Current song index int song; ///< Current song index
@@ -689,6 +693,13 @@ tab_selection_range (struct tab *self)
// --- Configuration ----------------------------------------------------------- // --- Configuration -----------------------------------------------------------
static void
on_poll_elapsed_time_changed (struct config_item *item)
{
// This is only set once, on application startup
g.elapsed_poll = item->value.boolean;
}
static struct config_schema g_config_settings[] = static struct config_schema g_config_settings[] =
{ {
{ .name = "address", { .name = "address",
@@ -701,13 +712,18 @@ static struct config_schema g_config_settings[] =
{ .name = "root", { .name = "root",
.comment = "Where all the files MPD is playing are located", .comment = "Where all the files MPD is playing are located",
.type = CONFIG_ITEM_STRING }, .type = CONFIG_ITEM_STRING },
{ .name = "poll_elapsed_time",
.comment = "Whether to actively poll MPD for the elapsed time",
.type = CONFIG_ITEM_BOOLEAN,
.on_change = on_poll_elapsed_time_changed,
.default_ = "off" },
{} {}
}; };
static struct config_schema g_config_colors[] = static struct config_schema g_config_colors[] =
{ {
#define XX(name_, config, fg_, bg_, attrs_) \ #define XX(name_, config, fg_, bg_, attrs_) \
{ .name = config, .type = CONFIG_ITEM_STRING }, { .name = #config, .type = CONFIG_ITEM_STRING },
ATTRIBUTE_TABLE (XX) ATTRIBUTE_TABLE (XX)
#undef XX #undef XX
{} {}
@@ -742,7 +758,7 @@ load_config_colors (struct config_item *subtree, void *user_data)
// For simplicity, we should reload the entire table on each change anyway. // For simplicity, we should reload the entire table on each change anyway.
const char *value; const char *value;
#define XX(name, config, fg_, bg_, attrs_) \ #define XX(name, config, fg_, bg_, attrs_) \
if ((value = get_config_string (subtree, config))) \ if ((value = get_config_string (subtree, #config))) \
g.attrs[ATTRIBUTE_ ## name] = attrs_decode (value); g.attrs[ATTRIBUTE_ ## name] = attrs_decode (value);
ATTRIBUTE_TABLE (XX) ATTRIBUTE_TABLE (XX)
#undef XX #undef XX
@@ -1528,67 +1544,69 @@ app_goto_tab (int tab_index)
// --- Actions ----------------------------------------------------------------- // --- Actions -----------------------------------------------------------------
#define ACTIONS(XX) \ #define ACTIONS(XX) \
XX( NONE, "Do nothing" ) \ XX( NONE, Do nothing ) \
\ \
XX( QUIT, "Quit" ) \ XX( QUIT, Quit ) \
XX( REDRAW, "Redraw screen" ) \ XX( REDRAW, Redraw screen ) \
XX( HELP_TAB, "Switch to help tab" ) \ XX( TAB_HELP, Switch to help tab ) \
XX( LAST_TAB, "Switch to previous tab" ) \ XX( TAB_LAST, Switch to last tab ) \
XX( TAB_PREVIOUS, Switch to previous tab ) \
XX( TAB_NEXT, Switch to next tab ) \
\ \
XX( MPD_TOGGLE, "Toggle play/pause" ) \ XX( MPD_TOGGLE, Toggle play/pause ) \
XX( MPD_STOP, "Stop playback" ) \ XX( MPD_STOP, Stop playback ) \
XX( MPD_PREVIOUS, "Previous song" ) \ XX( MPD_PREVIOUS, Previous song ) \
XX( MPD_NEXT, "Next song" ) \ XX( MPD_NEXT, Next song ) \
XX( MPD_BACKWARD, "Seek backwards" ) \ XX( MPD_BACKWARD, Seek backwards ) \
XX( MPD_FORWARD, "Seek forwards" ) \ XX( MPD_FORWARD, Seek forwards ) \
XX( MPD_VOLUME_UP, "Increase volume" ) \ XX( MPD_VOLUME_UP, Increase volume ) \
XX( MPD_VOLUME_DOWN, "Decrease volume" ) \ XX( MPD_VOLUME_DOWN, Decrease volume ) \
\ \
XX( MPD_SEARCH, "Global search" ) \ XX( MPD_SEARCH, Global search ) \
XX( MPD_ADD, "Add selection to playlist" ) \ XX( MPD_ADD, Add selection to playlist ) \
XX( MPD_REPLACE, "Replace playlist" ) \ XX( MPD_REPLACE, Replace playlist ) \
XX( MPD_REPEAT, "Toggle repeat" ) \ XX( MPD_REPEAT, Toggle repeat ) \
XX( MPD_RANDOM, "Toggle random playback" ) \ XX( MPD_RANDOM, Toggle random playback ) \
XX( MPD_SINGLE, "Toggle single song playback" ) \ XX( MPD_SINGLE, Toggle single song playback ) \
XX( MPD_CONSUME, "Toggle consume" ) \ XX( MPD_CONSUME, Toggle consume ) \
XX( MPD_UPDATE_DB, "Update MPD database" ) \ XX( MPD_UPDATE_DB, Update MPD database ) \
XX( MPD_COMMAND, "Send raw command to MPD" ) \ XX( MPD_COMMAND, Send raw command to MPD ) \
\ \
XX( CHOOSE, "Choose item" ) \ XX( CHOOSE, Choose item ) \
XX( DELETE, "Delete item" ) \ XX( DELETE, Delete item ) \
XX( UP, "Go up a level" ) \ XX( UP, Go up a level ) \
XX( MULTISELECT, "Toggle multiselect" ) \ XX( MULTISELECT, Toggle multiselect ) \
\ \
XX( SCROLL_UP, "Scroll up" ) \ XX( SCROLL_UP, Scroll up ) \
XX( SCROLL_DOWN, "Scroll down" ) \ XX( SCROLL_DOWN, Scroll down ) \
XX( MOVE_UP, "Move selection up" ) \ XX( MOVE_UP, Move selection up ) \
XX( MOVE_DOWN, "Move selection down" ) \ XX( MOVE_DOWN, Move selection down ) \
\ \
XX( GOTO_TOP, "Go to top" ) \ XX( GOTO_TOP, Go to top ) \
XX( GOTO_BOTTOM, "Go to bottom" ) \ XX( GOTO_BOTTOM, Go to bottom ) \
XX( GOTO_ITEM_PREVIOUS, "Go to previous item" ) \ XX( GOTO_ITEM_PREVIOUS, Go to previous item ) \
XX( GOTO_ITEM_NEXT, "Go to next item" ) \ XX( GOTO_ITEM_NEXT, Go to next item ) \
XX( GOTO_PAGE_PREVIOUS, "Go to previous page" ) \ XX( GOTO_PAGE_PREVIOUS, Go to previous page ) \
XX( GOTO_PAGE_NEXT, "Go to next page" ) \ XX( GOTO_PAGE_NEXT, Go to next page ) \
\ \
XX( GOTO_VIEW_TOP, "Select top item" ) \ XX( GOTO_VIEW_TOP, Select top item ) \
XX( GOTO_VIEW_CENTER, "Select center item" ) \ XX( GOTO_VIEW_CENTER, Select center item ) \
XX( GOTO_VIEW_BOTTOM, "Select bottom item" ) \ XX( GOTO_VIEW_BOTTOM, Select bottom item ) \
\ \
XX( EDITOR_CONFIRM, "Confirm input" ) \ XX( EDITOR_CONFIRM, Confirm input ) \
\ \
XX( EDITOR_B_CHAR, "Go back a character" ) \ XX( EDITOR_B_CHAR, Go back a character ) \
XX( EDITOR_F_CHAR, "Go forward a character" ) \ XX( EDITOR_F_CHAR, Go forward a character ) \
XX( EDITOR_B_WORD, "Go back a word" ) \ XX( EDITOR_B_WORD, Go back a word ) \
XX( EDITOR_F_WORD, "Go forward a word" ) \ XX( EDITOR_F_WORD, Go forward a word ) \
XX( EDITOR_HOME, "Go to start of line" ) \ XX( EDITOR_HOME, Go to start of line ) \
XX( EDITOR_END, "Go to end of line" ) \ XX( EDITOR_END, Go to end of line ) \
\ \
XX( EDITOR_B_DELETE, "Delete last character" ) \ XX( EDITOR_B_DELETE, Delete last character ) \
XX( EDITOR_F_DELETE, "Delete next character" ) \ XX( EDITOR_F_DELETE, Delete next character ) \
XX( EDITOR_B_KILL_WORD, "Delete last word" ) \ XX( EDITOR_B_KILL_WORD, Delete last word ) \
XX( EDITOR_B_KILL_LINE, "Delete everything up to BOL" ) \ XX( EDITOR_B_KILL_LINE, Delete everything up to BOL ) \
XX( EDITOR_F_KILL_LINE, "Delete everything up to EOL" ) XX( EDITOR_F_KILL_LINE, Delete everything up to EOL )
enum action enum action
{ {
@@ -1605,7 +1623,7 @@ static struct action_info
} }
g_actions[] = g_actions[] =
{ {
#define XX(name, description) { #name, description }, #define XX(name, description) { #name, #description },
ACTIONS (XX) ACTIONS (XX)
#undef XX #undef XX
}; };
@@ -1759,14 +1777,30 @@ app_process_action (enum action action)
tab->item_mark = tab->item_selected; tab->item_mark = tab->item_selected;
return true; return true;
case ACTION_LAST_TAB: case ACTION_TAB_LAST:
if (!g.last_tab) if (!g.last_tab)
return false; return false;
app_switch_tab (g.last_tab); app_switch_tab (g.last_tab);
return true; return true;
case ACTION_HELP_TAB: case ACTION_TAB_HELP:
app_switch_tab (g.help_tab); app_switch_tab (g.help_tab);
return true; return true;
case ACTION_TAB_PREVIOUS:
if (g.active_tab == g.help_tab)
return false;
if (!g.active_tab->prev)
app_switch_tab (g.help_tab);
else
app_switch_tab (g.active_tab->prev);
return true;
case ACTION_TAB_NEXT:
if (g.active_tab == g.help_tab)
app_switch_tab (g.tabs);
else if (g.active_tab->next)
app_switch_tab (g.active_tab->next);
else
return false;
return true;
case ACTION_MPD_TOGGLE: case ACTION_MPD_TOGGLE:
if (g.state == PLAYER_PLAYING) return MPD_SIMPLE ("pause", "1"); if (g.state == PLAYER_PLAYING) return MPD_SIMPLE ("pause", "1");
@@ -1987,8 +2021,12 @@ g_normal_defaults[] =
{ "Escape", ACTION_QUIT }, { "Escape", ACTION_QUIT },
{ "q", ACTION_QUIT }, { "q", ACTION_QUIT },
{ "C-l", ACTION_REDRAW }, { "C-l", ACTION_REDRAW },
{ "M-Tab", ACTION_LAST_TAB }, { "M-Tab", ACTION_TAB_LAST },
{ "F1", ACTION_HELP_TAB }, { "F1", ACTION_TAB_HELP },
{ "C-Left", ACTION_TAB_PREVIOUS },
{ "C-Right", ACTION_TAB_NEXT },
{ "C-PageUp", ACTION_TAB_PREVIOUS },
{ "C-PageDown", ACTION_TAB_NEXT },
{ "Home", ACTION_GOTO_TOP }, { "Home", ACTION_GOTO_TOP },
{ "End", ACTION_GOTO_BOTTOM }, { "End", ACTION_GOTO_BOTTOM },
@@ -2008,6 +2046,8 @@ g_normal_defaults[] =
{ "C-n", ACTION_GOTO_ITEM_NEXT }, { "C-n", ACTION_GOTO_ITEM_NEXT },
{ "C-b", ACTION_GOTO_PAGE_PREVIOUS }, { "C-b", ACTION_GOTO_PAGE_PREVIOUS },
{ "C-f", ACTION_GOTO_PAGE_NEXT }, { "C-f", ACTION_GOTO_PAGE_NEXT },
{ "C-y", ACTION_SCROLL_UP },
{ "C-e", ACTION_SCROLL_DOWN },
{ "H", ACTION_GOTO_VIEW_TOP }, { "H", ACTION_GOTO_VIEW_TOP },
{ "M", ACTION_GOTO_VIEW_CENTER }, { "M", ACTION_GOTO_VIEW_CENTER },
@@ -2493,8 +2533,7 @@ library_tab_change_level (const char *new_path)
str_reset (path); str_reset (path);
str_append (path, new_path); str_append (path, new_path);
free (g_library_tab.super.header); cstr_set (&g_library_tab.super.header, NULL);
g_library_tab.super.header = NULL;
g_library_tab.super.item_mark = -1; g_library_tab.super.item_mark = -1;
if (path->len) if (path->len)
@@ -2692,8 +2731,7 @@ library_tab_on_action (enum action action)
free (fake_subdir); free (fake_subdir);
} }
free (tab->header); cstr_set (&tab->header, xstrdup_printf ("Global search"));
tab->header = xstrdup_printf ("Global search");
g_library_tab.searching = true; g_library_tab.searching = true;
// Since we've already changed the header, empty the list, // Since we've already changed the header, empty the list,
@@ -2849,7 +2887,7 @@ streams_tab_extract_links (struct str *data, const char *content_type,
for (size_t i = 0; i < data->len; i++) for (size_t i = 0; i < data->len; i++)
{ {
uint8_t c = data->str[i]; uint8_t c = data->str[i];
if ((c < 32) & (c != '\t') & (c != '\r') & (c != '\n')) if (iscntrl_ascii (c) & (c != '\t') & (c != '\r') & (c != '\n'))
return false; return false;
} }
@@ -2943,6 +2981,8 @@ streams_tab_process (const char *uri, bool replace, struct error **e)
task.replace = replace; task.replace = replace;
bool result = false; bool result = false;
struct curl_slist *ok_headers = curl_slist_append (NULL, "ICY 200 OK");
CURLcode res; CURLcode res;
if ((res = curl_easy_setopt (easy, CURLOPT_FOLLOWLOCATION, 1L)) if ((res = curl_easy_setopt (easy, CURLOPT_FOLLOWLOCATION, 1L))
|| (res = curl_easy_setopt (easy, CURLOPT_NOPROGRESS, 1L)) || (res = curl_easy_setopt (easy, CURLOPT_NOPROGRESS, 1L))
@@ -2952,6 +2992,7 @@ streams_tab_process (const char *uri, bool replace, struct error **e)
|| (res = curl_easy_setopt (easy, CURLOPT_SSL_VERIFYPEER, 0L)) || (res = curl_easy_setopt (easy, CURLOPT_SSL_VERIFYPEER, 0L))
|| (res = curl_easy_setopt (easy, CURLOPT_SSL_VERIFYHOST, 0L)) || (res = curl_easy_setopt (easy, CURLOPT_SSL_VERIFYHOST, 0L))
|| (res = curl_easy_setopt (easy, CURLOPT_URL, uri)) || (res = curl_easy_setopt (easy, CURLOPT_URL, uri))
|| (res = curl_easy_setopt (easy, CURLOPT_HTTP200ALIASES, ok_headers))
|| (res = curl_easy_setopt (easy, CURLOPT_VERBOSE, (long) g_debug_mode)) || (res = curl_easy_setopt (easy, CURLOPT_VERBOSE, (long) g_debug_mode))
|| (res = curl_easy_setopt (easy, CURLOPT_DEBUGFUNCTION, print_curl_debug)) || (res = curl_easy_setopt (easy, CURLOPT_DEBUGFUNCTION, print_curl_debug))
@@ -2975,6 +3016,7 @@ streams_tab_process (const char *uri, bool replace, struct error **e)
error: error:
curl_easy_cleanup (task.curl.easy); curl_easy_cleanup (task.curl.easy);
curl_slist_free_all (ok_headers);
str_free (&task.data); str_free (&task.data);
poller_curl_free (&pc); poller_curl_free (&pc);
@@ -3377,11 +3419,20 @@ mpd_update_playback_state (void)
mpd_read_time (duration, &g.song_duration, NULL); mpd_read_time (duration, &g.song_duration, NULL);
strv_free (&fields); strv_free (&fields);
// We could also just poll the server each half a second but let's not
poller_timer_reset (&g.elapsed_event); poller_timer_reset (&g.elapsed_event);
if (g.state == PLAYER_PLAYING) if (g.state == PLAYER_PLAYING)
{ {
poller_timer_set (&g.elapsed_event, 1000 - msec_past_second); int until_next = 1000 - msec_past_second;
// We could make use of "until_next", however this might create
// an intensive busy loop when playback stalls (typically because of
// some network issues). Half a second will work reasonably well.
if (g.elapsed_poll)
until_next = 500;
// Set a timer for when the next round second of playback happens
poller_timer_set (&g.elapsed_event, until_next);
// Remember when the last round second was, relative to monotonic time
g.elapsed_since = clock_msec (CLOCK_BEST) - msec_past_second; g.elapsed_since = clock_msec (CLOCK_BEST) - msec_past_second;
} }
@@ -3508,15 +3559,19 @@ mpd_on_info_response (const struct mpd_response *response,
} }
static void static void
mpd_on_tick (void *user_data) mpd_on_elapsed_time_tick (void *user_data)
{ {
(void) user_data; (void) user_data;
// Compute how much time has elapsed since the last round second
int64_t diff_msec = clock_msec (CLOCK_BEST) - g.elapsed_since; int64_t diff_msec = clock_msec (CLOCK_BEST) - g.elapsed_since;
int elapsed_sec = diff_msec / 1000; int elapsed_sec = diff_msec / 1000;
int elapsed_msec = diff_msec % 1000; int elapsed_msec = diff_msec % 1000;
g.song_elapsed += elapsed_sec; g.song_elapsed += elapsed_sec;
g.elapsed_since += elapsed_sec * 1000; g.elapsed_since += elapsed_sec * 1000;
// Try to get called on the next round second of playback
poller_timer_set (&g.elapsed_event, 1000 - elapsed_msec); poller_timer_set (&g.elapsed_event, 1000 - elapsed_msec);
app_invalidate (); app_invalidate ();
@@ -3537,6 +3592,15 @@ mpd_request_info (void)
mpd_client_idle (c, 0); mpd_client_idle (c, 0);
} }
static void
mpd_on_elapsed_time_tick_poll (void *user_data)
{
(void) user_data;
// As soon as the reply arrives, we (may) set the timer again
mpd_request_info ();
}
static void static void
mpd_on_events (unsigned subsystems, void *user_data) mpd_on_events (unsigned subsystems, void *user_data)
{ {
@@ -3829,8 +3893,7 @@ app_on_message_timer (void *user_data)
{ {
(void) user_data; (void) user_data;
free (g.message); cstr_set (&g.message, NULL);
g.message = NULL;
app_invalidate (); app_invalidate ();
} }
@@ -3858,8 +3921,7 @@ app_log_handler (void *user_data, const char *quote, const char *fmt,
user_data == NULL ? 0 : g.attrs[(intptr_t) user_data].attrs); user_data == NULL ? 0 : g.attrs[(intptr_t) user_data].attrs);
else else
{ {
free (g.message); cstr_set (&g.message, xstrdup (message.str));
g.message = xstrdup (message.str);
app_invalidate (); app_invalidate ();
poller_timer_set (&g.message_timer, 5000); poller_timer_set (&g.message_timer, 5000);
} }
@@ -3890,7 +3952,9 @@ app_init_poller_events (void)
poller_timer_set (&g.connect_event, 0); poller_timer_set (&g.connect_event, 0);
g.elapsed_event = poller_timer_make (&g.poller); g.elapsed_event = poller_timer_make (&g.poller);
g.elapsed_event.dispatcher = mpd_on_tick; g.elapsed_event.dispatcher = g.elapsed_poll
? mpd_on_elapsed_time_tick_poll
: mpd_on_elapsed_time_tick;
g.refresh_event = poller_idle_make (&g.poller); g.refresh_event = poller_idle_make (&g.poller);
g.refresh_event.dispatcher = app_on_refresh; g.refresh_event.dispatcher = app_on_refresh;

2
termo

Submodule termo updated: 30e0eee1a8...8c4e867760