aboutsummaryrefslogtreecommitdiff
path: root/sys/cmd/dvtm/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cmd/dvtm/buffer.c')
-rw-r--r--sys/cmd/dvtm/buffer.c304
1 files changed, 304 insertions, 0 deletions
diff --git a/sys/cmd/dvtm/buffer.c b/sys/cmd/dvtm/buffer.c
new file mode 100644
index 0000000..f7e73c0
--- /dev/null
+++ b/sys/cmd/dvtm/buffer.c
@@ -0,0 +1,304 @@
+#include "buffer.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->rows; i++) {
+ Row *row = b->lines + i;
+ for (int j = 0; j < b->cols; j++) {
+ row->cells[j] = cell;
+ row->dirty = true;
+ }
+ }
+}
+
+void
+bfree(Buffer *b)
+{
+ int i;
+
+ for (i = 0; i < b->rows; i++)
+ free(b->lines[i].cells);
+
+ free(b->lines);
+
+ 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 rows, int cols)
+{
+ Row *lines = b->lines;
+
+ if (b->rows != rows) {
+ if (b->crow >= lines + rows) {
+ /* scroll up instead of simply chopping off bottom */
+ bscroll(b, (b->crow - b->lines) - rows + 1);
+ }
+ while (b->rows > rows) {
+ free(lines[b->rows - 1].cells);
+ b->rows--;
+ }
+
+ lines = realloc(lines, sizeof(Row) * rows);
+ }
+
+ if (b->maxcols < cols) {
+ for (int row = 0; row < b->rows; row++) {
+ lines[row].cells = realloc(lines[row].cells, sizeof(Cell) * cols);
+ if (b->cols < cols)
+ zero(lines + row, b->cols, cols - b->cols);
+ lines[row].dirty = true;
+ }
+ Row *sbuf = b->scroll.buf;
+ for (int row = 0; row < b->scroll.size; row++) {
+ sbuf[row].cells = realloc(sbuf[row].cells, sizeof(Cell) * cols);
+ if (b->cols < cols)
+ zero(sbuf + row, b->cols, cols - b->cols);
+ }
+ b->tabs = realloc(b->tabs, sizeof(*b->tabs) * cols);
+ for (int col = b->cols; col < cols; col++)
+ b->tabs[col] = !(col & 7);
+ b->maxcols = cols;
+ b->cols = cols;
+ } else if (b->cols != cols) {
+ for (int row = 0; row < b->rows; row++)
+ lines[row].dirty = true;
+ b->cols = cols;
+ }
+
+ int deltarows = 0;
+ if (b->rows < rows) {
+ while (b->rows < rows) {
+ lines[b->rows].cells = calloc(b->maxcols, sizeof(Cell));
+ zero(lines + b->rows, 0, b->maxcols);
+ b->rows++;
+ }
+
+ /* prepare for backfill */
+ if (b->crow >= b->scroll.bot - 1) {
+ deltarows = b->lines + rows - b->crow - 1;
+ if (deltarows > b->scroll.above)
+ deltarows = b->scroll.above;
+ }
+ }
+
+ b->crow += lines - b->lines;
+ b->scroll.top = lines;
+ b->scroll.bot = lines + rows;
+ b->lines = lines;
+
+ /* 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->lines;
+ bboundary(b, &bstart, nil, nil, nil);
+ return bstart;
+}
+
+Row *
+browlast(Buffer *b)
+{
+ Row *aend;
+ if (!b->scroll.size || !b->scroll.below)
+ return b->lines + b->rows - 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->lines, *last = b->lines + b->rows - 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->lines, *last = b->lines + b->rows - 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;
+}