summaryrefslogtreecommitdiff
path: root/src/ringview.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/ringview.cc')
-rw-r--r--src/ringview.cc237
1 files changed, 237 insertions, 0 deletions
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;
+}