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(Consreadmesg), 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)
97 r = insetrect(i->r, Selborder+1);
99 w->scrollr.max.x = r.min.x+Scrollwid;
101 r.min.x += Scrollwid+Scrollgap;
103 frinit(w, r, w->font, w->i, cols);
105 w->maxtab = maxtab*stringwidth(w->font, "0");
106 r = insetrect(w->i->r, Selborder);
107 draw(w->i, r, cols[BACK], nil, w->entire.min);
109 wsetselect(w, w->q0, w->q1);
111 wborder(w, Selborder);
112 flushimage(display, 1);
114 w->topped = ++topped;
126 wborder(w, Selborder);
128 wborder(w, Unselborder);
129 r = insetrect(w->i->r, Selborder);
130 draw(w->i, r, w->cols[BACK], nil, w->entire.min);
133 frdrawsel(w, frptofchar(w, 0), 0, w->p0, 0);
134 if(w->p1 < w->nchars)
135 frdrawsel(w, frptofchar(w, w->p1), w->p1, w->nchars, 0);
136 frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 1);
150 error("negative ref count");
152 wsendctlmesg(w, Exited, ZR, nil);
157 showcandidates(Window *, Completion *);
164 int nr, nb, c, wid, i, npart, initial, lastb;
165 char *s, *t, part[3];
168 enum { WKbd, WKbdread, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, WComplete, Wgone, NWALT };
176 char *kbdq[32], *kbds;
180 threadsetname("winctl-id%d", w->id);
182 mrm.cm = chancreate(sizeof(Mouse), 0);
183 crm.c1 = chancreate(sizeof(Stringpair), 0);
184 crm.c2 = chancreate(sizeof(Stringpair), 0);
185 cwm.cw = chancreate(sizeof(Stringpair), 0);
187 alts[WKbd].c = w->ck;
188 alts[WKbd].v = &kbds;
189 alts[WKbd].op = CHANRCV;
190 alts[WKbdread].c = w->kbdread;
191 alts[WKbdread].v = &crm;
192 alts[WKbdread].op = CHANSND;
193 alts[WMouse].c = w->mc.c;
194 alts[WMouse].v = &w->mc.Mouse;
195 alts[WMouse].op = CHANRCV;
196 alts[WMouseread].c = w->mouseread;
197 alts[WMouseread].v = &mrm;
198 alts[WMouseread].op = CHANSND;
199 alts[WCtl].c = w->cctl;
201 alts[WCtl].op = CHANRCV;
202 alts[WCwrite].c = w->conswrite;
203 alts[WCwrite].v = &cwm;
204 alts[WCwrite].op = CHANSND;
205 alts[WCread].c = w->consread;
206 alts[WCread].v = &crm;
207 alts[WCread].op = CHANSND;
208 alts[WWread].c = w->wctlread;
209 alts[WWread].v = &crm;
210 alts[WWread].op = CHANSND;
211 alts[WComplete].c = w->complete;
212 alts[WComplete].v = &cr;
213 alts[WComplete].op = CHANRCV;
214 alts[Wgone].c = w->gone;
215 alts[Wgone].v = "window deleted";
216 alts[Wgone].op = CHANNOP;
217 alts[NWALT].op = CHANEND;
225 alts[Wgone].op = CHANSND;
227 alts[WKbdread].op = CHANNOP;
228 alts[WMouseread].op = CHANNOP;
229 alts[WCwrite].op = CHANNOP;
230 alts[WWread].op = CHANNOP;
231 alts[WCread].op = CHANNOP;
233 alts[WKbdread].op = (w->kbdopen && kbdqw != kbdqr) ?
235 alts[WMouseread].op = (w->mouseopen && w->mouse.counter != w->mouse.lastcounter) ?
237 alts[WCwrite].op = w->scrolling || w->mouseopen || (w->qh <= w->org+w->nchars) ?
239 alts[WWread].op = w->wctlready ?
241 /* this code depends on NL and EOT fitting in a single byte */
242 /* kind of expensive for each loop; worth precomputing? */
244 alts[WCread].op = CHANNOP;
245 else if(npart || (w->rawing && w->nraw>0))
246 alts[WCread].op = CHANSND;
248 alts[WCread].op = CHANNOP;
249 for(i=w->qh; i<w->nr; i++){
251 if(c=='\n' || c=='\004'){
252 alts[WCread].op = CHANSND;
260 if(kbdqw - kbdqr < nelem(kbdq))
261 kbdq[kbdqw++ % nelem(kbdq)] = kbds;
266 while(kbdqr != kbdqw){
267 kbds = kbdq[kbdqr++ % nelem(kbdq)];
269 chartorune(&r, kbds+1);
279 while(kbdqr != kbdqw){
280 kbds = kbdq[kbdqr % nelem(kbdq)];
284 memmove((char*)pair.s + nb, kbds, i);
296 /* queue click events */
297 if(!w->mouse.qfull && lastb != w->mc.buttons) { /* add to ring */
298 mp = &w->mouse.queue[w->mouse.wi];
299 if(++w->mouse.wi == nelem(w->mouse.queue))
301 if(w->mouse.wi == w->mouse.ri)
302 w->mouse.qfull = TRUE;
304 mp->counter = w->mouse.counter;
305 lastb = w->mc.buttons;
311 /* send a queued event or, if the queue is empty, the current state */
312 /* if the queue has filled, we discard all the events it contained. */
313 /* the intent is to discard frantic clicking by the user during long latencies. */
314 w->mouse.qfull = FALSE;
315 if(w->mouse.wi != w->mouse.ri) {
316 m = w->mouse.queue[w->mouse.ri];
317 if(++w->mouse.ri == nelem(w->mouse.queue))
320 m = (Mousestate){w->mc.Mouse, w->mouse.counter};
322 w->mouse.lastcounter = m.counter;
323 send(mrm.cm, &m.Mouse);
326 if(wctlmesg(w, wcm.type, wcm.r, wcm.p) == Exited){
327 while(kbdqr != kbdqw)
328 free(kbdq[kbdqr++ % nelem(kbdq)]);
357 wdelete(w, qh, qh+initial);
363 w->qh = winsert(w, rp, nr, w->qh)+nr;
364 if(w->scrolling || w->mouseopen)
366 wsetselect(w, w->q0, w->q1);
378 while(i<nb && (w->qh<w->nr || w->nraw>0)){
380 wid = runetochar(t+i, &w->raw[0]);
382 runemove(w->raw, w->raw+1, w->nraw);
384 wid = runetochar(t+i, &w->r[w->qh++]);
385 c = t[i]; /* knows break characters fit in a byte */
387 if(!w->rawing && (c == '\n' || c=='\004')){
393 if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004')
397 memmove(part, t+nb, npart);
407 s = Dx(w->screenr) > 0 ? "visible" : "hidden";
411 pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
412 w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
418 showcandidates(w, cr);
420 rp = runesmprint("%s", cr->string);
424 q0 = winsert(w, rp, nr, q0);
433 if(w->i!=nil && Dx(w->screenr) > 0 && display->bufp > display->buf)
434 flushimage(display, 1);
439 waddraw(Window *w, Rune *r, int nr)
441 w->raw = runerealloc(w->raw, w->nraw+nr);
442 runemove(w->raw+w->nraw, r, nr);
447 * Need to do this in a separate proc because if process we're interrupting
448 * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
451 interruptproc(void *v)
456 write(*notefd, "interrupt", 9);
462 windfilewidth(Window *w, uint q0, int oneelement)
470 if(r<=' ' || r=='=' || r=='^' || r=='(' || r=='{')
472 if(oneelement && r=='/')
480 showcandidates(Window *w, Completion *c)
490 s = "[no matches in ";
494 fmtprint(&f, "%s%d files]\n", s, c->nfile);
496 fmtprint(&f, "%s", s);
497 for(i=0; i<c->nfile; i++){
500 fmtprint(&f, "%s", c->filename[i]);
504 rp = runefmtstrflush(&f);
507 /* place text at beginning of line before cursor and host point */
508 qline = min(w->qh, w->q0);
509 while(qline>0 && w->r[qline-1] != '\n')
513 /* advance host point to avoid readback */
514 w->qh = winsert(w, rp, nr, qline)+nr;
516 winsert(w, rp, nr, qline);
521 typedef struct Completejob Completejob;
530 completeproc(void *arg)
536 threadsetname("namecomplete %s", job->dir);
538 c = complete(job->dir, job->str);
539 if(c != nil && sendp(job->win->complete, c) <= 0)
550 namecomplete(Window *w)
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 = w->r+(w->q0-nstr);
562 npath = windfilewidth(w, w->q0-nstr, FALSE);
563 path = w->r+(w->q0-nstr-npath);
565 /* is path rooted? if not, we need to make it relative to window path */
566 if(npath>0 && path[0]=='/')
567 dir = runetobyte(path, npath, &npath);
569 if(strcmp(w->dir, "") == 0)
573 dir = smprint("%s/%.*S", root, npath, path);
578 /* run in background, winctl will collect the result on w->complete chan */
579 job = emalloc(sizeof *job);
580 job->str = runetobyte(str, nstr, &nstr);
581 job->dir = cleanname(dir);
584 proccreate(completeproc, job, STACK);
588 wkeyctl(Window *w, Rune r)
607 /* navigation keys work only when mouse and kbd is not open */
611 n = shiftdown ? 1 : w->maxlines/3;
614 n = mousescrollsize(w->maxlines);
621 q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+n*w->font->height));
622 wsetorigin(w, q0, TRUE);
625 n = shiftdown ? 1 : w->maxlines/3;
628 n = mousescrollsize(w->maxlines);
635 q0 = wbacknl(w, w->org, n);
636 wsetorigin(w, q0, TRUE);
641 wsetselect(w, q0, q0);
648 wsetselect(w, q1, q1);
662 case Ksoh: /* ^A: beginning of line */
663 if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
665 nb = wbswidth(w, 0x15 /* ^U */);
666 wsetselect(w, w->q0-nb, w->q0-nb);
669 case Kenq: /* ^E: end of line */
671 while(q0 < w->nr && w->r[q0]!='\n')
673 wsetselect(w, q0, q0);
676 case Kstx: /* ^B: output point */
677 wsetselect(w, w->qh, w->qh);
681 if(w->rawing && (w->q0==w->nr || w->mouseopen)){
685 if(r==Kesc || (w->holding && r==Kdel)){ /* toggle hold */
690 wsetcursor(w, FALSE);
700 case Kdel: /* send interrupt */
705 notefd = emalloc(sizeof(int));
706 *notefd = dup(w->notefd, -1);
707 proccreate(interruptproc, notefd, 4096);
709 case Kack: /* ^F: file name completion */
710 case Kins: /* Insert: file name completion */
713 case Kbs: /* ^H: erase character */
714 case Knack: /* ^U: erase line */
715 case Ketb: /* ^W: erase word */
716 if(w->q0==0 || w->q0==w->qh)
726 wdelete(w, q0, q0+nb);
727 wsetselect(w, q0, q0);
731 /* otherwise ordinary character; just insert */
733 q0 = winsert(w, &r, 1, q0);
738 wsetcols(Window *w, int topped)
742 w->cols[TEXT] = holdcol;
744 w->cols[TEXT] = lightholdcol;
747 w->cols[TEXT] = cols[TEXT];
749 w->cols[TEXT] = paletextcol;
755 wsetcols(w, w == input);
759 wborder(w, Selborder);
761 wborder(w, Unselborder);
765 wbswidth(Window *w, Rune c)
771 /* there is known to be at least one character to erase */
772 if(c == 0x08) /* ^H: erase character */
781 if(r == '\n'){ /* eat at most one more character */
782 if(q == w->q0) /* eat the newline */
788 if(eq && skipping) /* found one; stop skipping */
790 else if(!eq && !skipping)
803 nsnarf = w->q1-w->q0;
804 snarf = runerealloc(snarf, nsnarf);
805 snarfversion++; /* maybe modified by parent */
806 runemove(snarf, w->r+w->q0, nsnarf);
815 wdelete(w, w->q0, w->q1);
816 wsetselect(w, w->q0, w->q0);
828 if(w->rawing && q0==w->nr){
829 waddraw(w, snarf, nsnarf);
830 wsetselect(w, q0, q0);
832 q0 = winsert(w, snarf, nsnarf, w->q0);
833 wsetselect(w, q0, q0+nsnarf);
847 fd = plumbopen("send", OWRITE|OCEXEC);
850 m = emalloc(sizeof(Plumbmsg));
851 m->src = estrdup("rio");
853 m->wdir = estrdup(w->dir);
854 m->type = estrdup("text");
860 while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
862 while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
864 snprint(buf, sizeof(buf), "click=%d", w->q0-p0);
865 m->attr = plumbunpackattr(buf);
867 if(p1-p0 > messagesize-1024){
869 return; /* too large for 9P */
871 m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
872 if(plumbsend(fd, m) < 0){
874 riosetcursor(&query);
895 while(runestrncmp(w->r+w->q0, w->r+i, n) != 0){
902 wsetselect(w, i, i+n);
914 if(w->mc.buttons == 1<<(but-1))
918 incref(w); /* hold up window while we track */
920 if(shiftdown && but > 3)
921 wkeyctl(w, but == 4 ? Kscrolloneup : Kscrollonedown);
922 else if(ptinrect(w->mc.xy, w->scrollr) || (but > 3))
931 wdelete(Window *w, uint q0, uint q1)
938 runemove(w->r+q0, w->r+q1, w->nr-q1);
941 w->q0 -= min(n, w->q0-q0);
943 w->q1 -= min(n, w->q1-q0);
950 else if(q0 < w->org+w->nchars){
965 static Window *clickwin;
966 static uint clickmsec;
967 static Window *selectwin;
971 * called from frame library
974 framescroll(Frame *f, int dl)
976 if(f != &selectwin->Frame)
977 error("frameselect not right frame");
978 wframescroll(selectwin, dl);
982 wframescroll(Window *w, int dl)
991 q0 = wbacknl(w, w->org, -dl);
992 if(selectq > w->org+w->p0)
993 wsetselect(w, w->org+w->p0, selectq);
995 wsetselect(w, selectq, w->org+w->p0);
997 if(w->org+w->nchars == w->nr)
999 q0 = w->org+frcharofpt(w, Pt(w->Frame.r.min.x, w->Frame.r.min.y+dl*w->font->height));
1000 if(selectq >= w->org+w->p1)
1001 wsetselect(w, w->org+w->p1, selectq);
1003 wsetselect(w, selectq, w->org+w->p1);
1005 wsetorigin(w, q0, TRUE);
1017 * Double-click immediately if it might make sense.
1022 selectq = w->org+frcharofpt(w, w->mc.xy);
1023 if(clickwin==w && w->mc.msec-clickmsec<500)
1024 if(q0==q1 && selectq==w->q0){
1025 wdoubleclick(w, &q0, &q1);
1026 wsetselect(w, q0, q1);
1029 /* stay here until something interesting happens */
1032 while(w->mc.buttons==b && abs(w->mc.xy.x-x)<3 && abs(w->mc.xy.y-y)<3);
1033 w->mc.xy.x = x; /* in case we're calling frselect */
1035 q0 = w->q0; /* may have changed */
1039 if(w->mc.buttons == b){
1040 w->scroll = framescroll;
1041 frselect(w, &w->mc);
1042 /* horrible botch: while asleep, may have lost selection altogether */
1044 selectq = w->org + w->p0;
1045 w->Frame.scroll = nil;
1046 if(selectq < w->org)
1049 q0 = w->org + w->p0;
1050 if(selectq > w->org+w->nchars)
1056 if(q0==w->q0 && clickwin==w && w->mc.msec-clickmsec<500){
1057 wdoubleclick(w, &q0, &q1);
1061 clickmsec = w->mc.msec;
1065 wsetselect(w, q0, q1);
1066 while(w->mc.buttons){
1082 while(w->mc.buttons == b)
1089 wsendctlmesg(Window *w, int type, Rectangle r, void *p)
1096 send(w->cctl, &wcm);
1100 wctlmesg(Window *w, int m, Rectangle r, void *p)
1107 error("unknown control message");
1111 sendp((Channel*)p, w);
1118 oldname = estrdup(w->name);
1121 proccreate(deletetimeoutproc, oldname, 4096);
1122 if(Dx(r)<=0){ /* window got hidden, if we had the input, drop it */
1127 /* fall through to get input if needed */
1129 if(w->deleted || w==input)
1139 * have to wait until old input responds before
1140 * changing input to us because the window might
1141 * currently be mouse tracking and it is not
1142 * prepared for getting its input revoked.
1144 c = chancreate(sizeof(void*), 0);
1145 wsendctlmesg(oi, Wakeup, ZR, c);
1150 * if we are still top window and nobody else has taken
1151 * input from original window, take the input.
1153 if(!w->deleted && w->topped==topped && oi==input){
1157 wsendctlmesg(oi, Repaint, ZR, nil);
1162 wsetcursor(w, FALSE);
1165 if(m!=Topped && w==input)
1167 /* fall thrugh for redraw after input change */
1169 if(w->i==nil || Dx(w->screenr)<=0)
1172 flushimage(display, 1);
1175 if(w->i==nil || Dx(w->screenr)<=0 || w->mouseopen)
1178 flushimage(display, 1);
1181 if(w->i==nil || Dx(w->screenr)<=0 || !ptinrect(r.min, w->i->r))
1183 wmovemouse(w, r.min);
1188 wkeyctl(w, w->raw[0]);
1190 runemove(w->raw, w->raw+1, w->nraw);
1198 wsetcursor(w, FALSE);
1200 flushimage(display, 1);
1205 write(w->notefd, "hangup", 6);
1207 proccreate(deletetimeoutproc, estrdup(w->name), 4096);
1219 chanfree(w->conswrite);
1220 chanfree(w->consread);
1221 chanfree(w->mouseread);
1222 chanfree(w->wctlread);
1223 chanfree(w->kbdread);
1224 chanfree(w->complete);
1237 * Convert back to physical coordinates
1240 wmovemouse(Window *w, Point p)
1242 if(w != input || menuing || sweeping)
1244 p.x += w->screenr.min.x-w->i->r.min.x;
1245 p.y += w->screenr.min.y-w->i->r.min.y;
1246 moveto(mousectl, p);
1250 wborder(Window *w, int type)
1257 if(type == Selborder)
1262 if(type == Selborder)
1265 col = lighttitlecol;
1267 border(w->i, w->i->r, Selborder, col, ZP);
1277 for(i=0; i<nwindow; i++){
1279 if(ptinrect(pt, v->screenr))
1280 if(w==nil || v->topped>w->topped)
1289 if(w!=nil && w!=input)
1290 wsendctlmesg(w, Topped, ZR, nil);
1294 wsetcursor(Window *w, int force)
1298 if(menuing || sweeping)
1300 if(w==nil || Dx(w->screenr)<=0 || wpointto(mouse->xy)!=w)
1304 if(p==nil && w->holding)
1307 if(p && force) /* force cursor reload */
1313 riosetcursor(Cursor *p)
1317 setcursor(mousectl, p);
1324 if(w!=nil && w->i!=nil && w->topped!=topped){
1325 w->topped = ++topped;
1327 flushimage(display, 1);
1332 wbottomme(Window *w)
1334 if(w!=nil && w->i!=nil){
1335 w->topped = - ++topped;
1337 flushimage(display, 1);
1361 for(i=0; i<nwindow; i++)
1362 if(window[i]->id == id)
1381 for(i=0; i<nhidden; i++)
1384 memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
1387 for(i=0; i<nwindow; i++)
1390 memmove(window+i, window+i+1, (nwindow-i)*sizeof(window[0]));
1396 wclosewin(Window *w)
1400 assert(w->deleted==TRUE);
1405 /* move it off-screen to hide it, in case client is slow in letting it go */
1406 MOVEIT originwindow(i, i->r.min, view->r.max);
1408 flushimage(display, 1);
1413 wsetpid(Window *w, int pid, int dolabel)
1423 snprint(buf, sizeof(buf), "rc %d", pid);
1425 w->label = estrdup(buf);
1427 snprint(buf, sizeof(buf), "/proc/%d/notepg", pid);
1428 w->notefd = open(buf, OWRITE|OCEXEC);
1435 winshell(void *args)
1449 rfork(RFNAMEG|RFFDG|RFENVG);
1450 if(filsysmount(filsys, w->id) < 0){
1451 fprint(2, "mount failed: %r\n");
1453 threadexits("mount failed");
1456 if(open("/dev/cons", OREAD) < 0){
1457 fprint(2, "can't open /dev/cons: %r\n");
1459 threadexits("/dev/cons");
1462 if(open("/dev/cons", OWRITE) < 0){
1463 fprint(2, "can't open /dev/cons: %r\n");
1465 threadexits("open"); /* BUG? was terminate() */
1467 if(wclose(w) == 0){ /* remove extra ref hanging from creation */
1472 procexec(pidc, cmd, argv);
1473 _exits("exec failed");
1477 static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 };
1478 static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
1479 static Rune left2[] = { L'\n', 0 };
1480 static Rune left3[] = { L'\'', L'"', L'`', 0 };
1496 wdoubleclick(Window *w, uint *q0, uint *q1)
1502 for(i=0; left[i]!=nil; i++){
1506 /* try matching character to left, looking right */
1513 if(wclickmatch(w, c, r[p-l], 1, &q))
1517 /* try matching character to right, looking left */
1524 if(wclickmatch(w, c, l[p-r], -1, &q)){
1525 *q1 = *q0+(*q0<w->nr && c=='\n');
1527 if(c!='\n' || q!=0 || w->r[0]=='\n')
1533 /* try filling out word to right */
1534 while(*q1<w->nr && isalnum(w->r[*q1]))
1536 /* try filling out word to left */
1537 while(*q0>0 && isalnum(w->r[*q0-1]))
1542 wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
1566 return cl=='\n' && nest==1;
1571 wbacknl(Window *w, uint p, uint n)
1575 /* look for start of this line if n==0 */
1576 if(n==0 && p>0 && w->r[p-1]!='\n')
1579 while(i-->0 && p>0){
1580 --p; /* it's at a newline now; back over it */
1583 /* at 128 chars, call it a line anyway */
1584 for(j=128; --j>0 && p>0; p--)
1592 wshow(Window *w, uint q0)
1598 qe = w->org+w->nchars;
1599 if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
1602 nl = 4*w->maxlines/5;
1603 q = wbacknl(w, q0, nl);
1604 /* avoid going backwards if trying to go forwards - long lines! */
1605 if(!(q0>w->org && q<w->org))
1606 wsetorigin(w, q, TRUE);
1607 while(q0 > w->org+w->nchars)
1608 wsetorigin(w, w->org+1, FALSE);
1613 wsetorigin(Window *w, uint org, int exact)
1619 if(org>0 && !exact){
1620 /* org is an estimate of the char posn; find a newline */
1621 /* don't try harder than 256 chars */
1622 for(i=0; i<256 && org<w->nr; i++){
1623 if(w->r[org] == '\n'){
1632 if(a>=0 && a<w->nchars){
1634 fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
1635 }else if(a<0 && -a<w->nchars){
1638 frinsert(w, r, r+n, 0);
1640 frdelete(w, 0, w->nchars);
1644 wsetselect(w, w->q0, w->q1);
1645 if(fixup && w->p1 > w->p0)
1646 frdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1);
1650 wsetselect(Window *w, uint q0, uint q1)
1654 /* w->p0 and w->p1 are always right; w->q0 and w->q1 may be off */
1657 /* compute desired p0,p1 from q0,q1 */
1668 if(p0==w->p0 && p1==w->p1)
1670 /* screen disagrees with desired selection */
1671 if(w->p1<=p0 || p1<=w->p0 || p0==p1 || w->p1==w->p0){
1672 /* no overlap or too easy to bother trying */
1673 frdrawsel(w, frptofchar(w, w->p0), w->p0, w->p1, 0);
1674 frdrawsel(w, frptofchar(w, p0), p0, p1, 1);
1677 /* overlap; avoid unnecessary painting */
1679 /* extend selection backwards */
1680 frdrawsel(w, frptofchar(w, p0), p0, w->p0, 1);
1681 }else if(p0 > w->p0){
1682 /* trim first part of selection */
1683 frdrawsel(w, frptofchar(w, w->p0), w->p0, p0, 0);
1686 /* extend selection forwards */
1687 frdrawsel(w, frptofchar(w, w->p1), w->p1, p1, 1);
1688 }else if(p1 < w->p1){
1689 /* trim last part of selection */
1690 frdrawsel(w, frptofchar(w, p1), p1, w->p1, 0);
1699 winsert(Window *w, Rune *r, int n, uint q0)
1705 if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
1706 m = min(HiWater-LoWater, min(w->org, w->qh));
1718 runemove(w->r, w->r+m, w->nr);
1721 if(w->nr+n > w->maxr){
1723 * Minimize realloc breakage:
1724 * Allocate at least MinWater
1725 * Double allocation size each time
1726 * But don't go much above HiWater
1728 m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
1730 m = max(HiWater+MinWater, w->nr+n);
1732 w->r = runerealloc(w->r, m);
1736 runemove(w->r+q0+n, w->r+q0, w->nr-q0);
1737 runemove(w->r+q0, r, n);
1739 /* if output touches, advance selection, not qh; works best for keyboard and output */
1748 else if(q0 <= w->org+w->nchars)
1749 frinsert(w, r, r+n, q0-w->org);
1759 while(w->lastlinefull == FALSE){
1760 n = w->nr-(w->org+w->nchars);
1763 if(n > 2000) /* educated guess at reasonable amount */
1765 rp = w->r+(w->org+w->nchars);
1768 * it's expensive to frinsert more than we need, so
1771 nl = w->maxlines-w->nlines;
1774 if(rp[i++] == '\n'){
1780 frinsert(w, rp, rp+i, w->nchars);
1785 wcontents(Window *w, int *ip)
1787 return runetobyte(w->r, w->nr, ip);