JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
reset the alt screen in treset
[st.git] / st.c
diff --git a/st.c b/st.c
index baab589..0e228a7 100644 (file)
--- a/st.c
+++ b/st.c
@@ -76,8 +76,7 @@ char *argv0;
 #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_usec-t2.tv_usec)/1000)
-#define CEIL(x) (((x) != (int) (x)) ? (x) + 1 : (x))
+#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))
@@ -87,18 +86,19 @@ char *argv0;
 #define TRUEBLUE(x)      (((x) & 0xff) << 8)
 
 
-#define VT102ID "\033[?6c"
-
 enum glyph_attribute {
        ATTR_NULL      = 0,
-       ATTR_REVERSE   = 1,
-       ATTR_UNDERLINE = 2,
-       ATTR_BOLD      = 4,
-       ATTR_ITALIC    = 8,
+       ATTR_BOLD      = 1,
+       ATTR_FAINT     = 2,
+       ATTR_ITALIC    = 4,
+       ATTR_UNDERLINE = 8,
        ATTR_BLINK     = 16,
-       ATTR_WRAP      = 32,
-       ATTR_WIDE      = 64,
-       ATTR_WDUMMY    = 128,
+       ATTR_REVERSE   = 32,
+       ATTR_INVISIBLE = 64,
+       ATTR_STRUCK    = 128,
+       ATTR_WRAP      = 256,
+       ATTR_WIDE      = 512,
+       ATTR_WDUMMY    = 1024,
 };
 
 enum cursor_movement {
@@ -179,8 +179,7 @@ typedef unsigned long ulong;
 typedef unsigned short ushort;
 
 typedef XftDraw *Draw;
-typedef XftColor Colour;
-typedef Colormap Colourmap;
+typedef XftColor Color;
 
 typedef struct {
        char c[UTF_SIZ]; /* character code */
@@ -241,7 +240,7 @@ typedef struct {
 /* Purely graphic info */
 typedef struct {
        Display *dpy;
-       Colourmap cmap;
+       Colormap cmap;
        Window win;
        Drawable buf;
        Atom xembed, wmdeletewin, netwmname, netwmpid;
@@ -295,8 +294,8 @@ typedef struct {
        char *clip;
        Atom xtarget;
        bool alt;
-       struct timeval tclick1;
-       struct timeval tclick2;
+       struct timespec tclick1;
+       struct timespec tclick2;
 } Selection;
 
 typedef union {
@@ -340,7 +339,7 @@ typedef struct {
 
 /* Drawing Context */
 typedef struct {
-       Colour col[LEN(colorname) < 256 ? 256 : LEN(colorname)];
+       Color col[MAX(LEN(colorname), 256)];
        Font font, bfont, ifont, ibfont;
        GC gc;
 } DC;
@@ -373,6 +372,7 @@ static void tdeletechar(int);
 static void tdeleteline(int);
 static void tinsertblank(int);
 static void tinsertblankline(int);
+static int tlinelen(int);
 static void tmoveto(int, int);
 static void tmoveato(int, int);
 static void tnew(int, int);
@@ -380,7 +380,7 @@ static void tnewline(int);
 static void tputtab(int);
 static void tputc(char *, int);
 static void treset(void);
-static int tresize(int, int);
+static void tresize(int, int);
 static void tscrollup(int, int);
 static void tscrolldown(int, int);
 static void tsetattr(int *, int);
@@ -402,6 +402,7 @@ static void ttyread(void);
 static void ttyresize(void);
 static void ttysend(char *, size_t);
 static void ttywrite(const char *, size_t);
+static void tstrsequence(uchar c);
 
 static void xdraws(char *, Glyph, int, int, int, int);
 static void xhints(void);
@@ -441,7 +442,7 @@ static void selclear(XEvent *);
 static void selrequest(XEvent *);
 
 static void selinit(void);
-static void selsort(void);
+static void selnormalize(void);
 static inline bool selected(int, int);
 static char *getsel(void);
 static void selcopy(void);
@@ -657,9 +658,20 @@ y2row(int y) {
        return LIMIT(y, 0, term.row-1);
 }
 
+static int tlinelen(int y) {
+       int i = term.col;
+
+       while (i > 0 && term.line[y][i - 1].c[0] == ' ')
+               --i;
+
+       return i;
+}
+
 static void
-selsort(void) {
-       if(sel.ob.y == sel.oe.y) {
+selnormalize(void) {
+       int i;
+
+       if(sel.ob.y == sel.oe.y || sel.type == SEL_RECTANGULAR) {
                sel.nb.x = MIN(sel.ob.x, sel.oe.x);
                sel.ne.x = MAX(sel.ob.x, sel.oe.x);
        } else {
@@ -668,6 +680,15 @@ selsort(void) {
        }
        sel.nb.y = MIN(sel.ob.y, sel.oe.y);
        sel.ne.y = MAX(sel.ob.y, sel.oe.y);
+
+       /* expand selection over line breaks */
+       if (sel.type == SEL_RECTANGULAR)
+               return;
+       i = tlinelen(sel.nb.y);
+       if (i < sel.nb.x)
+               sel.nb.x = i;
+       if (tlinelen(sel.ne.y) <= sel.ne.x)
+               sel.ne.x = term.col - 1;
 }
 
 static inline bool
@@ -683,7 +704,8 @@ selected(int x, int y) {
 
 void
 selsnap(int mode, int *x, int *y, int direction) {
-       int i;
+       int newx, newy, xt, yt;
+       Glyph *gp;
 
        switch(mode) {
        case SNAP_WORD:
@@ -692,36 +714,31 @@ selsnap(int mode, int *x, int *y, int direction) {
                 * beginning of a line.
                 */
                for(;;) {
-                       if(direction < 0 && *x <= 0) {
-                               if(*y > 0 && term.line[*y - 1][term.col-1].mode
-                                               & ATTR_WRAP) {
-                                       *y -= 1;
-                                       *x = term.col-1;
-                               } else {
+                       newx = *x + direction;
+                       newy = *y;
+                       if(!BETWEEN(newx, 0, term.col - 1)) {
+                               newy += direction;
+                               newx = (newx + term.col) % term.col;
+                               if (!BETWEEN(newy, 0, term.row - 1))
                                        break;
-                               }
-                       }
-                       if(direction > 0 && *x >= term.col-1) {
-                               if(*y < term.row-1 && term.line[*y][*x].mode
-                                               & ATTR_WRAP) {
-                                       *y += 1;
-                                       *x = 0;
-                               } else {
+
+                               if(direction > 0)
+                                       yt = *y, xt = *x;
+                               else
+                                       yt = newy, xt = newx;
+                               if(!(term.line[yt][xt].mode & ATTR_WRAP))
                                        break;
-                               }
                        }
 
-                       if(term.line[*y][*x+direction].mode & ATTR_WDUMMY) {
-                               *x += direction;
-                               continue;
-                       }
+                       if (newx >= tlinelen(newy))
+                               break;
 
-                       if(strchr(worddelimiters,
-                                       term.line[*y][*x+direction].c[0])) {
+                       gp = &term.line[newy][newx];
+                       if (!(gp->mode & ATTR_WDUMMY) && strchr(worddelimiters, gp->c[0]))
                                break;
-                       }
 
-                       *x += direction;
+                       *x = newx;
+                       *y = newy;
                }
                break;
        case SNAP_LINE:
@@ -747,25 +764,13 @@ selsnap(int mode, int *x, int *y, int direction) {
                        }
                }
                break;
-       default:
-               /*
-                * Select the whole line when the end of line is reached.
-                */
-               if(direction > 0) {
-                       i = term.col;
-                       while(--i > 0 && term.line[*y][i].c[0] == ' ')
-                               /* nothing */;
-                       if(i > 0 && i < *x)
-                               *x = term.col - 1;
-               }
-               break;
        }
 }
 
 void
 getbuttoninfo(XEvent *e) {
        int type;
-       uint state = e->xbutton.state &~Button1Mask;
+       uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
 
        sel.alt = IS_SET(MODE_ALTSCREEN);
 
@@ -780,7 +785,7 @@ getbuttoninfo(XEvent *e) {
                selsnap(sel.snap, &sel.oe.x, &sel.oe.y, -1);
                selsnap(sel.snap, &sel.ob.x, &sel.ob.y, +1);
        }
-       selsort();
+       selnormalize();
 
        sel.type = SEL_REGULAR;
        for(type = 1; type < LEN(selmasks); ++type) {
@@ -829,6 +834,8 @@ mousereport(XEvent *e) {
                        /* MODE_MOUSEX10: no button release reporting */
                        if(IS_SET(MODE_MOUSEX10))
                                return;
+                       if (button == 64 || button == 65)
+                               return;
                }
        }
 
@@ -855,10 +862,10 @@ mousereport(XEvent *e) {
 
 void
 bpress(XEvent *e) {
-       struct timeval now;
+       struct timespec now;
        Mousekey *mk;
 
-       if(IS_SET(MODE_MOUSE)) {
+       if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
                mousereport(e);
                return;
        }
@@ -872,7 +879,7 @@ bpress(XEvent *e) {
        }
 
        if(e->xbutton.button == Button1) {
-               gettimeofday(&now, NULL);
+               clock_gettime(CLOCK_MONOTONIC, &now);
 
                /* Clear previous selection, logically and visually. */
                selclear(NULL);
@@ -894,7 +901,7 @@ bpress(XEvent *e) {
                }
                selsnap(sel.snap, &sel.ob.x, &sel.ob.y, -1);
                selsnap(sel.snap, &sel.oe.x, &sel.oe.y, +1);
-               selsort();
+               selnormalize();
 
                /*
                 * Draw selection, unless it's regular and we don't want to
@@ -912,7 +919,7 @@ bpress(XEvent *e) {
 char *
 getsel(void) {
        char *str, *ptr;
-       int x, y, bufsize, size, i, ex;
+       int y, bufsize, size, lastx, linelen;
        Glyph *gp, *last;
 
        if(sel.ob.x == -1)
@@ -923,16 +930,19 @@ getsel(void) {
 
        /* append every set & selected glyph to the selection */
        for(y = sel.nb.y; y < sel.ne.y + 1; y++) {
-               gp = &term.line[y][0];
-               last = &gp[term.col-1];
+               linelen = tlinelen(y);
 
-               while(last >= gp && !(selected(last - gp, y) &&
-                                     strcmp(last->c, " ") != 0)) {
-                       --last;
+               if(sel.type == SEL_RECTANGULAR) {
+                       gp = &term.line[y][sel.nb.x];
+                       lastx = sel.ne.x;
+               } else {
+                       gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
+                       lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
                }
+               last = &term.line[y][MIN(lastx, linelen-1)];
 
-               for(x = 0; gp <= last; x++, ++gp) {
-                       if(!selected(x, y) || (gp->mode & ATTR_WDUMMY))
+               for( ; gp <= last; ++gp) {
+                       if(gp->mode & ATTR_WDUMMY)
                                continue;
 
                        size = utf8len(gp->c);
@@ -949,23 +959,8 @@ getsel(void) {
                 * st.
                 * FIXME: Fix the computer world.
                 */
-               if(y < sel.ne.y && x > 0 && !((gp-1)->mode & ATTR_WRAP))
+               if(sel.ne.y > y || lastx >= linelen)
                        *ptr++ = '\n';
-
-               /*
-                * If the last selected line expands in the selection
-                * after the visible text '\n' is appended.
-                */
-               if(y == sel.ne.y) {
-                       i = term.col;
-                       while(--i > 0 && term.line[y][i].c[0] == ' ')
-                               /* nothing */;
-                       ex = sel.ne.x;
-                       if(sel.nb.y == sel.ne.y && sel.ne.x < sel.nb.x)
-                               ex = sel.nb.x;
-                       if(i < ex)
-                               *ptr++ = '\n';
-               }
        }
        *ptr = 0;
        return str;
@@ -1090,7 +1085,7 @@ xsetsel(char *str) {
 
 void
 brelease(XEvent *e) {
-       if(IS_SET(MODE_MOUSE)) {
+       if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
                mousereport(e);
                return;
        }
@@ -1113,7 +1108,7 @@ void
 bmotion(XEvent *e) {
        int oldey, oldex, oldsby, oldsey;
 
-       if(IS_SET(MODE_MOUSE)) {
+       if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
                mousereport(e);
                return;
        }
@@ -1179,16 +1174,15 @@ execsh(void) {
 
 void
 sigchld(int a) {
-       int stat = 0;
+       int stat, ret;
 
        if(waitpid(pid, &stat, 0) < 0)
                die("Waiting for pid %hd failed: %s\n", pid, strerror(errno));
 
-       if(WIFEXITED(stat)) {
-               exit(WEXITSTATUS(stat));
-       } else {
-               exit(EXIT_FAILURE);
-       }
+       ret = WIFEXITED(stat) ? WEXITSTATUS(stat) : EXIT_FAILURE;
+       if (ret != EXIT_SUCCESS)
+               die("child finished with error '%d'\n", stat);
+       exit(EXIT_SUCCESS);
 }
 
 void
@@ -1362,9 +1356,12 @@ treset(void) {
        memset(term.trantbl, sizeof(term.trantbl), CS_USA);
        term.charset = 0;
 
-       tclearregion(0, 0, term.col-1, term.row-1);
-       tmoveto(0, 0);
-       tcursor(CURSOR_SAVE);
+       for(i = 0; i < 2; i++) {
+               tmoveto(0, 0);
+               tcursor(CURSOR_SAVE);
+               tclearregion(0, 0, term.col-1, term.row-1);
+               tswapscreen();
+       }
 }
 
 void
@@ -1449,7 +1446,7 @@ selscroll(int orig, int n) {
                                sel.oe.x = term.col;
                        }
                }
-               selsort();
+               selnormalize();
        }
 }
 
@@ -1557,6 +1554,7 @@ tsetchar(char *c, Glyph *attr, int x, int y) {
 void
 tclearregion(int x1, int y1, int x2, int y2) {
        int x, y, temp;
+       Glyph *gp;
 
        if(x1 > x2)
                temp = x1, x1 = x2, x2 = temp;
@@ -1571,10 +1569,13 @@ tclearregion(int x1, int y1, int x2, int y2) {
        for(y = y1; y <= y2; y++) {
                term.dirty[y] = 1;
                for(x = x1; x <= x2; x++) {
+                       gp = &term.line[y][x];
                        if(selected(x, y))
                                selclear(NULL);
-                       term.line[y][x] = term.c.attr;
-                       memcpy(term.line[y][x].c, " ", 2);
+                       gp->fg = term.c.attr.fg;
+                       gp->bg = term.c.attr.bg;
+                       gp->mode = 0;
+                       memcpy(gp->c, " ", 2);
                }
        }
 }
@@ -1629,7 +1630,7 @@ tdefcolor(int *attr, int *npar, int l) {
        uint r, g, b;
 
        switch (attr[*npar + 1]) {
-       case 2: /* direct colour in RGB space */
+       case 2: /* direct color in RGB space */
                if (*npar + 4 >= l) {
                        fprintf(stderr,
                                "erresc(38): Incorrect number of parameters (%d)\n",
@@ -1646,7 +1647,7 @@ tdefcolor(int *attr, int *npar, int l) {
                else
                        idx = TRUECOLOR(r, g, b);
                break;
-       case 5: /* indexed colour */
+       case 5: /* indexed color */
                if (*npar + 2 >= l) {
                        fprintf(stderr,
                                "erresc(38): Incorrect number of parameters (%d)\n",
@@ -1661,8 +1662,8 @@ tdefcolor(int *attr, int *npar, int l) {
                break;
        case 0: /* implemented defined (only foreground) */
        case 1: /* transparent */
-       case 3: /* direct colour in CMY space */
-       case 4: /* direct colour in CMYK space */
+       case 3: /* direct color in CMY space */
+       case 4: /* direct color in CMYK space */
        default:
                fprintf(stderr,
                        "erresc(38): gfx attr %d unknown\n", attr[*npar]);
@@ -1680,15 +1681,24 @@ tsetattr(int *attr, int l) {
        for(i = 0; i < l; i++) {
                switch(attr[i]) {
                case 0:
-                       term.c.attr.mode &= ~(ATTR_REVERSE | ATTR_UNDERLINE \
-                                       | ATTR_BOLD | ATTR_ITALIC \
-                                       | ATTR_BLINK);
+                       term.c.attr.mode &= ~(
+                               ATTR_BOLD       |
+                               ATTR_FAINT      |
+                               ATTR_ITALIC     |
+                               ATTR_UNDERLINE  |
+                               ATTR_BLINK      |
+                               ATTR_REVERSE    |
+                               ATTR_INVISIBLE  |
+                               ATTR_STRUCK     );
                        term.c.attr.fg = defaultfg;
                        term.c.attr.bg = defaultbg;
                        break;
                case 1:
                        term.c.attr.mode |= ATTR_BOLD;
                        break;
+               case 2:
+                       term.c.attr.mode |= ATTR_FAINT;
+                       break;
                case 3:
                        term.c.attr.mode |= ATTR_ITALIC;
                        break;
@@ -1696,15 +1706,21 @@ tsetattr(int *attr, int l) {
                        term.c.attr.mode |= ATTR_UNDERLINE;
                        break;
                case 5: /* slow blink */
+                       /* FALLTHROUGH */
                case 6: /* rapid blink */
                        term.c.attr.mode |= ATTR_BLINK;
                        break;
                case 7:
                        term.c.attr.mode |= ATTR_REVERSE;
                        break;
-               case 21:
+               case 8:
+                       term.c.attr.mode |= ATTR_INVISIBLE;
+                       break;
+               case 9:
+                       term.c.attr.mode |= ATTR_STRUCK;
+                       break;
                case 22:
-                       term.c.attr.mode &= ~ATTR_BOLD;
+                       term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT);
                        break;
                case 23:
                        term.c.attr.mode &= ~ATTR_ITALIC;
@@ -1713,12 +1729,17 @@ tsetattr(int *attr, int l) {
                        term.c.attr.mode &= ~ATTR_UNDERLINE;
                        break;
                case 25:
-               case 26:
                        term.c.attr.mode &= ~ATTR_BLINK;
                        break;
                case 27:
                        term.c.attr.mode &= ~ATTR_REVERSE;
                        break;
+               case 28:
+                       term.c.attr.mode &= ~ATTR_INVISIBLE;
+                       break;
+               case 29:
+                       term.c.attr.mode &= ~ATTR_STRUCK;
+                       break;
                case 38:
                        if ((idx = tdefcolor(attr, &i, l)) >= 0)
                                term.c.attr.fg = idx;
@@ -1945,7 +1966,7 @@ csihandle(void) {
                break;
        case 'c': /* DA -- Device Attributes */
                if(csiescseq.arg[0] == 0)
-                       ttywrite(VT102ID, sizeof(VT102ID) - 1);
+                       ttywrite(vtiden, sizeof(vtiden) - 1);
                break;
        case 'C': /* CUF -- Cursor <n> Forward */
        case 'a': /* HPR -- Cursor <n> Forward */
@@ -2149,7 +2170,7 @@ strhandle(void) {
                        /* FALLTHROUGH */
                case 104: /* color reset, here p = NULL */
                        j = (narg > 1) ? atoi(strescseq.args[1]) : -1;
-                       if (!xsetcolorname(j, p)) {
+                       if(xsetcolorname(j, p)) {
                                fprintf(stderr, "erresc: invalid color %s\n", p);
                        } else {
                                /*
@@ -2254,12 +2275,10 @@ tdumpline(int n) {
        Glyph *bp, *end;
 
        bp = &term.line[n][0];
-       end = &bp[term.col-1];
-       while(end > bp && !strcmp(" ", end->c))
-               --end;
-       if(bp != end || strcmp(bp->c, " ")) {
+       end = &bp[MIN(tlinelen(n), term.col) - 1];
+       if(bp != end || bp->c[0] != ' ') {
                for( ;bp <= end; ++bp)
-                       tprinter(bp->c, strlen(bp->c));
+                       tprinter(bp->c, utf8len(bp->c));
        }
        tprinter("\n", 1);
 }
@@ -2293,13 +2312,13 @@ techo(char *buf, int len) {
        for(; len > 0; buf++, len--) {
                char c = *buf;
 
-               if(ISCONTROL(c)) { /* control code */
+               if(ISCONTROL((uchar) c)) { /* control code */
                        if(c & 0x80) {
                                c &= 0x7f;
                                tputc("^", 1);
                                tputc("[", 1);
                        } else if(c != '\n' && c != '\r' && c != '\t') {
-                               c ^= '\x40';
+                               c ^= 0x40;
                                tputc("^", 1);
                        }
                        tputc(&c, 1);
@@ -2313,19 +2332,39 @@ techo(char *buf, int len) {
 
 void
 tdeftran(char ascii) {
-       char c, (*bp)[2];
-       static char tbl[][2] = {
-               {'0', CS_GRAPHIC0}, {'B', CS_USA},
-               {0, 0}
-       };
-
-       for (bp = &tbl[0]; (c = (*bp)[0]) && c != ascii; ++bp)
-               /* nothing */;
+       static char cs[] = "0B";
+       static int vcs[] = {CS_GRAPHIC0, CS_USA};
+       char *p;
 
-       if (c == 0)
+       if((p = strchr(cs, ascii)) == NULL) {
                fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii);
-       else
-               term.trantbl[term.icharset] = (*bp)[1];
+       } else {
+               term.trantbl[term.icharset] = vcs[p - cs];
+       }
+}
+
+void
+tstrsequence(uchar c) {
+       if (c & 0x80) {
+               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
@@ -2335,19 +2374,19 @@ tcontrolcode(uchar ascii) {
        switch(ascii) {
        case '\t':   /* HT */
                tputtab(1);
-               break;
+               return;
        case '\b':   /* BS */
                tmoveto(term.c.x-1, term.c.y);
-               break;
+               return;
        case '\r':   /* CR */
                tmoveto(0, term.c.y);
-               break;
+               return;
        case '\f':   /* LF */
        case '\v':   /* VT */
        case '\n':   /* LF */
                /* go to first col if the mode is set */
                tnewline(IS_SET(MODE_CRLF));
-               break;
+               return;
        case '\a':   /* BEL */
                if(term.esc & ESC_STR_END) {
                        /* backwards compatibility to xterm */
@@ -2366,10 +2405,10 @@ tcontrolcode(uchar ascii) {
                return;
        case '\016': /* SO */
                term.charset = 0;
-               break;
+               return;
        case '\017': /* SI */
                term.charset = 1;
-               break;
+               return;
        case '\032': /* SUB */
                tsetchar(question, &term.c.attr, term.c.x, term.c.y);
        case '\030': /* CAN */
@@ -2380,22 +2419,34 @@ tcontrolcode(uchar ascii) {
        case '\021': /* XON (IGNORED) */
        case '\023': /* XOFF (IGNORED) */
        case 0177:   /* DEL (IGNORED) */
+               return;
        case 0x84:   /* TODO: IND */
-       case 0x85:   /* TODO: NEL */
-       case 0x88:   /* TODO: HTS */
+               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 0x90:   /* TODO: DCS */
        case 0x98:   /* TODO: SOS */
-       case 0x9a:   /* TODO: DECID */
+               break;
+       case 0x9a:   /* DECID -- Identify Terminal */
+               ttywrite(vtiden, sizeof(vtiden) - 1);
+               break;
        case 0x9b:   /* TODO: CSI */
        case 0x9c:   /* TODO: ST */
-       case 0x9d:   /* TODO: OSC */
-       case 0x9e:   /* TODO: PM */
-       case 0x9f:   /* TODO: APC */
                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;
 }
@@ -2509,9 +2560,7 @@ tputc(char *c, int len) {
                        case '^': /* PM -- Privacy Message */
                        case ']': /* OSC -- Operating System Command */
                        case 'k': /* old title set compatibility */
-                               strreset();
-                               strescseq.type = ascii;
-                               term.esc |= ESC_STR;
+                               tstrsequence(ascii);
                                return;
                        case '(': /* set primary charset G0 */
                        case ')': /* set secondary charset G1 */
@@ -2541,7 +2590,7 @@ tputc(char *c, int len) {
                                }
                                break;
                        case 'Z': /* DECID -- Identify Terminal */
-                               ttywrite(VT102ID, sizeof(VT102ID) - 1);
+                               ttywrite(vtiden, sizeof(vtiden) - 1);
                                break;
                        case 'c': /* RIS -- Reset to inital state */
                                treset();
@@ -2608,18 +2657,20 @@ tputc(char *c, int len) {
        }
 }
 
-int
+void
 tresize(int col, int row) {
        int i;
        int minrow = MIN(row, term.row);
        int mincol = MIN(col, term.col);
        int slide = term.c.y - row + 1;
        bool *bp;
-       Line *orig;
        TCursor c;
 
-       if(col < 1 || row < 1)
-               return 0;
+       if(col < 1 || row < 1) {
+               fprintf(stderr,
+                       "tresize: error resizing to %dx%d\n", col, row);
+               return;
+       }
 
        /* free unneeded rows */
        i = 0;
@@ -2649,14 +2700,12 @@ tresize(int col, int row) {
 
        /* resize each row to new width, zero-pad if needed */
        for(i = 0; i < minrow; i++) {
-               term.dirty[i] = 1;
                term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
                term.alt[i]  = xrealloc(term.alt[i],  col * sizeof(Glyph));
        }
 
        /* allocate any new rows */
        for(/* i == minrow */; i < row; i++) {
-               term.dirty[i] = 1;
                term.line[i] = xmalloc(col * sizeof(Glyph));
                term.alt[i] = xmalloc(col * sizeof(Glyph));
        }
@@ -2676,10 +2725,9 @@ tresize(int col, int row) {
        tsetscroll(0, row-1);
        /* make use of the LIMIT in tmoveto */
        tmoveto(term.c.x, term.c.y);
-       /* Clearing both screens */
-       orig = term.line;
+       /* Clearing both screens (it makes dirty all lines) */
        c = term.c;
-       do {
+       for(i = 0; i < 2; i++) {
                if(mincol < col && 0 < minrow) {
                        tclearregion(mincol, 0, col - 1, minrow - 1);
                }
@@ -2688,10 +2736,8 @@ tresize(int col, int row) {
                }
                tswapscreen();
                tcursor(CURSOR_LOAD);
-       } while(orig != term.line);
+       }
        term.c = c;
-
-       return (slide > 0);
 }
 
 void
@@ -2713,17 +2759,17 @@ sixd_to_16bit(int x) {
 
 void
 xloadcols(void) {
-       int i, r, g, b;
+       int i;
        XRenderColor color = { .alpha = 0xffff };
        static bool loaded;
-       Colour *cp;
+       Color *cp;
 
        if(loaded) {
                for (cp = dc.col; cp < dc.col + LEN(dc.col); ++cp)
                        XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
        }
 
-       /* load colors [0-15] colors and [256-LEN(colorname)[ (config.h) */
+       /* load colors [0-15] and [256-LEN(colorname)] (config.h) */
        for(i = 0; i < LEN(colorname); i++) {
                if(!colorname[i])
                        continue;
@@ -2732,27 +2778,20 @@ xloadcols(void) {
                }
        }
 
-       /* load colors [16-255] ; same colors as xterm */
-       for(i = 16, r = 0; r < 6; r++) {
-               for(g = 0; g < 6; g++) {
-                       for(b = 0; b < 6; b++) {
-                               color.red = sixd_to_16bit(r);
-                               color.green = sixd_to_16bit(g);
-                               color.blue = sixd_to_16bit(b);
-                               if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &dc.col[i])) {
-                                       die("Could not allocate color %d\n", i);
-                               }
-                               i++;
-                       }
-               }
+       /* load colors [16-231] ; same colors as xterm */
+       for(i = 16; i < 6*6*6+16; i++) {
+               color.red   = sixd_to_16bit( ((i-16)/36)%6 );
+               color.green = sixd_to_16bit( ((i-16)/6) %6 );
+               color.blue  = sixd_to_16bit( ((i-16)/1) %6 );
+               if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &dc.col[i]))
+                       die("Could not allocate color %d\n", i);
        }
 
-       for(r = 0; r < 24; r++, i++) {
-               color.red = color.green = color.blue = 0x0808 + 0x0a0a * r;
-               if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color,
-                                       &dc.col[i])) {
+       /* load colors [232-255] ; grayscale */
+       for(; i < 256; i++) {
+               color.red = color.green = color.blue = 0x0808 + 0x0a0a * (i-(6*6*6+16));
+               if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &dc.col[i]))
                        die("Could not allocate color %d\n", i);
-               }
        }
        loaded = true;
 }
@@ -2760,33 +2799,45 @@ xloadcols(void) {
 int
 xsetcolorname(int x, const char *name) {
        XRenderColor color = { .alpha = 0xffff };
-       Colour colour;
+       Color ncolor;
+
        if(!BETWEEN(x, 0, LEN(colorname)))
-               return -1;
+               return 1;
+
        if(!name) {
-               if(BETWEEN(x, 16, 16 + 215)) {
-                       int r = (x - 16) / 36, g = ((x - 16) % 36) / 6, b = (x - 16) % 6;
-                       color.red = sixd_to_16bit(r);
-                       color.green = sixd_to_16bit(g);
-                       color.blue = sixd_to_16bit(b);
-                       if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &colour))
-                               return 0; /* something went wrong */
-                       dc.col[x] = colour;
-                       return 1;
-               } else if(BETWEEN(x, 16 + 216, 255)) {
-                       color.red = color.green = color.blue = 0x0808 + 0x0a0a * (x - (16 + 216));
-                       if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &colour))
-                               return 0; /* something went wrong */
-                       dc.col[x] = colour;
-                       return 1;
-               } else {
+               if(BETWEEN(x, 16, 16 + 215)) { /* 256 color */
+                       color.red   = sixd_to_16bit( ((x-16)/36)%6 );
+                       color.green = sixd_to_16bit( ((x-16)/6) %6 );
+                       color.blue  = sixd_to_16bit( ((x-16)/1) %6 );
+                       if(!XftColorAllocValue(xw.dpy, xw.vis,
+                                               xw.cmap, &color, &ncolor)) {
+                               return 1;
+                       }
+
+                       XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
+                       dc.col[x] = ncolor;
+                       return 0;
+               } else if(BETWEEN(x, 16 + 216, 255)) { /* greyscale */
+                       color.red = color.green = color.blue = \
+                                   0x0808 + 0x0a0a * (x - (16 + 216));
+                       if(!XftColorAllocValue(xw.dpy, xw.vis,
+                                               xw.cmap, &color, &ncolor)) {
+                               return 1;
+                       }
+
+                       XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
+                       dc.col[x] = ncolor;
+                       return 0;
+               } else { /* system colors */
                        name = colorname[x];
                }
        }
-       if(!XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, &colour))
-               return 0;
-       dc.col[x] = colour;
-       return 1;
+       if(!XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, &ncolor))
+               return 1;
+
+       XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
+       dc.col[x] = ncolor;
+       return 0;
 }
 
 void
@@ -2887,6 +2938,7 @@ xloadfonts(char *fontstr, double fontsize) {
        FcPattern *pattern;
        FcResult r_sz, r_psz;
        double fontval;
+       float ceilf(float);
 
        if(fontstr[0] == '-') {
                pattern = XftXlfdParse(fontstr, False, False);
@@ -2932,8 +2984,8 @@ xloadfonts(char *fontstr, double fontsize) {
        }
 
        /* Setting character width and height. */
-       xw.cw = CEIL(dc.font.width * cwscale);
-       xw.ch = CEIL(dc.font.height * chscale);
+       xw.cw = ceilf(dc.font.width * cwscale);
+       xw.ch = ceilf(dc.font.height * chscale);
 
        FcPatternDel(pattern, FC_SLANT);
        FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
@@ -2988,6 +3040,7 @@ xzoom(const Arg *arg) {
        xloadfonts(usedfont, usedfontsize + arg->i);
        cresize(0, 0);
        redraw(0);
+       xhints();
 }
 
 void
@@ -3102,7 +3155,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
        FcPattern *fcpattern, *fontpattern;
        FcFontSet *fcsets[] = { NULL };
        FcCharSet *fccharset;
-       Colour *fg, *bg, *temp, revfg, revbg, truefg, truebg;
+       Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
        XRenderColor colfg, colbg;
        XRectangle r;
        int oneatatime;
@@ -3147,24 +3200,20 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
        }
 
        if(base.mode & ATTR_BOLD) {
-               if(BETWEEN(base.fg, 0, 7)) {
-                       /* basic system colors */
-                       fg = &dc.col[base.fg + 8];
-               } else if(BETWEEN(base.fg, 16, 195)) {
-                       /* 256 colors */
-                       fg = &dc.col[base.fg + 36];
-               } else if(BETWEEN(base.fg, 232, 251)) {
-                       /* greyscale */
-                       fg = &dc.col[base.fg + 4];
-               }
                /*
-                * Those ranges will not be brightened:
-                *    8 - 15 – bright system colors
-                *    196 - 231 – highest 256 color cube
-                *    252 - 255 – brightest colors in greyscale
+                * change basic system colors [0-7]
+                * to bright system colors [8-15]
                 */
-               font = &dc.bfont;
-               frcflags = FRC_BOLD;
+               if(BETWEEN(base.fg, 0, 7) && !(base.mode & ATTR_FAINT))
+                       fg = &dc.col[base.fg + 8];
+
+               if(base.mode & ATTR_ITALIC) {
+                       font = &dc.ibfont;
+                       frcflags = FRC_ITALICBOLD;
+               } else {
+                       font = &dc.bfont;
+                       frcflags = FRC_BOLD;
+               }
        }
 
        if(IS_SET(MODE_REVERSE)) {
@@ -3199,9 +3248,20 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
                bg = temp;
        }
 
+       if(base.mode & ATTR_FAINT && !(base.mode & ATTR_BOLD)) {
+               colfg.red = fg->color.red / 2;
+               colfg.green = fg->color.green / 2;
+               colfg.blue = fg->color.blue / 2;
+               XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg);
+               fg = &revfg;
+       }
+
        if(base.mode & ATTR_BLINK && term.mode & MODE_BLINK)
                fg = bg;
 
+       if(base.mode & ATTR_INVISIBLE)
+               fg = bg;
+
        /* Intelligent cleaning up of the borders. */
        if(x == 0) {
                xclear(0, (y == 0)? 0 : winy, borderpx,
@@ -3244,28 +3304,22 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
                        bytelen -= u8cblen;
 
                        doesexist = XftCharExists(xw.dpy, font->match, unicodep);
-                       if(oneatatime || !doesexist || bytelen <= 0) {
-                               if(oneatatime || bytelen <= 0) {
-                                       if(doesexist) {
-                                               u8fl++;
-                                               u8fblen += u8cblen;
-                                       }
-                               }
-
-                               if(u8fl > 0) {
-                                       XftDrawStringUtf8(xw.draw, fg,
-                                                       font->match, xp,
-                                                       winy + font->ascent,
-                                                       (FcChar8 *)u8fs,
-                                                       u8fblen);
-                                       xp += xw.cw * u8fl;
-
-                               }
-                               break;
+                       if(doesexist) {
+                                       u8fl++;
+                                       u8fblen += u8cblen;
+                                       if(!oneatatime && bytelen > 0)
+                                                       continue;
                        }
 
-                       u8fl++;
-                       u8fblen += u8cblen;
+                       if(u8fl > 0) {
+                               XftDrawStringUtf8(xw.draw, fg,
+                                               font->match, xp,
+                                               winy + font->ascent,
+                                               (FcChar8 *)u8fs,
+                                               u8fblen);
+                               xp += xw.cw * u8fl;
+                       }
+                       break;
                }
                if(doesexist) {
                        if(oneatatime)
@@ -3352,6 +3406,11 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
                                width, 1);
        }
 
+       if(base.mode & ATTR_STRUCK) {
+               XftDrawRect(xw.draw, fg, winx, winy + 2 * font->ascent / 3,
+                               width, 1);
+       }
+
        /* Reset clip to none. */
        XftDrawSetClip(xw.draw, 0);
 }
@@ -3437,6 +3496,7 @@ void
 redraw(int timeout) {
        struct timespec tv = {0, timeout * 1000};
 
+       tfulldirt();
        draw();
 
        if(timeout > 0) {
@@ -3712,11 +3772,14 @@ run(void) {
        int w = xw.w, h = xw.h;
        fd_set rfd;
        int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
-       struct timeval drawtimeout, *tv = NULL, now, last, lastblink;
+       struct timespec drawtimeout, *tv = NULL, now, last, lastblink;
+       long deltatime;
 
        /* Waiting for window mapping */
        while(1) {
                XNextEvent(xw.dpy, &ev);
+               if(XFilterEvent(&ev, None))
+                       continue;
                if(ev.type == ConfigureNotify) {
                        w = ev.xconfigure.width;
                        h = ev.xconfigure.height;
@@ -3728,17 +3791,15 @@ run(void) {
        ttynew();
        cresize(w, h);
 
-       gettimeofday(&last, NULL);
+       clock_gettime(CLOCK_MONOTONIC, &last);
        lastblink = last;
 
        for(xev = actionfps;;) {
-               long deltatime;
-
                FD_ZERO(&rfd);
                FD_SET(cmdfd, &rfd);
                FD_SET(xfd, &rfd);
 
-               if(select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv) < 0) {
+               if(pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
                        if(errno == EINTR)
                                continue;
                        die("select failed: %s\n", strerror(errno));
@@ -3755,9 +3816,9 @@ run(void) {
                if(FD_ISSET(xfd, &rfd))
                        xev = actionfps;
 
-               gettimeofday(&now, NULL);
+               clock_gettime(CLOCK_MONOTONIC, &now);
                drawtimeout.tv_sec = 0;
-               drawtimeout.tv_usec = (1000/xfps) * 1000;
+               drawtimeout.tv_nsec = (1000/xfps) * 1E6;
                tv = &drawtimeout;
 
                dodraw = 0;
@@ -3792,9 +3853,9 @@ run(void) {
                                if(blinkset) {
                                        if(TIMEDIFF(now, lastblink) \
                                                        > blinktimeout) {
-                                               drawtimeout.tv_usec = 1;
+                                               drawtimeout.tv_nsec = 1000;
                                        } else {
-                                               drawtimeout.tv_usec = (1000 * \
+                                               drawtimeout.tv_nsec = (1E6 * \
                                                        (blinktimeout - \
                                                        TIMEDIFF(now,
                                                                lastblink)));
@@ -3810,8 +3871,8 @@ run(void) {
 void
 usage(void) {
        die("%s " VERSION " (c) 2010-2014 st engineers\n" \
-       "usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]" \
-       " [-t title] [-w windowid] [-e command ...]\n", argv0);
+       "usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]\n"
+       "          [-i] [-t title] [-w windowid] [-e command ...]\n", argv0);
 }
 
 int