[vte] widget,emulation: Add support for double and curly underlines



commit efaf8f3c16809b8b2e43c39b13bf89464f51ba1a
Author: Egmont Koblinger <egmont gmail com>
Date:   Mon Dec 11 21:44:49 2017 +0100

    widget,emulation: Add support for double and curly underlines
    
    The underline escape sequences are:
      CSI 4m or CSI 4:1m for single underline
      CSI 21m or CSI 4:2m for double underline
      CSI 4:3m for curly underline
      CSI 24m or CSI 4:0m for turning them off
    
    https://bugzilla.gnome.org/show_bug.cgi?id=721761

 src/vte.cc         |   84 ++++++++++++++++++++++++++++++++++++++++------------
 src/vtedraw.cc     |   47 +++++++++++++++++++++++++++++
 src/vtedraw.hh     |    9 +++++
 src/vteinternal.hh |    6 +++-
 src/vterowdata.h   |    8 ++---
 src/vteseq.cc      |   44 +++++++++++++++++----------
 6 files changed, 157 insertions(+), 41 deletions(-)
---
diff --git a/src/vte.cc b/src/vte.cc
index f62548f..c12bc75 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -6197,6 +6197,17 @@ VteTerminalPrivate::cellattr_to_html(VteCellAttr const* attr,
                g_string_prepend(string, "<i>");
                g_string_append(string, "</i>");
        }
+        /* <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;
+
+                tag = g_strdup_printf("<u style=\"text-decoration-style:%s\">",
+                                      styles[attr->underline]);
+                g_string_prepend(string, tag);
+                g_free(tag);
+                g_string_append(string, "</u>");
+        }
        if (attr->fore != VTE_DEFAULT_FG || attr->reverse) {
                vte::color::rgb color;
                 char *tag;
@@ -6223,10 +6234,6 @@ VteTerminalPrivate::cellattr_to_html(VteCellAttr const* attr,
                g_free(tag);
                g_string_append(string, "</span>");
        }
-       if (attr->underline) {
-               g_string_prepend(string, "<u>");
-               g_string_append(string, "</u>");
-       }
        if (attr->strikethrough) {
                g_string_prepend(string, "<strike>");
                g_string_append(string, "</strike>");
@@ -7564,12 +7571,17 @@ VteTerminalPrivate::apply_font_metrics(int cell_width,
         }
         m_line_thickness = MAX (MIN (char_descent / 2, char_height / 14), 1);
         /* FIXME take these from pango_font_metrics_get_{underline,strikethrough}_{position,thickness} */
-        m_underline_position = MIN (char_spacing.top + char_ascent + m_line_thickness, cell_height - 
m_line_thickness);
         m_underline_thickness = m_line_thickness;
-        m_strikethrough_position = char_spacing.top + char_ascent - char_height / 4;
+        m_underline_position = MIN (char_spacing.top + char_ascent + m_line_thickness, cell_height - 
m_underline_thickness);
+        m_double_underline_thickness = m_line_thickness;
+        /* FIXME make sure this doesn't reach the baseline (switch to thinner lines, or one thicker line in 
that case) */
+        m_double_underline_position = MIN (char_spacing.top + char_ascent + m_line_thickness, cell_height - 
3 * m_double_underline_thickness);
+        m_undercurl_thickness = m_line_thickness;
+        m_undercurl_position = MIN (char_spacing.top + char_ascent + m_line_thickness, cell_height - 
_vte_draw_get_undercurl_height(cell_width, m_undercurl_thickness));
         m_strikethrough_thickness = m_line_thickness;
-        m_regex_underline_position = char_spacing.top + char_height - 1;  /* FIXME */
+        m_strikethrough_position = char_spacing.top + char_ascent - char_height / 4;
         m_regex_underline_thickness = 1;  /* FIXME */
+        m_regex_underline_position = char_spacing.top + char_height - m_regex_underline_thickness;  /* FIXME 
*/
 
        /* Queue a resize if anything's changed. */
        if (resize) {
@@ -8084,6 +8096,10 @@ VteTerminalPrivate::VteTerminalPrivate(VteTerminal *t) :
        m_line_thickness = 1;
        m_underline_position = 1;
         m_underline_thickness = 1;
+        m_double_underline_position = 1;
+        m_double_underline_thickness = 1;
+        m_undercurl_position = 1.;
+        m_undercurl_thickness = 1;
        m_strikethrough_position = 1;
         m_strikethrough_thickness = 1;
         m_regex_underline_position = 1;
@@ -8884,7 +8900,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                bool draw_default_bg,
                                bool bold,
                                bool italic,
-                               bool underline,
+                               guint underline,
                                bool strikethrough,
                                bool hyperlink,
                                bool hilite,
@@ -8950,7 +8966,8 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                        for (columns = 0; i < n && items[i].y == y; i++) {
                                columns += items[i].columns;
                        }
-                       if (underline) {
+                        switch (underline) {
+                        case 1:
                                 _vte_draw_draw_line(m_draw,
                                                     x,
                                                     y + m_underline_position,
@@ -8958,6 +8975,33 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                                     y + m_underline_position + m_underline_thickness - 1,
                                                     VTE_LINE_WIDTH,
                                                     &fg, VTE_DRAW_OPAQUE);
+                                break;
+                        case 2:
+                                _vte_draw_draw_line(m_draw,
+                                                    x,
+                                                    y + m_double_underline_position,
+                                                    x + (columns * column_width) - 1,
+                                                    y + m_double_underline_position + 
m_double_underline_thickness - 1,
+                                                    VTE_LINE_WIDTH,
+                                                    &fg, 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);
+                                break;
+                        case 3:
+                                for (int j = 0; j < columns; j++) {
+                                        _vte_draw_draw_undercurl(m_draw,
+                                                                 x + j * column_width,
+                                                                 y + m_undercurl_position,
+                                                                 column_width,
+                                                                 m_undercurl_thickness,
+                                                                 &fg, VTE_DRAW_OPAQUE);
+                                }
+                                break;
                        }
                        if (strikethrough) {
                                 _vte_draw_draw_line(m_draw,
@@ -9242,8 +9286,8 @@ 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;
-        gboolean underline, nunderline, bold, nbold, italic, nitalic,
+        guint fore, nfore, back, nback, underline, nunderline;
+        gboolean bold, nbold, italic, nitalic,
                  hyperlink, nhyperlink, hilite, nhilite,
                 selected, nselected, strikethrough, nstrikethrough;
        guint item_count;
@@ -9835,14 +9879,16 @@ VteTerminalPrivate::paint_im_preedit_string()
                        /* Cursored letter in reverse. */
                        draw_cells(
                                                &items[preedit_cursor], 1,
-                                               back, fore, TRUE, TRUE,
-                                               FALSE,
-                                               FALSE,
-                                               FALSE,
-                                               FALSE,
-                                                FALSE,
-                                               FALSE,
-                                               TRUE,
+                                                back, fore,
+                                                TRUE,  /* clear */
+                                                TRUE,  /* draw_default_bg */
+                                                FALSE, /* bold */
+                                                FALSE, /* italic */
+                                                0,     /* underline */
+                                                FALSE, /* strikethrough */
+                                                FALSE, /* hyperlink */
+                                                FALSE, /* hilite */
+                                                TRUE,  /* boxed */
                                                width, height);
                }
                g_free(items);
diff --git a/src/vtedraw.cc b/src/vtedraw.cc
index c99d02e..9e47bfa 100644
--- a/src/vtedraw.cc
+++ b/src/vtedraw.cc
@@ -1656,3 +1656,50 @@ _vte_draw_draw_line(struct _vte_draw *draw,
                                  MAX(line_width, xp - x + 1), MAX(line_width, yp - y + 1),
                                  color, alpha);
 }
+
+static inline double
+_vte_draw_get_undercurl_rad(gint width)
+{
+        return width / 2. / sqrt(2);
+}
+
+static inline double
+_vte_draw_get_undercurl_arc_height(gint width)
+{
+        return _vte_draw_get_undercurl_rad(width) * (1. - sqrt(2) / 2.);
+}
+
+double
+_vte_draw_get_undercurl_height(gint width, int line_width)
+{
+        return 2. * _vte_draw_get_undercurl_arc_height(width) + line_width;
+}
+
+void
+_vte_draw_draw_undercurl(struct _vte_draw *draw,
+                         gint x, double y, gint width,
+                         int line_width,
+                         vte::color::rgb const *color, double alpha)
+{
+        double rad = _vte_draw_get_undercurl_rad(width);
+        double yc = y + _vte_draw_get_undercurl_height(width, line_width) / 2.;
+
+        g_assert(draw->cr);
+
+        _vte_debug_print (VTE_DEBUG_DRAW,
+                        "draw_undercurl (%d, %f, %d, color=(%d,%d,%d,%.3f))\n",
+                        x,y,width,
+                        color->red, color->green, color->blue,
+                        alpha);
+
+        cairo_set_operator (draw->cr, CAIRO_OPERATOR_OVER);
+        cairo_new_sub_path(draw->cr);
+        /* First quarter circle, similar to the left half of the tilde symbol. */
+        cairo_arc (draw->cr, x + width / 4., yc + width / 4., rad, M_PI * 5 / 4, M_PI * 7 / 4);
+        /* Second quarter circle, similar to the right half of the tilde symbol. */
+        cairo_arc_negative (draw->cr, x + width * 3 / 4., yc - width / 4., rad, M_PI * 3 / 4, M_PI / 4);
+        _vte_draw_set_source_color_alpha (draw, color, alpha);
+        cairo_set_line_width (draw->cr, line_width);
+        /* FIXME: This is quite slow. The rendered bitmap should be cached and reused. */
+        cairo_stroke (draw->cr);
+}
diff --git a/src/vtedraw.hh b/src/vtedraw.hh
index 505d71b..745b841 100644
--- a/src/vtedraw.hh
+++ b/src/vtedraw.hh
@@ -93,6 +93,15 @@ void _vte_draw_draw_line(struct _vte_draw *draw,
                          int line_width,
                          vte::color::rgb const *color, double alpha);
 
+double
+_vte_draw_get_undercurl_height(gint width, int line_width);
+
+void
+_vte_draw_draw_undercurl(struct _vte_draw *draw,
+                         gint x, double y, gint width,
+                         int line_width,
+                         vte::color::rgb const *color, double alpha);
+
 G_END_DECLS
 
 #endif
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 7e1863e..bbfa437 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -652,6 +652,10 @@ public:
         long m_line_thickness;
         long m_underline_position;
         long m_underline_thickness;
+        long m_double_underline_position;
+        long m_double_underline_thickness;
+        double m_undercurl_position;
+        long m_undercurl_thickness;
         long m_strikethrough_position;
         long m_strikethrough_thickness;
         long m_regex_underline_position;
@@ -834,7 +838,7 @@ public:
                         bool draw_default_bg,
                         bool bold,
                         bool italic,
-                        bool underline,
+                        guint underline,
                         bool strikethrough,
                         bool hyperlink,
                         bool hilite,
diff --git a/src/vterowdata.h b/src/vterowdata.h
index 72412b4..630cd1a 100644
--- a/src/vterowdata.h
+++ b/src/vterowdata.h
@@ -54,7 +54,7 @@ typedef struct _VteCellAttr {
        /* 4-byte boundary */
        guint64 back: 25;       /* see vtedefines.hh */
 
-       guint64 underline: 1;
+        guint64 underline: 2;   /* 0: none, 1: single, 2: double, 3: curly */
        guint64 strikethrough: 1;
 
        guint64 reverse: 1;
@@ -62,7 +62,6 @@ typedef struct _VteCellAttr {
        guint64 dim: 1;         /* also known as faint, half intensity etc. */
 
        guint64 invisible: 1;
-        guint64 padding_unused_1: 1;
         /* 8-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
@@ -71,7 +70,7 @@ 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_2;
+        guint32 padding_unused;
 } VteCellAttr;
 G_STATIC_ASSERT (sizeof (VteCellAttr) == 16);
 G_STATIC_ASSERT (offsetof (VteCellAttr, hyperlink_idx) == VTE_CELL_ATTR_COMMON_BYTES);
@@ -123,9 +122,8 @@ static const VteCell basic_cell = {
                0, /* half */
 
                 0, /* invisible */
-                0, /* padding_unused_1 */
                 0, /* hyperlink_idx */
-                0, /* padding_unused_2 */
+                0, /* padding_unused */
        }
 };
 
diff --git a/src/vteseq.cc b/src/vteseq.cc
index 4369e65..ae68f0a 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -1856,24 +1856,34 @@ VteTerminalPrivate::seq_character_attributes(vte::parser::Params const& params)
                if (G_UNLIKELY(params.has_subparams_at_unchecked(i))) {
                         auto subparams = params.subparams_at_unchecked(i);
 
-                        long param0;
+                        long param0, param1;
                         if (G_UNLIKELY(!subparams.number_at(0, param0)))
                                 continue;
-                       if (G_UNLIKELY (param0 != 38 && param0 != 48))
-                               continue;
 
-                       unsigned int index = 1;
-                       auto color = parse_sgr_38_48_parameters(subparams, &index);
-                       /* Bail out on additional colon-separated values. */
-                       if (G_UNLIKELY(index != subparams.size() - 1))
-                               continue;
-                       if (G_LIKELY (color != -1)) {
-                               if (param0 == 38) {
-                                        m_defaults.attr.fore = color;
-                               } else {
-                                        m_defaults.attr.back = color;
-                               }
-                       }
+                        switch (param0) {
+                        case 4:
+                                if (subparams.number_at(1, param1) && param1 >= 0 && param1 <= 3)
+                                        m_defaults.attr.underline = param1;
+                                break;
+                        case 38:
+                        case 48:
+                        {
+                                unsigned int index = 1;
+                                auto color = parse_sgr_38_48_parameters(subparams, &index);
+                                /* Bail out on additional colon-separated values. */
+                                if (G_UNLIKELY(index != subparams.size() - 1))
+                                        continue;
+                                if (G_LIKELY (color != -1)) {
+                                        if (param0 == 38) {
+                                                m_defaults.attr.fore = color;
+                                        } else {
+                                                m_defaults.attr.back = color;
+                                        }
+                                }
+                                break;
+                        }
+                        }
+
                        continue;
                }
                /* If this parameter is not a number either, skip it. */
@@ -1909,7 +1919,9 @@ VteTerminalPrivate::seq_character_attributes(vte::parser::Params const& params)
                case 9:
                         m_defaults.attr.strikethrough = 1;
                        break;
-               case 21: /* Error in old versions of linux console. */
+                case 21:
+                        m_defaults.attr.underline = 2;
+                        break;
                case 22: /* ECMA 48. */
                         m_defaults.attr.bold = 0;
                         m_defaults.attr.dim = 0;


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