diff options
| author | Přemysl Janouch <p.janouch@gmail.com> | 2016-10-03 19:43:51 +0200 | 
|---|---|---|
| committer | Přemysl Janouch <p.janouch@gmail.com> | 2016-10-03 19:43:51 +0200 | 
| commit | 1e39ae52c510609fd50adc6ba3d7440b79ac3e33 (patch) | |
| tree | a03bc22120e0419bec71fdd9ea0d5efd256f9c02 | |
| parent | 91b6a799c8242d31b3ef511f67f02b1f348b4645 (diff) | |
| download | nncmpp-1e39ae52c510609fd50adc6ba3d7440b79ac3e33.tar.gz nncmpp-1e39ae52c510609fd50adc6ba3d7440b79ac3e33.tar.xz nncmpp-1e39ae52c510609fd50adc6ba3d7440b79ac3e33.zip | |
Make the debug tab prettier
And the MPD code a little bit more generic.
| -rw-r--r-- | mpd.c | 11 | ||||
| -rw-r--r-- | nncmpp.c | 187 | 
2 files changed, 146 insertions, 52 deletions
| @@ -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); @@ -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  	{ | 
