degesch: preparations for mIRC formatting

We're gonna have to do it the hard way, it seems.

Prepared color mapping for mIRC colors which are totally different
from regular ANSI colors in your terminal.

What's missing now is a solid algorithm for outputting the 16-color
range using what we have (bold, blink, color 8--15, ...)

It also, naturally, has to respect any current attributes that are
in conflict with the colors.
This commit is contained in:
Přemysl Eric Janouch 2015-05-10 05:28:12 +02:00
parent 0a990ad6f7
commit dce14b9961
1 changed files with 151 additions and 13 deletions

164
degesch.c
View File

@ -1473,8 +1473,8 @@ static struct
bool stdout_is_tty; ///< `stdout' is a terminal
bool stderr_is_tty; ///< `stderr' is a terminal
char *color_set_fg[8]; ///< Codes to set the foreground colour
char *color_set_bg[8]; ///< Codes to set the background colour
char *color_set_fg[256]; ///< Codes to set the foreground colour
char *color_set_bg[256]; ///< Codes to set the background colour
int lines; ///< Number of lines
int columns; ///< Number of columns
@ -1527,7 +1527,8 @@ init_terminal (void)
g_terminal.columns = tigetnum ("cols");
update_screen_size ();
for (size_t i = 0; i < N_ELEMENTS (g_terminal.color_set_fg); i++)
int max = MIN (256, max_colors);
for (int i = 0; i < max; i++)
{
g_terminal.color_set_fg[i] = xstrdup (tparm (set_a_foreground,
i, 0, 0, 0, 0, 0, 0, 0, 0));
@ -1544,7 +1545,7 @@ free_terminal (void)
if (!g_terminal.initialized)
return;
for (size_t i = 0; i < N_ELEMENTS (g_terminal.color_set_fg); i++)
for (int i = 0; i < 256; i++)
{
free (g_terminal.color_set_fg[i]);
free (g_terminal.color_set_bg[i]);
@ -1552,6 +1553,36 @@ free_terminal (void)
del_curterm (cur_term);
}
static int
index_to_terminal_palette (int color)
{
hard_assert (color >= 0 && color < 256);
// No conversion
if (max_colors == 256)
return color;
// These colours are the same everywhere
if (color < 16 && max_colors > color)
return color;
// Outside the 16-color range, we only support the 88-color palette,
// which we can interpolate to from the 256-color one on input
if (max_colors != 88)
return -1;
// 24 -> 8 extra shades of gray
if (color >= 232)
return 80 + (color - 232) / 3;
// 6 * 6 * 6 cube -> 4 * 4 * 4 cube
int x[6] = { 0, 1, 1, 2, 2, 3 };
int index = color - 16;
return 16 +
( x[ index / 36 ] << 8
| x[(index / 6) % 6 ] << 4
| x[(index % 6) ] );
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
typedef int (*terminal_printer_fn) (int);
@ -1678,6 +1709,7 @@ init_colors (struct app_context *ctx)
// Format strings use a #-quoted notation, to differentiate from printf:
// #s inserts a string
// #d inserts a signed integer; also supports the #<N> and #0<N> notation
// #m inserts a mIRC-formatted string (auto-resets at boundaries)
//
// #a inserts named attributes (auto-resets)
// #r resets terminal attributes
@ -1801,6 +1833,105 @@ formatter_add_bg_color (struct formatter *self, int color)
item->color = color;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
enum
{
MIRC_WHITE, MIRC_BLACK, MIRC_BLUE, MIRC_GREEN,
MIRC_L_RED, MIRC_RED, MIRC_PURPLE, MIRC_ORANGE,
MIRC_YELLOW, MIRC_L_GREEN, MIRC_CYAN, MIRC_L_CYAN,
MIRC_L_BLUE, MIRC_L_PURPLE, MIRC_GRAY, MIRC_L_GRAY,
};
// We use estimates from the 16 color terminal palette, or the 256 color cube,
// which is not always available. The mIRC orange colour is only in the cube.
// NOTE: commonly terminals have 8 colors (worst), 16 colors (allows brightness
// without the bold/blink attributes), 88 colors (the same plus a 4^3 RGB cube
// and a few shades of gray), or 256 colors (best)
#define FORMATTER_NORMAL 0
#define FORMATTER_BRIGHT 1 << 8
#define FORMATTER_MAP(name, flags, c256) \
COLOR_ ## name | FORMATTER_ ## flags | c256 << 16
static const uint32_t g_mirc_to_term[] =
{
[MIRC_WHITE] = FORMATTER_MAP (WHITE, BRIGHT, 231),
[MIRC_BLACK] = FORMATTER_MAP (BLACK, NORMAL, 16),
[MIRC_BLUE] = FORMATTER_MAP (BLUE, NORMAL, 19),
[MIRC_GREEN] = FORMATTER_MAP (GREEN, NORMAL, 34),
[MIRC_L_RED] = FORMATTER_MAP (RED, BRIGHT, 196),
[MIRC_RED] = FORMATTER_MAP (RED, NORMAL, 124),
[MIRC_PURPLE] = FORMATTER_MAP (MAGENTA, NORMAL, 127),
[MIRC_ORANGE] = FORMATTER_MAP (YELLOW, BRIGHT, 214),
[MIRC_YELLOW] = FORMATTER_MAP (YELLOW, BRIGHT, 226),
[MIRC_L_GREEN] = FORMATTER_MAP (GREEN, BRIGHT, 46),
[MIRC_CYAN] = FORMATTER_MAP (CYAN, NORMAL, 37),
[MIRC_L_CYAN] = FORMATTER_MAP (CYAN, BRIGHT, 51),
[MIRC_L_BLUE] = FORMATTER_MAP (BLUE, BRIGHT, 21),
[MIRC_L_PURPLE] = FORMATTER_MAP (MAGENTA, BRIGHT, 201),
[MIRC_GRAY] = FORMATTER_MAP (BLACK, BRIGHT, 244),
[MIRC_L_GRAY] = FORMATTER_MAP (WHITE, NORMAL, 252),
};
static void
formatter_parse_mirc (struct formatter *self, const char *s)
{
struct str buffer;
str_init (&buffer);
#define FLUSH if (buffer.len) \
{ formatter_add_text (self, buffer.str); str_reset (&buffer); }
formatter_add_reset (self);
char c;
while ((c = *s++))
{
switch (c)
{
case '\x02':
FLUSH
// TODO: bold
break;
case '\x03':
FLUSH
// TODO: color
// TODO: parse \d(\d(,\d(\d)?)?)?
break;
case '\x1d':
FLUSH
// TODO: italic
break;
case '\x1f':
FLUSH
// TODO: underline
break;
case '\x16':
FLUSH
// TODO: swap background/foreground
break;
case '\x0f':
FLUSH
formatter_add_reset (self);
break;
default:
str_append_c (&buffer, c);
}
}
FLUSH
str_free (&buffer);
#undef FLUSH
formatter_add_reset (self);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const char *
formatter_parse_field (struct formatter *self,
const char *field, struct str *buf, va_list *ap)
@ -1822,6 +1953,10 @@ restart:
str_append_c (buf, ' ');
str_append (buf, s);
break;
case 'm':
s = va_arg (*ap, char *);
formatter_parse_mirc (self, s);
break;
case 'd':
s = xstrdup_printf ("%d", va_arg (*ap, int));
for (size_t len = strlen (s); len < width; len++)
@ -1981,6 +2116,9 @@ buffer_line_display (struct app_context *ctx,
char *nick = NULL;
const char *userhost = NULL;
// TODO: always assign the default colour to ourselves
// FIXME: never use the black colour, use the default instead
int nick_color = -1;
int object_color = -1;
@ -2019,24 +2157,24 @@ buffer_line_display (struct app_context *ctx,
{
case BUFFER_LINE_PRIVMSG:
if (line->flags & BUFFER_LINE_HIGHLIGHT)
formatter_add (&f, "#a<#s>#r #s", ATTR_HIGHLIGHT, nick, a->text);
formatter_add (&f, "#a<#s>#r #m", ATTR_HIGHLIGHT, nick, a->text);
else
formatter_add (&f, "<#c#s#r> #s", nick_color, nick, a->text);
formatter_add (&f, "<#c#s#r> #m", nick_color, nick, a->text);
break;
case BUFFER_LINE_ACTION:
if (line->flags & BUFFER_LINE_HIGHLIGHT)
formatter_add (&f, " #a*#r ", ATTR_HIGHLIGHT);
else
formatter_add (&f, " #a*#r ", ATTR_ACTION);
formatter_add (&f, "#c#s#r #s", nick_color, nick, a->text);
formatter_add (&f, "#c#s#r #m", nick_color, nick, a->text);
break;
case BUFFER_LINE_NOTICE:
formatter_add (&f, " - ");
if (line->flags & BUFFER_LINE_HIGHLIGHT)
formatter_add (&f, "#a#s(#s)#r: #s",
formatter_add (&f, "#a#s(#s)#r: #m",
ATTR_HIGHLIGHT, "Notice", nick, a->text);
else
formatter_add (&f, "#s(#c#s#r): #s",
formatter_add (&f, "#s(#c#s#r): #m",
"Notice", nick_color, nick, a->text);
break;
case BUFFER_LINE_JOIN:
@ -2051,7 +2189,7 @@ buffer_line_display (struct app_context *ctx,
nick_color, nick, ATTR_USERHOST, userhost,
ATTR_PART, "has left", a->object);
if (a->reason)
formatter_add (&f, " (#s)", a->reason);
formatter_add (&f, " (#m)", a->reason);
break;
case BUFFER_LINE_KICK:
formatter_add (&f, "#a<--#r ", ATTR_PART);
@ -2059,7 +2197,7 @@ buffer_line_display (struct app_context *ctx,
nick_color, nick, ATTR_USERHOST, userhost,
ATTR_PART, "has kicked", object_color, a->object);
if (a->reason)
formatter_add (&f, " (#s)", a->reason);
formatter_add (&f, " (#m)", a->reason);
break;
case BUFFER_LINE_NICK:
formatter_add (&f, " - ");
@ -2073,7 +2211,7 @@ buffer_line_display (struct app_context *ctx,
break;
case BUFFER_LINE_TOPIC:
formatter_add (&f, " - ");
formatter_add (&f, "#c#s#r #s \"#s\"",
formatter_add (&f, "#c#s#r #s \"#m\"",
nick_color, nick,
"has changed the topic to", a->text);
break;
@ -2083,7 +2221,7 @@ buffer_line_display (struct app_context *ctx,
nick_color, nick, ATTR_USERHOST, userhost,
ATTR_PART, "has quit");
if (a->reason)
formatter_add (&f, " (#s)", a->reason);
formatter_add (&f, " (#m)", a->reason);
break;
case BUFFER_LINE_STATUS:
formatter_add (&f, " - ");