+static int iofd = STDOUT_FILENO;
+static char **opt_cmd = NULL;
+static char *opt_io = NULL;
+static char *opt_title = NULL;
+static char *opt_embed = NULL;
+static char *opt_class = NULL;
+static char *opt_font = NULL;
+static int oldbutton = 3; /* button event on startup: 3 = release */
+
+static char *usedfont = NULL;
+static double usedfontsize = 0;
+
+static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
+static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
+static long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
+static long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+/* Font Ring Cache */
+enum {
+ FRC_NORMAL,
+ FRC_ITALIC,
+ FRC_BOLD,
+ FRC_ITALICBOLD
+};
+
+typedef struct {
+ XftFont *font;
+ int flags;
+} Fontcache;
+
+/* Fontcache is an array now. A new font will be appended to the array. */
+static Fontcache frc[16];
+static int frclen = 0;
+
+ssize_t
+xwrite(int fd, const 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);
+
+ if(!p)
+ die("Out of memory\n");
+
+ return p;
+}
+
+void *
+xrealloc(void *p, size_t len) {
+ if((p = realloc(p, len)) == NULL)
+ die("Out of memory\n");
+
+ return p;
+}
+
+char *
+xstrdup(char *s) {
+ if((s = strdup(s)) == NULL)
+ die("Out of memory\n");
+
+ return s;
+}
+
+size_t
+utf8decode(char *c, long *u, size_t clen) {
+ size_t i, j, len, type;
+ long udecoded;
+
+ *u = UTF_INVALID;
+ if(!clen)
+ return 0;
+ udecoded = utf8decodebyte(c[0], &len);
+ if(!BETWEEN(len, 1, UTF_SIZ))
+ return 1;
+ for(i = 1, j = 1; i < clen && j < len; ++i, ++j) {
+ udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
+ if(type != 0)
+ return j;
+ }
+ if(j < len)
+ return 0;
+ *u = udecoded;
+ utf8validate(u, len);
+ return len;
+}
+
+long
+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;
+}
+
+size_t
+utf8encode(long u, char *c, size_t clen) {
+ size_t len, i;
+
+ len = utf8validate(&u, 0);
+ if(clen < len)
+ return 0;
+ for(i = len - 1; i != 0; --i) {
+ c[i] = utf8encodebyte(u, 0);
+ u >>= 6;
+ }
+ c[0] = utf8encodebyte(u, len);
+ return len;
+}
+
+char
+utf8encodebyte(long u, size_t i) {
+ return utfbyte[i] | (u & ~utfmask[i]);
+}
+
+size_t
+utf8len(char *c) {
+ return utf8decode(c, &(long){0}, UTF_SIZ);
+}
+
+size_t
+utf8validate(long *u, size_t i) {
+ if(!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
+ *u = UTF_INVALID;
+ for(i = 1; *u > utfmax[i]; ++i)
+ ;
+ return i;
+}
+
+static void
+selinit(void) {
+ memset(&sel.tclick1, 0, sizeof(sel.tclick1));
+ memset(&sel.tclick2, 0, sizeof(sel.tclick2));
+ sel.mode = 0;
+ sel.ob.x = -1;
+ sel.clip = NULL;
+ sel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
+ if(sel.xtarget == None)
+ sel.xtarget = XA_STRING;
+}
+
+static int
+x2col(int x) {
+ x -= borderpx;
+ x /= xw.cw;
+
+ return LIMIT(x, 0, term.col-1);
+}
+
+static int
+y2row(int y) {
+ y -= borderpx;
+ y /= xw.ch;
+
+ 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
+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 {
+ sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
+ sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
+ }
+ 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
+selected(int x, int y) {
+ if(sel.type == SEL_RECTANGULAR)
+ return BETWEEN(y, sel.nb.y, sel.ne.y)
+ && BETWEEN(x, sel.nb.x, sel.ne.x);
+
+ return BETWEEN(y, sel.nb.y, sel.ne.y)
+ && (y != sel.nb.y || x >= sel.nb.x)
+ && (y != sel.ne.y || x <= sel.ne.x);
+}
+
+void
+selsnap(int mode, int *x, int *y, int direction) {
+ int newx, newy, xt, yt;
+ Glyph *gp;
+
+ switch(mode) {
+ case SNAP_WORD:
+ /*
+ * Snap around if the word wraps around at the end or
+ * beginning of a line.
+ */
+ for(;;) {
+ 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)
+ yt = *y, xt = *x;
+ else
+ yt = newy, xt = newx;
+ if(!(term.line[yt][xt].mode & ATTR_WRAP))
+ break;
+ }
+
+ if (newx >= tlinelen(newy))
+ break;
+
+ gp = &term.line[newy][newx];
+ if (!(gp->mode & ATTR_WDUMMY) && strchr(worddelimiters, gp->c[0]))
+ break;
+
+ *x = newx;
+ *y = newy;
+ }
+ break;
+ case SNAP_LINE:
+ /*
+ * Snap around if the the previous line or the current one
+ * has set ATTR_WRAP at its end. Then the whole next or
+ * previous line will be selected.
+ */
+ *x = (direction < 0) ? 0 : term.col - 1;
+ if(direction < 0 && *y > 0) {
+ for(; *y > 0; *y += direction) {
+ if(!(term.line[*y-1][term.col-1].mode
+ & ATTR_WRAP)) {
+ break;
+ }
+ }
+ } else if(direction > 0 && *y < term.row-1) {
+ for(; *y < term.row; *y += direction) {
+ if(!(term.line[*y][term.col-1].mode
+ & ATTR_WRAP)) {
+ break;
+ }
+ }
+ }
+ break;
+ }
+}
+
+void
+getbuttoninfo(XEvent *e) {
+ int type;
+ uint state = e->xbutton.state & ~(Button1Mask | forceselmod);
+
+ sel.alt = IS_SET(MODE_ALTSCREEN);