Add infrastructure for field highlighting
This commit is contained in:
parent
d2aed61783
commit
80e511f3cb
225
hex.c
225
hex.c
|
@ -23,18 +23,23 @@
|
||||||
// We "need" to have an enum for attributes before including liberty.
|
// We "need" to have an enum for attributes before including liberty.
|
||||||
// Avoiding colours in the defaults here in order to support dumb terminals.
|
// Avoiding colours in the defaults here in order to support dumb terminals.
|
||||||
#define ATTRIBUTE_TABLE(XX) \
|
#define ATTRIBUTE_TABLE(XX) \
|
||||||
XX( FOOTER, "footer", -1, -1, 0 ) \
|
XX( FOOTER, "footer", -1, -1, 0 ) \
|
||||||
XX( FOOTER_HL, "footer_hl", -1, -1, A_BOLD ) \
|
XX( FOOTER_HL, "footer_hl", -1, -1, A_BOLD ) \
|
||||||
/* Bar */ \
|
/* Bar */ \
|
||||||
XX( BAR, "bar", -1, -1, A_REVERSE ) \
|
XX( BAR, "bar", -1, -1, A_REVERSE ) \
|
||||||
XX( BAR_HL, "bar_hl", -1, -1, A_REVERSE | A_BOLD ) \
|
XX( BAR_HL, "bar_hl", -1, -1, A_REVERSE | A_BOLD ) \
|
||||||
/* View */ \
|
/* View */ \
|
||||||
XX( EVEN, "even", -1, -1, 0 ) \
|
XX( EVEN, "even", -1, -1, 0 ) \
|
||||||
XX( ODD, "odd", -1, -1, 0 ) \
|
XX( ODD, "odd", -1, -1, 0 ) \
|
||||||
XX( SELECTION, "selection", -1, -1, A_REVERSE ) \
|
XX( SELECTION, "selection", -1, -1, A_REVERSE ) \
|
||||||
/* These are for debugging only */ \
|
/* Field highlights */ \
|
||||||
XX( WARNING, "warning", 3, -1, 0 ) \
|
XX( C1, "c1", 22, 194, 0 ) \
|
||||||
XX( ERROR, "error", 1, -1, 0 )
|
XX( C2, "c2", 88, 224, 0 ) \
|
||||||
|
XX( C3, "c3", 58, 229, 0 ) \
|
||||||
|
XX( C4, "c4", 20, 189, 0 ) \
|
||||||
|
/* These are for debugging only */ \
|
||||||
|
XX( WARNING, "warning", 3, -1, 0 ) \
|
||||||
|
XX( ERROR, "error", 1, -1, 0 )
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -88,6 +93,26 @@ update_curses_terminal_size (void)
|
||||||
#endif // HAVE_RESIZETERM && TIOCGWINSZ
|
#endif // HAVE_RESIZETERM && TIOCGWINSZ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Simple array support ----------------------------------------------------
|
||||||
|
|
||||||
|
// Primitives for arrays
|
||||||
|
|
||||||
|
#define ARRAY(type, name) type *name; size_t name ## _len, name ## _size;
|
||||||
|
#define ARRAY_INIT(a) \
|
||||||
|
BLOCK_START \
|
||||||
|
a = xcalloc (sizeof *a, (a ## _size = 16)); \
|
||||||
|
a ## _len = 0; \
|
||||||
|
BLOCK_END
|
||||||
|
#define ARRAY_RESERVE(a, n) \
|
||||||
|
array_reserve ((void **) &a, sizeof *a, a ## _len + n, &a ## _size)
|
||||||
|
|
||||||
|
static void
|
||||||
|
array_reserve (void **array, size_t element_size, size_t len, size_t *size)
|
||||||
|
{
|
||||||
|
while (len > *size)
|
||||||
|
*array = xreallocarray (*array, element_size, *size <<= 1);
|
||||||
|
}
|
||||||
|
|
||||||
// --- Application -------------------------------------------------------------
|
// --- Application -------------------------------------------------------------
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -101,6 +126,26 @@ enum endianity
|
||||||
ENDIANITY_BE ///< Big endian
|
ENDIANITY_BE ///< Big endian
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mark
|
||||||
|
{
|
||||||
|
int64_t offset; ///< Offset of the mark
|
||||||
|
int64_t len; ///< Length of the mark
|
||||||
|
size_t description; ///< Textual description string offset
|
||||||
|
};
|
||||||
|
|
||||||
|
// XXX: can we avoid constructing the marks_by_offset lookup array?
|
||||||
|
// How much memory is it even going to consume in reality?
|
||||||
|
|
||||||
|
/// This is the final result suitable for display, including unmarked areas.
|
||||||
|
/// We might infer `color` from the index of this entry but then unmarked areas
|
||||||
|
/// would skip a color, which is undesired.
|
||||||
|
struct marks_by_offset
|
||||||
|
{
|
||||||
|
int64_t offset; ///< Offset in the file
|
||||||
|
struct mark **marks; ///< Sentinelled array of mark pointers
|
||||||
|
int color; ///< Color of the area until next offset
|
||||||
|
};
|
||||||
|
|
||||||
static struct app_context
|
static struct app_context
|
||||||
{
|
{
|
||||||
// Event loop:
|
// Event loop:
|
||||||
|
@ -124,6 +169,13 @@ static struct app_context
|
||||||
int64_t data_len; ///< Length of the data
|
int64_t data_len; ///< Length of the data
|
||||||
int64_t data_offset; ///< Offset of the data within the file
|
int64_t data_offset; ///< Offset of the data within the file
|
||||||
|
|
||||||
|
// Field marking:
|
||||||
|
|
||||||
|
ARRAY (struct mark, marks) ///< Marks
|
||||||
|
struct str mark_strings; ///< Storage for mark descriptions
|
||||||
|
|
||||||
|
ARRAY (struct marks_by_offset, marks_by_offset)
|
||||||
|
|
||||||
// View:
|
// View:
|
||||||
|
|
||||||
int64_t view_top; ///< Offset of the top of the screen
|
int64_t view_top; ///< Offset of the top of the screen
|
||||||
|
@ -239,6 +291,10 @@ app_init_context (void)
|
||||||
poller_init (&g_ctx.poller);
|
poller_init (&g_ctx.poller);
|
||||||
config_init (&g_ctx.config);
|
config_init (&g_ctx.config);
|
||||||
|
|
||||||
|
ARRAY_INIT (g_ctx.marks);
|
||||||
|
str_init (&g_ctx.mark_strings);
|
||||||
|
ARRAY_INIT (g_ctx.marks_by_offset);
|
||||||
|
|
||||||
// This is also approximately what libunistring does internally,
|
// This is also approximately what libunistring does internally,
|
||||||
// since the locale name is canonicalized by locale_charset().
|
// since the locale name is canonicalized by locale_charset().
|
||||||
// Note that non-Unicode locales are handled pretty inefficiently.
|
// Note that non-Unicode locales are handled pretty inefficiently.
|
||||||
|
@ -268,6 +324,8 @@ app_init_terminal (void)
|
||||||
if (g_ctx.attrs[a].fg >= COLORS || g_ctx.attrs[a].fg < -1
|
if (g_ctx.attrs[a].fg >= COLORS || g_ctx.attrs[a].fg < -1
|
||||||
|| g_ctx.attrs[a].bg >= COLORS || g_ctx.attrs[a].bg < -1)
|
|| g_ctx.attrs[a].bg >= COLORS || g_ctx.attrs[a].bg < -1)
|
||||||
{
|
{
|
||||||
|
// FIXME: we need a 256color default palette that fails gracefully
|
||||||
|
// to something like underlined fields
|
||||||
app_init_attributes ();
|
app_init_attributes ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -283,6 +341,13 @@ app_free_context (void)
|
||||||
config_free (&g_ctx.config);
|
config_free (&g_ctx.config);
|
||||||
poller_free (&g_ctx.poller);
|
poller_free (&g_ctx.poller);
|
||||||
|
|
||||||
|
free (g_ctx.marks);
|
||||||
|
str_free (&g_ctx.mark_strings);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < g_ctx.marks_by_offset_len; i++)
|
||||||
|
free (g_ctx.marks_by_offset[i].marks);
|
||||||
|
free (g_ctx.marks_by_offset);
|
||||||
|
|
||||||
free (g_ctx.message);
|
free (g_ctx.message);
|
||||||
|
|
||||||
free (g_ctx.filename);
|
free (g_ctx.filename);
|
||||||
|
@ -317,6 +382,106 @@ app_is_character_in_locale (ucs4_t ch)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Field marking -----------------------------------------------------------
|
||||||
|
|
||||||
|
/// Find the "marks_by_offset" object covering the offset (if any)
|
||||||
|
static ssize_t
|
||||||
|
app_find_marks (int64_t offset)
|
||||||
|
{
|
||||||
|
ssize_t min = 0, end = g_ctx.marks_by_offset_len;
|
||||||
|
while (min < end)
|
||||||
|
{
|
||||||
|
ssize_t mid = min + (end - min) / 2;
|
||||||
|
if (offset >= g_ctx.marks_by_offset[mid].offset)
|
||||||
|
min = mid + 1;
|
||||||
|
else
|
||||||
|
end = mid;
|
||||||
|
}
|
||||||
|
return min - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct marks_by_offset *
|
||||||
|
app_marks_at_offset (int64_t offset)
|
||||||
|
{
|
||||||
|
ssize_t i = app_find_marks (offset);
|
||||||
|
if (i < 0 || (size_t) i >= g_ctx.marks_by_offset_len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
struct marks_by_offset *marks = &g_ctx.marks_by_offset[i];
|
||||||
|
if (marks->offset > offset)
|
||||||
|
return NULL;
|
||||||
|
return marks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
app_mark_cmp (const void *first, const void *second)
|
||||||
|
{
|
||||||
|
const struct mark *a = first, *b = second;
|
||||||
|
// This ordering is pretty much arbitrary, seemed to make sense
|
||||||
|
if (a->offset < b->offset) return -1;
|
||||||
|
if (a->offset > b->offset) return 1;
|
||||||
|
if (a->len < b->len) return 1;
|
||||||
|
if (a->len > b->len) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
app_flatten_marks (void)
|
||||||
|
{
|
||||||
|
qsort (g_ctx.marks, g_ctx.marks_len, sizeof *g_ctx.marks, app_mark_cmp);
|
||||||
|
if (!g_ctx.marks_len)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ARRAY (struct mark *, current)
|
||||||
|
ARRAY_INIT (current);
|
||||||
|
int current_color = 0;
|
||||||
|
|
||||||
|
struct mark *next = g_ctx.marks;
|
||||||
|
struct mark *end = next + g_ctx.marks_len;
|
||||||
|
while (current_len || next < end)
|
||||||
|
{
|
||||||
|
// Find the closest offset at which marks change
|
||||||
|
int64_t closest = g_ctx.data_offset + g_ctx.data_len;
|
||||||
|
if (next < end)
|
||||||
|
closest = next->offset;
|
||||||
|
for (size_t i = 0; i < current_len; i++)
|
||||||
|
closest = MIN (closest, current[i]->offset + current[i]->len);
|
||||||
|
|
||||||
|
// Remove from "current" marks that have ended
|
||||||
|
for (size_t i = 0; i < current_len; i++)
|
||||||
|
{
|
||||||
|
if (closest == current[i]->offset + current[i]->len)
|
||||||
|
memmove (current + i, current + i + 1,
|
||||||
|
(--current_len - i) * sizeof *current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any new marks at "closest"
|
||||||
|
while (next < end && next->offset == closest)
|
||||||
|
{
|
||||||
|
ARRAY_RESERVE (current, 1);
|
||||||
|
current[current_len++] = next++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save marks at that offset to be used by rendering
|
||||||
|
struct mark **marks = NULL;
|
||||||
|
int color = -1;
|
||||||
|
|
||||||
|
if (current_len)
|
||||||
|
{
|
||||||
|
marks = memcpy (xcalloc (sizeof *marks, current_len + 1),
|
||||||
|
current, sizeof *marks * current_len);
|
||||||
|
|
||||||
|
color = ATTRIBUTE_C1 + current_color++;
|
||||||
|
current_color %= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARRAY_RESERVE (g_ctx.marks_by_offset, 1);
|
||||||
|
g_ctx.marks_by_offset[g_ctx.marks_by_offset_len++] =
|
||||||
|
(struct marks_by_offset) { closest, marks, color };
|
||||||
|
}
|
||||||
|
free (current);
|
||||||
|
}
|
||||||
|
|
||||||
// --- Rendering ---------------------------------------------------------------
|
// --- Rendering ---------------------------------------------------------------
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -379,6 +544,10 @@ app_make_row (struct row_buffer *buf, int64_t addr, int attrs)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int cell_attrs = attrs;
|
int cell_attrs = attrs;
|
||||||
|
struct marks_by_offset *marks = app_marks_at_offset (cell_addr);
|
||||||
|
if (marks && marks->color >= 0)
|
||||||
|
cell_attrs = g_ctx.attrs[marks->color].attrs;
|
||||||
|
|
||||||
if (cell_addr >= g_ctx.view_cursor
|
if (cell_addr >= g_ctx.view_cursor
|
||||||
&& cell_addr < g_ctx.view_cursor + 8)
|
&& cell_addr < g_ctx.view_cursor + 8)
|
||||||
cell_attrs |= A_UNDERLINE;
|
cell_attrs |= A_UNDERLINE;
|
||||||
|
@ -418,6 +587,33 @@ app_draw_view (void)
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
static void
|
||||||
|
app_draw_info (void)
|
||||||
|
{
|
||||||
|
const struct marks_by_offset *marks;
|
||||||
|
if (!(marks = app_marks_at_offset (g_ctx.view_cursor)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int x_offset = 70;
|
||||||
|
struct mark **iter = marks->marks;
|
||||||
|
for (int y = 0; y < app_visible_rows (); y++)
|
||||||
|
{
|
||||||
|
struct mark *mark;
|
||||||
|
if (!iter || !(mark = *iter++))
|
||||||
|
break;
|
||||||
|
|
||||||
|
struct row_buffer buf;
|
||||||
|
row_buffer_init (&buf);
|
||||||
|
row_buffer_append (&buf,
|
||||||
|
g_ctx.mark_strings.str + mark->description, 0);
|
||||||
|
|
||||||
|
move (y, x_offset);
|
||||||
|
app_flush_buffer (&buf, COLS - x_offset, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
static uint64_t
|
static uint64_t
|
||||||
app_decode (const uint8_t *p, size_t len, enum endianity endianity)
|
app_decode (const uint8_t *p, size_t len, enum endianity endianity)
|
||||||
{
|
{
|
||||||
|
@ -564,6 +760,7 @@ app_on_refresh (void *user_data)
|
||||||
|
|
||||||
erase ();
|
erase ();
|
||||||
app_draw_view ();
|
app_draw_view ();
|
||||||
|
app_draw_info ();
|
||||||
app_draw_footer ();
|
app_draw_footer ();
|
||||||
|
|
||||||
int64_t diff = g_ctx.view_cursor - g_ctx.view_top;
|
int64_t diff = g_ctx.view_cursor - g_ctx.view_top;
|
||||||
|
@ -1060,6 +1257,8 @@ app_init_poller_events (void)
|
||||||
g_ctx.refresh_event.dispatcher = app_on_refresh;
|
g_ctx.refresh_event.dispatcher = app_on_refresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
/// Decode size arguments according to similar rules to those that dd(1) uses;
|
/// Decode size arguments according to similar rules to those that dd(1) uses;
|
||||||
/// we support octal and hexadecimal numbers but they clash with suffixes
|
/// we support octal and hexadecimal numbers but they clash with suffixes
|
||||||
static bool
|
static bool
|
||||||
|
@ -1200,6 +1399,8 @@ main (int argc, char *argv[])
|
||||||
print_warning ("failed to set the locale");
|
print_warning ("failed to set the locale");
|
||||||
|
|
||||||
app_init_context ();
|
app_init_context ();
|
||||||
|
app_flatten_marks ();
|
||||||
|
|
||||||
app_load_configuration ();
|
app_load_configuration ();
|
||||||
app_init_terminal ();
|
app_init_terminal ();
|
||||||
signals_setup_handlers ();
|
signals_setup_handlers ();
|
||||||
|
|
Loading…
Reference in New Issue