From ce05175372a9ddca1a225db0765ace1127a39293 Mon Sep 17 00:00:00 2001 From: Nicholas Date: Fri, 12 Nov 2021 09:22:01 -0800 Subject: chore: simplified organizational structure --- src/libterm/window.c | 408 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 src/libterm/window.c (limited to 'src/libterm/window.c') diff --git a/src/libterm/window.c b/src/libterm/window.c new file mode 100644 index 0000000..5d36c8b --- /dev/null +++ b/src/libterm/window.c @@ -0,0 +1,408 @@ +#include "term.h" + +// ----------------------------------------------------------------------- +// buffers + +static +void +zero(Row *row, int start, int len) +{ + int i; + Cell cell = { + .txt = L' ', + .pen = { + .state = PenNormal, + .col.fg = -1, + .col.bg = -1, + }, + }; + + for (i = start; i < len + start; i++) + row->cells[i] = cell; + row->dirty = 1; +} + +static +void +roll(Row *start, Row *end, int count) +{ + int n = end - start; + + /* enforce circularity */ + count %= n; + if (count < 0) + count += n; + + if (count) { + char buf[count * sizeof(Row)]; /* XXX: remove VLA */ + 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 = 1; + } +} + +/* buffer operations */ +static +void +bclear(Buffer *b) +{ + int i; + Cell cell = { + .txt = L' ', + .pen = { + .state = PenNormal, + .col.fg = -1, + .col.bg = -1, + }, + }; + + for (i = 0; i < b->h; i++) { + Row *row = b->row + i; + for (int j = 0; j < b->w; j++) { + row->cells[j] = cell; + row->dirty = 1; + } + } +} + +static +void +bfini(Buffer *b) +{ + int i; + + for (i = 0; i < b->h; i++) + free(b->row[i].cells); + + free(b->row); + + if (b->scroll.size) { + for (i = 0; i < b->scroll.size; i++) + free(b->scroll.buf[i].cells); + + free(b->scroll.buf); + } +} + +static +void +bscroll(Buffer *b, int s) +{ + Row tmp; + int i, ssz = b->scroll.bot - b->scroll.top; + + /* work in quanta of screen size */ + 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; + b->scroll.above = CLAMP(b->scroll.above, 0, b->scroll.size); + + if (s > 0) { + if (b->scroll.size) { + for (i = 0; i < s; i++) { + 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; + } + } else + for (i = 0; i < s; i++) + zero(b->scroll.top+i, 0, b->maxw); + } + + roll(b->scroll.top, b->scroll.bot, s); + + if (s < 0) { + if (b->scroll.size) { + for (i = (-s) - 1; i >= 0; i--) { + b->scroll.index--; + if (b->scroll.index == -1) + b->scroll.index = b->scroll.size - 1; + + 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 = 1; + } + } else + for (i = (-s) - 1; i >= 0; i--) + zero(b->scroll.top+i, 0, b->maxw); + } +} + +static +void +bresize(Buffer *b, int nrow, int ncol) +{ + int r, d; + Row *row = b->row; + Row *cur = row + b->cur.row; + + if (b->h != nrow) { + /* scroll if we can */ + if (cur >= row + nrow) + bscroll(b, b->cur.row - nrow + 1); + while (b->h > nrow) { + free(row[b->h - 1].cells); + b->h--; + } + + row = realloc(row, sizeof(Row) * nrow); + } + + if (b->maxw < ncol) { + /* expand each row */ + for (r = 0; r < b->h; r++) { + row[r].cells = realloc(row[r].cells, sizeof(Cell) * ncol); + if (b->h < ncol) + zero(row + r, b->w, ncol - b->w); + row[r].dirty = 1; + } + /* expand the scroll buffer */ + Row *sbuf = b->scroll.buf; + for (r = 0; r < b->scroll.size; r++) { + sbuf[r].cells = realloc(sbuf[r].cells, sizeof(Cell) * ncol); + if (b->w < ncol) + zero(sbuf + r, b->w, ncol - b->w); + } + b->maxw = b->w = ncol; + } else if (b->w != ncol) { + for (r = 0; r < b->h; r++) + row[r].dirty = 1; + b->w = ncol; + } + + d = 0; + if (b->h < nrow) { + while (b->h < nrow) { + row[b->h].cells = calloc(b->maxw, sizeof(Cell)); + zero(row + b->h, 0, b->maxw); + b->h++; + } + + /* prepare for backfill */ + if (cur >= b->scroll.bot - 1) { + d = b->row + nrow - cur - 1; + if (d > b->scroll.above) + d = b->scroll.above; + } + } + + b->cur.row += row - b->row; + b->scroll.top = row; + b->scroll.bot = row + nrow; + b->row = row; + + /* perform backfill */ + if (d > 0) { + bscroll(b, -d); + b->cur.row += d; + } +} + +static +bool +binit(Buffer *b, int cols, int rows, int scroll) +{ + int size; + + b->pen.state = PenNormal; + b->pen.col.fg = b->pen.col.bg = -1; + + size = MAX(scroll, 0); + if (size && !(b->scroll.buf = calloc(size, sizeof(Row)))) + return false; + + b->scroll.size = size; + bresize(b, rows, cols); + + b->cur = (Dot){0}; + b->save = b->cur; + + return true; +} + +static +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]; + } +} + +static +Row * +browfirst(Buffer *b) +{ + Row *bstart; + if (!b->scroll.size || !b->scroll.above) + return b->row; + bboundary(b, &bstart, nil, nil, nil); + return bstart; +} + +static +Row * +browlast(Buffer *b) +{ + Row *aend; + if (!b->scroll.size || !b->scroll.below) + return b->row + b->h - 1; + bboundary(b, nil, nil, nil, &aend); + return aend; +} + +static +Row * +brownext(Buffer *b, Row *row) +{ + Row *before_start, *before_end, *after_start, *after_end; + Row *first = b->row, *last = b->row + b->h - 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; +} + +static +Row * +bprevrow(Buffer *b, Row *row) +{ + Row *before_start, *before_end, *after_start, *after_end; + Row *first = b->row, *last = b->row + b->h - 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; +} + +// ----------------------------------------------------------------------- +// windows + +Window * +wmake(Window *root, int top, int left, int w, int h, int scroll) +{ + Window *child, *it; + + child = calloc(1, sizeof(*child)); + child->top = top; + child->left = left; + child->parent = root; + if (root) { + if (root->child) { + for (it = root->child; it->link != nil; it = it->link) + ; + it->link = child; + } else + root->child = child; + + child->curvis = root->curvis; + child->blink = root->blink; + } + + if (!binit((Buffer*)child, w, h, scroll)) { + free(child); + return nil; + } + + return child; +} + +void +wfree(Window *win) +{ + free(win); +} + +void +wresize(Window *win, int w, int h) +{ + bresize((Buffer*)win, w, h); +} + +/* TODO: more sophisticated damage tracking */ +void +wputrune(Window *win, rune r) +{ + Row *row = win->row + win->cur.row; + Cell *cell = row->cells + win->cur.col; + + cell->pen = win->pen; + cell->txt = r; + + if (win->cur.col++ >= win->w) { + win->cur.col = 0; + if (win->cur.row++ >= win->h) + win->cur.row = win->h-1; + } + row->dirty = 1; +} + +void +wscroll(Window *win, int s) +{ + bscroll((Buffer*)win, s); +} -- cgit v1.2.1