[vte/wip/egmont/bidi: 3/21] etap1



commit e95fa5d30b975db74500794864bd428010d41a68
Author: Egmont Koblinger <egmont gmail com>
Date:   Thu May 23 23:55:21 2019 +0200

    etap1

 src/bidi.cc       | 261 +++++++++++++-----------------------------------------
 src/bidi.hh       |  54 ++++-------
 src/meson.build   |   2 +
 src/ringview.cc   | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ringview.hh   |  93 +++++++++++++++++++
 src/vtedefines.hh |   2 +-
 src/vterowdata.cc |   9 ++
 src/vterowdata.hh |   1 +
 8 files changed, 436 insertions(+), 236 deletions(-)
---
diff --git a/src/bidi.cc b/src/bidi.cc
index 8bdef69d..c7411a2f 100644
--- a/src/bidi.cc
+++ b/src/bidi.cc
@@ -16,6 +16,13 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+/*
+ * This class implements mapping the logical letters to their visual positions
+ * according to the modes tracked for each paragraph.
+ *
+ * BiDi is implemented according to https://terminal-wg.pages.freedesktop.org/bidi/
+ */
+
 #include <config.h>
 
 #ifdef WITH_FRIBIDI
@@ -145,122 +152,29 @@ bool BidiRow::has_foreign() const
 }
 
 
-RingView::RingView()
-{
-        m_ring = nullptr;
-
-        m_start = m_len = m_width = 0;
-        m_height_alloc = 32;
-
-        m_bidirows = (BidiRow **) g_malloc (sizeof (BidiRow *) * m_height_alloc);
-        for (int i = 0; i < m_height_alloc; i++) {
-                m_bidirows[i] = new BidiRow();
-        }
-
-        m_invalid = true;
-}
-
-RingView::~RingView()
-{
-        for (int i = 0; i < m_height_alloc; i++) {
-                delete m_bidirows[i];
-        }
-        g_free (m_bidirows);
-}
-
-void RingView::set_ring(Ring *ring)
-{
-        if (ring == m_ring)
-                return;
-
-        m_ring = ring;
-        m_invalid = true;
-}
-
-void RingView::set_width(vte::grid::column_t width)
-{
-        if (width == m_width)
-                return;
-
-        m_width = width;
-        m_invalid = true;
-}
-
-void RingView::set_rows(vte::grid::row_t start, vte::grid::row_t len)
+BidiRunner::BidiRunner(RingView *ringview)
 {
-        if (start == m_start && len == m_len)
-                return;
-
-        if (G_UNLIKELY (len > m_height_alloc)) {
-                int i = m_height_alloc;
-                while (len > m_height_alloc) {
-                        m_height_alloc *= 2;
-                }
-                m_bidirows = (BidiRow **) g_realloc (m_bidirows, sizeof (BidiRow *) * m_height_alloc);
-                for (; i < m_height_alloc; i++) {
-                        m_bidirows[i] = new BidiRow();
-                }
-        }
-
-        m_start = start;
-        m_len = len;
-        m_invalid = true;
+        m_ringview = ringview;
 }
 
-void RingView::maybe_update()
-{
-        if (!m_invalid)
-                return;
-
-        vte::grid::row_t i = m_start;
-        const VteRowData *row_data = m_ring->index_safe(m_start);
-
-        if (row_data && (row_data->attr.bidi_flags & VTE_BIDI_IMPLICIT)) {
-                i = find_paragraph(m_start);
-                if (i == -1) {
-                        i = explicit_paragraph(m_start, row_data->attr.bidi_flags & VTE_BIDI_RTL);
-                }
-        }
-        while (i < m_start + m_len) {
-                i = paragraph(i);
-        }
-
-        m_invalid = false;
-}
-
-BidiRow const* RingView::get_row_map(vte::grid::row_t row) const
-{
-        g_assert_cmpint (row, >=, m_start);
-        g_assert_cmpint (row, <, m_start + m_len);
-        g_assert_false (m_invalid);
-
-        return m_bidirows[row - m_start];
-}
-
-BidiRow* RingView::get_row_map_writable(vte::grid::row_t row) const
-{
-        g_assert_cmpint (row, >=, m_start);
-        g_assert_cmpint (row, <, m_start + m_len);
-
-        return m_bidirows[row - m_start];
-}
+BidiRunner::~BidiRunner() {}
 
 /* Set up the mapping according to explicit mode for a given line. */
-void RingView::explicit_line(vte::grid::row_t row, bool rtl)
+void BidiRunner::explicit_line(vte::grid::row_t row, bool rtl)
 {
         int i;
 
-        if (G_UNLIKELY (row < m_start || row >= m_start + m_len))
+        BidiRow *bidirow = m_ringview->get_row_map_writable(row);
+        if (bidirow == nullptr)
                 return;
-
-        BidiRow *bidirow = get_row_map_writable(row);
         bidirow->m_base_rtl = rtl;
         bidirow->m_has_foreign = false;
 
         if (G_UNLIKELY (rtl)) {
-                bidirow->set_width(m_width);
-                for (i = 0; i < m_width; i++) {
-                        bidirow->m_log2vis[i] = bidirow->m_vis2log[i] = m_width - 1 - i;
+                auto width = m_ringview->get_width();
+                bidirow->set_width(width);
+                for (i = 0; i < width; i++) {
+                        bidirow->m_log2vis[i] = bidirow->m_vis2log[i] = width - 1 - i;
                         bidirow->m_vis_rtl[i] = true;
                         bidirow->m_vis_shaped_char[i] = 0;
                 }
@@ -272,56 +186,26 @@ void RingView::explicit_line(vte::grid::row_t row, bool rtl)
 }
 
 /* Set up the mapping according to explicit mode, for all the lines
- * of a paragraph beginning at the given line.
- * Returns the row number after the paragraph or viewport (whichever ends first). */
-vte::grid::row_t RingView::explicit_paragraph(vte::grid::row_t row, bool rtl)
+ * of a paragraph between the given lines. */
+void BidiRunner::explicit_paragraph(vte::grid::row_t start, vte::grid::row_t end, bool rtl)
 {
-        const VteRowData *row_data;
-
-        while (row < m_start + m_len) {
-                explicit_line(row, rtl);
-
-                row_data = m_ring->index_safe(row++);
-                if (row_data == nullptr || !row_data->attr.soft_wrapped)
-                        break;
+        for (; start < end; start++) {
+                explicit_line(start, rtl);
         }
-        return row;
 }
 
-/* For the given row, find the first row of its paragraph.
- * Returns -1 if have to walk backwards too much. */
-/* FIXME this could be much cheaper, we don't need to read the actual rows (text_stream),
- * we only need the soft_wrapped flag which is stored in row_stream. Needs method in ring. */
-vte::grid::row_t RingView::find_paragraph(vte::grid::row_t row)
+/* Figure out the mapping for the paragraph between the given rows. */
+void BidiRunner::paragraph(vte::grid::row_t start, vte::grid::row_t end)
 {
-        vte::grid::row_t row_stop = row - VTE_BIDI_PARAGRAPH_LENGTH_MAX;
-        const VteRowData *row_data;
-
-        while (row-- > row_stop) {
-                if (row < _vte_ring_delta(m_ring))
-                        return row + 1;
-                row_data = m_ring->index_safe(row);
-                if (row_data == nullptr || !row_data->attr.soft_wrapped)
-                        return row + 1;
-        }
-        return -1;
-}
-
-/* Figure out the mapping for the paragraph starting at the given row.
- * Returns the row number after the paragraph or viewport (whichever ends first). */
-vte::grid::row_t RingView::paragraph(vte::grid::row_t row)
-{
-        const VteRowData *row_data = m_ring->index_safe(row);
-        if (row_data == nullptr) {
-                return explicit_paragraph(row, false);
-        }
-
 #ifndef WITH_FRIBIDI
-        return explicit_paragraph(row, row_data->attr.bidi_flags & VTE_BIDI_RTL);
+        const VteRowData *row_data = m_ringview->get_row(start);
+        explicit_paragraph(start, end, row_data->attr.bidi_flags & VTE_BIDI_RTL);
 #else
         const VteCell *cell;
+        const VteRowData *row_data;
         bool rtl;
         bool autodir;
+        vte::grid::row_t row;
         FriBidiParType pbase_dir;
         FriBidiLevel level;
         FriBidiChar *fribidi_chars;
@@ -331,21 +215,24 @@ vte::grid::row_t RingView::paragraph(vte::grid::row_t row)
         FriBidiLevel *fribidi_levels;
         FriBidiStrIndex *fribidi_map;
         FriBidiStrIndex *fribidi_to_term;
-
         BidiRow *bidirow;
 
-        if (!(row_data->attr.bidi_flags & VTE_BIDI_IMPLICIT)) {
-                return explicit_paragraph(row, row_data->attr.bidi_flags & VTE_BIDI_RTL);
+        auto width = m_ringview->get_width();
+
+        row_data = m_ringview->get_row(start);
+        if (!(row_data->attr.bidi_flags & VTE_BIDI_IMPLICIT) ||
+            end - start > VTE_RINGVIEW_PARAGRAPH_LENGTH_MAX) {
+                explicit_paragraph(start, end, row_data->attr.bidi_flags & VTE_BIDI_RTL);
+                return;
         }
 
         rtl = row_data->attr.bidi_flags & VTE_BIDI_RTL;
         autodir = row_data->attr.bidi_flags & VTE_BIDI_AUTO;
 
-        int lines[VTE_BIDI_PARAGRAPH_LENGTH_MAX + 1];  /* offsets to the beginning of lines */
+        int lines[VTE_RINGVIEW_PARAGRAPH_LENGTH_MAX + 1];  /* offsets to the beginning of lines */
         lines[0] = 0;
         int line = 0;   /* line number within the paragraph */
         int count;      /* total character count */
-        int row_orig = row;
         int tl, tv;     /* terminal logical and visual */
         int fl, fv;     /* fribidi logical and visual */
         unsigned int col;
@@ -435,23 +322,13 @@ vte::grid::row_t RingView::paragraph(vte::grid::row_t row)
          * in the map to be shuffled. However, we can't get the embedding levels then.
          * TODO: File an issue for a better API.
          */
-        while (row < _vte_ring_next(m_ring)) {
-                row_data = m_ring->index_safe(row);
-                if (row_data == nullptr)
-                        break;
-
-                if (line == VTE_BIDI_PARAGRAPH_LENGTH_MAX) {
-                        /* Overlong paragraph, bail out. */
-                        g_array_free (fribidi_chars_array, TRUE);
-                        g_array_free (fribidi_map_array, TRUE);
-                        g_array_free (fribidi_to_term_array, TRUE);
-                        return explicit_paragraph (row_orig, rtl);
-                }
+        for (row = start; row < end; row++) {
+                row_data = m_ringview->get_row(row);
 
                 /* A row_data might be longer, in case rewrapping is disabled and the window was narrowed.
                  * Truncate the logical data before applying BiDi. */
                 // FIXME what the heck to do if this truncation cuts a TAB or CJK in half???
-                for (tl = 0; tl < m_width && tl < row_data->len; tl++) {
+                for (tl = 0; tl < width && tl < row_data->len; tl++) {
                         auto prev_len = fribidi_chars_array->len;
                         FriBidiStrIndex val;
 
@@ -481,19 +358,18 @@ vte::grid::row_t RingView::paragraph(vte::grid::row_t row)
                 }
 
                 lines[++line] = fribidi_chars_array->len;
-                row++;
-
-                if (!row_data->attr.soft_wrapped)
-                        break;
         }
 
+#if 0
         if (line == 0) {
                 /* Beyond the end of the ring. */
                 g_array_free (fribidi_chars_array, TRUE);
                 g_array_free (fribidi_map_array, TRUE);
                 g_array_free (fribidi_to_term_array, TRUE);
-                return explicit_paragraph (row_orig, rtl);
+                explicit_paragraph (start, end, rtl);
+                return;
         }
+#endif
 
         /* Convenience stuff, we no longer need the auto-growing GArray wrapper. */
         count = fribidi_chars_array->len;
@@ -520,7 +396,8 @@ vte::grid::row_t RingView::paragraph(vte::grid::row_t row)
                 g_array_free (fribidi_chars_array, TRUE);
                 g_array_free (fribidi_map_array, TRUE);
                 g_array_free (fribidi_to_term_array, TRUE);
-                return explicit_paragraph (row_orig, rtl);
+                explicit_paragraph (start, end, rtl);
+                return;
         }
 
         /* Arabic shaping
@@ -552,26 +429,21 @@ vte::grid::row_t RingView::paragraph(vte::grid::row_t row)
                 g_array_free (fribidi_chars_array, TRUE);
                 g_array_free (fribidi_map_array, TRUE);
                 g_array_free (fribidi_to_term_array, TRUE);
-                return explicit_paragraph (row_orig, false);
+                explicit_paragraph (start, end, false);
+                return;
         }
 
         /* Reshuffle line by line. */
-        row = row_orig;
-        line = 0;
-        if (G_UNLIKELY (row < m_start)) {
-                line = m_start - row;
-                row = m_start;
-        }
+        for (row = start, line = 0; row < end; row++, line++) {
+                bidirow = m_ringview->get_row_map_writable(row);
+                if (bidirow == nullptr)
+                        continue;
 
-        while (row < _vte_ring_next(m_ring) && row < m_start + m_len) {
-                bidirow = get_row_map_writable(row);
                 bidirow->m_base_rtl = rtl;
                 bidirow->m_has_foreign = true;
-                bidirow->set_width(m_width);
+                bidirow->set_width(width);
 
-                row_data = m_ring->index_safe(row);
-                if (row_data == nullptr)
-                        break;
+                row_data = m_ringview->get_row(row);
 
                 level = fribidi_reorder_line (FRIBIDI_FLAGS_DEFAULT,
                                               fribidi_chartypes,
@@ -586,23 +458,23 @@ vte::grid::row_t RingView::paragraph(vte::grid::row_t row)
                         /* error, what should we do? */
                         explicit_line (row, rtl);
                         bidirow->m_has_foreign = true;
-                        goto next_line;
+                        continue;
                 }
 
                 if (!rtl && level == 1) {
                         /* Fast shortcut for LTR-only lines. */
                         explicit_line (row, false);
                         bidirow->m_has_foreign = true;
-                        goto next_line;
+                        continue;
                 }
 
                 /* Copy to our realm. Proceed in visual order.*/
                 tv = 0;
                 if (rtl) {
                         /* Unused cells on the left for RTL paragraphs */
-                        int unused = MAX(m_width - row_data->len, 0);
+                        int unused = MAX(width - row_data->len, 0);
                         for (; tv < unused; tv++) {
-                                bidirow->m_vis2log[tv] = m_width - 1 - tv;
+                                bidirow->m_vis2log[tv] = width - 1 - tv;
                                 bidirow->m_vis_rtl[tv] = true;
                                 bidirow->m_vis_shaped_char[tv] = 0;
                         }
@@ -638,46 +510,37 @@ vte::grid::row_t RingView::paragraph(vte::grid::row_t row)
                 }
                 if (!rtl) {
                         /* Unused cells on the right for LTR paragraphs */
-                        g_assert_cmpint (tv, ==, MIN (row_data->len, m_width));
-                        for (; tv < m_width; tv++) {
+                        g_assert_cmpint (tv, ==, MIN (row_data->len, width));
+                        for (; tv < width; tv++) {
                                 bidirow->m_vis2log[tv] = tv;
                                 bidirow->m_vis_rtl[tv] = false;
                                 bidirow->m_vis_shaped_char[tv] = 0;
                         }
                 }
-                g_assert_cmpint (tv, ==, m_width);
+                g_assert_cmpint (tv, ==, width);
 
                 /* From vis2log create the log2vis mapping too.
                  * In debug mode assert that we have a bijective mapping. */
                 if (_vte_debug_on (VTE_DEBUG_BIDI)) {
-                        for (tl = 0; tl < m_width; tl++) {
+                        for (tl = 0; tl < width; tl++) {
                                 bidirow->m_log2vis[tl] = -1;
                         }
                 }
 
-                for (tv = 0; tv < m_width; tv++) {
+                for (tv = 0; tv < width; tv++) {
                         bidirow->m_log2vis[bidirow->m_vis2log[tv]] = tv;
                 }
 
                 if (_vte_debug_on (VTE_DEBUG_BIDI)) {
-                        for (tl = 0; tl < m_width; tl++) {
+                        for (tl = 0; tl < width; tl++) {
                                 g_assert_cmpint (bidirow->m_log2vis[tl], !=, -1);
                         }
                 }
-
-next_line:
-                line++;
-                row++;
-
-                if (!row_data->attr.soft_wrapped)
-                        break;
         }
 
         g_array_free (fribidi_chars_array, TRUE);
         g_array_free (fribidi_map_array, TRUE);
         g_array_free (fribidi_to_term_array, TRUE);
-
-        return row;
 #endif /* !WITH_FRIBIDI */
 }
 
diff --git a/src/bidi.hh b/src/bidi.hh
index 38e8035e..11f130f1 100644
--- a/src/bidi.hh
+++ b/src/bidi.hh
@@ -21,6 +21,7 @@
 #include <glib.h>
 
 #include "ring.hh"
+#include "ringview.hh"
 #include "vterowdata.hh"
 #include "vtetypes.hh"
 #include "vteunistr.h"
@@ -29,9 +30,12 @@ namespace vte {
 
 namespace base {  // FIXME ???
 
+class RingView;
+
 /* BidiRow contains the BiDi transformation of a single row. */
 class BidiRow {
-        friend class RingView;
+        friend class RingView;  // is this needed?
+        friend class BidiRunner;
 
 public:
         BidiRow();
@@ -69,48 +73,26 @@ private:
 };
 
 
-/* RingView contains the BiDi transformations for all the rows of the viewport. */
-class RingView {
+/* BidiRunner is not a real class, rather the collection of methods that run the BiDi algorithm. */
+class BidiRunner {
 public:
-        RingView();
-        ~RingView();
+        BidiRunner(RingView *ringview);
+        ~BidiRunner();
 
         // prevent accidents
-        RingView(RingView& o) = delete;
-        RingView(RingView const& o) = delete;
-        RingView(RingView&& o) = delete;
-        RingView& operator= (RingView& o) = delete;
-        RingView& operator= (RingView const& o) = delete;
-        RingView& operator= (RingView&& o) = delete;
-
-        void set_ring(Ring *ring);
-        void set_rows(vte::grid::row_t start, vte::grid::row_t len);
-        void set_width(vte::grid::column_t width);
-
-        inline void invalidate() { m_invalid = true; }
-        void maybe_update();
-
-        BidiRow const* get_row_map(vte::grid::row_t row) const;
+        BidiRunner(BidiRunner& o) = delete;
+        BidiRunner(BidiRunner const& o) = delete;
+        BidiRunner(BidiRunner&& o) = delete;
+        BidiRunner& operator= (BidiRunner& o) = delete;
+        BidiRunner& operator= (BidiRunner const& o) = delete;
+        BidiRunner& operator= (BidiRunner&& o) = delete;
 
 private:
-        Ring *m_ring;
-
-        BidiRow **m_bidirows;
-
-        vte::grid::row_t m_start;
-        vte::grid::row_t m_len;
-        vte::grid::column_t m_width;
-
-        vte::grid::row_t m_height_alloc;
-
-        bool m_invalid;
-
-        BidiRow* get_row_map_writable(vte::grid::row_t row) const;
+        RingView *m_ringview;
 
         void explicit_line(vte::grid::row_t row, bool rtl);
-        vte::grid::row_t explicit_paragraph(vte::grid::row_t row, bool rtl);
-        vte::grid::row_t find_paragraph(vte::grid::row_t row);
-        vte::grid::row_t paragraph(vte::grid::row_t row);
+        void explicit_paragraph(vte::grid::row_t start, vte::grid::row_t end, bool rtl);
+        void paragraph(vte::grid::row_t start, vte::grid::row_t end);
 };
 
 }; /* namespace base */
diff --git a/src/meson.build b/src/meson.build
index fef7da1e..1481c089 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -72,6 +72,8 @@ libvte_common_sources = debug_sources + modes_sources + parser_sources + utf8_so
   'refptr.hh',
   'ring.cc',
   'ring.hh',
+  'ringview.cc',
+  'ringview.hh',
   'utf8.cc',
   'utf8.hh',
   'vte.cc',
diff --git a/src/ringview.cc b/src/ringview.cc
new file mode 100644
index 00000000..76df16c6
--- /dev/null
+++ b/src/ringview.cc
@@ -0,0 +1,250 @@
+/*
+ * Copyright © 2018–2019 Egmont Koblinger
+ *
+ * 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 2.1 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 Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/*
+ * RingView provides a "view" to a continuous segment of the Ring (or stream),
+ * typically the user visible area.
+ *
+ * It computes additional data that are necessary to display the contents
+ * properly, but are not needed for the terminal emulation logic. In order to
+ * save tons of resources, these data are computed when the Ring's contents are
+ * about to be displayed, rather than whenever they change.
+ *
+ * In addition to the area covered by the Ringview, context lines are also
+ * taken into account up to the next hard newline or a safety limit.
+ *
+ * Currently RingView is used for BiDi: to figure out which character is mapped
+ * to which visual position.
+ *
+ * Future possible uses include "highlight all" for the search match, and
+ * syntax highlighting. URL autodetection might also be ported to this
+ * infrastructure one day.
+ */
+
+#include <config.h>
+
+#ifdef WITH_FRIBIDI
+#include <fribidi.h>
+#endif
+
+#include "bidi.hh"
+#include "debug.h"
+#include "vtedefines.hh"
+#include "vteinternal.hh"
+
+#ifdef WITH_FRIBIDI
+static_assert (sizeof (gunichar) == sizeof (FriBidiChar), "Whoooo");
+#endif
+
+using namespace vte::base;
+
+RingView::RingView()
+{
+        m_ring = nullptr;
+        m_start = m_len = m_width = 0;
+        m_rows_alloc_len = m_bidirows_alloc_len = 0;
+
+        m_invalid = true;
+        m_paused = true;
+}
+
+RingView::~RingView()
+{
+        pause();
+}
+
+/* Pausing a RingView frees up pretty much all of its memory. This is to be
+ * used when the terminal is unlikely to be painted in the near future, e.g.
+ * the widget is unmapped. Not to be called too frequently, in order to avoid
+ * memory fragmentation. The RingView is unpaused automatically on demand.
+ */
+void RingView::pause()
+{
+        if (m_paused)
+                return;
+
+        for (int i = 0; i < m_bidirows_alloc_len; i++) {
+                delete m_bidirows[i];
+        }
+        g_free (m_bidirows);
+        m_bidirows_alloc_len = 0;
+
+        for (int i = 0; i < m_rows_alloc_len; i++) {
+                _vte_row_data_fini(m_rows[i]);
+        }
+        g_free (m_rows);
+        m_rows_alloc_len = 0;
+
+        m_invalid = true;
+        m_paused = true;
+}
+
+void RingView::unpause()
+{
+        g_assert_cmpint (m_len, >=, 1);
+
+        m_bidirows_alloc_len = m_len;
+        m_bidirows = (BidiRow **) g_malloc (sizeof (BidiRow *) * m_bidirows_alloc_len);
+        for (int i = 0; i < m_bidirows_alloc_len; i++) {
+                m_bidirows[i] = new BidiRow();
+        }
+
+        m_rows_alloc_len = m_len + 10;  /* a bit of random heuristics */
+        m_rows = (VteRowData **) g_malloc (sizeof (VteRowData *) * m_rows_alloc_len);
+        for (int i = 0; i < m_rows_alloc_len; i++) {
+                m_rows[i] = (VteRowData *) malloc (sizeof (VteRowData));
+                _vte_row_data_init (m_rows[i]);
+        }
+
+        m_invalid = true;
+        m_paused = false;
+}
+
+void RingView::set_ring(Ring *ring)
+{
+        if (ring == m_ring)
+                return;
+
+        m_ring = ring;
+        m_invalid = true;
+}
+
+void RingView::set_width(vte::grid::column_t width)
+{
+        if (width == m_width)
+                return;
+
+        m_width = width;
+        m_invalid = true;
+}
+
+void RingView::set_rows(vte::grid::row_t start, vte::grid::row_t len)
+{
+        g_assert_cmpint (len, >=, 1);
+
+        if (start == m_start && len == m_len)
+                return;
+
+        if (G_UNLIKELY (!m_paused && len > m_bidirows_alloc_len)) {
+                int i = m_bidirows_alloc_len;
+                while (len > m_bidirows_alloc_len) {
+                        m_bidirows_alloc_len *= 2;
+                }
+                m_bidirows = (BidiRow **) g_realloc (m_bidirows, sizeof (BidiRow *) * m_bidirows_alloc_len);
+                for (; i < m_bidirows_alloc_len; i++) {
+                        m_bidirows[i] = new BidiRow();
+                }
+        }
+
+        m_start = start;
+        m_len = len;
+        m_invalid = true;
+}
+
+VteRowData *RingView::get_row(vte::grid::row_t row) {
+        // FIXME safety boundary checks!
+
+        return m_rows[row - m_top];
+}
+
+void RingView::maybe_update()
+{
+        if (!m_invalid)
+                return;
+        if (m_paused)
+                unpause();
+
+        /* Find the beginning of the topmost paragraph.
+         * Bail out if it's preceded by VTE_BIDI_PARAGRAPH_LENGTH_MAX (or more)
+         * soft newlines. */
+
+        vte::grid::row_t row = m_start;
+        const VteRowData *row_data;
+
+        int i = VTE_RINGVIEW_PARAGRAPH_LENGTH_MAX;
+        while (i--) {
+                // FIXME this could be much cheaper, we don't need to read the actual rows (text_stream),
+                // we only need the soft_wrapped flag which is stored in row_stream. Needs method in ring. */
+                row_data = m_ring->index_safe(row - 1);
+                if (row_data == nullptr || !row_data->attr.soft_wrapped)
+                        break;
+                row--;
+        }
+        if (i == 0) {
+                /* Paragraph has more than VTE_BIDI_PARAGRAPH_LENGTH_MAX lines; take a note of it. */
+//                cut_at_top = true;
+                row++;
+        }
+
+        /* Extract the data beginning at the found row. */
+        m_rows_len = 0;
+        m_top = row;
+        while (row < m_start + m_len) {
+                if (m_rows_len == m_rows_alloc_len) {
+                        m_rows_alloc_len *= 2;
+                        m_rows = (VteRowData **) g_realloc (m_rows, sizeof (VteRowData *) * 
m_rows_alloc_len);
+                        for (int j = m_rows_len; j < m_rows_alloc_len; j++) {
+                                m_rows[j] = (VteRowData *) malloc (sizeof (VteRowData));
+                                _vte_row_data_init (m_rows[j]);
+                        }
+                }
+
+                row_data = m_ring->index_safe(row);
+                if (row_data != nullptr) {
+                        _vte_row_data_copy (row_data, m_rows[m_rows_len]);
+               } else {
+                       // FIXME
+                       _vte_row_data_shrink (m_rows[m_rows_len], 0);
+               }
+                m_rows_len++;
+                row++;
+        }
+
+
+        // FIXME extract further rows
+        
+        m_invalid = false;
+}
+
+
+
+
+
+
+
+
+
+
+
+BidiRow const* RingView::get_row_map(vte::grid::row_t row) const
+{
+        g_assert_cmpint (row, >=, m_start);
+        g_assert_cmpint (row, <, m_start + m_len);
+        g_assert_false (m_invalid);
+        g_assert_false (m_paused);
+
+        return m_bidirows[row - m_start];
+}
+
+BidiRow* RingView::get_row_map_writable(vte::grid::row_t row) const
+{
+        if (row < m_start || row >= m_start + m_len)
+                return nullptr;
+
+        return m_bidirows[row - m_start];
+}
diff --git a/src/ringview.hh b/src/ringview.hh
new file mode 100644
index 00000000..918a983c
--- /dev/null
+++ b/src/ringview.hh
@@ -0,0 +1,93 @@
+/*
+ * Copyright © 2018–2019 Egmont Koblinger
+ *
+ * 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 2.1 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 Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#pragma once
+
+#include <glib.h>
+
+#include "bidi.hh"
+#include "ring.hh"
+#include "vterowdata.hh"
+#include "vtetypes.hh"
+#include "vteunistr.h"
+
+namespace vte {
+
+namespace base {  // FIXME ???
+
+class BidiRow;
+
+/* RingView contains the BiDi transformations for all the rows of the viewport. */
+class RingView {
+public:
+        RingView();
+        ~RingView();
+
+        // prevent accidents
+        RingView(RingView& o) = delete;
+        RingView(RingView const& o) = delete;
+        RingView(RingView&& o) = delete;
+        RingView& operator= (RingView& o) = delete;
+        RingView& operator= (RingView const& o) = delete;
+        RingView& operator= (RingView&& o) = delete;
+
+        void set_ring(Ring *ring);
+        void set_rows(vte::grid::row_t start, vte::grid::row_t len);
+        void set_width(vte::grid::column_t width);
+        vte::grid::column_t get_width() { return m_width; }
+
+        inline void invalidate() { m_invalid = true; }
+        void maybe_update();
+        void pause();
+
+        VteRowData *get_row(vte::grid::row_t row);
+
+        BidiRow const* get_row_map(vte::grid::row_t row) const;
+        BidiRow* get_row_map_writable(vte::grid::row_t row) const;
+
+private:
+        Ring *m_ring;
+
+        VteRowData **m_rows;  // FIXME remove one pointer indirection, or use GArray
+        int m_rows_len;
+        int m_rows_alloc_len;
+
+        BidiRow **m_bidirows;  // FIXME remove one pointer indirection, or use GArray
+        int m_bidirows_alloc_len;
+
+        vte::grid::row_t m_top;  /* the row of the Ring corresponding to m_rows[0] */
+
+        vte::grid::row_t m_start;
+        vte::grid::row_t m_len;
+        vte::grid::column_t m_width;
+
+        bool m_invalid;
+        bool m_paused;
+
+        void unpause();
+};
+
+}; /* namespace base */
+
+}; /* namespace vte */
+
+G_BEGIN_DECLS
+
+gboolean vte_bidi_get_mirror_char (vteunistr unistr, gboolean mirror_box_drawing, vteunistr 
*unistr_mirrored);
+
+G_END_DECLS
diff --git a/src/vtedefines.hh b/src/vtedefines.hh
index 3502fec1..66247b06 100644
--- a/src/vtedefines.hh
+++ b/src/vtedefines.hh
@@ -138,4 +138,4 @@
 #define VTE_WINDOW_TITLE_STACK_MAX_DEPTH (8)
 
 /* Maximum length of a paragraph, in lines, that might get proper BiDi treatment. */
-#define VTE_BIDI_PARAGRAPH_LENGTH_MAX   500
+#define VTE_RINGVIEW_PARAGRAPH_LENGTH_MAX   500
diff --git a/src/vterowdata.cc b/src/vterowdata.cc
index 107a1f1b..c86fa69c 100644
--- a/src/vterowdata.cc
+++ b/src/vterowdata.cc
@@ -175,6 +175,15 @@ void _vte_row_data_shrink (VteRowData *row, gulong max_len)
                row->len = max_len;
 }
 
+// FIXME swap the two params a la memcpy?
+void _vte_row_data_copy (const VteRowData *src, VteRowData *dst)
+{
+        _vte_row_data_ensure (dst, src->len);
+        dst->len = src->len;
+        dst->attr = src->attr;
+        memcpy(dst->cells, src->cells, src->len * sizeof (src->cells[0]));
+}
+
 /* Get the length, ignoring trailing empty cells (with a custom background color). */
 guint16 _vte_row_data_nonempty_length (const VteRowData *row)
 {
diff --git a/src/vterowdata.hh b/src/vterowdata.hh
index 12cbf311..10b76b6a 100644
--- a/src/vterowdata.hh
+++ b/src/vterowdata.hh
@@ -80,6 +80,7 @@ void _vte_row_data_append (VteRowData *row, const VteCell *cell);
 void _vte_row_data_remove (VteRowData *row, gulong col);
 void _vte_row_data_fill (VteRowData *row, const VteCell *cell, gulong len);
 void _vte_row_data_shrink (VteRowData *row, gulong max_len);
+void _vte_row_data_copy (const VteRowData *src, VteRowData *dst);
 guint16 _vte_row_data_nonempty_length (const VteRowData *row);
 
 G_END_DECLS


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