[vte/wip/attr: 4/5] ring: Store cell attributes together in an uint32_t



commit e7663808a4dfbbf6e3dd42683b721fb005719fca
Author: Christian Persch <chpe src gnome org>
Date:   Fri Jan 12 18:49:04 2018 +0100

    ring: Store cell attributes together in an uint32_t
    
    Instead of using a bitfield, use an uint32_t and set/get
    the values manually. This allows to check for multiple
    values at once with just one operation instead of
    checking each bitfield member separately.,

 perf/deco.sh       |   85 ++++++++++++++++
 src/Makefile.am    |    1 +
 src/attr.hh        |  137 ++++++++++++++++++++++++++
 src/ring.cc        |   32 +++---
 src/vte.cc         |  276 ++++++++++++++++++++++------------------------------
 src/vteinternal.hh |    8 +--
 src/vterowdata.h   |  108 ++++++++++++++-------
 src/vteseq.cc      |   47 +++++-----
 8 files changed, 453 insertions(+), 241 deletions(-)
---
diff --git a/perf/deco.sh b/perf/deco.sh
new file mode 100755
index 0000000..bce86e2
--- /dev/null
+++ b/perf/deco.sh
@@ -0,0 +1,85 @@
+#!/usr/bin/env bash
+
+# Test deco color support
+# Copyright © 2014 Egmont Koblinger
+# Copyright © 2018 Christian Persch
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+sep=":"
+sepsep="::"
+if [ "$1" = "-colon" -o "$1" = "-official" -o "$1" = "-dejure" ]; then
+  shift
+elif [ "$1" = "-semicolon" -o "$1" = "-common" -o "$1" = "-defacto" ]; then
+    sep=";"
+    sepsep=";" # no empty param for legacy format
+  shift
+fi
+
+if [ $# != 0 ]; then
+  echo 'Usage: deco.sh [FORMAT]' >&2
+  echo >&2
+  echo '  -colon|-official|-dejure:     Official format (default)  CSI 58:2::R:G:Bm' >&2
+  echo '  -semicolon|-common|-defacto:  Commonly used format       CSI 58;2;R;G;Bm' >&2
+  exit 1
+fi
+
+row() {
+    local format="$1"
+    local n="$2"
+    local v;
+    for m in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
+       v=$(($n * 16 + $m))
+       printf "\e[${format};4m%02X\e[0m%.0s%.0s%.0s" 38 $v $v $v $v $v $v
+    done
+    printf "\t"
+    for m in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
+       v=$(($n * 16 + $m))
+       printf "\e[${format};4m%02X\e[0m%.0s%.0s%.0s" 48 $v $v $v $v $v $v
+    done
+    printf "\n"
+}
+
+cubes() {
+    local format1="$1"
+    local format2="$2"
+    local format="%d${sep}2${sepsep}${format1};58${sep}2${sepsep}${format2}"
+    for n in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
+       row "$format" $n
+    done
+}
+
+printf "\e[0m"
+cubes "%d${sep}0${sep}0" "%d${sep}0${sep}0"
+printf "\n"
+cubes "0${sep}%d${sep}0" "0${sep}%d${sep}0"
+printf "\n"
+cubes "0${sep}0${sep}%d" "0${sep}0${sep}%d"
+printf "\n"
+cubes "%d${sep}0${sep}0" "0${sep}%d${sep}0"
+printf "\n"
+cubes "%d${sep}0${sep}0" "0${sep}0${sep}%d"
+printf "\n"
+cubes "0${sep}%d${sep}0" "%d${sep}0${sep}0"
+printf "\n"
+cubes "0${sep}%d${sep}0" "0${sep}0${sep}%d"
+printf "\n"
+cubes "0${sep}0${sep}%d" "%d${sep}0${sep}0"
+printf "\n"
+cubes "0${sep}0${sep}%d" "0${sep}%d${sep}0"
+printf "\n"
+printf "\e[0m"
+
+exit 0
diff --git a/src/Makefile.am b/src/Makefile.am
index 6f68b1e..e26d9e5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -45,6 +45,7 @@ libvte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_la_SOURCES = \
        vte/vtepty.h \
        vte/vteregex.h \
        vte/vteterminal.h \
+       attr.hh \
        buffer.h \
        caps.hh \
        caps-list.hh \
diff --git a/src/attr.hh b/src/attr.hh
new file mode 100644
index 0000000..9089927
--- /dev/null
+++ b/src/attr.hh
@@ -0,0 +1,137 @@
+/*
+ * Copyright © 2018 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#define VTE_ATTR_VALUE_MASK(bits)      ((1U << (bits)) - 1U)
+#define VTE_ATTR_MASK(shift,bits)      (VTE_ATTR_VALUE_MASK(bits) << (shift))
+
+/* Number of visible columns (as determined by g_unicode_iswide(c)).
+ * Also (ab)used for tabs; bug 353610.
+ */
+#define VTE_ATTR_COLUMNS_SHIFT         (0)
+#define VTE_ATTR_COLUMNS_BITS          (4) /* Has to be able to store the value of 8. */
+#define VTE_ATTR_COLUMNS_MASK          (VTE_ATTR_MASK(VTE_ATTR_COLUMNS_SHIFT, VTE_ATTR_COLUMNS_BITS))
+#define VTE_ATTR_COLUMNS_VALUE_MASK    (VTE_ATTR_VALUE_MASK(VTE_ATTR_COLUMNS_BITS))
+#define VTE_ATTR_COLUMNS(v)            ((v) << VTE_ATTR_COLUMNS_SHIFT)
+
+/* A continuation cell */
+#define VTE_ATTR_FRAGMENT_SHIFT        (VTE_ATTR_COLUMNS_SHIFT + VTE_ATTR_COLUMNS_BITS)
+#define VTE_ATTR_FRAGMENT_BITS         (1)
+#define VTE_ATTR_FRAGMENT_MASK         (VTE_ATTR_MASK(VTE_ATTR_FRAGMENT_SHIFT, VTE_ATTR_FRAGMENT_BITS))
+#define VTE_ATTR_FRAGMENT              (1U << VTE_ATTR_FRAGMENT_SHIFT)
+
+#define VTE_ATTR_BOLD_SHIFT            (VTE_ATTR_FRAGMENT_SHIFT + VTE_ATTR_FRAGMENT_BITS)
+#define VTE_ATTR_BOLD_BITS             (1)
+#define VTE_ATTR_BOLD_MASK             (VTE_ATTR_MASK(VTE_ATTR_BOLD_SHIFT, VTE_ATTR_BOLD_BITS))
+#define VTE_ATTR_BOLD                  (1U << VTE_ATTR_BOLD_SHIFT)
+
+#define VTE_ATTR_ITALIC_SHIFT          (VTE_ATTR_BOLD_SHIFT + VTE_ATTR_BOLD_BITS)
+#define VTE_ATTR_ITALIC_BITS           (1)
+#define VTE_ATTR_ITALIC_MASK           (VTE_ATTR_MASK(VTE_ATTR_ITALIC_SHIFT, VTE_ATTR_ITALIC_BITS))
+#define VTE_ATTR_ITALIC                (1U << VTE_ATTR_ITALIC_SHIFT)
+
+/* 0: none, 1: single, 2: double, 3: curly */
+#define VTE_ATTR_UNDERLINE_SHIFT       (VTE_ATTR_ITALIC_SHIFT + VTE_ATTR_ITALIC_BITS)
+#define VTE_ATTR_UNDERLINE_BITS        (2)
+#define VTE_ATTR_UNDERLINE_MASK        (VTE_ATTR_MASK(VTE_ATTR_UNDERLINE_SHIFT, VTE_ATTR_UNDERLINE_BITS))
+#define VTE_ATTR_UNDERLINE_VALUE_MASK  (VTE_ATTR_VALUE_MASK(VTE_ATTR_UNDERLINE_BITS))
+#define VTE_ATTR_UNDERLINE(v)          ((v) << VTE_ATTR_UNDERLINE_SHIFT)
+
+#define VTE_ATTR_STRIKETHROUGH_SHIFT   (VTE_ATTR_UNDERLINE_SHIFT + VTE_ATTR_UNDERLINE_BITS)
+#define VTE_ATTR_STRIKETHROUGH_BITS    (1)
+#define VTE_ATTR_STRIKETHROUGH_MASK    (VTE_ATTR_MASK(VTE_ATTR_STRIKETHROUGH_SHIFT, 
VTE_ATTR_STRIKETHROUGH_BITS))
+#define VTE_ATTR_STRIKETHROUGH         (1U << VTE_ATTR_STRIKETHROUGH_SHIFT)
+
+#define VTE_ATTR_OVERLINE_SHIFT        (VTE_ATTR_STRIKETHROUGH_SHIFT + VTE_ATTR_STRIKETHROUGH_BITS)
+#define VTE_ATTR_OVERLINE_BITS         (1)
+#define VTE_ATTR_OVERLINE_MASK         (VTE_ATTR_MASK(VTE_ATTR_OVERLINE_SHIFT, VTE_ATTR_OVERLINE_BITS))
+#define VTE_ATTR_OVERLINE              (1U << VTE_ATTR_OVERLINE_SHIFT)
+
+#define VTE_ATTR_REVERSE_SHIFT         (VTE_ATTR_OVERLINE_SHIFT + VTE_ATTR_OVERLINE_BITS)
+#define VTE_ATTR_REVERSE_BITS          (1)
+#define VTE_ATTR_REVERSE_MASK          (VTE_ATTR_MASK(VTE_ATTR_REVERSE_SHIFT, VTE_ATTR_REVERSE_BITS))
+#define VTE_ATTR_REVERSE               (1U << VTE_ATTR_REVERSE_SHIFT)
+
+#define VTE_ATTR_BLINK_SHIFT           (VTE_ATTR_REVERSE_SHIFT + VTE_ATTR_REVERSE_BITS)
+#define VTE_ATTR_BLINK_BITS            (1)
+#define VTE_ATTR_BLINK_MASK            (VTE_ATTR_MASK(VTE_ATTR_BLINK_SHIFT, VTE_ATTR_BLINK_BITS))
+#define VTE_ATTR_BLINK                 (1U << VTE_ATTR_BLINK_SHIFT)
+
+/* also known as faint, half intensity etc. */
+#define VTE_ATTR_DIM_SHIFT             (VTE_ATTR_BLINK_SHIFT + VTE_ATTR_BLINK_BITS)
+#define VTE_ATTR_DIM_BITS              (1)
+#define VTE_ATTR_DIM_MASK              (VTE_ATTR_MASK(VTE_ATTR_DIM_SHIFT, VTE_ATTR_DIM_BITS))
+#define VTE_ATTR_DIM                   (1U << VTE_ATTR_DIM_SHIFT)
+
+#define VTE_ATTR_INVISIBLE_SHIFT       (VTE_ATTR_DIM_SHIFT + VTE_ATTR_DIM_BITS)
+#define VTE_ATTR_INVISIBLE_BITS        (1)
+#define VTE_ATTR_INVISIBLE_MASK        (VTE_ATTR_MASK(VTE_ATTR_INVISIBLE_SHIFT, VTE_ATTR_INVISIBLE_BITS))
+#define VTE_ATTR_INVISIBLE             (1U << VTE_ATTR_INVISIBLE_SHIFT)
+
+/* Used internally only */
+#define VTE_ATTR_BOXED_SHIFT           (31)
+#define VTE_ATTR_BOXED_BITS            (1)
+#define VTE_ATTR_BOXED_MASK            (VTE_ATTR_MASK(VTE_ATTR_BOXED_SHIFT, VTE_ATTR_BOXED_BITS))
+#define VTE_ATTR_BOXED                 (1U << VTE_ATTR_BOXED_SHIFT)
+
+/* All attributes except DIM and BOXED */
+#define VTE_ATTR_ALL_MASK              (VTE_ATTR_BOLD_MASK | \
+                                        VTE_ATTR_ITALIC_MASK | \
+                                        VTE_ATTR_UNDERLINE_MASK | \
+                                        VTE_ATTR_STRIKETHROUGH_MASK | \
+                                        VTE_ATTR_OVERLINE_MASK | \
+                                        VTE_ATTR_REVERSE_MASK | \
+                                        VTE_ATTR_BLINK_MASK | \
+                                        VTE_ATTR_INVISIBLE_MASK)
+
+#define VTE_ATTR_NONE                  (0U)
+#define VTE_ATTR_DEFAULT               (VTE_ATTR_COLUMNS(1))
+
+static constexpr inline void vte_attr_set_bool(uint32_t* attr,
+                                               uint32_t mask,
+                                               bool value)
+{
+        if (value)
+                *attr |= mask;
+        else
+                *attr &= ~mask;
+}
+
+static constexpr inline void vte_attr_set_value(uint32_t* attr,
+                                                uint32_t mask,
+                                                unsigned int shift,
+                                                uint32_t value)
+{
+        g_assert_cmpuint(value << shift, <=, mask); /* assurance */
+        *attr = (*attr & ~mask) | ((value << shift) & mask /* assurance */);
+}
+
+static constexpr inline bool vte_attr_get_bool(uint32_t attr,
+                                               unsigned int shift)
+{
+        return (attr >> shift) & 1U;
+}
+
+static constexpr inline unsigned int vte_attr_get_value(uint32_t attr,
+                                                        uint32_t value_mask,
+                                                        unsigned int shift)
+{
+        return (attr >> shift) & value_mask;
+}
diff --git a/src/ring.cc b/src/ring.cc
index d91dc97..05e938e 100644
--- a/src/ring.cc
+++ b/src/ring.cc
@@ -362,7 +362,7 @@ _vte_ring_freeze_row (VteRing *ring, gulong position, const VteRowData *row)
                 * the text in real UTF-8.
                 */
                attr = cell->attr;
-               if (G_LIKELY (!attr.fragment)) {
+               if (G_LIKELY (!attr.fragment())) {
                        VteCellAttrChange attr_change;
                         guint16 hyperlink_length;
 
@@ -389,7 +389,7 @@ _vte_ring_freeze_row (VteRing *ring, gulong position, const VteRowData *row)
                        num_chars = _vte_unistr_strlen (cell->c);
                        if (num_chars > 1) {
                                 /* Combining chars */
-                               attr.columns = 0;
+                               attr.set_columns(0);
                                ring->last_attr_text_start_offset = record.text_start_offset + buffer->len
                                                                  + g_unichar_to_utf8 (_vte_unistr_get_base 
(cell->c), NULL);
                                memset(&attr_change, 0, sizeof (attr_change));
@@ -514,7 +514,7 @@ _vte_ring_thaw_row (VteRing *ring, gulong position, VteRowData *row, gboolean do
                 _VTE_DEBUG_IF(VTE_DEBUG_RING | VTE_DEBUG_HYPERLINK) {
                         /* Debug: Reverse the colors for the stream's contents. */
                         if (!do_truncate) {
-                                cell.attr.reverse = !cell.attr.reverse;
+                                cell.attr.attr ^= VTE_ATTR_REVERSE;
                         }
                 }
                cell.c = g_utf8_get_char (p);
@@ -523,12 +523,12 @@ _vte_ring_thaw_row (VteRing *ring, gulong position, VteRowData *row, gboolean do
                record.text_start_offset += q - p;
                p = q;
 
-               if (G_UNLIKELY (cell.attr.columns == 0)) {
+               if (G_UNLIKELY (cell.attr.columns() == 0)) {
                        if (G_LIKELY (row->len)) {
                                /* Combine it */
                                row->cells[row->len - 1].c = _vte_unistr_append_unichar (row->cells[row->len 
- 1].c, cell.c);
                        } else {
-                               cell.attr.columns = 1;
+                               cell.attr.set_columns(1);
                                 if (row->len == hyperlink_column && hyperlink != NULL)
                                         *hyperlink = strcpy(ring->hyperlink_buf, hyperlink_readbuf);
                                _vte_row_data_append (row, &cell);
@@ -537,11 +537,11 @@ _vte_ring_thaw_row (VteRing *ring, gulong position, VteRowData *row, gboolean do
                         if (row->len == hyperlink_column && hyperlink != NULL)
                                 *hyperlink = strcpy(ring->hyperlink_buf, hyperlink_readbuf);
                        _vte_row_data_append (row, &cell);
-                       if (cell.attr.columns > 1) {
+                       if (cell.attr.columns() > 1) {
                                /* Add the fragments */
-                               int i, columns = cell.attr.columns;
-                               cell.attr.fragment = 1;
-                               cell.attr.columns = 1;
+                               int i, columns = cell.attr.columns();
+                               cell.attr.set_fragment(true);
+                               cell.attr.set_columns(1);
                                 for (i = 1; i < columns; i++) {
                                         if (row->len == hyperlink_column && hyperlink != NULL)
                                                 *hyperlink = strcpy(ring->hyperlink_buf, hyperlink_readbuf);
@@ -1043,8 +1043,8 @@ _vte_frozen_row_column_to_text_offset (VteRing *ring,
        offset->eol_cells = -1;
        num_chars = 0;
        for (i = 0, cell = row->cells; i < row->len && i < column; i++, cell++) {
-               if (G_LIKELY (!cell->attr.fragment)) {
-                       if (G_UNLIKELY (i + cell->attr.columns > column)) {
+               if (G_LIKELY (!cell->attr.fragment())) {
+                       if (G_UNLIKELY (i + cell->attr.columns() > column)) {
                                offset->fragment_cells = column - i;
                                break;
                        }
@@ -1125,7 +1125,7 @@ _vte_frozen_row_text_offset_to_column (VteRing *ring,
 
        /* count the number of columns for the given number of characters */
        for (i = 0, cell = row->cells; i < row->len; i++, cell++) {
-               if (G_LIKELY (!cell->attr.fragment)) {
+               if (G_LIKELY (!cell->attr.fragment())) {
                        if (num_chars == 0) break;
                        nc = _vte_unistr_strlen(cell->c);
                        if (nc > num_chars) break;
@@ -1280,13 +1280,13 @@ _vte_ring_rewrap (VteRing *ring,
                        }
                        runlength = MIN(paragraph_len, attr_change.text_end_offset - text_offset);
 
-                       if (G_UNLIKELY (attr_change.attr.columns == 0)) {
+                       if (G_UNLIKELY (attr_change.attr.columns() == 0)) {
                                /* Combining characters all fit in the current row */
                                text_offset += runlength;
                                paragraph_len -= runlength;
                        } else {
                                while (runlength) {
-                                       if (col >= columns - attr_change.attr.columns + 1) {
+                                       if (col >= columns - attr_change.attr.columns() + 1) {
                                                /* Wrap now, write the soft wrapped row's record */
                                                new_record.soft_wrapped = 1;
                                                _vte_stream_append(new_row_stream, (const char *) 
&new_record, sizeof (new_record));
@@ -1319,7 +1319,7 @@ _vte_ring_rewrap (VteRing *ring,
                                                /* Process one character only. */
                                                char textbuf[6];  /* fits at least one UTF-8 character */
                                                int textbuf_len;
-                                               col += attr_change.attr.columns;
+                                               col += attr_change.attr.columns();
                                                /* Find beginning of next UTF-8 character */
                                                text_offset++; paragraph_len--; runlength--;
                                                textbuf_len = MIN(runlength, sizeof (textbuf));
@@ -1414,7 +1414,7 @@ _vte_ring_write_row (VteRing *ring,
         * TODO Should unify one day */
        g_string_set_size (buffer, 0);
        for (i = 0, cell = row->cells; i < row->len; i++, cell++) {
-               if (G_LIKELY (!cell->attr.fragment))
+               if (G_LIKELY (!cell->attr.fragment()))
                        _vte_unistr_append_to_string (cell->c, buffer);
        }
        if (!row->attr.soft_wrapped)
diff --git a/src/vte.cc b/src/vte.cc
index 4d3cdda..aa6a810 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -534,7 +534,7 @@ VteTerminalPrivate::find_start_column(vte::grid::column_t col,
                return col;
        if (row_data != nullptr) {
                const VteCell *cell = _vte_row_data_get (row_data, col);
-               while (col > 0 && cell != NULL && cell->attr.fragment) {
+               while (col > 0 && cell != NULL && cell->attr.fragment()) {
                        cell = _vte_row_data_get (row_data, --col);
                }
        }
@@ -552,11 +552,11 @@ VteTerminalPrivate::find_end_column(vte::grid::column_t col,
                return col;
        if (row_data != NULL) {
                const VteCell *cell = _vte_row_data_get (row_data, col);
-               while (col > 0 && cell != NULL && cell->attr.fragment) {
+               while (col > 0 && cell != NULL && cell->attr.fragment()) {
                        cell = _vte_row_data_get (row_data, --col);
                }
                if (cell) {
-                       columns = cell->attr.columns - 1;
+                       columns = cell->attr.columns() - 1;
                }
        }
         // FIXMEchp m__column_count - 1 ?
@@ -632,11 +632,11 @@ VteTerminalPrivate::invalidate_cell(vte::grid::column_t col,
                const VteCell *cell;
                cell = _vte_row_data_get (row_data, col);
                if (cell != NULL) {
-                       while (cell->attr.fragment && col> 0) {
+                       while (cell->attr.fragment() && col> 0) {
                                cell = _vte_row_data_get (row_data, --col);
                        }
-                       columns = cell->attr.columns;
-                       style = _vte_draw_get_style(cell->attr.bold, cell->attr.italic);
+                       columns = cell->attr.columns();
+                       style = _vte_draw_get_style(cell->attr.bold(), cell->attr.italic());
                         if (cell->c != 0) {
                                 int right;
                                 _vte_draw_get_char_edges(m_draw, cell->c, columns, style, NULL, &right);
@@ -677,8 +677,8 @@ VteTerminalPrivate::invalidate_cursor_once(bool periodic)
 
                auto cell = find_charcell(column, row);
                if (cell != NULL) {
-                       columns = cell->attr.columns;
-                       auto style = _vte_draw_get_style(cell->attr.bold, cell->attr.italic);
+                       columns = cell->attr.columns();
+                       auto style = _vte_draw_get_style(cell->attr.bold(), cell->attr.italic());
                         if (cell->c != 0) {
                                 int right;
                                 _vte_draw_get_char_edges(m_draw, cell->c, columns, style, NULL, &right);
@@ -2741,35 +2741,35 @@ VteTerminalPrivate::cleanup_fragments(long start,
          * handling the left hand side, but handling the right hand side first might
          * overwrite it if start == end (inserting to the middle of a character). */
         cell_start = _vte_row_data_get (row, start);
-        cell_start_is_fragment = cell_start != NULL && cell_start->attr.fragment;
+        cell_start_is_fragment = cell_start != NULL && cell_start->attr.fragment();
 
         /* On the right hand side, try to replace a TAB by a shorter TAB if we can.
          * This requires that the TAB on the left (which might be the same TAB) is
          * not yet converted to spaces, so start on the right hand side. */
         cell_end = _vte_row_data_get_writable (row, end);
-        if (G_UNLIKELY (cell_end != NULL && cell_end->attr.fragment)) {
+        if (G_UNLIKELY (cell_end != NULL && cell_end->attr.fragment())) {
                 col = end;
                 do {
                         col--;
                         g_assert(col >= 0);  /* The first cell can't be a fragment. */
                         cell_col = _vte_row_data_get_writable (row, col);
-                } while (cell_col->attr.fragment);
+                } while (cell_col->attr.fragment());
                 if (cell_col->c == '\t') {
                         _vte_debug_print(VTE_DEBUG_MISC,
                                          "Replacing right part of TAB with a shorter one at %ld (%ld cells) 
=> %ld (%ld cells)\n",
-                                         col, (long) cell_col->attr.columns, end, (long) 
cell_col->attr.columns - (end - col));
+                                         col, (long) cell_col->attr.columns(), end, (long) 
cell_col->attr.columns() - (end - col));
                         cell_end->c = '\t';
-                        cell_end->attr.fragment = 0;
-                        g_assert(cell_col->attr.columns > end - col);
-                        cell_end->attr.columns = cell_col->attr.columns - (end - col);
+                        cell_end->attr.set_fragment(false);
+                        g_assert(cell_col->attr.columns() > end - col);
+                        cell_end->attr.set_columns(cell_col->attr.columns() - (end - col));
                 } else {
                         _vte_debug_print(VTE_DEBUG_MISC,
                                          "Cleaning CJK right half at %ld\n",
                                          end);
-                        g_assert(end - col == 1 && cell_col->attr.columns == 2);
+                        g_assert(end - col == 1 && cell_col->attr.columns() == 2);
                         cell_end->c = ' ';
-                        cell_end->attr.fragment = 0;
-                        cell_end->attr.columns = 1;
+                        cell_end->attr.set_fragment(false);
+                        cell_end->attr.set_columns(1);
                         invalidate_cells(
                                               end, 1,
                                               m_screen->cursor.row, 1);
@@ -2785,11 +2785,11 @@ VteTerminalPrivate::cleanup_fragments(long start,
                         col--;
                         g_assert(col >= 0);  /* The first cell can't be a fragment. */
                         cell_col = _vte_row_data_get_writable (row, col);
-                        if (!cell_col->attr.fragment) {
+                        if (!cell_col->attr.fragment()) {
                                 if (cell_col->c == '\t') {
                                         _vte_debug_print(VTE_DEBUG_MISC,
                                                          "Replacing left part of TAB with spaces at %ld (%ld 
=> %ld cells)\n",
-                                                         col, (long)cell_col->attr.columns, start - col);
+                                                         col, (long)cell_col->attr.columns(), start - col);
                                         /* nothing to do here */
                                 } else {
                                         _vte_debug_print(VTE_DEBUG_MISC,
@@ -2803,8 +2803,8 @@ VteTerminalPrivate::cleanup_fragments(long start,
                                 keep_going = FALSE;
                         }
                         cell_col->c = ' ';
-                        cell_col->attr.fragment = 0;
-                        cell_col->attr.columns = 1;
+                        cell_col->attr.set_fragment(false);
+                        cell_col->attr.set_columns(1);
                 } while (keep_going);
         }
 }
@@ -3065,7 +3065,7 @@ VteTerminalPrivate::insert_char(gunichar c,
                        goto not_inserted;
 
                /* Find the previous cell */
-               while (cell && cell->attr.fragment && col > 0)
+               while (cell && cell->attr.fragment() && col > 0)
                        cell = _vte_row_data_get_writable (row, --col);
                if (G_UNLIKELY (!cell || cell->c == '\t'))
                        goto not_inserted;
@@ -3074,7 +3074,7 @@ VteTerminalPrivate::insert_char(gunichar c,
                c = _vte_unistr_append_unichar (cell->c, c);
 
                /* And set it */
-               columns = cell->attr.columns;
+               columns = cell->attr.columns();
                for (i = 0; i < columns; i++) {
                        cell = _vte_row_data_get_writable (row, col++);
                        cell->c = c;
@@ -3107,7 +3107,7 @@ VteTerminalPrivate::insert_char(gunichar c,
 
         attr = m_defaults.attr;
         attr.copy_colors(m_color_defaults.attr);
-       attr.columns = columns;
+       attr.set_columns(columns);
 
        {
                VteCell *pcell = _vte_row_data_get_writable (row, col);
@@ -3117,7 +3117,7 @@ VteTerminalPrivate::insert_char(gunichar c,
        }
 
        /* insert wide-char fragments */
-       attr.fragment = 1;
+       attr.set_fragment(true);
        for (i = 1; i < columns; i++) {
                VteCell *pcell = _vte_row_data_get_writable (row, col);
                pcell->c = c;
@@ -6035,7 +6035,7 @@ VteTerminalPrivate::get_text(vte::grid::row_t start_row,
                                /* If it's not part of a multi-column character,
                                 * and passes the selection criterion, add it to
                                 * the selection. */
-                               if (!pcell->attr.fragment) {
+                               if (!pcell->attr.fragment()) {
                                        /* Store the attributes of this character. */
                                         // FIXMEchpe shouldn't this use determine_colors?
                                         uint32_t fg, bg, dc;
@@ -6048,8 +6048,8 @@ VteTerminalPrivate::get_text(vte::grid::row_t start_row,
                                        attr.back.red = back.red;
                                        attr.back.green = back.green;
                                        attr.back.blue = back.blue;
-                                       attr.underline = (pcell->attr.underline == 1);
-                                       attr.strikethrough = pcell->attr.strikethrough;
+                                       attr.underline = (pcell->attr.underline() == 1);
+                                       attr.strikethrough = pcell->attr.strikethrough();
 
                                        /* Store the cell string */
                                        if (pcell->c == 0) {
@@ -6085,7 +6085,7 @@ VteTerminalPrivate::get_text(vte::grid::row_t start_row,
                                while ((pcell = _vte_row_data_get (row_data, col))) {
                                        col++;
 
-                                       if (pcell->attr.fragment)
+                                       if (pcell->attr.fragment())
                                                continue;
 
                                        if (pcell->c != 0)
@@ -6179,15 +6179,9 @@ static bool
 vte_terminal_cellattr_equal(VteCellAttr const* attr1,
                             VteCellAttr const* attr2)
 {
-       return (attr1->bold          == attr2->bold      &&
-               attr1->italic        == attr2->italic    &&
-                attr1->colors()      == attr2->colors()  &&
-               attr1->underline     == attr2->underline &&
-               attr1->strikethrough == attr2->strikethrough &&
-                attr1->overline      == attr2->overline  &&
-               attr1->reverse       == attr2->reverse   &&
-               attr1->blink         == attr2->blink     &&
-                attr1->invisible     == attr2->invisible &&
+        //FIXMEchpe why exclude DIM here?
+       return (((attr1->attr ^ attr2->attr) & VTE_ATTR_ALL_MASK) == 0 &&
+                attr1->colors()       == attr2->colors()   &&
                 attr1->hyperlink_idx  == attr2->hyperlink_idx);
 }
 
@@ -6207,16 +6201,16 @@ VteTerminalPrivate::cellattr_to_html(VteCellAttr const* attr,
 
         determine_colors(attr, false, false, &fore, &back, &deco);
 
-       if (attr->bold) {
+       if (attr->bold()) {
                g_string_prepend(string, "<b>");
                g_string_append(string, "</b>");
        }
-       if (attr->italic) {
+       if (attr->italic()) {
                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) {
+        if (attr->underline() != 0) {
                 static const char styles[][7] = {"", "single", "double", "wavy"};
                 char *tag, *colorattr;
 
@@ -6233,14 +6227,14 @@ VteTerminalPrivate::cellattr_to_html(VteCellAttr const* attr,
                 }
 
                 tag = g_strdup_printf("<u style=\"text-decoration-style:%s%s\">",
-                                      styles[attr->underline],
+                                      styles[attr->underline()],
                                       colorattr);
                 g_string_prepend(string, tag);
                 g_free(tag);
                 g_free(colorattr);
                 g_string_append(string, "</u>");
         }
-       if (fore != VTE_DEFAULT_FG || attr->reverse) {
+       if (fore != VTE_DEFAULT_FG || attr->reverse()) {
                vte::color::rgb color;
                 char *tag;
 
@@ -6253,7 +6247,7 @@ VteTerminalPrivate::cellattr_to_html(VteCellAttr const* attr,
                g_free(tag);
                g_string_append(string, "</font>");
        }
-       if (back != VTE_DEFAULT_BG || attr->reverse) {
+       if (back != VTE_DEFAULT_BG || attr->reverse()) {
                vte::color::rgb color;
                 char *tag;
 
@@ -6266,15 +6260,15 @@ VteTerminalPrivate::cellattr_to_html(VteCellAttr const* attr,
                g_free(tag);
                g_string_append(string, "</span>");
        }
-       if (attr->strikethrough) {
+       if (attr->strikethrough()) {
                g_string_prepend(string, "<strike>");
                g_string_append(string, "</strike>");
        }
-       if (attr->overline) {
+       if (attr->overline()) {
                g_string_prepend(string, "<span style=\"text-decoration-line:overline\">");
                g_string_append(string, "</span>");
        }
-       if (attr->blink) {
+       if (attr->blink()) {
                g_string_prepend(string, "<blink>");
                g_string_append(string, "</blink>");
        }
@@ -6631,7 +6625,7 @@ VteTerminalPrivate::extend_selection_expand()
                /* Find the last non-empty character on the first line. */
                for (i = _vte_row_data_length (rowdata); i > 0; i--) {
                        cell = _vte_row_data_get (rowdata, i - 1);
-                       if (cell->attr.fragment || cell->c != 0)
+                       if (cell->attr.fragment() || cell->c != 0)
                                break;
                }
        } else {
@@ -6657,7 +6651,7 @@ VteTerminalPrivate::extend_selection_expand()
                /* Find the last non-empty character on the last line. */
                for (i = _vte_row_data_length (rowdata); i > 0; i--) {
                        cell = _vte_row_data_get (rowdata, i - 1);
-                       if (cell->attr.fragment || cell->c != 0)
+                       if (cell->attr.fragment() || cell->c != 0)
                                break;
                }
                /* If the end point is to its right, then extend the
@@ -8878,7 +8872,7 @@ VteTerminalPrivate::determine_colors(VteCellAttr const* attr,
        }
 
        /* Handle bold by using set bold color or brightening */
-       if (attr->bold && m_bold_is_bright) {
+       if (attr->bold() && m_bold_is_bright) {
                if (fore == VTE_DEFAULT_FG)
                        fore = VTE_BOLD_FG;
                else if (fore >= VTE_LEGACY_COLORS_OFFSET && fore < VTE_LEGACY_COLORS_OFFSET + 
VTE_LEGACY_COLOR_SET_SIZE) {
@@ -8889,12 +8883,12 @@ VteTerminalPrivate::determine_colors(VteCellAttr const* attr,
         /* Handle dim colors.  Only apply to palette colors, dimming direct RGB wouldn't make sense.
          * Apply to the foreground color only, but do this before handling reverse/highlight so that
          * those can be used to dim the background instead. */
-        if (attr->dim && !(fore & VTE_RGB_COLOR_MASK(8, 8, 8))) {
+        if (attr->dim() && !(fore & VTE_RGB_COLOR_MASK(8, 8, 8))) {
                fore |= VTE_DIM_COLOR;
         }
 
        /* Reverse cell? */
-       if (attr->reverse) {
+       if (attr->reverse()) {
                swap (&fore, &back);
        }
 
@@ -8934,7 +8928,7 @@ VteTerminalPrivate::determine_colors(VteCellAttr const* attr,
         /* FIXME: This is dead code, this is not where we actually handle invisibile.
          * Instead, draw_cells() is not called from draw_rows().
          * That is required for the foreground to be transparent if so is the background. */
-       if (attr->invisible) {
+        if (attr->invisible()) {
                 fore = back;
                 deco = VTE_DEFAULT_FG;
        }
@@ -8985,15 +8979,9 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                uint32_t deco,
                                bool clear,
                                bool draw_default_bg,
-                               bool bold,
-                               bool italic,
-                               guint underline,
-                               bool strikethrough,
-                               bool overline,
-                               bool blink,
+                               uint32_t attr,
                                bool hyperlink,
                                bool hilite,
-                               bool boxed,
                                int column_width,
                                int row_height)
 {
@@ -9002,6 +8990,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
         vte::color::rgb fg, bg, dc;
 
        g_assert(n > 0);
+#if 0
        _VTE_DEBUG_IF(VTE_DEBUG_CELLS) {
                GString *str = g_string_new (NULL);
                gchar *tmp;
@@ -9017,8 +9006,9 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                 hyperlink, hilite, boxed);
                g_free (tmp);
        }
+#endif
 
-       bold = bold && m_allow_bold;
+       auto bold = (attr & VTE_ATTR_BOLD) && m_allow_bold;
         rgb_from_index<8, 8, 8>(fore, fg);
         rgb_from_index<8, 8, 8>(back, bg);
         // FIXMEchpe defer resolving deco color until we actually need to draw an underline?
@@ -9046,7 +9036,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                }
        } while (i < n);
 
-        if (blink) {
+        if (attr & VTE_ATTR_BLINK) {
                 /* Notify the caller that cells with the "blink" attribute were encountered (regardless of
                  * whether they're actually painted or skipped now), so that the caller can set up a timer
                  * to make them blink if it wishes to. */
@@ -9065,7 +9055,11 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
          * 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 | overline | hyperlink | hilite | boxed) {
+        if ((attr & (VTE_ATTR_UNDERLINE_MASK |
+                     VTE_ATTR_STRIKETHROUGH_MASK |
+                     VTE_ATTR_OVERLINE_MASK |
+                     VTE_ATTR_BOXED_MASK)) |
+            hyperlink | hilite) {
                i = 0;
                do {
                        x = items[i].x;
@@ -9073,7 +9067,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                        for (columns = 0; i < n && items[i].y == y; i++) {
                                columns += items[i].columns;
                        }
-                        switch (underline) {
+                        switch (vte_attr_get_value(attr, VTE_ATTR_UNDERLINE_VALUE_MASK, 
VTE_ATTR_UNDERLINE_SHIFT)) {
                         case 1:
                                 _vte_draw_draw_line(m_draw,
                                                     x,
@@ -9108,7 +9102,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                                          &dc, VTE_DRAW_OPAQUE);
                                 break;
                        }
-                       if (strikethrough) {
+                       if (attr & VTE_ATTR_STRIKETHROUGH) {
                                 _vte_draw_draw_line(m_draw,
                                                     x,
                                                     y + m_strikethrough_position,
@@ -9117,7 +9111,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                                     VTE_LINE_WIDTH,
                                                     &fg, VTE_DRAW_OPAQUE);
                        }
-                        if (overline) {
+                        if (attr & VTE_ATTR_OVERLINE) {
                                 _vte_draw_draw_line(m_draw,
                                                     x,
                                                     y + m_overline_position,
@@ -9144,7 +9138,7 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
                                                                  &fg, VTE_DRAW_OPAQUE);
                                 }
                         }
-                       if (boxed) {
+                       if (attr & VTE_ATTR_BOXED) {
                                 _vte_draw_draw_rectangle(m_draw,
                                                x, y,
                                                MAX(0, (columns * column_width)),
@@ -9157,7 +9151,8 @@ VteTerminalPrivate::draw_cells(struct _vte_draw_text_request *items,
         _vte_draw_text(m_draw,
                        items, n,
                        &fg, VTE_DRAW_OPAQUE,
-                       _vte_draw_get_style(bold, italic));
+                       _vte_draw_get_style(attr & VTE_ATTR_BOLD,
+                                           attr & VTE_ATTR_ITALIC));
 }
 
 /* FIXME: we don't have a way to tell GTK+ what the default text attributes
@@ -9230,7 +9225,7 @@ VteTerminalPrivate::fudge_pango_colors(GSList *attributes,
                                (props[i].bg.green == 0) &&
                                (props[i].bg.blue == 0)) {
                         cells[i].attr.copy_colors(m_color_defaults.attr);
-                       cells[i].attr.reverse = TRUE;
+                       cells[i].attr.set_reverse(true);
                }
        }
 }
@@ -9270,7 +9265,7 @@ VteTerminalPrivate::apply_pango_attr(PangoAttribute *attr,
                for (i = attr->start_index;
                     (i < attr->end_index) && (i < n_cells);
                     i++) {
-                       cells[i].attr.strikethrough = (ival != FALSE);
+                       cells[i].attr.set_strikethrough(ival != FALSE);
                }
                break;
        case PANGO_ATTR_UNDERLINE:
@@ -9279,7 +9274,7 @@ VteTerminalPrivate::apply_pango_attr(PangoAttribute *attr,
                for (i = attr->start_index;
                     (i < attr->end_index) && (i < n_cells);
                     i++) {
-                       cells[i].attr.underline = (ival != PANGO_UNDERLINE_NONE);
+                       cells[i].attr.set_underline(ival == PANGO_UNDERLINE_SINGLE ? 1 : 0);
                }
                break;
        case PANGO_ATTR_WEIGHT:
@@ -9288,7 +9283,7 @@ VteTerminalPrivate::apply_pango_attr(PangoAttribute *attr,
                for (i = attr->start_index;
                     (i < attr->end_index) && (i < n_cells);
                     i++) {
-                       cells[i].attr.bold = (ival >= PANGO_WEIGHT_BOLD);
+                       cells[i].attr.set_bold(ival >= PANGO_WEIGHT_BOLD);
                }
                break;
        default:
@@ -9375,14 +9370,9 @@ VteTerminalPrivate::draw_cells_with_attributes(struct _vte_draw_text_request *it
                                        back,
                                         deco,
                                        TRUE, draw_default_bg,
-                                       cells[j].attr.bold,
-                                       cells[j].attr.italic,
-                                       cells[j].attr.underline,
-                                       cells[j].attr.strikethrough,
-                                        cells[j].attr.overline,
-                                        cells[j].attr.blink,
+                                       cells[j].attr.attr,
                                         m_allow_hyperlink && cells[j].attr.hyperlink_idx != 0,
-                                       FALSE, FALSE, column_width, height);
+                                       FALSE, column_width, height);
                j += g_unichar_to_utf8(items[i].c, scratch_buf);
        }
        g_free(cells);
@@ -9407,11 +9397,9 @@ 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, deco, ndeco, underline, nunderline;
-        gboolean bold, nbold, italic, nitalic,
-                 hyperlink, nhyperlink, hilite, nhilite,
-                selected, nselected, strikethrough, nstrikethrough,
-                 overline, noverline, invisible, ninvisible, blink, nblink;
+        guint fore, nfore, back, nback, deco, ndeco;
+        gboolean hyperlink, nhyperlink, hilite, nhilite,
+                selected, nselected;
        guint item_count;
        const VteCell *cell;
        VteRowData const* row_data;
@@ -9432,7 +9420,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                if (row_data != NULL) {
                        cell = _vte_row_data_get (row_data, i);
                        if (cell != NULL) {
-                               while (cell->attr.fragment && i > 0) {
+                               while (cell->attr.fragment() && i > 0) {
                                        cell = _vte_row_data_get (row_data, --i);
                                }
                        }
@@ -9444,8 +9432,8 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                selected = cell_is_selected(i, row);
                                 determine_colors(cell, selected, &fore, &back, &deco);
 
-                               bold = cell && cell->attr.bold;
-                               j = i + (cell ? cell->attr.columns : 1);
+                               bool bold = cell && cell->attr.bold();
+                               j = i + (cell ? cell->attr.columns() : 1);
 
                                while (j < end_column){
                                        /* Retrieve the cell. */
@@ -9453,7 +9441,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                        /* Don't render fragments of multicolumn characters
                                         * which have the same attributes as the initial
                                         * portions. */
-                                       if (cell != NULL && cell->attr.fragment) {
+                                       if (cell != NULL && cell->attr.fragment()) {
                                                j++;
                                                continue;
                                        }
@@ -9465,8 +9453,8 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                        if (nback != back) {
                                                break;
                                        }
-                                       bold = cell && cell->attr.bold;
-                                       j += cell ? cell->attr.columns : 1;
+                                       bold = cell && cell->attr.bold();
+                                       j += cell ? cell->attr.columns() : 1;
                                }
                                if (back != VTE_DEFAULT_BG) {
                                        vte::color::rgb bg;
@@ -9532,7 +9520,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                if (cell == NULL) {
                        goto fg_skip_row;
                }
-               while (cell->attr.fragment && i > 0)
+               while (cell->attr.fragment() && i > 0)
                        cell = _vte_row_data_get (row_data, --i);
 
                /* Walk the line. */
@@ -9542,13 +9530,13 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                        if (cell == NULL) {
                                goto fg_skip_row;
                        }
-                       while (cell->c == 0 || cell->attr.invisible ||
+                       while (cell->c == 0 || cell->attr.invisible() ||
                                        (cell->c == ' ' &&
-                                        !cell->attr.underline &&
-                                         !cell->attr.strikethrough &&
-                                         !cell->attr.overline &&
+                                         cell->attr.has_none(VTE_ATTR_UNDERLINE_MASK |
+                                                             VTE_ATTR_STRIKETHROUGH_MASK |
+                                                             VTE_ATTR_OVERLINE_MASK) &&
                                          (!m_allow_hyperlink || cell->attr.hyperlink_idx == 0)) ||
-                                       cell->attr.fragment) {
+                               cell->attr.fragment()) {
                                if (++i >= end_column) {
                                        goto fg_skip_row;
                                }
@@ -9560,14 +9548,10 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                        /* Find the colors for this cell. */
                        selected = cell_is_selected(i, row);
                         determine_colors(cell, selected, &fore, &back, &deco);
-                       underline = cell->attr.underline;
-                       strikethrough = cell->attr.strikethrough;
-                        overline = cell->attr.overline;
-                        invisible = cell->attr.invisible;
+
+                        uint32_t const attr = cell->attr.attr;
+
                         hyperlink = (m_allow_hyperlink && cell->attr.hyperlink_idx != 0);
-                       bold = cell->attr.bold;
-                       italic = cell->attr.italic;
-                        blink = cell->attr.blink;
                         if (cell->attr.hyperlink_idx != 0 && cell->attr.hyperlink_idx == 
m_hyperlink_hover_idx) {
                                 hilite = true;
                         } else if (m_hyperlink_hover_idx == 0 && m_show_match) {
@@ -9577,7 +9561,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                        }
 
                        items[0].c = cell->c;
-                       items[0].columns = cell->attr.columns;
+                       items[0].columns = cell->attr.columns();
                        items[0].x = start_x + i * column_width;
                        items[0].y = y;
                        j = i + items[0].columns;
@@ -9593,7 +9577,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                        }
                                         /* Ignore the attributes on a fragment, the attributes
                                          * of the preceding character cell should apply. */
-                                        if (cell->attr.fragment) {
+                                        if (cell->attr.fragment()) {
                                                j++;
                                                continue;
                                        }
@@ -9601,7 +9585,10 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                                /* only break the run if we
                                                 * are drawing attributes
                                                 */
-                                                if (underline || strikethrough || overline || hyperlink || 
hilite) {
+                                                if ((attr & (VTE_ATTR_UNDERLINE_MASK |
+                                                             VTE_ATTR_STRIKETHROUGH_MASK |
+                                                             VTE_ATTR_OVERLINE_MASK)) |
+                                                    hyperlink | hilite) {
                                                        break;
                                                } else {
                                                        j++;
@@ -9619,39 +9606,24 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                         if (ndeco != deco) {
                                                 break;
                                         }
-                                       nbold = cell->attr.bold;
-                                       if (nbold != bold) {
-                                               break;
-                                       }
-                                       nitalic = cell->attr.italic;
-                                       if (nitalic != italic) {
-                                               break;
-                                       }
-                                       /* Break up underlined/not-underlined text. */
-                                       nunderline = cell->attr.underline;
-                                       if (nunderline != underline) {
-                                               break;
-                                       }
-                                       nstrikethrough = cell->attr.strikethrough;
-                                       if (nstrikethrough != strikethrough) {
-                                               break;
-                                       }
-                                        noverline = cell->attr.overline;
-                                        if (noverline != overline) {
-                                                break;
-                                        }
-                                        nblink = cell->attr.blink;
-                                        if (nblink != blink) {
+
+                                        /* Bold, italic, underline, strikethrough,
+                                         * overline, blink, or invisible differ;
+                                         * break the run.
+                                         */
+                                        if ((cell->attr.attr ^ attr) & (VTE_ATTR_BOLD_MASK |
+                                                                        VTE_ATTR_ITALIC_MASK |
+                                                                        VTE_ATTR_UNDERLINE_MASK |
+                                                                        VTE_ATTR_STRIKETHROUGH_MASK |
+                                                                        VTE_ATTR_OVERLINE_MASK |
+                                                                        VTE_ATTR_BLINK_MASK |
+                                                                        VTE_ATTR_INVISIBLE_MASK))
                                                 break;
-                                        }
+
                                         nhyperlink = (m_allow_hyperlink && cell->attr.hyperlink_idx != 0);
                                         if (nhyperlink != hyperlink) {
                                                 break;
                                         }
-                                        ninvisible = cell->attr.invisible;
-                                        if (ninvisible != invisible) {
-                                                break;
-                                        }
                                        /* Break up matched/not-matched text. */
                                        nhilite = false;
                                         if (cell->attr.hyperlink_idx != 0 && cell->attr.hyperlink_idx == 
m_hyperlink_hover_idx) {
@@ -9664,7 +9636,7 @@ VteTerminalPrivate::draw_rows(VteScreen *screen_,
                                        }
                                        /* Add this cell to the draw list. */
                                        items[item_count].c = cell->c;
-                                       items[item_count].columns = cell->attr.columns;
+                                       items[item_count].columns = cell->attr.columns();
                                        items[item_count].x = start_x + j * column_width;
                                        items[item_count].y = y;
                                        j +=  items[item_count].columns;
@@ -9694,7 +9666,7 @@ fg_next_row:
                                        j = start_column;
                                        cell = _vte_row_data_get (row_data, j);
                                } while (cell == NULL);
-                               while (cell->attr.fragment && j > 0) {
+                               while (cell->attr.fragment() && j > 0) {
                                        cell = _vte_row_data_get (row_data, --j);
                                }
                        } while (TRUE);
@@ -9704,8 +9676,8 @@ fg_draw:
                                        items,
                                        item_count,
                                         fore, back, deco, FALSE, FALSE,
-                                        bold, italic, underline, strikethrough,
-                                        overline, blink, hyperlink, hilite, FALSE,
+                                        attr /* & VTE_ATTR_ALL_MASK */,
+                                        hyperlink, hilite,
                                        column_width, row_height);
                        item_count = 1;
                        /* We'll need to continue at the first cell which didn't
@@ -9838,18 +9810,18 @@ VteTerminalPrivate::paint_cursor()
         /* Find the first cell of the character "under" the cursor.
          * This is for CJK.  For TAB, paint the cursor where it really is. */
        auto cell = find_charcell(col, drow);
-        while (cell != NULL && cell->attr.fragment && cell->c != '\t' && col > 0) {
+        while (cell != NULL && cell->attr.fragment() && cell->c != '\t' && col > 0) {
                col--;
                cell = find_charcell(col, drow);
        }
 
        /* Draw the cursor. */
        item.c = (cell && cell->c) ? cell->c : ' ';
-       item.columns = item.c == '\t' ? 1 : cell ? cell->attr.columns : 1;
+       item.columns = item.c == '\t' ? 1 : cell ? cell->attr.columns() : 1;
        item.x = col * width;
        item.y = row_to_pixel(drow);
        if (cell && cell->c != 0) {
-               style = _vte_draw_get_style(cell->attr.bold, cell->attr.italic);
+               style = _vte_draw_get_style(cell->attr.bold(), cell->attr.italic());
        }
 
        selected = cell_is_selected(col, drow);
@@ -9899,7 +9871,7 @@ VteTerminalPrivate::paint_cursor()
 
                         if (cell && cell->c != 0 && cell->c != ' ' && cell->c != '\t') {
                                 int l, r;
-                                _vte_draw_get_char_edges (m_draw, cell->c, cell->attr.columns, style, &l, 
&r);
+                                _vte_draw_get_char_edges (m_draw, cell->c, cell->attr.columns(), style, &l, 
&r);
                                 left = MIN(left, l);
                                 right = MAX(right, r);
                         }
@@ -9918,7 +9890,7 @@ VteTerminalPrivate::paint_cursor()
                         cursor_width = item.columns * width;
                         if (cell && cell->c != 0 && cell->c != ' ' && cell->c != '\t') {
                                 int r;
-                                _vte_draw_get_char_edges (m_draw, cell->c, cell->attr.columns, style, NULL, 
&r);
+                                _vte_draw_get_char_edges (m_draw, cell->c, cell->attr.columns(), style, 
NULL, &r);
                                 cursor_width = MAX(cursor_width, r);
                        }
 
@@ -9933,15 +9905,9 @@ VteTerminalPrivate::paint_cursor()
                                         draw_cells(
                                                         &item, 1,
                                                         fore, back, deco, TRUE, FALSE,
-                                                        cell->attr.bold,
-                                                        cell->attr.italic,
-                                                        cell->attr.underline,
-                                                        cell->attr.strikethrough,
-                                                        cell->attr.overline,
-                                                        cell->attr.blink,
+                                                        cell->attr.attr,
                                                         m_allow_hyperlink && cell->attr.hyperlink_idx != 0,
                                                         FALSE,
-                                                        FALSE,
                                                         width,
                                                         height);
                                }
@@ -10025,15 +9991,9 @@ VteTerminalPrivate::paint_im_preedit_string()
                                                 fore, back, deco,
                                                 TRUE,  /* clear */
                                                 TRUE,  /* draw_default_bg */
-                                                FALSE, /* bold */
-                                                FALSE, /* italic */
-                                                0,     /* underline */
-                                                FALSE, /* strikethrough */
-                                                FALSE, /* overline */
-                                                FALSE, /* blink */
+                                                VTE_ATTR_NONE | VTE_ATTR_BOXED,
                                                 FALSE, /* hyperlink */
                                                 FALSE, /* hilite */
-                                                TRUE,  /* boxed */
                                                width, height);
                }
                g_free(items);
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 41c1824..f25c87e 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -848,15 +848,9 @@ public:
                         uint32_t deco,
                         bool clear,
                         bool draw_default_bg,
-                        bool bold,
-                        bool italic,
-                        guint underline,
-                        bool strikethrough,
-                        bool overline,
-                        bool blink,
+                        uint32_t attr,
                         bool hyperlink,
                         bool hilite,
-                        bool boxed,
                         int column_width,
                         int row_height);
         void fudge_pango_colors(GSList *attributes,
diff --git a/src/vterowdata.h b/src/vterowdata.h
index d600ba9..410219a 100644
--- a/src/vterowdata.h
+++ b/src/vterowdata.h
@@ -21,19 +21,18 @@
 #ifndef vterowdata_h_included
 #define vterowdata_h_included
 
-#include <cstdint>
-
 #include <string.h>
 
 #include "vteunistr.h"
 #include "vtemacros.h"
 #include "vtedefines.hh"
+
+#include "attr.hh"
 #include "color-triple.hh"
 
 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_TAB_WIDTH_MAX              ((1 << VTE_ATTR_COLUMNS_BITS) - 1)
 
 #define VTE_CELL_ATTR_COMMON_BYTES      12  /* The number of common bytes in VteCellAttr and 
VteStreamCellAttr */
 
@@ -45,22 +44,30 @@ G_BEGIN_DECLS
  * Also don't forget to update basic_cell below!
  */
 
+#define CELL_ATTR_BOOL(lname,uname) \
+        inline constexpr void set_##lname(bool value) \
+        { \
+                vte_attr_set_bool(&attr, VTE_ATTR_##uname##_MASK, value); \
+        } \
+        \
+        inline constexpr bool lname() const \
+        { \
+                return vte_attr_get_bool(attr, VTE_ATTR_##uname##_SHIFT); \
+        }
+
+#define CELL_ATTR_UINT(lname,uname) \
+        inline constexpr void set_##lname(unsigned int value) \
+        { \
+                vte_attr_set_value(&attr, VTE_ATTR_##uname##_MASK, VTE_ATTR_##uname##_SHIFT, value); \
+        } \
+        \
+        inline constexpr uint32_t lname() const \
+        { \
+                return vte_attr_get_value(attr, VTE_ATTR_##uname##_VALUE_MASK, VTE_ATTR_##uname##_SHIFT); \
+        }
+
 typedef struct _VTE_GNUC_PACKED VteCellAttr {
-        uint32_t fragment : 1;                 /* A continuation cell. */
-        uint32_t columns : VTE_TAB_WIDTH_BITS; /* Number of visible columns
-                                                * (as determined by g_unicode_iswide(c)).
-                                                * Also abused for tabs; bug 353610
-                                                */
-        uint32_t bold : 1;
-        uint32_t italic : 1;
-        uint32_t underline : 2;                /* 0: none, 1: single, 2: double, 3: curly */
-        uint32_t strikethrough : 1;
-        uint32_t reverse : 1;
-        uint32_t blink : 1;
-        uint32_t dim : 1;                      /* also known as faint, half intensity etc. */
-        uint32_t invisible : 1;
-        uint32_t overline : 1;
-        uint32_t padding_unused : 17;
+        uint32_t attr;
 
        /* 4-byte boundary (8-byte boundary in VteCell) */
         uint64_t m_colors;                     /* fore, back and deco (underline) colour */
@@ -74,6 +81,8 @@ typedef struct _VTE_GNUC_PACKED VteCellAttr {
                                    (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. */
 
+        /* Methods */
+
         inline constexpr uint64_t colors() const { return m_colors; }
 
         inline constexpr void copy_colors(VteCellAttr const& other)
@@ -96,6 +105,39 @@ typedef struct _VTE_GNUC_PACKED VteCellAttr {
         CELL_ATTR_COLOR(back)
         CELL_ATTR_COLOR(deco)
 #undef CELL_ATTR_COLOR
+
+        inline constexpr bool has_any(uint32_t mask) const
+        {
+                return !!(attr & mask);
+        }
+
+        inline constexpr bool has_all(uint32_t mask) const
+        {
+                return (attr & mask) == mask;
+        }
+
+        inline constexpr bool has_none(uint32_t mask) const
+        {
+                return !(attr & mask);
+        }
+
+        inline constexpr void unset(uint32_t mask)
+        {
+                attr &= ~mask;
+        }
+
+        CELL_ATTR_UINT(columns, COLUMNS)
+        CELL_ATTR_BOOL(fragment, FRAGMENT)
+        CELL_ATTR_BOOL(bold, BOLD)
+        CELL_ATTR_BOOL(italic, ITALIC)
+        CELL_ATTR_UINT(underline, UNDERLINE)
+        CELL_ATTR_BOOL(strikethrough, STRIKETHROUGH)
+        CELL_ATTR_BOOL(overline, OVERLINE)
+        CELL_ATTR_BOOL(reverse, REVERSE)
+        CELL_ATTR_BOOL(blink, BLINK)
+        CELL_ATTR_BOOL(dim, DIM)
+        CELL_ATTR_BOOL(invisible, INVISIBLE)
+        /* ATTR_BOOL(boxed, BOXED) */
 } VteCellAttr;
 G_STATIC_ASSERT (sizeof (VteCellAttr) == 16);
 G_STATIC_ASSERT (offsetof (VteCellAttr, hyperlink_idx) == VTE_CELL_ATTR_COMMON_BYTES);
@@ -108,19 +150,24 @@ G_STATIC_ASSERT (offsetof (VteCellAttr, hyperlink_idx) == VTE_CELL_ATTR_COMMON_B
  */
 
 typedef struct _VTE_GNUC_PACKED _VteStreamCellAttr {
-        uint32_t fragment : 1;
-        uint32_t columns : VTE_TAB_WIDTH_BITS;
-        uint32_t remaining_main_attributes_1 : 27; /* 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. */
+        uint32_t attr; /* Same as VteCellAttr. We only access columns
+                        * and fragment, however.
+                        */
         /* 4-byte boundary */
         uint64_t colors;
         /* 12-byte boundary */
         guint16 hyperlink_length;       /* make sure it fits VTE_HYPERLINK_TOTAL_LENGTH_MAX */
+
+        /* Methods */
+        CELL_ATTR_UINT(columns, COLUMNS)
+        CELL_ATTR_BOOL(fragment, FRAGMENT)
 } VteStreamCellAttr;
 G_STATIC_ASSERT (sizeof (VteStreamCellAttr) == 14);
 G_STATIC_ASSERT (offsetof (VteStreamCellAttr, hyperlink_length) == VTE_CELL_ATTR_COMMON_BYTES);
 
+#undef CELL_ATTR_BOOL
+#undef CELL_ATTR_UINT
+
 /*
  * VteCell: A single cell's data
  */
@@ -134,18 +181,7 @@ G_STATIC_ASSERT (sizeof (VteCell) == 20);
 static const VteCell basic_cell = {
        0,
        {
-                0, /* fragment */
-                1, /* columns */
-                0, /* bold */
-                0, /* italic */
-                0, /* underline */
-                0, /* strikethrough */
-                0, /* reverse */
-                0, /* blink */
-                0, /* dim */
-                0, /* invisible */
-                0, /* overline */
-                0, /* padding_unused */
+                VTE_ATTR_DEFAULT, /* attr */
                 VTE_COLOR_TRIPLE_INIT_DEFAULT, /* colors */
                 0, /* hyperlink_idx */
        }
diff --git a/src/vteseq.cc b/src/vteseq.cc
index 3f25b1b..3c65110 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -1724,7 +1724,7 @@ VteTerminalPrivate::move_cursor_tab()
                        glong i;
                        VteCell *cell = _vte_row_data_get_writable (rowdata, col);
                        VteCell tab = *cell;
-                       tab.attr.columns = newcol - col;
+                       tab.attr.set_columns(newcol - col);
                        tab.c = '\t';
                        /* Save tab char */
                        *cell = tab;
@@ -1732,8 +1732,8 @@ VteTerminalPrivate::move_cursor_tab()
                        for (i = col + 1; i < newcol; i++) {
                                cell = _vte_row_data_get_writable (rowdata, i);
                                cell->c = '\t';
-                               cell->attr.columns = 1;
-                               cell->attr.fragment = 1;
+                               cell->attr.set_columns(1);
+                               cell->attr.set_fragment(true);
                        }
                }
 
@@ -1879,7 +1879,7 @@ VteTerminalPrivate::seq_character_attributes(vte::parser::Params const& params)
                         switch (param0) {
                         case 4:
                                 if (subparams.number_at(1, param1) && param1 >= 0 && param1 <= 3)
-                                        m_defaults.attr.underline = param1;
+                                        m_defaults.attr.set_underline(param1);
                                 break;
                         case 38: {
                                 unsigned int index = 1;
@@ -1916,53 +1916,52 @@ VteTerminalPrivate::seq_character_attributes(vte::parser::Params const& params)
                         reset_default_attributes(false);
                        break;
                case 1:
-                        m_defaults.attr.bold = 1;
+                        m_defaults.attr.set_bold(true);
                        break;
                case 2:
-                        m_defaults.attr.dim = 1;
+                        m_defaults.attr.set_dim(true);
                        break;
                case 3:
-                        m_defaults.attr.italic = 1;
+                        m_defaults.attr.set_italic(true);
                        break;
                case 4:
-                        m_defaults.attr.underline = 1;
+                        m_defaults.attr.set_underline(1);
                        break;
                case 5:
-                        m_defaults.attr.blink = 1;
+                        m_defaults.attr.set_blink(true);
                        break;
                case 7:
-                        m_defaults.attr.reverse = 1;
+                        m_defaults.attr.set_reverse(true);
                        break;
                case 8:
-                        m_defaults.attr.invisible = 1;
+                        m_defaults.attr.set_invisible(true);
                        break;
                case 9:
-                        m_defaults.attr.strikethrough = 1;
+                        m_defaults.attr.set_strikethrough(true);
                        break;
                 case 21:
-                        m_defaults.attr.underline = 2;
+                        m_defaults.attr.set_underline(2);
                         break;
                case 22: /* ECMA 48. */
-                        m_defaults.attr.bold = 0;
-                        m_defaults.attr.dim = 0;
+                        m_defaults.attr.unset(VTE_ATTR_BOLD_MASK | VTE_ATTR_DIM_MASK);
                        break;
                case 23:
-                        m_defaults.attr.italic = 0;
+                        m_defaults.attr.set_italic(false);
                        break;
                case 24:
-                        m_defaults.attr.underline = 0;
+                        m_defaults.attr.set_underline(0);
                        break;
                case 25:
-                        m_defaults.attr.blink = 0;
+                        m_defaults.attr.set_blink(false);
                        break;
                case 27:
-                        m_defaults.attr.reverse = 0;
+                        m_defaults.attr.set_reverse(false);
                        break;
                case 28:
-                        m_defaults.attr.invisible = 0;
+                        m_defaults.attr.set_invisible(false);
                        break;
                case 29:
-                        m_defaults.attr.strikethrough = 0;
+                        m_defaults.attr.set_strikethrough(false);
                        break;
                case 30:
                case 31:
@@ -2033,10 +2032,10 @@ VteTerminalPrivate::seq_character_attributes(vte::parser::Params const& params)
                         m_defaults.attr.set_back(VTE_DEFAULT_BG);
                        break;
                 case 53:
-                        m_defaults.attr.overline = 1;
+                        m_defaults.attr.set_overline(true);
                         break;
                 case 55:
-                        m_defaults.attr.overline = 0;
+                        m_defaults.attr.set_overline(false);
                         break;
              /* case 58: was handled above at 38 to avoid code duplication */
                 case 59:
@@ -2682,7 +2681,7 @@ VteTerminalPrivate::seq_screen_alignment_test(vte::parser::Params const& params)
                 VteCell cell;
                cell.c = 'E';
                cell.attr = basic_cell.attr;
-               cell.attr.columns = 1;
+               cell.attr.set_columns(1);
                 _vte_row_data_fill(rowdata, &cell, m_column_count);
                 emit_text_inserted();
        }


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