20 HiWater = 640000, /* max size of history */
21 LoWater = 400000, /* min size of history after max'ed */
22 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 *holdcol;
35 static Image *lightholdcol;
36 static Image *paleholdcol;
39 wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl)
45 /* greys are multiples of 0x11111100+0xFF, 14* being palest */
46 grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
47 darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF);
48 cols[BACK] = display->white;
49 cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
50 cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF);
51 cols[TEXT] = display->black;
52 cols[HTEXT] = display->black;
53 titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen);
54 lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen);
55 holdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue);
56 lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
57 paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
59 w = emalloc(sizeof(Window));
61 r = insetrect(i->r, Selborder+1);
67 w->conswrite = chancreate(sizeof(Conswritemesg), 0);
68 w->consread = chancreate(sizeof(Consreadmesg), 0);
69 w->mouseread = chancreate(sizeof(Mousereadmesg), 0);
70 w->wctlread = chancreate(sizeof(Consreadmesg), 0);
72 w->scrollr.max.x = r.min.x+Scrollwid;
74 r.min.x += Scrollwid+Scrollgap;
75 frinit(&w->f, r, font, i, cols);
76 w->f.maxtab = maxtab*stringwidth(font, "0");
80 w->dir = estrdup(startdir);
81 w->label = estrdup("<unnamed>");
82 r = insetrect(w->i->r, Selborder);
83 draw(w->i, r, cols[BACK], nil, w->f.entire.min);
84 wborder(w, Selborder);
86 incref(&w->ref); /* ref will be removed after mounting; avoids delete before ready to be deleted */
96 n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
97 for(i='A'; i<='Z'; i++){
98 if(nameimage(w->i, w->name, 1) > 0)
100 errstr(err, sizeof err);
101 if(strcmp(err, "image name in use") != 0)
107 fprint(2, "rio: setname failed: %s\n", err);
111 wresize(Window *w, Image *i, int move)
116 if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
117 draw(i, i->r, w->i, nil, w->i->r.min);
119 fprint(2, "res %p %p\n", w->i, i);
124 /*XXX w->mc.image = i; */
125 r = insetrect(i->r, Selborder+1);
127 w->scrollr.max.x = r.min.x+Scrollwid;
129 r.min.x += Scrollwid+Scrollgap;
131 frsetrects(&w->f, r, w->i);
133 frclear(&w->f, FALSE);
134 frinit(&w->f, r, w->f.font, w->i, cols);
136 w->f.maxtab = maxtab*stringwidth(w->f.font, "0");
137 r = insetrect(w->i->r, Selborder);
138 draw(w->i, r, cols[BACK], nil, w->f.entire.min);
140 wsetselect(w, w->q0, w->q1);
143 wborder(w, Selborder);
144 w->topped = ++topped;
150 wrefresh(Window *w, Rectangle r)
154 /* BUG: rectangle is ignored */
156 wborder(w, Selborder);
158 wborder(w, Unselborder);
161 draw(w->i, insetrect(w->i->r, Borderwidth), w->f.cols[BACK], nil, w->i->r.min);
164 frdrawsel(&w->f, frptofchar(&w->f, 0), 0, w->f.p0, 0);
165 if(w->f.p1 < w->f.nchars)
166 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, w->f.nchars, 0);
167 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 1);
181 error("negative ref count");
184 wsendctlmesg(w, Exited, ZR, nil);
192 Rune *rp, *bp, *up, *kbdr;
194 int nr, nb, c, wid, i, npart, initial, lastb, scrolling;
195 char *s, *t, part[UTFmax];
198 enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
209 snprint(buf, sizeof buf, "winctl-id%d", w->id);
212 mrm.cm = chancreate(sizeof(Mouse), 0);
213 cwm.cw = chancreate(sizeof(Stringpair), 0);
214 crm.c1 = chancreate(sizeof(Stringpair), 0);
215 crm.c2 = chancreate(sizeof(Stringpair), 0);
216 cwrm.c1 = chancreate(sizeof(Stringpair), 0);
217 cwrm.c2 = chancreate(sizeof(Stringpair), 0);
220 alts[WKey].c = w->ck;
221 alts[WKey].v = &kbdr;
222 alts[WKey].op = CHANRCV;
223 alts[WMouse].c = w->mc.c;
224 alts[WMouse].v = &w->mc.m;
225 alts[WMouse].op = CHANRCV;
226 alts[WMouseread].c = w->mouseread;
227 alts[WMouseread].v = &mrm;
228 alts[WMouseread].op = CHANSND;
229 alts[WCtl].c = w->cctl;
231 alts[WCtl].op = CHANRCV;
232 alts[WCwrite].c = w->conswrite;
233 alts[WCwrite].v = &cwm;
234 alts[WCwrite].op = CHANSND;
235 alts[WCread].c = w->consread;
236 alts[WCread].v = &crm;
237 alts[WCread].op = CHANSND;
238 alts[WWread].c = w->wctlread;
239 alts[WWread].v = &cwrm;
240 alts[WWread].op = CHANSND;
241 alts[NWALT].op = CHANEND;
246 if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter)
247 alts[WMouseread].op = CHANSND;
249 alts[WMouseread].op = CHANNOP;
250 alts[WCwrite].op = CHANSND;
251 if(w->deleted || !w->wctlready)
252 alts[WWread].op = CHANNOP;
254 alts[WWread].op = CHANSND;
255 /* this code depends on NL and EOT fitting in a single byte */
256 /* kind of expensive for each loop; worth precomputing? */
258 alts[WCread].op = CHANNOP;
259 else if(npart || (w->rawing && w->nraw>0))
260 alts[WCread].op = CHANSND;
262 alts[WCread].op = CHANNOP;
263 for(i=w->qh; i<w->nr; i++){
265 if(c=='\n' || c=='\004'){
266 alts[WCread].op = CHANSND;
273 for(i=0; kbdr[i]!=L'\0'; i++)
276 /* while(nbrecv(w->ck, &r)) */
283 /* queue click events */
284 if(!w->mouse.qfull && lastb != w->mc.m.buttons) { /* add to ring */
285 mp = &w->mouse.queue[w->mouse.wi];
286 if(++w->mouse.wi == nelem(w->mouse.queue))
288 if(w->mouse.wi == w->mouse.ri)
289 w->mouse.qfull = TRUE;
291 mp->counter = w->mouse.counter;
292 lastb = w->mc.m.buttons;
298 /* send a queued event or, if the queue is empty, the current state */
299 /* if the queue has filled, we discard all the events it contained. */
300 /* the intent is to discard frantic clicking by the user during long latencies. */
301 w->mouse.qfull = FALSE;
302 if(w->mouse.wi != w->mouse.ri) {
303 m = w->mouse.queue[w->mouse.ri];
304 if(++w->mouse.ri == nelem(w->mouse.queue))
308 m.counter = w->mouse.counter;
310 w->mouse.lastcounter = m.counter;
314 if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
342 while(i<nr-1 && *(bp+1) == '\r'){
346 if(i<nr-1 && *(bp+1) != '\n'){
347 while(up > rp && *(up-1) != '\n')
350 initial = wbswidth(w, '\r');
363 qh = w->qh - initial;
364 wdelete(w, qh, qh+initial);
368 scrolling = w->org <= w->qh && w->qh <= w->org + w->f.nchars;
369 w->qh = winsert(w, rp, nr, w->qh)+nr;
372 wsetselect(w, w->q0, w->q1);
384 while(i<nb && (w->qh<w->nr || w->nraw>0)){
386 wid = runetochar(t+i, &w->raw[0]);
388 runemove(w->raw, w->raw+1, w->nraw);
390 wid = runetochar(t+i, &w->r[w->qh++]);
391 c = t[i]; /* knows break characters fit in a byte */
393 if(!w->rawing && (c == '\n' || c=='\004')){
394 /* if(c == '\004') */
399 /* if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004') */
403 memmove(part, t+nb, npart);
412 recv(cwrm.c1, &pair);
413 if(w->deleted || w->i==nil)
414 pair.ns = sprint(pair.s, "");
417 for(i=0; i<nhidden; i++)
425 pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
426 w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
428 send(cwrm.c2, &pair);
432 flushimage(display, 1);
437 waddraw(Window *w, Rune *r, int nr)
439 w->raw = runerealloc(w->raw, w->nraw+nr);
440 runemove(w->raw+w->nraw, r, nr);
445 * Need to do this in a separate proc because if process we're interrupting
446 * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
449 interruptproc(void *v)
454 write(*notefd, "interrupt", 9);
459 windfilewidth(Window *w, uint q0, int oneelement)
469 if(oneelement && r=='/')
477 showcandidates(Window *w, Completion *c)
487 s = "[no matches in ";
491 fmtprint(&f, "%s%d files]\n", s, c->nfile);
493 fmtprint(&f, "%s", s);
494 for(i=0; i<c->nfile; i++){
497 fmtprint(&f, "%s", c->filename[i]);
501 /* place text at beginning of line before host point */
503 while(qline>0 && w->r[qline-1] != '\n')
506 rp = runefmtstrflush(&f);
510 q0 += winsert(w, rp, runestrlen(rp), qline) - qline;
512 wsetselect(w, q0+nr, q0+nr);
516 namecomplete(Window *w)
519 Rune *rp, *path, *str;
521 char *s, *dir, *root;
523 /* control-f: filename completion; works back to white space or / */
524 if(w->q0<w->nr && w->r[w->q0]>' ') /* must be at end of word */
526 nstr = windfilewidth(w, w->q0, TRUE);
527 str = runemalloc(nstr);
528 runemove(str, w->r+(w->q0-nstr), nstr);
529 npath = windfilewidth(w, w->q0-nstr, FALSE);
530 path = runemalloc(npath);
531 runemove(path, w->r+(w->q0-nstr-npath), npath);
534 /* is path rooted? if not, we need to make it relative to window path */
535 if(npath>0 && path[0]=='/'){
536 dir = malloc(UTFmax*npath+1);
537 sprint(dir, "%.*S", npath, path);
539 if(strcmp(w->dir, "") == 0)
543 dir = malloc(strlen(root)+1+UTFmax*npath+1);
544 sprint(dir, "%s/%.*S", root, npath, path);
546 dir = cleanname(dir);
548 s = smprint("%.*S", nstr, str);
549 c = complete(dir, s);
555 showcandidates(w, c);
558 rp = runesmprint("%s", c->string);
569 wkeyctl(Window *w, Rune r)
580 /* navigation keys work only when mouse is not open */
587 n = mousescrollsize(w->f.maxlines);
592 n = 2*w->f.maxlines/3;
594 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+n*w->f.font->height));
595 wsetorigin(w, q0, TRUE);
601 n = mousescrollsize(w->f.maxlines);
606 n = 2*w->f.maxlines/3;
608 q0 = wbacknl(w, w->org, n);
609 wsetorigin(w, q0, TRUE);
614 wsetselect(w, q0, q0);
621 wsetselect(w, q1, q1);
631 case 0x01: /* ^A: beginning of line */
632 if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
634 nb = wbswidth(w, 0x15 /* ^U */);
635 wsetselect(w, w->q0-nb, w->q0-nb);
638 case 0x05: /* ^E: end of line */
640 while(q0 < w->nr && w->r[q0]!='\n')
642 wsetselect(w, q0, q0);
647 * This if used to be below the if(w->rawing ...),
648 * but let's try putting it here. This will allow ESC-processing
649 * to toggle hold mode even in remote SSH connections.
650 * The drawback is that vi-style processing gets harder.
651 * If you find yourself in some weird readline mode, good
652 * luck getting out without ESC. Let's see who complains.
654 if(r==0x1B || (w->holding && r==0x7F)){ /* toggle hold */
663 if(!w->holding && w->rawing && (w->q0==w->nr || w->mouseopen)){
688 case 0x7F: /* send interrupt */
693 case 0x06: /* ^F: file name completion */
694 case Kins: /* Insert: file name completion */
695 rp = namecomplete(w);
700 q0 = winsert(w, rp, nr, q0);
704 case 0x08: /* ^H: erase character */
705 case 0x15: /* ^U: erase line */
706 case 0x17: /* ^W: erase word */
707 if(w->q0==0 || w->q0==w->qh)
717 wdelete(w, q0, q0+nb);
718 wsetselect(w, q0, q0);
722 /* otherwise ordinary character; just insert */
724 q0 = winsert(w, &r, 1, q0);
733 w->f.cols[TEXT] = w->f.cols[HTEXT] = holdcol;
735 w->f.cols[TEXT] = w->f.cols[HTEXT] = lightholdcol;
738 w->f.cols[TEXT] = w->f.cols[HTEXT] = display->black;
740 w->f.cols[TEXT] = w->f.cols[HTEXT] = darkgrey;
751 wborder(w, Selborder);
754 wborder(w, Unselborder);
758 wbswidth(Window *w, Rune c)
764 /* there is known to be at least one character to erase */
765 if(c == 0x08) /* ^H: erase character */
774 if(r == '\n'){ /* eat at most one more character */
775 if(q == w->q0 && c != '\r') /* eat the newline */
781 if(eq && skipping) /* found one; stop skipping */
783 else if(!eq && !skipping)
796 nsnarf = w->q1-w->q0;
797 snarf = runerealloc(snarf, nsnarf);
798 snarfversion++; /* maybe modified by parent */
799 runemove(snarf, w->r+w->q0, nsnarf);
808 wdelete(w, w->q0, w->q1);
809 wsetselect(w, w->q0, w->q0);
821 if(w->rawing && !w->holding && q0==w->nr){
822 waddraw(w, snarf, nsnarf);
823 wsetselect(w, q0, q0);
825 q0 = winsert(w, snarf, nsnarf, w->q0);
826 wsetselect(w, q0, q0+nsnarf);
840 fd = plumbopenfid("send", OWRITE);
843 m = emalloc(sizeof(Plumbmsg));
844 m->src = estrdup("rio");
846 m->wdir = estrdup(w->dir);
847 m->type = estrdup("text");
853 while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
855 while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
857 sprint(buf, "click=%d", w->q0-p0);
858 m->attr = plumbunpackattr(buf);
860 if(p1-p0 > messagesize-1024){
862 return; /* too large for 9P */
864 m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
865 if(plumbsendtofid(fd, m) < 0){
867 riosetcursor(&query, 1);
875 winborder(Window *w, Point xy)
877 return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder));
885 if(w->mc.m.buttons == 1)
887 else if(w->mc.m.buttons == 2)
889 else if(w->mc.m.buttons == 4)
892 if(w->mc.m.buttons == 8)
893 wkeyctl(w, Kscrolloneup);
894 if(w->mc.m.buttons == 16)
895 wkeyctl(w, Kscrollonedown);
899 incref(&w->ref); /* hold up window while we track */
902 if(ptinrect(w->mc.m.xy, w->scrollr)){
909 /* else all is handled by main process */
915 wdelete(Window *w, uint q0, uint q1)
922 runemove(w->r+q0, w->r+q1, w->nr-q1);
925 w->q0 -= min(n, w->q0-q0);
927 w->q1 -= min(n, w->q1-q0);
934 else if(q0 < w->org+w->f.nchars){
943 frdelete(&w->f, p0, p1);
949 static Window *clickwin;
950 static uint clickmsec;
951 static Window *selectwin;
955 * called from frame library
958 framescroll(Frame *f, int dl)
960 if(f != &selectwin->f)
961 error("frameselect not right frame");
962 wframescroll(selectwin, dl);
966 wframescroll(Window *w, int dl)
975 q0 = wbacknl(w, w->org, -dl);
976 if(selectq > w->org+w->f.p0)
977 wsetselect(w, w->org+w->f.p0, selectq);
979 wsetselect(w, selectq, w->org+w->f.p0);
981 if(w->org+w->f.nchars == w->nr)
983 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+dl*w->f.font->height));
984 if(selectq >= w->org+w->f.p1)
985 wsetselect(w, w->org+w->f.p1, selectq);
987 wsetselect(w, selectq, w->org+w->f.p1);
989 wsetorigin(w, q0, TRUE);
1001 * Double-click immediately if it might make sense.
1003 b = w->mc.m.buttons;
1006 selectq = w->org+frcharofpt(&w->f, w->mc.m.xy);
1007 if(clickwin==w && w->mc.m.msec-clickmsec<500)
1008 if(q0==q1 && selectq==w->q0){
1009 wdoubleclick(w, &q0, &q1);
1010 wsetselect(w, q0, q1);
1011 flushimage(display, 1);
1014 /* stay here until something interesting happens */
1017 while(w->mc.m.buttons==b && abs(w->mc.m.xy.x-x)<3 && abs(w->mc.m.xy.y-y)<3);
1018 w->mc.m.xy.x = x; /* in case we're calling frselect */
1020 q0 = w->q0; /* may have changed */
1024 if(w->mc.m.buttons == b){
1025 w->f.scroll = framescroll;
1026 frselect(&w->f, &w->mc);
1027 /* horrible botch: while asleep, may have lost selection altogether */
1029 selectq = w->org + w->f.p0;
1031 if(selectq < w->org)
1034 q0 = w->org + w->f.p0;
1035 if(selectq > w->org+w->f.nchars)
1038 q1 = w->org+w->f.p1;
1041 if(q0==w->q0 && clickwin==w && w->mc.m.msec-clickmsec<500){
1042 wdoubleclick(w, &q0, &q1);
1046 clickmsec = w->mc.m.msec;
1050 wsetselect(w, q0, q1);
1051 flushimage(display, 1);
1052 while(w->mc.m.buttons){
1054 b = w->mc.m.buttons;
1068 flushimage(display, 1);
1069 while(w->mc.m.buttons == b)
1076 wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
1083 send(w->cctl, &wcm);
1087 wctlmesg(Window *w, int m, Rectangle r, Image *i)
1093 error("unknown control message");
1104 strcpy(buf, w->name);
1105 wresize(w, i, m==Moved);
1110 }else if(w == input)
1112 flushimage(display, 1);
1115 if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
1119 flushimage(display, 1);
1122 if(sweeping || !ptinrect(r.min, w->i->r))
1124 wmovemouse(w, r.min);
1131 wkeyctl(w, w->raw[0]);
1133 runemove(w->raw, w->raw+1, w->nraw);
1141 flushimage(display, 1);
1146 write(w->notefd, "hangup", 6);
1150 frclear(&w->f, TRUE);
1155 chanfree(w->conswrite);
1156 chanfree(w->consread);
1157 chanfree(w->mouseread);
1158 chanfree(w->wctlread);
1170 * Convert back to physical coordinates
1173 wmovemouse(Window *w, Point p)
1175 p.x += w->screenr.min.x-w->i->r.min.x;
1176 p.y += w->screenr.min.y-w->i->r.min.y;
1177 moveto(mousectl, p);
1185 if(wkeyboard!=nil && w==wkeyboard)
1189 if(oi!=w && oi!=nil)
1198 wsendctlmesg(oi, Wakeup, ZR, nil);
1202 wsendctlmesg(w, Wakeup, ZR, nil);
1208 wsetcursor(Window *w, int force)
1212 if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
1214 else if(wpointto(mouse->xy) == w){
1216 if(p==nil && w->holding)
1221 riosetcursor(p, force && !menuing);
1225 riosetcursor(Cursor *p, int force)
1227 if(!force && p==lastcursor)
1229 setcursor(mousectl, p);
1240 if(w->topped == topped)
1244 flushimage(display, 1);
1245 w->topped = ++topped;
1253 if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){
1255 flushimage(display, 1);
1256 w->topped = ++ topped;
1261 wbottomme(Window *w)
1263 if(w!=nil && w->i!=nil && !w->deleted){
1265 flushimage(display, 1);
1275 for(i=0; i<nwindow; i++)
1276 if(window[i]->id == id)
1282 wclosewin(Window *w)
1294 for(i=0; i<nhidden; i++)
1297 memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
1300 for(i=0; i<nwindow; i++)
1303 memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*));
1306 /* move it off-screen to hide it, in case client is slow in letting it go */
1307 MOVEIT originwindow(w->i, r.min, view->r.max);
1312 error("unknown window in closewin");
1316 wsetpid(Window *w, int pid, int dolabel)
1322 sprint(buf, "rc %d", pid);
1324 w->label = estrdup(buf);
1325 drawsetlabel(w->label);
1329 static Rune left1[] = {
1330 '{', '[', '(', '<', 0xAB,
1331 0x207d, 0x2329, 0x27e6, 0x27e8, 0x27ea,
1332 0xfe59, 0xfe5b, 0xfe5d, 0xff08, 0xff3b, 0xff5b,
1335 static Rune right1[] = {
1336 '}', ']', ')', '>', 0xBB,
1337 0x207e, 0x232a, 0x27e7, 0x27e9, 0x27eb,
1338 0xfe5a, 0xfe5c, 0xfe5e, 0xff09, 0xff3d, 0xff5d,
1341 static Rune left2[] = { '\n', 0 };
1342 static Rune left3[] = { '\'', '"', '`', 0 };
1358 wdoubleclick(Window *w, uint *q0, uint *q1)
1364 for(i=0; left[i]!=nil; i++){
1368 /* try matching character to left, looking right */
1375 if(wclickmatch(w, c, r[p-l], 1, &q))
1379 /* try matching character to right, looking left */
1386 if(wclickmatch(w, c, l[p-r], -1, &q)){
1387 *q1 = *q0+(*q0<w->nr && c=='\n');
1389 if(c!='\n' || q!=0 || w->r[0]=='\n')
1395 /* try filling out word to right */
1396 while(*q1<w->nr && isalnum(w->r[*q1]))
1398 /* try filling out word to left */
1399 while(*q0>0 && isalnum(w->r[*q0-1]))
1404 wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
1428 return cl=='\n' && nest==1;
1433 wbacknl(Window *w, uint p, uint n)
1437 /* look for start of this line if n==0 */
1438 if(n==0 && p>0 && w->r[p-1]!='\n')
1441 while(i-->0 && p>0){
1442 --p; /* it's at a newline now; back over it */
1445 /* at 128 chars, call it a line anyway */
1446 for(j=128; --j>0 && p>0; p--)
1454 wshow(Window *w, uint q0)
1460 qe = w->org+w->f.nchars;
1461 if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
1464 nl = 4*w->f.maxlines/5;
1465 q = wbacknl(w, q0, nl);
1466 /* avoid going backwards if trying to go forwards - long lines! */
1467 if(!(q0>w->org && q<w->org))
1468 wsetorigin(w, q, TRUE);
1469 while(q0 > w->org+w->f.nchars)
1470 wsetorigin(w, w->org+1, FALSE);
1475 wsetorigin(Window *w, uint org, int exact)
1481 if(org>0 && !exact){
1482 /* org is an estimate of the char posn; find a newline */
1483 /* don't try harder than 256 chars */
1484 for(i=0; i<256 && org<w->nr; i++){
1485 if(w->r[org] == '\n'){
1494 if(a>=0 && a<w->f.nchars){
1495 frdelete(&w->f, 0, a);
1496 fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
1497 }else if(a<0 && -a<w->f.nchars){
1500 runemove(r, w->r+org, n);
1501 frinsert(&w->f, r, r+n, 0);
1504 frdelete(&w->f, 0, w->f.nchars);
1508 wsetselect(w, w->q0, w->q1);
1509 if(fixup && w->f.p1 > w->f.p0)
1510 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1-1), w->f.p1-1, w->f.p1, 1);
1514 wsetselect(Window *w, uint q0, uint q1)
1518 /* w->f.p0 and w->f.p1 are always right; w->q0 and w->q1 may be off */
1521 /* compute desired p0,p1 from q0,q1 */
1528 if(p0 > w->f.nchars)
1530 if(p1 > w->f.nchars)
1532 if(p0==w->f.p0 && p1==w->f.p1)
1534 /* screen disagrees with desired selection */
1535 if(w->f.p1<=p0 || p1<=w->f.p0 || p0==p1 || w->f.p1==w->f.p0){
1536 /* no overlap or too easy to bother trying */
1537 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 0);
1538 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, p1, 1);
1541 /* overlap; avoid unnecessary painting */
1543 /* extend selection backwards */
1544 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, w->f.p0, 1);
1545 }else if(p0 > w->f.p0){
1546 /* trim first part of selection */
1547 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, p0, 0);
1550 /* extend selection forwards */
1551 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, p1, 1);
1552 }else if(p1 < w->f.p1){
1553 /* trim last part of selection */
1554 frdrawsel(&w->f, frptofchar(&w->f, p1), p1, w->f.p1, 0);
1563 winsert(Window *w, Rune *r, int n, uint q0)
1569 if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
1570 m = min(HiWater-LoWater, min(w->org, w->qh));
1582 runemove(w->r, w->r+m, w->nr);
1585 if(w->nr+n > w->maxr){
1587 * Minimize realloc breakage:
1588 * Allocate at least MinWater
1589 * Double allocation size each time
1590 * But don't go much above HiWater
1592 m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
1594 m = max(HiWater+MinWater, w->nr+n);
1596 w->r = runerealloc(w->r, m);
1600 runemove(w->r+q0+n, w->r+q0, w->nr-q0);
1601 runemove(w->r+q0, r, n);
1603 /* if output touches, advance selection, not qh; works best for keyboard and output */
1612 else if(q0 <= w->org+w->f.nchars)
1613 frinsert(&w->f, r, r+n, q0-w->org);
1623 if(w->f.lastlinefull)
1625 rp = malloc(messagesize);
1627 n = w->nr-(w->org+w->f.nchars);
1630 if(n > 2000) /* educated guess at reasonable amount */
1632 runemove(rp, w->r+(w->org+w->f.nchars), n);
1634 * it's expensive to frinsert more than we need, so
1637 nl = w->f.maxlines-w->f.nlines;
1640 if(rp[i++] == '\n'){
1646 frinsert(&w->f, rp, rp+i, w->f.nchars);
1647 }while(w->f.lastlinefull == FALSE);
1652 wcontents(Window *w, int *ip)
1654 return runetobyte(w->r, w->nr, ip);