summaryrefslogtreecommitdiff
path: root/term.c
diff options
context:
space:
mode:
Diffstat (limited to 'term.c')
-rw-r--r--term.c501
1 files changed, 501 insertions, 0 deletions
diff --git a/term.c b/term.c
new file mode 100644
index 0000000..cf57560
--- /dev/null
+++ b/term.c
@@ -0,0 +1,501 @@
+/* uuterm, Copyright (C) 2006 Rich Felker; licensed under GNU GPL v2 only */
+
+#include <wchar.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include "uuterm.h"
+
+#define isdigit(x) ((unsigned)x-'0' < 10)
+
+static void process_char(struct uuterm *t, unsigned c);
+
+static void scroll(struct uuterm *t, int y1, int y2, int rev)
+{
+ int fwd = 1-rev;
+ struct uurow *new = t->rows[(y1&-fwd)|(y2&-rev)];
+ memset(new->cells, 0, t->w * sizeof(struct uucell));
+ new->x1 = 0;
+ new->x2 = t->w-1;
+ memmove(&t->rows[y1+rev], &t->rows[y1+fwd], (y2-y1) * sizeof(struct uurow *));
+ t->rows[(y2&-fwd)|(y1&-rev)] = new;
+}
+
+static void dirty(struct uuterm *t, int y, int x1, int len)
+{
+ int x2 = x1 + len - 1;
+ struct uurow *r = t->rows[y];
+ //r->x1 += (x1 - r->x1) & (x1 - r->x1)>>8*sizeof(int)-1;
+ //r->x2 += (x2 - r->x2) & (r->x2 - x2)>>8*sizeof(int)-1;
+ if (x1 < r->x1) r->x1 = x1;
+ if (x2 > r->x2) r->x2 = x2;
+}
+
+static void bell(struct uuterm *t)
+{
+}
+
+static void erase_line(struct uuterm *t, int dir)
+{
+ int start[3] = { t->x, 0, 0 };
+ int len[3] = { t->w-t->x, t->x+1, t->w };
+ if ((unsigned)dir > 2) return;
+ memset(t->rows[t->y]->cells+start[dir], 0, len[dir] * sizeof(struct uucell));
+ dirty(t, t->y, start[dir], len[dir]);
+}
+
+static void erase_display(struct uuterm *t, int dir)
+{
+ int i;
+ int start[3] = { t->y+1, 0, 0 };
+ int stop[3] = { t->h, t->y, t->h };
+ if ((unsigned)dir > 2) return;
+ erase_line(t, dir);
+ for (i=start[dir]; i<stop[dir]; i++) {
+ memset(t->rows[i]->cells, 0, t->w * sizeof(struct uucell));
+ dirty(t, i, 0, t->w);
+ }
+}
+
+static void reset(struct uuterm *t)
+{
+ /* cheap trick */
+ memset(&t->reset, 0, sizeof *t - offsetof(struct uuterm, reset));
+ t->attr = 7;
+ t->sr_y2 = t->h - 1;
+ erase_display(t, 2);
+}
+
+static void ins_del_char(struct uuterm *t, unsigned n, int ins)
+{
+ int del = 1-ins;
+ struct uucell *base;
+ if (n >= t->w - t->x) {
+ erase_line(t, 0);
+ return;
+ }
+ base = t->rows[t->y]->cells + t->x;
+ memmove(base+(n&-ins), base+(n&-del), (t->w-t->x-n) * sizeof(struct uucell));
+ memset(base+((t->w-t->x-n)&-del), 0, n * sizeof(struct uucell));
+ dirty(t, t->y, t->x, t->w - t->x);
+}
+
+static void ins_del_lines(struct uuterm *t, int n, int ins)
+{
+ if ((unsigned)t->y - t->sr_y1 > t->sr_y2 - t->sr_y1) return;
+ while (n--) scroll(t, t->y, t->sr_y2, ins);
+}
+
+static void newline(struct uuterm *t)
+{
+ process_char(t, '\r');
+ process_char(t, '\n');
+}
+
+static int itoa(char *s, unsigned n)
+{
+ // FIXME!!
+ *s = '1';
+ return 1;
+}
+
+#define CSI '['
+#define OSC ']'
+#define IGNORE1 2
+#define ID_STRING "\033[?6c"
+#define OK_STRING "\033[0n"
+#define PRIVATE ((unsigned)-1)
+
+static void csi(struct uuterm *t, unsigned c)
+{
+ int i;
+ int ins=0;
+ if (c == '?') {
+ t->param[t->nparam] = PRIVATE;
+ if (++t->nparam != 16) return;
+ }
+ if (isdigit(c)) {
+ t->param[t->nparam] = 10*t->param[t->nparam] + c - '0';
+ return;
+ }
+ if (c == ';' && ++t->nparam != 16)
+ return;
+ t->esc = 0;
+ t->nparam++;
+ if (strchr("@ABCDEFGHLMPXaefr`", c) && !t->param[0])
+ t->param[0]++;
+ switch (c) {
+ case 'F':
+ process_char(t, '\r');
+ case 'A':
+ t->y -= t->param[0];
+ break;
+ case 'E':
+ process_char(t, '\r');
+ case 'e':
+ case 'B':
+ t->y += t->param[0];
+ break;
+ case 'a':
+ case 'C':
+ t->x += t->param[0];
+ break;
+ case 'D':
+ t->x -= t->param[0];
+ break;
+ case '`':
+ case 'G':
+ t->x = t->param[0]-1;
+ break;
+ case 'f':
+ case 'H':
+ if (!t->param[1]) t->param[1]++;
+ t->y = t->param[0]-1;
+ t->x = t->param[1]-1;
+ break;
+ case 'J':
+ erase_display(t, t->param[0]);
+ break;
+ case 'K':
+ erase_line(t, t->param[0]);
+ break;
+ case 'L':
+ ins=1;
+ case 'M':
+ ins_del_lines(t, t->param[0], ins);
+ break;
+ case '@':
+ ins=1;
+ case 'P':
+ ins_del_char(t, t->param[0], ins);
+ break;
+ case 'd':
+ t->y = t->param[0]-1;
+ break;
+ case 'h':
+ case 'l':
+ i = c=='h';
+ switch (t->param[0]) {
+ case PRIVATE:
+ switch(t->param[1]) {
+ case 1:
+ t->ckp = i;
+ break;
+ }
+ break;
+ case 4:
+ t->ins = i;
+ break;
+ }
+ break;
+ case 'm':
+ for (i=0; i<t->nparam; i++) {
+ static const unsigned char cmap[] = {
+ 0, 4, 2, 6, 1, 5, 3, 7
+ };
+ static const unsigned short attr[] = {
+ UU_ATTR_BOLD, UU_ATTR_DIM, 0,
+ UU_ATTR_UL, UU_ATTR_BLINK, 0,
+ UU_ATTR_REV
+ };
+ if (t->param[i] == 39) t->param[i] = 37;
+ if (t->param[i] == 49) t->param[i] = 40;
+ if (!t->param[i]) {
+ t->attr = 7;
+ } else if (t->param[i] < 8) {
+ t->attr |= attr[t->param[i]-1];
+ } else if (t->param[i]-21 < 7) {
+ t->attr &= ~attr[t->param[i]-21];
+ } else if (t->param[i]-30 < 8) {
+ t->attr &= ~UU_ATTR_FG;
+ t->attr |= cmap[(t->param[i] - 30)];
+ } else if (t->param[i]-40 < 8) {
+ t->attr &= ~UU_ATTR_BG;
+ t->attr |= cmap[(t->param[i] - 40)] << 4;
+ }
+ }
+ break;
+ case 'n':
+ switch (t->param[0]) {
+ case 5:
+ strcpy(t->res, OK_STRING);
+ t->reslen = sizeof(OK_STRING)-1;
+ break;
+ case 6:
+ t->res[0] = 033;
+ t->res[1] = '[';
+ i=2+itoa(t->res+2, t->x+1);
+ t->res[i++] = ';';
+ i+=itoa(t->res+i, t->y+1);
+ t->res[i++] = 'R';
+ t->reslen = i;
+ break;
+ }
+ break;
+ case 'r':
+ if (!t->param[1]) t->param[1] = t->h;
+ t->sr_y1 = t->param[0]-1;
+ t->sr_y2 = t->param[1]-1;
+ if (t->sr_y1 < 0) t->sr_y1 = 0;
+ if (t->sr_y2 >= t->h) t->sr_y2 = t->h-1;
+ if (t->sr_y1 > t->sr_y2) {
+ t->sr_y1 = 0;
+ t->sr_y2 = t->h-1;
+ }
+ break;
+ case 's':
+ t->save_x = t->x;
+ t->save_y = t->y;
+ break;
+ case 'u':
+ t->x = t->save_x;
+ t->y = t->save_y;
+ break;
+ case '[':
+ t->esc = IGNORE1;
+ break;
+ }
+ if ((unsigned)t->y >= t->h) {
+ if (t->y < 0) t->y = 0;
+ else t->y = t->h-1;
+ }
+ if ((unsigned)t->x >= t->w) {
+ if (t->x < 0) t->x = 0;
+ else t->x = t->w-1;
+ }
+}
+
+static void escape(struct uuterm *t, unsigned c)
+{
+ int rev=0;
+ if ((c & ~2) == 030) {
+ t->esc = 0;
+ return;
+ }
+ if (t->esc == CSI) {
+ csi(t, c);
+ return;
+ } else if (t->esc == OSC) {
+ //FIXME
+ t->esc = 0;
+ return;
+ } else if (t->esc == IGNORE1) {
+ t->esc = 0;
+ return;
+ }
+ t->esc = 0;
+ switch (c) {
+ case 'c':
+ reset(t);
+ break;
+ case 'D':
+ process_char(t, '\n');
+ break;
+ case 'E':
+ newline(t);
+ break;
+ case 'M':
+ if (t->y == t->sr_y1) scroll(t, t->sr_y1, t->sr_y2, 1);
+ else if (t->y > 0) t->y--;
+ break;
+ case 'Z':
+ strcpy(t->res, ID_STRING);
+ t->reslen = sizeof(ID_STRING)-1;
+ break;
+ case '7':
+ t->save_attr = t->attr;
+ t->save_x = t->x;
+ t->save_y = t->y;
+ break;
+ case '8':
+ t->attr = t->save_attr;
+ t->x = t->save_x;
+ t->y = t->save_y;
+ break;
+ case '[':
+ t->esc = CSI;
+ t->nparam = 0;
+ memset(t->param, 0, sizeof t->param);
+ break;
+ case '>':
+ t->kp = 0;
+ break;
+ case '=':
+ t->kp = 1;
+ break;
+ case ']':
+ t->esc = OSC;
+ t->nparam = 0;
+ memset(t->param, 0, sizeof t->param);
+ break;
+ }
+}
+
+static void setchar(struct uucell *cell, unsigned c, unsigned a)
+{
+ int i;
+ if (a & UU_ATTR_REV)
+ cell->a = (a & ~(0xff))
+ | ((a & 0x0f) << 4)
+ | ((a & 0xf0) >> 4);
+ else
+ cell->a = a;
+ for (i=0; i<sizeof(cell->c); i++, c>>=8)
+ cell->c[i] = c;
+}
+
+static void addchar(struct uucell *cell, unsigned c)
+{
+ int i;
+ unsigned b;
+
+ for (i=b=0; i<3; i++) b |= cell->c[i] << 8*i;
+ if (b == 0 || b == ' ' || b == 0xa0) {
+ setchar(cell, c, cell->a);
+ return;
+ }
+ if (!(c = uu_combine_involution(b, c))) return;
+ for (; i<sizeof(cell->c); i++) {
+ if (!cell->c[i]) {
+ cell->c[i] = c;
+ break;
+ }
+ }
+}
+
+static void process_char(struct uuterm *t, unsigned c)
+{
+ int x, y, w;
+ t->reslen = 0; // default no response
+ if (c - 0x80 < 0x20) {
+ /* C1 control codes */
+ process_char(t, 033);
+ process_char(t, c-0x40);
+ return;
+ }
+ if (t->esc) {
+ escape(t, c);
+ return;
+ }
+ switch(w=wcwidth(c)) {
+ case -1:
+ switch (c) {
+ case 033:
+ t->esc = 1;
+ break;
+ case '\r':
+ t->x = 0;
+ break;
+ case '\n':
+ if (t->y == t->sr_y2) scroll(t, t->sr_y1, t->sr_y2, 0);
+ else if (t->y < t->h-1) t->y++;
+ break;
+ case '\t':
+ t->x = (t->x + 8) & ~7;
+ if (t->x >= t->w) t->x = t->w - 1;
+ break;
+ case '\b':
+ if (!t->am && t->x) t->x--;
+ break;
+ case '\a':
+ bell(t);
+ break;
+ default:
+ process_char(t, 0xfffd);
+ break;
+ }
+ t->am = 0;
+ break;
+ case 0:
+ y = t->y;
+ x = t->x;
+ if (!x--) {
+ /* nothing to combine at home position */
+ if (!y--) return;
+ x = 0;
+ }
+
+ addchar(&t->rows[y]->cells[x], c);
+ dirty(t, y, x, 1);
+ break;
+ case 1:
+ case 2:
+ if (t->ins) ins_del_char(t, w, 1);
+ while (w--) {
+ if (t->am) newline(t); // kills am flag
+ dirty(t, t->y, t->x, 1);
+ setchar(&t->rows[t->y]->cells[t->x++], w?UU_FULLWIDTH:c, t->attr);
+ if (t->x == t->w) {
+ t->x--;
+ t->am=1;
+ }
+ }
+ break;
+ }
+}
+
+#define BAD_CHAR 0xffff
+
+void uuterm_stuff_byte(struct uuterm *t, unsigned char b)
+{
+ int i, l;
+ wchar_t wc;
+ mbstate_t init_mbs = { };
+
+ for (i=0; i<2; i++) {
+ l = mbrtowc(&wc, &b, 1, &t->mbs);
+ if (l >= 0) {
+ if (wc) process_char(t, wc);
+ return;
+ } else if (l == -2) return;
+
+ if (!i) process_char(t, BAD_CHAR);
+ t->mbs = init_mbs;
+ }
+}
+
+void uuterm_replace_buffer(struct uuterm *t, int w, int h, void *buf)
+{
+ int i;
+ char *p = buf;
+ struct uurow **rows;
+ int wmin = w < t->w ? w : t->w;
+
+ // keep the cursor on the screen after resizing
+ if (t->y >= h) {
+ int n = t->y - h + 1;
+ t->y -= n;
+ t->sr_y1 -= n;
+ t->sr_y2 -= n;
+ if (t->sr_y1 < 0) t->sr_y1 = 0;
+ if (t->sr_y2 < 0) t->sr_y2 = 0;
+ while (n--) scroll(t, 0, t->h-1, 0);
+ }
+
+ memset(p, 0, UU_BUF_SIZE(w, h));
+
+ rows = (void *)p;
+ p += h*sizeof(struct uurow *);
+
+ for (i=0; i<h; i++, p+=UU_ROW_SIZE(w)) {
+ rows[i] = (void *)p;
+ if (i < t->h)
+ memcpy(rows[i], t->rows[i], UU_ROW_SIZE(wmin));
+ rows[i]->idx = i;
+ rows[i]->x1 = 0;
+ rows[i]->x2 = w-1;
+ }
+
+ t->w = w;
+ t->h = h;
+ t->rows = rows;
+
+ if (t->x >= t->w) t->x = t->w - 1;
+ if (t->sr_y1 >= t->h) t->sr_y1 = t->h - 1;
+ if (t->sr_y2 >= t->h) t->sr_y2 = t->h - 1;
+}
+
+void uuterm_reset(struct uuterm *t)
+{
+ reset(t);
+}