diff options
Diffstat (limited to 'sys/cmd/dvtm/buffer.c')
-rw-r--r-- | sys/cmd/dvtm/buffer.c | 304 |
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; +} |