[vte/wip/egmont/bidi: 3/21] etap1
- From: Egmont Koblinger <egmontkob src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte/wip/egmont/bidi: 3/21] etap1
- Date: Fri, 31 May 2019 21:38:46 +0000 (UTC)
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]