+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_class = NULL;
+static char *opt_font = NULL;
+
+static char *usedfont = NULL;
+static int usedfontsize = 0;
+
+/* Font Ring Cache */
+enum {
+ FRC_NORMAL,
+ FRC_ITALIC,
+ FRC_BOLD,
+ FRC_ITALICBOLD
+};
+
+typedef struct {
+ XftFont *font;
+ long c;
+ int flags;
+} Fontcache;
+
+/*
+ * Fontcache is a ring buffer, with frccur as current position and frclen as
+ * the current length of used elements.
+ */
+
+static Fontcache frc[1024];
+static int frccur = -1, frclen = 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);
+
+ 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;
+}
+
+void *
+xcalloc(size_t nmemb, size_t size) {
+ void *p = calloc(nmemb, size);
+
+ if(!p)
+ die("Out of memory\n");
+
+ return p;
+}
+
+int
+utf8decode(char *s, long *u) {
+ uchar c;
+ int i, n, rtn;
+
+ rtn = 1;
+ c = *s;
+ if(~c & B7) { /* 0xxxxxxx */
+ *u = c;
+ return rtn;
+ } else if((c & (B7|B6|B5)) == (B7|B6)) { /* 110xxxxx */
+ *u = c&(B4|B3|B2|B1|B0);
+ n = 1;
+ } else if((c & (B7|B6|B5|B4)) == (B7|B6|B5)) { /* 1110xxxx */
+ *u = c&(B3|B2|B1|B0);
+ n = 2;
+ } else if((c & (B7|B6|B5|B4|B3)) == (B7|B6|B5|B4)) { /* 11110xxx */
+ *u = c & (B2|B1|B0);
+ n = 3;
+ } else {
+ goto invalid;
+ }
+
+ for(i = n, ++s; i > 0; --i, ++rtn, ++s) {
+ c = *s;
+ if((c & (B7|B6)) != B7) /* 10xxxxxx */
+ goto invalid;
+ *u <<= 6;
+ *u |= c & (B5|B4|B3|B2|B1|B0);
+ }
+
+ if((n == 1 && *u < 0x80) ||
+ (n == 2 && *u < 0x800) ||
+ (n == 3 && *u < 0x10000) ||
+ (*u >= 0xD800 && *u <= 0xDFFF)) {
+ goto invalid;
+ }
+
+ return rtn;
+invalid:
+ *u = 0xFFFD;
+
+ return rtn;
+}
+
+int
+utf8encode(long *u, char *s) {
+ uchar *sp;
+ ulong uc;
+ int i, n;
+
+ sp = (uchar *)s;
+ uc = *u;
+ if(uc < 0x80) {
+ *sp = uc; /* 0xxxxxxx */
+ return 1;
+ } else if(*u < 0x800) {
+ *sp = (uc >> 6) | (B7|B6); /* 110xxxxx */
+ n = 1;
+ } else if(uc < 0x10000) {
+ *sp = (uc >> 12) | (B7|B6|B5); /* 1110xxxx */
+ n = 2;
+ } else if(uc <= 0x10FFFF) {
+ *sp = (uc >> 18) | (B7|B6|B5|B4); /* 11110xxx */
+ n = 3;
+ } else {
+ goto invalid;
+ }
+
+ for(i=n,++sp; i>0; --i,++sp)
+ *sp = ((uc >> 6*(i-1)) & (B5|B4|B3|B2|B1|B0)) | B7; /* 10xxxxxx */
+
+ return n+1;
+invalid:
+ /* U+FFFD */
+ *s++ = '\xEF';
+ *s++ = '\xBF';
+ *s = '\xBD';
+
+ return 3;
+}
+
+/* use this if your buffer is less than UTF_SIZ, it returns 1 if you can decode
+ UTF-8 otherwise return 0 */
+int
+isfullutf8(char *s, int b) {
+ uchar *c1, *c2, *c3;
+
+ c1 = (uchar *)s;
+ c2 = (uchar *)++s;
+ c3 = (uchar *)++s;
+ if(b < 1) {
+ return 0;
+ } else if((*c1&(B7|B6|B5)) == (B7|B6) && b == 1) {
+ return 0;
+ } else if((*c1&(B7|B6|B5|B4)) == (B7|B6|B5) &&
+ ((b == 1) ||
+ ((b == 2) && (*c2&(B7|B6)) == B7))) {
+ return 0;
+ } else if((*c1&(B7|B6|B5|B4|B3)) == (B7|B6|B5|B4) &&
+ ((b == 1) ||
+ ((b == 2) && (*c2&(B7|B6)) == B7) ||
+ ((b == 3) && (*c2&(B7|B6)) == B7 && (*c3&(B7|B6)) == B7))) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+int
+utf8size(char *s) {
+ uchar c = *s;
+
+ if(~c&B7) {
+ return 1;
+ } else if((c&(B7|B6|B5)) == (B7|B6)) {
+ return 2;
+ } else if((c&(B7|B6|B5|B4)) == (B7|B6|B5)) {
+ return 3;
+ } else {
+ return 4;
+ }
+}
+
+void
+selinit(void) {
+ memset(&sel.tclick1, 0, sizeof(sel.tclick1));
+ memset(&sel.tclick2, 0, sizeof(sel.tclick2));
+ sel.mode = 0;
+ sel.bx = -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 inline bool
+selected(int x, int y) {
+ int bx, ex;
+
+ if(sel.ey == y && sel.by == y) {
+ bx = MIN(sel.bx, sel.ex);
+ ex = MAX(sel.bx, sel.ex);
+
+ return BETWEEN(x, bx, ex);
+ }
+
+ if(sel.type == SEL_RECTANGULAR) {
+ return ((sel.b.y <= y && y <= sel.e.y)
+ && (sel.b.x <= x && x <= sel.e.x));
+ }
+ return ((sel.b.y < y && y < sel.e.y)
+ || (y == sel.e.y && x <= sel.e.x))
+ || (y == sel.b.y && x >= sel.b.x
+ && (x <= sel.e.x || sel.b.y != sel.e.y));
+}
+
+void
+selsnap(int mode, int *x, int *y, int direction) {
+ switch(mode) {
+ case SNAP_WORD:
+ while(*x > 0 && *x < term.col-1
+ && term.line[*y][*x + direction].c[0] != ' ') {
+ *x += direction;
+ }
+ break;
+ case SNAP_LINE:
+ *x = (direction < 0) ? 0 : term.col - 1;
+ break;
+ default:
+ break;
+ }
+}
+
+void
+getbuttoninfo(XEvent *e) {
+ int type;
+ uint state = e->xbutton.state &~Button1Mask;
+
+ sel.alt = IS_SET(MODE_ALTSCREEN);
+
+ sel.ex = x2col(e->xbutton.x);
+ sel.ey = y2row(e->xbutton.y);
+
+ if (sel.by < sel.ey
+ || (sel.by == sel.ey && sel.bx < sel.ex)) {
+ selsnap(sel.snap, &sel.bx, &sel.by, -1);
+ selsnap(sel.snap, &sel.ex, &sel.ey, +1);
+ } else {
+ selsnap(sel.snap, &sel.ex, &sel.ey, -1);
+ selsnap(sel.snap, &sel.bx, &sel.by, +1);
+ }
+
+ sel.b.x = sel.by < sel.ey ? sel.bx : sel.ex;
+ sel.b.y = MIN(sel.by, sel.ey);
+ sel.e.x = sel.by < sel.ey ? sel.ex : sel.bx;
+ sel.e.y = MAX(sel.by, sel.ey);
+
+ sel.type = SEL_REGULAR;
+ for(type = 1; type < LEN(selmasks); ++type) {
+ if(match(selmasks[type], state)) {
+ sel.type = type;
+ break;
+ }
+ }
+}
+
+void
+mousereport(XEvent *e) {
+ int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y),
+ button = e->xbutton.button, state = e->xbutton.state,
+ len;
+ char buf[40];
+ static int ob, ox, oy;
+
+ /* from urxvt */
+ if(e->xbutton.type == MotionNotify) {
+ if(!IS_SET(MODE_MOUSEMOTION) || (x == ox && y == oy))
+ return;
+ button = ob + 32;
+ ox = x, oy = y;
+ } else if(!IS_SET(MODE_MOUSESGR)
+ && (e->xbutton.type == ButtonRelease
+ || button == AnyButton)) {
+ button = 3;
+ } else {
+ button -= Button1;
+ if(button >= 3)
+ button += 64 - 3;
+ if(e->xbutton.type == ButtonPress) {
+ ob = button;
+ ox = x, oy = y;
+ }
+ }
+
+ button += (state & ShiftMask ? 4 : 0)
+ + (state & Mod4Mask ? 8 : 0)
+ + (state & ControlMask ? 16 : 0);
+
+ len = 0;
+ if(IS_SET(MODE_MOUSESGR)) {
+ len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
+ button, x+1, y+1,
+ e->xbutton.type == ButtonRelease ? 'm' : 'M');
+ } else if(x < 223 && y < 223) {
+ len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
+ 32+button, 32+x+1, 32+y+1);
+ } else {
+ return;
+ }
+
+ ttywrite(buf, len);
+}
+
+void
+bpress(XEvent *e) {
+ struct timeval now;
+
+ if(IS_SET(MODE_MOUSE)) {
+ mousereport(e);
+ } else if(e->xbutton.button == Button1) {
+ gettimeofday(&now, NULL);
+
+ /* Clear previous selection, logically and visually. */
+ if(sel.bx != -1) {
+ sel.bx = -1;
+ tsetdirt(sel.b.y, sel.e.y);
+ draw();
+ }
+ sel.mode = 1;
+ sel.type = SEL_REGULAR;
+ sel.ex = sel.bx = x2col(e->xbutton.x);
+ sel.ey = sel.by = y2row(e->xbutton.y);
+
+ /*
+ * Snap handling.
+ * If user clicks are fasst enough (e.g. below timeouts),
+ * we ignore if his hand slipped left or down and accidentally
+ * selected more; we are just snapping to whatever we're
+ * snapping.
+ */
+ if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) {
+ sel.snap = SNAP_LINE;
+ } else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) {
+ sel.snap = SNAP_WORD;
+ } else {
+ sel.snap = 0;
+ }
+ selsnap(sel.snap, &sel.bx, &sel.by, -1);
+ selsnap(sel.snap, &sel.ex, &sel.ey, 1);
+ sel.b.x = sel.bx;
+ sel.b.y = sel.by;
+ sel.e.x = sel.ex;
+ sel.e.y = sel.ey;
+
+ /*
+ * Draw selection, unless it's regular and we don't want to
+ * make clicks visible
+ */
+ if (sel.snap != 0) {
+ tsetdirt(sel.b.y, sel.e.y);
+ draw();
+ }
+ sel.tclick2 = sel.tclick1;
+ sel.tclick1 = now;
+ } else if(e->xbutton.button == Button4) {
+ ttywrite("\031", 1);
+ } else if(e->xbutton.button == Button5) {
+ ttywrite("\005", 1);
+ }
+}