]> git.armaanb.net Git - st.git/blobdiff - st.c
Add parsing of DCS q sequences
[st.git] / st.c
diff --git a/st.c b/st.c
index 486735d7a79cf21f6078dbea177ec7de69237658..6c1638651fe372e829ec57dd47e3e4a0fbb9f920 100644 (file)
--- a/st.c
+++ b/st.c
@@ -17,6 +17,7 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <termios.h>
 #include <time.h>
 #include <unistd.h>
 #include <libgen.h>
@@ -62,26 +63,29 @@ char *argv0;
 #define XK_SWITCH_MOD (1<<13)
 
 /* macros */
-#define MIN(a, b)  ((a) < (b) ? (a) : (b))
-#define MAX(a, b)  ((a) < (b) ? (b) : (a))
-#define LEN(a)     (sizeof(a) / sizeof(a)[0])
-#define DEFAULT(a, b)     (a) = (a) ? (a) : (b)
-#define BETWEEN(x, a, b)  ((a) <= (x) && (x) <= (b))
-#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177')
-#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
-#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
-#define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL)
-#define LIMIT(x, a, b)    (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
-#define IS_SET(flag) ((term.mode & (flag)) != 0)
-#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_nsec-t2.tv_nsec)/1E6)
-#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
-
-#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
-#define IS_TRUECOL(x)    (1 << 24 & (x))
-#define TRUERED(x)       (((x) & 0xff0000) >> 8)
-#define TRUEGREEN(x)     (((x) & 0xff00))
-#define TRUEBLUE(x)      (((x) & 0xff) << 8)
+#define MIN(a, b)              ((a) < (b) ? (a) : (b))
+#define MAX(a, b)              ((a) < (b) ? (b) : (a))
+#define LEN(a)                 (sizeof(a) / sizeof(a)[0])
+#define DEFAULT(a, b)          (a) = (a) ? (a) : (b)
+#define BETWEEN(x, a, b)       ((a) <= (x) && (x) <= (b))
+#define DIVCEIL(n, d)          (((n) + ((d) - 1)) / (d))
+#define ISCONTROLC0(c)         (BETWEEN(c, 0, 0x1f) || (c) == '\177')
+#define ISCONTROLC1(c)         (BETWEEN(c, 0x80, 0x9f))
+#define ISCONTROL(c)           (ISCONTROLC0(c) || ISCONTROLC1(c))
+#define ISDELIM(u)             (utf8strchr(worddelimiters, u) != NULL)
+#define LIMIT(x, a, b)         (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
+#define ATTRCMP(a, b)          ((a).mode != (b).mode || (a).fg != (b).fg || \
+                               (a).bg != (b).bg)
+#define IS_SET(flag)           ((term.mode & (flag)) != 0)
+#define TIMEDIFF(t1, t2)       ((t1.tv_sec-t2.tv_sec)*1000 + \
+                               (t1.tv_nsec-t2.tv_nsec)/1E6)
+#define MODBIT(x, set, bit)    ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+
+#define TRUECOLOR(r,g,b)       (1 << 24 | (r) << 16 | (g) << 8 | (b))
+#define IS_TRUECOL(x)          (1 << 24 & (x))
+#define TRUERED(x)             (((x) & 0xff0000) >> 8)
+#define TRUEGREEN(x)           (((x) & 0xff00))
+#define TRUEBLUE(x)            (((x) & 0xff) << 8)
 
 
 enum glyph_attribute {
@@ -133,6 +137,8 @@ enum term_mode {
        MODE_MOUSEMANY   = 1 << 18,
        MODE_BRCKTPASTE  = 1 << 19,
        MODE_PRINT       = 1 << 20,
+       MODE_UTF8        = 1 << 21,
+       MODE_SIXEL       = 1 << 22,
        MODE_MOUSE       = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
                          |MODE_MOUSEMANY,
 };
@@ -150,10 +156,12 @@ enum charset {
 enum escape_state {
        ESC_START      = 1,
        ESC_CSI        = 2,
-       ESC_STR        = 4,  /* DCS, OSC, PM, APC */
+       ESC_STR        = 4,  /* OSC, PM, APC */
        ESC_ALTCHARSET = 8,
        ESC_STR_END    = 16, /* a final string was encountered */
        ESC_TEST       = 32, /* Enter in test mode */
+       ESC_UTF8       = 64,
+       ESC_DCS        =128,
 };
 
 enum window_state {
@@ -272,7 +280,7 @@ typedef struct {
        uint b;
        uint mask;
        char *s;
-} Mousekey;
+} MouseShortcut;
 
 typedef struct {
        KeySym k;
@@ -331,6 +339,7 @@ static void xzoomreset(const Arg *);
 static void printsel(const Arg *);
 static void printscreen(const Arg *) ;
 static void toggleprinter(const Arg *);
+static void sendbreak(const Arg *);
 
 /* Config.h for applying patches and the configuration. */
 #include "config.h"
@@ -407,11 +416,12 @@ static void tfulldirt(void);
 static void techo(Rune);
 static void tcontrolcode(uchar );
 static void tdectest(char );
+static void tdefutf8(char);
 static int32_t tdefcolor(int *, int *, int);
 static void tdeftran(char);
 static inline int match(uint, uint);
 static void ttynew(void);
-static void ttyread(void);
+static size_t ttyread(void);
 static void ttyresize(void);
 static void ttysend(char *, size_t);
 static void ttywrite(const char *, size_t);
@@ -435,7 +445,6 @@ static void xresettitle(void);
 static void xsetpointermotion(int);
 static void xseturgency(int);
 static void xsetsel(char *, Time);
-static void xtermclear(int, int, int, int);
 static void xunloadfont(Font *);
 static void xunloadfonts(void);
 static void xresize(int, int);
@@ -519,14 +528,15 @@ static int cmdfd;
 static pid_t pid;
 static Selection sel;
 static int iofd = 1;
-static char **opt_cmd = NULL;
-static char *opt_io = NULL;
-static char *opt_title = NULL;
-static char *opt_embed = NULL;
+static char **opt_cmd  = NULL;
 static char *opt_class = NULL;
-static char *opt_font = NULL;
-static char *opt_line = NULL;
-static int oldbutton = 3; /* button event on startup: 3 = release */
+static char *opt_embed = NULL;
+static char *opt_font  = NULL;
+static char *opt_io    = NULL;
+static char *opt_line  = NULL;
+static char *opt_name  = NULL;
+static char *opt_title = NULL;
+static int oldbutton   = 3; /* button event on startup: 3 = release */
 
 static char *usedfont = NULL;
 static double usedfontsize = 0;
@@ -559,14 +569,16 @@ ssize_t
 xwrite(int fd, const char *s, size_t len)
 {
        size_t aux = len;
+       ssize_t r;
 
        while (len > 0) {
-               ssize_t r = write(fd, s, len);
+               r = write(fd, s, len);
                if (r < 0)
                        return r;
                len -= r;
                s += r;
        }
+
        return aux;
 }
 
@@ -620,6 +632,7 @@ utf8decode(char *c, Rune *u, size_t clen)
                return 0;
        *u = udecoded;
        utf8validate(u, len);
+
        return len;
 }
 
@@ -629,6 +642,7 @@ utf8decodebyte(char c, size_t *i)
        for (*i = 0; *i < LEN(utfmask); ++(*i))
                if (((uchar)c & utfmask[*i]) == utfbyte[*i])
                        return (uchar)c & ~utfmask[*i];
+
        return 0;
 }
 
@@ -640,11 +654,13 @@ utf8encode(Rune u, char *c)
        len = utf8validate(&u, 0);
        if (len > UTF_SIZ)
                return 0;
+
        for (i = len - 1; i != 0; --i) {
                c[i] = utf8encodebyte(u, 0);
                u >>= 6;
        }
        c[0] = utf8encodebyte(u, len);
+
        return len;
 }
 
@@ -667,6 +683,7 @@ utf8strchr(char *s, Rune u)
                if (r == u)
                        return &(s[i]);
        }
+
        return NULL;
 }
 
@@ -677,15 +694,17 @@ utf8validate(Rune *u, size_t i)
                *u = UTF_INVALID;
        for (i = 1; *u > utfmax[i]; ++i)
                ;
+
        return i;
 }
 
 void
 selinit(void)
 {
-       memset(&sel.tclick1, 0, sizeof(sel.tclick1));
-       memset(&sel.tclick2, 0, sizeof(sel.tclick2));
+       clock_gettime(CLOCK_MONOTONIC, &sel.tclick1);
+       clock_gettime(CLOCK_MONOTONIC, &sel.tclick2);
        sel.mode = SEL_IDLE;
+       sel.snap = 0;
        sel.ob.x = -1;
        sel.primary = NULL;
        sel.clipboard = NULL;
@@ -931,17 +950,17 @@ void
 bpress(XEvent *e)
 {
        struct timespec now;
-       Mousekey *mk;
+       MouseShortcut *ms;
 
        if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
                mousereport(e);
                return;
        }
 
-       for (mk = mshortcuts; mk < mshortcuts + LEN(mshortcuts); mk++) {
-               if (e->xbutton.button == mk->b
-                               && match(mk->mask, e->xbutton.state)) {
-                       ttysend(mk->s, strlen(mk->s));
+       for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
+               if (e->xbutton.button == ms->b
+                               && match(ms->mask, e->xbutton.state)) {
+                       ttysend(ms->s, strlen(ms->s));
                        return;
                }
        }
@@ -992,7 +1011,10 @@ getsel(void)
 
        /* append every set & selected glyph to the selection */
        for (y = sel.nb.y; y <= sel.ne.y; y++) {
-               linelen = tlinelen(y);
+               if ((linelen = tlinelen(y)) == 0) {
+                       *ptr++ = '\n';
+                       continue;
+               }
 
                if (sel.type == SEL_RECTANGULAR) {
                        gp = &term.line[y][sel.nb.x];
@@ -1120,10 +1142,10 @@ selnotify(XEvent *e)
                        *repl++ = '\r';
                }
 
-               if (IS_SET(MODE_BRCKTPASTE))
+               if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
                        ttywrite("\033[200~", 6);
                ttysend((char *)data, nitems * format / 8);
-               if (IS_SET(MODE_BRCKTPASTE))
+               if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
                        ttywrite("\033[201~", 6);
                XFree(data);
                /* number of 32-bit chunks returned */
@@ -1134,8 +1156,7 @@ selnotify(XEvent *e)
         * Deleting the property again tells the selection owner to send the
         * next data chunk in the property.
         */
-       if (e->type == PropertyNotify)
-               XDeleteProperty(xw.dpy, xw.win, (int)property);
+       XDeleteProperty(xw.dpy, xw.win, (int)property);
 }
 
 void
@@ -1320,9 +1341,8 @@ execsh(void)
                        die("who are you?\n");
        }
 
-       if (!(sh = getenv("SHELL"))) {
+       if ((sh = getenv("SHELL")) == NULL)
                sh = (pw->pw_shell[0]) ? pw->pw_shell : shell;
-       }
 
        if (opt_cmd)
                prog = opt_cmd[0];
@@ -1388,9 +1408,9 @@ stty(void)
                if ((n = strlen(s)) > siz-1)
                        die("stty parameter length too long\n");
                *q++ = ' ';
-               q = memcpy(q, s, n);
+               memcpy(q, s, n);
                q += n;
-               siz-= n + 1;
+               siz -= n + 1;
        }
        *q = '\0';
        if (system(cmd) != 0)
@@ -1416,8 +1436,7 @@ ttynew(void)
        if (opt_line) {
                if ((cmdfd = open(opt_line, O_RDWR)) < 0)
                        die("open line failed: %s\n", strerror(errno));
-               close(0);
-               dup(cmdfd);
+               dup2(cmdfd, 0);
                stty();
                return;
        }
@@ -1450,7 +1469,7 @@ ttynew(void)
        }
 }
 
-void
+size_t
 ttyread(void)
 {
        static char buf[BUFSIZ];
@@ -1464,25 +1483,39 @@ ttyread(void)
        if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0)
                die("Couldn't read from shell: %s\n", strerror(errno));
 
-       /* process every complete utf8 char */
        buflen += ret;
        ptr = buf;
-       while ((charsize = utf8decode(ptr, &unicodep, buflen))) {
-               tputc(unicodep);
-               ptr += charsize;
-               buflen -= charsize;
-       }
 
+       for (;;) {
+               if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
+                       /* process a complete utf8 char */
+                       charsize = utf8decode(ptr, &unicodep, buflen);
+                       if (charsize == 0)
+                               break;
+                       tputc(unicodep);
+                       ptr += charsize;
+                       buflen -= charsize;
+
+               } else {
+                       if (buflen <= 0)
+                               break;
+                       tputc(*ptr++ & 0xFF);
+                       buflen--;
+               }
+       }
        /* keep any uncomplete utf8 char for the next call */
-       memmove(buf, ptr, buflen);
+       if (buflen > 0)
+               memmove(buf, ptr, buflen);
+
+       return ret;
 }
 
 void
 ttywrite(const char *s, size_t n)
 {
-       fd_set wfd;
-       struct timespec tv;
+       fd_set wfd, rfd;
        ssize_t r;
+       size_t lim = 256;
 
        /*
         * Remember that we are using a pty, which might be a modem line.
@@ -1492,37 +1525,32 @@ ttywrite(const char *s, size_t n)
         */
        while (n > 0) {
                FD_ZERO(&wfd);
+               FD_ZERO(&rfd);
                FD_SET(cmdfd, &wfd);
-               tv.tv_sec = 0;
-               tv.tv_nsec = 0;
+               FD_SET(cmdfd, &rfd);
 
                /* Check if we can write. */
-               if (pselect(cmdfd+1, NULL, &wfd, NULL, &tv, NULL) < 0) {
+               if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) {
                        if (errno == EINTR)
                                continue;
                        die("select failed: %s\n", strerror(errno));
                }
-               if(!FD_ISSET(cmdfd, &wfd)) {
-                       /* No, then free some buffer space. */
-                       ttyread();
-               } else {
+               if (FD_ISSET(cmdfd, &wfd)) {
                        /*
-                        * Only write 256 bytes at maximum. This seems to be a
-                        * reasonable value for a serial line. Bigger values
-                        * might clog the I/O.
+                        * Only write the bytes written by ttywrite() or the
+                        * default of 256. This seems to be a reasonable value
+                        * for a serial line. Bigger values might clog the I/O.
                         */
-                       r = write(cmdfd, s, (n < 256)? n : 256);
-                       if (r < 0) {
-                               die("write error on tty: %s\n",
-                                               strerror(errno));
-                       }
+                       if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0)
+                               goto write_error;
                        if (r < n) {
                                /*
                                 * We weren't able to write out everything.
                                 * This means the buffer is getting full
                                 * again. Empty it.
                                 */
-                               ttyread();
+                               if (n < lim)
+                                       lim = ttyread();
                                n -= r;
                                s += r;
                        } else {
@@ -1530,22 +1558,39 @@ ttywrite(const char *s, size_t n)
                                break;
                        }
                }
+               if (FD_ISSET(cmdfd, &rfd))
+                       lim = ttyread();
        }
+       return;
+
+write_error:
+       die("write error on tty: %s\n", strerror(errno));
 }
 
 void
 ttysend(char *s, size_t n)
 {
        int len;
+       char *t, *lim;
        Rune u;
 
        ttywrite(s, n);
-       if (IS_SET(MODE_ECHO))
-               while ((len = utf8decode(s, &u, n)) > 0) {
-                       techo(u);
-                       n -= len;
-                       s += len;
+       if (!IS_SET(MODE_ECHO))
+               return;
+
+       lim = &s[n];
+       for (t = s; t < lim; t += len) {
+               if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
+                       len = utf8decode(t, &u, n);
+               } else {
+                       u = *t & 0xFF;
+                       len = 1;
                }
+               if (len <= 0)
+                       break;
+               techo(u);
+               n -= len;
+       }
 }
 
 void
@@ -1639,7 +1684,7 @@ treset(void)
                term.tabs[i] = 1;
        term.top = 0;
        term.bot = term.row - 1;
-       term.mode = MODE_WRAP;
+       term.mode = MODE_WRAP|MODE_UTF8;
        memset(term.trantbl, CS_USA, sizeof(term.trantbl));
        term.charset = 0;
 
@@ -2419,15 +2464,15 @@ csihandle(void)
                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:
+               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;
        }
@@ -2505,6 +2550,7 @@ strhandle(void)
                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;
@@ -2568,6 +2614,13 @@ 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)
 {
@@ -2665,6 +2718,15 @@ techo(Rune u)
        tputc(u);
 }
 
+void
+tdefutf8(char ascii)
+{
+       if (ascii == 'G')
+               term.mode |= MODE_UTF8;
+       else if (ascii == '@')
+               term.mode &= ~MODE_UTF8;
+}
+
 void
 tdeftran(char ascii)
 {
@@ -2695,9 +2757,12 @@ tdectest(char c)
 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 = '_';
@@ -2709,7 +2774,6 @@ tstrsequence(uchar c)
                c = ']';
                break;
        }
-       strreset();
        strescseq.type = c;
        term.esc |= ESC_STR;
 }
@@ -2764,18 +2828,37 @@ tcontrolcode(uchar ascii)
        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);
@@ -2784,9 +2867,9 @@ tcontrolcode(uchar ascii)
        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 */
+       case 0x9e:   /* PM -- Privacy Message */
+       case 0x9f:   /* APC -- Application Program Command */
                tstrsequence(ascii);
                return;
        }
@@ -2808,6 +2891,9 @@ eschandle(uchar ascii)
        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 */
@@ -2886,15 +2972,20 @@ tputc(Rune u)
        int width, len;
        Glyph *gp;
 
-       len = utf8encode(u, c);
-       if ((width = wcwidth(u)) == -1) {
-               memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
-               width = 1;
+       control = ISCONTROL(u);
+       if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) {
+               c[0] = u;
+               width = len = 1;
+       } else {
+               len = utf8encode(u, c);
+               if (!control && (width = wcwidth(u)) == -1) {
+                       memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
+                       width = 1;
+               }
        }
 
        if (IS_SET(MODE_PRINT))
                tprinter(c, len);
-       control = ISCONTROL(u);
 
        /*
         * STR sequence must be checked before anything else
@@ -2905,30 +2996,47 @@ tputc(Rune u)
        if (term.esc & ESC_STR) {
                if (u == '\a' || u == 030 || u == 032 || u == 033 ||
                   ISCONTROLC1(u)) {
-                       term.esc &= ~(ESC_START|ESC_STR);
+                       term.esc &= ~(ESC_START|ESC_STR|ESC_DCS);
+                       if (IS_SET(MODE_SIXEL)) {
+                               /* TODO: render sixel */;
+                               term.mode &= ~MODE_SIXEL;
+                               return;
+                       }
                        term.esc |= ESC_STR_END;
-               } else if (strescseq.len + len < sizeof(strescseq.buf) - 1) {
-                       memmove(&strescseq.buf[strescseq.len], c, len);
-                       strescseq.len += len;
+                       goto check_control_code;
+               }
+
+
+               if (IS_SET(MODE_SIXEL)) {
+                       /* TODO: implement sixel mode */
                        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();
-                */
+               }
+               if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q')
+                       term.mode |= MODE_SIXEL;
+
+               if (strescseq.len+len >= sizeof(strescseq.buf)-1) {
+                       /*
+                        * 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;
                }
+
+               memmove(&strescseq.buf[strescseq.len], c, len);
+               strescseq.len += len;
+               return;
        }
 
+check_control_code:
        /*
         * Actions of control codes must be performed as soon they arrive
         * because they can be embedded inside a control sequence, and
@@ -2951,6 +3059,8 @@ tputc(Rune u)
                                csihandle();
                        }
                        return;
+               } else if (term.esc & ESC_UTF8) {
+                       tdefutf8(u);
                } else if (term.esc & ESC_ALTCHARSET) {
                        tdeftran(u);
                } else if (term.esc & ESC_TEST) {
@@ -3125,6 +3235,7 @@ xloadcolor(int i, const char *name, Color *ncolor)
                } else
                        name = colorname[i];
        }
+
        return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
 }
 
@@ -3164,18 +3275,8 @@ xsetcolorname(int x, const char *name)
 
        XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
        dc.col[x] = ncolor;
-       return 0;
-}
 
-void
-xtermclear(int col1, int row1, int col2, int row2)
-{
-       XftDrawRect(xw.draw,
-                       &dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg],
-                       borderpx + col1 * xw.cw,
-                       borderpx + row1 * xw.ch,
-                       (col2-col1+1) * xw.cw,
-                       (row2-row1+1) * xw.ch);
+       return 0;
 }
 
 /*
@@ -3192,7 +3293,8 @@ xclear(int x1, int y1, int x2, int y2)
 void
 xhints(void)
 {
-       XClassHint class = {opt_class ? opt_class : termname, termname};
+       XClassHint class = {opt_name ? opt_name : termname,
+                           opt_class ? opt_class : termname};
        XWMHints wm = {.flags = InputHint, .input = 1};
        XSizeHints *sizeh = NULL;
 
@@ -3233,6 +3335,7 @@ xgeommasktogravity(int mask)
        case YNegative:
                return SouthWestGravity;
        }
+
        return SouthEastGravity;
 }
 
@@ -3241,8 +3344,9 @@ xloadfont(Font *f, FcPattern *pattern)
 {
        FcPattern *match;
        FcResult result;
+       XGlyphInfo extents;
 
-       match = FcFontMatch(NULL, pattern, &result);
+       match = XftFontMatch(xw.dpy, xw.scr, pattern, &result);
        if (!match)
                return 1;
 
@@ -3251,6 +3355,10 @@ xloadfont(Font *f, FcPattern *pattern)
                return 1;
        }
 
+       XftTextExtentsUtf8(xw.dpy, f->match,
+               (const FcChar8 *) ascii_printable,
+               strlen(ascii_printable), &extents);
+
        f->set = NULL;
        f->pattern = FcPatternDuplicate(pattern);
 
@@ -3260,7 +3368,7 @@ xloadfont(Font *f, FcPattern *pattern)
        f->rbearing = f->match->max_advance_width;
 
        f->height = f->ascent + f->descent;
-       f->width = f->lbearing + f->rbearing;
+       f->width = DIVCEIL(extents.xOff, strlen(ascii_printable));
 
        return 0;
 }
@@ -3304,9 +3412,6 @@ xloadfonts(char *fontstr, double fontsize)
                defaultfontsize = usedfontsize;
        }
 
-       FcConfigSubstitute(0, pattern, FcMatchPattern);
-       FcDefaultSubstitute(pattern);
-
        if (xloadfont(&dc.font, pattern))
                die("st: can't open font %s\n", fontstr);
 
@@ -3377,6 +3482,7 @@ xzoomabs(const Arg *arg)
        xunloadfonts();
        xloadfonts(usedfont, arg->f);
        cresize(0, 0);
+       ttyresize();
        redraw();
        xhints();
 }
@@ -3399,6 +3505,7 @@ xinit(void)
        Cursor cursor;
        Window parent;
        pid_t thispid = getpid();
+       XColor xmousefg, xmousebg;
 
        if (!(xw.dpy = XOpenDisplay(NULL)))
                die("Can't open display\n");
@@ -3422,7 +3529,7 @@ xinit(void)
        if (xw.gm & XNegative)
                xw.l += DisplayWidth(xw.dpy, xw.scr) - xw.w - 2;
        if (xw.gm & YNegative)
-               xw.t += DisplayWidth(xw.dpy, xw.scr) - xw.h - 2;
+               xw.t += DisplayHeight(xw.dpy, xw.scr) - xw.h - 2;
 
        /* Events */
        xw.attrs.background_pixel = dc.col[defaultbg].pixel;
@@ -3471,11 +3578,22 @@ xinit(void)
                die("XCreateIC failed. Could not obtain input method.\n");
 
        /* white cursor, black outline */
-       cursor = XCreateFontCursor(xw.dpy, XC_xterm);
+       cursor = XCreateFontCursor(xw.dpy, mouseshape);
        XDefineCursor(xw.dpy, xw.win, cursor);
-       XRecolorCursor(xw.dpy, cursor,
-               &(XColor){.red = 0xffff, .green = 0xffff, .blue = 0xffff},
-               &(XColor){.red = 0x0000, .green = 0x0000, .blue = 0x0000});
+
+       if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) {
+               xmousefg.red   = 0xffff;
+               xmousefg.green = 0xffff;
+               xmousefg.blue  = 0xffff;
+       }
+
+       if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) {
+               xmousebg.red   = 0x0000;
+               xmousebg.green = 0x0000;
+               xmousebg.blue  = 0x0000;
+       }
+
+       XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
 
        xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
        xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
@@ -3616,7 +3734,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
                specs[numspecs].font = frc[f].font;
                specs[numspecs].glyph = glyphidx;
                specs[numspecs].x = (short)xp;
-               specs[numspecs].y = (short)(winy + frc[f].font->ascent);
+               specs[numspecs].y = (short)yp;
                xp += runewidth;
                numspecs++;
        }
@@ -3763,6 +3881,7 @@ xdrawglyph(Glyph g, int x, int y)
 {
        int numspecs;
        XftGlyphFontSpec spec;
+
        numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
        xdrawglyphfontspecs(&spec, g, numspecs, x, y);
 }
@@ -3772,7 +3891,9 @@ xdrawcursor(void)
 {
        static int oldx = 0, oldy = 0;
        int curx;
-       Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs};
+       Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og;
+       int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
+       Color drawcol;
 
        LIMIT(oldx, 0, term.col-1);
        LIMIT(oldy, 0, term.row-1);
@@ -3785,10 +3906,36 @@ xdrawcursor(void)
        if (term.line[term.c.y][curx].mode & ATTR_WDUMMY)
                curx--;
 
+       /* remove the old cursor */
+       og = term.line[oldy][oldx];
+       if (ena_sel && selected(oldx, oldy))
+               og.mode ^= ATTR_REVERSE;
+       xdrawglyph(og, oldx, oldy);
+
        g.u = term.line[term.c.y][term.c.x].u;
 
-       /* remove the old cursor */
-       xdrawglyph(term.line[oldy][oldx], oldx, oldy);
+       /*
+        * Select the right color for the right mode.
+        */
+       if (IS_SET(MODE_REVERSE)) {
+               g.mode |= ATTR_REVERSE;
+               g.bg = defaultfg;
+               if (ena_sel && selected(term.c.x, term.c.y)) {
+                       drawcol = dc.col[defaultcs];
+                       g.fg = defaultrcs;
+               } else {
+                       drawcol = dc.col[defaultrcs];
+                       g.fg = defaultcs;
+               }
+       } else {
+               if (ena_sel && selected(term.c.x, term.c.y)) {
+                       drawcol = dc.col[defaultrcs];
+                       g.fg = defaultfg;
+                       g.bg = defaultrcs;
+               } else {
+                       drawcol = dc.col[defaultcs];
+               }
+       }
 
        if (IS_SET(MODE_HIDE))
                return;
@@ -3796,47 +3943,44 @@ xdrawcursor(void)
        /* draw the new one */
        if (xw.state & WIN_FOCUSED) {
                switch (xw.cursor) {
-                       case 0: /* Blinking Block */
-                       case 1: /* Blinking Block (Default) */
-                       case 2: /* Steady Block */
-                               if (IS_SET(MODE_REVERSE)) {
-                                               g.mode |= ATTR_REVERSE;
-                                               g.fg = defaultcs;
-                                               g.bg = defaultfg;
-                                       }
-
-                               g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE;
-                               xdrawglyph(g, term.c.x, term.c.y);
-                               break;
-                       case 3: /* Blinking Underline */
-                       case 4: /* Steady Underline */
-                               XftDrawRect(xw.draw, &dc.col[defaultcs],
-                                               borderpx + curx * xw.cw,
-                                               borderpx + (term.c.y + 1) * xw.ch - cursorthickness,
-                                               xw.cw, cursorthickness);
-                               break;
-                       case 5: /* Blinking bar */
-                       case 6: /* Steady bar */
-                               XftDrawRect(xw.draw, &dc.col[defaultcs],
-                                               borderpx + curx * xw.cw,
-                                               borderpx + term.c.y * xw.ch,
-                                               cursorthickness, xw.ch);
-                               break;
+               case 7: /* st extension: snowman */
+                       utf8decode("☃", &g.u, UTF_SIZ);
+               case 0: /* Blinking Block */
+               case 1: /* Blinking Block (Default) */
+               case 2: /* Steady Block */
+                       g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE;
+                       xdrawglyph(g, term.c.x, term.c.y);
+                       break;
+               case 3: /* Blinking Underline */
+               case 4: /* Steady Underline */
+                       XftDrawRect(xw.draw, &drawcol,
+                                       borderpx + curx * xw.cw,
+                                       borderpx + (term.c.y + 1) * xw.ch - \
+                                               cursorthickness,
+                                       xw.cw, cursorthickness);
+                       break;
+               case 5: /* Blinking bar */
+               case 6: /* Steady bar */
+                       XftDrawRect(xw.draw, &drawcol,
+                                       borderpx + curx * xw.cw,
+                                       borderpx + term.c.y * xw.ch,
+                                       cursorthickness, xw.ch);
+                       break;
                }
        } else {
-               XftDrawRect(xw.draw, &dc.col[defaultcs],
+               XftDrawRect(xw.draw, &drawcol,
                                borderpx + curx * xw.cw,
                                borderpx + term.c.y * xw.ch,
                                xw.cw - 1, 1);
-               XftDrawRect(xw.draw, &dc.col[defaultcs],
+               XftDrawRect(xw.draw, &drawcol,
                                borderpx + curx * xw.cw,
                                borderpx + term.c.y * xw.ch,
                                1, xw.ch - 1);
-               XftDrawRect(xw.draw, &dc.col[defaultcs],
+               XftDrawRect(xw.draw, &drawcol,
                                borderpx + (curx + 1) * xw.cw - 1,
                                borderpx + term.c.y * xw.ch,
                                1, xw.ch - 1);
-               XftDrawRect(xw.draw, &dc.col[defaultcs],
+               XftDrawRect(xw.draw, &drawcol,
                                borderpx + curx * xw.cw,
                                borderpx + (term.c.y + 1) * xw.ch - 1,
                                xw.cw, 1);
@@ -3886,7 +4030,7 @@ drawregion(int x1, int y1, int x2, int y2)
 {
        int i, x, y, ox, numspecs;
        Glyph base, new;
-       XftGlyphFontSpecspecs;
+       XftGlyphFontSpec *specs;
        int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
 
        if (!(xw.state & WIN_VISIBLE))
@@ -3896,7 +4040,6 @@ drawregion(int x1, int y1, int x2, int y2)
                if (!term.dirty[y])
                        continue;
 
-               xtermclear(0, y, term.col, y);
                term.dirty[y] = 0;
 
                specs = term.specbuf;
@@ -4122,7 +4265,6 @@ cresize(int width, int height)
 
        tresize(col, row);
        xresize(col, row);
-       ttyresize();
 }
 
 void
@@ -4132,6 +4274,7 @@ resize(XEvent *e)
                return;
 
        cresize(e->xconfigure.width, e->xconfigure.height);
+       ttyresize();
 }
 
 void
@@ -4148,9 +4291,9 @@ run(void)
        do {
                XNextEvent(xw.dpy, &ev);
                /*
-                * XFilterEvent is required to be called after you using XOpenIM,
-                * this is not unnecessary.It does not only filter the key event,
-                * but some clientmessage for input method as well.
+                * This XFilterEvent call is required because of XOpenIM. It
+                * does filter out the key event and some client message for
+                * the input method too.
                 */
                if (XFilterEvent(&ev, None))
                        continue;
@@ -4160,8 +4303,9 @@ run(void)
                }
        } while (ev.type != MapNotify);
 
-       ttynew();
        cresize(w, h);
+       ttynew();
+       ttyresize();
 
        clock_gettime(CLOCK_MONOTONIC, &last);
        lastblink = last;
@@ -4245,12 +4389,14 @@ run(void)
 void
 usage(void)
 {
-       die("%s " VERSION " (c) 2010-2015 st engineers\n"
-       "usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]\n"
-       "          [-i] [-t title] [-w windowid] [-e command ...] [command ...]\n"
-       "       st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]\n"
-       "          [-i] [-t title] [-w windowid] [-l line] [stty_args ...]\n",
-       argv0);
+       die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]"
+           " [-n name] [-o file]\n"
+           "          [-T title] [-t title] [-w windowid]"
+           " [[-e] command [args ...]]\n"
+           "       %s [-aiv] [-c class] [-f font] [-g geometry]"
+           " [-n name] [-o file]\n"
+           "          [-T title] [-t title] [-w windowid] -l line"
+           " [stty_args ...]\n", argv0, argv0);
 }
 
 int
@@ -4260,7 +4406,7 @@ main(int argc, char *argv[])
 
        xw.l = xw.t = 0;
        xw.isfixed = False;
-       xw.cursor = 0;
+       xw.cursor = cursorshape;
 
        ARGBEGIN {
        case 'a':
@@ -4289,13 +4435,19 @@ main(int argc, char *argv[])
        case 'l':
                opt_line = EARGF(usage());
                break;
+       case 'n':
+               opt_name = EARGF(usage());
+               break;
        case 't':
+       case 'T':
                opt_title = EARGF(usage());
                break;
        case 'w':
                opt_embed = EARGF(usage());
                break;
        case 'v':
+               die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0);
+               break;
        default:
                usage();
        } ARGEND;
@@ -4316,3 +4468,4 @@ run:
 
        return 0;
 }
+