aboutsummaryrefslogtreecommitdiff
path: root/sys/libterm
diff options
context:
space:
mode:
Diffstat (limited to 'sys/libterm')
-rw-r--r--sys/libterm/term.c489
-rw-r--r--sys/libterm/term.h270
-rw-r--r--sys/libterm/window.c408
3 files changed, 0 insertions, 1167 deletions
diff --git a/sys/libterm/term.c b/sys/libterm/term.c
deleted file mode 100644
index 11591fc..0000000
--- a/sys/libterm/term.c
+++ /dev/null
@@ -1,489 +0,0 @@
-#include "term.h"
-
-#include <signal.h>
-#include <sys/ioctl.h>
-
-struct ExtraInfo
-{
- char *enteralt;
- char *exitalt;
-
- char *entermouse;
- char *exitmouse;
-};
-
-static
-struct ExtraInfo vt200 =
-{
- .enteralt = "\e[?1049h",
- .exitalt = "\e[?1049l",
-
- .entermouse = "\e[?1049h\e[?1006l",
- .exitmouse = "\e[?1002l\e[?1006l",
-};
-
-static Term *sigwinchhead;
-
-// -----------------------------------------------------------------------
-// database lookup
-
-static
-char*
-tryinfostr(Term *t, enum unibi_string s)
-{
- char *val = (char*)unibi_get_str(t->info, s);
- /* TODO: provide fallbacks */
- return val;
-}
-
-static
-char*
-guessinfostr(Term *t, enum unibi_string s, char *guess)
-{
- char *val = (char*)unibi_get_str(t->info, s);
- if (!val)
- return guess;
- return val;
-}
-
-static
-char*
-getinfostr(Term *t, enum unibi_string s)
-{
- char *val = tryinfostr(t, s);
- if (!val)
- panicf("required term info string '%s' missing", unibi_name_str(s));
-
- return val;
-}
-
-static
-char *
-tryextrastr(Term *t, char *name)
-{
- const char *nm;
- size_t max = unibi_count_ext_str(t->info);
- for (size_t i = 0; i < max; i++) {
- nm = unibi_get_ext_str_name(t->info, i);
- if (nm && !strcmp(nm, name)) {
- return (char *)nm;
- }
- }
- return nil;
-}
-
-static
-char *
-guessextrastr(Term *t, char *name, char *guess)
-{
- char *s;
- if ((s = tryextrastr(t, name)))
- return s;
-
- return guess;
-}
-
-/* formats escape strings and writes to output */
-static void tfmt(Term *t, char *esc, int n, ...);
-static void tclear(Term *t);
-
-// -----------------------------------------------------------------------
-// exported term methods
-
-static
-char *
-ttmpbuf(Term *t, int len)
-{
- if (t->tmp.len >= len)
- return t->tmp.b;
-
- /* TODO: error handling */
- return (t->tmp.b = realloc(t->tmp.b, len));
-}
-
-void twrite(Term *t, long len, char *s);
-void tlistensigwinch(Term *t);
-
-Term*
-tmake(void)
-{
- Term *t;
-
- t = calloc(1, sizeof(*t));
-
- /* meta data */
- t->name = getenv("TERM");
- t->info = unibi_from_term(t->name);
- if (!t->info)
- panicf("could not identify terminal");
-
- t->fd = 1; // stdout
- tlistensigwinch(t);
-
- t->mode.mouse = 0;
- t->mode.cursorvis = 1;
- t->mode.altscreen = 0;
-
- t->cap.colors = unibi_get_num(t->info, unibi_max_colors);
- t->cap.bce = unibi_get_bool(t->info, unibi_back_color_erase);
-
- /* initialize root window (get current size)*/
- struct winsize ws = { 0 };
- if (ioctl(t->fd, TIOCGWINSZ, &ws) == 1)
- goto bad;
-
- t->root = wmake(nil, 0, 0, ws.ws_col, ws.ws_row, 0);
-
- t->root->curvis = 1;
- t->root->blink = 0;
-
- t->pen = (Pen){
- .state = PenNormal,
- .col = {.fg = -1, .bg = -1},
- };
-
- /* fill in output buffers */
- t->buf.c = t->buf.b;
- t->tmp.b = nil;
- t->tmp.len = 0;
-
- /* get all term info format strings */
- t->esc.cup = getinfostr(t, unibi_cursor_address);
- t->esc.vpa = tryinfostr(t, unibi_row_address);
- t->esc.hpa = tryinfostr(t, unibi_column_address);
- t->esc.cuu = getinfostr(t, unibi_parm_up_cursor);
- t->esc.cuu1 = tryinfostr(t, unibi_cursor_up);
- t->esc.cud = getinfostr(t, unibi_parm_down_cursor);
- t->esc.cud1 = tryinfostr(t, unibi_cursor_down);
- t->esc.cuf = getinfostr(t, unibi_parm_right_cursor);
- t->esc.cuf1 = tryinfostr(t, unibi_cursor_right);
- t->esc.cub = getinfostr(t, unibi_parm_left_cursor);
- t->esc.cub1 = tryinfostr(t, unibi_cursor_left);
- t->esc.ich = getinfostr(t, unibi_parm_ich);
- t->esc.ich1 = tryinfostr(t, unibi_insert_character);
- t->esc.dch = getinfostr(t, unibi_parm_dch);
- t->esc.dch1 = tryinfostr(t, unibi_delete_character);
- t->esc.il = getinfostr(t, unibi_parm_insert_line);
- t->esc.il1 = tryinfostr(t, unibi_insert_line);
- t->esc.dl = getinfostr(t, unibi_parm_delete_line);
- t->esc.dl1 = tryinfostr(t, unibi_delete_line);
- t->esc.ech = getinfostr(t, unibi_erase_chars);
- t->esc.ed2 = getinfostr(t, unibi_clear_screen);
- t->esc.stbm = getinfostr(t, unibi_change_scroll_region);
- t->esc.sgr = getinfostr(t, unibi_set_attributes);
- t->esc.sgr0 = getinfostr(t, unibi_exit_attribute_mode);
- t->esc.sgr_i0 = tryinfostr(t, unibi_exit_italics_mode);
- t->esc.sgr_i1 = tryinfostr(t, unibi_enter_italics_mode);
- t->esc.sgr_fg = getinfostr(t, unibi_set_a_foreground);
- t->esc.sgr_bg = getinfostr(t, unibi_set_a_background);
- t->esc.sm_csr = getinfostr(t, unibi_cursor_normal);
- t->esc.rm_csr = getinfostr(t, unibi_cursor_invisible);
-
- /* extensions to terminfo */
- t->esc.ext.rgbf = guessextrastr(t, "setrgbf", "\x1b[38;2;%p1%d;%p2%d;%p3%dm");
- t->esc.ext.rgbb = guessextrastr(t, "setrgbb", "\x1b[48;2;%p1%d;%p2%d;%p3%dm");
-
- return t;
-
-bad:
- panicf("failed to initialize terminal instance");
- free(t);
- return nil;
-}
-
-void
-tfree(Term *t)
-{
- if (t->mode.mouse)
- twrite(t, 0, vt200.exitmouse);
- if (!t->mode.cursorvis)
- tfmt(t, t->esc.rm_csr, 0);
- if (t->mode.altscreen)
- twrite(t, 0, vt200.exitalt);
-
- tfmt(t, t->esc.sgr0, 0);
- tclear(t);
- free(t);
-}
-
-/* handle resize events */
-void
-tresize(Term *t)
-{
- if (t->fd == -1)
- return;
-
- struct winsize ws = { 0 };
- if (ioctl(t->fd, TIOCGWINSZ, &ws) == 1)
- return;
-
- printf("[%d,%d]\n", ws.ws_col, ws.ws_row);
- if (t->root->w != ws.ws_col || t->root->h != ws.ws_row)
- wresize(t->root, ws.ws_col, ws.ws_row);
-}
-
-static
-void
-sigwinch(int num)
-{
- Term *it;
- for (it = sigwinchhead; it; it = it->link)
- tresize(it);
-}
-
-void
-tlistensigwinch(Term *t)
-{
- sigset_t new, old;
- Term *it;
-
- sigemptyset(&new);
- sigaddset(&new, SIGWINCH);
- sigprocmask(SIG_BLOCK, &new, &old);
-
- if (!sigwinchhead) {
- sigaction(SIGWINCH, &(struct sigaction){ .sa_handler = sigwinch }, nil);
- sigwinchhead = t;
- } else {
- it = sigwinchhead;
- while (it->link)
- it = it->link;
- it->link = t;
- }
-
- sigprocmask(SIG_SETMASK, &old, nil);
-}
-
-void
-tflush(Term *t)
-{
- if (t->fd != -1)
- write(t->fd, t->buf.b, t->buf.c - t->buf.b);
-
- t->buf.c = t->buf.b;
-}
-
-void
-twrite(Term *t, long len, char *s)
-{
- int n;
- if (!len)
- len = strlen(s);
-
-loop:
- n = MIN(len, arrend(t->buf.b) - t->buf.c);
- memcpy(t->buf.c, s, n);
- t->buf.c += n;
- len -= n;
- if (len) {
- tflush(t);
- goto loop;
- }
-}
-
-void
-tsetpen(Term *t, Pen new)
-{
- int c;
- ushort ic, in;
- Pen cur = t->pen;
- if (!memcmp(&new, &cur, sizeof(new)))
- return;
-
- /* attributes */
- tfmt(t, t->esc.sgr, 9,
- 0, /* standout */
- new.state & PenUnderline,
- new.state & PenReverse,
- new.state & PenBlink,
- new.state & PenDim,
- new.state & PenBold,
- new.state & PenInvis,
- 0, /* protect */
- 0); /* alt */
-
- ic = cur.state & PenItalic;
- in = new.state & PenItalic;
- if (ic & ~in)
- tfmt(t, t->esc.sgr_i0, 0);
- else if (~ic & in)
- tfmt(t, t->esc.sgr_i1, 0);
-
- /* fg/bg color */
- /* TODO: add a check for if the terminal supports true color */
- /* TODO: deal w/ negative indices properly */
- if (new.state & PenRGB) {
- tfmt(t, t->esc.ext.rgbf, 3, new.rgb.fg.r, new.rgb.fg.g, new.rgb.fg.b);
- tfmt(t, t->esc.ext.rgbb, 3, new.rgb.bg.r, new.rgb.bg.g, new.rgb.bg.b);
- } else {
- tfmt(t, t->esc.sgr_fg, 1, new.col.fg);
- tfmt(t, t->esc.sgr_bg, 1, new.col.bg);
- }
-
- t->pen = new;
-}
-
-static
-void
-tfmt(Term *t, char *esc, int n, ...)
-{
- int i;
- long len;
- va_list args;
- unibi_var_t param[9];
- char buf[64], *c = buf;
-
- if (!esc)
- panicf("no terminfo escape string given");
-
- va_start(args, n);
- for (i = 0; i < arrlen(param) && i < n; i++) {
- param[i] = unibi_var_from_num(va_arg(args, int));
- }
- va_end(args);
-
- len = unibi_run(esc, param, c, sizeof(buf));
- if (len >= arrlen(buf)) {
- c = ttmpbuf(t, len);
- unibi_run(esc, param, c, len);
- }
-
- twrite(t, len, c);
-}
-
-/* absolute move */
-static
-int
-tgoto(Term *t, int row, int col)
-{
- if (row != -1 && col != -1)
- tfmt(t, t->esc.cup, 2, row, col);
- else if (row != -1) {
- if (!t->esc.vpa)
- return 0;
- tfmt(t, t->esc.vpa, 1, row);
- } else if (col != -1) {
- if (col == 0) {
- twrite(t, 1, "\r");
- return 1;
- }
- if (t->esc.hpa)
- tfmt(t, t->esc.hpa, 1, col);
- else if (t->esc.cuf) {
- twrite(t, 1, "\r");
- tfmt(t, t->esc.cuf, 1, col);
- } else
- return 0;
- } else
- return 0; /* unreachable */
-
- return 1;
-}
-
-/* relative move */
-static
-void
-tjump(Term *t, int down, int right)
-{
- if (down == 1 && t->esc.cud1)
- tfmt(t, t->esc.cud1, 0);
- else if (down == -1 && t->esc.cuu1)
- tfmt(t, t->esc.cuu1, 0);
- else if (down > 0)
- tfmt(t, t->esc.cud, 1, down);
- else if (down < 0)
- tfmt(t, t->esc.cuu, 1, -down);
-
- if (right == 1 && t->esc.cuf1)
- tfmt(t, t->esc.cuf1, 0);
- else if (right == -1 && t->esc.cub1)
- tfmt (t, t->esc.cub1, 0);
- else if (right > 0)
- tfmt(t, t->esc.cuf, 1, right);
- else if( right < 0)
- tfmt(t, t->esc.cub, 1, -right);
-}
-
-static
-void
-tclear(Term *t)
-{
- tfmt(t, t->esc.ed2, 0);
-}
-
-void
-tblit(Term *t, Window *win)
-{
- int r, c, n, j;
- Row *row;
- char u[UTFmax+1] = {0};
-
- j = 0;
- tgoto(t, win->top, win->left);
- for (r = 0; r < win->h; r++) {
- row = win->row + r;
- if (!row->dirty) {
- j++;
- continue;
- }
-
- if (j) {
- tjump(t, j, 0);
- j = 0;
- }
-
- for (c = 0; c < win->w; c++) {
- tsetpen(t, row->cells[c].pen);
- n = utf8·runetobyte(u, &row->cells[c].txt);
- twrite(t, n, u);
- }
-
- row->dirty = 0;
- }
-
- tflush(t);
-}
-
-// -----------------------------------------------------------------------
-// testing
-
-int
-main()
-{
- int i;
- Term *t;
- Window *win;
-
- t = tmake();
- win = t->root;
- tclear(t);
-
- win->pen = (Pen){
- .state = PenNormal,
- .col = {.fg=-1, .bg=-1},
- };
- for (i = 0; i < 2000; i++)
- wputrune(win, 'a');
-
- tblit(t, win);
-
- win->cur.row = 10;
- win->cur.col = 0;
-
- win->pen = (Pen){
- .state=PenNormal|PenRGB,
- .rgb={.fg={200, 100, 100}, .bg={0, 0, 0} },
- };
-
- for (i = 0; i < 500; i++)
- wputrune(win, 'b');
-
- tblit(t, win);
-
- sleep(5);
- wscroll(win, 10);
- tblit(t, win);
- sleep(5);
-
- tfree(t);
-}
diff --git a/sys/libterm/term.h b/sys/libterm/term.h
deleted file mode 100644
index 6bd2f6b..0000000
--- a/sys/libterm/term.h
+++ /dev/null
@@ -1,270 +0,0 @@
-#pragma once
-
-#include <u.h>
-#include <libn.h>
-
-#include <termios.h>
-#include <unibilium.h>
-
-#define iota(x) 1 << (x)
-
-typedef struct RGB8 RGB8;
-typedef struct Pen Pen;
-
-typedef struct Dot Dot;
-typedef struct Cell Cell;
-typedef struct Row Row;
-typedef struct Buffer Buffer;
-typedef struct Window Window;
-
-typedef struct Node Node;
-typedef struct Key Key;
-typedef struct Input Input;
-
-typedef struct Term Term;
-
-struct RGB8
-{
- uint8 r, g, b;
-};
-
-enum
-{
- PenNormal = 0,
- PenBold = iota(0),
- PenDim = iota(1),
- PenInvis = iota(2),
- PenItalic = iota(3),
- PenReverse = iota(4),
- PenStrike = iota(5),
- PenUnderline = iota(6),
- PenBlink = iota(7),
- /* ... */
- PenRGB = iota(15),
-};
-
-struct Pen
-{
- ushort state;
- union {
- /* 256 color (legacy) */
- struct {
- sshort fg : 8, bg : 8; /* 0 - 255 or COLOUR_DEFAULT */
- } col;
- /* true color (modern) */
- struct {
- RGB8 fg, bg;
- } rgb;
- };
-};
-
-/* outputs */
-struct Cell
-{
- rune txt;
- Pen pen;
-};
-
-struct Row
-{
- Cell *cells;
- uint dirty : 1;
-};
-
-struct Dot
-{
- int row, col;
-};
-
-/*
- * scroll.top & scroll.bot are pointers into the viewport.
- *
- * scroll back buffer
- *
- * scroll.buf->+----------------+-----+
- * | | | ^ \
- * | before | | | |
- * current terminal content | viewport | | | |
- * | | | |
- * +----------------+-----+\ | | | s > scroll.above
- * ^ | | i | \ | | i | c |
- * | | | n | \ | | n | r |
- * | | v | \ | | v | o |
- * | | i | \ | | i | l /
- * | buffer | s | >|<- scroll.index | s | l \
- * h | | i | / | | i | |
- * | | b | / | after | b | s > scroll.below
- * | | l | / | viewport | l | i |
- * v | | e | / | | e | z /
- * +----------------+-----+/ | unused | | e
- * <- maxw -> | scroll back | |
- * <- w -> | buffer | | |
- * | | | |
- * | | | v
- * scroll.buf + scroll.size->+----------------+-----+
- * <- maxw ->
- * <- w ->
- */
-
-struct Buffer
-{
- int w, h; /* dimension of buffer */
- Pen pen; /* default attributes */
- int maxw; /* allocated cells (maximal cols over time) */
- Row *row; /* array of row pointers of size 'h' */
- struct {
- Row *buf;
- Row *top;
- Row *bot;
- int size;
- int index;
- int above;
- int below;
- } scroll;
- Dot cur, save; /* cursor position within buffer */
-};
-
-struct Window
-{
- struct Buffer;
- int top, left;
- uchar curvis : 1;
- uchar blink : 2;
-
- Window *parent, *child, *link;
-};
-
-/* input */
-struct Key
-{
- int type;
- int mods;
- uchar utf8[UTFmax+1];
- union {
- rune pt;
- int num;
- int sym;
- char mouse[4];
- } code;
-};
-
-struct KeyInfo
-{
- int type;
- int sym;
- int modmask;
- int modset;
-};
-
-struct Input
-{
- int fd;
- int flags;
- int wait; /* in ms */
-
- /* modifiers */
- uchar closed : 1;
- uchar started : 1;
- uchar hasold : 1;
-
- struct termios oldterm;
-
- /* buffer */
- struct {
- long off;
- uchar *b, *c, *e, bytes[256];
- } rbuf;
- struct {
- uchar *s, bytes[256];
- } ebuf;
-
- /* key data */
- Node *keys;
- struct KeyInfo c0[32];
-};
-
-
-struct Term
-{
- /* meta data */
- char *name;
- unibi_term *info;
- struct {
- uchar altscreen : 1;
- uchar cursorvis : 1;
- uchar mouse : 1;
- } mode;
- struct {
- uchar bce : 1;
- int colors;
- } cap;
-
- /* input capture */
- Input input;
-
- /* output display */
- Window *root;
- Pen pen;
-
- /* raw text to pty */
- int fd;
- struct {
- char *c, b[512];
- } buf;
-
- struct {
- int len;
- char *b;
- } tmp;
-
- /* info */
- struct {
- /* Positioning */
- char *cup; // cursor_address
- char *vpa; // row_address == vertical position absolute
- char *hpa; // column_address = horizontal position absolute
-
- /* Moving */
- char *cuu; char *cuu1; // Cursor Up
- char *cud; char *cud1; // Cursor Down
- char *cuf; char *cuf1; // Cursor Forward == Right
- char *cub; char *cub1; // Cursor Backward == Left
-
- /* Editing */
- char *ich; char *ich1; // Insert Character
- char *dch; char *dch1; // Delete Character
- char *il; char *il1; // Insert Line
- char *dl; char *dl1; // Delete Line
- char *ech; // Erase Character
- char *ed2; // Erase Data 2 == Clear screen
- char *stbm; // Set Top/Bottom Margins
-
- /* formatting */
- char *sgr; // Select Graphic Rendition
- char *sgr0; // Exit Attribute Mode
- char *sgr_i0, *sgr_i1; // SGR italic off/on
- char *sgr_fg; // SGR foreground colour
- char *sgr_bg; // SGR background colour
-
- /* Mode setting/clearing */
- char *sm_csr; char *rm_csr; // Set/reset mode: Cursor visible
-
- /* augmentations to terminfo */
- struct {
- char *rgbf; // rgb foreground
- char *rgbb; // rgb background
- char *smxx; // strikethrough
- char *smulx; // curly underline
- } ext;
- } esc;
-
- Term *link;
-};
-
-/* functions */
-void tresize(Term *t);
-
-Window *wmake(Window *root, int top, int left, int w, int h, int scroll);
-void wresize(Window *root, int w, int h);
-void wputrune(Window *win, rune r);
-void wscroll(Window *win, int s);
diff --git a/sys/libterm/window.c b/sys/libterm/window.c
deleted file mode 100644
index 5d36c8b..0000000
--- a/sys/libterm/window.c
+++ /dev/null
@@ -1,408 +0,0 @@
-#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);
-}