xF: render formatting
This commit is contained in:
@@ -34,6 +34,27 @@
|
||||
|
||||
// ~~~ Buffer lines ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
enum buffer_line_item_style
|
||||
{
|
||||
BUFFER_LINE_ITEM_BOLD = 1 << 0,
|
||||
BUFFER_LINE_ITEM_ITALIC = 1 << 1,
|
||||
BUFFER_LINE_ITEM_UNDERLINE = 1 << 2,
|
||||
BUFFER_LINE_ITEM_INVERSE = 1 << 3,
|
||||
BUFFER_LINE_ITEM_CROSSED_OUT = 1 << 4,
|
||||
BUFFER_LINE_ITEM_MONOSPACE = 1 << 5,
|
||||
};
|
||||
|
||||
// See: union relay_item_data
|
||||
struct buffer_line_item
|
||||
{
|
||||
LIST_HEADER (struct buffer_line_item)
|
||||
|
||||
int16_t fg;
|
||||
int16_t bg;
|
||||
unsigned style;
|
||||
char text[];
|
||||
};
|
||||
|
||||
// See: struct relay_event_data_buffer_line
|
||||
struct buffer_line
|
||||
{
|
||||
@@ -46,29 +67,50 @@ struct buffer_line
|
||||
bool is_highlight;
|
||||
enum relay_rendition rendition;
|
||||
uint64_t when; ///< Unix timestamp in milliseconds
|
||||
char text[];
|
||||
struct buffer_line_item *items;
|
||||
struct buffer_line_item *items_tail;
|
||||
};
|
||||
|
||||
static struct buffer_line *
|
||||
buffer_line_new_text (const char *text, enum relay_rendition rendition,
|
||||
uint64_t when)
|
||||
static void
|
||||
buffer_line_item_add (struct buffer_line_item **head,
|
||||
struct buffer_line_item **tail, const char *text, int16_t fg, int16_t bg,
|
||||
unsigned style)
|
||||
{
|
||||
size_t len = strlen (text);
|
||||
struct buffer_line *self = xcalloc (1, sizeof *self + len + 1);
|
||||
memcpy (self->text, text, len + 1);
|
||||
self->rendition = rendition;
|
||||
self->when = when;
|
||||
return self;
|
||||
struct buffer_line_item *item = xcalloc (1, sizeof *item + len + 1);
|
||||
item->fg = fg;
|
||||
item->bg = bg;
|
||||
item->style = style;
|
||||
memcpy (item->text, text, len + 1);
|
||||
LIST_APPEND_WITH_TAIL (*head, *tail, item);
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_line_items_free (struct buffer_line_item *items)
|
||||
{
|
||||
LIST_FOR_EACH (struct buffer_line_item, item, items)
|
||||
free (item);
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_line_free (struct buffer_line *self)
|
||||
{
|
||||
buffer_line_items_free (self->items);
|
||||
free (self);
|
||||
}
|
||||
|
||||
static struct buffer_line *
|
||||
buffer_line_clone (const struct buffer_line *line)
|
||||
{
|
||||
struct buffer_line *self =
|
||||
buffer_line_new_text (line->text, line->rendition, line->when);
|
||||
struct buffer_line *self = xcalloc (1, sizeof *self);
|
||||
self->rendition = line->rendition;
|
||||
self->when = line->when;
|
||||
self->leaked = line->leaked;
|
||||
self->is_unimportant = line->is_unimportant;
|
||||
self->is_highlight = line->is_highlight;
|
||||
LIST_FOR_EACH (struct buffer_line_item, item, line->items)
|
||||
buffer_line_item_add (&self->items, &self->items_tail,
|
||||
item->text, item->fg, item->bg, item->style);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -82,7 +124,8 @@ struct buffer
|
||||
char *buffer_name;
|
||||
enum relay_buffer_kind kind;
|
||||
char *server_name;
|
||||
char *topic;
|
||||
struct buffer_line_item *topic;
|
||||
struct buffer_line_item *topic_tail;
|
||||
char *modes;
|
||||
bool hide_unimportant;
|
||||
|
||||
@@ -132,7 +175,7 @@ static void
|
||||
buffer_clear (struct buffer *self)
|
||||
{
|
||||
LIST_FOR_EACH (struct buffer_line, iter, self->lines)
|
||||
free (iter);
|
||||
buffer_line_free (iter);
|
||||
self->lines = self->lines_tail = NULL;
|
||||
}
|
||||
|
||||
@@ -151,7 +194,7 @@ buffer_pop_excess_lines (struct buffer *self)
|
||||
{
|
||||
struct buffer_line *line = self->lines;
|
||||
LIST_UNLINK_WITH_TAIL (self->lines, self->lines_tail, line);
|
||||
free (line);
|
||||
buffer_line_free (line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +203,7 @@ buffer_destroy (struct buffer *self)
|
||||
{
|
||||
free (self->buffer_name);
|
||||
free (self->server_name);
|
||||
free (self->topic);
|
||||
buffer_line_items_free (self->topic);
|
||||
free (self->modes);
|
||||
free (self->input);
|
||||
strv_free (&self->history);
|
||||
@@ -269,7 +312,23 @@ struct binding
|
||||
XX (ACTION, 1, -1, 0) \
|
||||
XX (LEAKED, 248, -1, 0) \
|
||||
XX (PROMPT, -1, 255, A_BOLD) \
|
||||
XX (SCROLLBAR, 250, -1, 0)
|
||||
XX (SCROLLBAR, 250, -1, 0) \
|
||||
XX (IRC_0, 16, -1, 0) \
|
||||
XX (IRC_1, 124, -1, 0) \
|
||||
XX (IRC_2, 34, -1, 0) \
|
||||
XX (IRC_3, 142, -1, 0) \
|
||||
XX (IRC_4, 19, -1, 0) \
|
||||
XX (IRC_5, 127, -1, 0) \
|
||||
XX (IRC_6, 37, -1, 0) \
|
||||
XX (IRC_7, 252, -1, 0) \
|
||||
XX (IRC_8, 245, -1, 0) \
|
||||
XX (IRC_9, 196, -1, 0) \
|
||||
XX (IRC_10, 46, -1, 0) \
|
||||
XX (IRC_11, 226, -1, 0) \
|
||||
XX (IRC_12, 21, -1, 0) \
|
||||
XX (IRC_13, 201, -1, 0) \
|
||||
XX (IRC_14, 51, -1, 0) \
|
||||
XX (IRC_15, 231, -1, 0)
|
||||
|
||||
enum app_attribute
|
||||
{
|
||||
@@ -508,7 +567,11 @@ app_queue_local_line (enum relay_rendition rendition, const char *text)
|
||||
struct timespec tp = {};
|
||||
hard_assert (clock_gettime (CLOCK_REALTIME, &tp) != -1);
|
||||
uint64_t when = (uint64_t) tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
|
||||
struct buffer_line *line = buffer_line_new_text (text, rendition, when);
|
||||
|
||||
struct buffer_line *line = xcalloc (1, sizeof *line);
|
||||
line->rendition = rendition;
|
||||
line->when = when;
|
||||
buffer_line_item_add (&line->items, &line->items_tail, text, -1, -1, 0);
|
||||
LIST_APPEND_WITH_TAIL (g.local_lines, g.local_lines_tail, line);
|
||||
app_flush_local_lines ();
|
||||
}
|
||||
@@ -528,9 +591,7 @@ app_log_handler (void *user_data, const char *quote, const char *fmt,
|
||||
str_append_vprintf (&message, fmt, ap);
|
||||
|
||||
enum relay_rendition rendition = RELAY_RENDITION_BARE;
|
||||
if (!strcmp (quote, "-- "))
|
||||
rendition = RELAY_RENDITION_STATUS;
|
||||
else if (!strcmp (quote, "warning: ")
|
||||
if (!strcmp (quote, "warning: ")
|
||||
|| !strcmp (quote, "error: ")
|
||||
|| !strcmp (quote, "fatal: "))
|
||||
rendition = RELAY_RENDITION_ERROR;
|
||||
@@ -888,26 +949,66 @@ buffer_find (const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *
|
||||
relay_items_text (const union relay_item_data *items, uint32_t items_len)
|
||||
static void
|
||||
buffer_line_items_convert (const union relay_item_data *items, uint32_t len,
|
||||
struct buffer_line_item **head, struct buffer_line_item **tail)
|
||||
{
|
||||
struct str s = str_make ();
|
||||
for (uint32_t i = 0; i < items_len; i++)
|
||||
if (items[i].kind == RELAY_ITEM_TEXT)
|
||||
str_append_str (&s, &items[i].text.text);
|
||||
return str_steal (&s);
|
||||
int16_t fg = -1, bg = -1;
|
||||
unsigned style = 0;
|
||||
for (uint32_t i = 0; i < len; i++)
|
||||
{
|
||||
const union relay_item_data *item = &items[i];
|
||||
switch (item->kind)
|
||||
{
|
||||
case RELAY_ITEM_TEXT:
|
||||
buffer_line_item_add (head, tail,
|
||||
item->text.text.str, fg, bg, style);
|
||||
break;
|
||||
case RELAY_ITEM_RESET:
|
||||
fg = bg = -1;
|
||||
style = 0;
|
||||
break;
|
||||
case RELAY_ITEM_FG_COLOR:
|
||||
fg = item->fg_color.color;
|
||||
break;
|
||||
case RELAY_ITEM_BG_COLOR:
|
||||
bg = item->bg_color.color;
|
||||
break;
|
||||
case RELAY_ITEM_FLIP_BOLD:
|
||||
style ^= BUFFER_LINE_ITEM_BOLD;
|
||||
break;
|
||||
case RELAY_ITEM_FLIP_ITALIC:
|
||||
style ^= BUFFER_LINE_ITEM_ITALIC;
|
||||
break;
|
||||
case RELAY_ITEM_FLIP_UNDERLINE:
|
||||
style ^= BUFFER_LINE_ITEM_UNDERLINE;
|
||||
break;
|
||||
case RELAY_ITEM_FLIP_INVERSE:
|
||||
style ^= BUFFER_LINE_ITEM_INVERSE;
|
||||
break;
|
||||
case RELAY_ITEM_FLIP_CROSSED_OUT:
|
||||
style ^= BUFFER_LINE_ITEM_CROSSED_OUT;
|
||||
break;
|
||||
case RELAY_ITEM_FLIP_MONOSPACE:
|
||||
style ^= BUFFER_LINE_ITEM_MONOSPACE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct buffer_line *
|
||||
buffer_line_new (const struct relay_event_data_buffer_line *m)
|
||||
{
|
||||
char *text = relay_items_text (m->items, m->items_len);
|
||||
struct buffer_line *self =
|
||||
buffer_line_new_text (text, m->rendition, m->when);
|
||||
free (text);
|
||||
|
||||
struct buffer_line *self = xcalloc (1, sizeof *self);
|
||||
self->rendition = m->rendition;
|
||||
self->when = m->when;
|
||||
self->is_unimportant = m->is_unimportant;
|
||||
self->is_highlight = m->is_highlight;
|
||||
|
||||
buffer_line_items_convert (m->items, m->items_len,
|
||||
&self->items, &self->items_tail);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -1004,13 +1105,15 @@ relay_process_buffer_update (struct buffer *b,
|
||||
if (server_name)
|
||||
b->server_name = xstrdup (server_name);
|
||||
|
||||
cstr_set (&b->topic, NULL);
|
||||
buffer_line_items_free (b->topic);
|
||||
b->topic = b->topic_tail = NULL;
|
||||
cstr_set (&b->modes, NULL);
|
||||
if (b->kind == RELAY_BUFFER_KIND_CHANNEL)
|
||||
{
|
||||
const struct relay_buffer_context_channel *channel =
|
||||
&e->context.channel;
|
||||
b->topic = relay_items_text (channel->topic, channel->topic_len);
|
||||
buffer_line_items_convert (channel->topic, channel->topic_len,
|
||||
&b->topic, &b->topic_tail);
|
||||
b->modes = xstrdup (channel->modes.str);
|
||||
}
|
||||
}
|
||||
@@ -1748,15 +1851,21 @@ app_push (struct layout *l, struct widget *w)
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
app_label (enum app_attribute attribute, const char *text, bool fill)
|
||||
app_label_attrs (chtype attrs, unsigned extended, const char *text, bool fill)
|
||||
{
|
||||
struct widget *widget =
|
||||
g_xui.ui->label (g_attrs[attribute].attrs, 0, text ? text : "");
|
||||
g_xui.ui->label (attrs, extended, text ? text : "");
|
||||
if (fill)
|
||||
widget->width = -1;
|
||||
return widget;
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
app_label (enum app_attribute attribute, const char *text, bool fill)
|
||||
{
|
||||
return app_label_attrs (g_attrs[attribute].attrs, 0, text, fill);
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
app_button (enum app_attribute attribute, const char *text, enum action action)
|
||||
{
|
||||
@@ -1777,6 +1886,112 @@ app_padding (enum app_attribute attribute, float width)
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static void
|
||||
app_line_item_color_rgb (int color, int *r, int *g, int *b)
|
||||
{
|
||||
static const uint16_t base[] =
|
||||
{
|
||||
0x000, 0x800, 0x080, 0x880, 0x008, 0x808, 0x088, 0xccc,
|
||||
0x888, 0xf00, 0x0f0, 0xff0, 0x00f, 0xf0f, 0x0ff, 0xfff,
|
||||
};
|
||||
if (color < 16)
|
||||
{
|
||||
*r = (0xf & (base[color] >> 8)) * 0x11;
|
||||
*g = (0xf & (base[color] >> 4)) * 0x11;
|
||||
*b = (0xf & base[color]) * 0x11;
|
||||
}
|
||||
else if (color >= 232)
|
||||
*r = *g = *b = 8 + (color - 232) * 10;
|
||||
else
|
||||
{
|
||||
int i = color - 16;
|
||||
*r = i / 36;
|
||||
*g = i / 6 % 6;
|
||||
*b = i % 6;
|
||||
*r = !*r ? 0 : 55 + 40 * *r;
|
||||
*g = !*g ? 0 : 55 + 40 * *g;
|
||||
*b = !*b ? 0 : 55 + 40 * *b;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
app_line_item_color (int16_t color)
|
||||
{
|
||||
// These are the closest 256-colour palette matches for the base 16,
|
||||
// using the same RGB estimates as xC and XUI.
|
||||
if (color < 0 || color >= 256)
|
||||
return -1;
|
||||
|
||||
int r = 0, g = 0, b = 0;
|
||||
app_line_item_color_rgb (color, &r, &g, &b);
|
||||
int best = 0, best_distance = INT_MAX;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
int rr = 0, gg = 0, bb = 0;
|
||||
app_line_item_color_rgb (g_attrs[ATTRIBUTE_IRC_0 + i].fg,
|
||||
&rr, &gg, &bb);
|
||||
int distance =
|
||||
(r - rr) * (r - rr) +
|
||||
(g - gg) * (g - gg) +
|
||||
(b - bb) * (b - bb);
|
||||
if (distance < best_distance)
|
||||
{
|
||||
best = i;
|
||||
best_distance = distance;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
static chtype
|
||||
app_line_item_attrs (enum app_attribute attribute, bool leaked,
|
||||
const struct buffer_line_item *item)
|
||||
{
|
||||
chtype attrs = g_attrs[attribute].attrs;
|
||||
if (item->style & BUFFER_LINE_ITEM_BOLD)
|
||||
attrs |= A_BOLD;
|
||||
#ifdef A_ITALIC
|
||||
if (item->style & BUFFER_LINE_ITEM_ITALIC)
|
||||
attrs |= A_ITALIC;
|
||||
#endif // A_ITALIC
|
||||
if (item->style & BUFFER_LINE_ITEM_UNDERLINE)
|
||||
attrs |= A_UNDERLINE;
|
||||
|
||||
// Match xW: leaked lines retain font styling, but not their colours.
|
||||
if (leaked)
|
||||
return attrs;
|
||||
|
||||
int16_t fg = item->fg, bg = item->bg;
|
||||
if (item->style & BUFFER_LINE_ITEM_INVERSE)
|
||||
{
|
||||
int16_t tmp = fg;
|
||||
fg = bg;
|
||||
bg = tmp;
|
||||
}
|
||||
|
||||
// XUI attributes use a small, preallocated colour-pair table.
|
||||
// Preserve its common 16-colour palette, approximating extended colours,
|
||||
// and preferring the foreground when both colours are set.
|
||||
fg = app_line_item_color (fg);
|
||||
bg = app_line_item_color (bg);
|
||||
if (fg >= 0 && bg < 0)
|
||||
{
|
||||
attrs &= ~A_COLOR;
|
||||
attrs |= g_attrs[ATTRIBUTE_IRC_0 + fg].attrs & A_COLOR;
|
||||
}
|
||||
else if (bg >= 0 && fg < 0)
|
||||
{
|
||||
attrs &= ~A_COLOR;
|
||||
attrs |= g_attrs[ATTRIBUTE_IRC_0 + bg].attrs & A_COLOR;
|
||||
attrs ^= A_REVERSE;
|
||||
}
|
||||
else if (item->style & BUFFER_LINE_ITEM_INVERSE)
|
||||
attrs ^= A_REVERSE;
|
||||
return attrs;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
static struct widget *
|
||||
app_make_activity_indicator (void)
|
||||
{
|
||||
@@ -1795,8 +2010,19 @@ app_make_title (void)
|
||||
{
|
||||
struct layout row = {};
|
||||
app_push (&row, app_padding (ATTRIBUTE_CHROME, 0.25));
|
||||
app_push (&row, app_label (ATTRIBUTE_CHROME,
|
||||
g.buffer_current ? g.buffer_current->topic : NULL, true));
|
||||
if (g.buffer_current && g.buffer_current->topic)
|
||||
LIST_FOR_EACH (struct buffer_line_item, item, g.buffer_current->topic)
|
||||
{
|
||||
unsigned extended = item->style & BUFFER_LINE_ITEM_MONOSPACE
|
||||
? XUI_ATTR_MONOSPACE
|
||||
: 0;
|
||||
chtype attrs = app_line_item_attrs (ATTRIBUTE_CHROME, false, item);
|
||||
app_push (&row, app_label_attrs (attrs, extended, item->text,
|
||||
item == g.buffer_current->topic_tail));
|
||||
}
|
||||
else
|
||||
app_push (&row, app_label (ATTRIBUTE_CHROME, "", true));
|
||||
|
||||
app_push (&row, app_padding (ATTRIBUTE_CHROME, 0.5));
|
||||
app_push (&row, app_make_activity_indicator ());
|
||||
app_push (&row, app_padding (ATTRIBUTE_CHROME, 0.5));
|
||||
@@ -1847,29 +2073,11 @@ app_make_buffer_list (void)
|
||||
return list;
|
||||
}
|
||||
|
||||
static const char *
|
||||
app_line_mark (enum relay_rendition rendition)
|
||||
static enum app_attribute
|
||||
app_mark_attribute (enum relay_rendition rendition)
|
||||
{
|
||||
switch (rendition)
|
||||
{
|
||||
case RELAY_RENDITION_INDENT: return " ";
|
||||
case RELAY_RENDITION_STATUS: return "– ";
|
||||
case RELAY_RENDITION_ERROR: return "⚠ ";
|
||||
case RELAY_RENDITION_JOIN: return "→ ";
|
||||
case RELAY_RENDITION_PART: return "← ";
|
||||
case RELAY_RENDITION_ACTION: return "✶ ";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
static enum app_attribute
|
||||
app_line_attribute (const struct buffer_line *line)
|
||||
{
|
||||
if (line->leaked)
|
||||
return ATTRIBUTE_LEAKED;
|
||||
|
||||
switch (line->rendition)
|
||||
{
|
||||
case RELAY_RENDITION_ERROR: return ATTRIBUTE_ERROR;
|
||||
case RELAY_RENDITION_JOIN: return ATTRIBUTE_JOIN;
|
||||
case RELAY_RENDITION_PART: return ATTRIBUTE_PART;
|
||||
@@ -1878,6 +2086,31 @@ app_line_attribute (const struct buffer_line *line)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
app_line_mark (enum relay_rendition rendition)
|
||||
{
|
||||
switch (rendition)
|
||||
{
|
||||
case RELAY_RENDITION_INDENT: return " ";
|
||||
case RELAY_RENDITION_STATUS: return " – ";
|
||||
case RELAY_RENDITION_ERROR: return " ⚠ ";
|
||||
case RELAY_RENDITION_JOIN: return " → ";
|
||||
case RELAY_RENDITION_PART: return " ← ";
|
||||
case RELAY_RENDITION_ACTION: return " ✶ ";
|
||||
default: return " ";
|
||||
}
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
app_make_mark (const struct buffer_line *line)
|
||||
{
|
||||
chtype attrs = line->leaked
|
||||
? ATTRIBUTE_LEAKED
|
||||
: g_attrs[app_mark_attribute (line->rendition)].attrs;
|
||||
return app_label_attrs (attrs, XUI_ATTR_MONOSPACE,
|
||||
app_line_mark (line->rendition), false);
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
app_make_line (const struct buffer_line *line)
|
||||
{
|
||||
@@ -1887,15 +2120,27 @@ app_make_line (const struct buffer_line *line)
|
||||
if (localtime_r (&when, &tm))
|
||||
strftime (timestamp, sizeof timestamp, "%H:%M:%S", &tm);
|
||||
|
||||
enum app_attribute attribute = app_line_attribute (line);
|
||||
enum app_attribute attribute = ATTRIBUTE_NORMAL;
|
||||
if (line->leaked)
|
||||
attribute = ATTRIBUTE_LEAKED;
|
||||
|
||||
struct layout row = {};
|
||||
app_push (&row, app_padding (ATTRIBUTE_TIME, 0.25));
|
||||
app_push (&row, app_label (ATTRIBUTE_TIME, timestamp, false));
|
||||
app_push (&row, app_padding (ATTRIBUTE_TIME, 0.25));
|
||||
app_push (&row, app_padding (ATTRIBUTE_NORMAL, 0.5));
|
||||
app_push (&row, app_label (attribute,
|
||||
app_line_mark (line->rendition), false));
|
||||
app_push (&row, app_label (attribute, line->text, true));
|
||||
app_push (&row, app_make_mark (line->rendition));
|
||||
|
||||
LIST_FOR_EACH (struct buffer_line_item, item, line->items)
|
||||
{
|
||||
unsigned extended = item->style & BUFFER_LINE_ITEM_MONOSPACE
|
||||
? XUI_ATTR_MONOSPACE
|
||||
: 0;
|
||||
chtype attrs = app_line_item_attrs (attribute, line->leaked, item);
|
||||
app_push (&row, app_label_attrs (attrs, extended, item->text,
|
||||
item == line->items_tail));
|
||||
}
|
||||
if (!line->items)
|
||||
app_push (&row, app_label (attribute, "", true));
|
||||
app_push (&row, app_padding (ATTRIBUTE_NORMAL, 0.25));
|
||||
return xui_hbox (row.head);
|
||||
}
|
||||
@@ -2946,7 +3191,7 @@ app_free_context (void)
|
||||
{
|
||||
app_reset_relay_state ();
|
||||
LIST_FOR_EACH (struct buffer_line, iter, g.local_lines)
|
||||
free (iter);
|
||||
buffer_line_free (iter);
|
||||
g.local_lines = g.local_lines_tail = NULL;
|
||||
|
||||
log_view_free (&g.log);
|
||||
|
||||
Reference in New Issue
Block a user