+void
+tstrsequence(uchar c) {
+ switch (c) {
+ case 0x90: /* DCS -- Device Control String */
+ c = 'P';
+ break;
+ case 0x9f: /* APC -- Application Program Command */
+ c = '_';
+ break;
+ case 0x9e: /* PM -- Privacy Message */
+ c = '^';
+ break;
+ case 0x9d: /* OSC -- Operating System Command */
+ c = ']';
+ break;
+ }
+ strreset();
+ strescseq.type = c;
+ term.esc |= ESC_STR;
+ return;
+}
+
+void
+tcontrolcode(uchar ascii) {
+ static char question[UTF_SIZ] = "?";
+
+ switch(ascii) {
+ case '\t': /* HT */
+ tputtab(1);
+ return;
+ case '\b': /* BS */
+ tmoveto(term.c.x-1, term.c.y);
+ return;
+ case '\r': /* CR */
+ tmoveto(0, term.c.y);
+ return;
+ case '\f': /* LF */
+ case '\v': /* VT */
+ case '\n': /* LF */
+ /* go to first col if the mode is set */
+ tnewline(IS_SET(MODE_CRLF));
+ return;
+ case '\a': /* BEL */
+ if(term.esc & ESC_STR_END) {
+ /* backwards compatibility to xterm */
+ strhandle();
+ } else {
+ if(!(xw.state & WIN_FOCUSED))
+ xseturgency(1);
+ if (bellvolume)
+ XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL);
+ }
+ break;
+ case '\033': /* ESC */
+ csireset();
+ term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST);
+ term.esc |= ESC_START;
+ return;
+ case '\016': /* SO (LS1 -- Locking shift 1) */
+ case '\017': /* SI (LS0 -- Locking shift 0) */
+ term.charset = 1 - (ascii - '\016');
+ return;
+ case '\032': /* SUB */
+ tsetchar(question, &term.c.attr, term.c.x, term.c.y);
+ case '\030': /* CAN */
+ csireset();
+ break;
+ case '\005': /* ENQ (IGNORED) */
+ case '\000': /* NUL (IGNORED) */
+ case '\021': /* XON (IGNORED) */
+ case '\023': /* XOFF (IGNORED) */
+ case 0177: /* DEL (IGNORED) */
+ return;
+ case 0x84: /* TODO: IND */
+ break;
+ case 0x85: /* NEL -- Next line */
+ tnewline(1); /* always go to first col */
+ break;
+ case 0x88: /* HTS -- Horizontal tab stop */
+ term.tabs[term.c.x] = 1;
+ break;
+ case 0x8d: /* TODO: RI */
+ case 0x8e: /* TODO: SS2 */
+ case 0x8f: /* TODO: SS3 */
+ case 0x98: /* TODO: SOS */
+ break;
+ case 0x9a: /* DECID -- Identify Terminal */
+ ttywrite(vtiden, sizeof(vtiden) - 1);
+ break;
+ case 0x9b: /* TODO: CSI */
+ case 0x9c: /* TODO: ST */
+ break;
+ case 0x90: /* DCS -- Device Control String */
+ case 0x9f: /* APC -- Application Program Command */
+ case 0x9e: /* PM -- Privacy Message */
+ case 0x9d: /* OSC -- Operating System Command */
+ tstrsequence(ascii);
+ return;
+ }
+ /* only CAN, SUB, \a and C1 chars interrupt a sequence */
+ term.esc &= ~(ESC_STR_END|ESC_STR);
+ return;
+}
+
+/*
+ * returns 1 when the sequence is finished and it hasn't to read
+ * more characters for this sequence, otherwise 0
+ */
+int
+eschandle(uchar ascii) {
+ switch(ascii) {
+ case '[':
+ term.esc |= ESC_CSI;
+ return 0;
+ case '#':
+ term.esc |= ESC_TEST;
+ return 0;
+ case 'P': /* DCS -- Device Control String */
+ case '_': /* APC -- Application Program Command */
+ case '^': /* PM -- Privacy Message */
+ case ']': /* OSC -- Operating System Command */
+ case 'k': /* old title set compatibility */
+ tstrsequence(ascii);
+ return 0;
+ case 'n': /* LS2 -- Locking shift 2 */
+ case 'o': /* LS3 -- Locking shift 3 */
+ term.charset = 2 + (ascii - 'n');
+ break;
+ case '(': /* GZD4 -- set primary charset G0 */
+ case ')': /* G1D4 -- set secondary charset G1 */
+ case '*': /* G2D4 -- set tertiary charset G2 */
+ case '+': /* G3D4 -- set quaternary charset G3 */
+ term.icharset = ascii - '(';
+ term.esc |= ESC_ALTCHARSET;
+ return 0;
+ case 'D': /* IND -- Linefeed */
+ if(term.c.y == term.bot) {
+ tscrollup(term.top, 1);
+ } else {
+ tmoveto(term.c.x, term.c.y+1);
+ }
+ break;
+ case 'E': /* NEL -- Next line */
+ tnewline(1); /* always go to first col */
+ break;
+ case 'H': /* HTS -- Horizontal tab stop */
+ term.tabs[term.c.x] = 1;
+ break;
+ case 'M': /* RI -- Reverse index */
+ if(term.c.y == term.top) {
+ tscrolldown(term.top, 1);
+ } else {
+ tmoveto(term.c.x, term.c.y-1);
+ }
+ break;
+ case 'Z': /* DECID -- Identify Terminal */
+ ttywrite(vtiden, sizeof(vtiden) - 1);
+ break;
+ case 'c': /* RIS -- Reset to inital state */
+ treset();
+ xresettitle();
+ xloadcols();
+ break;
+ case '=': /* DECPAM -- Application keypad */
+ term.mode |= MODE_APPKEYPAD;
+ break;
+ case '>': /* DECPNM -- Normal keypad */
+ term.mode &= ~MODE_APPKEYPAD;
+ break;
+ case '7': /* DECSC -- Save Cursor */
+ tcursor(CURSOR_SAVE);
+ break;
+ case '8': /* DECRC -- Restore Cursor */
+ tcursor(CURSOR_LOAD);
+ break;
+ case '\\': /* ST -- String Terminator */
+ if(term.esc & ESC_STR_END)
+ strhandle();
+ break;
+ default:
+ fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n",
+ (uchar) ascii, isprint(ascii)? ascii:'.');
+ break;
+ }
+ return 1;
+}
+
+void
+tputc(char *c, int len) {
+ uchar ascii;
+ bool control;
+ long unicodep;
+ int width;
+ Glyph *gp;
+
+ if(len == 1) {
+ width = 1;
+ unicodep = ascii = *c;
+ } else {
+ utf8decode(c, &unicodep, UTF_SIZ);
+ if ((width = wcwidth(unicodep)) == -1) {
+ c = "\357\277\275"; /* UTF_INVALID */
+ width = 1;
+ }
+ ascii = unicodep;
+ }
+
+ if(IS_SET(MODE_PRINT))
+ tprinter(c, len);
+ control = ISCONTROL(unicodep);
+
+ /*
+ * STR sequence must be checked before anything else
+ * because it uses all following characters until it
+ * receives a ESC, a SUB, a ST or any other C1 control
+ * character.
+ */
+ if(term.esc & ESC_STR) {
+ if(len == 1 &&
+ (ascii == '\a' || ascii == 030 ||
+ ascii == 032 || ascii == 033 ||
+ ISCONTROLC1(unicodep))) {
+ term.esc &= ~(ESC_START|ESC_STR);
+ term.esc |= ESC_STR_END;
+ } else if(strescseq.len + len < sizeof(strescseq.buf) - 1) {
+ memmove(&strescseq.buf[strescseq.len], c, len);
+ strescseq.len += len;
+ return;
+ } else {
+ /*
+ * Here is a bug in terminals. If the user never sends
+ * some code to stop the str or esc command, then st
+ * will stop responding. But this is better than
+ * silently failing with unknown characters. At least
+ * then users will report back.
+ *
+ * In the case users ever get fixed, here is the code:
+ */
+ /*
+ * term.esc = 0;
+ * strhandle();
+ */
+ return;
+ }
+ }
+
+ /*
+ * Actions of control codes must be performed as soon they arrive
+ * because they can be embedded inside a control sequence, and
+ * they must not cause conflicts with sequences.
+ */
+ if(control) {
+ tcontrolcode(ascii);
+ /*
+ * control codes are not shown ever
+ */
+ return;
+ } else if(term.esc & ESC_START) {
+ if(term.esc & ESC_CSI) {
+ csiescseq.buf[csiescseq.len++] = ascii;
+ if(BETWEEN(ascii, 0x40, 0x7E)
+ || csiescseq.len >= \
+ sizeof(csiescseq.buf)-1) {
+ term.esc = 0;
+ csiparse();
+ csihandle();
+ }
+ return;
+ } else if(term.esc & ESC_ALTCHARSET) {
+ tdeftran(ascii);
+ } else if(term.esc & ESC_TEST) {
+ tdectest(ascii);
+ } else {
+ if (!eschandle(ascii))
+ return;
+ /* sequence already finished */
+ }
+ term.esc = 0;
+ /*
+ * All characters which form part of a sequence are not
+ * printed
+ */
+ return;
+ }
+ if(sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y))
+ selclear(NULL);
+
+ gp = &term.line[term.c.y][term.c.x];
+ if(IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
+ gp->mode |= ATTR_WRAP;
+ tnewline(1);
+ gp = &term.line[term.c.y][term.c.x];
+ }
+
+ if(IS_SET(MODE_INSERT) && term.c.x+width < term.col)
+ memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph));
+
+ if(term.c.x+width > term.col) {
+ tnewline(1);
+ gp = &term.line[term.c.y][term.c.x];
+ }
+
+ tsetchar(c, &term.c.attr, term.c.x, term.c.y);
+
+ if(width == 2) {
+ gp->mode |= ATTR_WIDE;
+ if(term.c.x+1 < term.col) {
+ gp[1].c[0] = '\0';
+ gp[1].mode = ATTR_WDUMMY;
+ }
+ }
+ if(term.c.x+width < term.col) {
+ tmoveto(term.c.x+width, term.c.y);
+ } else {
+ term.c.state |= CURSOR_WRAPNEXT;
+ }
+}
+
+void
+tresize(int col, int row) {
+ int i;
+ int minrow = MIN(row, term.row);
+ int mincol = MIN(col, term.col);
+ bool *bp;
+ TCursor c;
+
+ if(col < 1 || row < 1) {
+ fprintf(stderr,
+ "tresize: error resizing to %dx%d\n", col, row);
+ return;
+ }
+
+ /*
+ * slide screen to keep cursor where we expect it -
+ * tscrollup would work here, but we can optimize to
+ * memmove because we're freeing the earlier lines
+ */
+ for(i = 0; i <= term.c.y - row; i++) {
+ free(term.line[i]);
+ free(term.alt[i]);
+ }
+ if(i > 0) {
+ memmove(term.line, term.line + i, row * sizeof(Line));
+ memmove(term.alt, term.alt + i, row * sizeof(Line));
+ }
+ for(i += row; i < term.row; i++) {
+ free(term.line[i]);
+ free(term.alt[i]);
+ }
+
+ /* resize to new height */
+ term.line = xrealloc(term.line, row * sizeof(Line));
+ term.alt = xrealloc(term.alt, row * sizeof(Line));
+ term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
+ term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
+
+ /* resize each row to new width, zero-pad if needed */
+ for(i = 0; i < minrow; i++) {
+ term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
+ term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph));
+ }
+
+ /* allocate any new rows */