[vte] widget,emulation: Add support for colored underlines



commit a8af47bcabc66417f3b0930c7f3e2a903f1b9710
Author: Egmont Koblinger <egmont gmail com>
Date:   Mon Dec 11 21:50:12 2017 +0100

    widget,emulation: Add support for colored underlines
    
    The escape sequences are, similarly to CSI 38...m and CSI 39m:
      CSI 58:5:...m or CSI 58;5;...m for an item of the 256-color palette
      CSI 58:2:...m or CSI 58;2;...m for a direct RGB color
      CSI 59m to turn this off (underline's color is the same as the text's)
    
    https://bugzilla.gnome.org/show_bug.cgi?id=721761

 src/vte.cc         |  110 +++++++++++++++++++++++++++++++++-------------------
 src/vteinternal.hh |   10 +++-
 src/vterowdata.h   |   19 ++++++---
 src/vteseq.cc      |   17 +++++++-
 4 files changed, 103 insertions(+), 53 deletions(-)
---
diff --git a/src/vte.cc b/src/vte.cc
index c12bc75..d7b7075 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -6165,6 +6165,7 @@ vte_terminal_cellattr_equal(VteCellAttr const *attr1,
                attr1->italic        == attr2->italic    &&
                attr1->fore          == attr2->fore      &&
                attr1->back          == attr2->back      &&
+                attr1->deco          == attr2->deco      &&
                attr1->underline     == attr2->underline &&
                attr1->strikethrough == attr2->strikethrough &&
                attr1->reverse       == attr2->reverse   &&
@@ -6183,11 +6184,11 @@ VteTerminalPrivate::cellattr_to_html(VteCellAttr const* attr,
                                      char const* text) const
 {
        GString *string;
-       guint fore, back;
+        guint fore, back, deco;
 
        string = g_string_new(text);
 
-       determine_colors(attr, false, false, &fore, &back);
+        determine_colors(attr, false, false, &fore, &back, &deco);
 
        if (attr->bold) {
                g_string_prepend(string, "<b>");
@@ -6200,12 +6201,26 @@ VteTerminalPrivate::cellattr_to_html(VteCellAttr const* attr,
         /* <u> should be inside <font> so that it inherits its color by default */
         if (attr->underline) {
                 static const char styles[][7] = {"", "single", "double", "wavy"};
-                char *tag;
+                char *tag, *colorattr;
+
+                if (attr->deco != VTE_DEFAULT_FG) {
+                        vte::color::rgb color;
 
-                tag = g_strdup_printf("<u style=\"text-decoration-style:%s\">",
-                                      styles[attr->underline]);
+                        rgb_from_index(attr->deco, color);
+                        colorattr = g_strdup_printf(";text-decoration-color:#%02X%02X%02X",
+                                                    color.red >> 8,
+                                                    color.green >> 8,
+                                                    color.blue >> 8);
+                } else {
+                        colorattr = g_strdup("");
+                }
+
+                tag = g_strdup_printf("<u style=\"text-decoration-style:%s%s\">",
+                                      styles[attr->underline],
+                                      colorattr);
                 g_string_prepend(string, tag);
                 g_free(tag);
+                g_free(colorattr);
                 g_string_append(string, "</u>");
         }
        if (attr->fore != VTE_DEFAULT_FG || attr->reverse) {
@@ -8788,15 +8803,17 @@ VteTerminalPrivate::determine_colors(VteCellAttr const* attr,
                                      bool is_selected,
                                      bool is_cursor,
                                      guint *pfore,
-                                     guint *pback) const
+                                     guint *pback,
+                                     guint *pdeco) const
 {
-       guint fore, back;
+        guint fore, back, deco;
 
         g_assert(attr);
 
        /* Start with cell colors */
        fore = attr->fore;
        back = attr->back;
+        deco = attr->deco;
 
        /* Reverse-mode switches default fore and back colors */
         if (G_UNLIKELY (m_reverse_mode)) {
@@ -8861,33 +8878,36 @@ VteTerminalPrivate::determine_colors(VteCellAttr const* attr,
 
        /* Invisible? */
        if (attr->invisible) {
-               fore = back;
+                fore = deco = back;
        }
 
        *pfore = fore;
        *pback = back;
+        *pdeco = deco;
 }
 
 void
 VteTerminalPrivate::determine_colors(VteCell const* cell,
                                      bool highlight,
                                      guint *fore,
-                                     guint *back) const
+                                     guint *back,
+                                     guint *deco) const
 {
        determine_colors(cell ? &cell->attr : &basic_cell.attr,
                          highlight, false /* not cursor */,
-                         fore, back);
+                         fore, back, deco);
 }
 
 void
 VteTerminalPrivate::determine_cursor_colors(VteCell const* cell,
                                             bool highlight,
                                             guint *fore,
-                                            guint *back) const
+                                            guint *back,
+                                            guint *deco) const
 {
        determine_colors(cell ? &cell->attr : &basic_cell.attr,
                          highlight, true /* cursor */,
-                         fore, back);
+                         fore, back, deco);
 }
 
 /* Draw a string of characters with similar attributes. */
@@ -8896,6 +8916,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                gssize n,
                                guint fore,
                                guint back,
+                               guint deco,
                                bool clear,
                                bool draw_default_bg,
                                bool bold,
@@ -8910,7 +8931,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
 {
        int i, x, y;
        gint columns = 0;
-       vte::color::rgb fg, bg;
+        vte::color::rgb fg, bg, dc;
 
        g_assert(n > 0);
        _VTE_DEBUG_IF(VTE_DEBUG_CELLS) {
@@ -8920,10 +8941,10 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                        g_string_append_unichar (str, items[i].c);
                }
                tmp = g_string_free (str, FALSE);
-               g_printerr ("draw_cells('%s', fore=%d, back=%d, bold=%d,"
+                g_printerr ("draw_cells('%s', fore=%d, back=%d, deco=%d, bold=%d,"
                                 " ul=%d, strike=%d,"
                                 " hyperlink=%d, hilite=%d, boxed=%d)\n",
-                               tmp, fore, back, bold,
+                                tmp, fore, back, deco, bold,
                                 underline, strikethrough,
                                 hyperlink, hilite, boxed);
                g_free (tmp);
@@ -8932,6 +8953,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
        bold = bold && m_allow_bold;
        rgb_from_index(fore, fg);
        rgb_from_index(back, bg);
+        rgb_from_index(deco == VTE_DEFAULT_FG ? fore : deco, dc);
 
        i = 0;
        do {
@@ -8952,12 +8974,10 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                }
        } while (i < n);
 
-       _vte_draw_text(m_draw,
-                       items, n,
-                       &fg, VTE_DRAW_OPAQUE,
-                       _vte_draw_get_style(bold, italic));
-
-       /* Draw whatever SFX are required. */
+        /* Draw whatever SFX are required. Do this before drawing the letters,
+         * so that if the descent of a letter crosses an underline of a different color,
+         * it's the letter's color that wins. Other kinds of decorations always have the
+         * same color as the text, so the order is irrelevant there. */
         if (underline | strikethrough | hyperlink | hilite | boxed) {
                i = 0;
                do {
@@ -8974,7 +8994,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                                     x + (columns * column_width) - 1,
                                                     y + m_underline_position + m_underline_thickness - 1,
                                                     VTE_LINE_WIDTH,
-                                                    &fg, VTE_DRAW_OPAQUE);
+                                                    &dc, VTE_DRAW_OPAQUE);
                                 break;
                         case 2:
                                 _vte_draw_draw_line(m_draw,
@@ -8983,14 +9003,14 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                                     x + (columns * column_width) - 1,
                                                     y + m_double_underline_position + 
m_double_underline_thickness - 1,
                                                     VTE_LINE_WIDTH,
-                                                    &fg, VTE_DRAW_OPAQUE);
+                                                    &dc, VTE_DRAW_OPAQUE);
                                 _vte_draw_draw_line(m_draw,
                                                     x,
                                                     y + m_double_underline_position + 2 * 
m_double_underline_thickness,
                                                     x + (columns * column_width) - 1,
                                                     y + m_double_underline_position + 3 * 
m_double_underline_thickness - 1,
                                                     VTE_LINE_WIDTH,
-                                                    &fg, VTE_DRAW_OPAQUE);
+                                                    &dc, VTE_DRAW_OPAQUE);
                                 break;
                         case 3:
                                 for (int j = 0; j < columns; j++) {
@@ -8999,7 +9019,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                                                  y + m_undercurl_position,
                                                                  column_width,
                                                                  m_undercurl_thickness,
-                                                                 &fg, VTE_DRAW_OPAQUE);
+                                                                 &dc, VTE_DRAW_OPAQUE);
                                 }
                                 break;
                        }
@@ -9037,8 +9057,13 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                                          MAX(0, row_height),
                                                          &fg, VTE_DRAW_OPAQUE);
                        }
-               }while (i < n);
+                } while (i < n);
        }
+
+        _vte_draw_text(m_draw,
+                       items, n,
+                       &fg, VTE_DRAW_OPAQUE,
+                       _vte_draw_get_style(bold, italic));
 }
 
 /* FIXME: we don't have a way to tell GTK+ what the default text attributes
@@ -9239,7 +9264,7 @@ VteTerminalPrivate::draw_cells_with_attributes(struct _vte_draw_text_request *it
         int i, j, cell_count;
        VteCell *cells;
        char scratch_buf[VTE_UTF8_BPC];
-       guint fore, back;
+        guint fore, back, deco;
 
        /* Note: since this function is only called with the pre-edit text,
         * all the items contain gunichar only, not vteunistr. */
@@ -9251,10 +9276,11 @@ VteTerminalPrivate::draw_cells_with_attributes(struct _vte_draw_text_request *it
        cells = g_new(VteCell, cell_count);
        translate_pango_cells(attrs, cells, cell_count);
        for (i = 0, j = 0; i < n; i++) {
-               determine_colors(&cells[j], false, &fore, &back);
+                determine_colors(&cells[j], false, &fore, &back, &deco);
                draw_cells(items + i, 1,
                                        fore,
                                        back,
+                                        deco,
                                        TRUE, draw_default_bg,
                                        cells[j].attr.bold,
                                        cells[j].attr.italic,
@@ -9286,7 +9312,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
         vte::grid::row_t row, rows;
         vte::grid::column_t i, j;
         long x, y;
-        guint fore, nfore, back, nback, underline, nunderline;
+        guint fore, nfore, back, nback, deco, ndeco, underline, nunderline;
         gboolean bold, nbold, italic, nitalic,
                  hyperlink, nhyperlink, hilite, nhilite,
                 selected, nselected, strikethrough, nstrikethrough;
@@ -9320,7 +9346,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                cell = _vte_row_data_get (row_data, i);
                                /* Find the colors for this cell. */
                                selected = cell_is_selected(i, row);
-                               determine_colors(cell, selected, &fore, &back);
+                                determine_colors(cell, selected, &fore, &back, &deco);
 
                                bold = cell && cell->attr.bold;
                                j = i + (cell ? cell->attr.columns : 1);
@@ -9339,7 +9365,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                         * compare visual attributes to the first character
                                         * in this chunk. */
                                        selected = cell_is_selected(j, row);
-                                       determine_colors(cell, selected, &nfore, &nback);
+                                        determine_colors(cell, selected, &nfore, &nback, &ndeco);
                                        if (nback != back) {
                                                break;
                                        }
@@ -9374,7 +9400,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                        }
                                        j++;
                                }
-                               determine_colors(nullptr, selected, &fore, &back);
+                                determine_colors(nullptr, selected, &fore, &back, &deco);
                                if (back != VTE_DEFAULT_BG) {
                                        vte::color::rgb bg;
                                        rgb_from_index(back, bg);
@@ -9436,7 +9462,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                        }
                        /* Find the colors for this cell. */
                        selected = cell_is_selected(i, row);
-                       determine_colors(cell, selected, &fore, &back);
+                        determine_colors(cell, selected, &fore, &back, &deco);
                        underline = cell->attr.underline;
                        strikethrough = cell->attr.strikethrough;
                         hyperlink = (m_allow_hyperlink && cell->attr.hyperlink_idx != 0);
@@ -9487,10 +9513,13 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                         * compare visual attributes to the first character
                                         * in this chunk. */
                                        selected = cell_is_selected(j, row);
-                                       determine_colors(cell, selected, &nfore, &nback);
+                                        determine_colors(cell, selected, &nfore, &nback, &ndeco);
                                        if (nfore != fore) {
                                                break;
                                        }
+                                        if (ndeco != deco) {
+                                                break;
+                                        }
                                        nbold = cell->attr.bold;
                                        if (nbold != bold) {
                                                break;
@@ -9563,7 +9592,7 @@ fg_draw:
                        draw_cells(
                                        items,
                                        item_count,
-                                       fore, back, FALSE, FALSE,
+                                        fore, back, deco, FALSE, FALSE,
                                        bold, italic, underline,
                                         strikethrough, hyperlink, hilite, FALSE,
                                        column_width, row_height);
@@ -9669,7 +9698,7 @@ VteTerminalPrivate::paint_cursor()
         vte::grid::column_t col;
         int width, height, cursor_width;
         guint style = 0;
-       guint fore, back;
+        guint fore, back, deco;
        vte::color::rgb bg;
        int x, y;
        gboolean blink, selected, focus;
@@ -9713,7 +9742,7 @@ VteTerminalPrivate::paint_cursor()
        }
 
        selected = cell_is_selected(col, drow);
-       determine_cursor_colors(cell, selected, &fore, &back);
+        determine_cursor_colors(cell, selected, &fore, &back, &deco);
        rgb_from_index(back, bg);
 
        x = item.x;
@@ -9792,7 +9821,7 @@ VteTerminalPrivate::paint_cursor()
                                 if (cell && cell->c != 0 && cell->c != ' ' && cell->c != '\t') {
                                         draw_cells(
                                                         &item, 1,
-                                                        fore, back, TRUE, FALSE,
+                                                        fore, back, deco, TRUE, FALSE,
                                                         cell->attr.bold,
                                                         cell->attr.italic,
                                                         cell->attr.underline,
@@ -9824,7 +9853,7 @@ VteTerminalPrivate::paint_im_preedit_string()
        int col, columns;
        long width, height;
        int i, len;
-       guint fore, back;
+        guint fore, back, deco;
 
        if (!m_im_preedit)
                return;
@@ -9869,6 +9898,7 @@ VteTerminalPrivate::paint_im_preedit_string()
                                 get_color(VTE_DEFAULT_BG), m_background_alpha);
                 fore = m_color_defaults.attr.fore;
                 back = m_color_defaults.attr.back;
+                deco = m_color_defaults.attr.deco;
                draw_cells_with_attributes(
                                                        items, len,
                                                        m_im_preedit_attrs,
@@ -9879,7 +9909,7 @@ VteTerminalPrivate::paint_im_preedit_string()
                        /* Cursored letter in reverse. */
                        draw_cells(
                                                &items[preedit_cursor], 1,
-                                                back, fore,
+                                                back, fore, deco,
                                                 TRUE,  /* clear */
                                                 TRUE,  /* draw_default_bg */
                                                 FALSE, /* bold */
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index bbfa437..62e71b5 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -834,6 +834,7 @@ public:
                         gssize n,
                         guint fore,
                         guint back,
+                        guint deco,
                         bool clear,
                         bool draw_default_bg,
                         bool bold,
@@ -985,15 +986,18 @@ public:
                                      bool selected,
                                      bool cursor,
                                      guint *pfore,
-                                     guint *pback) const;
+                                     guint *pback,
+                                     guint *pdeco) const;
         inline void determine_colors(VteCell const* cell,
                                      bool selected,
                                      guint *pfore,
-                                     guint *pback) const;
+                                     guint *pback,
+                                     guint *pdeco) const;
         inline void determine_cursor_colors(VteCell const* cell,
                                             bool selected,
                                             guint *pfore,
-                                            guint *pback) const;
+                                            guint *pback,
+                                            guint *pdeco) const;
 
         char *cellattr_to_html(VteCellAttr const* attr,
                                char const* text) const;
diff --git a/src/vterowdata.h b/src/vterowdata.h
index 630cd1a..f441952 100644
--- a/src/vterowdata.h
+++ b/src/vterowdata.h
@@ -32,7 +32,7 @@ G_BEGIN_DECLS
 #define VTE_TAB_WIDTH_BITS             4  /* Has to be able to store the value of 8. */
 #define VTE_TAB_WIDTH_MAX              ((1 << VTE_TAB_WIDTH_BITS) - 1)
 
-#define VTE_CELL_ATTR_COMMON_BYTES      8  /* The number of common bytes in VteCellAttr and 
VteStreamCellAttr */
+#define VTE_CELL_ATTR_COMMON_BYTES      12  /* The number of common bytes in VteCellAttr and 
VteStreamCellAttr */
 
 /*
  * VteCellAttr: A single cell style attributes
@@ -63,6 +63,9 @@ typedef struct _VteCellAttr {
 
        guint64 invisible: 1;
         /* 8-byte boundary */
+        guint32 deco: 25;       /* decoration color (currently for underline) */
+        guint32 padding_unused: 7;
+        /* 12-byte boundary */
         guint32 hyperlink_idx;  /* a unique hyperlink index at a time for the ring's cells,
                                    0 means not a hyperlink, VTE_HYPERLINK_IDX_TARGET_IN_STREAM
                                    means the target is irrelevant/unknown at the moment.
@@ -70,7 +73,6 @@ typedef struct _VteCellAttr {
                                    for every cell in the ring but not yet in the stream
                                    (currently the height rounded up to the next power of two, times width)
                                    for supported VTE sizes, and update VTE_HYPERLINK_IDX_TARGET_IN_STREAM. */
-        guint32 padding_unused;
 } VteCellAttr;
 G_STATIC_ASSERT (sizeof (VteCellAttr) == 16);
 G_STATIC_ASSERT (offsetof (VteCellAttr, hyperlink_idx) == VTE_CELL_ATTR_COMMON_BYTES);
@@ -85,13 +87,15 @@ G_STATIC_ASSERT (offsetof (VteCellAttr, hyperlink_idx) == VTE_CELL_ATTR_COMMON_B
 typedef struct _VTE_GNUC_PACKED _VteStreamCellAttr {
         guint64 fragment: 1;
         guint64 columns: VTE_TAB_WIDTH_BITS;
-        guint64 remaining_main_attributes: 59;  /* All the non-hyperlink related attributes from VteCellAttr.
-                                                   We don't individually access them in the stream, so 
there's
-                                                   no point in repeating each field separately. */
+        guint64 remaining_main_attributes_1: 59;  /* All the non-hyperlink related attributes from 
VteCellAttr, part 1.
+                                                     We don't individually access them in the stream, so 
there's
+                                                     no point in repeating each field separately. */
         /* 8-byte boundary */
+        guint32 remaining_main_attributes_2;      /* Non-hyperlink related attributes, part 2. */
+        /* 12-byte boundary */
         guint16 hyperlink_length;       /* make sure it fits VTE_HYPERLINK_TOTAL_LENGTH_MAX */
 } VteStreamCellAttr;
-G_STATIC_ASSERT (sizeof (VteStreamCellAttr) == 10);
+G_STATIC_ASSERT (sizeof (VteStreamCellAttr) == 14);
 G_STATIC_ASSERT (offsetof (VteStreamCellAttr, hyperlink_length) == VTE_CELL_ATTR_COMMON_BYTES);
 
 /*
@@ -122,8 +126,9 @@ static const VteCell basic_cell = {
                0, /* half */
 
                 0, /* invisible */
-                0, /* hyperlink_idx */
+                VTE_DEFAULT_FG, /* deco */
                 0, /* padding_unused */
+                0, /* hyperlink_idx */
        }
 };
 
diff --git a/src/vteseq.cc b/src/vteseq.cc
index ae68f0a..dea9871 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -1797,7 +1797,7 @@ VteTerminalPrivate::seq_vertical_tab(vte::parser::Params const& params)
         line_feed();
 }
 
-/* Parse parameters of SGR 38 or 48, starting at @index within @params.
+/* Parse parameters of SGR 38, 48 or 58, starting at @index within @params.
  * Returns the color index, or -1 on error.
  * Increments @index to point to the last consumed parameter (not beyond). */
 int32_t
@@ -1867,6 +1867,7 @@ VteTerminalPrivate::seq_character_attributes(vte::parser::Params const& params)
                                 break;
                         case 38:
                         case 48:
+                        case 58:
                         {
                                 unsigned int index = 1;
                                 auto color = parse_sgr_38_48_parameters(subparams, &index);
@@ -1876,8 +1877,10 @@ VteTerminalPrivate::seq_character_attributes(vte::parser::Params const& params)
                                 if (G_LIKELY (color != -1)) {
                                         if (param0 == 38) {
                                                 m_defaults.attr.fore = color;
-                                        } else {
+                                        } else if (param0 == 48) {
                                                 m_defaults.attr.back = color;
+                                        } else {
+                                                m_defaults.attr.deco = color;
                                         }
                                 }
                                 break;
@@ -1956,6 +1959,7 @@ VteTerminalPrivate::seq_character_attributes(vte::parser::Params const& params)
                        break;
                case 38:
                case 48:
+                case 58:
                {
                        /* The format looks like:
                         * - 256 color indexed palette:
@@ -1991,8 +1995,10 @@ VteTerminalPrivate::seq_character_attributes(vte::parser::Params const& params)
                                if (G_LIKELY (color != -1)) {
                                        if (param == 38) {
                                                 m_defaults.attr.fore = color;
-                                       } else {
+                                        } else if (param == 48) {
                                                 m_defaults.attr.back = color;
+                                        } else {
+                                                m_defaults.attr.deco = color;
                                        }
                                }
                        }
@@ -2017,6 +2023,11 @@ VteTerminalPrivate::seq_character_attributes(vte::parser::Params const& params)
                        /* default background */
                         m_defaults.attr.back = VTE_DEFAULT_BG;
                        break;
+             /* case 58: was handled above at 38 to avoid code duplication */
+                case 59:
+                        /* default decoration color, that is, same as the cell's foreground */
+                        m_defaults.attr.deco = VTE_DEFAULT_FG;
+                        break;
                case 90:
                case 91:
                case 92:


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]