Rearrange code

I need to update tabs from MPD message handlers.
This commit is contained in:
Přemysl Eric Janouch 2016-10-05 01:12:31 +02:00
parent 319d0faffa
commit a59f0b237e
Signed by: p
GPG Key ID: B715679E3A361BE6
1 changed files with 199 additions and 203 deletions

404
nncmpp.c
View File

@ -1467,69 +1467,152 @@ app_process_termo_event (termo_key_t *event)
return true; return true;
} }
// --- Signals ----------------------------------------------------------------- // --- Info tab ----------------------------------------------------------------
static int g_signal_pipe[2]; ///< A pipe used to signal... signals // TODO: either find something else to put in here or remove the wrapper struct
static struct
/// Program termination has been requested by a signal {
static volatile sig_atomic_t g_termination_requested; struct tab super; ///< Parent class
/// The window has changed in size }
static volatile sig_atomic_t g_winch_received; g_info_tab;
static void static void
signals_postpone_handling (char id) info_tab_on_item_draw (struct tab *self, unsigned item_index,
struct row_buffer *buffer, int width)
{ {
int original_errno = errno; (void) self;
if (write (g_signal_pipe[1], &id, 1) == -1) (void) width;
soft_assert (errno == EAGAIN);
errno = original_errno; // TODO
}
static struct tab *
info_tab_create (void)
{
struct tab *super = &g_info_tab.super;
tab_init (super, "Info");
super->on_item_draw = info_tab_on_item_draw;
super->item_count = 0;
super->item_selected = 0;
return super;
}
// --- Help tab ----------------------------------------------------------------
// TODO: either find something else to put in here or remove the wrapper struct
static struct
{
struct tab super; ///< Parent class
}
g_help_tab;
static void
help_tab_on_item_draw (struct tab *self, unsigned item_index,
struct row_buffer *buffer, int width)
{
(void) self;
(void) width;
// TODO: group them the other way around for clarity
hard_assert (item_index < N_ELEMENTS (g_default_bindings));
struct binding *binding = &g_default_bindings[item_index];
char *text = xstrdup_printf ("%-12s %s",
binding->key, g_user_actions[binding->action].description);
row_buffer_append (buffer, text, 0);
free (text);
}
static struct tab *
help_tab_create (void)
{
struct tab *super = &g_help_tab.super;
tab_init (super, "Help");
super->on_item_draw = help_tab_on_item_draw;
super->item_count = N_ELEMENTS (g_default_bindings);
super->item_selected = 0;
return super;
}
// --- 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 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, int width)
{
(void) self;
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 static void
signals_superhandler (int signum) debug_tab_push (const char *message, chtype attrs)
{ {
switch (signum) // TODO: uh... aren't we rather going to write our own abstraction?
if (g_debug_tab.items_alloc <= g_debug_tab.super.item_count)
{ {
case SIGWINCH: g_debug_tab.items = xreallocarray (g_debug_tab.items,
g_winch_received = true; sizeof *g_debug_tab.items, (g_debug_tab.items_alloc <<= 1));
signals_postpone_handling ('w');
break;
case SIGINT:
case SIGTERM:
g_termination_requested = true;
signals_postpone_handling ('t');
break;
default:
hard_assert (!"unhandled signal");
}
} }
static void // TODO: there should be a better, more efficient mechanism for this
signals_setup_handlers (void) struct debug_item *item =
&g_debug_tab.items[g_debug_tab.super.item_count++];
item->text = xstrdup (message);
item->attrs = attrs;
item->timestamp = clock_msec (CLOCK_REALTIME);
app_invalidate ();
}
static struct tab *
debug_tab_create (void)
{ {
if (pipe (g_signal_pipe) == -1) g_debug_tab.items = xcalloc
exit_fatal ("%s: %s", "pipe", strerror (errno)); ((g_debug_tab.items_alloc = 16), sizeof *g_debug_tab.items);
g_debug_tab.active = true;
set_cloexec (g_signal_pipe[0]); struct tab *super = &g_debug_tab.super;
set_cloexec (g_signal_pipe[1]); tab_init (super, "Debug");
super->on_item_draw = debug_tab_on_item_draw;
// So that the pipe cannot overflow; it would make write() block within super->item_count = 0;
// the signal handler, which is something we really don't want to happen. super->item_selected = 0;
// The same holds true for read(). return super;
set_blocking (g_signal_pipe[0], false);
set_blocking (g_signal_pipe[1], false);
signal (SIGPIPE, SIG_IGN);
struct sigaction sa;
sa.sa_flags = SA_RESTART;
sa.sa_handler = signals_superhandler;
sigemptyset (&sa.sa_mask);
if (sigaction (SIGWINCH, &sa, NULL) == -1
|| sigaction (SIGINT, &sa, NULL) == -1
|| sigaction (SIGTERM, &sa, NULL) == -1)
exit_fatal ("sigaction: %s", strerror (errno));
} }
// --- MPD interface ----------------------------------------------------------- // --- MPD interface -----------------------------------------------------------
@ -1735,7 +1818,25 @@ mpd_on_failure (void *user_data)
mpd_queue_reconnect (); mpd_queue_reconnect ();
} }
static void mpd_on_io_hook (void *user_data, bool outgoing, const char *line); 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);
}
static void static void
app_on_reconnect (void *user_data) app_on_reconnect (void *user_data)
@ -1780,174 +1881,69 @@ app_on_reconnect (void *user_data)
free (address); free (address);
} }
// --- Help tab ---------------------------------------------------------------- // --- Signals -----------------------------------------------------------------
// TODO: either find something else to put in here or remove the wrapper struct static int g_signal_pipe[2]; ///< A pipe used to signal... signals
static struct
{ /// Program termination has been requested by a signal
struct tab super; ///< Parent class static volatile sig_atomic_t g_termination_requested;
} /// The window has changed in size
g_help_tab; static volatile sig_atomic_t g_winch_received;
static void static void
help_tab_on_item_draw (struct tab *self, unsigned item_index, signals_postpone_handling (char id)
struct row_buffer *buffer, int width)
{ {
(void) self; int original_errno = errno;
(void) width; if (write (g_signal_pipe[1], &id, 1) == -1)
soft_assert (errno == EAGAIN);
// TODO: group them the other way around for clarity errno = original_errno;
hard_assert (item_index < N_ELEMENTS (g_default_bindings));
struct binding *binding = &g_default_bindings[item_index];
char *text = xstrdup_printf ("%-12s %s",
binding->key, g_user_actions[binding->action].description);
row_buffer_append (buffer, text, 0);
free (text);
}
static struct tab *
help_tab_create (void)
{
struct tab *super = &g_help_tab.super;
tab_init (super, "Help");
super->on_item_draw = help_tab_on_item_draw;
super->item_count = N_ELEMENTS (g_default_bindings);
super->item_selected = 0;
return super;
}
// --- Info tab ----------------------------------------------------------------
// TODO: either find something else to put in here or remove the wrapper struct
static struct
{
struct tab super; ///< Parent class
}
g_info_tab;
static void
info_tab_on_item_draw (struct tab *self, unsigned item_index,
struct row_buffer *buffer, int width)
{
(void) self;
(void) width;
// TODO
}
static struct tab *
info_tab_create (void)
{
struct tab *super = &g_info_tab.super;
tab_init (super, "Info");
super->on_item_draw = info_tab_on_item_draw;
super->item_count = 0;
super->item_selected = 0;
return super;
}
// --- 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 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, int width)
{
(void) self;
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 static void
debug_tab_push (const char *message, chtype attrs) signals_superhandler (int signum)
{ {
// TODO: uh... aren't we rather going to write our own abstraction? switch (signum)
if (g_debug_tab.items_alloc <= g_debug_tab.super.item_count)
{ {
g_debug_tab.items = xreallocarray (g_debug_tab.items, case SIGWINCH:
sizeof *g_debug_tab.items, (g_debug_tab.items_alloc <<= 1)); g_winch_received = true;
signals_postpone_handling ('w');
break;
case SIGINT:
case SIGTERM:
g_termination_requested = true;
signals_postpone_handling ('t');
break;
default:
hard_assert (!"unhandled signal");
} }
// 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;
item->timestamp = clock_msec (CLOCK_REALTIME);
app_invalidate ();
} }
static struct tab *
debug_tab_create (void)
{
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;
tab_init (super, "Debug");
super->on_item_draw = debug_tab_on_item_draw;
super->item_count = 0;
super->item_selected = 0;
return super;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void static void
mpd_on_io_hook (void *user_data, bool outgoing, const char *line) signals_setup_handlers (void)
{ {
(void) user_data; if (pipe (g_signal_pipe) == -1)
exit_fatal ("%s: %s", "pipe", strerror (errno));
struct str s; set_cloexec (g_signal_pipe[0]);
str_init (&s); set_cloexec (g_signal_pipe[1]);
if (outgoing)
{ // So that the pipe cannot overflow; it would make write() block within
str_append_printf (&s, "<< %s", line); // the signal handler, which is something we really don't want to happen.
debug_tab_push (s.str, APP_ATTR (OUTGOING)); // The same holds true for read().
} set_blocking (g_signal_pipe[0], false);
else set_blocking (g_signal_pipe[1], false);
{
str_append_printf (&s, ">> %s", line); signal (SIGPIPE, SIG_IGN);
debug_tab_push (s.str, APP_ATTR (INCOMING));
} struct sigaction sa;
str_free (&s); sa.sa_flags = SA_RESTART;
sa.sa_handler = signals_superhandler;
sigemptyset (&sa.sa_mask);
if (sigaction (SIGWINCH, &sa, NULL) == -1
|| sigaction (SIGINT, &sa, NULL) == -1
|| sigaction (SIGTERM, &sa, NULL) == -1)
exit_fatal ("sigaction: %s", strerror (errno));
} }
// --- Initialisation, event handling ------------------------------------------ // --- Initialisation, event handling ------------------------------------------