From 1e39ae52c510609fd50adc6ba3d7440b79ac3e33 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Mon, 3 Oct 2016 19:43:51 +0200
Subject: Make the debug tab prettier
And the MPD code a little bit more generic.
---
mpd.c | 11 +++-
nncmpp.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++-----------------
2 files changed, 146 insertions(+), 52 deletions(-)
diff --git a/mpd.c b/mpd.c
index f093691..02b1d8f 100644
--- a/mpd.c
+++ b/mpd.c
@@ -1,4 +1,5 @@
// Copied from desktop-tools, should go to liberty if it proves useful
+// XXX: changed: added a debugging callback instead of hardcoded print_debug
// --- MPD client interface ----------------------------------------------------
@@ -107,6 +108,9 @@ struct mpd_client
/// Callback to receive "idle" updates.
/// Remember to restart the idle if needed.
void (*on_event) (unsigned subsystems, void *user_data);
+
+ /// Callback to trace protocol I/O
+ void (*on_io_hook) (void *user_data, bool outgoing, const char *line);
};
static void mpd_client_reset (struct mpd_client *self);
@@ -258,7 +262,8 @@ mpd_client_parse_hello (struct mpd_client *self, const char *line)
static bool
mpd_client_parse_line (struct mpd_client *self, const char *line)
{
- print_debug ("MPD >> %s", line);
+ if (self->on_io_hook)
+ self->on_io_hook (self->user_data, false, line);
if (!self->got_hello)
return mpd_client_parse_hello (self, line);
@@ -414,7 +419,9 @@ mpd_client_send_commandv (struct mpd_client *self, char **commands)
str_append (&line, *commands);
}
- print_debug ("MPD << %s", line.str);
+ if (self->on_io_hook)
+ self->on_io_hook (self->user_data, true, line.str);
+
str_append_c (&line, '\n');
str_append_str (&self->write_buffer, &line);
str_free (&line);
diff --git a/nncmpp.c b/nncmpp.c
index a6faaa1..8a277d9 100644
--- a/nncmpp.c
+++ b/nncmpp.c
@@ -20,10 +20,45 @@
#include "config.h"
+// We "need" to have an enum for attributes before including liberty.
+// Avoiding colours in the defaults here in order to support dumb terminals.
+#define ATTRIBUTE_TABLE(XX) \
+ XX( HEADER, "header", -1, -1, 0 ) \
+ XX( HIGHLIGHT, "highlight", -1, -1, A_BOLD ) \
+ /* Gauge */ \
+ XX( ELAPSED, "elapsed", -1, -1, A_REVERSE ) \
+ XX( REMAINS, "remains", -1, -1, A_UNDERLINE ) \
+ /* Tab bar */ \
+ XX( TAB_BAR, "tab_bar", -1, -1, A_REVERSE ) \
+ XX( TAB_ACTIVE, "tab_active", -1, -1, A_UNDERLINE ) \
+ /* Listview */ \
+ XX( EVEN, "even", -1, -1, 0 ) \
+ XX( ODD, "odd", -1, -1, 0 ) \
+ XX( SELECTION, "selection", -1, -1, A_REVERSE ) \
+ XX( SCROLLBAR, "scrollbar", -1, -1, 0 ) \
+ /* These are for debugging only */ \
+ XX( WARNING, "warning", 3, -1, 0 ) \
+ XX( ERROR, "error", 1, -1, 0 ) \
+ XX( INCOMING, "incoming", 2, -1, 0 ) \
+ XX( OUTGOING, "outgoing", 4, -1, 0 )
+
+enum
+{
+#define XX(name, config, fg_, bg_, attrs_) ATTRIBUTE_ ## name,
+ ATTRIBUTE_TABLE (XX)
+#undef XX
+ ATTRIBUTE_COUNT
+};
+
// My battle-tested C framework acting as a GLib replacement. Its one big
// disadvantage is missing support for i18n but that can eventually be added
// as an optional feature. Localised applications look super awkward, though.
+// User data for logger functions to enable formatted logging
+#define print_fatal_data ((void *) ATTRIBUTE_ERROR)
+#define print_error_data ((void *) ATTRIBUTE_ERROR)
+#define print_warning_data ((void *) ATTRIBUTE_WARNING)
+
#define LIBERTY_WANT_POLLER
#define LIBERTY_WANT_ASYNC
#include "liberty/liberty.c"
@@ -100,39 +135,6 @@ update_curses_terminal_size (void)
// Function names are prefixed mostly because of curses which clutters the
// global namespace and makes it harder to distinguish what functions relate to.
-// Avoiding colours in the defaults here in order to support dumb terminals
-#define ATTRIBUTE_TABLE(XX) \
- XX( HEADER, "header", -1, -1, 0 ) \
- XX( HIGHLIGHT, "highlight", -1, -1, A_BOLD ) \
- \
- XX( ELAPSED, "elapsed", -1, -1, A_REVERSE ) \
- XX( REMAINS, "remains", -1, -1, A_UNDERLINE ) \
- \
- XX( TAB_BAR, "tab_bar", -1, -1, A_REVERSE ) \
- XX( TAB_ACTIVE, "tab_active", -1, -1, A_UNDERLINE ) \
- \
- XX( EVEN, "even", -1, -1, 0 ) \
- XX( ODD, "odd", -1, -1, 0 ) \
- XX( SELECTION, "selection", -1, -1, A_REVERSE ) \
- XX( SCROLLBAR, "scrollbar", -1, -1, 0 )
-
-enum
-{
-#define XX(name, config, fg_, bg_, attrs_) ATTRIBUTE_ ## name,
- ATTRIBUTE_TABLE (XX)
-#undef XX
- ATTRIBUTE_COUNT
-};
-
-struct attrs
-{
- short fg; ///< Foreground colour index
- short bg; ///< Background colour index
- chtype attrs; ///< Other attributes
-};
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
// The user interface is focused on conceptual simplicity. That is important
// since we're not using any TUI framework (which are mostly a lost cause to me
// in the post-Unicode era and not worth pursuing), and the code would get
@@ -151,9 +153,8 @@ struct row_buffer;
typedef bool (*tab_event_fn) (struct tab *self, termo_key_t *event);
/// Draw an item to the screen using the row buffer API
-// TODO: this will probably want to know the actual width
-typedef void (*tab_item_draw_fn)
- (struct tab *self, unsigned item_index, struct row_buffer *buffer);
+typedef void (*tab_item_draw_fn) (struct tab *self,
+ unsigned item_index, struct row_buffer *buffer, int width);
struct tab
{
@@ -181,6 +182,13 @@ struct tab
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+struct attrs
+{
+ short fg; ///< Foreground colour index
+ short bg; ///< Background colour index
+ chtype attrs; ///< Other attributes
+};
+
enum player_state { PLAYER_STOPPED, PLAYER_PLAYING, PLAYER_PAUSED };
// Basically a container for most of the globals; no big sense in handing
@@ -1005,7 +1013,13 @@ app_redraw_view (void)
struct row_buffer buf;
row_buffer_init (&buf);
- tab->on_item_draw (tab, item_index, &buf);
+ tab->on_item_draw (tab, item_index, &buf, view_width);
+ if (item_index == tab->item_selected)
+ {
+ // Make it so that the selection color always wins
+ LIST_FOR_EACH (struct row_char, iter, buf.chars)
+ iter->attrs &= ~(A_COLOR | A_REVERSE);
+ }
if (buf.total_width > view_width)
row_buffer_ellipsis (&buf, view_width, row_attrs);
@@ -1682,6 +1696,8 @@ mpd_on_failure (void *user_data)
mpd_queue_reconnect ();
}
+static void mpd_on_io_hook (void *user_data, bool outgoing, const char *line);
+
static void
app_on_reconnect (void *user_data)
{
@@ -1691,6 +1707,7 @@ app_on_reconnect (void *user_data)
c->on_failure = mpd_on_failure;
c->on_connected = mpd_on_connected;
c->on_event = mpd_on_events;
+ c->on_io_hook = mpd_on_io_hook;
// We accept hostname/IPv4/IPv6 in pseudo-URL format, as well as sockets
char *address = xstrdup (get_config_string (g_ctx.config.root,
@@ -1744,9 +1761,10 @@ g_help_items[] =
static void
help_tab_on_item_draw (struct tab *self, unsigned item_index,
- struct row_buffer *buffer)
+ struct row_buffer *buffer, int width)
{
(void) self;
+ (void) width;
hard_assert (item_index <= N_ELEMENTS (g_help_items));
row_buffer_append (buffer, g_help_items[item_index].text, 0);
@@ -1765,28 +1783,80 @@ help_tab_create (void)
// --- Debug tab ---------------------------------------------------------------
+struct debug_item
+{
+ char *text; ///< Logged line
+ int64_t timestamp; ///< Timestamp
+ chtype attrs; ///< Line attributes
+};
+
static struct
{
struct tab super; ///< Parent class
- struct str_vector lines; ///< Lines
+ struct debug_item *items; ///< Items
+ size_t items_alloc; ///< How many items are allocated
bool active; ///< The tab is present
}
g_debug_tab;
static void
debug_tab_on_item_draw (struct tab *self, unsigned item_index,
- struct row_buffer *buffer)
+ struct row_buffer *buffer, int width)
{
(void) self;
- hard_assert (item_index <= g_debug_tab.lines.len);
- row_buffer_append (buffer, g_debug_tab.lines.vector[item_index], 0);
+ hard_assert (item_index <= g_debug_tab.super.item_count);
+ struct debug_item *item = &g_debug_tab.items[item_index];
+
+ char buf[16];
+ struct tm tm;
+ time_t when = item->timestamp / 1000;
+ strftime (buf, sizeof buf, "%T", localtime_r (&when, &tm));
+
+ char *prefix = xstrdup_printf
+ ("%s.%03d", buf, (int) (item->timestamp % 1000));
+ row_buffer_append (buffer, prefix, 0);
+ free (prefix);
+
+ row_buffer_append (buffer, " ", item->attrs);
+ row_buffer_append (buffer, item->text, item->attrs);
+
+ // We override the formatting including colors -- do it for the whole line
+ if (buffer->total_width > width)
+ row_buffer_ellipsis (buffer, width, item->attrs);
+ while (buffer->total_width < width)
+ row_buffer_append (buffer, " ", item->attrs);
+}
+
+static void
+debug_tab_push (const char *message, chtype attrs)
+{
+ // TODO: uh... aren't we rather going to write our own abstraction?
+ if (g_debug_tab.items_alloc <= g_debug_tab.super.item_count)
+ {
+ g_debug_tab.items = xreallocarray (g_debug_tab.items,
+ sizeof *g_debug_tab.items, (g_debug_tab.items_alloc <<= 1));
+ }
+
+ // TODO: there should be a better, more efficient mechanism for this
+ struct debug_item *item =
+ &g_debug_tab.items[g_debug_tab.super.item_count++];
+ item->text = xstrdup (message);
+ item->attrs = attrs;
+
+ struct timespec tp;
+ hard_assert (clock_gettime (CLOCK_REALTIME, &tp) != -1);
+ item->timestamp = (int64_t) tp.tv_sec * 1000
+ + (int64_t) tp.tv_nsec / 1000000;
+
+ app_redraw_view ();
}
static struct tab *
debug_tab_create (void)
{
- str_vector_init (&g_debug_tab.lines);
+ g_debug_tab.items = xcalloc
+ ((g_debug_tab.items_alloc = 16), sizeof *g_debug_tab.items);
g_debug_tab.active = true;
struct tab *super = &g_debug_tab.super;
@@ -1797,6 +1867,28 @@ debug_tab_create (void)
return super;
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static void
+mpd_on_io_hook (void *user_data, bool outgoing, const char *line)
+{
+ (void) user_data;
+
+ struct str s;
+ str_init (&s);
+ if (outgoing)
+ {
+ str_append_printf (&s, "<< %s", line);
+ debug_tab_push (s.str, APP_ATTR (OUTGOING));
+ }
+ else
+ {
+ str_append_printf (&s, ">> %s", line);
+ debug_tab_push (s.str, APP_ATTR (INCOMING));
+ }
+ str_free (&s);
+}
+
// --- Initialisation, event handling ------------------------------------------
static void
@@ -1869,9 +1961,6 @@ static void
app_log_handler (void *user_data, const char *quote, const char *fmt,
va_list ap)
{
- // TODO: we might want to make use of the user_data (attribute?)
- (void) user_data;
-
// We certainly don't want to end up in a possibly infinite recursion
static bool in_processing;
if (in_processing)
@@ -1891,10 +1980,8 @@ app_log_handler (void *user_data, const char *quote, const char *fmt,
fprintf (stderr, "%s\n", message.str);
else if (g_debug_tab.active)
{
- str_vector_add (&g_debug_tab.lines, message.str);
- // TODO: there should be a better, more efficient mechanism for this
- g_debug_tab.super.item_count++;
- app_redraw_view ();
+ debug_tab_push (message.str,
+ user_data == NULL ? 0 : g_ctx.attrs[(intptr_t) user_data].attrs);
}
else
{
--
cgit v1.2.3-70-g09d2