20 HiWater = 64000000, /* 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, int scrolling)
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->scrolling = scrolling;
81 w->dir = estrdup(startdir);
82 w->label = estrdup("<unnamed>");
83 r = insetrect(w->i->r, Selborder);
84 draw(w->i, r, cols[BACK], nil, w->f.entire.min);
85 wborder(w, Selborder);
87 incref(&w->ref); /* ref will be removed after mounting; avoids delete before ready to be deleted */
97 n = sprint(w->name, "window.%d.%d", w->id, w->namecount++);
98 for(i='A'; i<='Z'; i++){
99 if(nameimage(w->i, w->name, 1) > 0)
101 errstr(err, sizeof err);
102 if(strcmp(err, "image name in use") != 0)
108 fprint(2, "rio: setname failed: %s\n", err);
112 wresize(Window *w, Image *i, int move)
117 if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r)))
118 draw(i, i->r, w->i, nil, w->i->r.min);
120 fprint(2, "res %p %p\n", w->i, i);
125 /*XXX w->mc.image = i; */
126 r = insetrect(i->r, Selborder+1);
128 w->scrollr.max.x = r.min.x+Scrollwid;
130 r.min.x += Scrollwid+Scrollgap;
132 frsetrects(&w->f, r, w->i);
134 frclear(&w->f, FALSE);
135 frinit(&w->f, r, w->f.font, w->i, cols);
137 w->f.maxtab = maxtab*stringwidth(w->f.font, "0");
138 r = insetrect(w->i->r, Selborder);
139 draw(w->i, r, cols[BACK], nil, w->f.entire.min);
141 wsetselect(w, w->q0, w->q1);
144 wborder(w, Selborder);
145 w->topped = ++topped;
151 wrefresh(Window *w, Rectangle r)
155 /* BUG: rectangle is ignored */
157 wborder(w, Selborder);
159 wborder(w, Unselborder);
162 draw(w->i, insetrect(w->i->r, Borderwidth), w->f.cols[BACK], nil, w->i->r.min);
165 frdrawsel(&w->f, frptofchar(&w->f, 0), 0, w->f.p0, 0);
166 if(w->f.p1 < w->f.nchars)
167 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, w->f.nchars, 0);
168 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 1);
182 error("negative ref count");
185 wsendctlmesg(w, Exited, ZR, nil);
193 Rune *rp, *bp, *up, *kbdr;
195 int nr, nb, c, wid, i, npart, initial, lastb, scrolling;
196 char *s, *t, part[UTFmax];
199 enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, NWALT };
210 snprint(buf, sizeof buf, "winctl-id%d", w->id);
213 mrm.cm = chancreate(sizeof(Mouse), 0);
214 cwm.cw = chancreate(sizeof(Stringpair), 0);
215 crm.c1 = chancreate(sizeof(Stringpair), 0);
216 crm.c2 = chancreate(sizeof(Stringpair), 0);
217 cwrm.c1 = chancreate(sizeof(Stringpair), 0);
218 cwrm.c2 = chancreate(sizeof(Stringpair), 0);
221 alts[WKey].c = w->ck;
222 alts[WKey].v = &kbdr;
223 alts[WKey].op = CHANRCV;
224 alts[WMouse].c = w->mc.c;
225 alts[WMouse].v = &w->mc.m;
226 alts[WMouse].op = CHANRCV;
227 alts[WMouseread].c = w->mouseread;
228 alts[WMouseread].v = &mrm;
229 alts[WMouseread].op = CHANSND;
230 alts[WCtl].c = w->cctl;
232 alts[WCtl].op = CHANRCV;
233 alts[WCwrite].c = w->conswrite;
234 alts[WCwrite].v = &cwm;
235 alts[WCwrite].op = CHANSND;
236 alts[WCread].c = w->consread;
237 alts[WCread].v = &crm;
238 alts[WCread].op = CHANSND;
239 alts[WWread].c = w->wctlread;
240 alts[WWread].v = &cwrm;
241 alts[WWread].op = CHANSND;
242 alts[NWALT].op = CHANEND;
247 if(w->mouseopen && w->mouse.counter != w->mouse.lastcounter)
248 alts[WMouseread].op = CHANSND;
250 alts[WMouseread].op = CHANNOP;
251 // if(!w->scrolling && !w->mouseopen && w->qh>w->org+w->f.nchars)
252 // alts[WCwrite].op = CHANNOP;
254 alts[WCwrite].op = CHANSND;
255 if(w->deleted || !w->wctlready)
256 alts[WWread].op = CHANNOP;
258 alts[WWread].op = CHANSND;
259 /* this code depends on NL and EOT fitting in a single byte */
260 /* kind of expensive for each loop; worth precomputing? */
262 alts[WCread].op = CHANNOP;
263 else if(npart || (w->rawing && w->nraw>0))
264 alts[WCread].op = CHANSND;
266 alts[WCread].op = CHANNOP;
267 for(i=w->qh; i<w->nr; i++){
269 if(c=='\n' || c=='\004'){
270 alts[WCread].op = CHANSND;
277 for(i=0; kbdr[i]!=L'\0'; i++)
280 /* while(nbrecv(w->ck, &r)) */
287 /* queue click events */
288 if(!w->mouse.qfull && lastb != w->mc.m.buttons) { /* add to ring */
289 mp = &w->mouse.queue[w->mouse.wi];
290 if(++w->mouse.wi == nelem(w->mouse.queue))
292 if(w->mouse.wi == w->mouse.ri)
293 w->mouse.qfull = TRUE;
295 mp->counter = w->mouse.counter;
296 lastb = w->mc.m.buttons;
302 /* send a queued event or, if the queue is empty, the current state */
303 /* if the queue has filled, we discard all the events it contained. */
304 /* the intent is to discard frantic clicking by the user during long latencies. */
305 w->mouse.qfull = FALSE;
306 if(w->mouse.wi != w->mouse.ri) {
307 m = w->mouse.queue[w->mouse.ri];
308 if(++w->mouse.ri == nelem(w->mouse.queue))
312 m.counter = w->mouse.counter;
314 w->mouse.lastcounter = m.counter;
318 if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
346 while(i<nr-1 && *(bp+1) == '\r'){
350 if(i<nr-1 && *(bp+1) != '\n'){
351 while(up > rp && *(up-1) != '\n')
354 initial = wbswidth(w, '\r');
367 qh = w->qh - initial;
368 wdelete(w, qh, qh+initial);
372 scrolling = w->scrolling && w->org <= w->qh && w->qh <= w->org + w->f.nchars;
373 w->qh = winsert(w, rp, nr, w->qh)+nr;
376 wsetselect(w, w->q0, w->q1);
388 while(i<nb && (w->qh<w->nr || w->nraw>0)){
390 wid = runetochar(t+i, &w->raw[0]);
392 runemove(w->raw, w->raw+1, w->nraw);
394 wid = runetochar(t+i, &w->r[w->qh++]);
395 c = t[i]; /* knows break characters fit in a byte */
397 if(!w->rawing && (c == '\n' || c=='\004')){
398 /* if(c == '\004') */
403 /* if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004') */
407 memmove(part, t+nb, npart);
416 recv(cwrm.c1, &pair);
417 if(w->deleted || w->i==nil)
418 pair.ns = sprint(pair.s, "");
421 for(i=0; i<nhidden; i++)
429 pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %s %s ",
430 w->i->r.min.x, w->i->r.min.y, w->i->r.max.x, w->i->r.max.y, t, s);
432 send(cwrm.c2, &pair);
436 flushimage(display, 1);
441 waddraw(Window *w, Rune *r, int nr)
443 w->raw = runerealloc(w->raw, w->nraw+nr);
444 runemove(w->raw+w->nraw, r, nr);
449 * Need to do this in a separate proc because if process we're interrupting
450 * is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
453 interruptproc(void *v)
458 write(*notefd, "interrupt", 9);
463 windfilewidth(Window *w, uint q0, int oneelement)
473 if(oneelement && r=='/')
481 showcandidates(Window *w, Completion *c)
491 s = "[no matches in ";
495 fmtprint(&f, "%s%d files]\n", s, c->nfile);
497 fmtprint(&f, "%s", s);
498 for(i=0; i<c->nfile; i++){
501 fmtprint(&f, "%s", c->filename[i]);
505 /* place text at beginning of line before host point */
507 while(qline>0 && w->r[qline-1] != '\n')
510 rp = runefmtstrflush(&f);
514 q0 += winsert(w, rp, runestrlen(rp), qline) - qline;
516 wsetselect(w, q0+nr, q0+nr);
520 namecomplete(Window *w)
523 Rune *rp, *path, *str;
525 char *s, *dir, *root;
527 /* control-f: filename completion; works back to white space or / */
528 if(w->q0<w->nr && w->r[w->q0]>' ') /* must be at end of word */
530 nstr = windfilewidth(w, w->q0, TRUE);
531 str = runemalloc(nstr);
532 runemove(str, w->r+(w->q0-nstr), nstr);
533 npath = windfilewidth(w, w->q0-nstr, FALSE);
534 path = runemalloc(npath);
535 runemove(path, w->r+(w->q0-nstr-npath), npath);
538 /* is path rooted? if not, we need to make it relative to window path */
539 if(npath>0 && path[0]=='/'){
540 dir = malloc(UTFmax*npath+1);
541 sprint(dir, "%.*S", npath, path);
543 if(strcmp(w->dir, "") == 0)
547 dir = malloc(strlen(root)+1+UTFmax*npath+1);
548 sprint(dir, "%s/%.*S", root, npath, path);
550 dir = cleanname(dir);
552 s = smprint("%.*S", nstr, str);
553 c = complete(dir, s);
559 showcandidates(w, c);
562 rp = runesmprint("%s", c->string);
573 wkeyctl(Window *w, Rune r)
584 /* navigation keys work only when mouse is not open */
591 n = mousescrollsize(w->f.maxlines);
596 n = 2*w->f.maxlines/3;
598 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+n*w->f.font->height));
599 wsetorigin(w, q0, TRUE);
605 n = mousescrollsize(w->f.maxlines);
610 n = 2*w->f.maxlines/3;
612 q0 = wbacknl(w, w->org, n);
613 wsetorigin(w, q0, TRUE);
618 wsetselect(w, q0, q0);
625 wsetselect(w, q1, q1);
630 if(w->org > w->iq1) {
631 q0 = wbacknl(w, w->iq1, 1);
632 wsetorigin(w, q0, TRUE);
637 if(w->iq1 > w->org+w->f.nchars) {
638 q0 = wbacknl(w, w->iq1, 1);
639 wsetorigin(w, q0, TRUE);
643 case 0x01: /* ^A: beginning of line */
644 if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\n')
646 nb = wbswidth(w, 0x15 /* ^U */);
647 wsetselect(w, w->q0-nb, w->q0-nb);
650 case 0x05: /* ^E: end of line */
652 while(q0 < w->nr && w->r[q0]!='\n')
654 wsetselect(w, q0, q0);
659 * This if used to be below the if(w->rawing ...),
660 * but let's try putting it here. This will allow ESC-processing
661 * to toggle hold mode even in remote SSH connections.
662 * The drawback is that vi-style processing gets harder.
663 * If you find yourself in some weird readline mode, good
664 * luck getting out without ESC. Let's see who complains.
666 if(r==0x1B || (w->holding && r==0x7F)){ /* toggle hold */
675 if(!w->holding && w->rawing && (w->q0==w->nr || w->mouseopen)){
700 case 0x7F: /* send interrupt */
706 case 0x06: /* ^F: file name completion */
707 case Kins: /* Insert: file name completion */
708 rp = namecomplete(w);
713 q0 = winsert(w, rp, nr, q0);
718 case 0x08: /* ^H: erase character */
719 case 0x15: /* ^U: erase line */
720 case 0x17: /* ^W: erase word */
721 if(w->q0==0 || w->q0==w->qh)
731 wdelete(w, q0, q0+nb);
732 wsetselect(w, q0, q0);
737 /* otherwise ordinary character; just insert */
739 q0 = winsert(w, &r, 1, q0);
749 w->f.cols[TEXT] = w->f.cols[HTEXT] = holdcol;
751 w->f.cols[TEXT] = w->f.cols[HTEXT] = lightholdcol;
754 w->f.cols[TEXT] = w->f.cols[HTEXT] = display->black;
756 w->f.cols[TEXT] = w->f.cols[HTEXT] = darkgrey;
767 wborder(w, Selborder);
770 wborder(w, Unselborder);
774 wbswidth(Window *w, Rune c)
780 /* there is known to be at least one character to erase */
781 if(c == 0x08) /* ^H: erase character */
790 if(r == '\n'){ /* eat at most one more character */
791 if(q == w->q0 && c != '\r') /* eat the newline */
797 if(eq && skipping) /* found one; stop skipping */
799 else if(!eq && !skipping)
812 nsnarf = w->q1-w->q0;
813 snarf = runerealloc(snarf, nsnarf);
814 snarfversion++; /* maybe modified by parent */
815 runemove(snarf, w->r+w->q0, nsnarf);
824 wdelete(w, w->q0, w->q1);
825 wsetselect(w, w->q0, w->q0);
837 if(w->rawing && !w->holding && q0==w->nr){
838 waddraw(w, snarf, nsnarf);
839 wsetselect(w, q0, q0);
841 q0 = winsert(w, snarf, nsnarf, w->q0);
842 wsetselect(w, q0, q0+nsnarf);
856 fd = plumbopenfid("send", OWRITE);
859 m = emalloc(sizeof(Plumbmsg));
860 m->src = estrdup("rio");
862 m->wdir = estrdup(w->dir);
863 m->type = estrdup("text");
869 while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->r[p0-1]!='\n')
871 while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->r[p1]!='\n')
873 sprint(buf, "click=%d", w->q0-p0);
874 m->attr = plumbunpackattr(buf);
876 if(p1-p0 > messagesize-1024){
878 return; /* too large for 9P */
880 m->data = runetobyte(w->r+p0, p1-p0, &m->ndata);
881 if(plumbsendtofid(fd, m) < 0){
883 riosetcursor(&query, 1);
891 winborder(Window *w, Point xy)
893 return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->screenr, Selborder));
901 if(w->mc.m.buttons == 1)
903 else if(w->mc.m.buttons == 2)
905 else if(w->mc.m.buttons == 4)
908 if(w->mc.m.buttons == 8)
909 wkeyctl(w, Kscrolloneup);
910 if(w->mc.m.buttons == 16)
911 wkeyctl(w, Kscrollonedown);
915 incref(&w->ref); /* hold up window while we track */
918 if(ptinrect(w->mc.m.xy, w->scrollr)){
925 /* else all is handled by main process */
931 wdelete(Window *w, uint q0, uint q1)
938 runemove(w->r+q0, w->r+q1, w->nr-q1);
941 w->iq1 -= min(n, w->iq1-q0);
943 w->q0 -= min(n, w->q0-q0);
945 w->q1 -= min(n, w->q1-q0);
952 else if(q0 < w->org+w->f.nchars){
961 frdelete(&w->f, p0, p1);
967 static Window *clickwin;
968 static uint clickmsec;
969 static Window *selectwin;
973 * called from frame library
976 framescroll(Frame *f, int dl)
978 if(f != &selectwin->f)
979 error("frameselect not right frame");
980 wframescroll(selectwin, dl);
984 wframescroll(Window *w, int dl)
993 q0 = wbacknl(w, w->org, -dl);
994 if(selectq > w->org+w->f.p0)
995 wsetselect(w, w->org+w->f.p0, selectq);
997 wsetselect(w, selectq, w->org+w->f.p0);
999 if(w->org+w->f.nchars == w->nr)
1001 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.min.y+dl*w->f.font->height));
1002 if(selectq >= w->org+w->f.p1)
1003 wsetselect(w, w->org+w->f.p1, selectq);
1005 wsetselect(w, selectq, w->org+w->f.p1);
1007 wsetorigin(w, q0, TRUE);
1019 * Double-click immediately if it might make sense.
1021 b = w->mc.m.buttons;
1024 selectq = w->org+frcharofpt(&w->f, w->mc.m.xy);
1025 if(clickwin==w && w->mc.m.msec-clickmsec<500)
1026 if(q0==q1 && selectq==w->q0){
1027 wdoubleclick(w, &q0, &q1);
1028 wsetselect(w, q0, q1);
1029 flushimage(display, 1);
1032 /* stay here until something interesting happens */
1035 while(w->mc.m.buttons==b && abs(w->mc.m.xy.x-x)<3 && abs(w->mc.m.xy.y-y)<3);
1036 w->mc.m.xy.x = x; /* in case we're calling frselect */
1038 q0 = w->q0; /* may have changed */
1042 if(w->mc.m.buttons == b){
1043 w->f.scroll = framescroll;
1044 frselect(&w->f, &w->mc);
1045 /* horrible botch: while asleep, may have lost selection altogether */
1047 selectq = w->org + w->f.p0;
1049 if(selectq < w->org)
1052 q0 = w->org + w->f.p0;
1053 if(selectq > w->org+w->f.nchars)
1056 q1 = w->org+w->f.p1;
1059 if(q0==w->q0 && clickwin==w && w->mc.m.msec-clickmsec<500){
1060 wdoubleclick(w, &q0, &q1);
1064 clickmsec = w->mc.m.msec;
1068 wsetselect(w, q0, q1);
1069 flushimage(display, 1);
1070 while(w->mc.m.buttons){
1072 b = w->mc.m.buttons;
1086 flushimage(display, 1);
1087 while(w->mc.m.buttons == b)
1094 wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
1101 send(w->cctl, &wcm);
1105 wctlmesg(Window *w, int m, Rectangle r, Image *i)
1111 error("unknown control message");
1122 strcpy(buf, w->name);
1123 wresize(w, i, m==Moved);
1128 }else if(w == input)
1130 flushimage(display, 1);
1133 if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r))
1137 flushimage(display, 1);
1140 if(sweeping || !ptinrect(r.min, w->i->r))
1142 wmovemouse(w, r.min);
1149 wkeyctl(w, w->raw[0]);
1151 runemove(w->raw, w->raw+1, w->nraw);
1159 flushimage(display, 1);
1164 write(w->notefd, "hangup", 6);
1168 frclear(&w->f, TRUE);
1173 chanfree(w->conswrite);
1174 chanfree(w->consread);
1175 chanfree(w->mouseread);
1176 chanfree(w->wctlread);
1188 * Convert back to physical coordinates
1191 wmovemouse(Window *w, Point p)
1193 p.x += w->screenr.min.x-w->i->r.min.x;
1194 p.y += w->screenr.min.y-w->i->r.min.y;
1195 moveto(mousectl, p);
1203 if(wkeyboard!=nil && w==wkeyboard)
1207 if(oi!=w && oi!=nil)
1216 wsendctlmesg(oi, Wakeup, ZR, nil);
1220 wsendctlmesg(w, Wakeup, ZR, nil);
1226 wsetcursor(Window *w, int force)
1230 if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0)
1232 else if(wpointto(mouse->xy) == w){
1234 if(p==nil && w->holding)
1239 riosetcursor(p, force && !menuing);
1243 riosetcursor(Cursor *p, int force)
1245 if(!force && p==lastcursor)
1247 setcursor(mousectl, p);
1258 if(w->topped == topped)
1262 flushimage(display, 1);
1263 w->topped = ++topped;
1271 if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){
1273 flushimage(display, 1);
1274 w->topped = ++ topped;
1279 wbottomme(Window *w)
1281 if(w!=nil && w->i!=nil && !w->deleted){
1283 flushimage(display, 1);
1293 for(i=0; i<nwindow; i++)
1294 if(window[i]->id == id)
1300 wclosewin(Window *w)
1312 for(i=0; i<nhidden; i++)
1315 memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof(hidden[0]));
1318 for(i=0; i<nwindow; i++)
1321 memmove(window+i, window+i+1, (nwindow-i)*sizeof(Window*));
1324 /* move it off-screen to hide it, in case client is slow in letting it go */
1325 MOVEIT originwindow(w->i, r.min, view->r.max);
1330 error("unknown window in closewin");
1334 wsetpid(Window *w, int pid, int dolabel)
1340 sprint(buf, "rc %d", pid);
1342 w->label = estrdup(buf);
1343 drawsetlabel(w->label);
1347 static Rune left1[] = {
1348 '{', '[', '(', '<', 0xAB,
1349 0x207d, 0x2329, 0x27e6, 0x27e8, 0x27ea,
1350 0xfe59, 0xfe5b, 0xfe5d, 0xff08, 0xff3b, 0xff5b,
1353 static Rune right1[] = {
1354 '}', ']', ')', '>', 0xBB,
1355 0x207e, 0x232a, 0x27e7, 0x27e9, 0x27eb,
1356 0xfe5a, 0xfe5c, 0xfe5e, 0xff09, 0xff3d, 0xff5d,
1359 static Rune left2[] = { '\n', 0 };
1360 static Rune left3[] = { '\'', '"', '`', 0 };
1376 wdoubleclick(Window *w, uint *q0, uint *q1)
1382 for(i=0; left[i]!=nil; i++){
1386 /* try matching character to left, looking right */
1393 if(wclickmatch(w, c, r[p-l], 1, &q))
1397 /* try matching character to right, looking left */
1404 if(wclickmatch(w, c, l[p-r], -1, &q)){
1405 *q1 = *q0+(*q0<w->nr && c=='\n');
1407 if(c!='\n' || q!=0 || w->r[0]=='\n')
1413 /* try filling out word to right */
1414 while(*q1<w->nr && isalnum(w->r[*q1]))
1416 /* try filling out word to left */
1417 while(*q0>0 && isalnum(w->r[*q0-1]))
1422 wclickmatch(Window *w, int cl, int cr, int dir, uint *q)
1446 return cl=='\n' && nest==1;
1451 wbacknl(Window *w, uint p, uint n)
1455 /* look for start of this line if n==0 */
1456 if(n==0 && p>0 && w->r[p-1]!='\n')
1459 while(i-->0 && p>0){
1460 --p; /* it's at a newline now; back over it */
1463 /* at 128 chars, call it a line anyway */
1464 for(j=128; --j>0 && p>0; p--)
1472 wshow(Window *w, uint q0)
1478 qe = w->org+w->f.nchars;
1479 if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr)))
1482 nl = 4*w->f.maxlines/5;
1483 q = wbacknl(w, q0, nl);
1484 /* avoid going backwards if trying to go forwards - long lines! */
1485 if(!(q0>w->org && q<w->org))
1486 wsetorigin(w, q, TRUE);
1487 while(q0 > w->org+w->f.nchars)
1488 wsetorigin(w, w->org+1, FALSE);
1493 wsetorigin(Window *w, uint org, int exact)
1499 if(org>0 && !exact){
1500 /* org is an estimate of the char posn; find a newline */
1501 /* don't try harder than 256 chars */
1502 for(i=0; i<256 && org<w->nr; i++){
1503 if(w->r[org] == '\n'){
1512 if(a>=0 && a<w->f.nchars){
1513 frdelete(&w->f, 0, a);
1514 fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
1515 }else if(a<0 && -a<w->f.nchars){
1518 runemove(r, w->r+org, n);
1519 frinsert(&w->f, r, r+n, 0);
1522 frdelete(&w->f, 0, w->f.nchars);
1526 wsetselect(w, w->q0, w->q1);
1527 if(fixup && w->f.p1 > w->f.p0)
1528 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1-1), w->f.p1-1, w->f.p1, 1);
1532 wsetselect(Window *w, uint q0, uint q1)
1536 /* w->f.p0 and w->f.p1 are always right; w->q0 and w->q1 may be off */
1539 /* compute desired p0,p1 from q0,q1 */
1546 if(p0 > w->f.nchars)
1548 if(p1 > w->f.nchars)
1550 if(p0==w->f.p0 && p1==w->f.p1)
1552 /* screen disagrees with desired selection */
1553 if(w->f.p1<=p0 || p1<=w->f.p0 || p0==p1 || w->f.p1==w->f.p0){
1554 /* no overlap or too easy to bother trying */
1555 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 0);
1556 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, p1, 1);
1559 /* overlap; avoid unnecessary painting */
1561 /* extend selection backwards */
1562 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, w->f.p0, 1);
1563 }else if(p0 > w->f.p0){
1564 /* trim first part of selection */
1565 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, p0, 0);
1568 /* extend selection forwards */
1569 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, p1, 1);
1570 }else if(p1 < w->f.p1){
1571 /* trim last part of selection */
1572 frdrawsel(&w->f, frptofchar(&w->f, p1), p1, w->f.p1, 0);
1581 winsert(Window *w, Rune *r, int n, uint q0)
1587 if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){
1588 m = min(HiWater-LoWater, min(w->org, w->qh));
1600 runemove(w->r, w->r+m, w->nr);
1603 if(w->nr+n > w->maxr){
1605 * Minimize realloc breakage:
1606 * Allocate at least MinWater
1607 * Double allocation size each time
1608 * But don't go much above HiWater
1610 m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater;
1612 m = max(HiWater+MinWater, w->nr+n);
1614 w->r = runerealloc(w->r, m);
1618 runemove(w->r+q0+n, w->r+q0, w->nr-q0);
1619 runemove(w->r+q0, r, n);
1621 /* if output touches, advance selection, not qh; works best for keyboard and output */
1632 else if(q0 <= w->org+w->f.nchars)
1633 frinsert(&w->f, r, r+n, q0-w->org);
1643 if(w->f.lastlinefull)
1645 rp = malloc(messagesize);
1647 n = w->nr-(w->org+w->f.nchars);
1650 if(n > 2000) /* educated guess at reasonable amount */
1652 runemove(rp, w->r+(w->org+w->f.nchars), n);
1654 * it's expensive to frinsert more than we need, so
1657 nl = w->f.maxlines-w->f.nlines;
1660 if(rp[i++] == '\n'){
1666 frinsert(&w->f, rp, rp+i, w->f.nchars);
1667 }while(w->f.lastlinefull == FALSE);
1672 wcontents(Window *w, int *ip)
1674 return runetobyte(w->r, w->nr, ip);