19 HiWater = 640000, /* max size of history */
20 LoWater = 400000, /* min size of history after max'ed */
21 MinWater = 20000, /* room to leave available when reallocating */
28 static Image *cols[NCOL];
30 static Image *darkgrey;
31 static Cursor *lastcursor;
32 static Image *titlecol;
33 static Image *lighttitlecol;
34 static Image *dholdcol;
35 static Image *holdcol;
36 static Image *lightholdcol;
37 static Image *paleholdcol;
40 wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
46 /* there are no pastel paints in the dungeons and dragons world
50 if(getenv("reverse") != nil)
53 /* greys are multiples of 0x11111100+0xFF, 14* being palest */
54 grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF^reverse);
55 darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF^reverse);
56 cols[BACK] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xFFFFFFFF^reverse);
57 cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF^reverse);
58 cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF);
59 cols[TEXT] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x000000FF^reverse);
60 cols[HTEXT] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x000000FF^reverse);
62 titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen);
63 lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen);
65 titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPurpleblue);
66 lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF^reverse);
68 dholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue);
69 lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
70 paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
75 holdcol = paleholdcol;
77 w = emalloc(sizeof(Window));
79 r = insetrect(i->r, Selborder+1);
85 w->conswrite = chancreate(sizeof(Conswritemesg), 0);
86 w->consread = chancreate(sizeof(Consreadmesg), 0);
87 w->kbdread = chancreate(sizeof(Kbdreadmesg), 0);
88 w->mouseread = chancreate(sizeof(Mousereadmesg), 0);
89 w->wctlread = chancreate(sizeof(Consreadmesg), 0);
91 w->scrollr.max.x = r.min.x+Scrollwid;
93 r.min.x += Scrollwid+Scrollgap;
94 frinit(w, r, font, i, cols);
95 w->maxtab = maxtab*stringwidth(font, "0");
99 w->scrolling = scrolling;
100 w->dir = estrdup(startdir);
101 w->label = estrdup("<unnamed>");
102 r = insetrect(w->i->r, Selborder);
103 draw(w->i, r, cols[BACK], nil, w->entire.min);
104 wborder(w, Selborder);
106 incref(w); /* ref will be removed after mounting; avoids delete before ready to be deleted */
116 n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
117 for(i='A'; i<='Z'; i++){
118 if(nameimage(w->i, w->name, 1) > 0)
120 errstr(err, sizeof err);
121 if(strcmp(err, "image name in use") != 0)
127 fprint(2, "rio: setname failed: %s\n", err);
131 wresize(Window *w, Image *i, int move)
136 if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
137 draw(i, i->r, w->i, nil, w->i->r.min);
142 r = insetrect(i->r, Selborder+1);
144 w->scrollr.max.x = r.min.x+Scrollwid;
146 r.min.x += Scrollwid+Scrollgap;
148 frsetrects(w, r, w->i);
151 frinit(w, r, w->font, w->i, cols);
153 w->maxtab = maxtab*stringwidth(w->font, "0");
154 r = insetrect(w->i->r, Selborder);
155 draw(w->i, r, cols[BACK], nil, w->entire.min);
157 wsetselect(w, w->q0, w->q1);
160 wborder(w, Selborder);
161 w->topped = ++topped;
167 wrefresh(Window *w, Rectangle)
169 /* BUG: rectangle is ignored */
171 wborder(w, Selborder);
173 wborder(w, Unselborder);
176 draw(w->i, insetrect(w->i->r, Borderwidth), w->cols[BACK], nil, w->i->r.min);
179 frdrawsel(w, frptofchar(w, 0), 0, w->p0, 0);
180 if(w->p1 < w->nchars)
181 frdrawsel(w, frptofchar(w, w->p1), w->p1, w->nchars, 0);
182 frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 1);
196 error("negative ref count");
199 wsendctlmesg(w, Exited, ZR, nil);
207 Rune *rp, *bp, *tp, *up;
209 int nr, nb, c, wid, i, npart, initial, lastb;
210 char *s, *t, part[3];
213 enum { WKbd, WKbdread, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
222 char buf[4*12+1], *kbdq[8], *kbds;
226 snprint(buf, sizeof buf, "winctl-id%d", w->id);
229 mrm.cm = chancreate(sizeof(Mouse), 0);
230 krm.ck = chancreate(sizeof(char*), 0);
231 cwm.cw = chancreate(sizeof(Stringpair), 0);
232 crm.c1 = chancreate(sizeof(Stringpair), 0);
233 crm.c2 = chancreate(sizeof(Stringpair), 0);
234 cwrm.c1 = chancreate(sizeof(Stringpair), 0);
235 cwrm.c2 = chancreate(sizeof(Stringpair), 0);
237 alts[WKbd].c = w->ck;
238 alts[WKbd].v = &kbds;
239 alts[WKbd].op = CHANRCV;
240 alts[WKbdread].c = w->kbdread;
241 alts[WKbdread].v = &krm;
242 alts[WKbdread].op = CHANSND;
243 alts[WMouse].c = w->mc.c;
244 alts[WMouse].v = &w->mc.Mouse;
245 alts[WMouse].op = CHANRCV;
246 alts[WMouseread].c = w->mouseread;
247 alts[WMouseread].v = &mrm;
248 alts[WMouseread].op = CHANSND;
249 alts[WCtl].c = w->cctl;
251 alts[WCtl].op = CHANRCV;
252 alts[WCwrite].c = w->conswrite;
253 alts[WCwrite].v = &cwm;
254 alts[WCwrite].op = CHANSND;
255 alts[WCread].c = w->consread;
256 alts[WCread].v = &crm;
257 alts[WCread].op = CHANSND;
258 alts[WWread].c = w->wctlread;
259 alts[WWread].v = &cwrm;
260 alts[WWread].op = CHANSND;
261 alts[NWALT].op = CHANEND;
263 memset(kbdq, 0, sizeof(kbdq));
268 alts[WKbdread].op = (w->kbdopen && kbdqw != kbdqr) ?
270 alts[WMouseread].op = (w->mouseopen && w->mouse.counter != w->mouse.lastcounter) ?
272 alts[WCwrite].op = (!w->scrolling && !w->mouseopen && w->qh>w->org+w->nchars) ?
274 alts[WWread].op = (w->deleted || !w->wctlready) ?
277 /* this code depends on NL and EOT fitting in a single byte */
278 /* kind of expensive for each loop; worth precomputing? */
280 alts[WCread].op = CHANNOP;
281 else if(npart || (w->rawing && w->nraw>0))
282 alts[WCread].op = CHANSND;
284 alts[WCread].op = CHANNOP;
285 for(i=w->qh; i<w->nr; i++){
287 if(c=='\n' || c=='\004'){
288 alts[WCread].op = CHANSND;
296 i = (kbdqw+1) % nelem(kbdq);
299 } else if(*kbds == 'c'){
302 chartorune(&r, kbds+1);
311 i = (kbdqr+1) % nelem(kbdq);
315 sendp(krm.ck, kbdq[i]);
318 sendp(krm.ck, strdup("K"));
325 /* queue click events */
326 if(!w->mouse.qfull && lastb != w->mc.buttons) { /* add to ring */
327 mp = &w->mouse.queue[w->mouse.wi];
328 if(++w->mouse.wi == nelem(w->mouse.queue))
330 if(w->mouse.wi == w->mouse.ri)
331 w->mouse.qfull = TRUE;
333 mp->counter = w->mouse.counter;
334 lastb = w->mc.buttons;
340 /* send a queued event or, if the queue is empty, the current state */
341 /* if the queue has filled, we discard all the events it contained. */
342 /* the intent is to discard frantic clicking by the user during long latencies. */
343 w->mouse.qfull = FALSE;
344 if(w->mouse.wi != w->mouse.ri) {
345 m = w->mouse.queue[w->mouse.ri];
346 if(++w->mouse.ri == nelem(w->mouse.queue))
349 m = (Mousestate){w->mc.Mouse, w->mouse.counter};
351 w->mouse.lastcounter = m.counter;
352 send(mrm.cm, &m.Mouse);
355 if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
356 for(i=0; i<nelem(kbdq); i++)
394 wdelete(w, qh, qh+initial);
403 w->qh = winsert(w, rp, nr, w->qh)+nr;
404 if(w->scrolling || w->mouseopen)
406 wsetselect(w, w->q0, w->q1);
418 while(i<nb && (w->qh<w->nr || w->nraw>0)){
420 wid = runetochar(t+i, &w->raw[0]);
422 runemove(w->raw, w->raw+1, w->nraw);
424 wid = runetochar(t+i, &w->r[w->qh++]);
425 c = t[i]; /* knows break characters fit in a byte */
427 if(!w->rawing && (c == '\n' || c=='\004')){
433 if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004')
437 memmove(part, t+nb, npart);
446 recv(cwrm.c1, &pair);
447 if(w->deleted || w->i==nil)
448 pair.ns = sprint(pair.s, "");
451 for(i=0; i<nhidden; i++)
459 pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
460 w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
462 send(cwrm.c2, &pair);
466 flushimage(display, 1);
471 waddraw(Window *w, Rune *r, int nr)
473 w->raw = runerealloc(w->raw, w->nraw+nr);
474 runemove(w->raw+w->nraw, r, nr);
479 * Need to do this in a separate proc because if process we're interrupting
480 * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
483 interruptproc(void *v)
488 write(*notefd, "interrupt", 9);
493 windfilewidth(Window *w, uint q0, int oneelement)
503 if(oneelement && r=='/')
511 showcandidates(Window *w, Completion *c)
521 s = "[no matches in ";
525 fmtprint(&f, "%s%d files]\n", s, c->nfile);
527 fmtprint(&f, "%s", s);
528 for(i=0; i<c->nfile; i++){
531 fmtprint(&f, "%s", c->filename[i]);
535 /* place text at beginning of line before host point */
537 while(qline>0 && w->r[qline-1] != '\n')
540 rp = runefmtstrflush(&f);
544 q0 += winsert(w, rp, runestrlen(rp), qline) - qline;
546 wsetselect(w, q0+nr, q0+nr);
550 namecomplete(Window *w)
553 Rune *rp, *path, *str;
555 char *s, *dir, *root;
557 /* control-f: filename completion; works back to white space or / */
558 if(w->q0<w->nr && w->r[w->q0]>' ') /* must be at end of word */
560 nstr = windfilewidth(w, w->q0, TRUE);
561 str = runemalloc(nstr);
562 runemove(str, w->r+(w->q0-nstr), nstr);
563 npath = windfilewidth(w, w->q0-nstr, FALSE);
564 path = runemalloc(npath);
565 runemove(path, w->r+(w->q0-nstr-npath), npath);
568 /* is path rooted? if not, we need to make it relative to window path */
569 if(npath>0 && path[0]=='/'){
570 dir = malloc(UTFmax*npath+1);
571 sprint(dir, "%.*S", npath, path);
573 if(strcmp(w->dir, "") == 0)
577 dir = malloc(strlen(root)+1+UTFmax*npath+1);
578 sprint(dir, "%s/%.*S", root, npath, path);
580 dir = cleanname(dir);
582 s = smprint("%.*S", nstr, str);
583 c = complete(dir, s);
589 showcandidates(w, c);
592 rp = runesmprint("%s", c->string);
603 wkeyctl(Window *w, Rune r)
623 /* navigation keys work only when mouse and kbd is not open */
630 n = mousescrollsize(w->maxlines);
637 q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+n*w->font->height));
638 wsetorigin(w, q0, TRUE);
644 n = mousescrollsize(w->maxlines);
651 q0 = wbacknl(w, w->org, n);
652 wsetorigin(w, q0, TRUE);
657 wsetselect(w, q0, q0);
664 wsetselect(w, q1, q1);
674 case Ksoh: /* ^A: beginning of line */
675 if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
677 nb = wbswidth(w, 0x15 /* ^U */);
678 wsetselect(w, w->q0-nb, w->q0-nb);
681 case Kenq: /* ^E: end of line */
683 while(q0 < w->nr && w->r[q0]!='\n')
685 wsetselect(w, q0, q0);
689 if(w->rawing && (w->q0==w->nr || w->mouseopen)){
693 if(r==Kesc || (w->holding && r==Kdel)){ /* toggle hold */
707 case Kdel: /* send interrupt */
710 notefd = emalloc(sizeof(int));
712 proccreate(interruptproc, notefd, 4096);
714 case Kack: /* ^F: file name completion */
715 case Kins: /* Insert: file name completion */
716 rp = namecomplete(w);
721 q0 = winsert(w, rp, nr, q0);
725 case Kbs: /* ^H: erase character */
726 case Knack: /* ^U: erase line */
727 case Ketb: /* ^W: erase word */
728 if(w->q0==0 || w->q0==w->qh)
738 wdelete(w, q0, q0+nb);
739 wsetselect(w, q0, q0);
743 /* otherwise ordinary character; just insert */
745 q0 = winsert(w, &r, 1, q0);
754 w->cols[TEXT] = w->cols[HTEXT] = holdcol;
756 w->cols[TEXT] = w->cols[HTEXT] = lightholdcol;
759 w->cols[TEXT] = w->cols[HTEXT] = cols[TEXT];
761 w->cols[TEXT] = w->cols[HTEXT] = darkgrey;
771 wborder(w, Selborder);
774 wborder(w, Unselborder);
778 wbswidth(Window *w, Rune c)
784 /* there is known to be at least one character to erase */
785 if(c == 0x08) /* ^H: erase character */
794 if(r == '\n'){ /* eat at most one more character */
795 if(q == w->q0) /* eat the newline */
801 if(eq && skipping) /* found one; stop skipping */
803 else if(!eq && !skipping)
816 nsnarf = w->q1-w->q0;
817 snarf = runerealloc(snarf, nsnarf);
818 snarfversion++; /* maybe modified by parent */
819 runemove(snarf, w->r+w->q0, nsnarf);
828 wdelete(w, w->q0, w->q1);
829 wsetselect(w, w->q0, w->q0);
841 if(w->rawing && q0==w->nr){
842 waddraw(w, snarf, nsnarf);
843 wsetselect(w, q0, q0);
845 q0 = winsert(w, snarf, nsnarf, w->q0);
846 wsetselect(w, q0, q0+nsnarf);
860 fd = plumbopen("send", OWRITE|OCEXEC);
863 m = emalloc(sizeof(Plumbmsg));
864 m->src = estrdup("rio");
866 m->wdir = estrdup(w->dir);
867 m->type = estrdup("text");
873 while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
875 while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
877 sprint(buf, "click=%d", w->q0-p0);
878 m->attr = plumbunpackattr(buf);
880 if(p1-p0 > messagesize-1024){
882 return; /* too large for 9P */
884 m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
885 if(plumbsend(fd, m) < 0){
887 riosetcursor(&query, 1);
895 winborder(Window *w, Point xy)
897 return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder));
905 if(w->mc.buttons == 1)
907 else if(w->mc.buttons == 2)
909 else if(w->mc.buttons == 4)
912 if(w->mc.buttons == 8)
913 wkeyctl(w, Kscrolloneup);
914 if(w->mc.buttons == 16)
915 wkeyctl(w, Kscrollonedown);
919 incref(w); /* hold up window while we track */
922 if(ptinrect(w->mc.xy, w->scrollr)){
929 /* else all is handled by main process */
935 wdelete(Window *w, uint q0, uint q1)
942 runemove(w->r+q0, w->r+q1, w->nr-q1);
945 w->q0 -= min(n, w->q0-q0);
947 w->q1 -= min(n, w->q1-q0);
954 else if(q0 < w->org+w->nchars){
969 static Window *clickwin;
970 static uint clickmsec;
971 static Window *selectwin;
975 * called from frame library
978 framescroll(Frame *f, int dl)
980 if(f != &selectwin->Frame)
981 error("frameselect not right frame");
982 wframescroll(selectwin, dl);
986 wframescroll(Window *w, int dl)
995 q0 = wbacknl(w, w->org, -dl);
996 if(selectq > w->org+w->p0)
997 wsetselect(w, w->org+w->p0, selectq);
999 wsetselect(w, selectq, w->org+w->p0);
1001 if(w->org+w->nchars == w->nr)
1003 q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+dl*w->font->height));
1004 if(selectq >= w->org+w->p1)
1005 wsetselect(w, w->org+w->p1, selectq);
1007 wsetselect(w, selectq, w->org+w->p1);
1009 wsetorigin(w, q0, TRUE);
1021 * Double-click immediately if it might make sense.
1026 selectq = w->org+frcharofpt(w, w->mc.xy);
1027 if(clickwin==w && w->mc.msec-clickmsec<500)
1028 if(q0==q1 && selectq==w->q0){
1029 wdoubleclick(w, &q0, &q1);
1030 wsetselect(w, q0, q1);
1031 flushimage(display, 1);
1034 /* stay here until something interesting happens */
1037 while(w->mc.buttons==b && abs(w->mc.xy.x-x)<3 && abs(w->mc.xy.y-y)<3);
1038 w->mc.xy.x = x; /* in case we're calling frselect */
1040 q0 = w->q0; /* may have changed */
1044 if(w->mc.buttons == b){
1045 w->scroll = framescroll;
1046 frselect(w, &w->mc);
1047 /* horrible botch: while asleep, may have lost selection altogether */
1049 selectq = w->org + w->p0;
1050 w->Frame.scroll = nil;
1051 if(selectq < w->org)
1054 q0 = w->org + w->p0;
1055 if(selectq > w->org+w->nchars)
1061 if(q0==w->q0 && clickwin==w && w->mc.msec-clickmsec<500){
1062 wdoubleclick(w, &q0, &q1);
1066 clickmsec = w->mc.msec;
1070 wsetselect(w, q0, q1);
1071 flushimage(display, 1);
1072 while(w->mc.buttons){
1088 flushimage(display, 1);
1089 while(w->mc.buttons == b)
1096 wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
1103 send(w->cctl, &wcm);
1107 wctlmesg(Window *w, int m, Rectangle r, Image *i)
1113 error("unknown control message");
1124 strcpy(buf, w->name);
1125 wresize(w, i, m==Moved);
1127 proccreate(deletetimeoutproc, estrdup(buf), 4096);
1131 }else if(w == input)
1133 flushimage(display, 1);
1136 if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
1140 flushimage(display, 1);
1143 if(sweeping || !ptinrect(r.min, w->i->r))
1145 wmovemouse(w, r.min);
1152 wkeyctl(w, w->raw[0]);
1154 runemove(w->raw, w->raw+1, w->nraw);
1162 flushimage(display, 1);
1167 write(w->notefd, "hangup", 6);
1168 proccreate(deletetimeoutproc, estrdup(w->name), 4096);
1177 chanfree(w->conswrite);
1178 chanfree(w->consread);
1179 chanfree(w->mouseread);
1180 chanfree(w->wctlread);
1181 chanfree(w->kbdread);
1193 * Convert back to physical coordinates
1196 wmovemouse(Window *w, Point p)
1198 p.x += w->screenr.min.x-w->i->r.min.x;
1199 p.y += w->screenr.min.y-w->i->r.min.y;
1200 moveto(mousectl, p);
1204 wborder(Window *w, int type)
1211 if(type == Selborder)
1216 if(type == Selborder)
1219 col = lighttitlecol;
1222 border(w->i, w->i->r, Selborder, col, ZP);
1232 for(i=0; i<nwindow; i++){
1234 if(ptinrect(pt, v->screenr))
1236 if(w==nil || v->topped>w->topped)
1247 if(wkeyboard!=nil && w==wkeyboard)
1251 if(oi!=w && oi!=nil)
1260 wsendctlmesg(oi, Wakeup, ZR, nil);
1264 wsendctlmesg(w, Wakeup, ZR, nil);
1270 wsetcursor(Window *w, int force)
1274 if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
1276 else if(wpointto(mouse->xy) == w){
1278 if(p==nil && w->holding)
1283 riosetcursor(p, force && !menuing);
1287 riosetcursor(Cursor *p, int force)
1289 if(!force && p==lastcursor)
1291 setcursor(mousectl, p);
1302 if(w->topped == topped)
1306 flushimage(display, 1);
1307 w->topped = ++topped;
1315 if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){
1317 flushimage(display, 1);
1318 w->topped = ++ topped;
1323 wbottomme(Window *w)
1325 if(w!=nil && w->i!=nil && !w->deleted){
1327 flushimage(display, 1);
1328 w->topped = - ++topped;
1337 for(i=0; i<nwindow; i++)
1338 if(window[i]->id == id)
1344 wclosewin(Window *w)
1356 for(i=0; i<nhidden; i++)
1359 memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
1362 for(i=0; i<nwindow; i++)
1365 memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*));
1368 /* move it off-screen to hide it, in case client is slow in letting it go */
1369 MOVEIT originwindow(w->i, r.min, view->r.max);
1374 error("unknown window in closewin");
1378 wsetpid(Window *w, int pid, int dolabel)
1385 sprint(buf, "rc %d", pid);
1387 w->label = estrdup(buf);
1389 sprint(buf, "/proc/%d/notepg", pid);
1390 fd = open(buf, OWRITE|OCEXEC);
1397 winshell(void *args)
1411 rfork(RFNAMEG|RFFDG|RFENVG);
1412 if(filsysmount(filsys, w->id) < 0){
1413 fprint(2, "mount failed: %r\n");
1415 threadexits("mount failed");
1418 if(open("/dev/cons", OREAD) < 0){
1419 fprint(2, "can't open /dev/cons: %r\n");
1421 threadexits("/dev/cons");
1424 if(open("/dev/cons", OWRITE) < 0){
1425 fprint(2, "can't open /dev/cons: %r\n");
1427 threadexits("open"); /* BUG? was terminate() */
1429 if(wclose(w) == 0){ /* remove extra ref hanging from creation */
1434 procexec(pidc, cmd, argv);
1435 _exits("exec failed");
1439 static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 };
1440 static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
1441 static Rune left2[] = { L'\n', 0 };
1442 static Rune left3[] = { L'\'', L'"', L'`', 0 };
1458 wdoubleclick(Window *w, uint *q0, uint *q1)
1464 for(i=0; left[i]!=nil; i++){
1468 /* try matching character to left, looking right */
1475 if(wclickmatch(w, c, r[p-l], 1, &q))
1479 /* try matching character to right, looking left */
1486 if(wclickmatch(w, c, l[p-r], -1, &q)){
1487 *q1 = *q0+(*q0<w->nr && c=='\n');
1489 if(c!='\n' || q!=0 || w->r[0]=='\n')
1495 /* try filling out word to right */
1496 while(*q1<w->nr && isalnum(w->r[*q1]))
1498 /* try filling out word to left */
1499 while(*q0>0 && isalnum(w->r[*q0-1]))
1504 wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
1528 return cl=='\n' && nest==1;
1533 wbacknl(Window *w, uint p, uint n)
1537 /* look for start of this line if n==0 */
1538 if(n==0 && p>0 && w->r[p-1]!='\n')
1541 while(i-->0 && p>0){
1542 --p; /* it's at a newline now; back over it */
1545 /* at 128 chars, call it a line anyway */
1546 for(j=128; --j>0 && p>0; p--)
1554 wshow(Window *w, uint q0)
1560 qe = w->org+w->nchars;
1561 if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
1564 nl = 4*w->maxlines/5;
1565 q = wbacknl(w, q0, nl);
1566 /* avoid going backwards if trying to go forwards - long lines! */
1567 if(!(q0>w->org && q<w->org))
1568 wsetorigin(w, q, TRUE);
1569 while(q0 > w->org+w->nchars)
1570 wsetorigin(w, w->org+1, FALSE);
1575 wsetorigin(Window *w, uint org, int exact)
1581 if(org>0 && !exact){
1582 /* org is an estimate of the char posn; find a newline */
1583 /* don't try harder than 256 chars */
1584 for(i=0; i<256 && org<w->nr; i++){
1585 if(w->r[org] == '\n'){
1594 if(a>=0 && a<w->nchars){
1596 fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
1597 }else if(a<0 && -a<w->nchars){
1600 runemove(r, w->r+org, n);
1601 frinsert(w, r, r+n, 0);
1604 frdelete(w, 0, w->nchars);
1608 wsetselect(w, w->q0, w->q1);
1609 if(fixup && w->p1 > w->p0)
1610 frdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1);
1614 wsetselect(Window *w, uint q0, uint q1)
1618 /* w->p0 and w->p1 are always right; w->q0 and w->q1 may be off */
1621 /* compute desired p0,p1 from q0,q1 */
1632 if(p0==w->p0 && p1==w->p1)
1634 /* screen disagrees with desired selection */
1635 if(w->p1<=p0 || p1<=w->p0 || p0==p1 || w->p1==w->p0){
1636 /* no overlap or too easy to bother trying */
1637 frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 0);
1638 frdrawsel(w, frptofchar(w, p0), p0, p1, 1);
1641 /* overlap; avoid unnecessary painting */
1643 /* extend selection backwards */
1644 frdrawsel(w, frptofchar(w, p0), p0, w->p0, 1);
1645 }else if(p0 > w->p0){
1646 /* trim first part of selection */
1647 frdrawsel(w, frptofchar(w, w->p0), w->p0, p0, 0);
1650 /* extend selection forwards */
1651 frdrawsel(w, frptofchar(w, w->p1), w->p1, p1, 1);
1652 }else if(p1 < w->p1){
1653 /* trim last part of selection */
1654 frdrawsel(w, frptofchar(w, p1), p1, w->p1, 0);
1663 winsert(Window *w, Rune *r, int n, uint q0)
1669 if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
1670 m = min(HiWater-LoWater, min(w->org, w->qh));
1682 runemove(w->r, w->r+m, w->nr);
1685 if(w->nr+n > w->maxr){
1687 * Minimize realloc breakage:
1688 * Allocate at least MinWater
1689 * Double allocation size each time
1690 * But don't go much above HiWater
1692 m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
1694 m = max(HiWater+MinWater, w->nr+n);
1696 w->r = runerealloc(w->r, m);
1700 runemove(w->r+q0+n, w->r+q0, w->nr-q0);
1701 runemove(w->r+q0, r, n);
1703 /* if output touches, advance selection, not qh; works best for keyboard and output */
1712 else if(q0 <= w->org+w->nchars)
1713 frinsert(w, r, r+n, q0-w->org);
1725 rp = malloc(messagesize);
1727 n = w->nr-(w->org+w->nchars);
1730 if(n > 2000) /* educated guess at reasonable amount */
1732 runemove(rp, w->r+(w->org+w->nchars), n);
1734 * it's expensive to frinsert more than we need, so
1737 nl = w->maxlines-w->nlines;
1740 if(rp[i++] == '\n'){
1746 frinsert(w, rp, rp+i, w->nchars);
1747 }while(w->lastlinefull == FALSE);
1752 wcontents(Window *w, int *ip)
1754 return runetobyte(w->r, w->nr, ip);