JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Adding a more flexible fontstring handling, shortcuts and a zoom function.
[st.git] / st.c
diff --git a/st.c b/st.c
index 8e25d23..5703e96 100644 (file)
--- a/st.c
+++ b/st.c
@@ -5,6 +5,7 @@
 #include <fcntl.h>
 #include <limits.h>
 #include <locale.h>
+#include <pwd.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -59,6 +60,8 @@
 
 #define REDRAW_TIMEOUT (80*1000) /* 80 ms */
 
+/* macros */
+#define CLEANMASK(mask) (mask & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
 #define SERRNO strerror(errno)
 #define MIN(a, b)  ((a) < (b) ? (a) : (b))
 #define MAX(a, b)  ((a) < (b) ? (b) : (a))
@@ -72,6 +75,8 @@
 #define X2COL(x) (((x) - BORDER)/xw.cw)
 #define Y2ROW(y) (((y) - BORDER)/xw.ch)
 
+#define VT102ID "\033[?6c"
+
 enum glyph_attribute {
        ATTR_NULL      = 0,
        ATTR_REVERSE   = 1,
@@ -121,6 +126,7 @@ enum escape_state {
        ESC_STR = 4, /* DSC, OSC, PM, APC */
        ESC_ALTCHARSET = 8,
        ESC_STR_END    = 16, /* a final string was encountered */
+       ESC_TEST       = 32, /* Enter in test mode */
 };
 
 enum window_state {
@@ -234,6 +240,24 @@ typedef struct {
        struct timeval tclick2;
 } Selection;
 
+typedef union {
+       int i;
+       unsigned int ui;
+       float f;
+       const void *v;
+} Arg;
+
+typedef struct {
+       unsigned int mod;
+       KeySym keysym;
+       void (*func)(const Arg *);
+       const Arg arg;
+} Shortcut;
+
+/* function definitions used in config.h */
+static void xzoom(const Arg *);
+
+/* Config.h for applying patches and the configuration. */
 #include "config.h"
 
 /* Font structure */
@@ -287,7 +311,7 @@ static int tresize(int, int);
 static void tscrollup(int, int);
 static void tscrolldown(int, int);
 static void tsetattr(int*, int);
-static void tsetchar(char*);
+static void tsetchar(char *, Glyph *, int, int);
 static void tsetscroll(int, int);
 static void tswapscreen(void);
 static void tsetdirt(int, int);
@@ -317,6 +341,7 @@ static void unmap(XEvent *);
 static char *kmap(KeySym, uint);
 static void kpress(XEvent *);
 static void cmessage(XEvent *);
+static void cresize(int width, int height);
 static void resize(XEvent *);
 static void focus(XEvent *);
 static void brelease(XEvent *);
@@ -337,10 +362,10 @@ static int utf8encode(long *, char *);
 static int utf8size(char *);
 static int isfullutf8(char *, int);
 
+static ssize_t xwrite(int, char *, size_t);
 static void *xmalloc(size_t);
 static void *xrealloc(void *, size_t);
 static void *xcalloc(size_t nmemb, size_t size);
-static char *smstrcat(char *, ...);
 
 static void (*handler[LASTEvent])(XEvent *) = {
        [KeyPress] = kpress,
@@ -376,6 +401,23 @@ static char *opt_embed = NULL;
 static char *opt_class = NULL;
 static char *opt_font = NULL;
 
+static char *usedfont = NULL;
+static int usedfontsize = 0;
+
+ssize_t
+xwrite(int fd, char *s, size_t len) {
+       size_t aux = len;
+
+       while(len > 0) {
+               ssize_t r = write(fd, s, len);
+               if(r < 0)
+                       return r;
+               len -= r;
+               s += r;
+       }
+       return aux;
+}
+
 void *
 xmalloc(size_t len) {
        void *p = malloc(len);
@@ -404,44 +446,6 @@ xcalloc(size_t nmemb, size_t size) {
        return p;
 }
 
-char *
-smstrcat(char *src, ...)
-{
-       va_list fmtargs;
-       char *ret, *p, *v;
-       int len, slen, flen;
-
-       len = slen = strlen(src);
-
-       va_start(fmtargs, src);
-       for(;;) {
-               v = va_arg(fmtargs, char *);
-               if(v == NULL)
-                       break;
-               len += strlen(v);
-       }
-       va_end(fmtargs);
-
-       p = ret = xmalloc(len+1);
-       memmove(p, src, slen);
-       p += slen;
-
-       va_start(fmtargs, src);
-       for(;;) {
-               v = va_arg(fmtargs, char *);
-               if(v == NULL)
-                       break;
-               flen = strlen(v);
-               memmove(p, v, flen);
-               p += flen;
-       }
-       va_end(fmtargs);
-
-       ret[len] = '\0';
-
-       return ret;
-}
-
 int
 utf8decode(char *s, long *u) {
        uchar c;
@@ -659,7 +663,7 @@ bpress(XEvent *e) {
 
 void
 selcopy(void) {
-       char *str, *ptr, *p;
+       char *str, *ptr;
        int x, y, bufsize, is_selected = 0, size;
        Glyph *gp;
 
@@ -674,11 +678,12 @@ selcopy(void) {
                        for(x = 0; x < term.col; x++) {
                                gp = &term.line[y][x];
 
-                               if(!(is_selected = selected(x, y)))
+                               if(!(is_selected = selected(x, y))
+                                               || !(gp->state & GLYPH_SET)) {
                                        continue;
-                               p = (gp->state & GLYPH_SET) ? gp->c : " ";
-                               size = utf8size(p);
-                               memcpy(ptr, p, size);
+                               }
+                               size = utf8size(gp->c);
+                               memcpy(ptr, gp->c, size);
                                ptr += size;
                        }
                        /* \n at the end of every selected line except for the last one */
@@ -861,11 +866,23 @@ void
 execsh(void) {
        char **args;
        char *envshell = getenv("SHELL");
+       const struct passwd *pass = getpwuid(getuid());
+       char buf[sizeof(long) * 8 + 1];
 
        unsetenv("COLUMNS");
        unsetenv("LINES");
        unsetenv("TERMCAP");
 
+       if(pass) {
+               setenv("LOGNAME", pass->pw_name, 1);
+               setenv("USER", pass->pw_name, 1);
+               setenv("SHELL", pass->pw_shell, 0);
+               setenv("HOME", pass->pw_dir, 0);
+       }
+
+       snprintf(buf, sizeof(buf), "%lu", xw.win);
+       setenv("WINDOWID", buf, 1);
+
        signal(SIGCHLD, SIG_DFL);
        signal(SIGHUP, SIG_DFL);
        signal(SIGINT, SIG_DFL);
@@ -875,7 +892,7 @@ execsh(void) {
 
        DEFAULT(envshell, SHELL);
        putenv("TERM="TNAME);
-       args = opt_cmd ? opt_cmd : (char*[]){envshell, "-i", NULL};
+       args = opt_cmd ? opt_cmd : (char *[]){envshell, "-i", NULL};
        execvp(args[0], args);
        exit(EXIT_FAILURE);
 }
@@ -923,13 +940,12 @@ ttynew(void) {
                cmdfd = m;
                signal(SIGCHLD, sigchld);
                if(opt_io) {
-                       if(!strcmp(opt_io, "-")) {
-                               iofd = STDOUT_FILENO;
-                       } else {
-                               if((iofd = open(opt_io, O_WRONLY | O_CREAT, 0666)) < 0) {
-                                       fprintf(stderr, "Error opening %s:%s\n",
-                                               opt_io, strerror(errno));
-                               }
+                       iofd = (!strcmp(opt_io, "-")) ?
+                                 STDOUT_FILENO :
+                                 open(opt_io, O_WRONLY | O_CREAT, 0666);
+                       if(iofd < 0) {
+                               fprintf(stderr, "Error opening %s:%s\n",
+                                       opt_io, strerror(errno));
                        }
                }
        }
@@ -1180,8 +1196,8 @@ tmoveto(int x, int y) {
 }
 
 void
-tsetchar(char *c) {
-       char *vt100_0[62] = { /* 0x41 - 0x7e */
+tsetchar(char *c, Glyph *attr, int x, int y) {
+       static char *vt100_0[62] = { /* 0x41 - 0x7e */
                "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
                0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
                0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
@@ -1195,17 +1211,17 @@ tsetchar(char *c) {
        /*
         * The table is proudly stolen from rxvt.
         */
-       if(term.c.attr.mode & ATTR_GFX) {
+       if(attr->mode & ATTR_GFX) {
                if(c[0] >= 0x41 && c[0] <= 0x7e
                                && vt100_0[c[0] - 0x41]) {
                        c = vt100_0[c[0] - 0x41];
                }
        }
 
-       term.dirty[term.c.y] = 1;
-       term.line[term.c.y][term.c.x] = term.c.attr;
-       memcpy(term.line[term.c.y][term.c.x].c, c, UTF_SIZ);
-       term.line[term.c.y][term.c.x].state |= GLYPH_SET;
+       term.dirty[y] = 1;
+       term.line[y][x] = *attr;
+       memcpy(term.line[y][x].c, c, UTF_SIZ);
+       term.line[y][x].state |= GLYPH_SET;
 }
 
 void
@@ -1510,6 +1526,10 @@ csihandle(void) {
                DEFAULT(csiescseq.arg[0], 1);
                tmoveto(term.c.x, term.c.y+csiescseq.arg[0]);
                break;
+       case 'c': /* DA -- Device Attributes */
+               if(csiescseq.arg[0] == 0)
+                       ttywrite(VT102ID, sizeof(VT102ID) - 1);
+               break;
        case 'C': /* CUF -- Cursor <n> Forward */
        case 'a':
                DEFAULT(csiescseq.arg[0], 1);
@@ -1784,53 +1804,86 @@ tputtab(bool forward) {
 void
 tputc(char *c, int len) {
        uchar ascii = *c;
-
-       if(iofd != -1)
-               write(iofd, c, len);
-
-       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)
+       bool control = ascii < '\x20' || ascii == 0177;
+
+       if(iofd != -1) {
+               if (xwrite(iofd, c, len) < 0) {
+                       fprintf(stderr, "Error writting in %s:%s\n",
+                               opt_io, strerror(errno));
+                       close(iofd);
+                       iofd = -1;
+               }
+       }
+       /*
+        * STR sequences must be checked before of anything
+        * because it can use some control codes as part of the sequence
+        */
+       if(term.esc & ESC_STR) {
+               switch(ascii) {
+               case '\033':
+                       term.esc = ESC_START | ESC_STR_END;
                        break;
-               if(!(xw.state & WIN_FOCUSED))
-                       xseturgency(1);
-               return;
-       case '\033':    /* ESC */
-               csireset();
-               term.esc = ESC_START;
-               return;
-       case '\016':    /* XXX: SO */
-       case '\017':    /* XXX: SI */
-               break;
-       case '\032':    /* SUB */
-       case '\030':    /* CAN */
-               csireset();
+               case '\a': /* backwards compatibility to xterm */
+                       term.esc = 0;
+                       strhandle();
+                       break;
+               default:
+                       strescseq.buf[strescseq.len++] = ascii;
+                       if(strescseq.len+1 >= STR_BUF_SIZ) {
+                               term.esc = 0;
+                               strhandle();
+                       }
+               }
                return;
-       default:
-       /* case '\005': ENQ (IGNORED) */
-       /* case '\000': NUL (IGNORED) */
-       /* case '\021': XON (IGNORED) */
-       /* case '\023': XOFF (IGNORED) */
-       /* case 0177:   DEL (IGNORED) */
-               break;
        }
-
-       if(term.esc & ESC_START) {
+       /*
+        * Actions of control codes must be performed as soon they arrive
+        * because they can be embedded inside a control sequence, and
+        * they must not cause conflicts with sequences.
+        */
+       if(control) {
+               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(!(xw.state & WIN_FOCUSED))
+                               xseturgency(1);
+                       return;
+               case '\033':    /* ESC */
+                       csireset();
+                       term.esc = ESC_START;
+                       return;
+               case '\016':    /* SO */
+                       term.c.attr.mode |= ATTR_GFX;
+                       return;
+               case '\017':    /* SI */
+                       term.c.attr.mode &= ~ATTR_GFX;
+                       return;
+               case '\032':    /* SUB */
+               case '\030':    /* CAN */
+                       csireset();
+                       return;
+                case '\005':   /* ENQ (IGNORED) */
+                case '\000':   /* NUL (IGNORED) */
+                case '\021':   /* XON (IGNORED) */
+                case '\023':   /* XOFF (IGNORED) */
+                case 0177:     /* DEL (IGNORED) */
+                       return;
+               }
+       } else if(term.esc & ESC_START) {
                if(term.esc & ESC_CSI) {
                        csiescseq.buf[csiescseq.len++] = ascii;
                        if(BETWEEN(ascii, 0x40, 0x7E)
@@ -1838,22 +1891,6 @@ tputc(char *c, int len) {
                                term.esc = 0;
                                csiparse(), csihandle();
                        }
-               } else if(term.esc & ESC_STR) {
-                       switch(ascii) {
-                       case '\033':
-                               term.esc = ESC_START | ESC_STR_END;
-                               break;
-                       case '\a': /* backwards compatibility to xterm */
-                               term.esc = 0;
-                               strhandle();
-                               break;
-                       default:
-                               strescseq.buf[strescseq.len++] = ascii;
-                               if(strescseq.len+1 >= STR_BUF_SIZ) {
-                                       term.esc = 0;
-                                       strhandle();
-                               }
-                       }
                } else if(term.esc & ESC_STR_END) {
                        term.esc = 0;
                        if(ascii == '\\')
@@ -1876,11 +1913,25 @@ tputc(char *c, int len) {
                                fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii);
                        }
                        term.esc = 0;
+               } else if(term.esc & ESC_TEST) {
+                       if(ascii == '8') { /* DEC screen alignment test. */
+                               char E[UTF_SIZ] = "E";
+                               int x, y;
+
+                               for(x = 0; x < term.col; ++x) {
+                                       for(y = 0; y < term.row; ++y)
+                                               tsetchar(E, &term.c.attr, x, y);
+                               }
+                       }
+                       term.esc = 0;
                } else {
                        switch(ascii) {
                        case '[':
                                term.esc |= ESC_CSI;
                                break;
+                       case '#':
+                               term.esc |= ESC_TEST;
+                               break;
                        case 'P': /* DCS -- Device Control String */
                        case '_': /* APC -- Application Program Command */
                        case '^': /* PM -- Privacy Message */
@@ -1922,6 +1973,10 @@ tputc(char *c, int len) {
                                }
                                term.esc = 0;
                                break;
+                       case 'Z': /* DECID -- Identify Terminal */
+                               ttywrite(VT102ID, sizeof(VT102ID) - 1);
+                               term.esc = 0;
+                               break;
                        case 'c': /* RIS -- Reset to inital state */
                                treset();
                                term.esc = 0;
@@ -1952,20 +2007,26 @@ tputc(char *c, int len) {
                                term.esc = 0;
                        }
                }
-       } else {
-               if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey))
-                       sel.bx = -1;
-               if(ascii >= '\020' || term.c.attr.mode & ATTR_GFX) {
-                       if(IS_SET(MODE_WRAP) && term.c.state & CURSOR_WRAPNEXT)
-                               tnewline(1); /* always go to first col */
-                       tsetchar(c);
-                       if(term.c.x+1 < term.col) {
-                               tmoveto(term.c.x+1, term.c.y);
-                       } else {
-                               term.c.state |= CURSOR_WRAPNEXT;
-                       }
-               }
+               /*
+                * All characters which forms part of a sequence are not
+                * printed
+                */
+               return;
        }
+       /*
+        * Display control codes only if we are in graphic mode
+        */
+       if(control && !(term.c.attr.mode & ATTR_GFX))
+               return;
+       if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey))
+               sel.bx = -1;
+       if(IS_SET(MODE_WRAP) && term.c.state & CURSOR_WRAPNEXT)
+               tnewline(1); /* always go to first col */
+       tsetchar(c, &term.c.attr, term.c.x, term.c.y);
+       if(term.c.x+1 < term.col)
+               tmoveto(term.c.x+1, term.c.y);
+       else
+               term.c.state |= CURSOR_WRAPNEXT;
 }
 
 int
@@ -2030,7 +2091,8 @@ tresize(int col, int row) {
                        *bp = 1;
        }
        /* update terminal size */
-       term.col = col, term.row = row;
+       term.col = col;
+       term.row = row;
        /* make use of the LIMIT in tmoveto */
        tmoveto(term.c.x, term.c.y);
        /* reset scrolling region */
@@ -2130,22 +2192,17 @@ xhints(void) {
        XFree(sizeh);
 }
 
-void
-xinitfont(Font *f, char *fontstr) {
-       FcPattern *pattern, *match;
+int
+xloadfont(Font *f, FcPattern *pattern) {
+       FcPattern *match;
        FcResult result;
 
-       pattern = FcNameParse((FcChar8 *)fontstr);
-       if(!pattern)
-               die("st: can't open font %s\n", fontstr);
-
        match = XftFontMatch(xw.dpy, xw.scr, pattern, &result);
-       FcPatternDestroy(pattern);
        if(!match)
-               die("st: can't open font %s\n", fontstr);
+               return 1;
        if(!(f->xft_set = XftFontOpenPattern(xw.dpy, match))) {
                FcPatternDestroy(match);
-               die("st: can't open font %s.\n", fontstr);
+               return 1;
        }
 
        f->ascent = f->xft_set->ascent;
@@ -2155,27 +2212,68 @@ xinitfont(Font *f, char *fontstr) {
 
        f->height = f->xft_set->height;
        f->width = f->lbearing + f->rbearing;
+
+       return 0;
 }
 
 void
-initfonts(char *fontstr) {
-       char *fstr;
+xloadfonts(char *fontstr, int fontsize) {
+       FcPattern *pattern;
+       FcResult result;
+       double fontval;
+
+       pattern = FcNameParse((FcChar8 *)fontstr);
+       if(!pattern)
+               die("st: can't open font %s\n", fontstr);
+
+       if(fontsize > 0) {
+               FcPatternDel(pattern, FC_PIXEL_SIZE);
+               FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
+               usedfontsize = fontsize;
+       } else {
+               result = FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval);
+               if(result == FcResultMatch) {
+                       usedfontsize = (int)fontval;
+               } else {
+                       /*
+                        * Default font size is 12, if none given. This is to
+                        * have a known usedfontsize value.
+                        */
+                       FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
+                       usedfontsize = 12;
+               }
+       }
 
-       xinitfont(&dc.font, fontstr);
+       if(xloadfont(&dc.font, pattern))
+               die("st: can't open font %s\n", fontstr);
+
+       /* Setting character width and height. */
        xw.cw = dc.font.width;
        xw.ch = dc.font.height;
 
-       fstr = smstrcat(fontstr, ":weight=bold", NULL);
-       xinitfont(&dc.bfont, fstr);
-       free(fstr);
+       FcPatternDel(pattern, FC_WEIGHT);
+       FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+       if(xloadfont(&dc.bfont, pattern))
+               die("st: can't open font %s\n", fontstr);
 
-       fstr = smstrcat(fontstr, ":slant=italic,oblique", NULL);
-       xinitfont(&dc.ifont, fstr);
-       free(fstr);
+       FcPatternDel(pattern, FC_SLANT);
+       FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+       if(xloadfont(&dc.ibfont, pattern))
+               die("st: can't open font %s\n", fontstr);
+
+       FcPatternDel(pattern, FC_WEIGHT);
+       if(xloadfont(&dc.ifont, pattern))
+               die("st: can't open font %s\n", fontstr);
 
-       fstr = smstrcat(fontstr, ":weight=bold:slant=italic,oblique", NULL);
-       xinitfont(&dc.ibfont, fstr);
-       free(fstr);
+       FcPatternDestroy(pattern);
+}
+
+void
+xzoom(const Arg *arg)
+{
+       xloadfonts(usedfont, usedfontsize + arg->i);
+       cresize(0, 0);
+       draw();
 }
 
 void
@@ -2191,7 +2289,8 @@ xinit(void) {
        xw.vis = XDefaultVisual(xw.dpy, xw.scr);
 
        /* font */
-       initfonts((opt_font != NULL)? opt_font : FONT);
+       usedfont = (opt_font == NULL)? FONT : opt_font;
+       xloadfonts(usedfont, 0);
 
        /* colors */
        xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
@@ -2293,7 +2392,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
                 * Those ranges will not be brightened:
                 *      8 - 15 – bright system colors
                 *      196 - 231 – highest 256 color cube
-                *      252 - 255 – brightest colors in grescale
+                *      252 - 255 – brightest colors in greyscale
                 */
                font = &dc.bfont;
        }
@@ -2338,7 +2437,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
        }
        if(x + charlen >= term.col-1) {
                xclear(winx + width, (y == 0)? 0 : winy, xw.w,
-                       winy + xw.ch + (y == term.row-1)? xw.h : 0);
+                       (y == term.row-1)? xw.h : (winy + xw.ch));
        }
        if(y == 0)
                xclear(winx, 0, winx + width, BORDER);
@@ -2527,11 +2626,8 @@ void
 kpress(XEvent *ev) {
        XKeyEvent *e = &ev->xkey;
        KeySym ksym;
-       char buf[32];
-       char *customkey;
-       int len;
-       int meta;
-       int shift;
+       char buf[32], *customkey;
+       int len, meta, shift, i;
        Status status;
 
        if (IS_SET(MODE_KBDLOCK))
@@ -2541,7 +2637,17 @@ kpress(XEvent *ev) {
        shift = e->state & ShiftMask;
        len = XmbLookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status);
 
-       /* 1. custom keys from config.h */
+       /* 1. shortcuts */
+       for(i = 0; i < LEN(shortcuts); i++) {
+               if((ksym == shortcuts[i].keysym)
+                               && (CLEANMASK(shortcuts[i].mod) == \
+                                       CLEANMASK(e->state))
+                               && shortcuts[i].func) {
+                       shortcuts[i].func(&(shortcuts[i].arg));
+               }
+       }
+
+       /* 2. custom keys from config.h */
        if((customkey = kmap(ksym, e->state))) {
                ttywrite(customkey, strlen(customkey));
        /* 2. hardcoded (overrides X lookup) */
@@ -2599,14 +2705,15 @@ cmessage(XEvent *e) {
 }
 
 void
-resize(XEvent *e) {
+cresize(int width, int height)
+{
        int col, row;
 
-       if(e->xconfigure.width == xw.w && e->xconfigure.height == xw.h)
-               return;
+       if(width != 0)
+               xw.w = width;
+       if(height != 0)
+               xw.h = height;
 
-       xw.w = e->xconfigure.width;
-       xw.h = e->xconfigure.height;
        col = (xw.w - 2*BORDER) / xw.cw;
        row = (xw.h - 2*BORDER) / xw.ch;
        if(col == term.col && row == term.row)
@@ -2618,6 +2725,14 @@ resize(XEvent *e) {
 }
 
 void
+resize(XEvent *e) {
+       if(e->xconfigure.width == xw.w && e->xconfigure.height == xw.h)
+               return;
+
+       cresize(e->xconfigure.width, e->xconfigure.height);
+}
+
+void
 run(void) {
        XEvent ev;
        fd_set rfd;
@@ -2731,8 +2846,8 @@ main(int argc, char *argv[]) {
 run:
        setlocale(LC_CTYPE, "");
        tnew(80, 24);
-       ttynew();
        xinit();
+       ttynew();
        selinit();
        run();
        return 0;