diff options
Diffstat (limited to 'src/ringview.cc')
-rw-r--r-- | src/ringview.cc | 237 |
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; +} |