+ tmoveato(0, 0);
+ }
+ break;
+ case 's': /* DECSC -- Save cursor position (ANSI.SYS) */
+ tcursor(CURSOR_SAVE);
+ break;
+ case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */
+ tcursor(CURSOR_LOAD);
+ break;
+ case ' ':
+ switch (csiescseq.mode[1]) {
+ case 'q': /* DECSCUSR -- Set Cursor Style */
+ DEFAULT(csiescseq.arg[0], 1);
+ if (!BETWEEN(csiescseq.arg[0], 0, 6)) {
+ goto unknown;
+ }
+ xw.cursor = csiescseq.arg[0];
+ break;
+ default:
+ goto unknown;
+ }
+ break;
+ }
+}
+
+void
+csidump(void)
+{
+ int i;
+ uint c;
+
+ fprintf(stderr, "ESC[");
+ for (i = 0; i < csiescseq.len; i++) {
+ c = csiescseq.buf[i] & 0xff;
+ if (isprint(c)) {
+ putc(c, stderr);
+ } else if (c == '\n') {
+ fprintf(stderr, "(\\n)");
+ } else if (c == '\r') {
+ fprintf(stderr, "(\\r)");
+ } else if (c == 0x1b) {
+ fprintf(stderr, "(\\e)");
+ } else {
+ fprintf(stderr, "(%02x)", c);
+ }
+ }
+ putc('\n', stderr);
+}
+
+void
+csireset(void)
+{
+ memset(&csiescseq, 0, sizeof(csiescseq));
+}
+
+void
+strhandle(void)
+{
+ char *p = NULL;
+ int j, narg, par;
+
+ term.esc &= ~(ESC_STR_END|ESC_STR);
+ strparse();
+ par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0;
+
+ switch (strescseq.type) {
+ case ']': /* OSC -- Operating System Command */
+ switch (par) {
+ case 0:
+ case 1:
+ case 2:
+ if (narg > 1)
+ xsettitle(strescseq.args[1]);
+ return;
+ case 4: /* color set */
+ if (narg < 3)
+ break;
+ p = strescseq.args[2];
+ /* FALLTHROUGH */
+ case 104: /* color reset, here p = NULL */
+ j = (narg > 1) ? atoi(strescseq.args[1]) : -1;
+ if (xsetcolorname(j, p)) {
+ fprintf(stderr, "erresc: invalid color %s\n", p);
+ } else {
+ /*
+ * TODO if defaultbg color is changed, borders
+ * are dirty
+ */
+ redraw();
+ }
+ return;
+ }
+ break;
+ case 'k': /* old title set compatibility */
+ xsettitle(strescseq.args[0]);
+ return;
+ case 'P': /* DCS -- Device Control String */
+ term.mode |= ESC_DCS;
+ case '_': /* APC -- Application Program Command */
+ case '^': /* PM -- Privacy Message */
+ return;
+ }
+
+ fprintf(stderr, "erresc: unknown str ");
+ strdump();
+}
+
+void
+strparse(void)
+{
+ int c;
+ char *p = strescseq.buf;
+
+ strescseq.narg = 0;
+ strescseq.buf[strescseq.len] = '\0';
+
+ if (*p == '\0')
+ return;
+
+ while (strescseq.narg < STR_ARG_SIZ) {
+ strescseq.args[strescseq.narg++] = p;
+ while ((c = *p) != ';' && c != '\0')
+ ++p;
+ if (c == '\0')
+ return;
+ *p++ = '\0';
+ }
+}
+
+void
+strdump(void)
+{
+ int i;
+ uint c;
+
+ fprintf(stderr, "ESC%c", strescseq.type);
+ for (i = 0; i < strescseq.len; i++) {
+ c = strescseq.buf[i] & 0xff;
+ if (c == '\0') {
+ putc('\n', stderr);
+ return;
+ } else if (isprint(c)) {
+ putc(c, stderr);
+ } else if (c == '\n') {
+ fprintf(stderr, "(\\n)");
+ } else if (c == '\r') {
+ fprintf(stderr, "(\\r)");
+ } else if (c == 0x1b) {
+ fprintf(stderr, "(\\e)");
+ } else {
+ fprintf(stderr, "(%02x)", c);
+ }
+ }
+ fprintf(stderr, "ESC\\\n");
+}
+
+void
+strreset(void)
+{
+ memset(&strescseq, 0, sizeof(strescseq));
+}
+
+void
+sendbreak(const Arg *arg)
+{
+ if (tcsendbreak(cmdfd, 0))
+ perror("Error sending break");
+}
+
+void
+tprinter(char *s, size_t len)
+{
+ if (iofd != -1 && xwrite(iofd, s, len) < 0) {
+ fprintf(stderr, "Error writing in %s:%s\n",
+ opt_io, strerror(errno));
+ close(iofd);
+ iofd = -1;
+ }
+}
+
+void
+iso14755(const Arg *arg)
+{
+ char cmd[sizeof(ISO14755CMD) + NUMMAXLEN(xw.win)];
+ FILE *p;
+ char *us, *e, codepoint[9], uc[UTF_SIZ];
+ unsigned long utf32;
+
+ snprintf(cmd, sizeof(cmd), ISO14755CMD, xw.win);
+ if (!(p = popen(cmd, "r")))
+ return;
+
+ us = fgets(codepoint, sizeof(codepoint), p);
+ pclose(p);
+
+ if (!us || *us == '\0' || *us == '-' || strlen(us) > 7)
+ return;
+ if ((utf32 = strtoul(us, &e, 16)) == ULONG_MAX ||
+ (*e != '\n' && *e != '\0'))
+ return;
+
+ ttysend(uc, utf8encode(utf32, uc));
+}
+
+void
+toggleprinter(const Arg *arg)
+{
+ term.mode ^= MODE_PRINT;
+}
+
+void
+printscreen(const Arg *arg)
+{
+ tdump();
+}
+
+void
+printsel(const Arg *arg)
+{
+ tdumpsel();
+}
+
+void
+tdumpsel(void)
+{
+ char *ptr;
+
+ if ((ptr = getsel())) {
+ tprinter(ptr, strlen(ptr));
+ free(ptr);
+ }
+}
+
+void
+tdumpline(int n)
+{
+ char buf[UTF_SIZ];
+ Glyph *bp, *end;
+
+ bp = &term.line[n][0];
+ end = &bp[MIN(tlinelen(n), term.col) - 1];
+ if (bp != end || bp->u != ' ') {
+ for ( ;bp <= end; ++bp)
+ tprinter(buf, utf8encode(bp->u, buf));
+ }
+ tprinter("\n", 1);
+}
+
+void
+tdump(void)
+{
+ int i;
+
+ for (i = 0; i < term.row; ++i)
+ tdumpline(i);
+}
+
+void
+tputtab(int n)
+{
+ uint x = term.c.x;
+
+ if (n > 0) {
+ while (x < term.col && n--)
+ for (++x; x < term.col && !term.tabs[x]; ++x)
+ /* nothing */ ;
+ } else if (n < 0) {
+ while (x > 0 && n++)
+ for (--x; x > 0 && !term.tabs[x]; --x)
+ /* nothing */ ;
+ }
+ term.c.x = LIMIT(x, 0, term.col-1);
+}
+
+void
+techo(Rune u)
+{
+ if (ISCONTROL(u)) { /* control code */
+ if (u & 0x80) {
+ u &= 0x7f;
+ tputc('^');
+ tputc('[');
+ } else if (u != '\n' && u != '\r' && u != '\t') {
+ u ^= 0x40;
+ tputc('^');
+ }
+ }
+ tputc(u);
+}
+
+void
+tdefutf8(char ascii)
+{
+ if (ascii == 'G')
+ term.mode |= MODE_UTF8;
+ else if (ascii == '@')
+ term.mode &= ~MODE_UTF8;
+}
+
+void
+tdeftran(char ascii)
+{
+ static char cs[] = "0B";
+ static int vcs[] = {CS_GRAPHIC0, CS_USA};
+ char *p;
+
+ if ((p = strchr(cs, ascii)) == NULL) {
+ fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii);
+ } else {
+ term.trantbl[term.icharset] = vcs[p - cs];
+ }
+}
+
+void
+tdectest(char c)
+{
+ int x, y;
+
+ if (c == '8') { /* DEC screen alignment test. */
+ for (x = 0; x < term.col; ++x) {
+ for (y = 0; y < term.row; ++y)
+ tsetchar('E', &term.c.attr, x, y);
+ }
+ }
+}
+
+void
+tstrsequence(uchar c)
+{
+ strreset();
+
+ switch (c) {
+ case 0x90: /* DCS -- Device Control String */
+ c = 'P';
+ term.esc |= ESC_DCS;
+ break;
+ case 0x9f: /* APC -- Application Program Command */
+ c = '_';
+ break;
+ case 0x9e: /* PM -- Privacy Message */
+ c = '^';
+ break;
+ case 0x9d: /* OSC -- Operating System Command */
+ c = ']';
+ break;
+ }
+ strescseq.type = c;
+ term.esc |= ESC_STR;
+}
+
+void
+tcontrolcode(uchar ascii)
+{
+ 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('?', &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 0x80: /* TODO: PAD */
+ case 0x81: /* TODO: HOP */
+ case 0x82: /* TODO: BPH */
+ case 0x83: /* TODO: NBH */
+ case 0x84: /* TODO: IND */
+ break;
+ case 0x85: /* NEL -- Next line */
+ tnewline(1); /* always go to first col */
+ break;
+ case 0x86: /* TODO: SSA */
+ case 0x87: /* TODO: ESA */
+ break;
+ case 0x88: /* HTS -- Horizontal tab stop */
+ term.tabs[term.c.x] = 1;
+ break;
+ case 0x89: /* TODO: HTJ */
+ case 0x8a: /* TODO: VTS */
+ case 0x8b: /* TODO: PLD */
+ case 0x8c: /* TODO: PLU */
+ case 0x8d: /* TODO: RI */
+ case 0x8e: /* TODO: SS2 */
+ case 0x8f: /* TODO: SS3 */
+ case 0x91: /* TODO: PU1 */
+ case 0x92: /* TODO: PU2 */
+ case 0x93: /* TODO: STS */
+ case 0x94: /* TODO: CCH */
+ case 0x95: /* TODO: MW */
+ case 0x96: /* TODO: SPA */
+ case 0x97: /* TODO: EPA */
+ case 0x98: /* TODO: SOS */
+ case 0x99: /* TODO: SGCI */
+ 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 0x9d: /* OSC -- Operating System Command */
+ case 0x9e: /* PM -- Privacy Message */
+ case 0x9f: /* APC -- Application Program Command */
+ tstrsequence(ascii);
+ return;
+ }
+ /* only CAN, SUB, \a and C1 chars interrupt a sequence */
+ term.esc &= ~(ESC_STR_END|ESC_STR);
+}
+
+/*
+ * 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 '%':
+ term.esc |= ESC_UTF8;
+ 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);