summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.adoc2
-rw-r--r--nncmpp.c231
2 files changed, 131 insertions, 102 deletions
diff --git a/README.adoc b/README.adoc
index 6902bac..661d79a 100644
--- a/README.adoc
+++ b/README.adoc
@@ -13,7 +13,7 @@ I focus on things that aren't worthless to me.
If it's not obvious enough, the name is a pun on all those ridiculous client
names, and should be pronounced as "nincompoop".
-Currently it's under development and doesn't work in any sense yet.
+Currently it's under development and doesn't do much.
Packages
--------
diff --git a/nncmpp.c b/nncmpp.c
index 0262b60..c8f964f 100644
--- a/nncmpp.c
+++ b/nncmpp.c
@@ -199,7 +199,7 @@ static struct app_context
// Connection:
struct mpd_client client; ///< MPD client interface
- struct poller_timer reconnect_event;///< MPD reconnect timer
+ struct poller_timer connect_event; ///< MPD reconnect timer
enum player_state state; ///< Player state
struct str_map song_info; ///< Current song info
@@ -540,6 +540,24 @@ row_buffer_append (struct row_buffer *self, const char *str, chtype attrs)
}
}
+static void
+row_buffer_addv (struct row_buffer *self, const char *s, ...)
+ ATTRIBUTE_SENTINEL;
+
+static void
+row_buffer_addv (struct row_buffer *self, const char *s, ...)
+{
+ va_list ap;
+ va_start (ap, s);
+
+ while (s)
+ {
+ row_buffer_append (self, s, va_arg (ap, chtype));
+ s = va_arg (ap, const char *);
+ }
+ va_end (ap);
+}
+
/// Pop as many codepoints as needed to free up "space" character cells.
/// Given the suffix nature of combining marks, this should work pretty fine.
static int
@@ -655,7 +673,7 @@ help_tab_create ()
return super;
}
-// --- Application -------------------------------------------------------------
+// --- Rendering ---------------------------------------------------------------
/// Write the given UTF-8 string padded with spaces.
/// @param[in] n The number of characters to write, or -1 for the whole string.
@@ -692,144 +710,155 @@ app_next_row (chtype attrs)
mvwhline (stdscr, g_ctx.list_offset++, 0, ' ' | attrs, COLS);
}
-static size_t
-app_write_time (int seconds, chtype attrs)
+// We typically write here to a single buffer serving the entire line
+static void
+app_flush_buffer (struct row_buffer *buf, chtype attrs)
{
- int minutes = seconds / 60; seconds %= 60;
- int hours = minutes / 60; hours %= 60;
-
- struct str s;
- str_init (&s);
-
- if (hours)
- {
- str_append_printf (&s, "%d:", hours);
- str_append_printf (&s, "%02d:", minutes);
- }
- else
- str_append_printf (&s, "%d:", minutes);
+ if (buf->total_width > COLS)
+ row_buffer_ellipsis (buf, COLS, attrs);
- str_append_printf (&s, "%02d", seconds);
- size_t result = app_write_utf8 (s.str, attrs, -1);
- str_free (&s);
- return result;
+ app_next_row (attrs);
+ row_buffer_flush (buf);
+ row_buffer_free (buf);
}
static void
-app_redraw_status (void)
+app_redraw_song_info (void)
{
- chtype normal = APP_ATTR (HEADER);
- chtype highlight = APP_ATTR (HIGHLIGHT);
-
- if (g_ctx.state == PLAYER_STOPPED)
- goto line;
-
// The map doesn't need to be initialized at all, so we need to check
struct str_map *map = &g_ctx.song_info;
if (!soft_assert (map->len != 0))
return;
+ // XXX: can we get rid of this and still make it look acceptable?
+ chtype a_normal = APP_ATTR (HEADER);
+ chtype a_highlight = APP_ATTR (HIGHLIGHT);
+
char *title;
if ((title = str_map_find (map, "title"))
|| (title = str_map_find (map, "name"))
|| (title = str_map_find (map, "file")))
{
- app_next_row (normal);
-
struct row_buffer buf;
row_buffer_init (&buf);
- row_buffer_append (&buf, title, highlight);
- if (buf.total_width > COLS)
- row_buffer_ellipsis (&buf, COLS, highlight);
- row_buffer_flush (&buf);
- row_buffer_free (&buf);
+ row_buffer_append (&buf, title, a_highlight);
+ app_flush_buffer (&buf, a_highlight);
}
char *artist = str_map_find (map, "artist");
char *album = str_map_find (map, "album");
- if (artist || album)
+ if (!artist && !album)
+ return;
+
+ struct row_buffer buf;
+ row_buffer_init (&buf);
+
+ if (artist)
{
- app_next_row (normal);
+ if (buf.total_width)
+ row_buffer_append (&buf, " ", a_normal);
+ row_buffer_addv (&buf, "by ", a_normal, artist, a_highlight, NULL);
+ }
+ if (album)
+ {
+ if (buf.total_width)
+ row_buffer_append (&buf, " ", a_normal);
+ row_buffer_addv (&buf, "from ", a_normal, album, a_highlight, NULL);
+ }
+ app_flush_buffer (&buf, a_normal);
+}
- struct row_buffer buf;
- row_buffer_init (&buf);
+static void
+app_write_time (struct row_buffer *buf, int seconds, chtype attrs)
+{
+ int minutes = seconds / 60; seconds %= 60;
+ int hours = minutes / 60; hours %= 60;
- bool first = true;
- if (artist)
- {
- if (!first) row_buffer_append (&buf, " ", normal);
- row_buffer_append (&buf, "by ", normal);
- row_buffer_append (&buf, artist, highlight);
- first = false;
- }
- if (album)
- {
- if (!first) row_buffer_append (&buf, " ", normal);
- row_buffer_append (&buf, "from ", normal);
- row_buffer_append (&buf, album, highlight);
- first = false;
- }
+ struct str s;
+ str_init (&s);
- if (buf.total_width > COLS)
- row_buffer_ellipsis (&buf, COLS, normal);
- row_buffer_flush (&buf);
- row_buffer_free (&buf);
- }
+ if (hours)
+ str_append_printf (&s, "%d:%02d:", hours, minutes);
+ else
+ str_append_printf (&s, "%d:", minutes);
-line:
- app_next_row (normal);
+ str_append_printf (&s, "%02d", seconds);
+ row_buffer_append (buf, s.str, attrs);
+ str_free (&s);
+}
- bool stopped = g_ctx.state == PLAYER_STOPPED;
- chtype active = stopped ? normal : highlight;
+static void
+app_redraw_status (void)
+{
+ if (g_ctx.state != PLAYER_STOPPED)
+ app_redraw_song_info ();
+
+ // XXX: can we get rid of this and still make it look acceptable?
+ chtype a_normal = APP_ATTR (HEADER);
+ chtype a_highlight = APP_ATTR (HIGHLIGHT);
- // TODO: we desperately need some better abstractions for this;
- // at minimum we need to count characters already written
- app_write_utf8 ("<<", active, -1);
- addch (' ' | normal);
+ struct row_buffer buf;
+ row_buffer_init (&buf);
+ bool stopped = g_ctx.state == PLAYER_STOPPED;
+
+ chtype a_song_action = stopped ? a_normal : a_highlight;
+ row_buffer_addv (&buf, "<<", a_song_action, " ", a_normal, NULL);
if (g_ctx.state == PLAYER_PLAYING)
- app_write_utf8 ("||", highlight, -1);
+ row_buffer_addv (&buf, "||", a_highlight, " ", a_normal, NULL);
else
- app_write_utf8 ("|>", highlight, -1);
- addch (' ' | normal);
-
- app_write_utf8 ("[]", active, -1);
- addch (' ' | normal);
- app_write_utf8 (">>", active, -1);
- addch (' ' | normal);
- addch (' ' | normal);
+ row_buffer_addv (&buf, "|>", a_highlight, " ", a_normal, NULL);
+ row_buffer_addv (&buf, "[]", a_song_action, " ", a_normal, NULL);
+ row_buffer_addv (&buf, ">>", a_song_action, " ", a_normal, NULL);
if (stopped)
- app_write_utf8 ("Stopped", normal, COLS);
+ row_buffer_append (&buf, "Stopped", a_normal);
else
{
- // TODO: convert the display to minutes
if (g_ctx.song_elapsed >= 0)
{
- app_write_time (g_ctx.song_elapsed, normal);
- addch (' ' | normal);
+ app_write_time (&buf, g_ctx.song_elapsed, a_normal);
+ row_buffer_append (&buf, " ", a_normal);
}
- if (g_ctx.song_duration >= 0)
+ if (g_ctx.song_duration >= 1)
{
- addch ('/' | normal);
- addch (' ' | normal);
- app_write_time (g_ctx.song_duration, normal);
- addch (' ' | normal);
+ row_buffer_append (&buf, "/ ", a_normal);
+ app_write_time (&buf, g_ctx.song_duration, a_normal);
+ row_buffer_append (&buf, " ", a_normal);
}
- addch (' ' | normal);
+ row_buffer_append (&buf, " ", a_normal);
+ }
- if (g_ctx.song_elapsed >= 0 && g_ctx.song_duration >= 0)
- {
- // TODO: display a gauge representing the same information,
- // attributes BAR_ELAPSED and BAR_REMAINS
- }
- else
- {
- // TODO: just fill the space
- }
+ // It gets a bit complicated due to the only right-aligned item on the row
+ char *volume = NULL;
+ int remaining = COLS - buf.total_width;
+ if (g_ctx.volume >= 0)
+ {
+ volume = xstrdup_printf (" %3d%%", g_ctx.volume);
+ remaining -= strlen (volume);
+ }
+
+ // TODO: store the coordinates of the progress bar
+ if (!stopped && g_ctx.song_elapsed >= 0 && g_ctx.song_duration >= 1
+ && remaining > 0)
+ {
+ int len_elapsed = (int) ((float) g_ctx.song_elapsed
+ / g_ctx.song_duration * remaining + 0.5);
+ int len_remains = remaining - len_elapsed;
+ while (len_elapsed-- > 0)
+ row_buffer_append (&buf, " ", APP_ATTR (ELAPSED));
+ while (len_remains-- > 0)
+ row_buffer_append (&buf, " ", APP_ATTR (REMAINS));
}
+ else while (remaining-- > 0)
+ row_buffer_append (&buf, " ", a_normal);
- // TODO: append the volume value if available
+ if (volume)
+ {
+ row_buffer_append (&buf, volume, a_normal);
+ free (volume);
+ }
+ app_flush_buffer (&buf, a_normal);
}
static void
@@ -915,7 +944,7 @@ app_redraw (void)
app_redraw_view ();
}
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// --- Actions -----------------------------------------------------------------
/// Scroll up @a n items. Doesn't redraw.
static bool
@@ -1427,7 +1456,7 @@ mpd_on_events (unsigned subsystems, void *user_data)
static void
mpd_queue_reconnect (void)
{
- poller_timer_set (&g_ctx.reconnect_event, 5 * 1000);
+ poller_timer_set (&g_ctx.connect_event, 5 * 1000);
}
static void
@@ -1624,9 +1653,9 @@ app_init_poller_events (void)
poller_timer_init (&g_ctx.tk_timer, &g_ctx.poller);
g_ctx.tk_timer.dispatcher = app_on_key_timer;
- poller_timer_init (&g_ctx.reconnect_event, &g_ctx.poller);
- g_ctx.reconnect_event.dispatcher = app_on_reconnect;
- poller_timer_set (&g_ctx.reconnect_event, 0);
+ poller_timer_init (&g_ctx.connect_event, &g_ctx.poller);
+ g_ctx.connect_event.dispatcher = app_on_reconnect;
+ poller_timer_set (&g_ctx.connect_event, 0);
poller_timer_init (&g_ctx.elapsed_event, &g_ctx.poller);
g_ctx.elapsed_event.dispatcher = mpd_on_tick;