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, " - ");