Rearrange code
I need to update tabs from MPD message handlers.
This commit is contained in:
parent
319d0faffa
commit
a59f0b237e
402
nncmpp.c
402
nncmpp.c
|
@ -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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 void
|
static struct tab *
|
||||||
signals_setup_handlers (void)
|
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 ------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue