[vte/wip/egmont/bidi: 8/16] ringview: Add RingView infrastructure



commit 684567dc4986e89ec0127fafc53541b6c9246515
Author: Egmont Koblinger <egmont gmail com>
Date:   Sat Jun 1 15:12:32 2019 +0200

    ringview: Add RingView infrastructure
    
    The RingView extracts the contents of the currently visible area plus
    context lines up to the next hard newline (or a safety limit).
    
    This can be used to perform display related tweaks, such as BiDi.

 src/debug.cc       |   1 +
 src/debug.h        |   3 +-
 src/meson.build    |   2 +
 src/ringview.cc    | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ringview.hh    |  97 ++++++++++++++++++++++
 src/vte.cc         |  87 +++++++++++++++++++-
 src/vteinternal.hh |   7 ++
 src/vterowdata.cc  |   8 ++
 src/vterowdata.hh  |   1 +
 src/widget.cc      |   2 +
 10 files changed, 443 insertions(+), 2 deletions(-)
---
diff --git a/src/debug.cc b/src/debug.cc
index afbd7413..e8dd8d08 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -57,6 +57,7 @@ _vte_debug_init(void)
     { "hyperlink",    VTE_DEBUG_HYPERLINK    },
     { "modes",        VTE_DEBUG_MODES        },
     { "emulation",    VTE_DEBUG_EMULATION    },
+    { "ringview",     VTE_DEBUG_RINGVIEW     },
     { "bidi",         VTE_DEBUG_BIDI         },
   };
 
diff --git a/src/debug.h b/src/debug.h
index 92d9d0f9..8601dad0 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -64,7 +64,8 @@ typedef enum {
         VTE_DEBUG_HYPERLINK     = 1 << 24,
         VTE_DEBUG_MODES         = 1 << 25,
         VTE_DEBUG_EMULATION     = 1 << 26,
-        VTE_DEBUG_BIDI          = 1 << 27,
+        VTE_DEBUG_RINGVIEW      = 1 << 27,
+        VTE_DEBUG_BIDI          = 1 << 28,
 } VteDebugFlags;
 
 void _vte_debug_init(void);
diff --git a/src/meson.build b/src/meson.build
index 29046b77..6b97aaec 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -70,6 +70,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..9e25bcbc
--- /dev/null
+++ b/src/ringview.cc
@@ -0,0 +1,237 @@
+/*
+ * 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
+ */
+
+#include <config.h>
+
+#include "debug.h"
+#include "vtedefines.hh"
+#include "vteinternal.hh"
+
+using namespace vte::base;
+
+RingView::RingView()
+{
+}
+
+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 or interacted with
+ * 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 resumed automatically on demand.
+ */
+void
+RingView::pause()
+{
+        int i;
+
+        if (m_paused)
+                return;
+
+        _vte_debug_print (VTE_DEBUG_RINGVIEW, "Ringview: pause, freeing %d rows.\n",
+                                              m_rows_alloc_len);
+
+        for (i = 0; i < m_rows_alloc_len; i++) {
+                _vte_row_data_fini(m_rows[i]);
+                g_free (m_rows[i]);
+        }
+        g_free (m_rows);
+        m_rows_alloc_len = 0;
+
+        m_invalid = true;
+        m_paused = true;
+}
+
+/* Allocate (again) the required memory. */
+void
+RingView::resume()
+{
+        g_assert_cmpint (m_len, >=, 1);
+
+        /* +16: A bit of arbitrary heuristics to likely prevent a quickly following
+         * realloc for the required context lines. */
+        m_rows_alloc_len = m_len + 16;
+        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 *) g_malloc (sizeof (VteRowData));
+                _vte_row_data_init (m_rows[i]);
+        }
+
+        _vte_debug_print (VTE_DEBUG_RINGVIEW, "Ringview: resume, allocating %d rows\n",
+                                              m_rows_alloc_len);
+
+        m_paused = false;
+}
+
+void
+RingView::set_ring(Ring *ring)
+{
+        if (G_LIKELY (ring == m_ring))
+                return;
+
+        m_ring = ring;
+        m_invalid = true;
+}
+
+void
+RingView::set_width(vte::grid::column_t width)
+{
+        if (G_LIKELY (width == m_width))
+                return;
+
+        m_width = width;
+        m_invalid = true;
+}
+
+void
+RingView::set_rows(vte::grid::row_t start, vte::grid::row_t len)
+{
+        /* Force at least 1 row, see bug 134. */
+        len = MAX(len, 1);
+
+        if (start == m_start && len == m_len)
+                return;
+
+        /* With per-pixel scrolling, the desired viewport often shrinks by
+         * one row at one end, and remains the same at the other end.
+         * Save work by just keeping the current valid data in this case. */
+        if (!m_invalid && start >= m_start && start + len <= m_start + m_len)
+                return;
+
+        /* m_rows is expanded on demand in update() */
+
+        m_start = start;
+        m_len = len;
+        m_invalid = true;
+}
+
+VteRowData const*
+RingView::get_row(vte::grid::row_t row) const
+{
+        g_assert_cmpint(row, >=, m_top);
+        g_assert_cmpint(row, <, m_top + m_rows_len);
+
+        return m_rows[row - m_top];
+}
+
+void
+RingView::update()
+{
+        if (!m_invalid)
+                return;
+        if (m_paused)
+                resume();
+
+        /* Find the beginning of the topmost paragraph.
+         *
+         * Extract at most VTE_RINGVIEW_PARAGRAPH_LENGTH_MAX context rows.
+         * If this safety limit is reached then together with the first
+         * non-context row this paragraph fragment is already longer
+         * than VTE_RINGVIEW_PARAGRAPH_LENGTH_MAX lines, and thus the
+         * BiDi code will skip it. */
+        vte::grid::row_t row = m_start;
+        const VteRowData *row_data;
+
+        _vte_debug_print (VTE_DEBUG_RINGVIEW, "Ringview: updating for [%ld..%ld] (%ld rows).\n",
+                                              m_start, m_start + m_len - 1, m_len);
+
+        int i = VTE_RINGVIEW_PARAGRAPH_LENGTH_MAX;
+        while (i--) {
+                if (!m_ring->is_soft_wrapped(row - 1))
+                        break;
+                row--;
+        }
+
+        /* Extract the data beginning at the found row.
+         *
+         * Extract at most VTE_RINGVIEW_PARAGRAPH_LENGTH_MAX rows
+         * beyond the end of the specified area. Again, if this safety
+         * limit is reached then together with the last non-context row
+         * this paragraph fragment is already longer than
+         * VTE_RINGVIEW_PARAGRAPH_LENGTH_MAX lines, and thus the
+         * BiDi code will skip it. */
+        m_top = row;
+        m_rows_len = 0;
+        while (row < m_start + m_len + VTE_RINGVIEW_PARAGRAPH_LENGTH_MAX) {
+                if (G_UNLIKELY (m_rows_len == m_rows_alloc_len)) {
+                        /* Don't realloc too aggressively. */
+                        m_rows_alloc_len = std::max(m_rows_alloc_len + 1, m_rows_alloc_len * 5 / 4 /* 
whatever */);
+                        _vte_debug_print (VTE_DEBUG_RINGVIEW, "Ringview: reallocate to %d rows\n",
+                                                              m_rows_alloc_len);
+                        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 *) g_malloc (sizeof (VteRowData));
+                                _vte_row_data_init (m_rows[j]);
+                        }
+                }
+
+                row_data = _vte_ring_contains(m_ring, row) ? m_ring->index(row) : nullptr;
+                if (G_LIKELY (row_data != nullptr)) {
+                        _vte_row_data_copy (row_data, m_rows[m_rows_len]);
+                        /* Make sure that the extracted data is not wider than the screen,
+                         * something that can happen if the window was narrowed with rewrapping disabled.
+                         * Also make sure that we won't end up with unfinished characters.
+                         * FIXME remove this once bug 135 is addressed. */
+                        if (G_UNLIKELY (_vte_row_data_length(m_rows[m_rows_len]) > m_width)) {
+                                int j = m_width;
+                                while (j > 0) {
+                                        VteCell const* cell = _vte_row_data_get(m_rows[m_rows_len], j);
+                                        if (!cell->attr.fragment())
+                                                break;
+                                        j--;
+                                }
+                                _vte_row_data_shrink(m_rows[m_rows_len], j);
+                        }
+                } else {
+                        _vte_row_data_clear (m_rows[m_rows_len]);
+                }
+                m_rows_len++;
+                row++;
+
+                /* Once the bottom of the specified area is reached, stop at a hard newline. */
+                if (row >= m_start + m_len && (!row_data || !row_data->attr.soft_wrapped))
+                        break;
+        }
+
+        _vte_debug_print (VTE_DEBUG_RINGVIEW, "Ringview: extracted %ld+%ld context lines: [%ld..%ld] (%d 
rows).\n",
+                                              m_start - m_top, (m_top + m_rows_len) - (m_start + m_len),
+                                              m_top, m_top + m_rows_len - 1, m_rows_len);
+
+        /* Loop through paragraphs of the extracted text, and do whatever we need to do on each paragraph. */
+        auto top = m_top;
+        row = top;
+        while (row < m_top + m_rows_len) {
+                row_data = m_rows[row - m_top];
+                if (!row_data->attr.soft_wrapped || row == m_top + m_rows_len - 1) {
+                        /* Found a paragraph from @top to @row, inclusive. */
+
+                        /* Doing BiDi, syntax highlighting etc. come here in the future. */
+
+                        top = row + 1;
+                }
+                row++;
+        }
+
+        m_invalid = false;
+}
diff --git a/src/ringview.hh b/src/ringview.hh
new file mode 100644
index 00000000..32b12878
--- /dev/null
+++ b/src/ringview.hh
@@ -0,0 +1,97 @@
+/*
+ * 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 "ring.hh"
+#include "vterowdata.hh"
+#include "vtetypes.hh"
+#include "vteunistr.h"
+
+namespace vte {
+
+namespace base {
+
+/*
+ * RingView provides a "view" to a continuous segment of the Ring (or stream),
+ * typically the user visible area.
+ *
+ * It computes additional data that are needed to display the contents (or handle
+ * user events such as mouse click), but 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.
+ *
+ * For computing these data, context lines (outside of the specified region of the
+ * Ring) 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 logical 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.
+ */
+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);
+        inline constexpr vte::grid::column_t get_width() const noexcept { return m_width; }
+
+        inline void invalidate() { m_invalid = true; }
+        inline constexpr bool is_updated() const noexcept { return !m_invalid; }
+        void update();
+        void pause();
+
+        VteRowData const* get_row(vte::grid::row_t row) const;
+
+private:
+        Ring *m_ring{nullptr};
+
+        VteRowData **m_rows{nullptr};
+        int m_rows_len{0};
+        int m_rows_alloc_len{0};
+
+        vte::grid::row_t m_top{0};  /* the row of the Ring corresponding to m_rows[0] */
+
+        vte::grid::row_t m_start{0};
+        vte::grid::row_t m_len{0};
+        vte::grid::column_t m_width{0};
+
+        bool m_invalid{true};
+        bool m_paused{true};
+
+        void resume();
+};
+
+}; /* namespace base */
+
+}; /* namespace vte */
diff --git a/src/vte.cc b/src/vte.cc
index c3917907..5971a15a 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -41,6 +41,7 @@
 #include "vtedraw.hh"
 #include "reaper.hh"
 #include "ring.hh"
+#include "ringview.hh"
 #include "caps.hh"
 #include "widget.hh"
 
@@ -561,6 +562,7 @@ Terminal::set_hard_wrapped(vte::grid::row_t row)
 
         row_data->attr.soft_wrapped = false;
 
+        m_ringview.invalidate();
         invalidate_rows_and_context(row, row + 1);
 }
 
@@ -595,6 +597,7 @@ Terminal::set_soft_wrapped(vte::grid::row_t row)
                 } while (row_data != nullptr);
         }
 
+        m_ringview.invalidate();
         invalidate_rows_and_context(row, row + 1);
 }
 
@@ -1628,6 +1631,9 @@ Terminal::confined_grid_coords_from_event(GdkEvent const* event) const
 vte::grid::coords
 Terminal::grid_coords_from_view_coords(vte::view::coords const& pos) const
 {
+        /* Our caller had to update the ringview (we can't do because we're const). */
+        g_assert(m_ringview.is_updated());
+
         vte::grid::column_t col;
         if (pos.x >= 0 && pos.x < m_view_usable_extents.width())
                 col = pos.x / m_cell_width;
@@ -1715,6 +1721,9 @@ Terminal::confine_grid_coords(vte::grid::coords const& rowcol) const
 vte::grid::halfcoords
 Terminal::selection_grid_halfcoords_from_view_coords(vte::view::coords const& pos) const
 {
+        /* Our caller had to update the ringview (we can't do because we're const). */
+        g_assert(m_ringview.is_updated());
+
         vte::grid::row_t row = pixel_to_row(pos.y);
         vte::grid::halfcolumn_t halfcolumn;
 
@@ -1746,6 +1755,9 @@ Terminal::selection_maybe_swap_endpoints(vte::view::coords const& pos)
         if (m_selection_resolved.empty())
                 return;
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         auto current = selection_grid_halfcoords_from_view_coords (pos);
 
         if (m_selection_block_mode) {
@@ -1797,7 +1809,13 @@ Terminal::hyperlink_check(GdkEvent *event)
         const char *hyperlink;
         const char *separator;
 
-        if (!m_allow_hyperlink || !rowcol_from_event(event, &col, &row))
+        if (!m_allow_hyperlink)
+                return NULL;
+
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
+        if (!rowcol_from_event(event, &col, &row))
                 return NULL;
 
         _vte_ring_get_hyperlink_at_position(m_screen->row_data, row, col, false, &hyperlink);
@@ -1821,6 +1839,10 @@ Terminal::regex_match_check(GdkEvent *event,
                                       int *tag)
 {
         long col, row;
+
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         if (!rowcol_from_event(event, &col, &row))
                 return FALSE;
 
@@ -1847,6 +1869,9 @@ Terminal::regex_match_check_extra(GdkEvent *event,
         g_assert(regexes != nullptr || n_regexes == 0);
         g_assert(matches != nullptr);
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         if (!rowcol_from_event(event, &col, &row))
                 return false;
 
@@ -3175,6 +3200,7 @@ Terminal::apply_bidi_attributes(vte::grid::row_t start, guint8 bidi_flags, guint
         _vte_debug_print(VTE_DEBUG_BIDI,
                          "Applied BiDi parameters to rows %ld..%ld.\n", start, row);
 
+        m_ringview.invalidate();
         invalidate_rows(start, row);
 }
 
@@ -3948,6 +3974,7 @@ Terminal::process_incoming()
        }
 
        if (modified || (m_screen != previous_screen)) {
+                m_ringview.invalidate();
                /* Signal that the visible contents changed. */
                queue_contents_changed();
        }
@@ -5593,6 +5620,9 @@ Terminal::modify_selection (vte::view::coords const& pos)
 {
         g_assert (m_selecting);
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         auto current = selection_grid_halfcoords_from_view_coords (pos);
 
         if (current == m_selection_last)
@@ -5611,6 +5641,9 @@ bool
 Terminal::cell_is_selected(vte::grid::column_t col,
                                      vte::grid::row_t row) const
 {
+        /* Our caller had to update the ringview (we can't do because we're const). */
+        g_assert(m_ringview.is_updated());
+
         if (m_selection_block_mode) {
                 /* In block mode, make sure CJKs and TABs aren't cut in half. */
                 while (col > 0) {
@@ -5856,6 +5889,9 @@ bool
 Terminal::maybe_send_mouse_drag(vte::grid::coords const& unconfined_rowcol,
                                           GdkEventType event_type)
 {
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         auto rowcol = confine_grid_coords(unconfined_rowcol);
 
        /* First determine if we even want to send notification. */
@@ -5975,6 +6011,9 @@ Terminal::hyperlink_hilite_update()
         _vte_debug_print (VTE_DEBUG_HYPERLINK,
                          "hyperlink_hilite_update\n");
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         /* m_mouse_last_position contains the current position, see bug 789536 comment 24. */
         auto pos = m_mouse_last_position;
 
@@ -6076,6 +6115,9 @@ Terminal::invalidate_match_span()
 void
 Terminal::match_hilite_update()
 {
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         /* m_mouse_last_position contains the current position, see bug 789536 comment 24. */
         auto pos = m_mouse_last_position;
 
@@ -6826,6 +6868,9 @@ Terminal::start_selection (vte::view::coords const& pos,
        if (m_selection_block_mode)
                type = selection_type_char;
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         m_selection_origin = m_selection_last = selection_grid_halfcoords_from_view_coords(pos);
 
        /* Record the selection type. */
@@ -6987,6 +7032,9 @@ Terminal::widget_motion_notify(GdkEventMotion *event)
 {
        bool handled = false;
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         GdkEvent* base_event = reinterpret_cast<GdkEvent*>(event);
         auto pos = view_coords_from_event(base_event);
         auto rowcol = grid_coords_from_view_coords(pos);
@@ -7052,6 +7100,9 @@ Terminal::widget_button_press(GdkEventButton *event)
        bool handled = false;
        gboolean start_selecting = FALSE, extend_selecting = FALSE;
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         GdkEvent* base_event = reinterpret_cast<GdkEvent*>(event);
         auto pos = view_coords_from_event(base_event);
         auto rowcol = grid_coords_from_view_coords(pos);
@@ -7201,6 +7252,9 @@ Terminal::widget_button_release(GdkEventButton *event)
 {
        bool handled = false;
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         GdkEvent* base_event = reinterpret_cast<GdkEvent*>(event);
         auto pos = view_coords_from_event(base_event);
         auto rowcol = grid_coords_from_view_coords(pos);
@@ -8214,6 +8268,12 @@ Terminal::widget_size_allocate(GtkAllocation *allocation)
        }
 }
 
+void
+Terminal::widget_unmap()
+{
+        m_ringview.pause();
+}
+
 void
 Terminal::widget_unrealize()
 {
@@ -9006,6 +9066,19 @@ Terminal::draw_cells_with_attributes(struct _vte_draw_text_request *items,
        g_free(cells);
 }
 
+void
+Terminal::ringview_update()
+{
+        auto first_row = first_displayed_row();
+        auto last_row = last_displayed_row();
+        if (cursor_is_onscreen())
+                last_row = std::max(last_row, m_screen->cursor.row);
+
+        m_ringview.set_ring (m_screen->row_data);
+        m_ringview.set_rows (first_row, last_row - first_row + 1);
+        m_ringview.set_width (m_column_count);
+        m_ringview.update ();
+}
 
 /* Paint the contents of a given row at the given location.  Take advantage
  * of multiple-draw APIs by finding runs of characters with identical
@@ -9034,6 +9107,9 @@ Terminal::draw_rows(VteScreen *screen_,
         auto const column_count = m_column_count;
         uint32_t const attr_mask = m_allow_bold ? ~0 : ~VTE_ATTR_BOLD_MASK;
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         items = g_newa (struct _vte_draw_text_request, column_count);
 
         /* Paint the background.
@@ -9259,6 +9335,9 @@ Terminal::paint_cursor()
        if (CLAMP(col, 0, m_column_count - 1) != col)
                return;
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         /* 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);
@@ -9390,6 +9469,9 @@ Terminal::paint_im_preedit_string()
        if (m_im_preedit.empty())
                return;
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
        /* Keep local copies of rendering information. */
        width = m_cell_width;
        height = m_cell_height;
@@ -9609,6 +9691,9 @@ Terminal::widget_scroll(GdkEventScroll *event)
        gint cnt, i;
        int button;
 
+        /* Need to ensure the ringview is updated. */
+        ringview_update();
+
         GdkEvent *base_event = reinterpret_cast<GdkEvent*>(event);
         auto rowcol = confined_grid_coords_from_event(base_event);
 
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index a5d33abe..28c65d1f 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -25,6 +25,7 @@
 #include "vtedraw.hh"
 #include "reaper.hh"
 #include "ring.hh"
+#include "ringview.hh"
 #include "buffer.h"
 #include "parser.hh"
 #include "parser-glue.hh"
@@ -612,6 +613,9 @@ public:
         const char *m_hyperlink_hover_uri; /* data is owned by the ring */
         long m_hyperlink_auto_id;
 
+        /* RingView and friends */
+        vte::base::RingView m_ringview;
+
         /* BiDi parameters outside of ECMA and DEC private modes */
         guint m_bidi_rtl : 1;
 
@@ -749,6 +753,7 @@ public:
         void widget_constructed();
         void widget_realize();
         void widget_unrealize();
+        void widget_unmap();
         void widget_style_updated();
         void widget_focus_in(GdkEventFocus *event);
         void widget_focus_out(GdkEventFocus *event);
@@ -1327,6 +1332,8 @@ public:
                                    vte::parser::StringTokeniser::const_iterator& token,
                                    vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept;
 
+        void ringview_update();
+
         /* Sequence handlers */
         bool m_line_wrapped; // signals line wrapped from character insertion
         // Note: inlining the handlers seems to worsen the performance, so we don't do that
diff --git a/src/vterowdata.cc b/src/vterowdata.cc
index 88edba1a..7f9f6b40 100644
--- a/src/vterowdata.cc
+++ b/src/vterowdata.cc
@@ -172,6 +172,14 @@ void _vte_row_data_shrink (VteRowData *row, gulong max_len)
                row->len = max_len;
 }
 
+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
diff --git a/src/widget.cc b/src/widget.cc
index 331ddba9..5af5d47c 100644
--- a/src/widget.cc
+++ b/src/widget.cc
@@ -334,6 +334,8 @@ Widget::size_allocate(GtkAllocation* allocation) noexcept
 void
 Widget::unmap() noexcept
 {
+        m_terminal->widget_unmap();
+
         if (m_event_window)
                 gdk_window_hide(m_event_window);
 }


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