+static void try_interpret_escape_seq(madtty_t *t)
+{
+ char lastchar = t->ebuf[t->elen-1];
+
+ switch (*t->ebuf) {
+ case '\0':
+ return;
+
+ case 'M':
+ interpret_csi_SR(t);
+ cancel_escape_sequence(t);
+ return;
+
+ case '(':
+ case ')':
+ if (t->elen == 2)
+ goto cancel;
+ break;
+
+ case ']': /* xterm thing */
+ if (lastchar == '\a')
+ goto cancel;
+ break;
+
+ default:
+ goto cancel;
+
+ case '[':
+ if (is_valid_csi_ender(lastchar)) {
+ es_interpret_csi(t);
+ cancel_escape_sequence(t);
+ return;
+ }
+ break;
+ }
+
+ if (t->elen + 1 >= (int)sizeof(t->ebuf)) {
+cancel:
+ cancel_escape_sequence(t);
+ }
+}
+
+static void madtty_process_nonprinting(madtty_t *t, wchar_t wc)
+{
+ switch (wc) {
+ case C0_ESC:
+ new_escape_sequence(t);
+ break;
+
+ case C0_BEL:
+ /* do nothing for now... maybe a visual bell would be nice? */
+ break;
+
+ case C0_BS:
+ if (t->curs_col > 0)
+ t->curs_col--;
+ break;
+
+ case C0_HT: /* tab */
+ t->curs_col = (t->curs_col + 8) & ~7;
+ if (t->curs_col >= t->cols)
+ t->curs_col = t->cols - 1;
+ break;
+
+ case C0_CR:
+ t->curs_col = 0;
+ break;
+
+ case C0_VT:
+ case C0_FF:
+ case C0_LF:
+ cursor_line_down(t);
+ break;
+
+ case C0_SO: /* shift out - acs */
+ t->graphmode = true;
+ break;
+ case C0_SI: /* shift in - acs */
+ t->graphmode = false;
+ break;
+ }
+}
+
+// vt100 special graphics and line drawing
+// 5f-7e standard vt100
+// 40-5e rxvt extension for extra curses acs chars
+static uint16_t const vt100_0[62] = { // 41 .. 7e
+ 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47 hi mr. snowman!
+ 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f
+ 0, 0, 0, 0, 0, 0, 0, 0, // 50-57
+ 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f
+ 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67
+ 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f
+ 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77
+ 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
+};
+
+static void madtty_putc(madtty_t *t, wchar_t wc)
+{
+ int width = 0;
+
+ if (!t->seen_input) {
+ t->seen_input = 1;
+ kill(-t->childpid, SIGWINCH);
+ }
+
+ if (t->escaped) {
+ assert (t->elen + 1 < (int)sizeof(t->ebuf));
+ t->ebuf[t->elen] = wc;
+ t->ebuf[++t->elen] = '\0';
+ try_interpret_escape_seq(t);
+ } else if (IS_CONTROL(wc)) {
+ madtty_process_nonprinting(t, wc);
+ } else {
+ t_row_t *tmp;
+
+ if (t->graphmode) {
+ if (wc >= 0x41 && wc <= 0x7e && vt100_0[wc - 0x41]) {
+ wc = vt100_0[wc - 0x41];
+ }
+ width = 1;
+ } else {
+ width = wcwidth(wc) ?: 1;
+ }
+
+ if (width == 2 && t->curs_col == t->cols - 1) {
+ tmp = t->curs_row;
+ tmp->dirty = true;
+ tmp->text[t->curs_col] = 0;
+ tmp->attr[t->curs_col] = build_attrs(t->curattrs);
+ t->curs_col++;
+ }
+
+ if (t->curs_col >= t->cols) {
+ t->curs_col = 0;
+ cursor_line_down(t);
+ }
+
+ tmp = t->curs_row;
+ tmp->dirty = true;
+
+ if (t->insert) {
+ wmemmove(tmp->text + t->curs_col + width, tmp->text + t->curs_col,
+ (t->cols - t->curs_col - width));
+ memmove(tmp->attr + t->curs_col + width, tmp->attr + t->curs_col,
+ (t->cols - t->curs_col - width) * sizeof(tmp->attr[0]));
+ }
+
+ tmp->text[t->curs_col] = wc;
+ tmp->attr[t->curs_col] = build_attrs(t->curattrs);
+ t->curs_col++;
+ if (width == 2) {
+ tmp->text[t->curs_col] = 0;
+ tmp->attr[t->curs_col] = build_attrs(t->curattrs);
+ t->curs_col++;
+ }
+ }
+}
+
+int madtty_process(madtty_t *t)
+{
+ int res, pos = 0;
+
+ if (t->pty < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ res = read(t->pty, t->rbuf + t->rlen, sizeof(t->rbuf) - t->rlen);
+ if (res < 0)
+ return -1;
+
+ t->rlen += res;
+ while (pos < t->rlen) {
+ wchar_t wc;
+ ssize_t len;
+
+ len = (ssize_t)mbrtowc(&wc, t->rbuf + pos, t->rlen - pos, &t->ps);
+ if (len == -2) {
+ t->rlen -= pos;
+ memmove(t->rbuf, t->rbuf + pos, t->rlen);
+ return 0;
+ }
+
+ if (len == -1) {
+ len = 1;
+ wc = t->rbuf[pos];
+ }
+
+ pos += len ? len : 1;
+ madtty_putc(t, wc);
+ }
+
+ t->rlen -= pos;
+ memmove(t->rbuf, t->rbuf + pos, t->rlen);
+ return 0;
+}
+
+madtty_t *madtty_create(int rows, int cols)