aboutsummaryrefslogtreecommitdiff
path: root/sys/libterm/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/libterm/buffer.c')
-rw-r--r--sys/libterm/buffer.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/sys/libterm/buffer.c b/sys/libterm/buffer.c
new file mode 100644
index 0000000..b903e71
--- /dev/null
+++ b/sys/libterm/buffer.c
@@ -0,0 +1,326 @@
+#include "term.h"
+
+/* row operations */
+void
+zero(Row *row, int start, int len)
+{
+ int i;
+ Cell cell = {
+ .r = L'\0',
+ .pen = {0},
+ };
+
+ for (i = start; i < len + start; i++)
+ row->cells[i] = cell;
+ row->dirty = true;
+}
+
+void
+roll(Row *start, Row *end, int count)
+{
+ int n = end - start;
+
+ count %= n;
+ if (count < 0)
+ count += n;
+
+ if (count) {
+ char buf[count * sizeof(Row)];
+ memcpy(buf, start, count * sizeof(Row));
+ memmove(start, start + count, (n - count) * sizeof(Row));
+ memcpy(end - count, buf, count * sizeof(Row));
+ for (Row *row = start; row < end; row++)
+ row->dirty = true;
+ }
+}
+
+/* buffer operations */
+void
+bclear(Buffer *b)
+{
+ int i;
+ Cell cell = {
+ .r = L'\0',
+ .pen = {
+ .state = PenNormal,
+ .col.fg = -1,
+ .col.bg = -1,
+ },
+ };
+
+ for (i = 0; i < b->nrow; i++) {
+ Row *row = b->row + i;
+ for (int j = 0; j < b->ncol; j++) {
+ row->cells[j] = cell;
+ row->dirty = true;
+ }
+ }
+}
+
+void
+bfree(Buffer *b)
+{
+ int i;
+
+ for (i = 0; i < b->nrow; i++)
+ free(b->row[i].cells);
+
+ free(b->row);
+
+ for (i = 0; i < b->scroll.size; i++)
+ free(b->scroll.buf[i].cells);
+
+ free(b->scroll.buf);
+ free(b->tabs);
+}
+
+void
+bscroll(Buffer *b, int s)
+{
+ /* work in screenfuls */
+ int ssz = b->scroll.bot - b->scroll.top;
+
+ if (s > ssz) {
+ bscroll(b, ssz);
+ bscroll(b, s - ssz);
+ return;
+ }
+ if (s < -ssz) {
+ bscroll(b, -ssz);
+ bscroll(b, s + ssz);
+ return;
+ }
+
+ b->scroll.above += s;
+ if (b->scroll.above >= b->scroll.size)
+ b->scroll.above = b->scroll.size;
+
+ if (s > 0 && b->scroll.size) {
+ for (int i = 0; i < s; i++) {
+ Row tmp = b->scroll.top[i];
+ b->scroll.top[i] = b->scroll.buf[b->scroll.index];
+ b->scroll.buf[b->scroll.index] = tmp;
+
+ b->scroll.index++;
+ if (b->scroll.index == b->scroll.size)
+ b->scroll.index = 0;
+ }
+ }
+ roll(b->scroll.top, b->scroll.bot, s);
+ if (s < 0 && b->scroll.size) {
+ for (int i = (-s) - 1; i >= 0; i--) {
+ b->scroll.index--;
+ if (b->scroll.index == -1)
+ b->scroll.index = b->scroll.size - 1;
+
+ Row tmp = b->scroll.top[i];
+ b->scroll.top[i] = b->scroll.buf[b->scroll.index];
+ b->scroll.buf[b->scroll.index] = tmp;
+ b->scroll.top[i].dirty = true;
+ }
+ }
+}
+
+void
+bresize(Buffer *b, int nrow, int ncol)
+{
+ Row *row = b->row;
+
+ if (b->nrow != nrow) {
+ if (b->crow >= row + nrow) {
+ /* scroll up instead of simply chopping off bottom */
+ bscroll(b, (b->crow - b->row) - nrow + 1);
+ }
+ while (b->nrow > nrow) {
+ free(row[b->nrow - 1].cells);
+ b->nrow--;
+ }
+
+ row = realloc(row, sizeof(Row) * nrow);
+ }
+
+ if (b->maxcols < ncol) {
+ for (int r = 0; r < b->nrow; r++) {
+ row[r].cells = realloc(row[r].cells, sizeof(Cell) * ncol);
+ if (b->ncol < ncol)
+ zero(row + r, b->ncol, ncol - b->ncol);
+ row[r].dirty = true;
+ }
+ Row *sbuf = b->scroll.buf;
+ for (int r = 0; r < b->scroll.size; r++) {
+ sbuf[r].cells = realloc(sbuf[r].cells, sizeof(Cell) * ncol);
+ if (b->ncol < ncol)
+ zero(sbuf + r, b->ncol, ncol - b->ncol);
+ }
+ b->tabs = realloc(b->tabs, sizeof(*b->tabs) * ncol);
+ for (int c = b->ncol; c < ncol; c++)
+ b->tabs[c] = !(c & 7);
+ b->maxcols = ncol;
+ b->ncol = ncol;
+ } else if (b->ncol != ncol) {
+ for (int r = 0; r < b->nrow; r++)
+ row[r].dirty = true;
+ b->ncol = ncol;
+ }
+
+ int deltarows = 0;
+ if (b->nrow < nrow) {
+ while (b->nrow < nrow) {
+ row[b->nrow].cells = calloc(b->maxcols, sizeof(Cell));
+ zero(row + b->nrow, 0, b->maxcols);
+ b->nrow++;
+ }
+
+ /* prepare for backfill */
+ if (b->crow >= b->scroll.bot - 1) {
+ deltarows = b->row + nrow - b->crow - 1;
+ if (deltarows > b->scroll.above)
+ deltarows = b->scroll.above;
+ }
+ }
+
+ b->crow += row - b->row;
+ b->scroll.top = row;
+ b->scroll.bot = row + nrow;
+ b->row = row;
+
+ /* perform backfill */
+ if (deltarows > 0) {
+ bscroll(b, -deltarows);
+ b->crow += deltarows;
+ }
+}
+
+bool
+binit(Buffer *b, int rows, int cols, int size)
+{
+ b->pen.state = PenNormal;
+ b->pen.col.fg = b->pen.col.fg = -1;
+
+ if (size < 0)
+ size = 0;
+ if (size && !(b->scroll.buf = calloc(size, sizeof(Row))))
+ return false;
+
+ b->scroll.size = size;
+ bresize(b, rows, cols);
+ return true;
+}
+
+void
+bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae)
+{
+ if (bs)
+ *bs = nil;
+ if (be)
+ *be = nil;
+ if (as)
+ *as = nil;
+ if (ae)
+ *ae = nil;
+ if (!b->scroll.size)
+ return;
+
+ if (b->scroll.above) {
+ if (bs)
+ *bs = &b->scroll.buf[(b->scroll.index - b->scroll.above + b->scroll.size) % b->scroll.size];
+ if (be)
+ *be = &b->scroll.buf[(b->scroll.index-1 + b->scroll.size) % b->scroll.size];
+ }
+ if (b->scroll.below) {
+ if (as)
+ *as = &b->scroll.buf[b->scroll.index];
+ if (ae)
+ *ae = &b->scroll.buf[(b->scroll.index + b->scroll.below-1) % b->scroll.size];
+ }
+}
+
+Row *
+browfirst(Buffer *b)
+{
+ Row *bstart;
+ if (!b->scroll.size || !b->scroll.above)
+ return b->row;
+ bboundary(b, &bstart, nil, nil, nil);
+ return bstart;
+}
+
+Row *
+browlast(Buffer *b)
+{
+ Row *aend;
+ if (!b->scroll.size || !b->scroll.below)
+ return b->row + b->nrow - 1;
+ bboundary(b, nil, nil, nil, &aend);
+ return aend;
+}
+
+Row *
+brownext(Buffer *b, Row *row)
+{
+ Row *before_start, *before_end, *after_start, *after_end;
+ Row *first = b->row, *last = b->row + b->nrow - 1;
+
+ if (!row)
+ return nil;
+
+ bboundary(b, &before_start, &before_end, &after_start, &after_end);
+
+ if (row >= first && row < last)
+ return ++row;
+ if (row == last)
+ return after_start;
+ if (row == before_end)
+ return first;
+ if (row == after_end)
+ return nil;
+ if (row == &b->scroll.buf[b->scroll.size - 1])
+ return b->scroll.buf;
+ return ++row;
+}
+
+Row *
+bprevrow(Buffer *b, Row *row)
+{
+ Row *before_start, *before_end, *after_start, *after_end;
+ Row *first = b->row, *last = b->row + b->nrow - 1;
+
+ if (!row)
+ return nil;
+
+ bboundary(b, &before_start, &before_end, &after_start, &after_end);
+
+ if (row > first && row <= last)
+ return --row;
+ if (row == first)
+ return before_end;
+ if (row == before_start)
+ return nil;
+ if (row == after_start)
+ return last;
+ if (row == b->scroll.buf)
+ return &b->scroll.buf[b->scroll.size - 1];
+ return --row;
+}
+
+void
+brender(Buffer *b, Term *t)
+{
+ int r, c, n;
+ char u[UTFmax+1];
+ Row *row;
+ Cell *cell;
+
+ for (r = 0; r < b->nrow; r++) {
+ row = b->row + r;
+ if (!row->dirty)
+ continue;
+
+ for (c = 0; c < b->ncol; c++) {
+ cell = row->cells + c;
+ tsetpen(t, cell->pen);
+ n = utf8·runetobyte(u, &cell->r);
+ twrite(t, n, u);
+ }
+ }
+}