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 */
26 static Cursor *lastcursor;
29 wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling)
34 w = emalloc(sizeof(Window));
36 r = insetrect(i->r, Selborder+1);
42 w->conswrite = chancreate(sizeof(Conswritemesg), 0);
43 w->consread = chancreate(sizeof(Consreadmesg), 0);
44 w->kbdread = chancreate(sizeof(Kbdreadmesg), 0);
45 w->mouseread = chancreate(sizeof(Mousereadmesg), 0);
46 w->wctlread = chancreate(sizeof(Consreadmesg), 0);
47 w->complete = chancreate(sizeof(Completion*), 0);
48 w->gone = chancreate(sizeof(char*), 0);
50 w->scrollr.max.x = r.min.x+Scrollwid;
52 r.min.x += Scrollwid+Scrollgap;
53 frinit(w, r, font, i, cols);
54 w->maxtab = maxtab*stringwidth(font, "0");
58 w->scrolling = scrolling;
59 w->dir = estrdup(startdir);
60 w->label = estrdup("<unnamed>");
61 r = insetrect(w->i->r, Selborder);
62 draw(w->i, r, cols[BACK], nil, w->entire.min);
63 wborder(w, Selborder);
65 incref(w); /* ref will be removed after mounting; avoids delete before ready to be deleted */
75 n = snprint(w->name, sizeof(w->name)-2, "window.%d.%d", w->id, w->namecount++);
76 for(i='A'; i<='Z'; i++){
77 if(nameimage(w->i, w->name, 1) > 0)
79 errstr(err, sizeof err);
80 if(strcmp(err, "image name in use") != 0)
86 fprint(2, "rio: setname failed: %s\n", err);
90 wresize(Window *w, Image *i, int move)
95 if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
96 draw(i, i->r, w->i, nil, w->i->r.min);
100 r = insetrect(i->r, Selborder+1);
102 w->scrollr.max.x = r.min.x+Scrollwid;
104 r.min.x += Scrollwid+Scrollgap;
106 frsetrects(w, r, w->i);
109 frinit(w, r, w->font, w->i, cols);
111 w->maxtab = maxtab*stringwidth(w->font, "0");
112 r = insetrect(w->i->r, Selborder);
113 draw(w->i, r, cols[BACK], nil, w->entire.min);
115 wsetselect(w, w->q0, w->q1);
118 wborder(w, Selborder);
120 w->topped = ++topped;
127 wrefresh(Window *w, Rectangle r)
129 /* BUG: rectangle is ignored */
131 wborder(w, Selborder);
133 wborder(w, Unselborder);
134 r = insetrect(w->i->r, Selborder);
135 draw(w->i, r, w->cols[BACK], nil, w->entire.min);
138 frdrawsel(w, frptofchar(w, 0), 0, w->p0, 0);
139 if(w->p1 < w->nchars)
140 frdrawsel(w, frptofchar(w, w->p1), w->p1, w->nchars, 0);
141 frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 1);
155 error("negative ref count");
157 wsendctlmesg(w, Exited, ZR, nil);
162 showcandidates(Window *, Completion *);
167 Rune *rp, *bp, *tp, *up;
169 int nr, nb, c, wid, i, npart, initial, lastb;
170 char *s, *t, part[3];
173 enum { WKbd, WKbdread, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, WComplete, Wgone, NWALT };
183 char buf[4*12+1], *kbdq[8], *kbds;
187 snprint(buf, sizeof buf, "winctl-id%d", w->id);
190 mrm.cm = chancreate(sizeof(Mouse), 0);
191 krm.ck = chancreate(sizeof(char*), 0);
192 cwm.cw = chancreate(sizeof(Stringpair), 0);
193 crm.c1 = chancreate(sizeof(Stringpair), 0);
194 crm.c2 = chancreate(sizeof(Stringpair), 0);
195 cwrm.c1 = chancreate(sizeof(Stringpair), 0);
196 cwrm.c2 = chancreate(sizeof(Stringpair), 0);
198 alts[WKbd].c = w->ck;
199 alts[WKbd].v = &kbds;
200 alts[WKbd].op = CHANRCV;
201 alts[WKbdread].c = w->kbdread;
202 alts[WKbdread].v = &krm;
203 alts[WKbdread].op = CHANSND;
204 alts[WMouse].c = w->mc.c;
205 alts[WMouse].v = &w->mc.Mouse;
206 alts[WMouse].op = CHANRCV;
207 alts[WMouseread].c = w->mouseread;
208 alts[WMouseread].v = &mrm;
209 alts[WMouseread].op = CHANSND;
210 alts[WCtl].c = w->cctl;
212 alts[WCtl].op = CHANRCV;
213 alts[WCwrite].c = w->conswrite;
214 alts[WCwrite].v = &cwm;
215 alts[WCwrite].op = CHANSND;
216 alts[WCread].c = w->consread;
217 alts[WCread].v = &crm;
218 alts[WCread].op = CHANSND;
219 alts[WWread].c = w->wctlread;
220 alts[WWread].v = &cwrm;
221 alts[WWread].op = CHANSND;
222 alts[WComplete].c = w->complete;
223 alts[WComplete].v = &cr;
224 alts[WComplete].op = CHANRCV;
225 alts[Wgone].c = w->gone;
226 alts[Wgone].v = "window deleted";
227 alts[Wgone].op = CHANNOP;
228 alts[NWALT].op = CHANEND;
230 memset(kbdq, 0, sizeof(kbdq));
237 alts[Wgone].op = CHANSND;
239 alts[WKbdread].op = CHANNOP;
240 alts[WMouseread].op = CHANNOP;
241 alts[WCwrite].op = CHANNOP;
242 alts[WWread].op = CHANNOP;
243 alts[WCread].op = CHANNOP;
245 alts[WKbdread].op = (w->kbdopen && kbdqw != kbdqr) ?
247 alts[WMouseread].op = (w->mouseopen && w->mouse.counter != w->mouse.lastcounter) ?
249 alts[WCwrite].op = w->scrolling || w->mouseopen || (w->qh <= w->org+w->nchars) ?
251 alts[WWread].op = w->wctlready ?
253 /* this code depends on NL and EOT fitting in a single byte */
254 /* kind of expensive for each loop; worth precomputing? */
256 alts[WCread].op = CHANNOP;
257 else if(npart || (w->rawing && w->nraw>0))
258 alts[WCread].op = CHANSND;
260 alts[WCread].op = CHANNOP;
261 for(i=w->qh; i<w->nr; i++){
263 if(c=='\n' || c=='\004'){
264 alts[WCread].op = CHANSND;
273 i = (kbdqw+1) % nelem(kbdq);
276 } else if(*kbds == 'c'){
279 chartorune(&r, kbds+1);
288 i = (kbdqr+1) % nelem(kbdq);
292 sendp(krm.ck, kbdq[i]);
295 sendp(krm.ck, strdup("K"));
302 /* queue click events */
303 if(!w->mouse.qfull && lastb != w->mc.buttons) { /* add to ring */
304 mp = &w->mouse.queue[w->mouse.wi];
305 if(++w->mouse.wi == nelem(w->mouse.queue))
307 if(w->mouse.wi == w->mouse.ri)
308 w->mouse.qfull = TRUE;
310 mp->counter = w->mouse.counter;
311 lastb = w->mc.buttons;
317 /* send a queued event or, if the queue is empty, the current state */
318 /* if the queue has filled, we discard all the events it contained. */
319 /* the intent is to discard frantic clicking by the user during long latencies. */
320 w->mouse.qfull = FALSE;
321 if(w->mouse.wi != w->mouse.ri) {
322 m = w->mouse.queue[w->mouse.ri];
323 if(++w->mouse.ri == nelem(w->mouse.queue))
326 m = (Mousestate){w->mc.Mouse, w->mouse.counter};
328 w->mouse.lastcounter = m.counter;
329 send(mrm.cm, &m.Mouse);
332 if(wctlmesg(w, wcm.type, wcm.r, wcm.p) == Exited){
333 for(i=0; i<nelem(kbdq); i++)
371 wdelete(w, qh, qh+initial);
380 w->qh = winsert(w, rp, nr, w->qh)+nr;
381 if(w->scrolling || w->mouseopen)
383 wsetselect(w, w->q0, w->q1);
395 while(i<nb && (w->qh<w->nr || w->nraw>0)){
397 wid = runetochar(t+i, &w->raw[0]);
399 runemove(w->raw, w->raw+1, w->nraw);
401 wid = runetochar(t+i, &w->r[w->qh++]);
402 c = t[i]; /* knows break characters fit in a byte */
404 if(!w->rawing && (c == '\n' || c=='\004')){
410 if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004')
414 memmove(part, t+nb, npart);
423 recv(cwrm.c1, &pair);
424 s = Dx(w->screenr) > 0 ? "visible" : "hidden";
428 pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
429 w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
430 send(cwrm.c2, &pair);
435 showcandidates(w, cr);
437 rp = runesmprint("%s", cr->string);
440 q0 = winsert(w, rp, nr, q0);
448 if(w->i!=nil && Dx(w->screenr) > 0)
449 flushimage(display, 1);
454 waddraw(Window *w, Rune *r, int nr)
456 w->raw = runerealloc(w->raw, w->nraw+nr);
457 runemove(w->raw+w->nraw, r, nr);
462 * Need to do this in a separate proc because if process we're interrupting
463 * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
466 interruptproc(void *v)
471 write(*notefd, "interrupt", 9);
477 windfilewidth(Window *w, uint q0, int oneelement)
487 if(oneelement && r=='/')
495 showcandidates(Window *w, Completion *c)
505 s = "[no matches in ";
509 fmtprint(&f, "%s%d files]\n", s, c->nfile);
511 fmtprint(&f, "%s", s);
512 for(i=0; i<c->nfile; i++){
515 fmtprint(&f, "%s", c->filename[i]);
519 rp = runefmtstrflush(&f);
522 /* place text at beginning of line before cursor and host point */
523 qline = min(w->qh, w->q0);
524 while(qline>0 && w->r[qline-1] != '\n')
528 /* advance host point to avoid readback */
529 w->qh = winsert(w, rp, nr, qline)+nr;
531 winsert(w, rp, nr, qline);
536 typedef struct Completejob Completejob;
545 completeproc(void *arg)
552 snprint(buf, sizeof(buf), "namecomplete %s", job->dir);
555 c = complete(job->dir, job->str);
556 if(c != nil && sendp(job->win->complete, c) <= 0)
567 namecomplete(Window *w)
574 /* control-f: filename completion; works back to white space or / */
575 if(w->q0<w->nr && w->r[w->q0]>' ') /* must be at end of word */
577 nstr = windfilewidth(w, w->q0, TRUE);
578 str = runemalloc(nstr);
579 runemove(str, w->r+(w->q0-nstr), nstr);
580 npath = windfilewidth(w, w->q0-nstr, FALSE);
581 path = runemalloc(npath);
582 runemove(path, w->r+(w->q0-nstr-npath), npath);
584 /* is path rooted? if not, we need to make it relative to window path */
585 if(npath>0 && path[0]=='/'){
586 dir = malloc(UTFmax*npath+1);
587 sprint(dir, "%.*S", npath, path);
589 if(strcmp(w->dir, "") == 0)
593 dir = malloc(strlen(root)+1+UTFmax*npath+1);
594 sprint(dir, "%s/%.*S", root, npath, path);
597 /* run in background, winctl will collect the result on w->complete chan */
598 job = emalloc(sizeof *job);
599 job->str = smprint("%.*S", nstr, str);
600 job->dir = cleanname(dir);
603 proccreate(completeproc, job, STACK);
610 wkeyctl(Window *w, Rune r)
629 /* navigation keys work only when mouse and kbd is not open */
633 n = shiftdown ? 1 : w->maxlines/3;
636 n = mousescrollsize(w->maxlines);
643 q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+n*w->font->height));
644 wsetorigin(w, q0, TRUE);
647 n = shiftdown ? 1 : w->maxlines/3;
650 n = mousescrollsize(w->maxlines);
657 q0 = wbacknl(w, w->org, n);
658 wsetorigin(w, q0, TRUE);
663 wsetselect(w, q0, q0);
670 wsetselect(w, q1, q1);
684 case Ksoh: /* ^A: beginning of line */
685 if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
687 nb = wbswidth(w, 0x15 /* ^U */);
688 wsetselect(w, w->q0-nb, w->q0-nb);
691 case Kenq: /* ^E: end of line */
693 while(q0 < w->nr && w->r[q0]!='\n')
695 wsetselect(w, q0, q0);
698 case Kstx: /* ^B: output point */
699 wsetselect(w, w->qh, w->qh);
703 if(w->rawing && (w->q0==w->nr || w->mouseopen)){
707 if(r==Kesc || (w->holding && r==Kdel)){ /* toggle hold */
722 case Kdel: /* send interrupt */
727 notefd = emalloc(sizeof(int));
728 *notefd = dup(w->notefd, -1);
729 proccreate(interruptproc, notefd, 4096);
731 case Kack: /* ^F: file name completion */
732 case Kins: /* Insert: file name completion */
735 case Kbs: /* ^H: erase character */
736 case Knack: /* ^U: erase line */
737 case Ketb: /* ^W: erase word */
738 if(w->q0==0 || w->q0==w->qh)
748 wdelete(w, q0, q0+nb);
749 wsetselect(w, q0, q0);
753 /* otherwise ordinary character; just insert */
755 q0 = winsert(w, &r, 1, q0);
760 wsetcols(Window *w, int topped)
764 w->cols[TEXT] = holdcol;
766 w->cols[TEXT] = lightholdcol;
769 w->cols[TEXT] = cols[TEXT];
771 w->cols[TEXT] = paletextcol;
777 wsetcols(w, w == input);
781 wborder(w, Selborder);
783 wborder(w, Unselborder);
787 wbswidth(Window *w, Rune c)
793 /* there is known to be at least one character to erase */
794 if(c == 0x08) /* ^H: erase character */
803 if(r == '\n'){ /* eat at most one more character */
804 if(q == w->q0) /* eat the newline */
810 if(eq && skipping) /* found one; stop skipping */
812 else if(!eq && !skipping)
825 nsnarf = w->q1-w->q0;
826 snarf = runerealloc(snarf, nsnarf);
827 snarfversion++; /* maybe modified by parent */
828 runemove(snarf, w->r+w->q0, nsnarf);
837 wdelete(w, w->q0, w->q1);
838 wsetselect(w, w->q0, w->q0);
850 if(w->rawing && q0==w->nr){
851 waddraw(w, snarf, nsnarf);
852 wsetselect(w, q0, q0);
854 q0 = winsert(w, snarf, nsnarf, w->q0);
855 wsetselect(w, q0, q0+nsnarf);
869 fd = plumbopen("send", OWRITE|OCEXEC);
872 m = emalloc(sizeof(Plumbmsg));
873 m->src = estrdup("rio");
875 m->wdir = estrdup(w->dir);
876 m->type = estrdup("text");
882 while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
884 while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
886 snprint(buf, sizeof(buf), "click=%d", w->q0-p0);
887 m->attr = plumbunpackattr(buf);
889 if(p1-p0 > messagesize-1024){
891 return; /* too large for 9P */
893 m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
894 if(plumbsend(fd, m) < 0){
896 riosetcursor(&query, 1);
904 winborder(Window *w, Point xy)
906 return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder));
914 if(w->mc.buttons == 1)
916 else if(w->mc.buttons == 2)
918 else if(w->mc.buttons == 4)
921 if(w->mc.buttons == 8)
922 wkeyctl(w, Kscrolloneup);
923 if(w->mc.buttons == 16)
924 wkeyctl(w, Kscrollonedown);
928 incref(w); /* hold up window while we track */
931 if(ptinrect(w->mc.xy, w->scrollr)){
938 /* else all is handled by main process */
944 wdelete(Window *w, uint q0, uint q1)
951 runemove(w->r+q0, w->r+q1, w->nr-q1);
954 w->q0 -= min(n, w->q0-q0);
956 w->q1 -= min(n, w->q1-q0);
963 else if(q0 < w->org+w->nchars){
978 static Window *clickwin;
979 static uint clickmsec;
980 static Window *selectwin;
984 * called from frame library
987 framescroll(Frame *f, int dl)
989 if(f != &selectwin->Frame)
990 error("frameselect not right frame");
991 wframescroll(selectwin, dl);
995 wframescroll(Window *w, int dl)
1004 q0 = wbacknl(w, w->org, -dl);
1005 if(selectq > w->org+w->p0)
1006 wsetselect(w, w->org+w->p0, selectq);
1008 wsetselect(w, selectq, w->org+w->p0);
1010 if(w->org+w->nchars == w->nr)
1012 q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+dl*w->font->height));
1013 if(selectq >= w->org+w->p1)
1014 wsetselect(w, w->org+w->p1, selectq);
1016 wsetselect(w, selectq, w->org+w->p1);
1018 wsetorigin(w, q0, TRUE);
1030 * Double-click immediately if it might make sense.
1035 selectq = w->org+frcharofpt(w, w->mc.xy);
1036 if(clickwin==w && w->mc.msec-clickmsec<500)
1037 if(q0==q1 && selectq==w->q0){
1038 wdoubleclick(w, &q0, &q1);
1039 wsetselect(w, q0, q1);
1040 flushimage(display, 1);
1043 /* stay here until something interesting happens */
1046 while(w->mc.buttons==b && abs(w->mc.xy.x-x)<3 && abs(w->mc.xy.y-y)<3);
1047 w->mc.xy.x = x; /* in case we're calling frselect */
1049 q0 = w->q0; /* may have changed */
1053 if(w->mc.buttons == b){
1054 w->scroll = framescroll;
1055 frselect(w, &w->mc);
1056 /* horrible botch: while asleep, may have lost selection altogether */
1058 selectq = w->org + w->p0;
1059 w->Frame.scroll = nil;
1060 if(selectq < w->org)
1063 q0 = w->org + w->p0;
1064 if(selectq > w->org+w->nchars)
1070 if(q0==w->q0 && clickwin==w && w->mc.msec-clickmsec<500){
1071 wdoubleclick(w, &q0, &q1);
1075 clickmsec = w->mc.msec;
1079 wsetselect(w, q0, q1);
1080 flushimage(display, 1);
1081 while(w->mc.buttons){
1097 flushimage(display, 1);
1098 while(w->mc.buttons == b)
1105 wsendctlmesg(Window *w, int type, Rectangle r, void *p)
1112 send(w->cctl, &wcm);
1116 wctlmesg(Window *w, int m, Rectangle r, void *p)
1123 error("unknown control message");
1127 sendp((Channel*)p, w);
1136 strcpy(buf, w->name);
1137 wresize(w, i, m==Moved);
1138 proccreate(deletetimeoutproc, estrdup(buf), 4096);
1139 flushimage(display, 1);
1140 if(Dx(r)<=0){ /* window got hidden, if we had the input, drop it */
1145 /* fall through to get input if needed */
1147 if(w->deleted || w==input)
1157 * have to wait until old input responds before
1158 * changing input to us because the window might
1159 * currently be mouse tracking and it is not
1160 * prepared for getting its input revoked.
1162 c = chancreate(sizeof(void*), 0);
1163 wsendctlmesg(oi, Wakeup, ZR, c);
1168 * if we are still top window and nobody else has taken
1169 * input from original window, take the input.
1171 if(!w->deleted && w->topped==topped && oi==input){
1175 wsendctlmesg(oi, Repaint, ZR, nil);
1181 if(m!=Topped && w==input)
1183 /* fall thrugh for redraw after input change */
1185 if(w->i==nil || Dx(w->screenr)<=0)
1188 flushimage(display, 1);
1191 if(w->i==nil || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r) || w->mouseopen)
1194 flushimage(display, 1);
1197 if(w->i==nil || Dx(w->screenr)<=0 || !ptinrect(r.min, w->i->r))
1199 wmovemouse(w, r.min);
1204 wkeyctl(w, w->raw[0]);
1206 runemove(w->raw, w->raw+1, w->nraw);
1216 flushimage(display, 1);
1221 write(w->notefd, "hangup", 6);
1223 proccreate(deletetimeoutproc, estrdup(w->name), 4096);
1235 chanfree(w->conswrite);
1236 chanfree(w->consread);
1237 chanfree(w->mouseread);
1238 chanfree(w->wctlread);
1239 chanfree(w->kbdread);
1240 chanfree(w->complete);
1253 * Convert back to physical coordinates
1256 wmovemouse(Window *w, Point p)
1258 if(w != input || menuing || sweeping)
1260 p.x += w->screenr.min.x-w->i->r.min.x;
1261 p.y += w->screenr.min.y-w->i->r.min.y;
1262 moveto(mousectl, p);
1266 wborder(Window *w, int type)
1273 if(type == Selborder)
1278 if(type == Selborder)
1281 col = lighttitlecol;
1283 border(w->i, w->i->r, Selborder, col, ZP);
1293 for(i=0; i<nwindow; i++){
1295 if(ptinrect(pt, v->screenr))
1296 if(w==nil || v->topped>w->topped)
1305 if(w!=nil && w!=input)
1306 wsendctlmesg(w, Topped, ZR, nil);
1310 wsetcursor(Window *w, int force)
1314 if(w==nil || w->i==nil || Dx(w->screenr)<=0)
1316 else if(wpointto(mouse->xy) == w){
1318 if(p==nil && w->holding)
1323 riosetcursor(p, force && !menuing);
1327 riosetcursor(Cursor *p, int force)
1329 if(!force && p==lastcursor)
1331 setcursor(mousectl, p);
1339 if(w!=nil && w->i!=nil && w->topped!=topped){
1340 w->topped = ++topped;
1342 flushimage(display, 1);
1347 wbottomme(Window *w)
1349 if(w!=nil && w->i!=nil){
1350 w->topped = - ++topped;
1352 flushimage(display, 1);
1376 for(i=0; i<nwindow; i++)
1377 if(window[i]->id == id)
1396 for(i=0; i<nhidden; i++)
1399 memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
1402 for(i=0; i<nwindow; i++)
1405 memmove(window+i, window+i+1, (nwindow-i)*sizeof(window[0]));
1411 wclosewin(Window *w)
1415 assert(w->deleted==TRUE);
1420 /* move it off-screen to hide it, in case client is slow in letting it go */
1421 MOVEIT originwindow(i, i->r.min, view->r.max);
1427 wsetpid(Window *w, int pid, int dolabel)
1437 snprint(buf, sizeof(buf), "rc %d", pid);
1439 w->label = estrdup(buf);
1441 snprint(buf, sizeof(buf), "/proc/%d/notepg", pid);
1442 w->notefd = open(buf, OWRITE|OCEXEC);
1449 winshell(void *args)
1463 rfork(RFNAMEG|RFFDG|RFENVG);
1464 if(filsysmount(filsys, w->id) < 0){
1465 fprint(2, "mount failed: %r\n");
1467 threadexits("mount failed");
1470 if(open("/dev/cons", OREAD) < 0){
1471 fprint(2, "can't open /dev/cons: %r\n");
1473 threadexits("/dev/cons");
1476 if(open("/dev/cons", OWRITE) < 0){
1477 fprint(2, "can't open /dev/cons: %r\n");
1479 threadexits("open"); /* BUG? was terminate() */
1481 if(wclose(w) == 0){ /* remove extra ref hanging from creation */
1486 procexec(pidc, cmd, argv);
1487 _exits("exec failed");
1491 static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 };
1492 static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
1493 static Rune left2[] = { L'\n', 0 };
1494 static Rune left3[] = { L'\'', L'"', L'`', 0 };
1510 wdoubleclick(Window *w, uint *q0, uint *q1)
1516 for(i=0; left[i]!=nil; i++){
1520 /* try matching character to left, looking right */
1527 if(wclickmatch(w, c, r[p-l], 1, &q))
1531 /* try matching character to right, looking left */
1538 if(wclickmatch(w, c, l[p-r], -1, &q)){
1539 *q1 = *q0+(*q0<w->nr && c=='\n');
1541 if(c!='\n' || q!=0 || w->r[0]=='\n')
1547 /* try filling out word to right */
1548 while(*q1<w->nr && isalnum(w->r[*q1]))
1550 /* try filling out word to left */
1551 while(*q0>0 && isalnum(w->r[*q0-1]))
1556 wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
1580 return cl=='\n' && nest==1;
1585 wbacknl(Window *w, uint p, uint n)
1589 /* look for start of this line if n==0 */
1590 if(n==0 && p>0 && w->r[p-1]!='\n')
1593 while(i-->0 && p>0){
1594 --p; /* it's at a newline now; back over it */
1597 /* at 128 chars, call it a line anyway */
1598 for(j=128; --j>0 && p>0; p--)
1606 wshow(Window *w, uint q0)
1612 qe = w->org+w->nchars;
1613 if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
1616 nl = 4*w->maxlines/5;
1617 q = wbacknl(w, q0, nl);
1618 /* avoid going backwards if trying to go forwards - long lines! */
1619 if(!(q0>w->org && q<w->org))
1620 wsetorigin(w, q, TRUE);
1621 while(q0 > w->org+w->nchars)
1622 wsetorigin(w, w->org+1, FALSE);
1627 wsetorigin(Window *w, uint org, int exact)
1633 if(org>0 && !exact){
1634 /* org is an estimate of the char posn; find a newline */
1635 /* don't try harder than 256 chars */
1636 for(i=0; i<256 && org<w->nr; i++){
1637 if(w->r[org] == '\n'){
1646 if(a>=0 && a<w->nchars){
1648 fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
1649 }else if(a<0 && -a<w->nchars){
1652 runemove(r, w->r+org, n);
1653 frinsert(w, r, r+n, 0);
1656 frdelete(w, 0, w->nchars);
1660 wsetselect(w, w->q0, w->q1);
1661 if(fixup && w->p1 > w->p0)
1662 frdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1);
1666 wsetselect(Window *w, uint q0, uint q1)
1670 /* w->p0 and w->p1 are always right; w->q0 and w->q1 may be off */
1673 /* compute desired p0,p1 from q0,q1 */
1684 if(p0==w->p0 && p1==w->p1)
1686 /* screen disagrees with desired selection */
1687 if(w->p1<=p0 || p1<=w->p0 || p0==p1 || w->p1==w->p0){
1688 /* no overlap or too easy to bother trying */
1689 frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 0);
1690 frdrawsel(w, frptofchar(w, p0), p0, p1, 1);
1693 /* overlap; avoid unnecessary painting */
1695 /* extend selection backwards */
1696 frdrawsel(w, frptofchar(w, p0), p0, w->p0, 1);
1697 }else if(p0 > w->p0){
1698 /* trim first part of selection */
1699 frdrawsel(w, frptofchar(w, w->p0), w->p0, p0, 0);
1702 /* extend selection forwards */
1703 frdrawsel(w, frptofchar(w, w->p1), w->p1, p1, 1);
1704 }else if(p1 < w->p1){
1705 /* trim last part of selection */
1706 frdrawsel(w, frptofchar(w, p1), p1, w->p1, 0);
1715 winsert(Window *w, Rune *r, int n, uint q0)
1721 if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
1722 m = min(HiWater-LoWater, min(w->org, w->qh));
1734 runemove(w->r, w->r+m, w->nr);
1737 if(w->nr+n > w->maxr){
1739 * Minimize realloc breakage:
1740 * Allocate at least MinWater
1741 * Double allocation size each time
1742 * But don't go much above HiWater
1744 m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
1746 m = max(HiWater+MinWater, w->nr+n);
1748 w->r = runerealloc(w->r, m);
1752 runemove(w->r+q0+n, w->r+q0, w->nr-q0);
1753 runemove(w->r+q0, r, n);
1755 /* if output touches, advance selection, not qh; works best for keyboard and output */
1764 else if(q0 <= w->org+w->nchars)
1765 frinsert(w, r, r+n, q0-w->org);
1777 rp = malloc(messagesize);
1779 n = w->nr-(w->org+w->nchars);
1782 if(n > 2000) /* educated guess at reasonable amount */
1784 runemove(rp, w->r+(w->org+w->nchars), n);
1786 * it's expensive to frinsert more than we need, so
1789 nl = w->maxlines-w->nlines;
1792 if(rp[i++] == '\n'){
1798 frinsert(w, rp, rp+i, w->nchars);
1799 }while(w->lastlinefull == FALSE);
1804 wcontents(Window *w, int *ip)
1806 return runetobyte(w->r, w->nr, ip);