add a ring buffer for storing scrollback data. use a ring buffer instead
authorNalin Dahyabhai <nalin@src.gnome.org>
Fri, 26 Apr 2002 16:09:29 +0000 (16:09 +0000)
committerNalin Dahyabhai <nalin@src.gnome.org>
Fri, 26 Apr 2002 16:09:29 +0000 (16:09 +0000)
* src/Makefile.am, src/ring.c, ring.h: add a ring buffer for storing scrollback data.
* src/vte.c: use a ring buffer instead of a GArray to hold scrollback rows.

ChangeLog
src/Makefile.am
src/ring.c [new file with mode: 0644]
src/ring.h [new file with mode: 0644]
src/vte.c

index d195b16..08bb29c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2002-04-26 12:09  nalin
+       * src/Makefile.am, src/ring.c, ring.h: add a ring buffer for storing
+       scrollback data.
+       * src/vte.c: use a ring buffer instead of a GArray to hold scrollback
+       rows.
 2002-04-26 00:49  nalin
        * Makefile.am: run autogen before tagging so that the tag is always
        correct.
index 9e73af1..8a51508 100644 (file)
@@ -2,7 +2,7 @@ bin_PROGRAMS = vte
 pkginclude_HEADERS = caps.h pty.h termcap.h trie.h vte.h
 lib_LTLIBRARIES = libvte.la
 noinst_PROGRAMS = interpret utf8echo utf8mode iso8859mode
-EXTRA_PROGRAMS = pty termcap trie
+EXTRA_PROGRAMS = pty ring termcap trie
 EXTRA_DIST = marshal.list
 
 CFLAGS = @CFLAGS@ @X_CFLAGS@ @GTK_CFLAGS@
@@ -14,6 +14,8 @@ libvte_la_SOURCES = \
        marshal.h \
        pty.c \
        pty.h \
+       ring.c \
+       ring.h \
        termcap.c \
        termcap.h \
        trie.c \
@@ -44,6 +46,12 @@ interpret_LDADD = @GLIB_LIBS@
 utf8echo_SOURCES = \
        utf8echo.c
 
+ring_CFLAGS = @CFLAGS@ @GLIB_CFLAGS@ -DRING_MAIN
+ring_SOURCES = \
+       ring.c \
+       ring.h
+ring_LDADD = @GLIB_LIBS@
+
 trie_CFLAGS = @CFLAGS@ @GLIB_CFLAGS@ -DTRIE_MAIN
 trie_SOURCES = \
        termcap.c \
diff --git a/src/ring.c b/src/ring.c
new file mode 100644 (file)
index 0000000..a4613a1
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ident "$Id$"
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include "ring.h"
+
+struct _VteRing {
+       VteRingFreeFunc free;
+       gpointer user_data;
+       gpointer *array;
+       long delta, length, max;
+};
+
+static void
+vte_ring_validate(VteRing *ring)
+{
+       long i;
+       for (i = ring->delta; i < ring->delta + ring->length; i++) {
+               g_assert(vte_ring_contains(ring, i));
+               g_assert(ring->array[i % ring->max] != NULL);
+       }
+}
+
+VteRing *
+vte_ring_new(long max_elements, VteRingFreeFunc free, gpointer data)
+{
+       VteRing *ret = g_malloc0(sizeof(VteRing));
+       ret->user_data = data;
+       ret->delta = ret->length = 0;
+       ret->max = max_elements;
+       ret->array = g_malloc0(sizeof(gpointer) * ret->max);
+       ret->free = free;
+       return ret;
+}
+
+void
+vte_ring_insert(VteRing *ring, long position, gpointer data)
+{
+       long point, i;
+#ifdef VTE_DEBUG
+       fprintf(stderr, "Inserting at position %ld.\n", position);
+       fprintf(stderr, " Delta = %ld, Length = %ld, Max = %ld.\n",
+               ring->delta, ring->length, ring->max);
+#endif
+       g_return_if_fail(position >= ring->delta);
+       g_return_if_fail(position <= ring->delta + ring->length);
+       g_return_if_fail(data != NULL);
+
+       /* Initial insertion, or append. */
+       if (position == ring->length + ring->delta) {
+               /* If there was something there before, free it. */
+               if (ring->array[position % ring->max] && ring->free) {
+                       ring->free(ring->array[position % ring->max],
+                                  ring->user_data);
+               }
+               ring->array[position % ring->max] = data;
+               if (ring->length == ring->max) {
+                       ring->delta++;
+               } else {
+                       ring->length++;
+               }
+#ifdef VTE_DEBUG
+               fprintf(stderr, " Delta = %ld, Length = %ld, Max = %ld.\n",
+                       ring->delta, ring->length, ring->max);
+               vte_ring_validate(ring);
+#endif
+               return;
+       }
+
+       /* All other cases. */
+       point = ring->delta + ring->length - 1;
+       while (point < 0) {
+               point += ring->max;
+       }
+
+       /* If the buffer's full, then the last item will be lost. */
+       if (ring->length == ring->max) {
+               if (ring->free && ring->array[point % ring->max]) {
+                       ring->free(ring->array[point % ring->max],
+                                  ring->user_data);
+               }
+       }
+
+       /* Bubble the rest down.  This isn't as slow as you probably think
+        * it is due to the pattern of usage. */
+       if (ring->length != ring->max) {
+               /* We'll need to copy the last item, too. */
+               point++;
+       }
+       for (i = point; i > position; i--) {
+               ring->array[i % ring->max] = ring->array[(i - 1) % ring->max];
+       }
+       ring->array[position % ring->max] = data;
+       ring->length = MIN(ring->length + 1, ring->max);
+#ifdef VTE_DEBUG
+       fprintf(stderr, " Delta = %ld, Length = %ld, Max = %ld.\n",
+               ring->delta, ring->length, ring->max);
+       vte_ring_validate(ring);
+#endif
+}
+
+void
+vte_ring_remove(VteRing *ring, long position, gboolean free)
+{
+       long i;
+#ifdef VTE_DEBUG
+       fprintf(stderr, "Removing item at position %ld.\n", position);
+       fprintf(stderr, " Delta = %ld, Length = %ld, Max = %ld.\n",
+               ring->delta, ring->length, ring->max);
+#endif
+       /* Remove the data at this position. */
+       if (ring->array[position % ring->max] && ring->free) {
+               ring->free(ring->array[position % ring->max],
+                          ring->user_data);
+       }
+       ring->array[position % ring->max] = NULL;
+
+       /* Bubble the rest of the buffer up one notch.  This is also less
+        * of a problem than it might appear, again due to usage patterns. */
+       for (i = position; i < ring->delta + ring->length - 1; i++) {
+               ring->array[i % ring->max] = ring->array[(i + 1) % ring->max];
+       }
+       if (ring->length > 0) {
+               ring->array[(ring->delta + ring->length - 1) % ring->max] = NULL;
+               ring->length--;
+       }
+#ifdef VTE_DEBUG
+       fprintf(stderr, " Delta = %ld, Length = %ld, Max = %ld.\n",
+               ring->delta, ring->length, ring->max);
+       vte_ring_validate(ring);
+#endif
+}
+
+void
+vte_ring_append(VteRing *ring, gpointer data)
+{
+       vte_ring_insert(ring, ring->delta + ring->length, data);
+}
+
+gpointer
+vte_ring_at(VteRing *ring, long position)
+{
+       if (vte_ring_contains(ring, position)) {
+               if (ring->array[position % ring->max] == NULL) {
+                       g_error("NULL at %ld(%ld) delta %ld, length %ld.\n",
+                               position, position % ring->max,
+                               ring->delta, ring->length);
+               }
+               g_assert(ring->array[position % ring->max]);
+               return ring->array[position % ring->max];
+       }
+       return NULL;
+}
+
+gboolean
+vte_ring_contains(VteRing *ring, long position)
+{
+       return (position >= ring->delta) &&
+              (position < (ring->delta + ring->length));
+}
+
+long
+vte_ring_delta(VteRing *ring)
+{
+       return ring->delta;
+}
+
+long
+vte_ring_length(VteRing *ring)
+{
+       return ring->length;
+}
+
+long
+vte_ring_next(VteRing *ring)
+{
+       return ring->delta + ring->length;
+}
+
+void
+vte_ring_free(VteRing *ring, gboolean free)
+{
+       long i;
+       if (free) {
+               for (i = 0; i < ring->max; i++) {
+                       if (ring->array[i]) {
+                               ring->free(ring->array[i], ring->user_data);
+                               ring->array[i] = NULL;
+                       }
+               }
+       }
+       g_free(ring->array);
+       memset(ring, 0, sizeof(ring));
+       g_free(ring);
+}
+
+#ifdef RING_MAIN
+static void
+scrolled_off(gpointer freed, gpointer data)
+{
+       long *l = (long *)freed;
+       char *fmt = data;
+       g_print(fmt, *l);
+}
+
+int
+main(int argc, char **argv)
+{
+       long i, j, k, bias;
+       const int size = 8;
+       long values[24];
+       long lone = 42;
+       long *value;
+       VteRing *ring;
+
+       for (i = 0; i < G_N_ELEMENTS(values); i++) {
+               values[i] = i;
+       }
+
+       ring = vte_ring_new(size, scrolled_off, "Lost value %ld.\n");
+       bias = 0;
+       fprintf(stderr, "Initializing.\n");
+       for (i = 0; i + bias <= G_N_ELEMENTS(values); i++) {
+               k = 0;
+               fprintf(stderr, "[%ld] ", i);
+               for (j = 0; j < G_N_ELEMENTS(values); j++) {
+                       value = vte_ring_index(ring, long *, j);
+                       if (value) {
+                               fprintf(stderr, "%s%ld->%ld",
+                                       (k > 0) ? ", " : "",
+                                       j, *value);
+                               k++;
+                       }
+               }
+               fprintf(stderr, "\n");
+               fprintf(stderr, "[%ld] max %ld, delta %ld, length %ld = {",
+                       i, ring->max, ring->delta, ring->length);
+               for (j = 0; j < size; j++) {
+                       value = ring->array[j];
+                       if (j > 0) {
+                               fprintf(stderr, ", ");
+                       }
+                       if (value) {
+                               fprintf(stderr, "%ld", *value);
+                       }
+               }
+               fprintf(stderr, "}\n");
+               if (i == 3) {
+                       fprintf(stderr, "Removing item 3.\n");
+                       vte_ring_remove(ring, 4, TRUE);
+                       bias--;
+               } else
+               if (i == 10) {
+                       fprintf(stderr, "Inserting item 7.\n");
+                       vte_ring_insert(ring, 7, &lone);
+                       bias--;
+               } else
+               if (i == 20) {
+                       fprintf(stderr, "Inserting item 13.\n");
+                       vte_ring_insert(ring, 13, &lone);
+                       bias--;
+               } else
+               if (i < G_N_ELEMENTS(values)) {
+                       fprintf(stderr, "Appending item.\n");
+                       vte_ring_append(ring, &values[i + bias]);
+               }
+       }
+
+       vte_ring_free(ring, TRUE);
+
+       return 0;
+}
+#endif
diff --git a/src/ring.h b/src/ring.h
new file mode 100644 (file)
index 0000000..c14e5ad
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef ring_h_included
+#define ring_h_included
+
+#ident "$Id$"
+
+#include <glib.h>
+
+typedef struct _VteRing VteRing;
+typedef void (*VteRingFreeFunc)(gpointer freeing, gpointer data);
+
+#define vte_ring_index(ring, cast, position) (cast) vte_ring_at(ring, position)
+VteRing *vte_ring_new(long max_elements, VteRingFreeFunc free, gpointer data);
+void vte_ring_insert(VteRing *ring, long position, gpointer data);
+void vte_ring_remove(VteRing *ring, long position, gboolean free_element);
+gpointer vte_ring_at(VteRing *ring, long position);
+void vte_ring_append(VteRing *ring, gpointer data);
+long vte_ring_delta(VteRing *ring);
+long vte_ring_length(VteRing *ring);
+long vte_ring_next(VteRing *ring);
+gboolean vte_ring_contains(VteRing *ring, long position);
+void vte_ring_free(VteRing *ring, gboolean free_elements);
+
+#endif
index 415ba35..f7eb9a3 100644 (file)
--- a/src/vte.c
+++ b/src/vte.c
@@ -48,6 +48,7 @@
 #include "marshal.h"
 #include "pty.h"
 #include "termcap.h"
+#include "ring.h"
 #include "trie.h"
 #include "vte.h"
 #include <X11/Xlib.h>
@@ -148,7 +149,7 @@ struct _VteTerminalPrivate {
        /* Screen data.  We support the normal screen, and an alternate
         * screen, which seems to be a DEC-specific feature. */
        struct _VteScreen {
-               GArray *row_data;       /* row data, arranged as a GArray of
+               VteRing *row_data;      /* row data, arranged as a GArray of
                                           vte_charcell structures */
                struct {
                        long row, col;
@@ -245,7 +246,7 @@ static GdkFilterReturn vte_terminal_filter_property_changes(GdkXEvent *xevent,
 
 /* Free a no-longer-used row data array. */
 static void
-vte_free_row_data_row(gpointer freeing, gpointer data)
+vte_free_row_data(gpointer freeing, gpointer data)
 {
        if (freeing) {
                g_array_free((GArray*)freeing, FALSE);
@@ -321,8 +322,8 @@ vte_terminal_find_charcell(VteTerminal *terminal, long row, long col)
        struct _VteScreen *screen;
        g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
        screen = terminal->pvt->screen;
-       if (screen->row_data->len > row) {
-               rowdata = g_array_index(screen->row_data, GArray*, row);
+       if (vte_ring_contains(screen->row_data, row)) {
+               rowdata = vte_ring_index(screen->row_data, GArray*, row);
                if (rowdata->len > col) {
                        ret = &g_array_index(rowdata, struct vte_charcell, col);
                }
@@ -445,7 +446,12 @@ vte_terminal_adjust_adjustments(VteTerminal *terminal)
        changed = FALSE;
 
        /* The lower value should be the first row in the buffer. */
-       delta = 0;
+       delta = vte_ring_delta(terminal->pvt->screen->row_data);
+#ifdef VTE_DEBUG
+       fprintf(stderr, "Changing adjustment values "
+               "(delta = %ld, scroll = %ld).\n",
+               delta, terminal->pvt->screen->scroll_delta);
+#endif
        if (terminal->adjustment->lower != delta) {
                terminal->adjustment->lower = delta;
                changed = TRUE;
@@ -453,7 +459,8 @@ vte_terminal_adjust_adjustments(VteTerminal *terminal)
 
        /* The upper value is the number of rows which might be visible.  (Add
         * one to the cursor offset because it's zero-based.) */
-       next = terminal->pvt->screen->row_data->len;
+       next = vte_ring_delta(terminal->pvt->screen->row_data) +
+              terminal->row_count;
        rows = MAX(next,
                   terminal->pvt->screen->cursor_current.row + 1);
        if (terminal->adjustment->upper != rows) {
@@ -492,6 +499,11 @@ vte_terminal_adjust_adjustments(VteTerminal *terminal)
 
        /* If anything changed, signal that there was a change. */
        if (changed == TRUE) {
+#ifdef VTE_DEBUG
+               fprintf(stderr, "Changed adjustment values "
+                       "(delta = %ld, scroll = %ld).\n",
+                       delta, terminal->pvt->screen->scroll_delta);
+#endif
                gtk_adjustment_changed(terminal->adjustment);
        }
 }
@@ -586,16 +598,16 @@ vte_insert_line_int(VteTerminal *terminal, long position)
        GArray *array;
        g_return_if_fail(VTE_IS_TERMINAL(terminal));
        /* Pad out the line data to the insertion point. */
-       while (terminal->pvt->screen->row_data->len < position) {
+       while (vte_ring_next(terminal->pvt->screen->row_data) < position) {
                array = vte_new_row_data();
-               g_array_append_val(terminal->pvt->screen->row_data, array);
+               vte_ring_append(terminal->pvt->screen->row_data, array);
        }
        /* If we haven't inserted a line yet, insert a new one. */
        array = vte_new_row_data();
-       if (terminal->pvt->screen->row_data->len >= position) {
-               g_array_insert_val(terminal->pvt->screen->row_data, position, array);
+       if (vte_ring_next(terminal->pvt->screen->row_data) >= position) {
+               vte_ring_insert(terminal->pvt->screen->row_data, position, array);
        } else {
-               g_array_append_val(terminal->pvt->screen->row_data, array);
+               vte_ring_append(terminal->pvt->screen->row_data, array);
        }
 }
 
@@ -603,15 +615,11 @@ vte_insert_line_int(VteTerminal *terminal, long position)
 static void
 vte_remove_line_int(VteTerminal *terminal, long position)
 {
-       GArray *array;
        g_return_if_fail(VTE_IS_TERMINAL(terminal));
-       if (terminal->pvt->screen->row_data->len > position) {
-               array = g_array_index(terminal->pvt->screen->row_data,
-                                     GArray *,
-                                     position);
-               g_array_remove_index(terminal->pvt->screen->row_data, position);
-               g_array_free(array, TRUE);
+       if (vte_ring_next(terminal->pvt->screen->row_data) > position) {
+               vte_ring_remove(terminal->pvt->screen->row_data, position, TRUE);
        }
+       vte_terminal_adjust_adjustments(terminal);
 }
 
 /* Change the encoding used for the terminal to the given codeset, or the
@@ -833,11 +841,11 @@ vte_sequence_handler_cb(VteTerminal *terminal,
        screen = terminal->pvt->screen;
        /* If the cursor is actually on the screen, clear data in the row
         * which corresponds to the cursor. */
-       if (screen->row_data->len > screen->cursor_current.row) {
+       if (vte_ring_next(screen->row_data) > screen->cursor_current.row) {
                /* Get the data for the row which the cursor points to. */
-               rowdata = g_array_index(screen->row_data,
-                                       GArray*,
-                                       screen->cursor_current.row);
+               rowdata = vte_ring_index(screen->row_data,
+                                        GArray*,
+                                        screen->cursor_current.row);
                /* Clear the data up to the current column. */
                for (i = 0;
                     (i < screen->cursor_current.col) && (i < rowdata->len);
@@ -873,10 +881,10 @@ vte_sequence_handler_cd(VteTerminal *terminal,
        /* If the cursor is actually on the screen, clear data in the rows
         * below the cursor. */
        for (i = screen->cursor_current.row + 1;
-            i < screen->row_data->len;
+            i < vte_ring_next(screen->row_data);
             i++) {
                /* Get the data for the row we're removing. */
-               rowdata = g_array_index(screen->row_data, GArray*, i);
+               rowdata = vte_ring_index(screen->row_data, GArray*, i);
                /* Remove it. */
                while ((rowdata != NULL) && (rowdata->len > 0)) {
                        g_array_remove_index(rowdata, rowdata->len - 1);
@@ -901,10 +909,10 @@ vte_sequence_handler_ce(VteTerminal *terminal,
        screen = terminal->pvt->screen;
        /* If the cursor is actually on the screen, clear data in the row
         * which corresponds to the cursor. */
-       if (screen->row_data->len > screen->cursor_current.row) {
+       if (vte_ring_next(screen->row_data) > screen->cursor_current.row) {
                /* Get the data for the row which the cursor points to. */
-               rowdata = g_array_index(screen->row_data, GArray*,
-                                       screen->cursor_current.row);
+               rowdata = vte_ring_index(screen->row_data, GArray*,
+                                        screen->cursor_current.row);
                /* Remove the data at the end of the array. */
                while (rowdata->len > screen->cursor_current.col) {
                        g_array_remove_index(rowdata, rowdata->len - 1);
@@ -986,10 +994,10 @@ vte_sequence_handler_clear_current_line(VteTerminal *terminal,
        screen = terminal->pvt->screen;
        /* If the cursor is actually on the screen, clear data in the row
         * which corresponds to the cursor. */
-       if (screen->row_data->len > screen->cursor_current.row) {
+       if (vte_ring_next(screen->row_data) > screen->cursor_current.row) {
                /* Get the data for the row which the cursor points to. */
-               rowdata = g_array_index(screen->row_data, GArray*,
-                                       screen->cursor_current.row);
+               rowdata = vte_ring_index(screen->row_data, GArray*,
+                                        screen->cursor_current.row);
                /* Remove it. */
                while (rowdata->len > 0) {
                        g_array_remove_index(rowdata, rowdata->len - 1);
@@ -1079,11 +1087,11 @@ vte_sequence_handler_dc(VteTerminal *terminal,
        g_return_if_fail(VTE_IS_TERMINAL(terminal));
        screen = terminal->pvt->screen;
 
-       if (screen->row_data->len > screen->cursor_current.row) {
+       if (vte_ring_next(screen->row_data) > screen->cursor_current.row) {
                /* Get the data for the row which the cursor points to. */
-               rowdata = g_array_index(screen->row_data,
-                                       GArray*,
-                                       screen->cursor_current.row);
+               rowdata = vte_ring_index(screen->row_data,
+                                        GArray*,
+                                        screen->cursor_current.row);
                col = screen->cursor_current.col;
                /* Remove the column. */
                if (col < rowdata->len) {
@@ -1159,14 +1167,14 @@ vte_terminal_ensure_cursor(VteTerminal *terminal)
 
        screen = terminal->pvt->screen;
 
-       while (screen->cursor_current.row >= screen->row_data->len) {
+       while (screen->cursor_current.row >= vte_ring_next(screen->row_data)) {
                array = vte_new_row_data();
-               g_array_append_val(screen->row_data, array);
+               vte_ring_append(screen->row_data, array);
        }
 
-       array = g_array_index(screen->row_data,
-                             GArray*,
-                             screen->cursor_current.row);
+       array = vte_ring_index(screen->row_data,
+                              GArray*,
+                              screen->cursor_current.row);
 
        if (array != NULL) {
                /* Add enough characters to fill out the row. */
@@ -1181,12 +1189,15 @@ vte_terminal_ensure_cursor(VteTerminal *terminal)
                /* Add one more cell to the end of the line to get
                 * it into the column, and use it. */
                array = g_array_append_val(array, cell);
+#if 0
                /* Remove and reinsert this row. */
-               g_array_remove_index(screen->row_data,
-                                    screen->cursor_current.row);
-               g_array_insert_val(screen->row_data,
-                                  screen->cursor_current.row,
-                                  array);
+               vte_ring_remove(screen->row_data,
+                               screen->cursor_current.row,
+                               FALSE);
+               vte_ring_insert(screen->row_data,
+                               screen->cursor_current.row,
+                               array);
+#endif
        }
 }
 
@@ -1252,7 +1263,7 @@ vte_sequence_handler_do(VteTerminal *terminal,
                /* Make sure that the bottom row is visible, and that it's in
                 * the buffer (even if it's empty).  This usually causes the
                 * top row to become a history-only row. */
-               rows = MAX(screen->row_data->len,
+               rows = MAX(vte_ring_next(screen->row_data),
                           screen->cursor_current.row + 1);
                delta = MAX(0, rows - terminal->row_count);
                if (delta != screen->insert_delta) {
@@ -1303,11 +1314,11 @@ vte_sequence_handler_ec(VteTerminal *terminal,
        }
 
        /* Clear out the given number of characters. */
-       if (screen->row_data->len > screen->cursor_current.row) {
+       if (vte_ring_next(screen->row_data) > screen->cursor_current.row) {
                /* Get the data for the row which the cursor points to. */
-               rowdata = g_array_index(screen->row_data,
-                                       GArray*,
-                                       screen->cursor_current.row);
+               rowdata = vte_ring_index(screen->row_data,
+                                        GArray*,
+                                        screen->cursor_current.row);
                /* Write over the same characters. */
                for (i = 0; i < count; i++) {
                        col = screen->cursor_current.col + i;
@@ -1877,9 +1888,9 @@ vte_sequence_handler_clear_above_current(VteTerminal *terminal,
        /* If the cursor is actually on the screen, clear data in the row
         * which corresponds to the cursor. */
        for (i = screen->insert_delta; i < screen->cursor_current.row; i++) {
-               if (screen->row_data->len > i) {
+               if (vte_ring_next(screen->row_data) > i) {
                        /* Get the data for the row we're erasing. */
-                       rowdata = g_array_index(screen->row_data, GArray*, i);
+                       rowdata = vte_ring_index(screen->row_data, GArray*, i);
                        /* Remove it. */
                        while (rowdata->len > 0) {
                                g_array_remove_index(rowdata, rowdata->len - 1);
@@ -1909,9 +1920,9 @@ vte_sequence_handler_clear_screen(VteTerminal *terminal,
        for (i = screen->insert_delta;
             i < screen->insert_delta + terminal->row_count;
             i++) {
-               if (screen->row_data->len > i) {
+               if (vte_ring_next(screen->row_data) > i) {
                        /* Get the data for the row we're removing. */
-                       rowdata = g_array_index(screen->row_data, GArray*, i);
+                       rowdata = vte_ring_index(screen->row_data, GArray*, i);
                        /* Remove it. */
                        while (rowdata->len > 0) {
                                g_array_remove_index(rowdata, rowdata->len - 1);
@@ -2971,9 +2982,9 @@ vte_terminal_insert_char(GtkWidget *widget, wchar_t c)
        vte_terminal_ensure_cursor(terminal);
 
        /* Get a handle on the array for the insertion row. */
-       array = g_array_index(screen->row_data,
-                             GArray*,
-                             screen->cursor_current.row);
+       array = vte_ring_index(screen->row_data,
+                              GArray*,
+                              screen->cursor_current.row);
 
        /* Read the deltas. */
        for (i = 0; i < columns; i++) {
@@ -2996,12 +3007,15 @@ vte_terminal_insert_char(GtkWidget *widget, wchar_t c)
                        pcell = &g_array_index(array,
                                               struct vte_charcell,
                                               col);
-                       /* Reintroduce this row. */
-                       g_array_remove_index(screen->row_data,
-                                            screen->cursor_current.row);
-                       g_array_insert_val(screen->row_data,
-                                          screen->cursor_current.row,
-                                          array);
+#if 0
+                       /* Remove and reinsert this row. */
+                       vte_ring_remove(screen->row_data,
+                                       screen->cursor_current.row,
+                                       FALSE);
+                       vte_ring_insert(screen->row_data,
+                                       screen->cursor_current.row,
+                                       array);
+#endif
                } else {
                        /* If we're in insert mode, insert a new cell here
                         * and use it. */
@@ -5228,6 +5242,7 @@ vte_terminal_set_termcap(VteTerminal *terminal, const char *path)
 
 /* Set the length of a scrollback buffer.  This is a placeholder until the
  * ring-buffer code gets merged in. */
+#if 0
 static void
 vte_terminal_reset_rowdata(GArray **ring, long lines)
 {
@@ -5249,6 +5264,31 @@ vte_terminal_reset_rowdata(GArray **ring, long lines)
                *ring = g_array_new(TRUE, FALSE, sizeof(GArray*));
        }
 }
+#else
+static void
+vte_terminal_reset_rowdata(VteRing **ring, long lines)
+{
+       VteRing *new_ring;
+       GArray *row;
+       long i, next;
+       new_ring = vte_ring_new(lines, vte_free_row_data, NULL);
+       if (*ring) {
+               next = vte_ring_next(*ring);
+               for (i = vte_ring_delta(*ring); i < next; i++) {
+                       row = vte_ring_index(*ring, GArray*, i);
+                       if (row) {
+                               if (i > next - lines) {
+                                       vte_ring_append(new_ring, row);
+                               } else {
+                                       g_array_free(row, FALSE);
+                               }
+                       }
+               }
+               vte_ring_free(*ring, FALSE);
+       }
+       *ring = new_ring;
+}
+#endif
 
 /* Initialize the terminal widget after the base widget stuff is initialized.
  * We need to create a new psuedo-terminal pair, read in the termcap file, and
@@ -5587,24 +5627,9 @@ vte_terminal_finalize(GObject *object)
        terminal->pvt->trie = NULL;
 
        /* Clear the output histories. */
-       for (i = 0; i < terminal->pvt->normal_screen.row_data->len; i++) {
-               array = g_array_index(terminal->pvt->normal_screen.row_data,
-                                     GArray*,
-                                     i);
-               if (array) {
-                       g_array_free(array, TRUE);
-               }
-       }
-       g_array_free(terminal->pvt->normal_screen.row_data, FALSE);
+       vte_ring_free(terminal->pvt->normal_screen.row_data, TRUE);
        terminal->pvt->normal_screen.row_data = NULL;
-
-       for (i = 0; i < terminal->pvt->alternate_screen.row_data->len; i++) {
-               array = g_array_index(terminal->pvt->alternate_screen.row_data,
-                                     GArray*,
-                                     i);
-               g_array_free(array, TRUE);
-       }
-       g_array_free(terminal->pvt->alternate_screen.row_data, FALSE);
+       vte_ring_free(terminal->pvt->alternate_screen.row_data, TRUE);
        terminal->pvt->alternate_screen.row_data = NULL;
 
        /* Free strings. */