Scrollback functionality

diff -r f941a85a4a31 dvtm.c
--- a/dvtm.c	Fri Aug 22 23:14:36 2008 -0700
+++ b/dvtm.c	Fri Aug 22 23:46:54 2008 -0700
@@ -110,6 +110,7 @@ void togglebar(const char *args[]);
 void togglebar(const char *args[]);
 void setmwfact(const char *args[]);
 void setlayout(const char *args[]);
+void scrollback(const char *args[]);
 void redraw(const char *args[]);
 void zoom(const char *args[]);
 
@@ -355,6 +356,19 @@ setmwfact(const char *args[]) {
 			mwfact = 0.9;
 	}
 	arrange();
+}
+
+void
+scrollback(const char *args[]) {
+	if (!sel) return;
+
+	if (!args[0] || atoi(args[0])<0)
+		madtty_scroll(sel->term, -sel->h/2);
+	else
+		madtty_scroll(sel->term,  sel->h/2);
+
+	wrefresh(curscr);
+	draw_all(true);
 }
 
 void
diff -r f941a85a4a31 madtty.c
--- a/madtty.c	Fri Aug 22 23:14:36 2008 -0700
+++ b/madtty.c	Fri Aug 22 23:46:54 2008 -0700
@@ -94,6 +94,13 @@ struct madtty_t {
     /* geometry */
     int rows, cols;
     unsigned curattrs;
+
+    /* scrollback buffer */
+    struct t_row_t *scroll_buf;
+    int    scroll_buf_sz;
+    int    scroll_buf_ptr;
+    int    scroll_buf_len;
+    int    scroll_amount;
 
     struct t_row_t *lines;
     struct t_row_t *scroll_top;
@@ -205,14 +212,61 @@ static void clamp_cursor_to_bounds(madtt
     }
 }
 
+static void fill_scroll_buf(madtty_t *t, int s)
+{
+    /* work in screenfuls */
+    int ssz = t->scroll_bot-t->scroll_top;
+    if (s > ssz) {
+	fill_scroll_buf(t, ssz);
+	fill_scroll_buf(t, s-ssz);
+	return;
+    }
+    if (s < -ssz) {
+	fill_scroll_buf(t, -ssz);
+	fill_scroll_buf(t, s+ssz);
+	return;
+    }
+
+    t->scroll_buf_len += s;
+    if (t->scroll_buf_len >= t->scroll_buf_sz)
+	t->scroll_buf_len = t->scroll_buf_sz;
+
+    if (s > 0) {
+	for (int i=0; i<s; i++) {
+	    struct t_row_t tmp = t->scroll_top[i];
+	    t->scroll_top[i] = t->scroll_buf[t->scroll_buf_ptr];
+	    t->scroll_buf[t->scroll_buf_ptr] = tmp;
+
+	    t->scroll_buf_ptr++;
+	    if (t->scroll_buf_ptr == t->scroll_buf_sz)
+		t->scroll_buf_ptr = 0;
+	}
+    }
+    t_row_roll(t->scroll_top, t->scroll_bot, s);
+    if (s < 0) {
+	for (int i=(-s)-1; i>=0; i--) {
+	    t->scroll_buf_ptr--;
+	    if (t->scroll_buf_ptr == -1)
+		t->scroll_buf_ptr = t->scroll_buf_sz-1;
+
+	    struct t_row_t tmp = t->scroll_top[i];
+	    t->scroll_top[i] = t->scroll_buf[t->scroll_buf_ptr];
+	    t->scroll_buf[t->scroll_buf_ptr] = tmp;
+	    t->scroll_top[i].dirty = true;
+	}
+    }
+}
+
 static void cursor_line_down(madtty_t *t)
 {
     t->curs_row++;
     if (t->curs_row < t->scroll_bot)
         return;
 
+    madtty_noscroll(t);
+
     t->curs_row = t->scroll_bot - 1;
-    t_row_roll(t->scroll_top, t->scroll_bot, 1);
+    fill_scroll_buf(t, 1);
     t_row_set(t->curs_row, 0, t->cols, 0);
 }
 
@@ -909,6 +963,15 @@ madtty_t *madtty_create(int rows, int co
     t->scroll_top = t->lines;
     t->scroll_bot = t->lines + t->rows;
 
+    /* scrollback buffer */
+    t->scroll_buf_sz = 1000;
+    t->scroll_buf = calloc(sizeof(t_row_t), t->scroll_buf_sz);
+    for (i = 0; i < t->scroll_buf_sz; i++) {
+        t->scroll_buf[i].text = calloc(sizeof(wchar_t),  t->cols);
+        t->scroll_buf[i].attr = calloc(sizeof(uint16_t), t->cols);
+    }
+    t->scroll_buf_ptr = t->scroll_buf_len = 0;
+    t->scroll_amount = 0;
     return t;
 }
 
@@ -920,7 +983,13 @@ void madtty_resize(madtty_t *t, int rows
     if (rows <= 0 || cols <= 0)
         return;
 
+    madtty_noscroll(t);
+
     if (t->rows != rows) {
+        if (t->curs_row > lines+rows) {
+            /* scroll up instead of simply chopping off bottom */
+            fill_scroll_buf(t, t->rows - rows);
+        }
         while (t->rows > rows) {
             free(lines[t->rows - 1].text);
             free(lines[t->rows - 1].attr);
@@ -938,6 +1007,13 @@ void madtty_resize(madtty_t *t, int rows
                 t_row_set(lines + row, t->cols, cols - t->cols, 0);
             else
                 lines[row].dirty = true;
+        }
+	t_row_t *sbuf = t->scroll_buf;
+        for (int row = 0; row < t->scroll_buf_sz; row++) {
+            sbuf[row].text = realloc(sbuf[row].text, sizeof(wchar_t) * cols);
+            sbuf[row].attr = realloc(sbuf[row].attr, sizeof(uint16_t) * cols);
+            if (t->cols < cols)
+                t_row_set(sbuf + row, t->cols, cols - t->cols, 0);
         }
         t->cols = cols;
     }
@@ -969,6 +1045,11 @@ void madtty_destroy(madtty_t *t)
         free(t->lines[i].attr);
     }
     free(t->lines);
+    for (i = 0; i < t->scroll_buf_sz; i++) {
+        free(t->scroll_buf[i].text);
+        free(t->scroll_buf[i].attr);
+    }
+    free(t->scroll_buf);
     free(t);
 }
 
@@ -1008,7 +1089,28 @@ void madtty_draw(madtty_t *t, WINDOW *wi
     }
 
     wmove(win, srow + t->curs_row - t->lines, scol + t->curs_col);
-    curs_set(!t->curshid);
+    curs_set(madtty_cursor(t));
+}
+
+void madtty_scroll(madtty_t *t, int rows)
+{
+    if (rows < 0) {
+	/* scroll back */
+	if (rows < -t->scroll_buf_len)
+	    rows = -t->scroll_buf_len;
+    } else {
+	/* scroll forward */
+	if (rows > t->scroll_amount)
+	    rows = t->scroll_amount;
+    }
+    fill_scroll_buf(t, rows);
+    t->scroll_amount -= rows;
+}
+
+void madtty_noscroll(madtty_t *t)
+{
+    if (t->scroll_amount)
+	madtty_scroll(t, t->scroll_amount);
 }
 
 /******************************************************/
@@ -1059,6 +1161,8 @@ void madtty_keypress(madtty_t *t, int ke
 void madtty_keypress(madtty_t *t, int keycode)
 {
     char c = (char)keycode;
+
+    madtty_noscroll(t);
 
     if (keycode >= 0 && keycode < KEY_MAX && keytable[keycode]) {
         switch(keycode) {
@@ -1153,6 +1257,6 @@ void *madtty_get_data(madtty_t *t)
 
 unsigned madtty_cursor(madtty_t *t)
 {
-    return !t->curshid;
+    return t->scroll_amount ? 0 : !t->curshid;
 }
 /* vim:set et sts=4: */
diff -r f941a85a4a31 madtty.h
--- a/madtty.h	Fri Aug 22 23:14:36 2008 -0700
+++ b/madtty.h	Fri Aug 22 23:46:54 2008 -0700
@@ -72,4 +72,7 @@ void madtty_dirty(madtty_t *t);
 void madtty_dirty(madtty_t *t);
 void madtty_draw(madtty_t *, WINDOW *win, int startrow, int startcol);
 
+void madtty_scroll(madtty_t *, int rows);
+void madtty_noscroll(madtty_t *);
+
 #endif /* MADTTY_MADTTY_H */
