+static DC dc;
+static XWindow xw;
+static Term term;
+static CSIEscape escseq;
+static int cmdfd;
+static pid_t pid;
+static Selection sel;
+static char *opt_cmd = NULL;
+static char *opt_title = NULL;
+
+void
+selinit(void) {
+ sel.mode = 0;
+ sel.bx = -1;
+ sel.clip = NULL;
+}
+
+static inline int selected(int x, int y) {
+ if(sel.ey == y && sel.by == y) {
+ int bx = MIN(sel.bx, sel.ex);
+ int ex = MAX(sel.bx, sel.ex);
+ return BETWEEN(x, bx, ex);
+ }
+ 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));
+}
+
+static void getbuttoninfo(XEvent *e, int *b, int *x, int *y) {
+ if(b)
+ *b = e->xbutton.button;
+
+ *x = e->xbutton.x/xw.cw;
+ *y = e->xbutton.y/xw.ch;
+ 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);
+}
+
+static void bpress(XEvent *e) {
+ sel.mode = 1;
+ sel.ex = sel.bx = e->xbutton.x/xw.cw;
+ sel.ey = sel.by = e->xbutton.y/xw.ch;
+}
+
+static char *getseltext() {
+ char *str, *ptr;
+ int ls, x, y, sz;
+ if(sel.bx == -1)
+ return NULL;
+ sz = (term.col+1) * (sel.e.y-sel.b.y+1);
+ ptr = str = malloc(sz);
+ for(y = 0; y < term.row; y++) {
+ for(x = 0; x < term.col; x++)
+ if(term.line[y][x].state & GLYPH_SET && (ls = selected(x, y)))
+ *ptr = term.line[y][x].c, ptr++;
+ if(ls)
+ *ptr = '\n', ptr++;
+ }
+ *ptr = 0;
+ return str;
+}
+
+static void selection_notify(XEvent *e) {
+ unsigned long nitems;
+ unsigned long length;
+ int format, res;
+ unsigned char *data;
+ Atom type;
+
+ res = XGetWindowProperty(xw.dis, xw.win, XA_PRIMARY, 0, 0, False,
+ AnyPropertyType, &type, &format, &nitems, &length, &data);
+ switch(res) {
+ case BadAtom:
+ case BadValue:
+ case BadWindow:
+ fprintf(stderr, "Invalid paste, XGetWindowProperty0");
+ return;
+ }
+
+ res = XGetWindowProperty(xw.dis, xw.win, XA_PRIMARY, 0, length, False,
+ AnyPropertyType, &type, &format, &nitems, &length, &data);
+ switch(res) {
+ case BadAtom:
+ case BadValue:
+ case BadWindow:
+ fprintf(stderr, "Invalid paste, XGetWindowProperty0");
+ return;
+ }
+
+ if(data) {
+ ttywrite((const char *) data, nitems * format / 8);
+ XFree(data);
+ }
+}
+
+static void selpaste() {
+ XConvertSelection(xw.dis, XA_PRIMARY, XA_STRING, XA_PRIMARY, xw.win, CurrentTime);
+}
+
+static void selection_request(XEvent *e)
+{
+ XSelectionRequestEvent *xsre;
+ XSelectionEvent xev;
+ int res;
+ Atom xa_targets;
+
+ xsre = (XSelectionRequestEvent *) e;
+ xev.type = SelectionNotify;
+ xev.requestor = xsre->requestor;
+ xev.selection = xsre->selection;
+ xev.target = xsre->target;
+ xev.time = xsre->time;
+ /* reject */
+ xev.property = None;
+
+ xa_targets = XInternAtom(xw.dis, "TARGETS", 0);
+ if(xsre->target == xa_targets) {
+ /* respond with the supported type */
+ Atom string = XA_STRING;
+ res = XChangeProperty(xsre->display, xsre->requestor, xsre->property, XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) &string, 1);
+ switch(res) {
+ case BadAlloc:
+ case BadAtom:
+ case BadMatch:
+ case BadValue:
+ case BadWindow:
+ fprintf(stderr, "Error in selection_request, TARGETS");
+ break;
+ default:
+ xev.property = xsre->property;
+ }
+ } else if(xsre->target == XA_STRING) {
+ res = XChangeProperty(xsre->display, xsre->requestor, xsre->property,
+ xsre->target, 8, PropModeReplace, (unsigned char *) sel.clip,
+ strlen(sel.clip));
+ switch(res) {
+ case BadAlloc:
+ case BadAtom:
+ case BadMatch:
+ case BadValue:
+ case BadWindow:
+ fprintf(stderr, "Error in selection_request, XA_STRING");
+ break;
+ default:
+ xev.property = xsre->property;
+ }
+ }
+
+ /* all done, send a notification to the listener */
+ res = XSendEvent(xsre->display, xsre->requestor, True, 0, (XEvent *) &xev);
+ switch(res) {
+ case 0:
+ case BadValue:
+ case BadWindow:
+ fprintf(stderr, "Error in selection_requested, XSendEvent");
+ }
+}
+
+static void selcopy(char *str) {
+ /* register the selection for both the clipboard and the primary */
+ Atom clipboard;
+ int res;
+
+ free(sel.clip);
+ sel.clip = str;
+
+ res = XSetSelectionOwner(xw.dis, XA_PRIMARY, xw.win, CurrentTime);
+ switch(res) {
+ case BadAtom:
+ case BadWindow:
+ fprintf(stderr, "Invalid copy, XSetSelectionOwner");
+ return;
+ }
+
+ clipboard = XInternAtom(xw.dis, "CLIPBOARD", 0);
+ res = XSetSelectionOwner(xw.dis, clipboard, xw.win, CurrentTime);
+ switch(res) {
+ case BadAtom:
+ case BadWindow:
+ fprintf(stderr, "Invalid copy, XSetSelectionOwner");
+ return;
+ }
+
+ XFlush(xw.dis);
+}
+
+/* TODO: doubleclick to select word */
+static void brelease(XEvent *e) {
+ int b;
+ sel.mode = 0;
+ getbuttoninfo(e, &b, &sel.ex, &sel.ey);
+ if(sel.bx==sel.ex && sel.by==sel.ey) {
+ sel.bx = -1;
+ if(b==2)
+ selpaste();
+ } else {
+ if(b==1)
+ selcopy(getseltext());
+ }
+ draw(1);
+}
+
+static void bmotion(XEvent *e) {
+ if (sel.mode) {
+ getbuttoninfo(e, NULL, &sel.ex, &sel.ey);
+ draw(1);
+ }
+}
+
+#ifdef DEBUG
+void
+tdump(void) {
+ int row, col;
+ Glyph c;
+
+ for(row = 0; row < term.row; row++) {
+ for(col = 0; col < term.col; col++) {
+ if(col == term.c.x && row == term.c.y)
+ putchar('#');
+ else {
+ c = term.line[row][col];
+ putchar(c.state & GLYPH_SET ? c.c : '.');
+ }
+ }
+ putchar('\n');
+ }
+}
+#endif