From dce14b996185c2899c9c0d5f24748643d5fd2809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Sun, 10 May 2015 05:28:12 +0200 Subject: [PATCH] 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. --- degesch.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 151 insertions(+), 13 deletions(-) diff --git a/degesch.c b/degesch.c index 275f744..2c300b2 100644 --- a/degesch.c +++ b/degesch.c @@ -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 # and #0 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, " - ");