513 lines
14 KiB
C
513 lines
14 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ This program is free software; you can redistribute it and/or modify │
|
|
│ it under the terms of the GNU General Public License as published by │
|
|
│ the Free Software Foundation; version 2 of the License. │
|
|
│ │
|
|
│ This program is distributed in the hope that it will be useful, but │
|
|
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
|
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
|
│ General Public License for more details. │
|
|
│ │
|
|
│ You should have received a copy of the GNU General Public License │
|
|
│ along with this program; if not, write to the Free Software │
|
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
|
│ 02110-1301 USA │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/bits/bits.h"
|
|
#include "libc/bits/safemacros.h"
|
|
#include "libc/conv/conv.h"
|
|
#include "libc/log/check.h"
|
|
#include "libc/macros.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/nexgen32e/bsr.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/str/thompike.h"
|
|
#include "libc/unicode/unicode.h"
|
|
#include "libc/x/x.h"
|
|
#include "tool/build/lib/pty.h"
|
|
|
|
struct MachinePty *MachinePtyNew(unsigned yn, unsigned xn) {
|
|
struct MachinePty *pty;
|
|
DCHECK_GT(yn, 0);
|
|
DCHECK_GT(xn, 0);
|
|
pty = xcalloc(1, sizeof(struct MachinePty));
|
|
pty->yn = yn;
|
|
pty->xn = xn;
|
|
pty->wcs = xcalloc(yn * xn, sizeof(pty->wcs[0]));
|
|
pty->fgs = xcalloc(yn * xn, sizeof(pty->fgs[0]));
|
|
pty->bgs = xcalloc(yn * xn, sizeof(pty->bgs[0]));
|
|
pty->prs = xcalloc(yn * xn, sizeof(pty->prs[0]));
|
|
return pty;
|
|
}
|
|
|
|
void MachinePtyFree(struct MachinePty *pty) {
|
|
if (pty) {
|
|
free(pty->wcs);
|
|
free(pty->fgs);
|
|
free(pty->bgs);
|
|
free(pty->prs);
|
|
free(pty);
|
|
}
|
|
}
|
|
|
|
static void MachinePtyScrollPlane(struct MachinePty *pty, uint32_t *p) {
|
|
memcpy(p, p + pty->xn, sizeof(p[0]) * pty->xn * (pty->yn - 1));
|
|
memset(p + pty->xn * (pty->yn - 1), 0, sizeof(p[0]) * pty->xn);
|
|
}
|
|
|
|
static void MachinePtyScroll(struct MachinePty *pty) {
|
|
MachinePtyScrollPlane(pty, pty->wcs);
|
|
MachinePtyScrollPlane(pty, pty->fgs);
|
|
MachinePtyScrollPlane(pty, pty->bgs);
|
|
MachinePtyScrollPlane(pty, pty->prs);
|
|
}
|
|
|
|
static void MachinePtyNewline(struct MachinePty *pty) {
|
|
pty->x = 0;
|
|
if (++pty->y == pty->yn) {
|
|
--pty->y;
|
|
MachinePtyScroll(pty);
|
|
}
|
|
}
|
|
|
|
static void SetMachinePtyCell(struct MachinePty *pty, wint_t wc) {
|
|
uint32_t w, i;
|
|
if ((w = MAX(0, wcwidth(wc))) > 0) {
|
|
i = pty->y * pty->xn + pty->x;
|
|
pty->wcs[i] = wc;
|
|
pty->fgs[i] = pty->fg;
|
|
pty->bgs[i] = pty->bg;
|
|
pty->prs[i] = pty->pr;
|
|
if ((pty->x += w) >= pty->xn) {
|
|
pty->x = 0;
|
|
MachinePtyNewline(pty);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void MachinePtyTab(struct MachinePty *pty) {
|
|
unsigned x, x2;
|
|
x2 = pty->x + ROUNDUP(pty->x + 1, 8);
|
|
if (x2 >= pty->xn) x2 = pty->xn - 1;
|
|
for (x = pty->x; x < x2; ++x) {
|
|
pty->wcs[pty->y * pty->xn + x] = 0;
|
|
}
|
|
pty->x = x2;
|
|
}
|
|
|
|
static void MachinePtyCursorSet(struct MachinePty *pty, int y, int x) {
|
|
pty->y = MAX(0, MIN(pty->yn - 1, y));
|
|
pty->x = MAX(0, MIN(pty->xn - 1, x));
|
|
}
|
|
|
|
static void MachinePtyCursorMove(struct MachinePty *pty, int dy, int dx) {
|
|
int n;
|
|
if (pty->esc.i > 1) {
|
|
n = atoi(pty->esc.s);
|
|
dy *= n;
|
|
dx *= n;
|
|
}
|
|
MachinePtyCursorSet(pty, pty->y + dy, pty->x + dx);
|
|
}
|
|
|
|
static void MachinePtyCursorPosition(struct MachinePty *pty) {
|
|
int row, col;
|
|
row = MAX(1, atoi(pty->esc.s));
|
|
col = MAX(1, atoi((char *)firstnonnull(strchr(pty->esc.s, ';'), "x") + 1));
|
|
MachinePtyCursorSet(pty, row - 1, col - 1);
|
|
}
|
|
|
|
static void MachinePtyEraseRange(struct MachinePty *pty, size_t i, size_t j) {
|
|
size_t n;
|
|
n = (j - i) * sizeof(uint32_t);
|
|
memset(pty->wcs + i, 0, n);
|
|
memset(pty->fgs + i, 0, n);
|
|
memset(pty->bgs + i, 0, n);
|
|
memset(pty->prs + i, 0, n);
|
|
}
|
|
|
|
static void MachinePtyEraseDisplay(struct MachinePty *pty) {
|
|
switch (atoi(pty->esc.s)) {
|
|
case 3:
|
|
case 2:
|
|
pty->y = 0;
|
|
pty->x = 0;
|
|
case 0:
|
|
MachinePtyEraseRange(pty, pty->y * pty->xn + pty->x, pty->yn * pty->xn);
|
|
break;
|
|
case 1:
|
|
MachinePtyEraseRange(pty, 0, pty->y * pty->xn + pty->x);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void MachinePtyEraseLine(struct MachinePty *pty) {
|
|
switch (atoi(pty->esc.s)) {
|
|
case 0:
|
|
MachinePtyEraseRange(pty, pty->y * pty->xn + pty->x,
|
|
pty->y * pty->xn + pty->xn);
|
|
break;
|
|
case 1:
|
|
MachinePtyEraseRange(pty, pty->y * pty->xn + pty->xn,
|
|
pty->y * pty->xn + pty->x);
|
|
break;
|
|
case 2:
|
|
MachinePtyEraseRange(pty, pty->y * pty->xn, pty->y * pty->xn + pty->xn);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void MachinePtySelectGraphicsRendition(struct MachinePty *pty) {
|
|
char *p, c;
|
|
unsigned x;
|
|
uint8_t code[4];
|
|
enum {
|
|
kSgr,
|
|
kSgrFg = 010,
|
|
kSgrFgTrue = 012,
|
|
kSgrFgXterm = 015,
|
|
kSgrBg = 020,
|
|
kSgrBgTrue = 022,
|
|
kSgrBgXterm = 025,
|
|
} t;
|
|
x = 0;
|
|
t = kSgr;
|
|
p = pty->esc.s;
|
|
memset(code, 0, sizeof(code));
|
|
for (;;) {
|
|
c = *p++;
|
|
switch (c) {
|
|
case '\0':
|
|
return;
|
|
case '0' ... '9':
|
|
x *= 10;
|
|
x += c - '0';
|
|
break;
|
|
case ';':
|
|
case 'm':
|
|
code[code[3]] = x;
|
|
x = 0;
|
|
switch (t) {
|
|
case kSgr:
|
|
switch (code[0]) {
|
|
case 0:
|
|
pty->fg = 0;
|
|
pty->bg = 0;
|
|
pty->pr = 0;
|
|
break;
|
|
case 1:
|
|
pty->pr |= kMachinePtyBold;
|
|
break;
|
|
case 21:
|
|
pty->pr &= ~kMachinePtyBold;
|
|
break;
|
|
case 2:
|
|
pty->pr |= kMachinePtyFaint;
|
|
break;
|
|
case 22:
|
|
pty->pr &= ~kMachinePtyFaint;
|
|
break;
|
|
case 7:
|
|
pty->pr |= kMachinePtyFlip;
|
|
break;
|
|
case 27:
|
|
pty->pr &= ~kMachinePtyFlip;
|
|
break;
|
|
case 90 ... 97:
|
|
code[0] -= 90 - 30;
|
|
code[0] += 8;
|
|
case 30 ... 37:
|
|
pty->fg = code[0] - 30;
|
|
pty->pr |= kMachinePtyFg;
|
|
pty->pr &= ~kMachinePtyTrue;
|
|
break;
|
|
case 38:
|
|
t = kSgrFg;
|
|
break;
|
|
case 39:
|
|
pty->pr &= kMachinePtyFg;
|
|
break;
|
|
case 100 ... 107:
|
|
code[0] -= 100 - 40;
|
|
code[0] += 8;
|
|
case 40 ... 47:
|
|
pty->bg = code[0] - 40;
|
|
pty->pr |= kMachinePtyBg;
|
|
pty->pr &= ~kMachinePtyTrue;
|
|
break;
|
|
case 48:
|
|
t = kSgrBg;
|
|
break;
|
|
case 49:
|
|
pty->pr &= kMachinePtyBg;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case kSgrFg:
|
|
case kSgrBg:
|
|
switch (code[0]) {
|
|
case 2:
|
|
case 5:
|
|
t += code[0];
|
|
break;
|
|
default:
|
|
t = kSgr;
|
|
break;
|
|
}
|
|
break;
|
|
case kSgrFgTrue:
|
|
if (++code[3] == 3) {
|
|
code[3] = 0;
|
|
t = kSgr;
|
|
pty->fg = READ32LE(code);
|
|
pty->pr |= kMachinePtyFg;
|
|
pty->pr |= kMachinePtyTrue;
|
|
}
|
|
break;
|
|
case kSgrBgTrue:
|
|
if (++code[3] == 3) {
|
|
code[3] = 0;
|
|
t = kSgr;
|
|
pty->bg = READ32LE(code);
|
|
pty->pr |= kMachinePtyBg;
|
|
pty->pr |= kMachinePtyTrue;
|
|
}
|
|
break;
|
|
case kSgrFgXterm:
|
|
t = kSgr;
|
|
pty->fg = code[0];
|
|
pty->pr |= kMachinePtyFg;
|
|
pty->pr &= ~kMachinePtyTrue;
|
|
break;
|
|
case kSgrBgXterm:
|
|
t = kSgr;
|
|
pty->bg = code[0];
|
|
pty->pr |= kMachinePtyBg;
|
|
pty->pr &= ~kMachinePtyTrue;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void MachinePtyHideCursor(struct MachinePty *pty) {
|
|
pty->conf |= kMachinePtyNocursor;
|
|
}
|
|
|
|
static void MachinePtyShowCursor(struct MachinePty *pty) {
|
|
pty->conf &= ~kMachinePtyNocursor;
|
|
}
|
|
|
|
static void MachinePtyCsi(struct MachinePty *pty) {
|
|
switch (pty->esc.s[pty->esc.i - 1]) {
|
|
case 'A':
|
|
MachinePtyCursorMove(pty, -1, +0);
|
|
break;
|
|
case 'B':
|
|
MachinePtyCursorMove(pty, +1, +0);
|
|
break;
|
|
case 'C':
|
|
MachinePtyCursorMove(pty, +0, +1);
|
|
break;
|
|
case 'D':
|
|
MachinePtyCursorMove(pty, +0, -1);
|
|
break;
|
|
case 'f':
|
|
case 'H':
|
|
MachinePtyCursorPosition(pty);
|
|
break;
|
|
case 'J':
|
|
MachinePtyEraseDisplay(pty);
|
|
break;
|
|
case 'K':
|
|
MachinePtyEraseLine(pty);
|
|
break;
|
|
case 'm':
|
|
MachinePtySelectGraphicsRendition(pty);
|
|
break;
|
|
case 'l':
|
|
if (strcmp(pty->esc.s, "?25l") == 0) {
|
|
MachinePtyHideCursor(pty);
|
|
}
|
|
break;
|
|
case 'h':
|
|
if (strcmp(pty->esc.s, "?25h") == 0) {
|
|
MachinePtyShowCursor(pty);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void MachinePtyEsc(struct MachinePty *pty) {
|
|
}
|
|
|
|
static void MachinePtyEscAppend(struct MachinePty *pty, char c) {
|
|
pty->esc.i = MIN(pty->esc.i + 1, ARRAYLEN(pty->esc.s) - 1);
|
|
pty->esc.s[pty->esc.i - 1] = c;
|
|
pty->esc.s[pty->esc.i - 0] = '\0';
|
|
}
|
|
|
|
ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) {
|
|
int i;
|
|
const uint8_t *p;
|
|
for (p = data, i = 0; i < n; ++i) {
|
|
switch (pty->state) {
|
|
case kMachinePtyAscii:
|
|
if (p[i] < 0b10000000) {
|
|
switch (p[i]) {
|
|
case '\e':
|
|
pty->state = kMachinePtyEsc;
|
|
pty->esc.i = 0;
|
|
break;
|
|
case '\t':
|
|
MachinePtyTab(pty);
|
|
break;
|
|
case '\r':
|
|
pty->x = 0;
|
|
break;
|
|
case '\n':
|
|
MachinePtyNewline(pty);
|
|
break;
|
|
default:
|
|
SetMachinePtyCell(pty, p[i]);
|
|
break;
|
|
}
|
|
} else if (!ThomPikeCont(p[i])) {
|
|
pty->state = kMachinePtyUtf8;
|
|
pty->u8 = ThomPikeByte(p[i]);
|
|
}
|
|
break;
|
|
case kMachinePtyUtf8:
|
|
if (ThomPikeCont(p[i])) {
|
|
pty->u8 <<= 6;
|
|
pty->u8 |= p[i] & 0b00111111;
|
|
} else {
|
|
SetMachinePtyCell(pty, pty->u8);
|
|
pty->state = kMachinePtyAscii;
|
|
pty->u8 = 0;
|
|
--i;
|
|
}
|
|
break;
|
|
case kMachinePtyEsc:
|
|
if (p[i] == '[') {
|
|
pty->state = kMachinePtyCsi;
|
|
} else if (0x30 <= p[i] && p[i] <= 0x7e) {
|
|
MachinePtyEscAppend(pty, p[i]);
|
|
MachinePtyEsc(pty);
|
|
pty->state = kMachinePtyAscii;
|
|
} else if (0x20 <= p[i] && p[i] <= 0x2f) {
|
|
MachinePtyEscAppend(pty, p[i]);
|
|
} else {
|
|
pty->state = kMachinePtyAscii;
|
|
}
|
|
break;
|
|
case kMachinePtyCsi:
|
|
MachinePtyEscAppend(pty, p[i]);
|
|
switch (p[i]) {
|
|
case ':':
|
|
case ';':
|
|
case '<':
|
|
case '=':
|
|
case '>':
|
|
case '?':
|
|
case '0' ... '9':
|
|
break;
|
|
case '`':
|
|
case '~':
|
|
case '^':
|
|
case '@':
|
|
case '[':
|
|
case ']':
|
|
case '{':
|
|
case '}':
|
|
case '_':
|
|
case '|':
|
|
case '\\':
|
|
case 'A' ... 'Z':
|
|
case 'a' ... 'z':
|
|
MachinePtyCsi(pty);
|
|
pty->state = kMachinePtyAscii;
|
|
break;
|
|
default:
|
|
pty->state = kMachinePtyAscii;
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
if (pty->u8) {
|
|
SetMachinePtyCell(pty, pty->u8);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
void MachinePtyAppendLine(struct MachinePty *pty, struct Buffer *buf,
|
|
unsigned y) {
|
|
uint32_t x, i, fg, bg, pr, wc, w;
|
|
CHECK_LT(y, pty->yn);
|
|
for (fg = bg = pr = x = 0; x < pty->xn; x += w) {
|
|
i = y * pty->xn + x;
|
|
wc = pty->wcs[i];
|
|
w = MAX(0, wcwidth(wc));
|
|
if (w) {
|
|
if (pty->prs[i] != pr || pty->fgs[i] != fg || pty->bgs[i] != bg) {
|
|
fg = pty->fgs[i];
|
|
bg = pty->bgs[i];
|
|
pr = pty->prs[i];
|
|
AppendStr(buf, "\e[0");
|
|
if (pr & kMachinePtyBold) AppendStr(buf, ";1");
|
|
if (pr & kMachinePtyFaint) AppendStr(buf, ";2");
|
|
if (pr & kMachinePtyFlip) AppendStr(buf, ";7");
|
|
if (pr & kMachinePtyFg) {
|
|
if (pr & kMachinePtyTrue) {
|
|
AppendFmt(buf, ";38;2;%d;%d;%d", (fg & 0x0000ff) >> 000,
|
|
(fg & 0x00ff00) >> 010, (fg & 0xff0000) >> 020);
|
|
} else {
|
|
AppendFmt(buf, ";38;5;%d", fg);
|
|
}
|
|
}
|
|
if (pr & kMachinePtyBg) {
|
|
if (pr & kMachinePtyTrue) {
|
|
AppendFmt(buf, ";48;2;%d;%d;%d", (bg & 0x0000ff) >> 000,
|
|
(bg & 0x00ff00) >> 010, (bg & 0xff0000) >> 020);
|
|
} else {
|
|
AppendFmt(buf, ";48;5;%d", bg);
|
|
}
|
|
}
|
|
AppendStr(buf, "m");
|
|
}
|
|
AppendWide(buf, wc);
|
|
} else {
|
|
w = 1;
|
|
if (y == pty->y && x == pty->x) {
|
|
if (!(pty->conf & kMachinePtyNocursor)) {
|
|
AppendStr(buf, "\e[5m▂\e[25m");
|
|
}
|
|
} else {
|
|
AppendChar(buf, ' ');
|
|
}
|
|
}
|
|
}
|
|
AppendStr(buf, "\e[0m");
|
|
}
|